#include "debugger.h"
namespace FMTOWNS {
+#define EVENT_DMAC_CYCLE 1
+
void TOWNS_DMAC::initialize()
{
UPD71071::initialize();
+ for(int ch = 0; ch < 4; ch++) {
+ address_aligns_16bit[ch] = true;
+ is_16bit_transfer[ch] = false;
+ is_16bit[ch] = false;
+ }
+ event_dmac_cycle = -1;
+ spent_clocks = 0;
+ transfer_ch = 0;
+}
+
+void TOWNS_DMAC::reset_from_io()
+{
+ for(int ch = 0; ch < 4; ch++) {
+ end_req[ch] = false;
+ calc_transfer_status(ch);
+ }
+
+ for(int ch = 0; ch < 4; ch++) {
+ //write_signals(&outputs_towns_tc[ch], ((tc & (1 << ch)) != 0) ? 0xffffffff : 0);
+ write_signals(&outputs_ube[ch], (is_16bit[ch]) ? 0xffffffff : 0);
+ }
+ clear_event(this, event_dmac_cycle);
+ //register_event(this, EVENT_DMAC_CYCLE, dmac_cycle_us, true, &event_dmac_cycle);
+ spent_clocks = 0;
+ transfer_ch = 0;
}
void TOWNS_DMAC::reset()
{
UPD71071::reset();
- dma_wrap_reg = 0xff;
-// dma_wrap_reg = 0x00;
- dma_addr_mask = 0xffffffff; // OK?
-// dma_addr_mask = 0x000fffff; // OK?
- for(int i = 0; i < 4; i++) {
- creg_set[i] = false;
- bcreg_set[i] = false;
+ set_mask_reg(mask);
+ dma_wrap = true;
+ reset_from_io();
+}
+
+
+void TOWNS_DMAC::check_mask_and_cmd()
+{
+ if(((cmd & 0x04) == 0) && ((mask & 0x0f) != 0x0f)) {
+ __UNLIKELY_IF(event_dmac_cycle < 0) {
+ register_event(this, EVENT_DMAC_CYCLE, dmac_cycle_us, true, &event_dmac_cycle);
+ }
+ } else {
+ spent_clocks = 0;
+ transfer_ch = 0;
+ __UNLIKELY_IF(event_dmac_cycle >= 0) {
+ cancel_event(this, event_dmac_cycle);
+ }
+ event_dmac_cycle = -1;
}
-// b16 = 2; // Fixed 16bit.
}
void TOWNS_DMAC::write_io16(uint32_t addr, uint32_t data)
{
- pair32_t _d, _bd;
-// out_debug_log(_T("OUT16 %04X,%04X"), addr & 0xffff, data & 0xffff);
-// if(b16 != 0) {
- switch(addr & 0x0e) {
+ switch(addr & 0x0f) {
case 0x02:
- if(base == 0) {
- creg_set[selch] = true;
- }
- bcreg_set[selch] = true;
+ dma[selch].bcreg = data;
+ dma[selch].creg = data;
+ // Reset TC bit for towns, by Tsugaru commit ab067790479064efce693f7317af13696cb68d96 .
+ tc &= ~(1 << selch);
+ write_signals(&outputs_towns_tc[selch], 0);
break;
- case 0x06:
- if(base == 0) {
- _d.d = dma[selch].areg;
- _d.w.h = data;
- dma[selch].areg = _d.d;
- }
- _d.d = dma[selch].bareg;
- _d.w.h = data;
- dma[selch].bareg = _d.d;
- return;
+ case 0x04: // ADDR LOW
+ dma[selch].bareg = (dma[selch].bareg & 0xffff0000) | (data & 0xffff);
+ dma[selch].areg = (dma[selch].areg & 0xffff0000) | (data & 0xffff);
break;
-#if 0
- case 0x08:
- if((data & 0x04) != (cmd & 0x04)) {
- if((data & 0x04) == 0) {
- out_debug_log(_T("START TRANSFER:CH=%d CMD=%04X -> %04X AREG=%08X BAREG=%08X CREG=%04X BCREG=%04X"),
- selch,
- cmd, data & 0xffff,
- dma[selch].areg, dma[selch].bareg,
- dma[selch].creg, dma[selch].bcreg
- );
- } else {
- out_debug_log(_T("CLEAR TRANSFER:CH=%d CMD=%04X -> %04X AREG=%08X BAREG=%08X CREG=%04X BCREG=%04X"),
- selch,
- cmd, data & 0xffff,
- dma[selch].areg, dma[selch].bareg,
- dma[selch].creg, dma[selch].bcreg
- );
- }
- }
+ case 0x06: // ADDR HIGH
+ dma[selch].bareg = (dma[selch].bareg & 0x0000ffff) | ((data & 0xffff) << 16);
+ dma[selch].areg = (dma[selch].areg & 0x0000ffff) | ((data & 0xffff) << 16);
+ break;
+ case 0x08: // Control
+ cmd = data;
+ check_mask_and_cmd();
+ break;
+ default:
+ write_io8((addr & 0x0e) + 0, data & 0x00ff);
+ write_io8((addr & 0x0e) + 1, (data & 0xff00) >> 8); // OK?
break;
-#endif
}
- UPD71071::write_io16(addr, data);
}
void TOWNS_DMAC::write_io8(uint32_t addr, uint32_t data)
{
// if((addr & 0x0f) == 0x0c) out_debug_log("WRITE REG: %08X %08X", addr, data);
// out_debug_log("WRITE REG: %04X %02X", addr, data);
- uint naddr;
- pair32_t _d;
- pair32_t _bd;
+ data &= 0xff;
+ uint8_t cmd_bak = cmd;
+ bool need_transfer = false;
switch(addr & 0x0f) {
case 0x00:
-// out_debug_log(_T("RESET REG(00h) to %02X"), data);
+ UPD71071::write_io8(0, data);
+ set_mask_reg(mask);
+ check_mask_and_cmd();
+ if(data & 1) {
+ reset_from_io();
+ out_debug_log(_T("RESET from I/O; B16=%s"), ((b16 & 2) != 0) ? _T("16bit") : _T("8bit"));
+ }
break;
case 0x02:
- case 0x03:
- // Note: This is *temporaly* workaround for 16bit transfer mode with 8bit bus.
- // 20200318 K.O
- if(base == 0) {
- creg_set[selch] = true;
+ // Reset TC bit for towns, by Tsugaru commit ab067790479064efce693f7317af13696cb68d96 . tc &= ~(1 << selch);
+ tc = tc & ~(1 << (selch & 3));
+ write_signals(&outputs_towns_tc[selch], 0);
+ UPD71071::write_io8(addr, data);
+ break;
+// case 0x03:
+ //tc = tc & ~(1 << (selch & 3));
+ //write_signals(&outputs_towns_tc[selch], 0);
+// UPD71071::write_io8(addr, data);
+// break;
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ {
+ uint32_t __d_mask = ~(0x000000ff << (((addr & 0x0f) - 4) * 8));
+ uint32_t __d = ((data & 0x000000ff) << (((addr & 0x0f) - 4) * 8));
+ dma[selch].bareg = (dma[selch].bareg & __d_mask) | __d;
+ dma[selch].areg = (dma[selch].areg & __d_mask) | __d;
}
- bcreg_set[selch] = true;
break;
case 0x07:
- dma[selch].bareg = manipulate_a_byte_from_dword_le(dma[selch].bareg, 3, data);
- if(base == 0) {
- dma[selch].areg = manipulate_a_byte_from_dword_le(dma[selch].areg, 3, data);
+ {
+ uint32_t __d_mask = 0x00ffffff;
+ uint32_t __d = (data & 0x000000ff) << 24;
+ dma[selch].bareg = (dma[selch].bareg & __d_mask) | __d;
+ dma[selch].areg = (dma[selch].areg & __d_mask) | __d;
}
- return;
break;
-#if 0
+ // MODE
case 0x08:
- if((data & 0x04) != (cmd & 0x04)) {
- if((data & 0x04) == 0) {
- out_debug_log(_T("START TRANSFER:CH=%d CMD=%04X -> %04X AREG=%08X BAREG=%08X CREG=%04X BCREG=%04X"),
- selch,
- cmd, (cmd & 0xff00) | (data & 0x00ff),
- dma[selch].areg, dma[selch].bareg,
- dma[selch].creg, dma[selch].bcreg
- );
- } else {
- out_debug_log(_T("CLEAR TRANSFER:CH=%d CMD=%04X -> %04X AREG=%08X BAREG=%08X CREG=%04X BCREG=%04X"),
- selch,
- cmd, (cmd & 0xff00) | (data & 0x00ff),
- dma[selch].areg, dma[selch].bareg,
- dma[selch].creg, dma[selch].bcreg
- );
- }
- }
-#endif
+ cmd = (cmd & 0xff00) | (data & 0xff);
+ check_mask_and_cmd();
break;
- case 0x0a:
-// if((selch == 3)) {
-// out_debug_log(_T("SET MODE[%d] to %02X"), selch, data);
-// }
+ case 0x09:
+ cmd = (cmd & 0xff) | ((data & 0xff) << 8);
break;
- case 0x0e:
- if(((data | req) & 0x08) != 0) {
- // out_debug_log(_T("TRANSFER ENABLE@REG0E DATA=%02X"), data);
- }
+ case 0x0a:
+ // BIT 7,6 : TRANSFER MODE
+ // DEMAND = 00
+ // SINGLE = 01
+ // BLOCK = 10
+ // CASCADE = 11
+ // BIT 5 : ADIR
+ // INC = 0
+ // DEC = 1
+ // BIT 4 : AUTI
+ // AUTO INIT = 1
+ // BIT 3, 2 : TDIR
+ // VERIFY = 00
+ // IO to MEM = 01
+ // MEM to IO = 10
+ // DONT = 11
+ // BIT 0 : W/B
+ // BYTE = 0
+ // WORD = 1
+ UPD71071::write_io8(addr, data);
+
+ #if 1
+ /* DO NOTHING */
+ #endif
+ #if 0
+ out_debug_log(_T("MODE CHANGED at CH.%d to 0x%02X Request 16bit=%s CMD=%02X"), selch, dma[selch].mode,
+ (is_16bit_transfer[selch]) ? _T("Yes") : _T("NO"),
+ cmd);
+ static const _TCHAR *dir[4] = {
+ _T("VERIFY"), _T("I/O->MEM"), _T("MEM->I/O"), _T("INVALID")
+ };
+ out_debug_log(_T("CH%d AREG=%08X CREG=%04X BAREG=%08X BCREG=%04X REQ=%d MASK=%d MODE=%02X %s"),
+ selch, dma[selch].areg, dma[selch].creg, dma[selch].bareg, dma[selch].bcreg,
+ ((req | sreq) >> selch) & 1,
+ (mask >> selch) & 1,
+ dma[selch].mode,
+ dir[(dma[selch].mode >> 2) & 3]);
+ #endif
break;
+ // MASK
case 0x0f:
- // Note: This is *temporaly* workaround for 16bit transfer mode with 8bit bus.
- // 20200318 K.O
-#if 0
-#if !defined(USE_QUEUED_SCSI_TRANSFER)
- if((dma[selch].is_16bit) && !(inputs_ube[selch])) {
- if(creg_set[selch]) {
- dma[selch].creg <<= 1;
- dma[selch].creg++;
- creg_set[selch] = false;
- }
- if(bcreg_set[selch]) {
- dma[selch].bcreg <<= 1;
- dma[selch].bcreg++;
- bcreg_set[selch] = false;
- }
- }
- bcreg_set[selch] = false;
- creg_set[selch] = false;
-#endif
-#endif
+ set_mask_reg(data);
+ check_mask_and_cmd();
break;
default:
+ UPD71071::write_io8(addr, data);
break;
}
- UPD71071::write_io8(addr, data);
+// if((need_transfer) && ((cmd & 4) == 0)) {
+// do_dma();
+// }
}
-uint32_t TOWNS_DMAC::read_io16(uint32_t addr)
+uint32_t TOWNS_DMAC::read_io8(uint32_t addr)
{
- switch(addr & 0x0e) {
- case 0x06:
- if(base == 0) {
- return ((dma[selch].areg >> 16) & 0xffff);
+ uint32_t val = 0xff;
+ switch(addr & 0x0f) {
+ case 0x07:
+ if(base) {
+ val = (dma[selch].bareg >> 24) & 0xff;
} else {
- return ((dma[selch].bareg >> 16) & 0xffff);
+ val = (dma[selch].areg >> 24) & 0xff;
}
break;
+ case 0x0b:
+ val = UPD71071::read_io8(addr);
+ // Q: Is reset TC* ? 20230429 K.O
+ //for(int ch = 0; ch < 4; ch++) {
+ // write_signals(&outputs_towns_tc[ch], 0);
+ //}
+ break;
default:
-// return read_io8(addr & 0x0e);
+ val = UPD71071::read_io8(addr);
break;
}
- return UPD71071::read_io16(addr);
+ return val;
}
-uint32_t TOWNS_DMAC::read_io8(uint32_t addr)
+uint32_t TOWNS_DMAC::read_io16(uint32_t addr)
{
- uint32_t val;
- pair32_t _d;
switch(addr & 0x0f) {
- case 0x07:
- if(base == 0) {
- _d.d = dma[selch].areg;
+ case 0x02:
+ if(base != 0) {
+ return dma[selch].bcreg;
+ } else {
+ return dma[selch].creg;
+ }
+ break;
+ case 0x04:
+ if(base != 0) {
+ return dma[selch].bareg & 0x0000ffff;
+ } else {
+ return dma[selch].areg & 0x0000ffff;
+ }
+ break;
+ case 0x06:
+ if(base != 0) {
+ return (dma[selch].bareg & 0xffff0000) >> 16;
} else {
- _d.d = dma[selch].bareg;
+ return (dma[selch].areg & 0xffff0000) >> 16;
}
- return (uint32_t)(_d.b.h3);
+ break;
+ case 0x08:
+ return cmd;
+ break;
+ default:
break;
}
- return UPD71071::read_io8(addr);
+ uint32_t val = 0;
+ val = read_io8((addr & 0x0e) + 0) & 0x00ff;
+ val = val | ((read_io8((addr & 0x0e) + 1) & 0xff) << 8); // OK?
+ return val;
}
-
-void TOWNS_DMAC::do_dma_inc_dec_ptr_8bit(int c)
+
+void TOWNS_DMAC::inc_dec_ptr_a_byte(uint32_t& addr, const bool inc)
{
// Note: FM-Towns may extend to 32bit.
- __LIKELY_IF(dma_wrap_reg != 0) {
- uint32_t high_a = dma[c].areg & 0xff000000;
- if(dma[c].mode & 0x20) {
- dma[c].areg = dma[c].areg - 1;
- } else {
- dma[c].areg = dma[c].areg + 1;
- }
- dma[c].areg = ((dma[c].areg & 0x00ffffff) | high_a) & dma_addr_mask;
+ // Note: By Tsugaru, commit e5920fdc1ba89ba10172f0954ecf1107bb592919,
+ // ADIR bit (bit 5 of mode register)
+ // has not supported by TOWNS DMAC.
+ // Very tanks to YAMAKAWA-San. - 20230617 K.O
+ //
+ // Commiting message is below:
+ // Author: CaptainYS <PEB01130@nifty.com>
+ // AuthorDate: Sat Feb 29 23:43:52 2020 -0500
+ // Commit: CaptainYS <PEB01130@nifty.com>
+ // CommitDate: Sat Feb 29 23:43:52 2020 -0500
+ //
+ // Parent: 9390f5be
+ // Found Device-DMACh correspondence in [2] pp. 56.
+ // Still trying to find the correct sequence of CD-ROM drive
+ // data transfer.
+ // Unit test still temporarily broken.
+
+// uint32_t incdec = (inc) ? 1 : UINT32_MAX;
+ const uint32_t incdec = 1;
+ __LIKELY_IF(dma_wrap) {
+ uint32_t high_a = addr & 0xff000000;
+ addr = (addr + incdec) & 0x00ffffff;
+ addr = addr | high_a;
} else {
- if(dma[c].mode & 0x20) {
- dma[c].areg = (dma[c].areg - 1) & dma_addr_mask;
- } else {
- dma[c].areg = (dma[c].areg + 1) & dma_addr_mask;
- }
+ addr = (addr + incdec) & 0xffffffff;
}
}
-void TOWNS_DMAC::do_dma_inc_dec_ptr_16bit(int c)
+void TOWNS_DMAC::inc_dec_ptr_two_bytes(uint32_t& addr, const bool inc)
{
// Note: FM-Towns may extend to 32bit.
- __LIKELY_IF(dma_wrap_reg != 0) {
- uint32_t high_a = dma[c].areg & 0xff000000;
- if(dma[c].mode & 0x20) {
- dma[c].areg = dma[c].areg - 2;
- } else {
- dma[c].areg = dma[c].areg + 2;
- }
- dma[c].areg = ((dma[c].areg & 0x00ffffff) | high_a) & dma_addr_mask;
+
+ //uint32_t incdec = (inc) ? 2 : (UINT32_MAX - 1);
+ const uint32_t incdec = 2;
+ __LIKELY_IF(dma_wrap) {
+ uint32_t high_a = addr & 0xff000000;
+ addr = (addr + incdec) & 0x00ffffff;
+ addr = addr | high_a;
} else {
- if(dma[c].mode & 0x20) {
- dma[c].areg = (dma[c].areg - 2) & dma_addr_mask;
- } else {
- dma[c].areg = (dma[c].areg + 2) & dma_addr_mask;
- }
+ addr = (addr + incdec) & 0xffffffff;
}
}
-bool TOWNS_DMAC::do_dma_epilogue(int c)
+uint32_t TOWNS_DMAC::read_8bit_from_device(DEVICE* dev, uint32_t addr, int* wait)
{
- if((dma[c].creg == 0) || ((dma[c].endreq) && !(dma[c].end) && ((dma[c].mode & 0xc0) != 0x40))) { // OK?
- // TC
- bool is_tc = false;
- if((dma[c].end) || (dma[c].endreq)) is_tc = true;
- // TC
- if(dma[c].bcreg < (dma[c].creg - 1)) {
- is_tc = true;
+ __UNLIKELY_IF(dev == nullptr) {
+ if(wait != nullptr) {
+ *wait = 0; // ToDo
}
- if(is_tc) {
-#if 0
- out_debug_log(_T("TRANSFER COMPLETED:CH=%d AREG=%08X BAREG=%08X CREG=%08X BCREG=%08X"),
- c,
- (dma[c].areg & 0xffffffff) ,
- (dma[c].bareg & 0xffffffff) ,
- dma[c].creg & 0x00ffffff,
- dma[c].bcreg & 0x00ffffff
- );
-#endif
+ return 0xff;
+ }
+ uint32_t val;
+ val = dev->read_dma_io8w(addr, wait);
+ return val;
+}
+
+void TOWNS_DMAC::write_8bit_to_device(DEVICE* dev, uint32_t addr, uint32_t data, int* wait)
+{
+ __UNLIKELY_IF(dev == nullptr) {
+ if(wait != nullptr) {
+ *wait = 0; // ToDo
}
+ return;
}
- return UPD71071::do_dma_epilogue(c);
+ dev->write_dma_io8w(addr, data, wait);
}
-
-uint32_t TOWNS_DMAC::read_signal(int id)
+
+
+uint32_t TOWNS_DMAC::read_8bit_from_memory(uint32_t addr, int* wait, const bool is_use_debugger)
{
- if(id == SIG_TOWNS_DMAC_WRAP_REG) {
- return dma_wrap_reg;
- } else if(id == SIG_TOWNS_DMAC_ADDR_MASK) {
- return dma_addr_mask;
+ __UNLIKELY_IF((is_use_debugger) && (d_debugger != NULL)) {
+ return d_debugger->read_via_debugger_data8w(addr, wait);
+ } else {
+ return read_via_debugger_data8w(addr, wait);
}
- return UPD71071::read_signal(id);
}
-void TOWNS_DMAC::write_signal(int id, uint32_t data, uint32_t _mask)
+void TOWNS_DMAC::write_8bit_to_memory(uint32_t addr, uint32_t data, int* wait, const bool is_use_debugger)
{
- if(id == SIG_TOWNS_DMAC_WRAP_REG) {
- dma_wrap_reg = data;
-// this->write_signal(SIG_TOWNS_DMAC_ADDR_MASK, data, mask);
- } else if(id == SIG_TOWNS_DMAC_ADDR_MASK) {
- // From eFMR50 / memory.cpp / update_dma_addr_mask()
- dma_addr_mask = data;
+ __UNLIKELY_IF((is_use_debugger) && (d_debugger != NULL)) {
+ d_debugger->write_via_debugger_data8w(addr, data, wait);
} else {
- // Fallthrough.
-// if(id == SIG_UPD71071_CH1) {
-// out_debug_log(_T("DRQ from SCSI %02X %02X"), data, mask);
-// }
- UPD71071::write_signal(id, data, _mask);
+ write_via_debugger_data8w(addr, data, wait);
}
-}
+}
-void TOWNS_DMAC::do_dma_dev_to_mem_8bit(int c)
+uint32_t TOWNS_DMAC::read_16bit_from_device(DEVICE* dev, uint32_t addr, int* wait)
{
- // io -> memory
+ __UNLIKELY_IF(dev == nullptr) {
+ if(wait != nullptr) {
+ *wait = 0; // ToDo
+ }
+ return 0xffff;
+ }
uint32_t val;
- uint32_t addr = dma[c].areg;
- reset_ube(c);
- val = dma[c].dev->read_dma_io8(0);
-
- // update temporary register
- tmp = (tmp >> 8) | (val << 8);
-
- if(_USE_DEBUGGER) {
- if(d_debugger != NULL && d_debugger->now_device_debugging) {
- d_debugger->write_via_debugger_data8(addr, val);
- } else {
- write_via_debugger_data8(addr, val);
+ val = dev->read_dma_io16w(addr, wait);
+ return val;
+}
+
+void TOWNS_DMAC::write_16bit_to_device(DEVICE* dev, uint32_t addr, uint32_t data, int* wait)
+{
+ __UNLIKELY_IF(dev == nullptr) {
+ if(wait != nullptr) {
+ *wait = 0; // ToDo
}
+ return;
+ }
+ dev->write_dma_io16w(addr, data, wait);
+}
+
+uint32_t TOWNS_DMAC::read_16bit_from_memory(uint32_t addr, int* wait, const bool is_use_debugger)
+{
+ __UNLIKELY_IF((is_use_debugger) && (d_debugger != NULL)) {
+ return d_debugger->read_via_debugger_data16w(addr, wait);
} else {
- write_via_debugger_data8(addr, val);
- }
+ return read_via_debugger_data16w(addr, wait);
+ }
}
-void TOWNS_DMAC::do_dma_mem_to_dev_8bit(int c)
+void TOWNS_DMAC::write_16bit_to_memory(uint32_t addr, uint32_t data, int* wait, const bool is_use_debugger)
{
- // memory -> io
- uint32_t val;
- uint32_t addr = dma[c].areg;
- reset_ube(c);
- if(_USE_DEBUGGER) {
- if(d_debugger != NULL && d_debugger->now_device_debugging) {
- val = d_debugger->read_via_debugger_data8(addr);
- } else {
- val = read_via_debugger_data8(addr);
- }
+ __UNLIKELY_IF((is_use_debugger) && (d_debugger != NULL)) {
+ d_debugger->write_via_debugger_data16w(addr, data, wait);
} else {
- val = read_via_debugger_data8(addr);
+ write_via_debugger_data16w(addr, data, wait);
+ }
+}
+
+void TOWNS_DMAC::do_dma_16bit(DEVICE* dev, const uint8_t tr_mode, uint32_t& memory_address, const bool compressed, const bool extended, bool is_use_debugger, int& wait)
+{
+ uint16_t val;
+ int wait_w = 0;
+ int wait_r = 0;
+ const int wait_compressed = (compressed) ? 5 : 7;
+ switch(tr_mode & 0x0c) {
+ case 0x00: // VERIFY
+ val = read_16bit_from_device(dev, 0, &wait_r);
+ tmp = val;
+ read_16bit_from_memory(memory_address, &wait_w, is_use_debugger);
+ break;
+ case 0x04: // DEVICE TO MEMORY
+ val = read_16bit_from_device(dev, 0, &wait_r);
+ tmp = val;
+ write_16bit_to_memory(memory_address, val, &wait_w, is_use_debugger);
+ break;
+ case 0x08: // MEMORY TO DEVICE
+ val = read_16bit_from_memory(memory_address, &wait_r, is_use_debugger);
+ tmp = val;
+ write_16bit_to_device(dev, 0, val, &wait_w);
+ break;
+ case 0x0c: // MEMORY TO MEMORY : still unimplemented
+ break;
+ default:
+ break;
}
- // update temporary register
- tmp = (tmp >> 8) | (val << 8);
-
- dma[c].dev->write_dma_io8(0, val);
+ wait += wait_compressed;
+ if(extended) {
+ wait = wait + wait_r + wait_w;
+ }
+// inc_dec_ptr_two_bytes(memory_adderss, !(tr_mode & 0x20));
+ inc_dec_ptr_two_bytes(memory_address, true);
}
-void TOWNS_DMAC::do_dma_dev_to_mem_16bit(int c)
+void TOWNS_DMAC::do_dma_8bit(DEVICE* dev, const uint8_t tr_mode, uint32_t& memory_address, const bool compressed, const bool extended, bool is_use_debugger, int& wait)
{
- // io -> memory
uint32_t val;
- uint32_t addr = dma[c].areg;
- set_ube(c);
- val = dma[c].dev->read_dma_io16(0);
- // update temporary register
- tmp = val;
-/*
- if((addr & 1) != 0) {
- // If odd address, write a byte.
- uint32_t tval = (val >> 8) & 0xff;
- if(_USE_DEBUGGER) {
- if(d_debugger != NULL && d_debugger->now_device_debugging) {
- d_debugger->write_via_debugger_data8(addr, tval);
- } else {
- write_via_debugger_data8(addr, tval);
+ int wait_w = 0;
+ int wait_r = 0;
+ const int wait_compressed = (compressed) ? 5 : 7;
+ switch(tr_mode & 0x0c) {
+ case 0x00: // VERIFY
+ val = read_8bit_from_device(dev, 0, &wait_r);
+ tmp = ((tmp & 0xff00) >> 8) | ((val & 0xff) << 8);
+ read_8bit_from_memory(memory_address, &wait_w, is_use_debugger);
+ break;
+ case 0x04: // DEVICE TO MEMORY
+ val = read_8bit_from_device(dev, 0, &wait_r);
+ tmp = ((tmp & 0xff00) >> 8) | ((val & 0xff) << 8);
+ write_8bit_to_memory(memory_address, val, &wait_w, is_use_debugger);
+ break;
+ case 0x08: // MEMORY TO DEVICE
+ val = read_8bit_from_memory(memory_address, &wait_r, is_use_debugger);
+ tmp = ((tmp & 0xff00) >> 8) | ((val & 0xff) << 8);
+ write_8bit_to_device(dev, 0, val, &wait_w);
+ break;
+ case 0x0c: // MEMORY TO MEMORY : still unimplemented
+ break;
+ default:
+ break;
+ }
+ wait += wait_compressed;
+ if(extended) {
+ wait = wait + wait_r + wait_w;
+ }
+// inc_dec_ptr_a_byte(memory_address, !(tr_mode & 0x20));
+ inc_dec_ptr_a_byte(memory_address, true);
+}
+
+bool TOWNS_DMAC::check_is_16bit(int ch)
+{
+ const bool __is_16bit = (is_16bit_transfer[ch] || force_16bit_transfer[ch]);
+ if(__is_16bit != is_16bit[ch]) {
+ is_16bit[ch] = __is_16bit;
+ write_signals(&outputs_ube[ch], (__is_16bit) ? 0xffffffff : 0x00000000); // Reset UBE
+ }
+ return __is_16bit;
+}
+
+bool TOWNS_DMAC::decrement_counter(const int ch, uint8_t mode, uint16_t& counter, bool& is_single)
+{
+ bool is_terminated = false;
+ uint8_t c = ch & 3;
+ uint16_t counter_bak = counter;
+ uint8_t bit = 1 << c;
+ __LIKELY_IF((_SINGLE_MODE_DMA) || ((mode & 0xc0) == 0x40)) {
+ is_single = true;
+ } else {
+ is_single = false;
+ }
+ counter--; // OK?
+
+ __UNLIKELY_IF((counter == 0xffff) && (counter_bak == 0x0000)) {
+ do_end_sequence(c, true);
+ set_ack(c, true);
+ is_terminated = true;
+ return is_terminated;
+ } else __UNLIKELY_IF(!(is_single) && (end_req[c])) {
+ do_end_sequence(c, false);
+ set_ack(c, true);
+ is_terminated = true;
+ return is_terminated;
+ } else {
+ // Continue
+ switch(mode & 0xc0) {
+ case 0x00: // DEMAND
+ // WHY STOP:
+ // - COUNTDOWN REACHED.
+ // - END_REQ[c] asserted.
+ // - DMA REQ MADE INACTIVE.
+ // -> CLEAR REQ and SREQ bit, ASSERT TC REGISTER.
+ __UNLIKELY_IF((req & bit) == 0) {
+ do_end_sequence(c, false);
+ is_terminated = true;
+ return is_terminated;
}
+ break;
+ case 0x40: // SINGLE
+ // WHY STOP:
+ // - COUNTDOWN REACHED.
+ // - STOP PER BYTE/WORD TRANSFER.
+ // SOMETHING DON'T ASSERT TC, EXCEPTS COUNT DOWN REACHED TO 0.
+ // -> CLEAR REQ and SREQ bit.
+ req &= ~bit;
+ sreq &= ~bit;
+ is_terminated = true;
+ break;
+ case 0x80: // BURST
+ // WHY STOP:
+ // - END_REQ[c] asserted.
+ // - COUNTDOWN REACHED.
+ // -> DO NOTHING.
+ break;
+ case 0xC0: // CASCADE
+ // ToDo.
+ break;
+ }
+ }
+ set_ack(c, true);
+ return is_terminated;
+}
+
+int TOWNS_DMAC::do_dma_single(const int ch, const bool is_use_debugger, bool compressed, bool extended, bool& is_terminated, bool& is_single)
+{
+ int _clocks = 0;
+ int c = ch & 3;
+ uint8_t bit = 1 << c;
+ if(((req | sreq) & bit) && !(mask & bit)) {
+ set_ack(c, false);
+ __UNLIKELY_IF(!running) {
+ _clocks += 2; // S0
+ running = true;
+ }
+ if(check_is_16bit(c)) {
+ do_dma_16bit(dma[c].dev, dma[c].mode, dma[c].areg, compressed, extended, is_use_debugger, _clocks);
} else {
- write_via_debugger_data8(addr, tval);
+ do_dma_8bit(dma[ch].dev, dma[c].mode, dma[c].areg, compressed, extended, is_use_debugger, _clocks);
}
- } else {
-*/
- // 16bit
- if(_USE_DEBUGGER) {
- if(d_debugger != NULL && d_debugger->now_device_debugging) {
- d_debugger->write_via_debugger_data16(addr, val);
- } else {
- write_via_debugger_data16(addr, val);
+ // Note: At FM-Towns, SCSI's DMAC will be set after
+ // SCSI bus phase become DATA IN/DATA OUT.
+ // Before bus phase became DATA IN/DATA OUT,
+ // DMAC mode and state was unstable (and ASSERTED
+ // DRQ came from SCSI before this state change).
+ // ToDo: Stop correctly before setting.
+ // CHECK COUNT DOWN REACHED.
+ is_terminated = decrement_counter(ch, dma[c].mode, dma[c].creg, is_single);
+ }
+ return _clocks;
+}
+
+
+void TOWNS_DMAC::do_dma_internal()
+{
+ __UNLIKELY_IF((cmd & 0x04) != 0) return;
+ bool is_hold = ((cmd & 0x0100) != 0) ? true : false;
+ bool compressed = ((cmd & 0x08) != 0);
+ bool extended = ((cmd & 0x20) != 0);
+ // run dma
+ bool is_use_debugger = false;
+ if(__USE_DEBUGGER) {
+ __LIKELY_IF(d_debugger != NULL) {
+ is_use_debugger = d_debugger->now_device_debugging;
+ }
+ }
+ bool is_rot = ((cmd & 0x10) != 0) ? true : false;
+ if(!(is_rot)) {
+ transfer_ch = 0;
+ }
+ for(int i = 0; i < 4; i++) {
+ bool is_single = false;
+ bool is_terminated = false;
+ int clocks = do_dma_single((transfer_ch + i) & 3, is_use_debugger, compressed, extended, is_terminated, is_single);
+ if(clocks > 0) {
+ spent_clocks += clocks;
+ if(!(is_hold)) {
+ break;
}
- } else {
- write_via_debugger_data16(addr, val);
+ } else if((is_terminated) && !(is_hold)) {
+ break;
}
-// }
+ }
+ if(is_rot) {
+ transfer_ch = (transfer_ch + 1) & 3;
+ }
+ __UNLIKELY_IF((spent_clocks > 0) && (d_cpu != NULL)) {
+ d_cpu->set_extra_clock(spent_clocks);
+ spent_clocks = 0;
+ }
+ return;
}
-void TOWNS_DMAC::do_dma_mem_to_dev_16bit(int c)
+void TOWNS_DMAC::do_dma()
{
- // memory -> io
- uint32_t val;
- uint32_t addr = dma[c].areg;
- set_ube(c);
- if(_USE_DEBUGGER) {
- if(d_debugger != NULL && d_debugger->now_device_debugging) {
- val = d_debugger->read_via_debugger_data16(addr);
- } else {
- val = this->read_via_debugger_data16(addr);
+ // check DDMA
+ __LIKELY_IF(spent_clocks > 0) {
+ spent_clocks -= clock_multiply;
+ } else if((cmd & 0x04) == 0) {
+ spent_clocks = 0;
+ do_dma_internal();
+ }
+ __UNLIKELY_IF(_SINGLE_MODE_DMA) {
+ __LIKELY_IF(d_dma) {
+ d_dma->do_dma();
+ }
+ }
+}
+
+void TOWNS_DMAC::event_callback(int event_id, int err)
+{
+ __LIKELY_IF(event_id == EVENT_DMAC_CYCLE) {
+ __LIKELY_IF(spent_clocks > 0) {
+ spent_clocks -= clock_multiply;
+ } else if((cmd & 0x04) == 0) {
+ spent_clocks = 0;
+ do_dma_internal();
}
+ }
+}
+uint32_t TOWNS_DMAC::read_signal(int id)
+{
+ if((id >= SIG_TOWNS_DMAC_MASK_CH0) && (id <= SIG_TOWNS_DMAC_MASK_CH3)) {
+ int ch = id - SIG_TOWNS_DMAC_MASK_CH0;
+ uint8_t _bit = 1 << ch;
+ return ((_bit & mask) == 0) ? 0xffffffff : 0x00000000;
+ }
+ if(id == SIG_TOWNS_DMAC_WRAP) {
+ return (dma_wrap) ? 0xffffffff : 0;
+ }
+ return UPD71071::read_signal(id);
+}
+
+void TOWNS_DMAC::write_signal(int id, uint32_t data, uint32_t _mask)
+{
+ if(id == SIG_TOWNS_DMAC_WRAP) {
+ dma_wrap = ((data & _mask) != 0) ? true : false;
+ } else if((id >= SIG_TOWNS_DMAC_EOT_CH0) && (id <= SIG_TOWNS_DMAC_EOT_CH3)) {
+ int ch = (id - SIG_TOWNS_DMAC_EOT_CH0) & 3;
+ end_req[ch] = ((data & _mask) != 0) ? true : false;
} else {
- val = this->read_via_debugger_data16(addr);
+ __LIKELY_IF((id >= SIG_UPD71071_CH0) && (id <= SIG_UPD71071_CH3)) {
+ int ch = (id - SIG_UPD71071_CH0) & 3;
+ uint8_t bit = 1 << ch;
+ if(data & _mask) {
+ if(!(req & bit)) {
+ req |= bit;
+ if(!(_SINGLE_MODE_DMA)) {
+ do_dma();
+ }
+ }
+ } else {
+ req &= ~bit;
+ }
+
+ } else {
+ // Fallthrough.
+ UPD71071::write_signal(id, data, _mask);
+ }
}
-// if((addr & 1) != 0) {
-// // If odd address, read a high byte.
-// val = (val >> 8) & 0xff;
-// }
- // update temporary register
- tmp = val;
-
- dma[c].dev->write_dma_io16(0, val);
}
-
+
// note: if SINGLE_MODE_DMA is defined, do_dma() is called in every machine cycle
bool TOWNS_DMAC::get_debug_regs_info(_TCHAR *buffer, size_t buffer_len)
-{
+{
static const _TCHAR *dir[4] = {
_T("VERIFY"), _T("I/O->MEM"), _T("MEM->I/O"), _T("INVALID")
};
}
{
my_stprintf_s(buffer, buffer_len,
- _T("16Bit=%s ADDR_MASK=%08X ADDR_WRAP=%02X \n")
+ _T("16Bit=%s ADDR_WRAP=%s \n")
_T("SELECT CH=%d BASE=%02X REQ=%02X SREQ=%02X MASK=%02X TC=%02X ")
_T("CMD=%04X TMP=%04X\n")
_T("%s")
_T("%s")
_T("%s")
_T("%s"),
- (b16 != 0) ? _T("YES") : _T("NO"), dma_addr_mask, dma_wrap_reg,
+ (b16 != 0) ? _T("YES") : _T("NO"), (dma_wrap) ? _T("YES") : _T("NO"),
selch, base, req, sreq, mask, tc,
cmd, tmp,
sbuf[0],
}
return false;
}
-
-#define STATE_VERSION 3
-
+
+#define STATE_VERSION 11
+
bool TOWNS_DMAC::process_state(FILEIO *state_fio, bool loading)
{
if(!state_fio->StateCheckUint32(STATE_VERSION)) {
if(!(UPD71071::process_state(state_fio, loading))) {
return false;
}
- state_fio->StateValue(dma_wrap_reg);
- state_fio->StateValue(dma_addr_mask);
- state_fio->StateArray(creg_set, sizeof(creg_set), 1);
- state_fio->StateArray(bcreg_set, sizeof(bcreg_set), 1);
+ state_fio->StateValue(dma_wrap);
+ state_fio->StateArray(end_req, sizeof(end_req), 1);
+ state_fio->StateValue(spent_clocks);
+ state_fio->StateValue(transfer_ch);
+ state_fio->StateValue(event_dmac_cycle);
+
+ if(loading) {
+ for(int ch = 0; ch < 4; ch++) {
+ calc_transfer_status(ch);
+ }
+ }
return true;
}
+
+
}