--- nono/vm/dmac.cpp 2026/04/29 17:05:33 1.1.1.15 +++ nono/vm/dmac.cpp 2026/04/29 17:06:00 1.1.1.19 @@ -10,18 +10,20 @@ #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_, wait_) do { \ - event.func = ToEventCallback(&DMACDevice::func_); \ - event.time = time_ * 80_nsec + wait_; \ + event->SetCallback(&DMACDevice::func_); \ + event->time = time_ * 80_nsec + wait_; \ scheduler->StartEvent(event); \ } while (0) @@ -29,32 +31,35 @@ 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 は未対応 monitor = gMonitorManager->Regist(ID_MONITOR_DMAC, this); - monitor->func = ToMonitorCallback(&DMACDevice::MonitorUpdate); + monitor->SetCallback(&DMACDevice::MonitorScreen); monitor->SetSize(80, 34); } @@ -67,17 +72,14 @@ DMACDevice::~DMACDevice() bool DMACDevice::Init() { - if (inherited::Init() == false) { - return false; - } - adpcm = GetADPCMDevice(); fdc = GetFDCDevice(); interrupt = GetInterruptDevice(); mainbus = GetMainbusDevice(); + syncer = GetSyncer(); - event.SetName("DMAC"); - scheduler->RegistEvent(event); + auto evman = GetEventManager(); + event = evman->Regist(this, NULL, "DMAC"); return true; } @@ -96,8 +98,7 @@ DMACDevice::ResetHard(bool poweron) // (X68030 IPLROM が #0, #1 の BFC を初期化していないので読める)。 // MFC、DFC くらいは同様かも知れない。 if (poweron) { - for (int ch = 0; ch < channel.size(); ch++) { - DMACChan *chan = &channel[ch]; + for (auto& chan : channels) { chan->SetMFC(0x07); chan->SetDFC(0x07); chan->SetBFC(0x07); @@ -105,8 +106,7 @@ DMACDevice::ResetHard(bool poweron) } 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); @@ -121,10 +121,11 @@ DMACDevice::ResetHard(bool poweron) chan->priority = 0; } + syncer->NotifyDMACActive(false); ChangeInterrupt(); // イベントを停止 - event.SetName("DMAC"); + event->SetName("DMAC"); scheduler->StopEvent(event); } @@ -147,15 +148,16 @@ DMACDevice::Read(busaddr addr) uint32 offset = paddr & 0xff; uint ch = offset / 0x40; uint n = offset % 0x40; + DMACChan *chan = channels[ch].get(); if (datasize == 1) { if (regname1[n]) { - putlogn("#%u %s -> $%02x", ch, regname1[n], data.Data()); + chan->putlogn("%s -> $%02x", regname1[n], data.Data()); } else if (offset == 0xff) { putlogn("GCR -> $%02x", data.Data()); } } else { if (regname2[n / 2]) { - putlogn("#%u %s -> $%04x", ch, regname2[n / 2], data.Data()); + chan->putlogn("%s -> $%04x", regname2[n / 2], data.Data()); } else if (offset == 0xfe) { putlogn("GCR(W) -> $%04x", data.Data()); } @@ -178,7 +180,7 @@ DMACDevice::Write(busaddr addr, uint32 d uint32 offset = paddr & 0xff; uint32 ch = offset / 0x40; uint32 n = offset % 0x40; - DMACChan *chan = &channel[ch]; + DMACChan *chan = channels[ch].get(); if (datasize == 1) { WriteByte(chan, n, data); } else { @@ -201,7 +203,7 @@ DMACDevice::Write(busaddr addr, uint32 d // ログ表示版 #define TIMING_ERR_IF_RUNNING_log(regname) do { \ if (chan->str || chan->active) { \ - putlog(2, "#%u %s <- $%02x", ch, regname, data); \ + chan_putlog(chan, 2, "%s <- $%02x", regname, data); \ Error(chan, DMAC::CER_TIMING); \ break; \ } \ @@ -223,19 +225,19 @@ DMACDevice::WriteByte(DMACChan *chan, ui break; case DMAC::DCR: - putlog(2, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetDCR(data); break; case DMAC::OCR: - putlog(2, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetOCR(data); break; case DMAC::SCR: - putlog(2, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetSCR(data); break; @@ -245,7 +247,7 @@ DMACDevice::WriteByte(DMACChan *chan, ui // %0 -> %1 に変化したビットだけを評価する。 uint8 up = (chan->GetCCR() ^ data) & data; // バイトサイズのログを (up 込みで) 表示。 - putlog(2, "#%u 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); @@ -255,111 +257,111 @@ DMACDevice::WriteByte(DMACChan *chan, ui case DMAC::MTC: TIMING_ERR_IF_RUNNING_log("MTC:H"); chan->mtc = (chan->mtc & 0x00ff) | (data << 8); - putlog(2, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); chan->SetNIV(data); break; case DMAC::EIV: - putlog(2, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); chan->SetEIV(data); break; case DMAC::MFC: - putlog(2, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetMFC(data); break; case DMAC::CPR: - putlog(2, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); chan->SetCPR(data); break; case DMAC::DFC: - putlog(2, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); TIMING_ERR_IF_RUNNING; chan->SetDFC(data); break; case DMAC::BFC: - putlog(2, "#%u %s <- $%02x", ch, regname1[n], data); + chan_putlog(chan, 2, "%s <- $%02x", regname1[n], data); chan->SetBFC(data); break; @@ -390,7 +392,7 @@ DMACDevice::WriteWord(DMACChan *chan, ui break; case DMAC::DCR: // DCR | OCR - putlog(2, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->SetDCR(data >> 8); chan->SetOCR(data & 0xff); @@ -401,7 +403,7 @@ DMACDevice::WriteWord(DMACChan *chan, ui // CCR の %0 -> %1 に変化したビットだけを取り出す。 uint8 up = (chan->GetCCR() ^ data) & data; // ワードサイズのログを (CCR の up 込みで) 表示。 - putlog(2, "#%u 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); @@ -414,7 +416,7 @@ DMACDevice::WriteWord(DMACChan *chan, ui } case DMAC::MTC: - putlog(2, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->mtc = data; break; @@ -422,27 +424,27 @@ DMACDevice::WriteWord(DMACChan *chan, ui case DMAC::MAR: TIMING_ERR_IF_RUNNING_log("MAR:H"); chan->mar = (chan->mar & 0x0000ffff) | (data << 16); - putlog(2, "#%u 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, "#%u 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, "#%u 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, "#%u 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, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->btc = data; break; @@ -450,43 +452,43 @@ DMACDevice::WriteWord(DMACChan *chan, ui case DMAC::BAR: TIMING_ERR_IF_RUNNING_log("BAR:H"); chan->bar = (chan->bar & 0x0000ffff) | (data << 16); - putlog(2, "#%u 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, "#%u 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 & ~1U: - putlog(2, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); chan->SetNIV(data); break; case DMAC::EIV & ~1U: - putlog(2, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); chan->SetEIV(data); break; case DMAC::MFC & ~1U: - putlog(2, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->SetMFC(data); break; case DMAC::CPR & ~1U: - putlog(2, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); chan->SetCPR(data); break; case DMAC::DFC & ~1U: - putlog(2, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); TIMING_ERR_IF_RUNNING; chan->SetDFC(data); break; case DMAC::BFC & ~1U: - putlog(2, "#%u %s <- $%04x", ch, regname2[n / 2], data); + chan_putlog(chan, 2, "%s <- $%04x", regname2[n / 2], data); chan->SetBFC(data); break; @@ -511,7 +513,7 @@ DMACDevice::Peek1(uint32 addr) uint32 offset = addr & 0xff; uint ch = offset / 0x40; uint n = offset % 0x40; - DMACChan *chan = &channel[ch]; + DMACChan *chan = channels[ch].get(); switch (n) { case DMAC::CSR: @@ -632,7 +634,7 @@ DMACDevice::Peek1(uint32 addr) } void -DMACDevice::MonitorUpdate(Monitor *, TextScreen& screen) +DMACDevice::MonitorScreen(Monitor *, TextScreen& screen) { int x; int y; @@ -671,9 +673,9 @@ DMACDevice::MonitorUpdate(Monitor *, Tex screen.Print(0, y++, "+$%02x DFC:", DMAC::DFC); screen.Print(0, y++, "+$%02x BFC:", DMAC::BFC); - for (uint ch = 0; ch < 4; ch++) { - DMACChan *chan = &channel[ch]; + for (auto& chan : channels) { uint val; + int ch = chan->ch; x = 13 + ch * 17; y = 0; @@ -866,7 +868,7 @@ DMACDevice::MonitorUpdate(Monitor *, Tex } } -// 4ビット分を表示する。MonitorUpdate の下請け +// 4ビット分を表示する。MonitorScreen の下請け void DMACDevice::MonitorReg4(TextScreen& screen, int x, int y, uint32 reg, const char * const *names) @@ -875,7 +877,7 @@ DMACDevice::MonitorReg4(TextScreen& scre 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]); } } @@ -924,7 +926,7 @@ DMACDevice::WriteCSR(DMACChan *chan, uin chan->pcl_prev = false; } - putlog(2, "#%u CSR <- $%02x (CSR = $%02x)", chan->ch, data, chan->GetCSR()); + chan_putlog(chan, 2, "CSR <- $%02x (CSR = $%02x)", data, chan->GetCSR()); ChangeInterrupt(); } @@ -988,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; @@ -1045,6 +1047,22 @@ 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) @@ -1057,7 +1075,7 @@ DMACDevice::StartTransfer(DMACChan *chan } // ACT を立てたら STR を下げる。 - chan->active = true; + ChangeACT(chan, true); chan->str = false; // XXX あとで移動する @@ -1066,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: @@ -1078,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: @@ -1112,65 +1130,149 @@ DMACDevice::StartTransfer(DMACChan *chan VMPANIC("corrupted reqg=%u", reqg); } - // 転送開始ログ。全パラメータは無理なので概要だけ表示。 - if (loglevel >= 1) { - static const char * const countstr[] = { - "-", - "", - "+", - }; - std::string msg; - if (chan->GetDIR() == DMAC::DIR_MtoD) { - msg = string_format("$%06x%s to $%06x%s", - chan->mar, countstr[chan->mac + 1], - chan->dar, countstr[chan->dac + 1]); - } else { - msg = string_format("$%06x%s to $%06x%s", - chan->dar, countstr[chan->dac + 1], - chan->mar, countstr[chan->mac + 1]); + // チェインモードなら初回読み込み。 + // 通常モードなら何もしなくていいので、ログを出すだけ。 + 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("#%u Start %s mtc=$%04x", chan->ch, msg.c_str(), chan->mtc); + break; + + default: + __unreachable(); } - if (event.IsRunning() == false) { + 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(); + + 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 #%u", 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(); @@ -1254,21 +1356,21 @@ DMACDevice::StartCallback(Event& ev) } } - if (__predict_false(loglevel >= 5)) { + if (__predict_false(chan->loglevel >= 5)) { std::string ss; for (const auto op : chan->seq) { ss += ' '; ss += seqname[op]; } - putlogn("#%u %s seq:%s", chan->ch, __func__, ss.c_str()); + 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(); @@ -1277,8 +1379,8 @@ DMACDevice::TransferCallback(Event& ev) } uint op = chan->seq[chan->seq_index]; - putlog(5, "#%u %s [%u] %s", - chan->ch, __func__, chan->seq_index, seqname[op]); + chan_putlog(chan, 5, "%s [%u] %s", + __func__, chan->seq_index, seqname[op]); uint64 data; busdata r; @@ -1289,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: @@ -1385,13 +1506,7 @@ DMACDevice::TransferCallback(Event& ev) case RD_M16: case RD_D16: if (accessible) { - r = mainbus->Read(addr); - // IODevice からの読み込みはポートサイズ単位で行う仕様なので、 - // ここでダイナミックバスサイジングの要領で必要な部分を抜き出す。 - // 実際の HD63450 は 68000 バス用のデバイスなので、このあたりの - // バス変換を YUKI ちゃんなどの周辺回路がやってるはず。 - data = MainbusDevice::DYNAMIC_BUS_SIZING_R(addr, r); - r.ChangeData(data); + r = ReadMem(addr); } else { r.SetBusErr(); } @@ -1401,7 +1516,7 @@ DMACDevice::TransferCallback(Event& ev) case WR_M16: case WR_D16: if (accessible) { - r = mainbus->Write(addr, data); + r = WriteMem(addr, data); } else { r.SetBusErr(); } @@ -1474,22 +1589,51 @@ DMACDevice::TransferCallback(Event& ev) VMPANIC("corrupted op=%u", op); } - // シーケンス完了で 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->seq_index++; + if (chan->seq_index < chan->seq.size()) { + // XXX ディレイはあとから見直す + CallAfter(TransferCallback, 4, r.GetWait()); + return; + } + + // ここで転送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, r.GetWait()); } else { - // XXX ディレイはあとから見直す - CallAfter(TransferCallback, 4, r.GetWait()); + // すべての転送が完了。 + chan_putlog(chan, 1, "Complete Transfer"); + chan->csr |= DMAC::CSR_COC; + ChangeACT(chan, false); + ChangeInterrupt(); } } @@ -1520,12 +1664,12 @@ DMACDevice::TransferError(DMACChan *chan } // 現在のイベントを再実行 - event.time = 1 * 80_nsec; + event->time = 1 * 80_nsec; scheduler->StartEvent(event); return; } else { // タイムアウトしたらバスエラーにフォールスルー - putlog(3, "#%u retry exceeds", chan->ch); + chan_putlog(chan, 3, "retry exceeds"); } } @@ -1563,7 +1707,7 @@ DMACDevice::TransferLog(DMACChan *chan, // Write $%06x <- $xx (1 retried) if (op == NOP) { - putlogn("#%u Nop", chan->ch); + chan->putlogn("Nop"); return; } @@ -1594,8 +1738,7 @@ DMACDevice::TransferLog(DMACChan *chan, break; } - std::string str = string_format("#%u %s $%06x %s ", - chan->ch, act, addr.Addr(), dir); + std::string str = string_format("%s $%06x %s ", act, addr.Addr(), dir); switch (op) { case RD_M8: @@ -1609,6 +1752,7 @@ DMACDevice::TransferLog(DMACChan *chan, if (r.IsOK()) { str += string_format("$%04x", r.Data()); } + break; case WR_M8: case WR_D8: str += string_format("$%02x", (uint32)data); @@ -1629,7 +1773,7 @@ DMACDevice::TransferLog(DMACChan *chan, str += string_format(" (%u retried)", chan->retry); } - putlogn("%s", str.c_str()); + chan->putlogn("%s", str.c_str()); } // エラー発生 @@ -1639,7 +1783,7 @@ 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(); @@ -1659,7 +1803,7 @@ DMACDevice::AssertACK(DMACChan *chan) // 接続されていない break; case 3: - adpcm->AssertDACK(chan->mtc == 1); + adpcm->AssertDACK(); break; default: __unreachable(); @@ -1679,7 +1823,7 @@ DMACDevice::NegateACK(DMACChan *chan) // 接続されていない break; case 3: - adpcm->NegateDACK(); + // 不要。 break; default: __unreachable(); @@ -1692,7 +1836,7 @@ DMACDevice::AssertREQ(uint ch) { assert(ch < 4); - DMACChan *chan = &channel[ch]; + DMACChan *chan = channels[ch].get(); chan->req = true; // #3(ADPCM) の REQ は PCL にも繋がっている @@ -1700,7 +1844,7 @@ DMACDevice::AssertREQ(uint ch) chan->pcl_pin = true; } - if (event.IsRunning() == false) { + if (event->IsRunning() == false) { scheduler->StartEvent(event); } } @@ -1711,7 +1855,7 @@ DMACDevice::NegateREQ(uint ch) { assert(ch < 4); - DMACChan *chan = &channel[ch]; + DMACChan *chan = channels[ch].get(); chan->req = false; // #3(ADPCM) の REQ は PCL にも繋がっている @@ -1724,7 +1868,7 @@ DMACDevice::NegateREQ(uint 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 が読めることはない。 @@ -1732,7 +1876,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; @@ -1740,6 +1885,52 @@ 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 ピンによらず) ユーザアクセス可能。 @@ -1758,9 +1949,7 @@ 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; } @@ -1772,24 +1961,21 @@ DMACDevice::ChangeInterrupt() 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 (__predict_true(top >= 0)) { - chan = &channel[top]; + DMACChan *chan = channels[top].get(); if ((chan->csr & DMAC::CSR_ERR)) { return chan->eiv; } else { @@ -1804,6 +1990,22 @@ DMACDevice::InterruptAcknowledge() // チャンネル // +// コンストラクタ +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