--- nono/vm/dmac.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/vm/dmac.cpp 2026/04/29 17:05:10 1.1.1.10 @@ -1,136 +1,238 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// DMAC (HD63450) // #include "dmac.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) +// グローバル参照用 +DMACDevice *gDMAC; + +// コンストラクタ DMACDevice::DMACDevice() + : inherited("DMAC") { - logname = "dmac"; - devname = "DMAC"; devaddr = baseaddr; devlen = 0x2000; + + 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"; + + 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; - struct DMAC::channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + gMPU->AddCycle(37); // InsideOut p.135 + + ch = offset / 0x40; + DMAC::Channel *chan = &dmac.chan[ch]; + int n = offset % 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 +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; - struct DMAC::channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + gMPU->AddCycle(31); // InsideOut p.135 + + ch = offset / 0x40; + DMAC::Channel *chan = &dmac.chan[ch]; + int n = offset % 0x40; switch (n) { case DMAC::CSR: - write_csr(ch, data); + chan->WriteCSR(data); + ChangeInterrupt(); break; case DMAC::CER: @@ -163,100 +268,130 @@ 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; + // 実効プライオリティの上位4bitは cpr に等しい + chan->priority = (chan->cpr << 4); 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 +403,17 @@ DMACDevice::Write8(uint32 addr, uint32 d } uint64 -DMACDevice::Peek8(uint32 addr) +DMACDevice::Peek(uint32 offset) { uint8 data; int ch; - ch = (addr - baseaddr) / 0x40; - struct DMAC::channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + ch = offset / 0x40; + const DMAC::Channel *chan = &dmac.chan[ch]; + int n = offset % 0x40; switch (n) { case DMAC::CSR: - data = chan->csr; + data = chan->PeekCSR(); break; case DMAC::CER: @@ -396,14 +531,625 @@ DMACDevice::Peek8(uint32 addr) } void -DMACDevice::write_csr(int ch, uint32 data) +DMACDevice::MonitorUpdate(Monitor *, TextScreen& screen) { - putlog(1, "write_csr[%d] <- $%02x 未実装書き込み", ch, data); + int x; + int y; + + screen.Clear(); + + y = 1; + screen.Puts(0, y++, "CSR"); + y += 2; + 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; + 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]; + int val; + x = 12 + ch * 17; + y = 0; + + // 地味だけど有効なチャンネルをハイライトしてみる + screen.Print(x, y++, (chan->active ? TA::Em : TA::Normal), + "#%d (%s)", chan->ch, chan->desc); + + // CSR + val = chan->PeekCSR(); + screen.Print(x, y++, "$%02x", val); + static const char * const csrname[] = { + "COC", "BTC", "NDT", "ERR", "ACT", "-", "PCT", "PCS", + }; + MonitorReg4(screen, x, y++, val >> 4, &csrname[0]); + MonitorReg4(screen, x, y++, val & 0xff, &csrname[4]); + + // CER + val = chan->cer & 0x1f; + const char *e; + if (val <= 0x11 && errnames[val]) { + e = errnames[val]; + } else { + e = "?"; + } + screen.Print(x, y, "$%02x", chan->cer); + if (val > 0) { + screen.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; + screen.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; + screen.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; + screen.Print(x, y++, "%d:%s", val, dcr_dps[val]); + + // DCR:PCL + screen.Print(x, y++, "XXX PCL"); + + // OCR:DIR + static const char * const ocr_dir[] = { + //1234567890123 + "MemoryToDevice", + "DeviceToMemory", + }; + val = (chan->ocr >> 7); + screen.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; + screen.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; + screen.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); + screen.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; + screen.Print(x, y++, "%d:%s", val, scr_xac[val]); + val = chan->scr & 3; + screen.Print(x, y++, "%d:%s", val, scr_xac[val]); + + // CCR + val = chan->ccr; + screen.Print(x, y++, "$%02x", val); + static const char * const ccrnames[] = { + "STR", "CNT", "HLT", "SAB", "INT", "-", "-", "-", + }; + MonitorReg4(screen, x, y++, (val >> 4), &ccrnames[0]); + MonitorReg4(screen, x, y++, (val & 0xff), &ccrnames[4]); + + // MTC + screen.Print(x, y++, "$%04x", chan->mtc); + // MAR + // X680x0 がターゲットなので24bit超える設定は目立たせる + if (chan->mar > 0xffffff) { + screen.Print(x, y++, TA::On, "$%08x", chan->mar); + } else { + screen.Print(x, y++, "$%06x", chan->mar); + } + // DAR + if (chan->dar > 0xffffff) { + screen.Print(x, y++, TA::On, "$%08x", chan->dar); + } else { + screen.Print(x, y++, "$%06x", chan->dar); + } + // BTC + screen.Print(x, y++, "$%04x", chan->btc); + // BAR + if (chan->bar > 0xffffff) { + screen.Print(x, y++, TA::On, "$%08x", chan->bar); + } else { + screen.Print(x, y++, "$%06x", chan->bar); + } + + // NIV + screen.Print(x, y++, "$%02x", chan->niv); + // EIV + screen.Print(x, y++, "$%02x", chan->eiv); + + // MFC + screen.Print(x, y++, "$%02x", chan->mfc); + // CPR + screen.Print(x, y++, "$%02x", chan->cpr); + // DFC + screen.Print(x, y++, "$%02x", chan->dfc); + // BFC + screen.Print(x, y++, "$%02x", chan->bfc); + + if (ch == 3) { + screen.Print(x - 5, y++, "GCR: $%02x", dmac.gcr); + } + } } +// 4ビット分を表示する。MonitorUpdate の下請け void -DMACDevice::write_ccr(int ch, uint32 data) +DMACDevice::MonitorReg4(TextScreen& screen, + int x, int y, uint32 reg, const char * const *names) +{ + for (int i = 0; i < 4; 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]); + } + } +} + +/*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 { - putlog(1, "write_ccr[%d] <- $%02x 未実装書き込み", ch, data); + 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->putlogf(4, lstr("#%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->putlogf(0, lstr("CCR HLT 未実装")); + } + + // CNT (Continue Operation) + if ((data & DMAC::CCR_CNT)) { + parent->putlogf(0, lstr("CCR CNT 未実装")); + } + + // STR (Start Operation) + if ((data & DMAC::CCR_STR)) { + StartTransfer(); + } +} + +// 転送開始 (チャンネル側) +void +DMAC::Channel::StartTransfer() +{ + // 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: + VMPANIC("REQG_AUTO_LIM 未実装"); + break; + + case DMAC::OCR_REQG_AUTO_MAX: + // 自発的に転送を開始する + parent->StartTransfer(); + break; + + case DMAC::OCR_REQG_EXTERNAL: + case DMAC::OCR_REQG_AUTOFIRST: + VMPANIC("未実装 REQG"); + break; + + default: + __unreachable(); + } +} + +// 転送開始 (デバイス側) +// (チャンネルが転送開始する際にデバイス側の private 変数(event)が必要に +// なるため、プロキシしている) +void +DMACDevice::StartTransfer() +{ + if (event.IsRunning() == false) { + CallAfter(StartCallback, 0); + } } +// 転送開始 +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]; + + // 実効プライオリティを同順位の最後に回す + 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ビットサイズ + VMPANIC("未実装 xfer type"); + + case 3: // 8ビットポート、8ビットサイズ、パックなし動作 + 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ビットサイズ、パックなし動作 + default: + VMPANIC("未実装 xfer type"); + } + + if ((int64)r < 0) { + Error(r); + return; + } + + CallAfter(DoneCallback, 5); +} + +// 1回の転送完了 +void +DMACDevice::DoneCallback(Event& ev) +{ + putlog(4, "DONE"); + + // バスエラーでなければカウンタを更新 + // XXX とりあえず。後で書く + switch (xfer_chan->GetMAC()) { + case DMAC::SCR_COUNT_UP: + xfer_chan->mar += 1; + break; + case DMAC::SCR_COUNT_DOWN: + xfer_chan->mar -= 1; + break; + } + switch (xfer_chan->GetDAC()) { + case DMAC::SCR_COUNT_UP: + xfer_chan->dar += 1; + break; + case DMAC::SCR_COUNT_DOWN: + xfer_chan->dar -= 1; + break; + } + + xfer_chan->mtc--; + if (xfer_chan->mtc == 0) { + // 転送完了 + xfer_chan->csr |= DMAC::CSR_COC; + xfer_chan->active = false; + ChangeInterrupt(); + } + + CallAfter(StartCallback, 1); +} + +// ソフトウェアアボート +// (イベントを持つのでこれはデバイスクラスのメソッド) +void +DMACDevice::AbortTransfer(DMAC::Channel *chan) +{ + putlog(2, "CCR SAB ソフトウェアアボート"); + + // %1 を書き込むことで実際には SAB はセットされるが、ERR が立つと + // SAB をクリアする動作のため、書き込んだ %1 が読めることはない。 + // ここでは SAB ビットのセットを省略。 + + chan->active = false; + 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() +{ + 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?"); +}