OSDN Git Service

[VM][FMTOWNS][CDROM] CDROM checks DMAC's mask as running DMAC CH.3.
[csp-qt/common_source_project-fm7.git] / source / src / vm / fmtowns / dmac.cpp
index 6db41f8..b6d8618 100644 (file)
@@ -3,79 +3,89 @@
 #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_addr_mask = 0xffffffff; // OK?
-//     dma_addr_mask = 0x000fffff; // OK?
-       for(int i = 0; i < 4; i++) {
-               creg_set[i] = false;
-               bcreg_set[i] = false;
-       }
-//     b16 = 2; // Fixed 16bit.
+       set_mask_reg(mask);
+       dma_wrap = true;
+       reset_from_io();
 }
 
-void TOWNS_DMAC::write_io16(uint32_t addr, uint32_t data)
+
+void TOWNS_DMAC::check_mask_and_cmd()
 {
-       pair32_t _d, _bd;
-       if(b16 != 0) {
-               switch(addr & 0x0f) {
-               case 0x02:
-               case 0x03:
-                       if(base == 0) {
-                               creg_set[selch] = true;
-                               dma[selch].creg = data & 0xffff;
-                       }
-                       dma[selch].bcreg = data & 0xffff;
-                       bcreg_set[selch] = true;
-                       return;
-                       break;
-               case 0x04:
-               case 0x05:
-               case 0x06:
-               case 0x07:
-                       _d.d = dma[selch].areg;
-                       _bd.d = dma[selch].bareg;
-                       if((addr & 0x0f) < 6) {
-                               if(base == 0) {
-                                       _d.w.l = (data & 0xffff);
-                                       dma[selch].areg = _d.d;
-                               }
-                               _bd.w.l = (data & 0xffff);
-                               dma[selch].bareg = _bd.d;
-                       } else {
-                               if(base == 0) {
-                                       _d.w.h = (data & 0xffff);
-                                       dma[selch].areg = _d.d;
-                               }
-                               _bd.w.h = (data & 0xffff);
-                               dma[selch].bareg = _bd.d;
-                       }
-                       break;
-               case 0x08:
-               case 0x09:
-                       cmd = data & 0xffff;
-//                     if(((data & 0x04) != (cmd & 0x04)) && (selch == 3)) {
-//                             if((data & 0x04) == 0) {
-//                                     out_debug_log(_T("TRANSFER: CMD=%04X -> %04X CH=%d\nADDR=%08X"), cmd, (cmd & 0xff00) | (data & 0xff), selch, dma[selch].areg);
-//                             }
-//                     }
-                       break;
-               default:
-//                     write_io8(addr & 0x0e, data);
-                       write_io8(addr, data);
-                       break;
+       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 {
-               write_io8(addr, data);
-//             write_io8((addr & 0x0e) + 0, data);
-//             write_io8((addr & 0x0e) + 1, data);
+               spent_clocks = 0;
+               transfer_ch = 0;
+               __UNLIKELY_IF(event_dmac_cycle >= 0) {
+                       cancel_event(this, event_dmac_cycle);
+               }
+               event_dmac_cycle = -1;
+       }
+}
+
+void TOWNS_DMAC::write_io16(uint32_t addr, uint32_t data)
+{
+       switch(addr & 0x0f) {
+       case 0x02:
+               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 0x04: // ADDR LOW
+               dma[selch].bareg = (dma[selch].bareg & 0xffff0000) | (data & 0xffff);
+               dma[selch].areg  = (dma[selch].areg  & 0xffff0000) | (data & 0xffff);
+               break;
+       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;
        }
 }
 
@@ -83,365 +93,586 @@ 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:
-               _d.d  = dma[selch].areg;
-               _bd.d = dma[selch].bareg;
-               _d.b.h3  = data;
-               _bd.b.h3 = data;
-               if(base == 0) {
-                       dma[selch].areg = _d.d;
+               {
+                       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;
                }
-               dma[selch].bareg = _bd.d;
-               return;
                break;
+               // MODE
        case 0x08:
-//             if(((data & 0x04) != (cmd & 0x04)) && (selch == 3)) {
-//                     if((data & 0x04) != 0) break;
-//                     out_debug_log(_T("TRANSFER: CMD=%04X -> %04X CH=%d\nADDR=%08X"), cmd, (cmd & 0xff00) | (data & 0xff), selch, dma[selch].areg);
-//             }
+               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 !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
+               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)
 {
-       if(b16 != 0) {
-               switch(addr & 0x0f) {
-               case 0x02:
-               case 0x03:
-                       if(base == 0) {
-                               return (dma[selch].creg & 0xffff);
-                       } else {
-                               return (dma[selch].bcreg & 0xffff);
-                       }
-                       break;
-               case 0x04:
-               case 0x05:
-                       if(base == 0) {
-                               return (dma[selch].areg & 0xffff);
-                       } else {
-                               return (dma[selch].bareg & 0xffff);
-                       }
-                       break;
-               case 0x06:
-               case 0x07:
-                       if(base == 0) {
-                               return ((dma[selch].areg >> 16) & 0xffff);
-                       } else {
-                               return ((dma[selch].bareg >> 16) & 0xffff);
-                       }
-                       break;
-               case 0x08:
-               case 0x09:
-                       return (uint32_t)(cmd & 0xffff);
-                       break;
-               default:
-                       return read_io8(addr);
-//                     return read_io8(addr & 0x0e);
-                       break;
+       uint32_t val = 0xff;
+       switch(addr & 0x0f) {
+       case 0x07:
+               if(base) {
+                       val = (dma[selch].bareg >> 24) & 0xff;
+               } else {
+                       val = (dma[selch].areg >> 24) & 0xff;
                }
-       } else {
-               pair16_t _d;
-               _d.w = 0;
-               _d.b.l = read_io8(addr);
-//             _d.b.l = read_io8((addr & 0x0e) + 0);
-//             _d.b.h = read_io8((addr & 0x0e) + 1);
-               return (uint32_t)(_d.w);
+               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:
+               val = UPD71071::read_io8(addr);
+               break;
        }
+       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 0x01:
-               return (base << 3) | (1 << (selch & 3));
-               break;
        case 0x02:
-       case 0x03:
-               if(base == 0) {
-                       _d.d = dma[selch].creg;
-#if !defined(USE_QUEUED_SCSI_TRANSFER)
-                       if((dma[selch].is_16bit) && !(inputs_ube[selch])) {
-                               if(!(creg_set[selch])) {
-                                       _d.d >>= 1;
-                               }
-                       }
-#endif
+               if(base != 0) {
+                       return dma[selch].bcreg;
                } else {
-                       _d.d = dma[selch].bcreg;
-#if !defined(USE_QUEUED_SCSI_TRANSFER)
-                       if((dma[selch].is_16bit) && !(inputs_ube[selch])) {
-                               if(!(bcreg_set[selch])) {
-                                       _d.d >>= 1;
-                               }
-                       }
-#endif
+                       return dma[selch].creg;
                }
-               switch(addr & 0x0f) {
-               case 2:
-                       return _d.b.l;
-                       break;
-               case 3:
-                       return _d.b.h;
-                       break;
+               break;
+       case 0x04:
+               if(base != 0) {
+                       return dma[selch].bareg & 0x0000ffff;
+               } else {
+                       return dma[selch].areg  & 0x0000ffff;
                }
                break;
-       case 0x07:
-               if(base == 0) {
-                       _d.d = dma[selch].areg;
+       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.
-       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.
-       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;
+       }
+}
+
+uint32_t TOWNS_DMAC::read_8bit_from_device(DEVICE* dev, uint32_t addr, int* wait)
+{
+       __UNLIKELY_IF(dev == nullptr) {
+               if(wait != nullptr) {
+                       *wait = 0; // ToDo
                }
+               return 0xff;
        }
+       uint32_t val;
+       val = dev->read_dma_io8w(addr, wait);
+       return val;
 }
 
-bool TOWNS_DMAC::do_dma_epilogue(int c)
+void TOWNS_DMAC::write_8bit_to_device(DEVICE* dev, uint32_t addr, uint32_t data, int* wait)
 {
-       if(dma[c].creg == 0) {  // OK?
-               // TC
-//             if(c == 3) {
-//                     out_debug_log(_T("TRANSFER COMPLETED CH.3: AREG=%08X BAREG=%08X CREG=%08X BCREG=%08X"),
-//                                               (dma[c].areg & 0xffffffff) ,
-//                                               (dma[c].bareg & 0xffffffff) ,
-//                                               dma[c].creg & 0x00ffffff,
-//                                               dma[c].bcreg & 0x00ffffff
-//                             );
-//                                               
-//             }
-       }
-       return UPD71071::do_dma_epilogue(c);
+       __UNLIKELY_IF(dev == nullptr) {
+               if(wait != nullptr) {
+                       *wait = 0; // ToDo
+               }
+               return;
+       }
+       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(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);
        }
-       // update temporary register
-       tmp = (tmp >> 8) | (val << 8);
-       
-       dma[c].dev->write_dma_io8(0, val);
 }
 
-void TOWNS_DMAC::do_dma_dev_to_mem_16bit(int c)
+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;
+       }
+       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_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")
        };
@@ -463,14 +694,14 @@ bool TOWNS_DMAC::get_debug_regs_info(_TCHAR *buffer, size_t buffer_len)
        }
        {
                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],
@@ -481,9 +712,9 @@ bool TOWNS_DMAC::get_debug_regs_info(_TCHAR *buffer, size_t buffer_len)
        }
        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)) {
@@ -495,11 +726,20 @@ bool TOWNS_DMAC::process_state(FILEIO *state_fio, bool loading)
        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;
 }
+
+
 }