--- nono/vm/dmac.cpp 2026/04/29 17:04:32 1.1.1.2 +++ nono/vm/dmac.cpp 2026/04/29 17:05:10 1.1.1.10 @@ -1,22 +1,37 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// DMAC (HD63450) // #include "dmac.h" -#include "mystring.h" +#include "bus.h" +#include "interrupt.h" +#include "mpu.h" +#include "scheduler.h" + +// time 時間後に呼び出すコールバックをセットする。 +// func の書式が面倒なのを省略して書きたいため。 +#define CallAfter(func_, time_) do { \ + event.func = ToEventCallback(&DMACDevice::func_); \ + event.time = time_ * 80_nsec; \ + gScheduler->StartEvent(event); \ +} while (0) -std::unique_ptr gDMAC; +// グローバル参照用 +DMACDevice *gDMAC; +// コンストラクタ DMACDevice::DMACDevice() + : inherited("DMAC") { - logname = "dmac"; - devname = "DMAC"; devaddr = baseaddr; devlen = 0x2000; - monitor.Init(80, 30); - for (int i = 0; i < countof(dmac.chan); i++) { dmac.chan[i].ch = i; dmac.chan[i].parent = this; @@ -26,27 +41,64 @@ DMACDevice::DMACDevice() dmac.chan[2].desc = "User"; dmac.chan[3].desc = "ADPCM"; - for (int i = 0; i < countof(event); i++) { - event[i].dev = this; - event[i].func = (DeviceCallback_t)&DMACDevice::Transfer; - event[i].SetName(string_format("DMAC #%d", i)); - event[i].code = i; - } + event.Regist("DMAC"); + + monitor.func = ToMonitorCallback(&DMACDevice::MonitorUpdate); + monitor.SetSize(80, 30); + monitor.Regist(ID_MONITOR_DMAC); } +// デストラクタ DMACDevice::~DMACDevice() { + gDMAC = NULL; +} + +// リセット +void +DMACDevice::ResetHard(bool poweron) +{ + 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; + + chan->priority = 0; + } + + // イベントを停止 + event.SetName("DMAC"); + gScheduler->StopEvent(event); } uint64 -DMACDevice::Read8(uint32 addr) +DMACDevice::Read(uint32 offset) { uint8 data; int ch; - ch = (addr - baseaddr) / 0x40; + gMPU->AddCycle(37); // InsideOut p.135 + + ch = offset / 0x40; DMAC::Channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + int n = offset % 0x40; switch (n) { case DMAC::CSR: data = chan->PeekCSR(); @@ -74,7 +126,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 +248,19 @@ DMACDevice::Read8(uint32 addr) } uint64 -DMACDevice::Write8(uint32 addr, uint32 data) +DMACDevice::Write(uint32 offset, uint32 data) { int ch; - ch = (addr - baseaddr) / 0x40; + gMPU->AddCycle(31); // InsideOut p.135 + + ch = offset / 0x40; DMAC::Channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + int n = offset % 0x40; switch (n) { case DMAC::CSR: chan->WriteCSR(data); + ChangeInterrupt(); break; case DMAC::CER: @@ -230,6 +285,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: @@ -319,6 +375,8 @@ DMACDevice::Write8(uint32 addr, uint32 d case DMAC::CPR: putlog(4, "#%d CPR <- $%02x", ch, data); chan->cpr = data & 3; + // 実効プライオリティの上位4bitは cpr に等しい + chan->priority = (chan->cpr << 4); break; case DMAC::DFC: @@ -345,14 +403,14 @@ DMACDevice::Write8(uint32 addr, uint32 d } uint64 -DMACDevice::Peek8(uint32 addr) +DMACDevice::Peek(uint32 offset) { uint8 data; int ch; - ch = (addr - baseaddr) / 0x40; + ch = offset / 0x40; const DMAC::Channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + int n = offset % 0x40; switch (n) { case DMAC::CSR: data = chan->PeekCSR(); @@ -375,7 +433,7 @@ DMACDevice::Peek8(uint32 addr) break; case DMAC::CCR: - data = chan->PeekCCR(); + data = chan->ccr; break; case DMAC::MTC: @@ -472,41 +530,41 @@ DMACDevice::Peek8(uint32 addr) return data; } -bool -DMACDevice::MonitorUpdate() +void +DMACDevice::MonitorUpdate(Monitor *, TextScreen& screen) { int x; int y; - monitor.Clear(); + screen.Clear(); y = 1; - monitor.Print(0, y++, "CSR"); + screen.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"); + screen.Puts(0, y++, "CER"); + screen.Puts(0, y++, "DCR:XRM"); + screen.Puts(0, y++, "DCR:DTYP"); + screen.Puts(0, y++, "DCR:DPS"); + screen.Puts(0, y++, "DCR:PCL"); + screen.Puts(0, y++, "OCR:DIR"); + screen.Puts(0, y++, "OCR:SIZE"); + screen.Puts(0, y++, "OCR:CHAIN"); + screen.Puts(0, y++, "OCR:REQG"); + screen.Puts(0, y++, "SCR:MAC"); + screen.Puts(0, y++, "SCR:DAC"); + screen.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"); + screen.Puts(0, y++, "MTC"); + screen.Puts(0, y++, "MAR"); + screen.Puts(0, y++, "DAR"); + screen.Puts(0, y++, "BTC"); + screen.Puts(0, y++, "BAR"); + screen.Puts(0, y++, "NIV"); + screen.Puts(0, y++, "EIV"); + screen.Puts(0, y++, "MFC"); + screen.Puts(0, y++, "CPR"); + screen.Puts(0, y++, "DFC"); + screen.Puts(0, y++, "BFC"); for (int ch = 0; ch < 4; ch++) { DMAC::Channel *chan = &dmac.chan[ch]; @@ -515,17 +573,17 @@ DMACDevice::MonitorUpdate() y = 0; // 地味だけど有効なチャンネルをハイライトしてみる - monitor.Print(x, y++, (chan->active ? TA::Em : TA::Off), + screen.Print(x, y++, (chan->active ? TA::Em : TA::Normal), "#%d (%s)", chan->ch, chan->desc); // CSR val = chan->PeekCSR(); - monitor.Print(x, y++, "$%02x", val); + screen.Print(x, y++, "$%02x", val); static const char * const csrname[] = { - "COC", "BTC", "NDT", "ERR", "ACT", "---", "PCT", "PCS", + "COC", "BTC", "NDT", "ERR", "ACT", "-", "PCT", "PCS", }; - MonitorReg4(x, y++, val >> 4, &csrname[0]); - MonitorReg4(x, y++, val & 0xff, &csrname[4]); + MonitorReg4(screen, x, y++, val >> 4, &csrname[0]); + MonitorReg4(screen, x, y++, val & 0xff, &csrname[4]); // CER val = chan->cer & 0x1f; @@ -535,9 +593,9 @@ DMACDevice::MonitorUpdate() } else { e = "?"; } - monitor.Print(x, y, "$%02x", chan->cer); + screen.Print(x, y, "$%02x", chan->cer); if (val > 0) { - monitor.Print(x + 3, y, ":%s", e); + screen.Print(x + 3, y, ":%s", e); } y++; @@ -550,7 +608,7 @@ DMACDevice::MonitorUpdate() "CycleWithHold", }; val = chan->dcr >> 6; - monitor.Print(x, y++, "%d:%s", val, dcr_xrm[val]); + screen.Print(x, y++, "%d:%s", val, dcr_xrm[val]); // DCR:DTYP static const char * const dcr_dtyp[] = { @@ -561,7 +619,7 @@ DMACDevice::MonitorUpdate() "ACK+READY", }; val = (chan->dcr >> 4) & 3; - monitor.Print(x, y++, "%d:%s", val, dcr_dtyp[val]); + screen.Print(x, y++, "%d:%s", val, dcr_dtyp[val]); // DCR:DPS static const char * const dcr_dps[] = { @@ -570,10 +628,10 @@ DMACDevice::MonitorUpdate() "16bit", }; val = (chan->dcr >> 3) & 1; - monitor.Print(x, y++, "%d:%s", val, dcr_dps[val]); + screen.Print(x, y++, "%d:%s", val, dcr_dps[val]); // DCR:PCL - monitor.Print(x, y++, "XXX PCL"); + screen.Print(x, y++, "XXX PCL"); // OCR:DIR static const char * const ocr_dir[] = { @@ -582,7 +640,7 @@ DMACDevice::MonitorUpdate() "DeviceToMemory", }; val = (chan->ocr >> 7); - monitor.Print(x, y++, "%d:%s", val, ocr_dir[val]); + screen.Print(x, y++, "%d:%s", val, ocr_dir[val]); // OCR:SIZE static const char * const ocr_size[] = { @@ -593,7 +651,7 @@ DMACDevice::MonitorUpdate() "8bit Unpacked", }; val = (chan->ocr >> 4) & 3; - monitor.Print(x, y++, "%d:%s", val, ocr_size[val]); + screen.Print(x, y++, "%d:%s", val, ocr_size[val]); // OCR:CHAIN static const char * const ocr_chain[] = { @@ -604,7 +662,7 @@ DMACDevice::MonitorUpdate() "LinkArrayChain", }; val = (chan->ocr >> 2) & 3; - monitor.Print(x, y++, "%d:%s", val, ocr_chain[val]); + screen.Print(x, y++, "%d:%s", val, ocr_chain[val]); // OCR:REQG static const char * const ocr_reqg[] = { @@ -615,7 +673,7 @@ DMACDevice::MonitorUpdate() "AutoThenExt", }; val = (chan->ocr & 3); - monitor.Print(x, y++, "%d:%s", val, ocr_reqg[val]); + screen.Print(x, y++, "%d:%s", val, ocr_reqg[val]); // SCR:MAC static const char * const scr_xac[] = { @@ -626,73 +684,75 @@ DMACDevice::MonitorUpdate() "undefined", }; val = (chan->scr >> 2) & 3; - monitor.Print(x, y++, "%d:%s", val, scr_xac[val]); + screen.Print(x, y++, "%d:%s", val, scr_xac[val]); val = chan->scr & 3; - monitor.Print(x, y++, "%d:%s", val, scr_xac[val]); + screen.Print(x, y++, "%d:%s", val, scr_xac[val]); // CCR - val = chan->PeekCCR(); - monitor.Print(x, y++, "$%02x", val); + val = chan->ccr; + screen.Print(x, y++, "$%02x", val); static const char * const ccrnames[] = { - "STR", "CNT", "HLT", "SAB", "INT", "---", "---", "---", + "STR", "CNT", "HLT", "SAB", "INT", "-", "-", "-", }; - MonitorReg4(x, y++, (val >> 4), &ccrnames[0]); - MonitorReg4(x, y++, (val & 0xff), &ccrnames[4]); + MonitorReg4(screen, x, y++, (val >> 4), &ccrnames[0]); + MonitorReg4(screen, x, y++, (val & 0xff), &ccrnames[4]); // MTC - monitor.Print(x, y++, "$%04x", chan->mtc); + screen.Print(x, y++, "$%04x", chan->mtc); // MAR // X680x0 がターゲットなので24bit超える設定は目立たせる if (chan->mar > 0xffffff) { - monitor.Print(x, y++, TA::On, "$%08x", chan->mar); + screen.Print(x, y++, TA::On, "$%08x", chan->mar); } else { - monitor.Print(x, y++, "$%06x", chan->mar); + screen.Print(x, y++, "$%06x", chan->mar); } // DAR if (chan->dar > 0xffffff) { - monitor.Print(x, y++, TA::On, "$%08x", chan->dar); + screen.Print(x, y++, TA::On, "$%08x", chan->dar); } else { - monitor.Print(x, y++, "$%06x", chan->dar); + screen.Print(x, y++, "$%06x", chan->dar); } // BTC - monitor.Print(x, y++, "$%04x", chan->btc); + screen.Print(x, y++, "$%04x", chan->btc); // BAR if (chan->bar > 0xffffff) { - monitor.Print(x, y++, TA::On, "$%08x", chan->bar); + screen.Print(x, y++, TA::On, "$%08x", chan->bar); } else { - monitor.Print(x, y++, "$%06x", chan->bar); + screen.Print(x, y++, "$%06x", chan->bar); } // NIV - monitor.Print(x, y++, "$%02x", chan->niv); + screen.Print(x, y++, "$%02x", chan->niv); // EIV - monitor.Print(x, y++, "$%02x", chan->eiv); + screen.Print(x, y++, "$%02x", chan->eiv); // MFC - monitor.Print(x, y++, "$%02x", chan->mfc); + screen.Print(x, y++, "$%02x", chan->mfc); // CPR - monitor.Print(x, y++, "$%02x", chan->cpr); + screen.Print(x, y++, "$%02x", chan->cpr); // DFC - monitor.Print(x, y++, "$%02x", chan->dfc); + screen.Print(x, y++, "$%02x", chan->dfc); // BFC - monitor.Print(x, y++, "$%02x", chan->bfc); + screen.Print(x, y++, "$%02x", chan->bfc); if (ch == 3) { - monitor.Print(x - 5, y++, "GCR: $%02x", dmac.gcr); + screen.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& screen, + 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]); + if (names[i][0] == '-') { + screen.Puts(x + i * 4, y, TA::Disable, "---"); + } else { + bool b = reg & (1 << (3 - i)); + screen.Puts(x + i * 4, y, TA::OnOff(b), names[i]); + } } } @@ -739,39 +799,34 @@ DMAC::Channel::PeekCSR() const return val; } +// CSR への書き込み。 +// CSR への書き込みによって割り込み信号線が変化するかも知れないので +// この後呼び出し側が ChangeInterrupt() を呼ぶこと。 void DMAC::Channel::WriteCSR(uint32 data) { // 上位4ビットは %1 の書き込みでクリア csr &= ~(data & 0xf0); - // PCT も %1 の書き込みでクリア + // PCT も %1 の書き込みでクリア。 // PCT は PCL が High → Low の時セットなので prev_pcl を下げればよい if ((data & DMAC::CSR_PCT)) { 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)) { @@ -781,12 +836,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) @@ -795,32 +850,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() { @@ -853,17 +883,17 @@ DMAC::Channel::StartTransfer() // 転送モード switch (GetREQG()) { case DMAC::OCR_REQG_AUTO_LIM: - PANIC("REQG_AUTO_LIM 未実装"); + VMPANIC("REQG_AUTO_LIM 未実装"); break; case DMAC::OCR_REQG_AUTO_MAX: // 自発的に転送を開始する - parent->Transfer(ch); + parent->StartTransfer(); break; case DMAC::OCR_REQG_EXTERNAL: case DMAC::OCR_REQG_AUTOFIRST: - PANIC("未実装 REQG"); + VMPANIC("未実装 REQG"); break; default: @@ -871,109 +901,190 @@ DMAC::Channel::StartTransfer() } } -// 転送 -// (イベントを持つのでこれはデバイスクラスのメソッド) +// 転送開始 (デバイス側) +// (チャンネルが転送開始する際にデバイス側の private 変数(event)が必要に +// なるため、プロキシしている) void -DMACDevice::Transfer(int ch) +DMACDevice::StartTransfer() { - DMAC::Channel *chan = &dmac.chan[ch]; - int64 rv; + if (event.IsRunning() == false) { + CallAfter(StartCallback, 0); + } +} - putlog(4, "#%d Transfer (MTC=$%04x)", ch, chan->mtc); +// 転送開始 +void +DMACDevice::StartCallback(Event& ev) +{ + // 転送チャンネルの決定 + int ch = -1; + uint8 prio = 255; + for (int i = 0; i < 4; i++) { + DMAC::Channel *chan = &dmac.chan[i]; + if (chan->active) { + uint8 p = chan->priority; + if (p < prio) { + prio = p; + // 実効プライオリティのラウンドロビン用のところを上げておく + chan->priority = (p & 0xf0) | ((p & 0x0f) >> 1); + ch = i; + } + } + } + if (ch == -1) { + // 転送チャンネルはもうないのでイベントは停止したままにする + event.SetName("DMAC"); + return; + } + xfer_chan = &dmac.chan[ch]; - rv = 0; - switch (chan->xfertype) { - case 0: // 8ビットポート、8ビットサイズ、パック動作 + // 実効プライオリティを同順位の最後に回す + xfer_chan->priority |= 0x08; + + if (xfer_chan->GetDIR() == DMAC::OCR_DIR_MtoD) { + xfer_src = xfer_chan->mar; + xfer_dst = xfer_chan->dar; + } else { + xfer_src = xfer_chan->dar; + xfer_dst = xfer_chan->mar; + } + + // XXX バスアービトレーションとか + + xfer_retry = 0; + event.SetName(string_format("DMAC #%d", ch)); + CallAfter(ReadCallback, 1); +} + +// 転送エラー +void +DMACDevice::Error(uint64 r) +{ + if ((int64)r == -2) { + // DTACK が出ていない (SPC)。 + // 本当は DTACK が出たことをコールバックしてくれれば + // 効率がいいのだがポーリングでもかまわんだろう。 + xfer_retry++; + putlog(4, "xfer_retry=%d", xfer_retry); + if (xfer_retry < 100) { + // 現在のイベントを再実行 + event.time = 1 * 80_nsec; + gScheduler->StartEvent(event); + return; + } + // タイムアウトしたらバスエラーにフォールスルー + } + // バスエラー + xfer_chan->csr |= DMAC::CSR_ERR; + xfer_chan->active = false; + ChangeInterrupt(); + CallAfter(StartCallback, 0); +} + +// 転送(読み込み) +void +DMACDevice::ReadCallback(Event& ev) +{ + uint64 r; + + // シングルアドレスモードはサポートしていない + + switch (xfer_chan->xfertype) { + case 0: // 8ビットポート、8ビットサイズ、パック動作 case 1: // 8ビットポート、16ビットサイズ + case 2: // 8ビットポート、32ビットサイズ + VMPANIC("未実装 xfer type"); + + case 3: // 8ビットポート、8ビットサイズ、パックなし動作 + r = vm_phys_read_8(xfer_src); + break; + + case 4: // 16ビットポート、8ビットサイズ、パック動作 + case 5: // 16ビットポート、16ビットサイズ + case 6: // 16ビットポート、32ビットサイズ + case 7: // 16ビットポート、8ビットサイズ、パックなし動作 + default: + VMPANIC("未実装 xfer type"); + } + if ((int64)r < 0) { + Error(r); + return; + } + xfer_retry = 0; + xfer_data = r; + + CallAfter(WriteCallback, 4); +} + +// 転送(書き込み) +void +DMACDevice::WriteCallback(Event& ev) +{ + uint64 r; + putlog(4, "WRITE xfer_data=$%02x", xfer_data); + + switch (xfer_chan->xfertype) { + case 0: // 8ビットポート、8ビットサイズ、パック動作 + case 1: // 8ビットポート、16ビットサイズ case 2: // 8ビットポート、32ビットサイズ - PANIC("未実装 xfer type"); + VMPANIC("未実装 xfer type"); case 3: // 8ビットポート、8ビットサイズ、パックなし動作 - if (chan->GetDIR() == DMAC::OCR_DIR_MtoD) { - rv = TransferM8toD8(chan); - } else { - rv = TransferD8toM8(chan); - } + r = vm_phys_write_8(xfer_dst, xfer_data); break; case 4: // 16ビットポート、8ビットサイズ、パック動作 case 5: // 16ビットポート、16ビットサイズ case 6: // 16ビットポート、32ビットサイズ case 7: // 16ビットポート、8ビットサイズ、パックなし動作 - PANIC("未実装 xfer type"); + default: + VMPANIC("未実装 xfer type"); } - // XXX バスエラーは? - if (rv < 0) { + if ((int64)r < 0) { + Error(r); return; } + CallAfter(DoneCallback, 5); +} + +// 1回の転送完了 +void +DMACDevice::DoneCallback(Event& ev) +{ + putlog(4, "DONE"); + // バスエラーでなければカウンタを更新 // XXX とりあえず。後で書く - switch (chan->GetMAC()) { + switch (xfer_chan->GetMAC()) { case DMAC::SCR_COUNT_UP: - chan->mar += 1; + xfer_chan->mar += 1; break; case DMAC::SCR_COUNT_DOWN: - chan->mar -= 1; + xfer_chan->mar -= 1; break; } - switch (chan->GetDAC()) { + switch (xfer_chan->GetDAC()) { case DMAC::SCR_COUNT_UP: - chan->dar += 1; + xfer_chan->dar += 1; break; case DMAC::SCR_COUNT_DOWN: - chan->dar -= 1; + xfer_chan->dar -= 1; break; } - chan->mtc--; - if (chan->mtc == 0) { + xfer_chan->mtc--; + if (xfer_chan->mtc == 0) { // 転送完了 - chan->active = false; - chan->SetINT(DMAC::CSR_COC); - } else { - // 次回の転送 - // XXX とりあえず 1us で1回の転送にする - event[ch].time = 1_usec; - event[ch].Start(); + xfer_chan->csr |= DMAC::CSR_COC; + xfer_chan->active = false; + ChangeInterrupt(); } -} -// メモリから 8ビットデバイスへ (パックなし動作) -int64 -DMACDevice::TransferM8toD8(DMAC::Channel *chan) -{ - uint64 data; - int64 rv; - - data = vm_phys_read_8(chan->mar); - if ((int64)data < 0) - return data; - rv = vm_phys_write_8(chan->dar, data); - if (rv < 0) - return rv; - - return 0; -} - -// 8ビットデバイスからメモリへ (パックなし動作) -int64 -DMACDevice::TransferD8toM8(DMAC::Channel *chan) -{ - uint64 data; - int64 rv; - - data = vm_phys_read_8(chan->dar); - if ((int64)data < 0) - return data; - rv = vm_phys_write_8(chan->mar, data); - if (rv < 0) - return rv; - - return 0; + CallAfter(StartCallback, 1); } // ソフトウェアアボート @@ -981,16 +1092,64 @@ DMACDevice::TransferD8toM8(DMAC::Channel void DMACDevice::AbortTransfer(DMAC::Channel *chan) { - int ch = chan->ch; - putlog(2, "CCR SAB ソフトウェアアボート"); // %1 を書き込むことで実際には SAB はセットされるが、ERR が立つと // SAB をクリアする動作のため、書き込んだ %1 が読めることはない。 // ここでは 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() +{ + DMAC::Channel *chan; + int top = -1; + uint8 prio = 255; + + // 割り込みを発生させているトッププライオリティのチャンネルを検索 + for (int ch = 0; ch < countof(dmac.chan); ch++) { + chan = &dmac.chan[ch]; + + if ((chan->ccr & DMAC::CCR_INT) && chan->IsINTR()) { + if (chan->priority < prio) { + prio = chan->priority; + top = ch; + } + } + } + + if (top >= 0) { + chan = &dmac.chan[top]; + if ((chan->csr & DMAC::CSR_ERR)) { + return chan->eiv; + } else { + return chan->niv; + } + } + // XXX 誰もいなかったら? + VMPANIC("InterruptAcknowledge no active channels?"); }