--- nono/vm/dmac.cpp 2026/04/29 17:04:32 1.1.1.2 +++ nono/vm/dmac.cpp 2026/04/29 17:04:44 1.1.1.6 @@ -1,9 +1,13 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "dmac.h" +#include "bus.h" +#include "interrupt.h" +#include "mpu680x0.h" #include "mystring.h" std::unique_ptr gDMAC; @@ -15,7 +19,7 @@ DMACDevice::DMACDevice() devaddr = baseaddr; devlen = 0x2000; - monitor.Init(80, 30); + monitor_size = nnSize(80, 30); for (int i = 0; i < countof(dmac.chan); i++) { dmac.chan[i].ch = i; @@ -38,15 +42,49 @@ DMACDevice::~DMACDevice() { } +void +DMACDevice::ResetHard() +{ + putlog(2, "リセット"); + + // Clears GCR, DCR, OCR, SCR, CCR, CSR, CPR and CER for all channels. + // NIV and EIV are all set to $0F (uninitialized interrupt vector). + // MTC, MAR, DAR, BTC, BAR, MFC, DFC and BFC are not affected. + + dmac.gcr = 0; + for (int ch = 0; ch < countof(dmac.chan); ch++) { + DMAC::Channel *chan = &dmac.chan[ch]; + chan->dcr = 0; + chan->ocr = 0; + chan->scr = 0; + chan->ccr = 0; + chan->csr = 0; + chan->active = false; + chan->pcl = false; + chan->cpr = 0; + chan->cer = 0; + + chan->niv = 0x0f; + chan->eiv = 0x0f; + } + + // イベントをすべて停止 + for (int i = 0; i < countof(event); i++) { + event[i].Stop(); + } +} + uint64 -DMACDevice::Read8(uint32 addr) +DMACDevice::Read(uint32 addr) { uint8 data; int ch; - ch = (addr - baseaddr) / 0x40; + gMPU->AddCycle(37); // InsideOut p.135 + + ch = addr / 0x40; DMAC::Channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + int n = addr % 0x40; switch (n) { case DMAC::CSR: data = chan->PeekCSR(); @@ -74,7 +112,7 @@ DMACDevice::Read8(uint32 addr) break; case DMAC::CCR: - data = chan->PeekCCR(); + data = chan->ccr; putlog(4, "#%d CCR -> $%02x", ch, data); break; @@ -196,16 +234,19 @@ DMACDevice::Read8(uint32 addr) } uint64 -DMACDevice::Write8(uint32 addr, uint32 data) +DMACDevice::Write(uint32 addr, uint32 data) { int ch; - ch = (addr - baseaddr) / 0x40; + gMPU->AddCycle(31); // InsideOut p.135 + + ch = addr / 0x40; DMAC::Channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + int n = addr % 0x40; switch (n) { case DMAC::CSR: chan->WriteCSR(data); + ChangeInterrupt(); break; case DMAC::CER: @@ -230,6 +271,7 @@ DMACDevice::Write8(uint32 addr, uint32 d case DMAC::CCR: putlog(4, "#%d CCR <- $%02x", ch, data); chan->WriteCCR(data); + ChangeInterrupt(); break; case DMAC::MTC: @@ -345,14 +387,14 @@ DMACDevice::Write8(uint32 addr, uint32 d } uint64 -DMACDevice::Peek8(uint32 addr) +DMACDevice::Peek(uint32 addr) { uint8 data; int ch; - ch = (addr - baseaddr) / 0x40; + ch = addr / 0x40; const DMAC::Channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + int n = addr % 0x40; switch (n) { case DMAC::CSR: data = chan->PeekCSR(); @@ -375,7 +417,7 @@ DMACDevice::Peek8(uint32 addr) break; case DMAC::CCR: - data = chan->PeekCCR(); + data = chan->ccr; break; case DMAC::MTC: @@ -472,8 +514,8 @@ DMACDevice::Peek8(uint32 addr) return data; } -bool -DMACDevice::MonitorUpdate() +void +DMACDevice::MonitorUpdate(TextScreen& monitor) { int x; int y; @@ -481,32 +523,32 @@ DMACDevice::MonitorUpdate() monitor.Clear(); y = 1; - monitor.Print(0, y++, "CSR"); + monitor.Puts(0, y++, "CSR"); y += 2; - monitor.Print(0, y++, "CER"); - monitor.Print(0, y++, "DCR:XRM"); - monitor.Print(0, y++, "DCR:DTYP"); - monitor.Print(0, y++, "DCR:DPS"); - monitor.Print(0, y++, "DCR:PCL"); - monitor.Print(0, y++, "OCR:DIR"); - monitor.Print(0, y++, "OCR:SIZE"); - monitor.Print(0, y++, "OCR:CHAIN"); - monitor.Print(0, y++, "OCR:REQG"); - monitor.Print(0, y++, "SCR:MAC"); - monitor.Print(0, y++, "SCR:DAC"); - monitor.Print(0, y++, "CCR"); + monitor.Puts(0, y++, "CER"); + monitor.Puts(0, y++, "DCR:XRM"); + monitor.Puts(0, y++, "DCR:DTYP"); + monitor.Puts(0, y++, "DCR:DPS"); + monitor.Puts(0, y++, "DCR:PCL"); + monitor.Puts(0, y++, "OCR:DIR"); + monitor.Puts(0, y++, "OCR:SIZE"); + monitor.Puts(0, y++, "OCR:CHAIN"); + monitor.Puts(0, y++, "OCR:REQG"); + monitor.Puts(0, y++, "SCR:MAC"); + monitor.Puts(0, y++, "SCR:DAC"); + monitor.Puts(0, y++, "CCR"); y += 2; - monitor.Print(0, y++, "MTC"); - monitor.Print(0, y++, "MAR"); - monitor.Print(0, y++, "DAR"); - monitor.Print(0, y++, "BTC"); - monitor.Print(0, y++, "BAR"); - monitor.Print(0, y++, "NIV"); - monitor.Print(0, y++, "EIV"); - monitor.Print(0, y++, "MFC"); - monitor.Print(0, y++, "CPR"); - monitor.Print(0, y++, "DFC"); - monitor.Print(0, y++, "BFC"); + monitor.Puts(0, y++, "MTC"); + monitor.Puts(0, y++, "MAR"); + monitor.Puts(0, y++, "DAR"); + monitor.Puts(0, y++, "BTC"); + monitor.Puts(0, y++, "BAR"); + monitor.Puts(0, y++, "NIV"); + monitor.Puts(0, y++, "EIV"); + monitor.Puts(0, y++, "MFC"); + monitor.Puts(0, y++, "CPR"); + monitor.Puts(0, y++, "DFC"); + monitor.Puts(0, y++, "BFC"); for (int ch = 0; ch < 4; ch++) { DMAC::Channel *chan = &dmac.chan[ch]; @@ -515,7 +557,7 @@ DMACDevice::MonitorUpdate() y = 0; // 地味だけど有効なチャンネルをハイライトしてみる - monitor.Print(x, y++, (chan->active ? TA::Em : TA::Off), + monitor.Print(x, y++, (chan->active ? TA::Em : TA::Normal), "#%d (%s)", chan->ch, chan->desc); // CSR @@ -524,8 +566,8 @@ DMACDevice::MonitorUpdate() static const char * const csrname[] = { "COC", "BTC", "NDT", "ERR", "ACT", "---", "PCT", "PCS", }; - MonitorReg4(x, y++, val >> 4, &csrname[0]); - MonitorReg4(x, y++, val & 0xff, &csrname[4]); + MonitorReg4(monitor, x, y++, val >> 4, &csrname[0]); + MonitorReg4(monitor, x, y++, val & 0xff, &csrname[4]); // CER val = chan->cer & 0x1f; @@ -631,13 +673,13 @@ DMACDevice::MonitorUpdate() 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", "---", "---", "---", }; - MonitorReg4(x, y++, (val >> 4), &ccrnames[0]); - MonitorReg4(x, y++, (val & 0xff), &ccrnames[4]); + MonitorReg4(monitor, x, y++, (val >> 4), &ccrnames[0]); + MonitorReg4(monitor, x, y++, (val & 0xff), &ccrnames[4]); // MTC monitor.Print(x, y++, "$%04x", chan->mtc); @@ -681,18 +723,16 @@ DMACDevice::MonitorUpdate() monitor.Print(x - 5, y++, "GCR: $%02x", dmac.gcr); } } - - return true; } // 4ビット分を表示する。MonitorUpdate の下請け void -DMACDevice::MonitorReg4(int x, int y, uint32 reg, const char * const *names) +DMACDevice::MonitorReg4(TextScreen& monitor, + int x, int y, uint32 reg, const char * const *names) { for (int i = 0; i < 4; i++) { bool b = reg & (1 << (3 - i)); - uint attr = b ? TA::On : TA::Off; - monitor.Print(x + i * 4, y, attr, "%s", names[i]); + monitor.Puts(x + i * 4, y, TA::OnOff(b), names[i]); } } @@ -739,6 +779,9 @@ DMAC::Channel::PeekCSR() const return val; } +// CSR への書き込み。 +// CSR への書き込みによって割り込み信号線が変化するかも知れないので +// この後呼び出し側が ChangeInterrupt() を呼ぶこと。 void DMAC::Channel::WriteCSR(uint32 data) { @@ -754,24 +797,15 @@ DMAC::Channel::WriteCSR(uint32 data) 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; -} - +// 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)) { @@ -795,32 +829,7 @@ 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(3, vector); - } -} - -// 転送開始 +// 転送開始 (チャンネル側) void DMAC::Channel::StartTransfer() { @@ -858,7 +867,7 @@ DMAC::Channel::StartTransfer() case DMAC::OCR_REQG_AUTO_MAX: // 自発的に転送を開始する - parent->Transfer(ch); + parent->StartTransfer(this); break; case DMAC::OCR_REQG_EXTERNAL: @@ -871,11 +880,21 @@ DMAC::Channel::StartTransfer() } } +// 転送開始 (デバイス側) +// (チャンネルが転送開始する際にデバイス側の private 変数(event[])が必要に +// なるため、プロキシしている) +void +DMACDevice::StartTransfer(DMAC::Channel *chan) +{ + Transfer(event[chan->ch]); +} + // 転送 // (イベントを持つのでこれはデバイスクラスのメソッド) void -DMACDevice::Transfer(int ch) +DMACDevice::Transfer(Event& ev) { + int& ch = ev.code; DMAC::Channel *chan = &dmac.chan[ch]; int64 rv; @@ -932,13 +951,13 @@ DMACDevice::Transfer(int ch) chan->mtc--; if (chan->mtc == 0) { // 転送完了 - chan->active = false; - chan->SetINT(DMAC::CSR_COC); + chan->csr |= DMAC::CSR_COC; + ChangeInterrupt(); } else { // 次回の転送 // XXX とりあえず 1us で1回の転送にする - event[ch].time = 1_usec; - event[ch].Start(); + ev.time = 1_usec; + ev.Start(); } } @@ -990,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?"); }