--- nono/vm/dmac.cpp 2026/04/29 17:04:41 1.1.1.5 +++ nono/vm/dmac.cpp 2026/04/29 17:04:50 1.1.1.7 @@ -6,8 +6,8 @@ #include "dmac.h" #include "bus.h" +#include "interrupt.h" #include "mpu680x0.h" -#include "mystring.h" std::unique_ptr gDMAC; @@ -57,7 +57,6 @@ DMACDevice::ResetHard() chan->ocr = 0; chan->scr = 0; chan->ccr = 0; - chan->int_enable = false; chan->csr = 0; chan->active = false; chan->pcl = false; @@ -75,16 +74,16 @@ DMACDevice::ResetHard() } uint64 -DMACDevice::Read(uint32 addr) +DMACDevice::Read(uint32 offset) { uint8 data; int ch; gMPU->AddCycle(37); // InsideOut p.135 - ch = addr / 0x40; + ch = offset / 0x40; DMAC::Channel *chan = &dmac.chan[ch]; - int n = addr % 0x40; + int n = offset % 0x40; switch (n) { case DMAC::CSR: data = chan->PeekCSR(); @@ -112,7 +111,7 @@ DMACDevice::Read(uint32 addr) break; case DMAC::CCR: - data = chan->PeekCCR(); + data = chan->ccr; putlog(4, "#%d CCR -> $%02x", ch, data); break; @@ -234,18 +233,19 @@ DMACDevice::Read(uint32 addr) } uint64 -DMACDevice::Write(uint32 addr, uint32 data) +DMACDevice::Write(uint32 offset, uint32 data) { int ch; gMPU->AddCycle(31); // InsideOut p.135 - ch = addr / 0x40; + ch = offset / 0x40; DMAC::Channel *chan = &dmac.chan[ch]; - int n = addr % 0x40; + int n = offset % 0x40; switch (n) { case DMAC::CSR: chan->WriteCSR(data); + ChangeInterrupt(); break; case DMAC::CER: @@ -270,6 +270,7 @@ DMACDevice::Write(uint32 addr, uint32 da case DMAC::CCR: putlog(4, "#%d CCR <- $%02x", ch, data); chan->WriteCCR(data); + ChangeInterrupt(); break; case DMAC::MTC: @@ -385,14 +386,14 @@ DMACDevice::Write(uint32 addr, uint32 da } uint64 -DMACDevice::Peek(uint32 addr) +DMACDevice::Peek(uint32 offset) { uint8 data; int ch; - ch = addr / 0x40; + ch = offset / 0x40; const DMAC::Channel *chan = &dmac.chan[ch]; - int n = addr % 0x40; + int n = offset % 0x40; switch (n) { case DMAC::CSR: data = chan->PeekCSR(); @@ -415,7 +416,7 @@ DMACDevice::Peek(uint32 addr) break; case DMAC::CCR: - data = chan->PeekCCR(); + data = chan->ccr; break; case DMAC::MTC: @@ -671,7 +672,7 @@ DMACDevice::MonitorUpdate(TextScreen& mo monitor.Print(x, y++, "%d:%s", val, scr_xac[val]); // CCR - val = chan->PeekCCR(); + val = chan->ccr; monitor.Print(x, y++, "$%02x", val); static const char * const ccrnames[] = { "STR", "CNT", "HLT", "SAB", "INT", "---", "---", "---", @@ -777,6 +778,9 @@ DMAC::Channel::PeekCSR() const return val; } +// CSR への書き込み。 +// CSR への書き込みによって割り込み信号線が変化するかも知れないので +// この後呼び出し側が ChangeInterrupt() を呼ぶこと。 void DMAC::Channel::WriteCSR(uint32 data) { @@ -789,27 +793,19 @@ DMAC::Channel::WriteCSR(uint32 data) prev_pcl = false; } - parent->putlog(4, "#%d CSR <- $%02x (CSR = $%02x)", ch, data, PeekCSR()); -} - -// Read と Peek から呼ばれるので副作用を持たせてはいけない。 -uint8 -DMAC::Channel::PeekCCR() const -{ - uint32 val; - - val = ccr & 0xf0; - if (int_enable) { - val |= DMAC::CCR_INT; - } - return val; + parent->putlogf(4, lstr("#%d CSR <- $%02x (CSR = $%02x)", + ch, data, PeekCSR())); } +// CCR への書き込み。 +// CCR への書き込みによって割り込み信号線が変化するかも知れないので +// この後呼び出し側が ChangeInterrupt() を呼ぶこと。 void DMAC::Channel::WriteCCR(uint32 data) { // 割り込み許可ビットは常に反映 - int_enable = (data & DMAC::CCR_INT); + ccr &= ~DMAC::CCR_INT; + ccr |= (data & DMAC::CCR_INT); // SAB (Software Abort) if ((data & DMAC::CCR_SAB)) { @@ -819,12 +815,12 @@ DMAC::Channel::WriteCCR(uint32 data) // HLT (Halt Operation) if ((data & DMAC::CCR_HLT)) { - parent->putlog(0, "CCR HLT 未実装"); + parent->putlogf(0, lstr("CCR HLT 未実装")); } // CNT (Continue Operation) if ((data & DMAC::CCR_CNT)) { - parent->putlog(0, "CCR CNT 未実装"); + parent->putlogf(0, lstr("CCR CNT 未実装")); } // STR (Start Operation) @@ -833,31 +829,6 @@ DMAC::Channel::WriteCCR(uint32 data) } } -// 割り込み要因ビットをセットし、必要なら割り込みを上げる。 -// PCL は割り込み条件を満たしている時だけ呼ぶこと。 -// ここはレジスタに対する書き込みではない。 -void -DMAC::Channel::SetINT(uint32 val) -{ - int vector; - - csr |= val; - - // ERR ビットが立つと SAB をクリアする - if ((csr & CSR_ERR)) { - ccr &= ~CCR_SAB; - } - - if (int_enable) { - if ((csr & CSR_ERR)) { - vector = eiv; - } else { - vector = niv; - } - gMPU680x0->Interrupt(parent, 3, vector); - } -} - // 転送開始 (チャンネル側) void DMAC::Channel::StartTransfer() @@ -980,8 +951,8 @@ DMACDevice::Transfer(Event& ev) chan->mtc--; if (chan->mtc == 0) { // 転送完了 - chan->active = false; - chan->SetINT(DMAC::CSR_COC); + chan->csr |= DMAC::CSR_COC; + ChangeInterrupt(); } else { // 次回の転送 // XXX とりあえず 1us で1回の転送にする @@ -1038,7 +1009,46 @@ DMACDevice::AbortTransfer(DMAC::Channel // ここでは SAB ビットのセットを省略。 event[ch].Stop(); - chan->active = false; chan->cer = DMAC::CER_SOFT_ABORT; - chan->SetINT(DMAC::CSR_ERR); + // ERR ビットが立つと SAB をクリアする + chan->csr |= DMAC::CSR_ERR; + chan->ccr &= ~DMAC::CCR_SAB; + ChangeInterrupt(); +} + +// 割り込み信号線の状態を変える。 +void +DMACDevice::ChangeInterrupt() +{ + bool irq = false; + + for (int ch = 0; ch < countof(dmac.chan); ch++) { + DMAC::Channel *chan = &dmac.chan[ch]; + + if ((chan->ccr & DMAC::CCR_INT) && chan->IsINTR()) { + irq = true; + } + } + gInterrupt->ChangeINT(this, irq); +} + +// 割り込みアクノリッジ +int +DMACDevice::InterruptAcknowledge() +{ + // XXX CPR のプライオリティは未対応、とりあえず前から順 + for (int ch = 0; ch < countof(dmac.chan); ch++) { + DMAC::Channel *chan = &dmac.chan[ch]; + + if (chan->active) { + if ((chan->csr & DMAC::CSR_ERR)) { + return chan->eiv; + } else { + return chan->niv; + } + chan->active = false; + } + } + // XXX 誰もいなかったら? + PANIC("InterruptAcknowledge no active channels?"); }