--- nono/vm/dmac.cpp 2026/04/29 17:05:18 1.1.1.12 +++ nono/vm/dmac.cpp 2026/04/29 17:05:55 1.1.1.18 @@ -10,49 +10,57 @@ #include "dmac.h" #include "adpcm.h" +#include "event.h" #include "fdc.h" #include "interrupt.h" #include "mainbus.h" +#include "monitor.h" #include "mpu.h" #include "scheduler.h" +#include "syncer.h" // time 時間後に呼び出すコールバックをセットする。 // func の書式が面倒なのを省略して書きたいため。 -#define CallAfter(func_, time_) do { \ - event.func = ToEventCallback(&DMACDevice::func_); \ - event.time = time_ * 80_nsec; \ +#define CallAfter(func_, time_, wait_) do { \ + event->func = ToEventCallback(&DMACDevice::func_); \ + event->time = time_ * 80_nsec + wait_; \ scheduler->StartEvent(event); \ } while (0) +// InsideOut p.135 +static const busdata read_wait = busdata::Wait(37 * 40_nsec); +static const busdata write_wait = busdata::Wait(31 * 40_nsec); + +// チャンネルログ +#define chan_putlog(chan, lv, fmt...) do { \ + if ((chan)->loglevel >= (lv)) \ + (chan)->putlogn(fmt); \ +} while (0) + // コンストラクタ DMACDevice::DMACDevice() : inherited(OBJ_DMAC) { - for (int i = 0; i < channel.size(); i++) { - channel[i].ch = i; - } - channel[0].desc = "FD"; - channel[1].desc = "HD"; - channel[2].desc = "User"; - channel[3].desc = "ADPCM"; + channels[0].reset(new DMACChan(0, "FD")); + channels[1].reset(new DMACChan(1, "HD")); + channels[2].reset(new DMACChan(2, "User")); + channels[3].reset(new DMACChan(3, "ADPCM")); // PCL 信号線 // #0 はスーパーインポーズしてなければ常に Low - channel[0].pcl_pin = false; - channel[0].pcl_prev = false; + channels[0]->pcl_pin = false; + channels[0]->pcl_prev = false; // #1 はプルアップされているので常に High - channel[1].pcl_pin = true; - channel[1].pcl_prev = true; + channels[1]->pcl_pin = true; + channels[1]->pcl_prev = true; // #2 は外部スロット用だが未接続なので High - channel[2].pcl_pin = true; - channel[2].pcl_prev = true; + channels[2]->pcl_pin = true; + channels[2]->pcl_prev = true; // #3 は未対応 - event.Regist("DMAC"); - - monitor.func = ToMonitorCallback(&DMACDevice::MonitorUpdate); - monitor.SetSize(80, 34); - monitor.Regist(ID_MONITOR_DMAC); + monitor = gMonitorManager->Regist(ID_MONITOR_DMAC, this); + monitor->func = ToMonitorCallback(&DMACDevice::MonitorUpdate); + monitor->SetSize(80, 34); } // デストラクタ @@ -64,14 +72,14 @@ DMACDevice::~DMACDevice() bool DMACDevice::Init() { - if (inherited::Init() == false) { - return false; - } - adpcm = GetADPCMDevice(); fdc = GetFDCDevice(); interrupt = GetInterruptDevice(); mainbus = GetMainbusDevice(); + syncer = GetSyncer(); + + auto evman = GetEventManager(); + event = evman->Regist(this, NULL, "DMAC"); return true; } @@ -86,9 +94,19 @@ DMACDevice::ResetHard(bool poweron) // NIV and EIV are all set to $0F (uninitialized interrupt vector). // MTC, MAR, DAR, BTC, BAR, MFC, DFC and BFC are not affected. + // 電源オン時、少なくとも BFC は $07 のようだ + // (X68030 IPLROM が #0, #1 の BFC を初期化していないので読める)。 + // MFC、DFC くらいは同様かも知れない。 + if (poweron) { + for (auto& chan : channels) { + chan->SetMFC(0x07); + chan->SetDFC(0x07); + chan->SetBFC(0x07); + } + } + gcr = 0; - for (int ch = 0; ch < channel.size(); ch++) { - DMACChan *chan = &channel[ch]; + for (auto& chan : channels) { chan->SetDCR(0); chan->SetOCR(0); chan->SetSCR(0); @@ -103,286 +121,75 @@ DMACDevice::ResetHard(bool poweron) chan->priority = 0; } + syncer->NotifyDMACActive(false); + ChangeInterrupt(); // イベントを停止 - event.SetName("DMAC"); + event->SetName("DMAC"); scheduler->StopEvent(event); } -uint64 -DMACDevice::Read8(uint32 addr) +busdata +DMACDevice::Read(busaddr addr) { - uint8 data; - - mpu->AddCycle(37); // InsideOut p.135 - - uint32 offset = addr & 0xff; - int ch = offset / 0x40; - int n = offset % 0x40; - DMACChan *chan = &channel[ch]; - - switch (n) { - case DMAC::CSR: - data = chan->GetCSR(); - putlog(3, "#%d CSR -> $%02x", ch, data); - break; - - case DMAC::CER: - data = chan->cer; - putlog(3, "#%d CER -> $%02x", ch, data); - break; - - case DMAC::DCR: - data = chan->GetDCR(); - putlog(3, "#%d DCR -> $%02x", ch, data); - break; - - case DMAC::OCR: - data = chan->GetOCR(); - putlog(3, "#%d OCR -> $%02x", ch, data); - break; - - case DMAC::SCR: - data = chan->GetSCR(); - putlog(3, "#%d SCR -> $%02x", ch, data); - break; - - case DMAC::CCR: - data = chan->GetCCR(); - putlog(3, "#%d CCR -> $%02x", ch, data); - break; - - case DMAC::MTC: - data = chan->mtc >> 8; - putlog(3, "#%d MTC:H -> $%02x", ch, data); - break; - case DMAC::MTC + 1: - data = chan->mtc & 0xff; - putlog(3, "#%d MTC:L -> $%02x", ch, data); - break; - - case DMAC::MAR: - data = chan->mar >> 24; - putlog(3, "#%d MAR:0 -> $%02x", ch, data); - break; - case DMAC::MAR + 1: - data = (chan->mar >> 16) & 0xff; - putlog(3, "#%d MAR:1 -> $%02x", ch, data); - break; - case DMAC::MAR + 2: - data = (chan->mar >> 8) & 0xff; - putlog(3, "#%d MAR:2 -> $%02x", ch, data); - break; - case DMAC::MAR + 3: - data = chan->mar & 0xff; - putlog(3, "#%d MAR:3 -> $%02x", ch, data); - break; - - case DMAC::DAR: - data = chan->dar >> 24; - putlog(3, "#%d DAR:0 -> $%02x", ch, data); - break; - case DMAC::DAR + 1: - data = (chan->dar >> 16) & 0xff; - putlog(3, "#%d DAR:1 -> $%02x", ch, data); - break; - case DMAC::DAR + 2: - data = (chan->dar >> 8) & 0xff; - putlog(3, "#%d DAR:2 -> $%02x", ch, data); - break; - case DMAC::DAR + 3: - data = chan->dar & 0xff; - putlog(3, "#%d DAR:3 -> $%02x", ch, data); - break; - - case DMAC::BTC: - data = chan->btc >> 8; - putlog(3, "#%d BTC:H -> $%02x", ch, data); - break; - case DMAC::BTC + 1: - data = chan->btc & 0xff; - putlog(3, "#%d BTC:L -> $%02x", ch, data); - break; - - case DMAC::BAR: - data = chan->bar >> 24; - putlog(3, "#%d BAR:0 -> $%02x", ch, data); - break; - case DMAC::BAR + 1: - data = (chan->bar >> 16) & 0xff; - putlog(3, "#%d BAR:1 -> $%02x", ch, data); - break; - case DMAC::BAR + 2: - data = (chan->bar >> 8) & 0xff; - putlog(3, "#%d BAR:2 -> $%02x", ch, data); - break; - case DMAC::BAR + 3: - data = chan->bar & 0xff; - putlog(3, "#%d BAR:3 -> $%02x", ch, data); - break; - - case DMAC::NIV: - data = chan->niv; - putlog(3, "#%d NIV -> $%02x", ch, data); - break; - - case DMAC::EIV: - data = chan->eiv; - putlog(3, "#%d EIV -> $%02x", ch, data); - break; - - case DMAC::MFC: - data = chan->mfc; - putlog(3, "#%d MFC -> $%02x", ch, data); - break; + uint32 paddr = addr.Addr(); + uint32 reqsize = addr.GetSize(); + uint32 datasize = std::min(2 - (paddr & 1U), reqsize); + busdata data; - case DMAC::CPR: - data = chan->cpr; - putlog(3, "#%d CPR -> $%02x", ch, data); - break; - - case DMAC::DFC: - data = chan->dfc; - putlog(3, "#%d DFC -> $%02x", ch, data); - break; - - case DMAC::BFC: - data = chan->bfc; - putlog(3, "#%d BFC -> $%02x", ch, data); - break; + if (datasize == 1) { + data = Peek1(paddr); + } else { + data = Peek1(paddr) << 8; + data |= Peek1(paddr + 1); + } - case DMAC::GCR: - if (ch == 3) { - data = gcr; - putlog(3, "GCR -> $%02x", data); - break; + if (__predict_false(loglevel >= 3)) { + uint32 offset = paddr & 0xff; + uint ch = offset / 0x40; + uint n = offset % 0x40; + DMACChan *chan = channels[ch].get(); + if (datasize == 1) { + if (regname1[n]) { + chan->putlogn("%s -> $%02x", regname1[n], data.Data()); + } else if (offset == 0xff) { + putlogn("GCR -> $%02x", data.Data()); + } } else { - data = 0xff; + if (regname2[n / 2]) { + chan->putlogn("%s -> $%04x", regname2[n / 2], data.Data()); + } else if (offset == 0xfe) { + putlogn("GCR(W) -> $%04x", data.Data()); + } } - break; - - default: - data = 0xff; - break; } + data |= read_wait; + data |= busdata::Size(datasize); return data; } -uint64 -DMACDevice::Read16(uint32 addr) +busdata +DMACDevice::Write(busaddr addr, uint32 data) { - uint16 data; - - mpu->AddCycle(37); // InsideOut p.135 - - uint32 offset = addr & 0xff; - int ch = offset / 0x40; - int n = offset % 0x40; - DMACChan *chan = &channel[ch]; - - switch (n) { - case DMAC::CSR: // CSR | CER - data = chan->GetCSR() << 8; - data |= chan->cer; - putlog(3, "#%d CSR:CER -> $%04x", ch, data); - break; - - case DMAC::DCR: // DCR | OCR - data = chan->GetDCR() << 8; - data |= chan->GetOCR(); - putlog(3, "#%d DCR:OCR -> $%04x", ch, data); - break; - - case DMAC::SCR: // SCR | CCR - data = chan->GetSCR() << 8; - data |= chan->GetCCR(); - putlog(3, "#%d SCR:CCR -> $%04x", ch, data); - break; - - case DMAC::MTC: - data = chan->mtc; - putlog(3, "#%d MTC -> $%04x", ch, data); - break; - - case DMAC::MAR: - data = chan->mar >> 16; - putlog(3, "#%d MAR:H -> $%04x", ch, data); - break; - case DMAC::MAR + 2: - data = chan->mar & 0xffff; - putlog(3, "#%d MAR:L -> $%04x", ch, data); - break; - - case DMAC::DAR: - data = chan->dar >> 16; - putlog(3, "#%d DAR:H -> $%04x", ch, data); - break; - case DMAC::DAR + 2: - data = chan->dar & 0xffff; - putlog(3, "#%d DAR:L -> $%04x", ch, data); - break; - - case DMAC::BTC: - data = chan->btc; - putlog(3, "#%d BTC -> $%04x", ch, data); - break; - - case DMAC::BAR: - data = chan->bar >> 16; - putlog(3, "#%d BAR:H -> $%04x", ch, data); - break; - case DMAC::BAR + 2: - data = chan->bar & 0xffff; - putlog(3, "#%d BAR:L -> $%04x", ch, data); - break; - - case DMAC::NIV: - data = chan->niv | 0xff00; - putlog(3, "#%d NIV -> $%04x", ch, data); - break; - - case DMAC::EIV: - data = chan->eiv | 0xff00; - putlog(3, "#%d EIV -> $%04x", ch, data); - break; - - case DMAC::MFC: - data = chan->mfc | 0xff00; - putlog(3, "#%d MFC -> $%04x", ch, data); - break; - - case DMAC::CPR: - data = chan->cpr | 0xff00; - putlog(3, "#%d CPR -> $%04x", ch, data); - break; - - case DMAC::DFC: - data = chan->dfc | 0xff00; - putlog(3, "#%d DFC -> $%04x", ch, data); - break; - - case DMAC::BFC: - data = chan->bfc | 0xff00; - putlog(3, "#%d BFC -> $%04x", ch, data); - break; - - case DMAC::GCR: - if (ch == 3) { - data = gcr | 0xff00; - putlog(3, "GCR -> $%04x", data); - break; - } else { - data = 0xffff; - } - break; - - default: - data = 0xffff; - break; + uint32 paddr = addr.Addr(); + uint32 reqsize = addr.GetSize(); + uint32 datasize = std::min(2 - (paddr & 1U), reqsize); + data >>= (reqsize - datasize) * 8; + + uint32 offset = paddr & 0xff; + uint32 ch = offset / 0x40; + uint32 n = offset % 0x40; + DMACChan *chan = channels[ch].get(); + if (datasize == 1) { + WriteByte(chan, n, data); + } else { + WriteWord(chan, n, data); } - return data; + busdata r = write_wait; + r |= busdata::Size(datasize); + return r; } // STR か ACT が立っている時に書き込むとタイミングエラー @@ -396,21 +203,17 @@ DMACDevice::Read16(uint32 addr) // ログ表示版 #define TIMING_ERR_IF_RUNNING_log(regname) do { \ if (chan->str || chan->active) { \ - putlog(2, "#%d %s <- $%02x", ch, regname, data); \ + chan_putlog(chan, 2, "%s <- $%02x", regname, data); \ Error(chan, DMAC::CER_TIMING); \ break; \ } \ } while (0) -uint64 -DMACDevice::Write8(uint32 addr, uint32 data) +// バイト書き込み。 +busdata +DMACDevice::WriteByte(DMACChan *chan, uint32 n, uint32 data) { - mpu->AddCycle(31); // InsideOut p.135 - - uint32 offset = addr & 0xff; - int ch = offset / 0x40; - int n = offset % 0x40; - DMACChan *chan = &channel[ch]; + uint ch = chan->ch; switch (n) { case DMAC::CSR: @@ -422,19 +225,19 @@ DMACDevice::Write8(uint32 addr, uint32 d break; case DMAC::DCR: - putlog(2, "#%d DCR <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetDCR(data); break; case DMAC::OCR: - putlog(2, "#%d OCR <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetOCR(data); break; case DMAC::SCR: - putlog(2, "#%d SCR <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetSCR(data); break; @@ -444,7 +247,7 @@ DMACDevice::Write8(uint32 addr, uint32 d // %0 -> %1 に変化したビットだけを評価する。 uint8 up = (chan->GetCCR() ^ data) & data; // バイトサイズのログを (up 込みで) 表示。 - putlog(2, "#%d CCR <- $%02x (up=$%02x)", chan->ch, data, up); + chan_putlog(chan, 2, "CCR <- $%02x (up=$%02x)", data, up); // CCR を更新してから WriteCCR() で動作をする。 chan->SetCCR(data); WriteCCR(chan, data, up); @@ -454,111 +257,111 @@ DMACDevice::Write8(uint32 addr, uint32 d case DMAC::MTC: TIMING_ERR_IF_RUNNING_log("MTC:H"); chan->mtc = (chan->mtc & 0x00ff) | (data << 8); - putlog(2, "#%d MTC:H <- $%02x (MTC=$%04x)", ch, data, chan->mtc); + chan_putlog(chan, 2, "MTC:H <- $%02x (MTC=$%04x)", data, chan->mtc); break; case DMAC::MTC + 1: TIMING_ERR_IF_RUNNING_log("MTC:L"); chan->mtc = (chan->mtc & 0xff00) | data; - putlog(2, "#%d MTC:L <- $%02x (MTC=$%04x)", ch, data, chan->mtc); + chan_putlog(chan, 2, "MTC:L <- $%02x (MTC=$%04x)", data, chan->mtc); break; case DMAC::MAR: TIMING_ERR_IF_RUNNING_log("MAR:0"); chan->mar = (chan->mar & 0x00ffffff) | (data << 24); - putlog(2, "#%d MAR:0 <- $%02x (MAR=$%08x)", ch, data, chan->mar); + chan_putlog(chan, 2, "MAR:0 <- $%02x (MAR=$%08x)", data, chan->mar); break; case DMAC::MAR + 1: TIMING_ERR_IF_RUNNING_log("MAR:1"); chan->mar = (chan->mar & 0xff00ffff) | (data << 16); - putlog(2, "#%d MAR:1 <- $%02x (MAR=$%08x)", ch, data, chan->mar); + chan_putlog(chan, 2, "MAR:1 <- $%02x (MAR=$%08x)", data, chan->mar); break; case DMAC::MAR + 2: TIMING_ERR_IF_RUNNING_log("MAR:2"); chan->mar = (chan->mar & 0xffff00ff) | (data << 8); - putlog(2, "#%d MAR:2 <- $%02x (MAR=$%08x)", ch, data, chan->mar); + chan_putlog(chan, 2, "MAR:2 <- $%02x (MAR=$%08x)", data, chan->mar); break; case DMAC::MAR + 3: TIMING_ERR_IF_RUNNING_log("MAR:3"); chan->mar = (chan->mar & 0xffffff00) | data; - putlog(2, "#%d MAR:3 <- $%02x (MAR=$%08x)", ch, data, chan->mar); + chan_putlog(chan, 2, "MAR:3 <- $%02x (MAR=$%08x)", data, chan->mar); break; case DMAC::DAR: TIMING_ERR_IF_RUNNING_log("DAR:0"); chan->dar = (chan->dar & 0x00ffffff) | (data << 24); - putlog(2, "#%d DAR:0 <- $%02x (DAR=$%08x)", ch, data, chan->dar); + chan_putlog(chan, 2, "DAR:0 <- $%02x (DAR=$%08x)", data, chan->dar); break; case DMAC::DAR + 1: TIMING_ERR_IF_RUNNING_log("DAR:1"); chan->dar = (chan->dar & 0xff00ffff) | (data << 16); - putlog(2, "#%d DAR:1 <- $%02x (DAR=$%08x)", ch, data, chan->dar); + chan_putlog(chan, 2, "DAR:1 <- $%02x (DAR=$%08x)", data, chan->dar); break; case DMAC::DAR + 2: TIMING_ERR_IF_RUNNING_log("DAR:2"); chan->dar = (chan->dar & 0xffff00ff) | (data << 8); - putlog(2, "#%d DAR:2 <- $%02x (DAR=$%08x)", ch, data, chan->dar); + chan_putlog(chan, 2, "DAR:2 <- $%02x (DAR=$%08x)", data, chan->dar); break; case DMAC::DAR + 3: TIMING_ERR_IF_RUNNING_log("DAR:3"); chan->dar = (chan->dar & 0xffffff00) | data; - putlog(2, "#%d DAR:3 <- $%02x (DAR=$%08x)", ch, data, chan->dar); + chan_putlog(chan, 2, "DAR:3 <- $%02x (DAR=$%08x)", data, chan->dar); break; case DMAC::BTC: chan->btc = (chan->btc & 0x00ff) | (data << 8); - putlog(2, "#%d BTC:H <- $%02x (BTC=$%04x)", ch, data, chan->btc); + chan_putlog(chan, 2, "BTC:H <- $%02x (BTC=$%04x)", data, chan->btc); break; case DMAC::BTC + 1: chan->btc = (chan->btc & 0xff00) | data; - putlog(2, "#%d BTC:L <- $%02x (BTC=$%04x)", ch, data, chan->btc); + chan_putlog(chan, 2, "BTC:L <- $%02x (BTC=$%04x)", data, chan->btc); break; case DMAC::BAR: chan->bar = (chan->bar & 0x00ffffff) | (data << 24); - putlog(2, "#%d BAR:0 <- $%02x (BAR=$%08x)", ch, data, chan->bar); + chan_putlog(chan, 2, "BAR:0 <- $%02x (BAR=$%08x)", data, chan->bar); break; case DMAC::BAR + 1: chan->bar = (chan->bar & 0xff00ffff) | (data << 16); - putlog(2, "#%d BAR:1 <- $%02x (BAR=$%08x)", ch, data, chan->bar); + chan_putlog(chan, 2, "BAR:1 <- $%02x (BAR=$%08x)", data, chan->bar); break; case DMAC::BAR + 2: chan->bar = (chan->bar & 0xffff00ff) | (data << 8); - putlog(2, "#%d BAR:2 <- $%02x (BAR=$%08x)", ch, data, chan->bar); + chan_putlog(chan, 2, "BAR:2 <- $%02x (BAR=$%08x)", data, chan->bar); break; case DMAC::BAR + 3: chan->bar = (chan->bar & 0xffffff00) | data; - putlog(2, "#%d BAR:3 <- $%02x (BAR=$%08x)", ch, data, chan->bar); + chan_putlog(chan, 2, "BAR:3 <- $%02x (BAR=$%08x)", data, chan->bar); break; case DMAC::NIV: - putlog(2, "#%d NIV <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); chan->SetNIV(data); break; case DMAC::EIV: - putlog(2, "#%d EIV <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); chan->SetEIV(data); break; case DMAC::MFC: - putlog(2, "#%d MFC <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetMFC(data); break; case DMAC::CPR: - putlog(2, "#%d CPR <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); chan->SetCPR(data); break; case DMAC::DFC: - putlog(2, "#%d DFC <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetDFC(data); break; case DMAC::BFC: - putlog(2, "#%d BFC <- $%02x", ch, data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); chan->SetBFC(data); break; @@ -571,18 +374,15 @@ DMACDevice::Write8(uint32 addr, uint32 d default: break; } - return 0; + + return write_wait; } -uint64 -DMACDevice::Write16(uint32 addr, uint32 data) +// ワード書き込み。 +busdata +DMACDevice::WriteWord(DMACChan *chan, uint32 n, uint32 data) { - mpu->AddCycle(31); // InsideOut p.135 - - uint32 offset = addr & 0xff; - int ch = offset / 0x40; - int n = offset % 0x40; - DMACChan *chan = &channel[ch]; + uint ch = chan->ch; switch (n) { case DMAC::CSR: // CSR | CER @@ -592,7 +392,7 @@ DMACDevice::Write16(uint32 addr, uint32 break; case DMAC::DCR: // DCR | OCR - putlog(2, "#%d DCR:OCR <- $%04x", ch, data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->SetDCR(data >> 8); chan->SetOCR(data & 0xff); @@ -603,7 +403,7 @@ DMACDevice::Write16(uint32 addr, uint32 // CCR の %0 -> %1 に変化したビットだけを取り出す。 uint8 up = (chan->GetCCR() ^ data) & data; // ワードサイズのログを (CCR の up 込みで) 表示。 - putlog(2, "#%d SCR:CCR <- $%04x (up=$%02x)", ch, data, up); + chan_putlog(chan, 2, "SCR:CCR <- $%04x (up=$%02x)", data, up); // 両方のレジスタの値を更新。 chan->SetSCR(data >> 8); chan->SetCCR(data); @@ -616,7 +416,7 @@ DMACDevice::Write16(uint32 addr, uint32 } case DMAC::MTC: - putlog(2, "#%d MTC <- $%04x", ch, data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->mtc = data; break; @@ -624,27 +424,27 @@ DMACDevice::Write16(uint32 addr, uint32 case DMAC::MAR: TIMING_ERR_IF_RUNNING_log("MAR:H"); chan->mar = (chan->mar & 0x0000ffff) | (data << 16); - putlog(2, "#%d MAR:H <- $%04x (MAR=$%08x)", ch, data, chan->mar); + chan_putlog(chan, 2, "MAR:H <- $%04x (MAR=$%08x)", data, chan->mar); break; case DMAC::MAR + 2: TIMING_ERR_IF_RUNNING_log("MAR:L"); chan->mar = (chan->mar & 0xffff0000) | data; - putlog(2, "#%d MAR:L <- $%04x (MAR=$%08x)", ch, data, chan->mar); + chan_putlog(chan, 2, "MAR:L <- $%04x (MAR=$%08x)", data, chan->mar); break; case DMAC::DAR: TIMING_ERR_IF_RUNNING_log("DAR:H"); chan->dar = (chan->dar & 0x0000ffff) | (data << 16); - putlog(2, "#%d DAR:H <- $%04x (DAR=$%08x)", ch, data, chan->dar); + chan_putlog(chan, 2, "DAR:H <- $%04x (DAR=$%08x)", data, chan->dar); break; case DMAC::DAR + 2: TIMING_ERR_IF_RUNNING_log("DAR:L"); chan->dar = (chan->dar & 0xffff0000) | data; - putlog(2, "#%d DAR:L <- $%04x (DAR=$%08x)", ch, data, chan->dar); + chan_putlog(chan, 2, "DAR:L <- $%04x (DAR=$%08x)", data, chan->dar); break; case DMAC::BTC: - putlog(2, "#%d BTC <- $%04x", ch, data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->btc = data; break; @@ -652,47 +452,47 @@ DMACDevice::Write16(uint32 addr, uint32 case DMAC::BAR: TIMING_ERR_IF_RUNNING_log("BAR:H"); chan->bar = (chan->bar & 0x0000ffff) | (data << 16); - putlog(2, "#%d BAR:H <- $%04x (BAR=$%08x)", ch, data, chan->bar); + chan_putlog(chan, 2, "BAR:H <- $%04x (BAR=$%08x)", data, chan->bar); break; case DMAC::BAR + 2: TIMING_ERR_IF_RUNNING_log("BAR:L"); chan->bar = (chan->bar & 0xffff0000) | data; - putlog(2, "#%d BAR:L <- $%04x (BAR=$%08x)", ch, data, chan->bar); + chan_putlog(chan, 2, "BAR:L <- $%04x (BAR=$%08x)", data, chan->bar); break; - case DMAC::NIV: - putlog(2, "#%d NIV <- $%02x", ch, data); + case DMAC::NIV & ~1U: + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); chan->SetNIV(data); break; - case DMAC::EIV: - putlog(2, "#%d EIV <- $%02x", ch, data); + case DMAC::EIV & ~1U: + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); chan->SetEIV(data); break; - case DMAC::MFC: - putlog(2, "#%d MFC <- $%02x", ch, data); + case DMAC::MFC & ~1U: + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->SetMFC(data); break; - case DMAC::CPR: - putlog(2, "#%d CPR <- $%02x", ch, data); + case DMAC::CPR & ~1U: + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); chan->SetCPR(data); break; - case DMAC::DFC: - putlog(2, "#%d DFC <- $%02x", ch, data); + case DMAC::DFC & ~1U: + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->SetDFC(data); break; - case DMAC::BFC: - putlog(2, "#%d BFC <- $%02x", ch, data); + case DMAC::BFC & ~1U: + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); chan->SetBFC(data); break; - case DMAC::GCR - 1: + case DMAC::GCR & ~1U: if (ch == 3) { WriteGCR(data); } @@ -701,18 +501,19 @@ DMACDevice::Write16(uint32 addr, uint32 default: break; } - return 0; + + return write_wait; } -uint64 -DMACDevice::Peek8(uint32 addr) +busdata +DMACDevice::Peek1(uint32 addr) { uint8 data; uint32 offset = addr & 0xff; - int ch = offset / 0x40; - int n = offset % 0x40; - DMACChan *chan = &channel[ch]; + uint ch = offset / 0x40; + uint n = offset % 0x40; + DMACChan *chan = channels[ch].get(); switch (n) { case DMAC::CSR: @@ -801,7 +602,7 @@ DMACDevice::Peek8(uint32 addr) break; case DMAC::MFC: - data = chan->mfc; + data = chan->mfc.GetFC(); break; case DMAC::CPR: @@ -809,11 +610,11 @@ DMACDevice::Peek8(uint32 addr) break; case DMAC::DFC: - data = chan->dfc; + data = chan->dfc.GetFC(); break; case DMAC::BFC: - data = chan->bfc; + data = chan->bfc.GetFC(); break; case DMAC::GCR: @@ -872,15 +673,15 @@ DMACDevice::MonitorUpdate(Monitor *, Tex screen.Print(0, y++, "+$%02x DFC:", DMAC::DFC); screen.Print(0, y++, "+$%02x BFC:", DMAC::BFC); - for (int ch = 0; ch < 4; ch++) { - DMACChan *chan = &channel[ch]; - int val; + for (auto& chan : channels) { + uint val; + int ch = chan->ch; x = 13 + ch * 17; y = 0; // 地味だけど有効なチャンネルをハイライトしてみる screen.Print(x, y++, (chan->active ? TA::Em : TA::Normal), - "#%d (%s)", chan->ch, chan->desc); + "#%u (%s)", chan->ch, chan->desc); // アドレス screen.Print(x, y++, "$%06x", baseaddr + ch * 0x40); @@ -920,7 +721,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "CycleWithHold", }; val = chan->GetXRM(); - screen.Print(x, y++, "%d:%s", val, dcr_xrm[val]); + screen.Print(x, y++, "%u:%s", val, dcr_xrm[val]); // DCR:DTYP static const char * const dcr_dtyp[] = { @@ -931,7 +732,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "ACK+READY", }; val = chan->GetDTYP(); - screen.Print(x, y++, "%d:%s", val, dcr_dtyp[val]); + screen.Print(x, y++, "%u:%s", val, dcr_dtyp[val]); // DCR:DPS static const char * const dcr_dps[] = { @@ -940,7 +741,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "16bit", }; val = chan->GetDPS(); - screen.Print(x, y++, "%d:%s", val, dcr_dps[val]); + screen.Print(x, y++, "%u:%s", val, dcr_dps[val]); // DCR:PCL static const char * const dcr_pcl_name[] = { @@ -951,7 +752,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "Abort", }; val = chan->dcr_pcl; - screen.Print(x, y++, "%d:%s", val, dcr_pcl_name[val]); + screen.Print(x, y++, "%u:%s", val, dcr_pcl_name[val]); // OCR screen.Print(x, y++, "$%02x", chan->GetOCR()); @@ -963,7 +764,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "DeviceToMemory", }; val = chan->GetDIR(); - screen.Print(x, y++, "%d:%s", val, ocr_dir[val]); + screen.Print(x, y++, "%u:%s", val, ocr_dir[val]); // OCR:SIZE static const char * const ocr_size[] = { @@ -974,7 +775,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "8bit Unpacked", }; val = chan->GetSIZE(); - screen.Print(x, y++, "%d:%s", val, ocr_size[val]); + screen.Print(x, y++, "%u:%s", val, ocr_size[val]); // OCR:CHAIN static const char * const ocr_chain[] = { @@ -985,7 +786,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "LinkArrayChain", }; val = chan->GetCHAIN(); - screen.Print(x, y++, "%d:%s", val, ocr_chain[val]); + screen.Print(x, y++, "%u:%s", val, ocr_chain[val]); // OCR:REQG static const char * const ocr_reqg[] = { @@ -996,7 +797,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "AutoThenExt", }; val = chan->GetREQG(); - screen.Print(x, y++, "%d:%s", val, ocr_reqg[val]); + screen.Print(x, y++, "%u:%s", val, ocr_reqg[val]); // SCR screen.Print(x, y++, "$%02x", chan->GetSCR()); @@ -1010,9 +811,9 @@ DMACDevice::MonitorUpdate(Monitor *, Tex "undefined", }; val = chan->GetMAC(); - screen.Print(x, y++, "%d:%s", val, scr_xac[val]); + screen.Print(x, y++, "%u:%s", val, scr_xac[val]); val = chan->GetDAC(); - screen.Print(x, y++, "%d:%s", val, scr_xac[val]); + screen.Print(x, y++, "%u:%s", val, scr_xac[val]); // CCR val = chan->GetCCR(); @@ -1053,13 +854,13 @@ DMACDevice::MonitorUpdate(Monitor *, Tex screen.Print(x, y++, "$%02x", chan->eiv); // MFC - screen.Print(x, y++, "$%02x", chan->mfc); + screen.Print(x, y++, "$%02x", chan->mfc.GetFC()); // CPR screen.Print(x, y++, "$%02x", chan->cpr); // DFC - screen.Print(x, y++, "$%02x", chan->dfc); + screen.Print(x, y++, "$%02x", chan->dfc.GetFC()); // BFC - screen.Print(x, y++, "$%02x", chan->bfc); + screen.Print(x, y++, "$%02x", chan->bfc.GetFC()); if (ch == 3) { screen.Print(x - 10, y++, "+$3f GCR: $%02x", gcr); @@ -1072,11 +873,11 @@ void DMACDevice::MonitorReg4(TextScreen& screen, int x, int y, uint32 reg, const char * const *names) { - for (int i = 0; i < 4; i++) { + for (uint i = 0; i < 4; i++) { if (names[i][0] == '-') { screen.Puts(x + i * 4, y, TA::Disable, "---"); } else { - bool b = reg & (1 << (3 - i)); + bool b = reg & (1U << (3 - i)); screen.Puts(x + i * 4, y, TA::OnOff(b), names[i]); } } @@ -1125,7 +926,7 @@ DMACDevice::WriteCSR(DMACChan *chan, uin chan->pcl_prev = false; } - putlog(2, "#%d CSR <- $%02x (CSR = $%02x)", chan->ch, data, chan->GetCSR()); + chan_putlog(chan, 2, "CSR <- $%02x (CSR = $%02x)", data, chan->GetCSR()); ChangeInterrupt(); } @@ -1189,7 +990,7 @@ DMACDevice::WriteCCR(DMACChan *chan, uin if (chan->GetXRM() == 1 || chan->GetMAC() == 3 || chan->GetDAC() == 3 || - chan->GetCHAIN() == 1) + chan->GetCHAIN() == DMAC::CHAIN_reserved) { Error(chan, DMAC::CER_CONFIG); return; @@ -1246,9 +1047,25 @@ DMACDevice::WriteCCR(DMACChan *chan, uin ChangeInterrupt(); } +// CSR.ACT ビットの状態を変更。 +void +DMACDevice::ChangeACT(DMACChan *chan, bool active) +{ + chan->active = active; + + // チャンネルの ACT を変更した結果、 + // トータルでアクティブかどうかを Syncer に通知する。 + bool dmac_active = + channels[0]->active || + channels[1]->active || + channels[2]->active || + channels[3]->active; + syncer->NotifyDMACActive(dmac_active); +} + // 転送開始 void -DMACDevice::StartTransfer(DMACChan* chan) +DMACDevice::StartTransfer(DMACChan *chan) { // Operation Timing Error (ii) // CSR の ACT,COC,BTC,NDT,ERR が立ってたら開始しない @@ -1258,7 +1075,7 @@ DMACDevice::StartTransfer(DMACChan* chan } // ACT を立てたら STR を下げる。 - chan->active = true; + ChangeACT(chan, true); chan->str = false; // XXX あとで移動する @@ -1267,7 +1084,7 @@ DMACDevice::StartTransfer(DMACChan* chan chan->mac = 1; break; case DMAC::SCR_COUNT_DOWN: - putlog(0, "SCR:MAC CountDown Mode (NOT SUPPORTED)"); + chan_putlog(chan, 0, "SCR:MAC CountDown Mode (NOT SUPPORTED)"); chan->mac = -1; break; default: @@ -1279,7 +1096,7 @@ DMACDevice::StartTransfer(DMACChan* chan chan->dac = 1; break; case DMAC::SCR_COUNT_DOWN: - putlog(0, "SCR:DAC CountDown Mode (NOT SUPPORTED)"); + chan_putlog(chan, 0, "SCR:DAC CountDown Mode (NOT SUPPORTED)"); chan->dac = -1; break; default: @@ -1310,61 +1127,152 @@ DMACDevice::StartTransfer(DMACChan* chan break; default: - VMPANIC("corrupted reqg=%d", reqg); + VMPANIC("corrupted reqg=%u", reqg); } - // 転送開始ログ。全パラメータは無理なので概要だけ表示。 - if (loglevel >= 1) { - std::string msg; - if (chan->GetDIR() == DMAC::DIR_MtoD) { - msg = string_format("$%06x to $%06x mtc=$%04x", - chan->mar, chan->dar, chan->mtc); - } else { - msg = string_format("$%06x to $%06x mtc=$%04x", - chan->dar, chan->mar, chan->mtc); + // チェインモードなら初回読み込み。 + // 通常モードなら何もしなくていいので、ログを出すだけ。 + switch (chan->GetCHAIN()) { + case DMAC::CHAIN_reserved: + // とりあえず DISABLED と同じ動作にしておくか。 + chan_putlog(chan, 1, "Undefined CHAIN=1 (ignored)"); + // FALLTHROUGH + case DMAC::CHAIN_DISABLED: + chan_putlog(chan, 1, "%s", MakeStartLog(chan).c_str()); + break; + + case DMAC::CHAIN_ARRAY: + chan_putlog(chan, 0, "Array Chaining Mode (NOT SUPPORTED)"); + return; + + case DMAC::CHAIN_LINKARRAY: + // ブロック開始時に BAR から1エントリ読んで、そのリンクアドレスが + // $00000000 ならこのブロックが最後と判断する、と書いてあるように + // 読めるので、開始時 BAR が $0000000 なら 0 番地から読むだろうか。 + if (LoadLinkArray(chan) == false) { + return; } - putlogn("#%d Start %s", chan->ch, msg.c_str()); + break; + + default: + __unreachable(); + } + + if (event->IsRunning() == false) { + CallAfter(StartCallback, 0, 0); + } +} + +// 転送開始ログを返す。 +// 全パラメータは無理なので概要だけ表示。 +std::string +DMACDevice::MakeStartLog(DMACChan *chan) +{ + static const char * const countstr[] = { + "-", + "", + "+", + }; + + std::string scrmsg; + if (chan->GetDIR() == DMAC::DIR_MtoD) { + scrmsg = string_format("$%06x%s to $%06x%s", + chan->mar, countstr[chan->mac + 1], + chan->dar, countstr[chan->dac + 1]); + } else { + scrmsg = string_format("$%06x%s to $%06x%s", + chan->dar, countstr[chan->dac + 1], + chan->mar, countstr[chan->mac + 1]); + } + + return string_format("Start %s mtc=$%04x", scrmsg.c_str(), chan->mtc); +} + +// BAR の指すリンクアレイチェインのブロックエントリを読み込む。 +// 読めたら true を返す。エラーなら Error をセットして false を返す。 +bool +DMACDevice::LoadLinkArray(DMACChan *chan) +{ + uint32 orig_bar = chan->bar; + busdata data; + + // リンクアレイチェインの BAR は偶数から始まり、 + // +$0.L アドレス(MAR) + // +$4.W カウント(MTC) + // +$6.L 次のリンクアドレス(BAR) + + busaddr bar_addr = busaddr(chan->bar) | chan->bfc; + if ((bar_addr.Addr() & 1U) != 0) { + goto error; + } + + data = ReadMem4(bar_addr); + if (__predict_false(data.IsBusErr())) { + goto error; } + chan->mar = data.Data(); - if (event.IsRunning() == false) { - CallAfter(StartCallback, 0); + bar_addr += 4; + bar_addr.ChangeSize(2); + data = ReadMem(bar_addr); + if (__predict_false(data.IsBusErr())) { + goto error; } + chan->mtc = data.Data(); + + bar_addr += 2; + data = ReadMem4(bar_addr); + if (__predict_false(data.IsBusErr())) { + goto error; + } + chan->bar = data.Data(); + + if (__predict_false(chan->loglevel >= 1)) { + chan_putlog(chan, 2, "%s bar=%06x next=%06x", + __func__, orig_bar, chan->bar); + std::string msg = MakeStartLog(chan); + chan->putlogn("%s", msg.c_str()); + } + + return true; + + error: + Error(chan, DMAC::CER_ADDR_BAR); + return false; } // 今回転送するチャンネルを決定して返す。なければ NULL を返す。 DMACChan * DMACDevice::SelectChannel() { - DMACChan *chan; int ch = -1; uint8 prio = 255; - for (int i = 0; i < channel.size(); i++) { - chan = &channel[i]; - if (chan->active && (chan->req || chan->seq_index < chan->seq.size())) { + for (auto& chan : channels) { + if (chan->active) { uint8 p = chan->priority; if (p < prio) { prio = p; // 実効プライオリティのラウンドロビン用のところを上げておく chan->priority = (p & 0xf0) | ((p & 0x0f) >> 1); - ch = i; + ch = chan->ch; } } } if (ch == -1) { // 転送チャンネルはもうないのでイベントは停止したままにする - event.SetName("DMAC"); + event->SetName("DMAC"); return NULL; } else { - chan = &channel[ch]; - event.SetName(string_format("DMAC #%d", chan->ch)); + DMACChan *chan = channels[ch].get(); + event->SetName(string_format("DMAC #%u", chan->ch)); return chan; } } -// 転送開始 +// 転送開始 (MTC 1回分の開始) void -DMACDevice::StartCallback(Event& ev) +DMACDevice::StartCallback(Event *ev) { // チャンネルを決定 auto chan = SelectChannel(); @@ -1448,12 +1356,21 @@ DMACDevice::StartCallback(Event& ev) } } - CallAfter(TransferCallback, 1); + if (__predict_false(chan->loglevel >= 5)) { + std::string ss; + for (const auto op : chan->seq) { + ss += ' '; + ss += seqname[op]; + } + chan->putlogn("%s seq:%s", __func__, ss.c_str()); + } + + CallAfter(TransferCallback, 1, 0); } -// 転送処理 +// 転送処理 (内部シーケンス) void -DMACDevice::TransferCallback(Event& ev) +DMACDevice::TransferCallback(Event *ev) { // チャンネルを決定 auto chan = SelectChannel(); @@ -1461,8 +1378,12 @@ DMACDevice::TransferCallback(Event& ev) return; } - int op = chan->seq[chan->seq_index]; + uint op = chan->seq[chan->seq_index]; + chan_putlog(chan, 5, "%s [%u] %s", + __func__, chan->seq_index, seqname[op]); + uint64 data; + busdata r; // デバイスアクセスなら ACK#n 信号と DONE 信号のドライブ switch (op) { @@ -1470,6 +1391,25 @@ DMACDevice::TransferCallback(Event& ev) case RD_D16: case WR_D8: case WR_D16: + if (chan->req == false) { + // REQ が立つまで待つ。構造上ポーリングするしか…。 + switch (chan->ch) { + case 0: + case 1: + case 2: + chan_putlog(chan, 0, "%s Wait REQ", __func__); + ev->time = 1_usec; + break; + case 3: + // ADPCM は最短でも 64_usec とかなので長めでよい。 + ev->time = 1_usec; + break; + default: + __unreachable(); + } + scheduler->StartEvent(ev); + return; + } AssertACK(chan); break; default: @@ -1498,43 +1438,98 @@ DMACDevice::TransferCallback(Event& ev) } } - // 1回分のアクセス - data = 0; + // アドレスを生成。 + busaddr addr; switch (op) { case RD_M8: - data = mainbus->Read8(chan->mar); - break; case RD_M16: - data = mainbus->Read16(chan->mar); + case WR_M8: + case WR_M16: + addr = busaddr(chan->mar) | chan->mfc; break; case RD_D8: - data = mainbus->Read8(chan->dar); - break; case RD_D16: - data = mainbus->Read16(chan->dar); + case WR_D8: + case WR_D16: + addr = busaddr(chan->dar) | chan->dfc; break; + default: + break; + } + + // サイズを指定。 + switch (op) { + case RD_M8: + case RD_D8: case WR_M8: - data = chan->data.Dequeue(); - data = mainbus->Write8(chan->mar, data); + case WR_D8: + addr.ChangeSize(1); break; + case RD_M16: + case RD_D16: case WR_M16: - data = chan->data.Dequeue() << 8; - data |= chan->data.Dequeue(); - data = mainbus->Write16(chan->mar, data); + case WR_D16: + addr.ChangeSize(2); break; + default: + break; + } + + + // 書き込みならキューからデータを取得。 + switch (op) { + case WR_M8: case WR_D8: data = chan->data.Dequeue(); - data = mainbus->Write8(chan->dar, data); break; + case WR_M16: case WR_D16: data = chan->data.Dequeue() << 8; data |= chan->data.Dequeue(); - data = mainbus->Write16(chan->dar, data); + break; + default: + data = 0; + break; + } + + // ユーザ空間へのアクセスが可能かどうかここで調べる。 + // 本来は DMAC ではなくメインバスで管理すべきことだが、パフォーマンスの + // 観点から MPU 側に FC2 カットによる可動部を増やしたくないので、 + // アクセス頻度の低い DMAC 側で肩代わりしている。 + bool accessible = + __predict_true(addr.IsSuper() || useraccess[addr.Addr() / 0x2000]); + + // 1回分のアクセス。 + switch (op) { + case RD_M8: + case RD_D8: + case RD_M16: + case RD_D16: + if (accessible) { + r = ReadMem(addr); + } else { + r.SetBusErr(); + } + break; + case WR_M8: + case WR_D8: + case WR_M16: + case WR_D16: + if (accessible) { + r = WriteMem(addr, data); + } else { + r.SetBusErr(); + } break; case NOP: + r = 0; break; default: - VMPANIC("corrupted op=%d", op); + VMPANIC("corrupted op=%u", op); + } + + if (__predict_false(loglevel >= 4)) { + TransferLog(chan, op, addr, r, data); } // デバイスアクセスなら ACK#n 信号と DONE 信号のドライブ @@ -1549,9 +1544,9 @@ DMACDevice::TransferCallback(Event& ev) break; } - // バスエラーか。ACK を下ろした後で行う。 - if ((int64)data < 0) { - TransferError(chan, op, data); + // バスエラー(もしくはリトライ)か。ACK を下ろした後で行う。 + if (r.IsOK() == false) { + TransferError(chan, op, r, data); return; } @@ -1559,12 +1554,12 @@ DMACDevice::TransferCallback(Event& ev) switch (op) { case RD_M8: case RD_D8: - chan->data.Enqueue(data); + chan->data.Enqueue(r.Data()); break; case RD_M16: case RD_D16: - chan->data.Enqueue(data >> 8); - chan->data.Enqueue(data & 0xff); + chan->data.Enqueue(r.Data() >> 8); + chan->data.Enqueue(r.Data() & 0xff); break; default: break; @@ -1591,45 +1586,91 @@ DMACDevice::TransferCallback(Event& ev) case NOP: break; default: - VMPANIC("corrupted op=%d", op); + VMPANIC("corrupted op=%u", op); + } + + // シーケンスを一つ進める。完了すれば転送1回分。 + chan->seq_index++; + if (chan->seq_index < chan->seq.size()) { + // XXX ディレイはあとから見直す + CallAfter(TransferCallback, 4, r.GetWait()); + return; } - // シーケンス完了で MTC の転送1回分 - if (++chan->seq_index >= chan->seq.size()) { - chan->mtc -= 1; - - // すべての転送完了か - if (__predict_false(chan->mtc == 0)) { - chan->csr |= DMAC::CSR_COC; - chan->active = false; - ChangeInterrupt(); + // ここで転送1回分が完了。 + chan->mtc -= 1; + + bool complete = false; + if (__predict_false(chan->mtc == 0)) { + // MTC 分転送すれば1ブロック完了。 + switch (chan->GetCHAIN()) { + case DMAC::CHAIN_DISABLED: + case DMAC::CHAIN_reserved: + case DMAC::CHAIN_ARRAY: // not yet + complete = true; + break; + + case DMAC::CHAIN_LINKARRAY: + // 次のブロックを BAR から読む。BAR が 0 ならここで終了。 + if (chan->bar == 0) { + complete = true; + } else { + if (LoadLinkArray(chan) == false) { + return; + } + } + break; + default: + __unreachable(); } + } + if (complete == false) { // XXX ディレイはあとから見直す - CallAfter(StartCallback, 4); + CallAfter(StartCallback, 4, r.GetWait()); } else { - // XXX ディレイはあとから見直す - CallAfter(TransferCallback, 4); + // すべての転送が完了。 + chan_putlog(chan, 1, "Complete Transfer"); + chan->csr |= DMAC::CSR_COC; + ChangeACT(chan, false); + ChangeInterrupt(); } } // 転送エラー void -DMACDevice::TransferError(DMACChan *chan, int op, uint64 r) +DMACDevice::TransferError(DMACChan *chan, uint op, busdata r, uint32 data) { - if ((int64)r == -2) { + if (r.IsRetry()) { // DTACK が出ていない (SPC)。 // 本当は DTACK が出たことをコールバックしてくれれば // 効率がいいのだがポーリングでもかまわんだろう。 chan->retry++; - putlog(4, "retry=%d", chan->retry); if (chan->retry < 100) { + // 書き込みがリトライになったら仕方ないので、今書こうとして + // 取り出したデータをキューに戻してから再実行。うーんこの。 + switch (op) { + case WR_M8: + case WR_D8: + chan->data.PushFront(data); + break; + case WR_M16: + case WR_D16: + chan->data.PushFront(data); + chan->data.PushFront(data >> 8); + break; + default: + break; + } + // 現在のイベントを再実行 - event.time = 1 * 80_nsec; + event->time = 1 * 80_nsec; scheduler->StartEvent(event); return; + } else { + // タイムアウトしたらバスエラーにフォールスルー + chan_putlog(chan, 3, "retry exceeds"); } - // タイムアウトしたらバスエラーにフォールスルー } // バスエラー @@ -1647,8 +1688,91 @@ DMACDevice::TransferError(DMACChan *chan Error(chan, DMAC::CER_BUS_DAR); break; default: - VMPANIC("corrupted op=%d", op); + VMPANIC("corrupted op=%u", op); + } +} + +// 1転送ごとのログを出力。 +// ログを出力することが決まってから呼ばれる。 +void +DMACDevice::TransferLog(DMACChan *chan, uint op, busaddr addr, + busdata r, uint64 data) +{ + // Nop + // Read $%06x -> $xx + // Read $%06x -> BusErr + // Read $%06x -> $xx (1 retried) + // Write $%06x <- $xx + // Write $%06x <- $xx BusErr + // Write $%06x <- $xx (1 retried) + + if (op == NOP) { + chan->putlogn("Nop"); + return; + } + + if (r.IsRetry()) { + return; + } + + const char *act; + const char *dir; + switch (op) { + case RD_M8: + case RD_D8: + case RD_M16: + case RD_D16: + act = "Read "; + dir = "->"; + break; + case WR_M8: + case WR_D8: + case WR_M16: + case WR_D16: + act = "Write"; + dir = "<-"; + break; + default: + act = ""; + dir = ""; + break; + } + + std::string str = string_format("%s $%06x %s ", act, addr.Addr(), dir); + + switch (op) { + case RD_M8: + case RD_D8: + if (r.IsOK()) { + str += string_format("$%02x", r.Data()); + } + break; + case RD_M16: + case RD_D16: + if (r.IsOK()) { + str += string_format("$%04x", r.Data()); + } + case WR_M8: + case WR_D8: + str += string_format("$%02x", (uint32)data); + break; + case WR_M16: + case WR_D16: + str += string_format("$%04x", (uint32)data); + break; + default: + break; + } + + if (r.IsBusErr()) { + str += " BusErr"; } + + if (chan->retry != 0) { + str += string_format(" (%u retried)", chan->retry); + } + + chan->putlogn("%s", str.c_str()); } // エラー発生 @@ -1658,11 +1782,11 @@ DMACDevice::Error(DMACChan *chan, uint8 // データシート p.43 chan->cer = errcode; chan->csr |= DMAC::CSR_COC | DMAC::CSR_ERR; - chan->active = false; + ChangeACT(chan, false); chan->str = false; chan->cnt = false; ChangeInterrupt(); - CallAfter(StartCallback, 0); + CallAfter(StartCallback, 0, 0); } // ACK#n 信号線をアサート @@ -1674,13 +1798,14 @@ DMACDevice::AssertACK(DMACChan *chan) fdc->AssertDACK(chan->mtc == 1); break; case 1: + case 2: // 接続されていない break; case 3: - adpcm->AssertDACK(chan->mtc == 1); + adpcm->AssertDACK(); break; default: - VMPANIC("未実装"); + __unreachable(); } } @@ -1693,23 +1818,24 @@ DMACDevice::NegateACK(DMACChan *chan) fdc->NegateDACK(); break; case 1: + case 2: // 接続されていない break; case 3: - adpcm->NegateDACK(); + // 不要。 break; default: - VMPANIC("未実装"); + __unreachable(); } } // REQ#n 信号線アサート (外部から呼ばれる) void -DMACDevice::AssertREQ(int ch) +DMACDevice::AssertREQ(uint ch) { - assert(0 <= ch && ch < 4); + assert(ch < 4); - DMACChan *chan = &channel[ch]; + DMACChan *chan = channels[ch].get(); chan->req = true; // #3(ADPCM) の REQ は PCL にも繋がっている @@ -1717,18 +1843,18 @@ DMACDevice::AssertREQ(int ch) chan->pcl_pin = true; } - if (event.IsRunning() == false) { + if (event->IsRunning() == false) { scheduler->StartEvent(event); } } // REQ#n 信号線ネゲート (外部から呼ばれる) void -DMACDevice::NegateREQ(int ch) +DMACDevice::NegateREQ(uint ch) { - assert(0 <= ch && ch < 4); + assert(ch < 4); - DMACChan *chan = &channel[ch]; + DMACChan *chan = channels[ch].get(); chan->req = false; // #3(ADPCM) の REQ は PCL にも繋がっている @@ -1741,7 +1867,7 @@ DMACDevice::NegateREQ(int ch) void DMACDevice::AbortTransfer(DMACChan *chan) { - putlog(2, "CCR SAB Software Abort"); + chan_putlog(chan, 2, "CCR SAB Software Abort"); // %1 を書き込むことで実際には SAB はセットされるが、ERR が立つと // SAB をクリアする動作のため、書き込んだ %1 が読めることはない。 @@ -1749,7 +1875,8 @@ DMACDevice::AbortTransfer(DMACChan *chan chan->str = false; chan->cnt = false; - chan->active = false; + ChangeACT(chan, false); + chan->cer = DMAC::CER_SOFT_ABORT; // ERR ビットが立つと SAB をクリアする chan->csr |= DMAC::CSR_ERR; @@ -1757,15 +1884,71 @@ DMACDevice::AbortTransfer(DMACChan *chan ChangeInterrupt(); } +// メインメモリから 1 or 2 バイト読み込む。 +busdata +DMACDevice::ReadMem(busaddr addr) +{ + busdata data = mainbus->Read(addr); + + // IODevice からの読み込みはポートサイズ単位で行う仕様なので、 + // ここでダイナミックバスサイジングの要領で必要な部分を抜き出す。 + // 実際の HD63450 は 68000 バス用のデバイスなので、このあたりの + // バス変換を YUKI ちゃんなどの周辺回路がやってるはず。 + uint64 rearranged = MainbusDevice::DYNAMIC_BUS_SIZING_R(addr, data); + data.ChangeData(rearranged); + + return data; +} + +// メインメモリから4バイト読み込む。 +// Size フィールドは指定しなくてもよい。 +busdata +DMACDevice::ReadMem4(busaddr addr) +{ + addr.ChangeSize(2); + + busdata hi = ReadMem(addr); + if (__predict_false(hi.IsBusErr())) { + return hi; + } + + addr += 2; + busdata lo = ReadMem(addr); + if (__predict_false(lo.IsBusErr())) { + return lo; + } + + busdata data((hi.Data() << 16) | lo.Data()); + return data; +} + +// メインメモリに書き込む。 +// ほぼ対称性のためだけ。 +busdata +DMACDevice::WriteMem(busaddr addr, uint32 data) +{ + return mainbus->Write(addr, data); +} + +// インデックス化されたアドレス start から end (の手前まで) のユーザ空間 +// アクセスの可否を設定する。x68kio から呼ばれる。 +// accessible true なら (MPU の FC2 ピンによらず) ユーザアクセス可能。 +// アドレスは 8KB 単位なので start=1 ならアドレスは $2000。 +void +DMACDevice::SetUdevice(uint32 start, uint32 end, bool accessible) +{ + for (int i = start; i < end; i++) { + useraccess[i] = accessible; + } +} + // 割り込み信号線の状態を変える。 void DMACDevice::ChangeInterrupt() { bool irq = false; - for (int ch = 0; ch < channel.size(); ch++) { - DMACChan *chan = &channel[ch]; - + for (auto& chan : channels) { if (chan->int_enable && chan->IsINTR()) { irq = true; } @@ -1774,41 +1957,54 @@ DMACDevice::ChangeInterrupt() } // 割り込みアクノリッジ -int +busdata DMACDevice::InterruptAcknowledge() { - DMACChan *chan; int top = -1; uint8 prio = 255; // 割り込みを発生させているトッププライオリティのチャンネルを検索 - for (int ch = 0; ch < channel.size(); ch++) { - chan = &channel[ch]; - + for (auto& chan : channels) { if (chan->int_enable && chan->IsINTR()) { if (chan->priority < prio) { prio = chan->priority; - top = ch; + top = chan->ch; } } } - if (top >= 0) { - chan = &channel[top]; + if (__predict_true(top >= 0)) { + DMACChan *chan = channels[top].get(); if ((chan->csr & DMAC::CSR_ERR)) { return chan->eiv; } else { return chan->niv; } + } else { + return BusData::BusErr; } - // XXX 誰もいなかったら? - VMPANIC("InterruptAcknowledge no active channels?"); } // // チャンネル // +// コンストラクタ +DMACChan::DMACChan(int ch_, const char *desc_) + : inherited(OBJ_DMAC_CH(ch_)) +{ + ch = ch_; + desc = desc_; + + ClearAlias(); + AddAlias(string_format("DMAC%u", ch)); +} + +// デストラクタ +DMACChan::~DMACChan() +{ +} + // CSR を取得する uint8 DMACChan::GetCSR() const @@ -1951,7 +2147,7 @@ DMACChan::SetEIV(uint8 val) void DMACChan::SetMFC(uint8 val) { - mfc = val & 0x07; + mfc = busaddr::FC(val & 0x07); } // CPR を設定する @@ -1967,12 +2163,132 @@ DMACChan::SetCPR(uint8 val) void DMACChan::SetDFC(uint8 val) { - dfc = val & 0x07; + dfc = busaddr::FC(val & 0x07); } // BFC を設定する void DMACChan::SetBFC(uint8 val) { - bfc = val & 0x07; + bfc = busaddr::FC(val & 0x07); } + +// レジスタ名 (バイト) +/*static*/ const char * const +DMACDevice::regname1[0x40] = { + "CSR", // 00 + "CER", // 01 + NULL, // 02 + NULL, // 03 + "DCR", // 04 + "OCR", // 05 + "SCR", // 06 + "CCR", // 07 + NULL, // 08 + NULL, // 09 + "MTC:H", // 0a + "MTC:L", // 0b + "MAR:0", // 0c + "MAR:1", // 0d + "MAR:2", // 0e + "MAR:3", // 0f + NULL, // 10 + NULL, // 11 + NULL, // 12 + NULL, // 13 + "DAR:0", // 14 + "DAR:1", // 15 + "DAR:2", // 16 + "DAR:3", // 17 + NULL, // 18 + NULL, // 19 + "BTC:H", // 1a + "BTC:L", // 1b + "BAR:0", // 1c + "BAR:1", // 1d + "BAR:2", // 1e + "BAR:3", // 1f + NULL, // 20 + NULL, // 21 + NULL, // 22 + NULL, // 23 + NULL, // 24 + "NIV", // 25 + NULL, // 26 + "EIV", // 27 + NULL, // 28 + "MFC", // 29 + NULL, // 2a + NULL, // 2b + NULL, // 2c + "CPR", // 2d + NULL, // 2e + NULL, // 2f + NULL, // 30 + "DFC", // 31 + NULL, // 32 + NULL, // 33 + NULL, // 34 + NULL, // 35 + NULL, // 36 + NULL, // 37 + NULL, // 38 + "BFC", // 39 + NULL, // 3a + NULL, // 3b + NULL, // 3c + NULL, // 3d + NULL, // 3e + NULL, // 3f (GCR は別処理) +}; + +// レジスタ名 (ワード) +/*static*/ const char * const +DMACDevice::regname2[0x40 / 2] = { + "CSR:CER", // 00 + NULL, // 02 + "DCR:OCR", // 04 + "SCR:CCR", // 06 + NULL, // 08 + "MTC", // 0a + "MAR:H", // 0c + "MAR:L", // 0e + NULL, // 10 + NULL, // 12 + "DAR:H", // 14 + "DAR:L", // 16 + NULL, // 18 + "BTC", // 1a + "BAR:H", // 1c + "BAR:L", // 1e + NULL, // 20 + NULL, // 22 + "NIV(W)", // 24 + "EIV(W)", // 26 + "MFC(W)", // 28 + NULL, // 2a + "CPR(W)", // 2c + NULL, // 2f + "DFC(W)", // 30 + NULL, // 32 + NULL, // 34 + NULL, // 36 + "BFC(W)", // 38 + NULL, // 3a + NULL, // 3c + NULL, // 3e (GCR は別処理) +}; + +/*static*/ const char * const +DMACDevice::seqname[DMACDevice::SEQ_MAX] = +{ + "NOP", + "RD_M8", + "RD_M16", + "RD_D8", + "RD_D16", + "WR_M8", + "WR_M16", + "WR_D8", + "WR_D16", +};