--- nono/vm/dmac.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/dmac.cpp 2026/04/29 17:04:44 1.1.1.6 @@ -1,9 +1,16 @@ // // 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; DMACDevice::DMACDevice() { @@ -11,126 +18,207 @@ DMACDevice::DMACDevice() devname = "DMAC"; devaddr = baseaddr; devlen = 0x2000; + + monitor_size = nnSize(80, 30); + + for (int i = 0; i < countof(dmac.chan); i++) { + dmac.chan[i].ch = i; + dmac.chan[i].parent = this; + } + dmac.chan[0].desc = "FD"; + dmac.chan[1].desc = "HD"; + 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; + } } 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; - struct DMAC::channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + gMPU->AddCycle(37); // InsideOut p.135 + + ch = addr / 0x40; + DMAC::Channel *chan = &dmac.chan[ch]; + int n = addr % 0x40; switch (n) { case DMAC::CSR: - data = chan->csr; + data = chan->PeekCSR(); + putlog(4, "#%d CSR -> $%02x", ch, data); break; case DMAC::CER: data = chan->cer; + putlog(4, "#%d CER -> $%02x", ch, data); break; case DMAC::DCR: data = chan->dcr; + putlog(4, "#%d DCR -> $%02x", ch, data); break; case DMAC::OCR: data = chan->ocr; + putlog(4, "#%d OCR -> $%02x", ch, data); break; case DMAC::SCR: data = chan->scr; + putlog(4, "#%d SCR -> $%02x", ch, data); break; case DMAC::CCR: data = chan->ccr; + putlog(4, "#%d CCR -> $%02x", ch, data); break; case DMAC::MTC: data = chan->mtc >> 8; + putlog(4, "#%d MTC:H -> $%02x", ch, data); break; case DMAC::MTC + 1: data = chan->mtc & 0xff; + putlog(4, "#%d MTC:L -> $%02x", ch, data); break; case DMAC::MAR: data = chan->mar >> 24; + putlog(4, "#%d MAR:0 -> $%02x", ch, data); break; case DMAC::MAR + 1: data = (chan->mar >> 16) & 0xff; + putlog(4, "#%d MAR:1 -> $%02x", ch, data); break; case DMAC::MAR + 2: data = (chan->mar >> 8) & 0xff; + putlog(4, "#%d MAR:2 -> $%02x", ch, data); break; case DMAC::MAR + 3: data = chan->mar & 0xff; + putlog(4, "#%d MAR:3 -> $%02x", ch, data); break; case DMAC::DAR: data = chan->dar >> 24; + putlog(4, "#%d DAR:0 -> $%02x", ch, data); break; case DMAC::DAR + 1: data = (chan->dar >> 16) & 0xff; + putlog(4, "#%d DAR:1 -> $%02x", ch, data); break; case DMAC::DAR + 2: data = (chan->dar >> 8) & 0xff; + putlog(4, "#%d DAR:2 -> $%02x", ch, data); break; case DMAC::DAR + 3: data = chan->dar & 0xff; + putlog(4, "#%d DAR:3 -> $%02x", ch, data); break; case DMAC::BTC: data = chan->btc >> 8; + putlog(4, "#%d BTC:H -> $%02x", ch, data); break; case DMAC::BTC + 1: data = chan->btc & 0xff; + putlog(4, "#%d BTC:L -> $%02x", ch, data); break; case DMAC::BAR: data = chan->bar >> 24; + putlog(4, "#%d BAR:0 -> $%02x", ch, data); break; case DMAC::BAR + 1: data = (chan->bar >> 16) & 0xff; + putlog(4, "#%d BAR:1 -> $%02x", ch, data); break; case DMAC::BAR + 2: data = (chan->bar >> 8) & 0xff; + putlog(4, "#%d BAR:2 -> $%02x", ch, data); break; case DMAC::BAR + 3: data = chan->bar & 0xff; + putlog(4, "#%d BAR:3 -> $%02x", ch, data); break; case DMAC::NIV: data = chan->niv; + putlog(4, "#%d NIV -> $%02x", ch, data); break; case DMAC::EIV: data = chan->eiv; + putlog(4, "#%d EIV -> $%02x", ch, data); break; case DMAC::MFC: data = chan->mfc; + putlog(4, "#%d MFC -> $%02x", ch, data); break; case DMAC::CPR: data = chan->cpr; + putlog(4, "#%d CPR -> $%02x", ch, data); break; case DMAC::DFC: data = chan->dfc; + putlog(4, "#%d DFC -> $%02x", ch, data); break; case DMAC::BFC: data = chan->bfc; + putlog(4, "#%d BFC -> $%02x", ch, data); break; case DMAC::GCR: if (ch == 3) { data = dmac.gcr; + putlog(4, "GCR -> $%02x", data); break; } else { data = 0xff; @@ -146,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; - struct DMAC::channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + gMPU->AddCycle(31); // InsideOut p.135 + + ch = addr / 0x40; + DMAC::Channel *chan = &dmac.chan[ch]; + int n = addr % 0x40; switch (n) { case DMAC::CSR: - write_csr(ch, data); + chan->WriteCSR(data); + ChangeInterrupt(); break; case DMAC::CER: @@ -163,100 +254,128 @@ DMACDevice::Write8(uint32 addr, uint32 d break; case DMAC::DCR: + putlog(4, "#%d DCR <- $%02x", ch, data); chan->dcr = data & 0xfb; break; case DMAC::OCR: + putlog(4, "#%d OCR <- $%02x", ch, data); chan->ocr = data; break; case DMAC::SCR: + putlog(4, "#%d SCR <- $%02x", ch, data); chan->scr = data & 0x0f; break; case DMAC::CCR: - write_ccr(ch, data); + putlog(4, "#%d CCR <- $%02x", ch, data); + chan->WriteCCR(data); + ChangeInterrupt(); break; case DMAC::MTC: chan->mtc = (chan->mtc & 0x00ff) | (data << 8); + putlog(4, "#%d MTC:H <- $%02x (MTC=$%04x)", ch, data, chan->mtc); break; case DMAC::MTC + 1: chan->mtc = (chan->mtc & 0xff00) | data; + putlog(4, "#%d MTC:L <- $%02x (MTC=$%04x)", ch, data, chan->mtc); break; case DMAC::MAR: chan->mar = (chan->mar & 0x00ffffff) | (data << 24); + putlog(4, "#%d MAR:0 <- $%02x (MAR=$%08x)", ch, data, chan->mar); break; case DMAC::MAR + 1: chan->mar = (chan->mar & 0xff00ffff) | (data << 16); + putlog(4, "#%d MAR:1 <- $%02x (MAR=$%08x)", ch, data, chan->mar); break; case DMAC::MAR + 2: chan->mar = (chan->mar & 0xffff00ff) | (data << 8); + putlog(4, "#%d MAR:2 <- $%02x (MAR=$%08x)", ch, data, chan->mar); break; case DMAC::MAR + 3: chan->mar = (chan->mar & 0xffffff00) | data; + putlog(4, "#%d MAR:3 <- $%02x (MAR=$%08x)", ch, data, chan->mar); break; case DMAC::DAR: chan->dar = (chan->dar & 0x00ffffff) | (data << 24); + putlog(4, "#%d DAR:0 <- $%02x (DAR=$%08x)", ch, data, chan->dar); break; case DMAC::DAR + 1: chan->dar = (chan->dar & 0xff00ffff) | (data << 16); + putlog(4, "#%d DAR:1 <- $%02x (DAR=$%08x)", ch, data, chan->dar); break; case DMAC::DAR + 2: chan->dar = (chan->dar & 0xffff00ff) | (data << 8); + putlog(4, "#%d DAR:2 <- $%02x (DAR=$%08x)", ch, data, chan->dar); break; case DMAC::DAR + 3: chan->dar = (chan->dar & 0xffffff00) | data; + putlog(4, "#%d DAR:3 <- $%02x (DAR=$%08x)", ch, data, chan->dar); break; case DMAC::BTC: chan->btc = (chan->btc & 0x00ff) | (data << 8); + putlog(4, "#%d BTC:H <- $%02x (BTC=$%04x)", ch, data, chan->btc); break; case DMAC::BTC + 1: chan->btc = (chan->btc & 0xff00) | data; + putlog(4, "#%d BTC:L <- $%02x (BTC=$%04x)", ch, data, chan->btc); break; case DMAC::BAR: chan->bar = (chan->bar & 0x00ffffff) | (data << 24); + putlog(4, "#%d BAR:0 <- $%02x (BAR=$%08x)", ch, data, chan->bar); break; case DMAC::BAR + 1: chan->bar = (chan->bar & 0xff00ffff) | (data << 16); + putlog(4, "#%d BAR:1 <- $%02x (BAR=$%08x)", ch, data, chan->bar); break; case DMAC::BAR + 2: chan->bar = (chan->bar & 0xffff00ff) | (data << 8); + putlog(4, "#%d BAR:2 <- $%02x (BAR=$%08x)", ch, data, chan->bar); break; case DMAC::BAR + 3: chan->bar = (chan->bar & 0xffffff00) | data; + putlog(4, "#%d BAR:3 <- $%02x (BAR=$%08x)", ch, data, chan->bar); break; case DMAC::NIV: + putlog(4, "#%d NIV <- $%02x", ch, data); chan->niv = data; break; case DMAC::EIV: + putlog(4, "#%d EIV <- $%02x", ch, data); chan->eiv = data; break; case DMAC::MFC: + putlog(4, "#%d MFC <- $%02x", ch, data); chan->mfc = data & 7; break; case DMAC::CPR: + putlog(4, "#%d CPR <- $%02x", ch, data); chan->cpr = data & 3; break; case DMAC::DFC: + putlog(4, "#%d DFC <- $%02x", ch, data); chan->dfc = data & 7; break; case DMAC::BFC: + putlog(4, "#%d BFC <- $%02x", ch, data); chan->bfc = data & 7; break; case DMAC::GCR: if (ch == 3) { + putlog(4, "GCR <- $%02x", data); dmac.gcr = data; } break; @@ -268,17 +387,17 @@ DMACDevice::Write8(uint32 addr, uint32 d } uint64 -DMACDevice::Peek8(uint32 addr) +DMACDevice::Peek(uint32 addr) { uint8 data; int ch; - ch = (addr - baseaddr) / 0x40; - struct DMAC::channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + ch = addr / 0x40; + const DMAC::Channel *chan = &dmac.chan[ch]; + int n = addr % 0x40; switch (n) { case DMAC::CSR: - data = chan->csr; + data = chan->PeekCSR(); break; case DMAC::CER: @@ -396,14 +515,540 @@ DMACDevice::Peek8(uint32 addr) } void -DMACDevice::write_csr(int ch, uint32 data) +DMACDevice::MonitorUpdate(TextScreen& monitor) +{ + int x; + int y; + + monitor.Clear(); + + y = 1; + monitor.Puts(0, y++, "CSR"); + y += 2; + 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.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]; + int val; + x = 12 + ch * 17; + y = 0; + + // 地味だけど有効なチャンネルをハイライトしてみる + monitor.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); + static const char * const csrname[] = { + "COC", "BTC", "NDT", "ERR", "ACT", "---", "PCT", "PCS", + }; + MonitorReg4(monitor, x, y++, val >> 4, &csrname[0]); + MonitorReg4(monitor, x, y++, val & 0xff, &csrname[4]); + + // CER + val = chan->cer & 0x1f; + const char *e; + if (val <= 0x11 && errnames[val]) { + e = errnames[val]; + } else { + e = "?"; + } + monitor.Print(x, y, "$%02x", chan->cer); + if (val > 0) { + monitor.Print(x + 3, y, ":%s", e); + } + y++; + + // DCR:XRM + static const char * const dcr_xrm[] = { + //1234567890123 + "Burst", + "undefined", + "CycleW/O Hold", + "CycleWithHold", + }; + val = chan->dcr >> 6; + monitor.Print(x, y++, "%d:%s", val, dcr_xrm[val]); + + // DCR:DTYP + static const char * const dcr_dtyp[] = { + //1234567890123 + "68000", + "6800", + "ACK", + "ACK+READY", + }; + val = (chan->dcr >> 4) & 3; + monitor.Print(x, y++, "%d:%s", val, dcr_dtyp[val]); + + // DCR:DPS + static const char * const dcr_dps[] = { + //1234567890123 + "8bit", + "16bit", + }; + val = (chan->dcr >> 3) & 1; + monitor.Print(x, y++, "%d:%s", val, dcr_dps[val]); + + // DCR:PCL + monitor.Print(x, y++, "XXX PCL"); + + // OCR:DIR + static const char * const ocr_dir[] = { + //1234567890123 + "MemoryToDevice", + "DeviceToMemory", + }; + val = (chan->ocr >> 7); + monitor.Print(x, y++, "%d:%s", val, ocr_dir[val]); + + // OCR:SIZE + static const char * const ocr_size[] = { + //1234567890123 + "8bit", + "16bit", + "32bit", + "8bit Unpacked", + }; + val = (chan->ocr >> 4) & 3; + monitor.Print(x, y++, "%d:%s", val, ocr_size[val]); + + // OCR:CHAIN + static const char * const ocr_chain[] = { + //1234567890123 + "NoChain", + "undefined", + "ArrayChain", + "LinkArrayChain", + }; + val = (chan->ocr >> 2) & 3; + monitor.Print(x, y++, "%d:%s", val, ocr_chain[val]); + + // OCR:REQG + static const char * const ocr_reqg[] = { + //1234567890123 + "AutoReq(Limit)", + "AutoReq(Max)", + "ExternalReq", + "AutoThenExt", + }; + val = (chan->ocr & 3); + monitor.Print(x, y++, "%d:%s", val, ocr_reqg[val]); + + // SCR:MAC + static const char * const scr_xac[] = { + //1234567890123 + "NoCount", + "CountUp", + "CountDown", + "undefined", + }; + val = (chan->scr >> 2) & 3; + monitor.Print(x, y++, "%d:%s", val, scr_xac[val]); + val = chan->scr & 3; + monitor.Print(x, y++, "%d:%s", val, scr_xac[val]); + + // CCR + val = chan->ccr; + monitor.Print(x, y++, "$%02x", val); + static const char * const ccrnames[] = { + "STR", "CNT", "HLT", "SAB", "INT", "---", "---", "---", + }; + MonitorReg4(monitor, x, y++, (val >> 4), &ccrnames[0]); + MonitorReg4(monitor, x, y++, (val & 0xff), &ccrnames[4]); + + // MTC + monitor.Print(x, y++, "$%04x", chan->mtc); + // MAR + // X680x0 がターゲットなので24bit超える設定は目立たせる + if (chan->mar > 0xffffff) { + monitor.Print(x, y++, TA::On, "$%08x", chan->mar); + } else { + monitor.Print(x, y++, "$%06x", chan->mar); + } + // DAR + if (chan->dar > 0xffffff) { + monitor.Print(x, y++, TA::On, "$%08x", chan->dar); + } else { + monitor.Print(x, y++, "$%06x", chan->dar); + } + // BTC + monitor.Print(x, y++, "$%04x", chan->btc); + // BAR + if (chan->bar > 0xffffff) { + monitor.Print(x, y++, TA::On, "$%08x", chan->bar); + } else { + monitor.Print(x, y++, "$%06x", chan->bar); + } + + // NIV + monitor.Print(x, y++, "$%02x", chan->niv); + // EIV + monitor.Print(x, y++, "$%02x", chan->eiv); + + // MFC + monitor.Print(x, y++, "$%02x", chan->mfc); + // CPR + monitor.Print(x, y++, "$%02x", chan->cpr); + // DFC + monitor.Print(x, y++, "$%02x", chan->dfc); + // BFC + monitor.Print(x, y++, "$%02x", chan->bfc); + + if (ch == 3) { + monitor.Print(x - 5, y++, "GCR: $%02x", dmac.gcr); + } + } +} + +// 4ビット分を表示する。MonitorUpdate の下請け +void +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)); + monitor.Puts(x + i * 4, y, TA::OnOff(b), names[i]); + } +} + +/*static*/ const char * const +DMACDevice::errnames[] = { + //12345678901 + "", // $00 No Error + "ConfigErr", // $01 + "OperTiming", // $02 + NULL, // $03 + NULL, // $04 + "AddrErrInMAR", // $05 + "AddrErrInDAR", // $06 + "AddrErrInBAR", // $07 + NULL, // $08 + "BusErrInMAR", // $09 + "BusErrInDAR", // $0a + "BusErrInBAR", // $0b + NULL, // $0c + "CntErrInMTC", // $0d + NULL, // $0e + "CntErrInBTC", // $0f + "ExternAbort", // $10 + "SoftAbort", // $11 +}; + +// Read と Peek とそれ以外からも呼ばれるので副作用を持たせてはいけない。 +uint8 +DMAC::Channel::PeekCSR() const +{ + uint32 val; + + val = csr & 0xf0; + if (active) { + val |= CSR_ACT; + } + // PCT は PCL が High から Low に変わるとセット + if (prev_pcl == true && pcl == false) { + val |= CSR_PCT; + } + if (pcl) { + val |= CSR_PCS; + } + return val; +} + +// CSR への書き込み。 +// CSR への書き込みによって割り込み信号線が変化するかも知れないので +// この後呼び出し側が ChangeInterrupt() を呼ぶこと。 +void +DMAC::Channel::WriteCSR(uint32 data) +{ + // 上位4ビットは %1 の書き込みでクリア + csr &= ~(data & 0xf0); + + // 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()); +} + +// CCR への書き込み。 +// CCR への書き込みによって割り込み信号線が変化するかも知れないので +// この後呼び出し側が ChangeInterrupt() を呼ぶこと。 +void +DMAC::Channel::WriteCCR(uint32 data) +{ + // 割り込み許可ビットは常に反映 + ccr &= ~DMAC::CCR_INT; + ccr |= (data & DMAC::CCR_INT); + + // SAB (Software Abort) + if ((data & DMAC::CCR_SAB)) { + // イベントを触るため親デバイスで処理する + parent->AbortTransfer(this); + } + + // HLT (Halt Operation) + if ((data & DMAC::CCR_HLT)) { + parent->putlog(0, "CCR HLT 未実装"); + } + + // CNT (Continue Operation) + if ((data & DMAC::CCR_CNT)) { + parent->putlog(0, "CCR CNT 未実装"); + } + + // STR (Start Operation) + if ((data & DMAC::CCR_STR)) { + StartTransfer(); + } +} + +// 転送開始 (チャンネル側) +void +DMAC::Channel::StartTransfer() { - putlog(1, "write_csr[%d] <- $%02x 未実装書き込み", ch, data); + // CSR の COC,BTC,NDT,ERR が立ってたら開始しない + if ((csr & 0xf0) != 0) { + cer = CER_CONFIG; // ? + csr |= CSR_ERR; + return; + } + + active = true; + + // XXX CER をどこかでクリアしないといけないようだがどこだ? + cer = 0; + + // 転送方法は DPS(1bit) と SIZE(2bit) で合計 8通り。 + // (その中にさらに転送方向が2通りずつあるけど) + // + // xfertype DPS SIZE + // 0 8bit 8bit packed + // 1 8bit 16bit + // 2 8bit 32bit + // 3 8bit 8bit + // 4 16bit 8bit packed + // 5 16bit 16bit + // 6 16bit 32bit + // 7 16bit 8bit + xfertype = (GetDPS() << 2) | GetSIZE(); + + // 転送モード + switch (GetREQG()) { + case DMAC::OCR_REQG_AUTO_LIM: + PANIC("REQG_AUTO_LIM 未実装"); + break; + + case DMAC::OCR_REQG_AUTO_MAX: + // 自発的に転送を開始する + parent->StartTransfer(this); + break; + + case DMAC::OCR_REQG_EXTERNAL: + case DMAC::OCR_REQG_AUTOFIRST: + PANIC("未実装 REQG"); + break; + + default: + __unreachable(); + } } +// 転送開始 (デバイス側) +// (チャンネルが転送開始する際にデバイス側の private 変数(event[])が必要に +// なるため、プロキシしている) void -DMACDevice::write_ccr(int ch, uint32 data) +DMACDevice::StartTransfer(DMAC::Channel *chan) { - putlog(1, "write_ccr[%d] <- $%02x 未実装書き込み", ch, data); + Transfer(event[chan->ch]); } +// 転送 +// (イベントを持つのでこれはデバイスクラスのメソッド) +void +DMACDevice::Transfer(Event& ev) +{ + int& ch = ev.code; + DMAC::Channel *chan = &dmac.chan[ch]; + int64 rv; + + putlog(4, "#%d Transfer (MTC=$%04x)", ch, chan->mtc); + + rv = 0; + switch (chan->xfertype) { + case 0: // 8ビットポート、8ビットサイズ、パック動作 + + case 1: // 8ビットポート、16ビットサイズ + + case 2: // 8ビットポート、32ビットサイズ + PANIC("未実装 xfer type"); + + case 3: // 8ビットポート、8ビットサイズ、パックなし動作 + if (chan->GetDIR() == DMAC::OCR_DIR_MtoD) { + rv = TransferM8toD8(chan); + } else { + rv = TransferD8toM8(chan); + } + break; + + case 4: // 16ビットポート、8ビットサイズ、パック動作 + case 5: // 16ビットポート、16ビットサイズ + case 6: // 16ビットポート、32ビットサイズ + case 7: // 16ビットポート、8ビットサイズ、パックなし動作 + PANIC("未実装 xfer type"); + } + + // XXX バスエラーは? + if (rv < 0) { + return; + } + + // バスエラーでなければカウンタを更新 + // XXX とりあえず。後で書く + switch (chan->GetMAC()) { + case DMAC::SCR_COUNT_UP: + chan->mar += 1; + break; + case DMAC::SCR_COUNT_DOWN: + chan->mar -= 1; + break; + } + switch (chan->GetDAC()) { + case DMAC::SCR_COUNT_UP: + chan->dar += 1; + break; + case DMAC::SCR_COUNT_DOWN: + chan->dar -= 1; + break; + } + + chan->mtc--; + if (chan->mtc == 0) { + // 転送完了 + chan->csr |= DMAC::CSR_COC; + ChangeInterrupt(); + } else { + // 次回の転送 + // XXX とりあえず 1us で1回の転送にする + ev.time = 1_usec; + ev.Start(); + } +} + +// メモリから 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; +} + +// ソフトウェアアボート +// (イベントを持つのでこれはデバイスクラスのメソッド) +void +DMACDevice::AbortTransfer(DMAC::Channel *chan) +{ + int ch = chan->ch; + + putlog(2, "CCR SAB ソフトウェアアボート"); + + // %1 を書き込むことで実際には SAB はセットされるが、ERR が立つと + // SAB をクリアする動作のため、書き込んだ %1 が読めることはない。 + // ここでは SAB ビットのセットを省略。 + + event[ch].Stop(); + chan->cer = DMAC::CER_SOFT_ABORT; + // 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?"); +}