--- nono/vm/dmac.cpp 2026/04/29 17:04:35 1.1.1.3 +++ nono/vm/dmac.cpp 2026/04/29 17:05:14 1.1.1.11 @@ -4,186 +4,248 @@ // Licensed under nono-license.txt // +// +// DMAC (HD63450) +// + #include "dmac.h" +#include "adpcm.h" #include "bus.h" -#include "mpu680x0.h" -#include "mystring.h" +#include "fdc.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; - } - 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; + for (int i = 0; i < channel.size(); i++) { + channel[i].ch = i; } + channel[0].desc = "FD"; + channel[1].desc = "HD"; + channel[2].desc = "User"; + channel[3].desc = "ADPCM"; + + // PCL 信号線 + // #0 はスーパーインポーズしてなければ常に Low + channel[0].pcl_pin = false; + channel[0].pcl_prev = false; + // #1 はプルアップされているので常に High + channel[1].pcl_pin = true; + channel[1].pcl_prev = true; + // #2 は外部スロット用だが未接続なので High + channel[2].pcl_pin = true; + channel[2].pcl_prev = true; + // #3 は未対応 + + event.Regist("DMAC"); + + monitor.func = ToMonitorCallback(&DMACDevice::MonitorUpdate); + monitor.SetSize(80, 34); + monitor.Regist(ID_MONITOR_DMAC); } +// デストラクタ DMACDevice::~DMACDevice() { + gDMAC = NULL; +} + +// リセット +void +DMACDevice::ResetHard(bool poweron) +{ + putlog(2, "Reset"); + + // 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. + + gcr = 0; + for (int ch = 0; ch < channel.size(); ch++) { + DMACChan *chan = &channel[ch]; + chan->SetDCR(0); + chan->SetOCR(0); + chan->SetSCR(0); + chan->SetCCR(0); + chan->csr = 0; + chan->active = false; + chan->cpr = 0; + chan->cer = 0; + + chan->niv = 0x0f; + chan->eiv = 0x0f; + + chan->priority = 0; + } + + // イベントを停止 + event.SetName("DMAC"); + gScheduler->StopEvent(event); } uint64 -DMACDevice::Read(uint32 addr) +DMACDevice::Read8(uint32 addr) { uint8 data; - int ch; - ch = addr / 0x40; - DMAC::Channel *chan = &dmac.chan[ch]; - int n = addr % 0x40; + gMPU->AddCycle(37); // InsideOut p.135 + + uint32 offset = addr & 0xff; + int ch = offset / 0x40; + int n = offset % 0x40; + DMACChan *chan = &channel[ch]; + switch (n) { case DMAC::CSR: - data = chan->PeekCSR(); - putlog(4, "#%d CSR -> $%02x", ch, data); + data = chan->GetCSR(); + putlog(3, "#%d CSR -> $%02x", ch, data); break; case DMAC::CER: data = chan->cer; - putlog(4, "#%d CER -> $%02x", ch, data); + putlog(3, "#%d CER -> $%02x", ch, data); break; case DMAC::DCR: - data = chan->dcr; - putlog(4, "#%d DCR -> $%02x", ch, data); + data = chan->GetDCR(); + putlog(3, "#%d DCR -> $%02x", ch, data); break; case DMAC::OCR: - data = chan->ocr; - putlog(4, "#%d OCR -> $%02x", ch, data); + data = chan->GetOCR(); + putlog(3, "#%d OCR -> $%02x", ch, data); break; case DMAC::SCR: - data = chan->scr; - putlog(4, "#%d SCR -> $%02x", ch, data); + data = chan->GetSCR(); + putlog(3, "#%d SCR -> $%02x", ch, data); break; case DMAC::CCR: - data = chan->PeekCCR(); - putlog(4, "#%d CCR -> $%02x", ch, data); + data = chan->GetCCR(); + putlog(3, "#%d CCR -> $%02x", ch, data); break; case DMAC::MTC: data = chan->mtc >> 8; - putlog(4, "#%d MTC:H -> $%02x", ch, data); + putlog(3, "#%d MTC:H -> $%02x", ch, data); break; case DMAC::MTC + 1: data = chan->mtc & 0xff; - putlog(4, "#%d MTC:L -> $%02x", ch, data); + putlog(3, "#%d MTC:L -> $%02x", ch, data); break; case DMAC::MAR: data = chan->mar >> 24; - putlog(4, "#%d MAR:0 -> $%02x", ch, data); + putlog(3, "#%d MAR:0 -> $%02x", ch, data); break; case DMAC::MAR + 1: data = (chan->mar >> 16) & 0xff; - putlog(4, "#%d MAR:1 -> $%02x", ch, data); + putlog(3, "#%d MAR:1 -> $%02x", ch, data); break; case DMAC::MAR + 2: data = (chan->mar >> 8) & 0xff; - putlog(4, "#%d MAR:2 -> $%02x", ch, data); + putlog(3, "#%d MAR:2 -> $%02x", ch, data); break; case DMAC::MAR + 3: data = chan->mar & 0xff; - putlog(4, "#%d MAR:3 -> $%02x", ch, data); + putlog(3, "#%d MAR:3 -> $%02x", ch, data); break; case DMAC::DAR: data = chan->dar >> 24; - putlog(4, "#%d DAR:0 -> $%02x", ch, data); + putlog(3, "#%d DAR:0 -> $%02x", ch, data); break; case DMAC::DAR + 1: data = (chan->dar >> 16) & 0xff; - putlog(4, "#%d DAR:1 -> $%02x", ch, data); + putlog(3, "#%d DAR:1 -> $%02x", ch, data); break; case DMAC::DAR + 2: data = (chan->dar >> 8) & 0xff; - putlog(4, "#%d DAR:2 -> $%02x", ch, data); + putlog(3, "#%d DAR:2 -> $%02x", ch, data); break; case DMAC::DAR + 3: data = chan->dar & 0xff; - putlog(4, "#%d DAR:3 -> $%02x", ch, data); + putlog(3, "#%d DAR:3 -> $%02x", ch, data); break; case DMAC::BTC: data = chan->btc >> 8; - putlog(4, "#%d BTC:H -> $%02x", ch, data); + putlog(3, "#%d BTC:H -> $%02x", ch, data); break; case DMAC::BTC + 1: data = chan->btc & 0xff; - putlog(4, "#%d BTC:L -> $%02x", ch, data); + putlog(3, "#%d BTC:L -> $%02x", ch, data); break; case DMAC::BAR: data = chan->bar >> 24; - putlog(4, "#%d BAR:0 -> $%02x", ch, data); + putlog(3, "#%d BAR:0 -> $%02x", ch, data); break; case DMAC::BAR + 1: data = (chan->bar >> 16) & 0xff; - putlog(4, "#%d BAR:1 -> $%02x", ch, data); + putlog(3, "#%d BAR:1 -> $%02x", ch, data); break; case DMAC::BAR + 2: data = (chan->bar >> 8) & 0xff; - putlog(4, "#%d BAR:2 -> $%02x", ch, data); + putlog(3, "#%d BAR:2 -> $%02x", ch, data); break; case DMAC::BAR + 3: data = chan->bar & 0xff; - putlog(4, "#%d BAR:3 -> $%02x", ch, data); + putlog(3, "#%d BAR:3 -> $%02x", ch, data); break; case DMAC::NIV: data = chan->niv; - putlog(4, "#%d NIV -> $%02x", ch, data); + putlog(3, "#%d NIV -> $%02x", ch, data); break; case DMAC::EIV: data = chan->eiv; - putlog(4, "#%d EIV -> $%02x", ch, data); + putlog(3, "#%d EIV -> $%02x", ch, data); break; case DMAC::MFC: data = chan->mfc; - putlog(4, "#%d MFC -> $%02x", ch, data); + putlog(3, "#%d MFC -> $%02x", ch, data); break; case DMAC::CPR: data = chan->cpr; - putlog(4, "#%d CPR -> $%02x", ch, data); + putlog(3, "#%d CPR -> $%02x", ch, data); break; case DMAC::DFC: data = chan->dfc; - putlog(4, "#%d DFC -> $%02x", ch, data); + putlog(3, "#%d DFC -> $%02x", ch, data); break; case DMAC::BFC: data = chan->bfc; - putlog(4, "#%d BFC -> $%02x", ch, data); + putlog(3, "#%d BFC -> $%02x", ch, data); break; case DMAC::GCR: if (ch == 3) { - data = dmac.gcr; - putlog(4, "GCR -> $%02x", data); + data = gcr; + putlog(3, "GCR -> $%02x", data); break; } else { data = 0xff; @@ -199,16 +261,151 @@ DMACDevice::Read(uint32 addr) } uint64 -DMACDevice::Write(uint32 addr, uint32 data) +DMACDevice::Read16(uint32 addr) { - int ch; + uint16 data; + + gMPU->AddCycle(37); // InsideOut p.135 + + uint32 offset = addr & 0xff; + int ch = offset / 0x40; + int n = offset % 0x40; + DMACChan *chan = &channel[ch]; + + switch (n) { + case DMAC::CSR: // CSR | CER + data = chan->GetCSR() << 8; + data |= chan->cer; + putlog(3, "#%d CSR:CER -> $%04x", ch, data); + break; + + case DMAC::DCR: // DCR | OCR + data = chan->GetDCR() << 8; + data |= chan->GetOCR(); + putlog(3, "#%d DCR:OCR -> $%04x", ch, data); + break; + + case DMAC::SCR: // SCR | CCR + data = chan->GetSCR() << 8; + data |= chan->GetCCR(); + putlog(3, "#%d SCR:CCR -> $%04x", ch, data); + break; + + case DMAC::MTC: + data = chan->mtc; + putlog(3, "#%d MTC -> $%04x", ch, data); + break; + + case DMAC::MAR: + data = chan->mar >> 16; + putlog(3, "#%d MAR:H -> $%04x", ch, data); + break; + case DMAC::MAR + 2: + data = chan->mar & 0xffff; + putlog(3, "#%d MAR:L -> $%04x", ch, data); + break; + + case DMAC::DAR: + data = chan->dar >> 16; + putlog(3, "#%d DAR:H -> $%04x", ch, data); + break; + case DMAC::DAR + 2: + data = chan->dar & 0xffff; + putlog(3, "#%d DAR:L -> $%04x", ch, data); + break; + + case DMAC::BTC: + data = chan->btc; + putlog(3, "#%d BTC -> $%04x", ch, data); + break; + + case DMAC::BAR: + data = chan->bar >> 16; + putlog(3, "#%d BAR:H -> $%04x", ch, data); + break; + case DMAC::BAR + 2: + data = chan->bar & 0xffff; + putlog(3, "#%d BAR:L -> $%04x", ch, data); + break; + + case DMAC::NIV: + data = chan->niv | 0xff00; + putlog(3, "#%d NIV -> $%04x", ch, data); + break; + + case DMAC::EIV: + data = chan->eiv | 0xff00; + putlog(3, "#%d EIV -> $%04x", ch, data); + break; + + case DMAC::MFC: + data = chan->mfc | 0xff00; + putlog(3, "#%d MFC -> $%04x", ch, data); + break; + + case DMAC::CPR: + data = chan->cpr | 0xff00; + putlog(3, "#%d CPR -> $%04x", ch, data); + break; + + case DMAC::DFC: + data = chan->dfc | 0xff00; + putlog(3, "#%d DFC -> $%04x", ch, data); + break; + + case DMAC::BFC: + data = chan->bfc | 0xff00; + putlog(3, "#%d BFC -> $%04x", ch, data); + break; + + case DMAC::GCR: + if (ch == 3) { + data = gcr | 0xff00; + putlog(3, "GCR -> $%04x", data); + break; + } else { + data = 0xffff; + } + break; + + default: + data = 0xffff; + break; + } + + return data; +} + +// STR か ACT が立っている時に書き込むとタイミングエラー +#define TIMING_ERR_IF_RUNNING do { \ + if (chan->str || chan->active) { \ + Error(chan, DMAC::CER_TIMING); \ + break; \ + } \ +} while (0) + +// ログ表示版 +#define TIMING_ERR_IF_RUNNING_log(regname) do { \ + if (chan->str || chan->active) { \ + putlog(2, "#%d %s <- $%02x", ch, regname, data); \ + Error(chan, DMAC::CER_TIMING); \ + break; \ + } \ +} while (0) + +uint64 +DMACDevice::Write8(uint32 addr, uint32 data) +{ + gMPU->AddCycle(31); // InsideOut p.135 + + uint32 offset = addr & 0xff; + int ch = offset / 0x40; + int n = offset % 0x40; + DMACChan *chan = &channel[ch]; - ch = addr / 0x40; - DMAC::Channel *chan = &dmac.chan[ch]; - int n = addr % 0x40; switch (n) { case DMAC::CSR: - chan->WriteCSR(data); + WriteCSR(chan, data); break; case DMAC::CER: @@ -216,128 +413,149 @@ DMACDevice::Write(uint32 addr, uint32 da break; case DMAC::DCR: - putlog(4, "#%d DCR <- $%02x", ch, data); - chan->dcr = data & 0xfb; + putlog(2, "#%d DCR <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetDCR(data); break; case DMAC::OCR: - putlog(4, "#%d OCR <- $%02x", ch, data); - chan->ocr = data; + putlog(2, "#%d OCR <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetOCR(data); break; case DMAC::SCR: - putlog(4, "#%d SCR <- $%02x", ch, data); - chan->scr = data & 0x0f; + putlog(2, "#%d SCR <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetSCR(data); break; case DMAC::CCR: - putlog(4, "#%d CCR <- $%02x", ch, data); - chan->WriteCCR(data); + { + // %0 -> %1 に変化したビットだけを評価する。 + uint8 up = (chan->GetCCR() ^ data) & data; + // バイトサイズのログを (up 込みで) 表示。 + putlog(2, "#%d CCR <- $%02x (up=$%02x)", chan->ch, data, up); + // CCR を更新してから WriteCCR() で動作をする。 + chan->SetCCR(data); + WriteCCR(chan, data, up); break; + } case DMAC::MTC: + TIMING_ERR_IF_RUNNING_log("MTC:H"); chan->mtc = (chan->mtc & 0x00ff) | (data << 8); - putlog(4, "#%d MTC:H <- $%02x (MTC=$%04x)", ch, data, chan->mtc); + putlog(2, "#%d MTC:H <- $%02x (MTC=$%04x)", ch, data, chan->mtc); break; case DMAC::MTC + 1: + TIMING_ERR_IF_RUNNING_log("MTC:L"); chan->mtc = (chan->mtc & 0xff00) | data; - putlog(4, "#%d MTC:L <- $%02x (MTC=$%04x)", ch, data, chan->mtc); + putlog(2, "#%d MTC:L <- $%02x (MTC=$%04x)", ch, data, chan->mtc); break; case DMAC::MAR: + TIMING_ERR_IF_RUNNING_log("MAR:0"); chan->mar = (chan->mar & 0x00ffffff) | (data << 24); - putlog(4, "#%d MAR:0 <- $%02x (MAR=$%08x)", ch, data, chan->mar); + putlog(2, "#%d MAR:0 <- $%02x (MAR=$%08x)", ch, data, chan->mar); break; case DMAC::MAR + 1: + TIMING_ERR_IF_RUNNING_log("MAR:1"); chan->mar = (chan->mar & 0xff00ffff) | (data << 16); - putlog(4, "#%d MAR:1 <- $%02x (MAR=$%08x)", ch, data, chan->mar); + putlog(2, "#%d MAR:1 <- $%02x (MAR=$%08x)", ch, data, chan->mar); break; case DMAC::MAR + 2: + TIMING_ERR_IF_RUNNING_log("MAR:2"); chan->mar = (chan->mar & 0xffff00ff) | (data << 8); - putlog(4, "#%d MAR:2 <- $%02x (MAR=$%08x)", ch, data, chan->mar); + putlog(2, "#%d MAR:2 <- $%02x (MAR=$%08x)", ch, data, chan->mar); break; case DMAC::MAR + 3: + TIMING_ERR_IF_RUNNING_log("MAR:3"); chan->mar = (chan->mar & 0xffffff00) | data; - putlog(4, "#%d MAR:3 <- $%02x (MAR=$%08x)", ch, data, chan->mar); + putlog(2, "#%d MAR:3 <- $%02x (MAR=$%08x)", ch, data, chan->mar); break; case DMAC::DAR: + TIMING_ERR_IF_RUNNING_log("DAR:0"); chan->dar = (chan->dar & 0x00ffffff) | (data << 24); - putlog(4, "#%d DAR:0 <- $%02x (DAR=$%08x)", ch, data, chan->dar); + putlog(2, "#%d DAR:0 <- $%02x (DAR=$%08x)", ch, data, chan->dar); break; case DMAC::DAR + 1: + TIMING_ERR_IF_RUNNING_log("DAR:1"); chan->dar = (chan->dar & 0xff00ffff) | (data << 16); - putlog(4, "#%d DAR:1 <- $%02x (DAR=$%08x)", ch, data, chan->dar); + putlog(2, "#%d DAR:1 <- $%02x (DAR=$%08x)", ch, data, chan->dar); break; case DMAC::DAR + 2: + TIMING_ERR_IF_RUNNING_log("DAR:2"); chan->dar = (chan->dar & 0xffff00ff) | (data << 8); - putlog(4, "#%d DAR:2 <- $%02x (DAR=$%08x)", ch, data, chan->dar); + putlog(2, "#%d DAR:2 <- $%02x (DAR=$%08x)", ch, data, chan->dar); break; case DMAC::DAR + 3: + TIMING_ERR_IF_RUNNING_log("DAR:3"); chan->dar = (chan->dar & 0xffffff00) | data; - putlog(4, "#%d DAR:3 <- $%02x (DAR=$%08x)", ch, data, chan->dar); + putlog(2, "#%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); + putlog(2, "#%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); + putlog(2, "#%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); + putlog(2, "#%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); + putlog(2, "#%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); + putlog(2, "#%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); + putlog(2, "#%d BAR:3 <- $%02x (BAR=$%08x)", ch, data, chan->bar); break; case DMAC::NIV: - putlog(4, "#%d NIV <- $%02x", ch, data); - chan->niv = data; + putlog(2, "#%d NIV <- $%02x", ch, data); + chan->SetNIV(data); break; case DMAC::EIV: - putlog(4, "#%d EIV <- $%02x", ch, data); - chan->eiv = data; + putlog(2, "#%d EIV <- $%02x", ch, data); + chan->SetEIV(data); break; case DMAC::MFC: - putlog(4, "#%d MFC <- $%02x", ch, data); - chan->mfc = data & 7; + putlog(2, "#%d MFC <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetMFC(data); break; case DMAC::CPR: - putlog(4, "#%d CPR <- $%02x", ch, data); - chan->cpr = data & 3; + putlog(2, "#%d CPR <- $%02x", ch, data); + chan->SetCPR(data); break; case DMAC::DFC: - putlog(4, "#%d DFC <- $%02x", ch, data); - chan->dfc = data & 7; + putlog(2, "#%d DFC <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetDFC(data); break; case DMAC::BFC: - putlog(4, "#%d BFC <- $%02x", ch, data); - chan->bfc = data & 7; + putlog(2, "#%d BFC <- $%02x", ch, data); + chan->SetBFC(data); break; case DMAC::GCR: if (ch == 3) { - putlog(4, "GCR <- $%02x", data); - dmac.gcr = data; + WriteGCR(data); } break; @@ -348,17 +566,148 @@ DMACDevice::Write(uint32 addr, uint32 da } uint64 -DMACDevice::Peek(uint32 addr) +DMACDevice::Write16(uint32 addr, uint32 data) +{ + gMPU->AddCycle(31); // InsideOut p.135 + + uint32 offset = addr & 0xff; + int ch = offset / 0x40; + int n = offset % 0x40; + DMACChan *chan = &channel[ch]; + + switch (n) { + case DMAC::CSR: // CSR | CER + // XXX ログがバイトサイズのまま出るがどうするか + WriteCSR(chan, data >> 8); + // CER は Read Only + break; + + case DMAC::DCR: // DCR | OCR + putlog(2, "#%d DCR:OCR <- $%04x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetDCR(data >> 8); + chan->SetOCR(data & 0xff); + break; + + case DMAC::SCR: // SCR | CCR + { + // CCR の %0 -> %1 に変化したビットだけを取り出す。 + uint8 up = (chan->GetCCR() ^ data) & data; + // ワードサイズのログを (CCR の up 込みで) 表示。 + putlog(2, "#%d SCR:CCR <- $%04x (up=$%02x)", ch, data, up); + // 両方のレジスタの値を更新。 + chan->SetSCR(data >> 8); + chan->SetCCR(data); + // ワード書き込みで STR が立っているとタイミングエラー (p.17) は + // ここで評価する。SCR 更新の後。 + TIMING_ERR_IF_RUNNING; + // タイミングエラーでなければ CCR に基づいて動作。 + WriteCCR(chan, data, up); + break; + } + + case DMAC::MTC: + putlog(2, "#%d MTC <- $%04x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->mtc = data; + break; + + case DMAC::MAR: + TIMING_ERR_IF_RUNNING_log("MAR:H"); + chan->mar = (chan->mar & 0x0000ffff) | (data << 16); + putlog(2, "#%d MAR:H <- $%04x (MAR=$%08x)", ch, data, chan->mar); + break; + case DMAC::MAR + 2: + TIMING_ERR_IF_RUNNING_log("MAR:L"); + chan->mar = (chan->mar & 0xffff0000) | data; + putlog(2, "#%d MAR:L <- $%04x (MAR=$%08x)", ch, data, chan->mar); + break; + + case DMAC::DAR: + TIMING_ERR_IF_RUNNING_log("DAR:H"); + chan->dar = (chan->dar & 0x0000ffff) | (data << 16); + putlog(2, "#%d DAR:H <- $%04x (DAR=$%08x)", ch, data, chan->dar); + break; + case DMAC::DAR + 2: + TIMING_ERR_IF_RUNNING_log("DAR:L"); + chan->dar = (chan->dar & 0xffff0000) | data; + putlog(2, "#%d DAR:L <- $%04x (DAR=$%08x)", ch, data, chan->dar); + break; + + case DMAC::BTC: + putlog(2, "#%d BTC <- $%04x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->btc = data; + break; + + case DMAC::BAR: + TIMING_ERR_IF_RUNNING_log("BAR:H"); + chan->bar = (chan->bar & 0x0000ffff) | (data << 16); + putlog(2, "#%d BAR:H <- $%04x (BAR=$%08x)", ch, data, chan->bar); + break; + case DMAC::BAR + 2: + TIMING_ERR_IF_RUNNING_log("BAR:L"); + chan->bar = (chan->bar & 0xffff0000) | data; + putlog(2, "#%d BAR:L <- $%04x (BAR=$%08x)", ch, data, chan->bar); + break; + + case DMAC::NIV: + putlog(2, "#%d NIV <- $%02x", ch, data); + chan->SetNIV(data); + break; + + case DMAC::EIV: + putlog(2, "#%d EIV <- $%02x", ch, data); + chan->SetEIV(data); + break; + + case DMAC::MFC: + putlog(2, "#%d MFC <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetMFC(data); + break; + + case DMAC::CPR: + putlog(2, "#%d CPR <- $%02x", ch, data); + chan->SetCPR(data); + break; + + case DMAC::DFC: + putlog(2, "#%d DFC <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetDFC(data); + break; + + case DMAC::BFC: + putlog(2, "#%d BFC <- $%02x", ch, data); + chan->SetBFC(data); + break; + + case DMAC::GCR - 1: + if (ch == 3) { + WriteGCR(data); + } + break; + + default: + break; + } + return 0; +} + +uint64 +DMACDevice::Peek8(uint32 addr) { uint8 data; - int ch; - ch = addr / 0x40; - const DMAC::Channel *chan = &dmac.chan[ch]; - int n = addr % 0x40; + uint32 offset = addr & 0xff; + int ch = offset / 0x40; + int n = offset % 0x40; + DMACChan *chan = &channel[ch]; + switch (n) { case DMAC::CSR: - data = chan->PeekCSR(); + data = chan->GetCSR(); break; case DMAC::CER: @@ -366,19 +715,19 @@ DMACDevice::Peek(uint32 addr) break; case DMAC::DCR: - data = chan->dcr; + data = chan->GetDCR(); break; case DMAC::OCR: - data = chan->ocr; + data = chan->GetOCR(); break; case DMAC::SCR: - data = chan->scr; + data = chan->GetSCR(); break; case DMAC::CCR: - data = chan->PeekCCR(); + data = chan->GetCCR(); break; case DMAC::MTC: @@ -460,8 +809,7 @@ DMACDevice::Peek(uint32 addr) case DMAC::GCR: if (ch == 3) { - data = dmac.gcr; - break; + data = gcr; } else { data = 0xff; } @@ -475,75 +823,85 @@ DMACDevice::Peek(uint32 addr) return data; } -bool -DMACDevice::MonitorUpdate() +void +DMACDevice::MonitorUpdate(Monitor *, TextScreen& screen) { int x; int y; - monitor.Clear(); + screen.Clear(); y = 1; - monitor.Puts(0, y++, "CSR"); + screen.Puts(0, y++, "BaseAddress"); + screen.Print(0, y++, "+$%02x CSR:", DMAC::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"); + screen.Print(0, y++, "+$%02x CER:", DMAC::CER); + screen.Print(0, y++, "+$%02x DCR:", DMAC::DCR); + screen.Print(5, y++, "%6s", ".XRM"); + screen.Print(5, y++, "%6s", ".DTYP"); + screen.Print(5, y++, "%6s", ".DPS"); + screen.Print(5, y++, "%6s", ".PCL"); + screen.Print(0, y++, "+$%02x OCR:", DMAC::OCR); + screen.Print(5, y++, "%6s", ".DIR"); + screen.Print(5, y++, "%6s", ".SIZE"); + screen.Print(5, y++, "%6s", ".CHAIN"); + screen.Print(5, y++, "%6s", ".REQG"); + screen.Print(0, y++, "+$%02x SCR:", DMAC::SCR); + screen.Print(5, y++, "%6s", ".MAC"); + screen.Print(5, y++, "%6s", ".DAC"); + screen.Print(0, y++, "+$%02x CCR:", DMAC::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"); + screen.Print(0, y++, "+$%02x MTC:", DMAC::MTC); + screen.Print(0, y++, "+$%02x MAR:", DMAC::MAR); + screen.Print(0, y++, "+$%02x DAR:", DMAC::DAR); + screen.Print(0, y++, "+$%02x BTC:", DMAC::BTC); + screen.Print(0, y++, "+$%02x BAR:", DMAC::BAR); + screen.Print(0, y++, "+$%02x NIV:", DMAC::NIV); + screen.Print(0, y++, "+$%02x EIV:", DMAC::EIV); + screen.Print(0, y++, "+$%02x MFC:", DMAC::MFC); + screen.Print(0, y++, "+$%02x CPR:", DMAC::CPR); + screen.Print(0, y++, "+$%02x DFC:", DMAC::DFC); + screen.Print(0, y++, "+$%02x BFC:", DMAC::BFC); for (int ch = 0; ch < 4; ch++) { - DMAC::Channel *chan = &dmac.chan[ch]; + DMACChan *chan = &channel[ch]; int val; - x = 12 + ch * 17; + x = 13 + ch * 17; y = 0; // 地味だけど有効なチャンネルをハイライトしてみる - monitor.Print(x, y++, (chan->active ? TA::Em : TA::Normal), + screen.Print(x, y++, (chan->active ? TA::Em : TA::Normal), "#%d (%s)", chan->ch, chan->desc); + // アドレス + screen.Print(x, y++, "$%06x", baseaddr + ch * 0x40); + // CSR - val = chan->PeekCSR(); - monitor.Print(x, y++, "$%02x", val); + val = chan->GetCSR(); + screen.Print(x, y++, "$%02x", val); static const char * const csrname[] = { - "COC", "BTC", "NDT", "ERR", "ACT", "---", "PCT", "PCS", + "COC", "BTC", "NDT", "ERR", "ACT", "DIT", "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; + val = chan->cer; 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); + screen.Print(x, y, "$%02x", chan->cer); + if (val != 0) { + screen.Print(x + 3, y, ":%s", e); } y++; + // DCR + screen.Print(x, y++, "$%02x", chan->GetDCR()); + // DCR:XRM static const char * const dcr_xrm[] = { //1234567890123 @@ -552,8 +910,8 @@ DMACDevice::MonitorUpdate() "CycleW/O Hold", "CycleWithHold", }; - val = chan->dcr >> 6; - monitor.Print(x, y++, "%d:%s", val, dcr_xrm[val]); + val = chan->GetXRM(); + screen.Print(x, y++, "%d:%s", val, dcr_xrm[val]); // DCR:DTYP static const char * const dcr_dtyp[] = { @@ -563,8 +921,8 @@ DMACDevice::MonitorUpdate() "ACK", "ACK+READY", }; - val = (chan->dcr >> 4) & 3; - monitor.Print(x, y++, "%d:%s", val, dcr_dtyp[val]); + val = chan->GetDTYP(); + screen.Print(x, y++, "%d:%s", val, dcr_dtyp[val]); // DCR:DPS static const char * const dcr_dps[] = { @@ -572,11 +930,22 @@ DMACDevice::MonitorUpdate() "8bit", "16bit", }; - val = (chan->dcr >> 3) & 1; - monitor.Print(x, y++, "%d:%s", val, dcr_dps[val]); + val = chan->GetDPS(); + screen.Print(x, y++, "%d:%s", val, dcr_dps[val]); // DCR:PCL - monitor.Print(x, y++, "XXX PCL"); + static const char * const dcr_pcl_name[] = { + //1234567890123 + "Status", + "StatusInt", + "Pulse(output)", + "Abort", + }; + val = chan->dcr_pcl; + screen.Print(x, y++, "%d:%s", val, dcr_pcl_name[val]); + + // OCR + screen.Print(x, y++, "$%02x", chan->GetOCR()); // OCR:DIR static const char * const ocr_dir[] = { @@ -584,19 +953,19 @@ DMACDevice::MonitorUpdate() "MemoryToDevice", "DeviceToMemory", }; - val = (chan->ocr >> 7); - monitor.Print(x, y++, "%d:%s", val, ocr_dir[val]); + val = chan->GetDIR(); + screen.Print(x, y++, "%d:%s", val, ocr_dir[val]); // OCR:SIZE static const char * const ocr_size[] = { //1234567890123 - "8bit", + "8bit (Packed)", "16bit", "32bit", "8bit Unpacked", }; - val = (chan->ocr >> 4) & 3; - monitor.Print(x, y++, "%d:%s", val, ocr_size[val]); + val = chan->GetSIZE(); + screen.Print(x, y++, "%d:%s", val, ocr_size[val]); // OCR:CHAIN static const char * const ocr_chain[] = { @@ -606,8 +975,8 @@ DMACDevice::MonitorUpdate() "ArrayChain", "LinkArrayChain", }; - val = (chan->ocr >> 2) & 3; - monitor.Print(x, y++, "%d:%s", val, ocr_chain[val]); + val = chan->GetCHAIN(); + screen.Print(x, y++, "%d:%s", val, ocr_chain[val]); // OCR:REQG static const char * const ocr_reqg[] = { @@ -617,10 +986,13 @@ DMACDevice::MonitorUpdate() "ExternalReq", "AutoThenExt", }; - val = (chan->ocr & 3); - monitor.Print(x, y++, "%d:%s", val, ocr_reqg[val]); + val = chan->GetREQG(); + screen.Print(x, y++, "%d:%s", val, ocr_reqg[val]); - // SCR:MAC + // SCR + screen.Print(x, y++, "$%02x", chan->GetSCR()); + + // SCR:MAC,DAC static const char * const scr_xac[] = { //1234567890123 "NoCount", @@ -628,73 +1000,76 @@ DMACDevice::MonitorUpdate() "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]); + val = chan->GetMAC(); + screen.Print(x, y++, "%d:%s", val, scr_xac[val]); + val = chan->GetDAC(); + screen.Print(x, y++, "%d:%s", val, scr_xac[val]); // CCR - val = chan->PeekCCR(); - monitor.Print(x, y++, "$%02x", val); + val = chan->GetCCR(); + 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 - 10, y++, "+$3f GCR: $%02x", 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)); - monitor.Puts(x + i * 4, y, TA::OnOff(b), 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]); + } } } @@ -721,278 +1096,873 @@ DMACDevice::errnames[] = { "SoftAbort", // $11 }; -// Read と Peek とそれ以外からも呼ばれるので副作用を持たせてはいけない。 -uint8 -DMAC::Channel::PeekCSR() const +// CSR への書き込み。 +void +DMACDevice::WriteCSR(DMACChan *chan, uint32 data) { - uint32 val; + // 上位3ビットと DIT は %1 の書き込みでクリア + chan->csr &= ~(data & 0xe4); - val = csr & 0xf0; - if (active) { - val |= CSR_ACT; + // ERR も %1 の書き込みでクリア。 + // このとき CER もクリアする。 + if ((data & DMAC::CSR_ERR)) { + chan->csr &= ~DMAC::CSR_ERR; + chan->cer = 0; } - // PCT は PCL が High から Low に変わるとセット - if (prev_pcl == true && pcl == false) { - val |= CSR_PCT; - } - if (pcl) { - val |= CSR_PCS; - } - return val; -} - -void -DMAC::Channel::WriteCSR(uint32 data) -{ - // 上位4ビットは %1 の書き込みでクリア - csr &= ~(data & 0xf0); - // PCT も %1 の書き込みでクリア - // PCT は PCL が High → Low の時セットなので prev_pcl を下げればよい + // PCT も %1 の書き込みでクリア。 + // PCT は PCL が High → Low の時セットなので pcl_prev を下げればよい if ((data & DMAC::CSR_PCT)) { - prev_pcl = false; + chan->pcl_prev = false; } - parent->putlog(4, "#%d CSR <- $%02x (CSR = $%02x)", ch, data, PeekCSR()); + putlog(2, "#%d CSR <- $%02x (CSR = $%02x)", chan->ch, data, chan->GetCSR()); + + ChangeInterrupt(); } -// Read と Peek から呼ばれるので副作用を持たせてはいけない。 -uint8 -DMAC::Channel::PeekCCR() const +// GCR への書き込み。 +void +DMACDevice::WriteGCR(uint8 data) { - uint32 val; - - val = ccr & 0xf0; - if (int_enable) { - val |= DMAC::CCR_INT; - } - return val; + putlog(2, "GCR <- $%02x", data); + gcr = data & 0x0f; } +// CCR への書き込み。これだけいろいろ変則的なので注意。 +// up は data のうち %0 -> %1 に変化したビット。 +// SetCCR() 実行済み、ログは出力済み。 void -DMAC::Channel::WriteCCR(uint32 data) +DMACDevice::WriteCCR(DMACChan *chan, uint32 data, uint8 up) { - // 割り込み許可ビットは常に反映 - int_enable = (data & DMAC::CCR_INT); + // コンフィギュレーションエラーのチェック。 + // データシート p43 Error Conditions (a) + if ((up & DMAC::CCR_STR)) { + // (i) The CNT bit is set at the same time STR bit in the chaining mode. + if ((up & DMAC::CCR_CNT) && chan->IsChain()) { + Error(chan, DMAC::CER_CONFIG); + return; + } + + if (chan->IsSingleAddress()) { + // (ii) DTYP specifies a single addressing mode, and + // the device port size is not the same as the operand size. + if (chan->GetDPS() == DMAC::DPS_8BIT) { + if (!(chan->GetSIZE() == DMAC::SIZE_8BIT_PACK || + chan->GetSIZE() == DMAC::SIZE_8BIT_UNPK)) + { + Error(chan, DMAC::CER_CONFIG); + return; + } + } else { + if (!(chan->GetSIZE() == DMAC::SIZE_16BIT)) { + Error(chan, DMAC::CER_CONFIG); + return; + } + } + } else { + // (iii) DTYP specifies a dual addressing mode, DPS is 16 bits, + // SIZE is 8 bits and REQG is "10" or "11". + if (chan->GetDPS() == DMAC::DPS_16BIT) { + // XXX UNPK は? + if (chan->GetSIZE() == DMAC::SIZE_8BIT_PACK) { + if (chan->GetREQG() == DMAC::REQG_EXTERNAL || + chan->GetREQG() == DMAC::REQG_AUTOFIRST) + { + Error(chan, DMAC::CER_CONFIG); + return; + } + } + } + } + + // (iv) An undefined configuration is set in the registers. + if (chan->GetXRM() == 1 || + chan->GetMAC() == 3 || + chan->GetDAC() == 3 || + chan->GetCHAIN() == 1) + { + Error(chan, DMAC::CER_CONFIG); + return; + } + if (chan->IsSingleAddress() == false && + chan->GetSIZE() == DMAC::SIZE_8BIT_UNPK && + chan->GetDPS() != DMAC::DPS_8BIT) + { + Error(chan, DMAC::CER_CONFIG); + return; + } + + // (e) Count Error + // ここでは (i), (ii) のみ + if (chan->IsChain() == false && chan->mtc == 0) { + Error(chan, DMAC::CER_COUNT_MTC); + return; + } + if (chan->GetCHAIN() == DMAC::CHAIN_ARRAY && chan->btc == 0) { + Error(chan, DMAC::CER_COUNT_BTC); + return; + } + } // SAB (Software Abort) - if ((data & DMAC::CCR_SAB)) { - // イベントを触るため親デバイスで処理する - parent->AbortTransfer(this); + if ((up & DMAC::CCR_SAB)) { + AbortTransfer(chan); } // HLT (Halt Operation) - if ((data & DMAC::CCR_HLT)) { - parent->putlog(0, "CCR HLT 未実装"); + if ((up & DMAC::CCR_HLT)) { + putlog(0, "CCR HLT (NOT IMPLEMENTED)"); } // CNT (Continue Operation) - if ((data & DMAC::CCR_CNT)) { - parent->putlog(0, "CCR CNT 未実装"); + if ((up & DMAC::CCR_CNT)) { + // Opeation Timing Error (i) + if (chan->IsChain() && chan->active) { + Error(chan, DMAC::CER_TIMING); + return; + } + if (chan->str == false && chan->active == false) { + Error(chan, DMAC::CER_TIMING); + return; + } + putlog(0, "CCR CNT (NOT IMPLEMENTED)"); } // STR (Start Operation) - if ((data & DMAC::CCR_STR)) { - StartTransfer(); + if ((up & DMAC::CCR_STR)) { + StartTransfer(chan); } -} - -// 割り込み要因ビットをセットし、必要なら割り込みを上げる。 -// 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); - } + ChangeInterrupt(); } // 転送開始 void -DMAC::Channel::StartTransfer() +DMACDevice::StartTransfer(DMACChan* chan) { - // CSR の COC,BTC,NDT,ERR が立ってたら開始しない - if ((csr & 0xf0) != 0) { - cer = CER_CONFIG; // ? - csr |= CSR_ERR; + // Operation Timing Error (ii) + // CSR の ACT,COC,BTC,NDT,ERR が立ってたら開始しない + if (chan->active || (chan->csr & 0xf0) != 0) { + Error(chan, DMAC::CER_TIMING); return; } - active = true; + // ACT を立てたら STR を下げる。 + chan->active = true; + chan->str = false; - // XXX CER をどこかでクリアしないといけないようだがどこだ? - cer = 0; + // XXX あとで移動する + switch (chan->GetMAC()) { + case DMAC::SCR_COUNT_UP: + chan->mac = 1; + break; + case DMAC::SCR_COUNT_DOWN: + putlog(0, "SCR:MAC CountDown Mode (NOT SUPPORTED)"); + chan->mac = -1; + break; + default: + chan->mac = 0; + break; + } + switch (chan->GetDAC()) { + case DMAC::SCR_COUNT_UP: + chan->dac = 1; + break; + case DMAC::SCR_COUNT_DOWN: + putlog(0, "SCR:DAC CountDown Mode (NOT SUPPORTED)"); + chan->dac = -1; + break; + default: + chan->dac = 0; + break; + } - // 転送方法は 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(); + chan->data.Clear(); // 転送モード - switch (GetREQG()) { - case DMAC::OCR_REQG_AUTO_LIM: - PANIC("REQG_AUTO_LIM 未実装"); + switch (chan->GetREQG()) { + case DMAC::REQG_AUTO_LIM: + VMPANIC("REQG_AUTO_LIM 未実装"); break; - case DMAC::OCR_REQG_AUTO_MAX: + case DMAC::REQG_AUTO_MAX: // 自発的に転送を開始する - parent->Transfer(ch); + chan->req = true; + break; + + case DMAC::REQG_EXTERNAL: + // 外部リクエスト転送。ここでは何もしなくてよい break; - case DMAC::OCR_REQG_EXTERNAL: - case DMAC::OCR_REQG_AUTOFIRST: - PANIC("未実装 REQG"); + case DMAC::REQG_AUTOFIRST: + VMPANIC("未実装 REQG"); break; default: __unreachable(); } + + // 転送開始ログ。全パラメータは無理なので概要だけ表示。 + if (loglevel >= 1) { + std::string msg; + if (chan->GetDIR() == DMAC::DIR_MtoD) { + msg = string_format("$%06x to $%06x mtc=$%04x", + chan->mar, chan->dar, chan->mtc); + } else { + msg = string_format("$%06x to $%06x mtc=$%04x", + chan->dar, chan->mar, chan->mtc); + } + putlogn("#%d Start %s", chan->ch, msg.c_str()); + } + + if (event.IsRunning() == false) { + CallAfter(StartCallback, 0); + } } -// 転送 -// (イベントを持つのでこれはデバイスクラスのメソッド) -void -DMACDevice::Transfer(int ch) +// 今回転送するチャンネルを決定して返す。なければ NULL を返す。 +DMACChan * +DMACDevice::SelectChannel() { - DMAC::Channel *chan = &dmac.chan[ch]; - int64 rv; + DMACChan *chan; + int ch = -1; + uint8 prio = 255; + + for (int i = 0; i < channel.size(); i++) { + chan = &channel[i]; + if (chan->active && (chan->req || chan->seq_index < chan->seq.size())) { + 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 NULL; + } else { + chan = &channel[ch]; + event.SetName(string_format("DMAC #%d", chan->ch)); + return chan; + } +} - putlog(4, "#%d Transfer (MTC=$%04x)", ch, chan->mtc); +// 転送開始 +void +DMACDevice::StartCallback(Event& ev) +{ + // チャンネルを決定 + auto chan = SelectChannel(); + if (chan == NULL) { + return; + } - rv = 0; - switch (chan->xfertype) { - case 0: // 8ビットポート、8ビットサイズ、パック動作 + // 実効プライオリティを同順位の最後に回す + chan->priority |= 0x08; - case 1: // 8ビットポート、16ビットサイズ + chan->retry = 0; - case 2: // 8ビットポート、32ビットサイズ - PANIC("未実装 xfer type"); + // XXX バスアービトレーションとか - case 3: // 8ビットポート、8ビットサイズ、パックなし動作 - if (chan->GetDIR() == DMAC::OCR_DIR_MtoD) { - rv = TransferM8toD8(chan); + // 転送シーケンス決定 + chan->seq.clear(); + chan->seq_index = 0; + if (chan->GetDIR() == DMAC::DIR_MtoD) { + if (chan->GetDPS() == DMAC::DPS_8BIT) { + if (chan->GetSIZE() == DMAC::SIZE_32BIT) { + chan->seq.push_back(RD_M16); + chan->seq.push_back(WR_D8); + chan->seq.push_back(WR_D8); + chan->seq.push_back(RD_M16); + chan->seq.push_back(WR_D8); + chan->seq.push_back(WR_D8); + } else if (chan->GetSIZE() == DMAC::SIZE_16BIT) { + chan->seq.push_back(RD_M16); + chan->seq.push_back(WR_D8); + chan->seq.push_back(WR_D8); + } else { + chan->seq.push_back(RD_M8); + chan->seq.push_back(WR_D8); + } } else { - rv = TransferD8toM8(chan); + // DPS==16 + if (chan->GetSIZE() == DMAC::SIZE_32BIT) { + chan->seq.push_back(RD_M16); + chan->seq.push_back(WR_D16); + chan->seq.push_back(RD_M16); + chan->seq.push_back(WR_D16); + } else if (chan->GetSIZE() == DMAC::SIZE_16BIT) { + chan->seq.push_back(RD_M16); + chan->seq.push_back(WR_D16); + } else { + chan->seq.push_back(RD_M8); + chan->seq.push_back(WR_D8); + } + } + } else { // DtoM + if (chan->GetDPS() == DMAC::DPS_8BIT) { + if (chan->GetSIZE() == DMAC::SIZE_32BIT) { + chan->seq.push_back(RD_D8); + chan->seq.push_back(RD_D8); + chan->seq.push_back(WR_M16); + chan->seq.push_back(RD_D8); + chan->seq.push_back(RD_D8); + chan->seq.push_back(WR_M16); + } else if (chan->GetSIZE() == DMAC::SIZE_16BIT) { + chan->seq.push_back(RD_D8); + chan->seq.push_back(RD_D8); + chan->seq.push_back(WR_M16); + } else { + chan->seq.push_back(RD_D8); + chan->seq.push_back(WR_M8); + } + } else { + // DPS==16 + if (chan->GetSIZE() == DMAC::SIZE_32BIT) { + chan->seq.push_back(RD_D16); + chan->seq.push_back(WR_M16); + chan->seq.push_back(RD_D16); + chan->seq.push_back(WR_M16); + } else if (chan->GetSIZE() == DMAC::SIZE_16BIT) { + chan->seq.push_back(RD_D16); + chan->seq.push_back(WR_M16); + } else { + chan->seq.push_back(RD_D8); + chan->seq.push_back(WR_M8); + } + } + } + + CallAfter(TransferCallback, 1); +} + +// 転送処理 +void +DMACDevice::TransferCallback(Event& ev) +{ + // チャンネルを決定 + auto chan = SelectChannel(); + if (chan == NULL) { + return; + } + + int op = chan->seq[chan->seq_index]; + uint64 data; + + // デバイスアクセスなら ACK#n 信号と DONE 信号のドライブ + switch (op) { + case RD_D8: + case RD_D16: + case WR_D8: + case WR_D16: + AssertACK(chan); + break; + default: + break; + } + + // パック動作が起きるケースなら op を差し替える。 + if (chan->GetSIZE() == DMAC::SIZE_8BIT_PACK) { + if (op == RD_M8) { + if (chan->mtc > 1 && chan->mar % 2 == 0 && chan->GetMAC() != 0) { + if (chan->data.Length() == 0) { + op = RD_M16; + } else { + op = NOP; + } + } } + if (op == WR_M8) { + if (chan->data.Length() >= 2) { + op = WR_M16; + } else { + if (chan->mtc > 1 && chan->mar % 2 == 0 && chan->GetMAC() != 0){ + op = NOP; + } + } + } + } + + // 1回分のアクセス + data = 0; + switch (op) { + case RD_M8: + data = vm_phys_read_8(chan->mar); + break; + case RD_M16: + data = vm_phys_read_16(chan->mar); + break; + case RD_D8: + data = vm_phys_read_8(chan->dar); + break; + case RD_D16: + data = vm_phys_read_16(chan->dar); + break; + case WR_M8: + data = chan->data.Dequeue(); + data = vm_phys_write_8(chan->mar, data); + break; + case WR_M16: + data = chan->data.Dequeue() << 8; + data |= chan->data.Dequeue(); + data = vm_phys_write_16(chan->mar, data); + break; + case WR_D8: + data = chan->data.Dequeue(); + data = vm_phys_write_8(chan->dar, data); + break; + case WR_D16: + data = chan->data.Dequeue() << 8; + data |= chan->data.Dequeue(); + data = vm_phys_write_16(chan->dar, data); + break; + case NOP: break; + default: + __unreachable(); + } - case 4: // 16ビットポート、8ビットサイズ、パック動作 - case 5: // 16ビットポート、16ビットサイズ - case 6: // 16ビットポート、32ビットサイズ - case 7: // 16ビットポート、8ビットサイズ、パックなし動作 - PANIC("未実装 xfer type"); + // デバイスアクセスなら ACK#n 信号と DONE 信号のドライブ + switch (op) { + case RD_D8: + case RD_D16: + case WR_D8: + case WR_D16: + NegateACK(chan); + break; + default: + break; } - // XXX バスエラーは? - if (rv < 0) { + // バスエラーか。ACK を下ろした後で行う。 + if ((int64)data < 0) { + TransferError(chan, op, data); return; } - // バスエラーでなければカウンタを更新 - // XXX とりあえず。後で書く - switch (chan->GetMAC()) { - case DMAC::SCR_COUNT_UP: - chan->mar += 1; + // 読み込んだデータをキューに投入。バスエラー判定後に行う。 + switch (op) { + case RD_M8: + case RD_D8: + chan->data.Enqueue(data); + break; + case RD_M16: + case RD_D16: + chan->data.Enqueue(data >> 8); + chan->data.Enqueue(data & 0xff); break; - case DMAC::SCR_COUNT_DOWN: - chan->mar -= 1; + default: break; } - switch (chan->GetDAC()) { - case DMAC::SCR_COUNT_UP: - chan->dar += 1; + + // カウンタを更新。バスエラー判定後に行う。 + switch (op) { + case RD_M8: + case WR_M8: + chan->mar += chan->mac; + break; + case RD_M16: + case WR_M16: + chan->mar += chan->mac * 2; + break; + case RD_D8: + case WR_D8: + chan->dar += chan->dac * 2; + break; + case RD_D16: + case WR_D16: + chan->dar += chan->dac * 2; break; - case DMAC::SCR_COUNT_DOWN: - chan->dar -= 1; + case NOP: break; + default: + __unreachable(); } - chan->mtc--; - if (chan->mtc == 0) { - // 転送完了 - chan->active = false; - chan->SetINT(DMAC::CSR_COC); + // シーケンス完了で MTC の転送1回分 + if (++chan->seq_index >= chan->seq.size()) { + chan->mtc -= 1; + + // すべての転送完了か + if (__predict_false(chan->mtc == 0)) { + chan->csr |= DMAC::CSR_COC; + chan->active = false; + ChangeInterrupt(); + } + + // XXX ディレイはあとから見直す + CallAfter(StartCallback, 4); } else { - // 次回の転送 - // XXX とりあえず 1us で1回の転送にする - event[ch].time = 1_usec; - event[ch].Start(); + // XXX ディレイはあとから見直す + CallAfter(TransferCallback, 4); } } -// メモリから 8ビットデバイスへ (パックなし動作) -int64 -DMACDevice::TransferM8toD8(DMAC::Channel *chan) +// 転送エラー +void +DMACDevice::TransferError(DMACChan *chan, int op, uint64 r) { - uint64 data; - int64 rv; + if ((int64)r == -2) { + // DTACK が出ていない (SPC)。 + // 本当は DTACK が出たことをコールバックしてくれれば + // 効率がいいのだがポーリングでもかまわんだろう。 + chan->retry++; + putlog(4, "retry=%d", chan->retry); + if (chan->retry < 100) { + // 現在のイベントを再実行 + event.time = 1 * 80_nsec; + gScheduler->StartEvent(event); + return; + } + // タイムアウトしたらバスエラーにフォールスルー + } - 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; + // バスエラー + switch (op) { + case RD_M8: + case RD_M16: + case WR_M8: + case WR_M16: + Error(chan, DMAC::CER_BUS_MAR); + break; + case RD_D8: + case RD_D16: + case WR_D8: + case WR_D16: + Error(chan, DMAC::CER_BUS_DAR); + break; + default: + __unreachable(); + } +} - return 0; +// エラー発生 +void +DMACDevice::Error(DMACChan *chan, uint8 errcode) +{ + // データシート p.43 + chan->cer = errcode; + chan->csr |= DMAC::CSR_COC | DMAC::CSR_ERR; + chan->active = false; + chan->str = false; + chan->cnt = false; + ChangeInterrupt(); + CallAfter(StartCallback, 0); } -// 8ビットデバイスからメモリへ (パックなし動作) -int64 -DMACDevice::TransferD8toM8(DMAC::Channel *chan) +// ACK#n 信号線をアサート +void +DMACDevice::AssertACK(DMACChan *chan) { - uint64 data; - int64 rv; + switch (chan->ch) { + case 0: + gFDC->AssertDACK(chan->mtc == 1); + break; + case 1: + // 接続されていない + break; + case 3: + gADPCM->AssertDACK(chan->mtc == 1); + break; + default: + VMPANIC("未実装"); + } +} - 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; +// ACK#n 信号線をネゲート +void +DMACDevice::NegateACK(DMACChan *chan) +{ + switch (chan->ch) { + case 0: + gFDC->NegateDACK(); + break; + case 1: + // 接続されていない + break; + case 3: + gADPCM->NegateDACK(); + break; + default: + VMPANIC("未実装"); + } +} - return 0; +// REQ#n 信号線アサート (外部から呼ばれる) +void +DMACDevice::AssertREQ(int ch) +{ + assert(0 <= ch && ch < 4); + + DMACChan *chan = &channel[ch]; + chan->req = true; + + // #3(ADPCM) の REQ は PCL にも繋がっている + if (ch == 3) { + chan->pcl_pin = true; + } + + if (event.IsRunning() == false) { + gScheduler->StartEvent(event); + } } -// ソフトウェアアボート -// (イベントを持つのでこれはデバイスクラスのメソッド) +// REQ#n 信号線ネゲート (外部から呼ばれる) void -DMACDevice::AbortTransfer(DMAC::Channel *chan) +DMACDevice::NegateREQ(int ch) { - int ch = chan->ch; + assert(0 <= ch && ch < 4); - putlog(2, "CCR SAB ソフトウェアアボート"); + DMACChan *chan = &channel[ch]; + chan->req = false; + + // #3(ADPCM) の REQ は PCL にも繋がっている + if (ch == 3) { + chan->pcl_pin = false; + } +} + +// ソフトウェアアボート +void +DMACDevice::AbortTransfer(DMACChan *chan) +{ + putlog(2, "CCR SAB Software Abort"); // %1 を書き込むことで実際には SAB はセットされるが、ERR が立つと // SAB をクリアする動作のため、書き込んだ %1 が読めることはない。 // ここでは SAB ビットのセットを省略。 - event[ch].Stop(); + chan->str = false; + chan->cnt = false; chan->active = false; chan->cer = DMAC::CER_SOFT_ABORT; - chan->SetINT(DMAC::CSR_ERR); + // ERR ビットが立つと SAB をクリアする + chan->csr |= DMAC::CSR_ERR; + chan->sab = false; + ChangeInterrupt(); +} + +// 割り込み信号線の状態を変える。 +void +DMACDevice::ChangeInterrupt() +{ + bool irq = false; + + for (int ch = 0; ch < channel.size(); ch++) { + DMACChan *chan = &channel[ch]; + + if (chan->int_enable && chan->IsINTR()) { + irq = true; + } + } + gInterrupt->ChangeINT(this, irq); +} + +// 割り込みアクノリッジ +int +DMACDevice::InterruptAcknowledge() +{ + DMACChan *chan; + int top = -1; + uint8 prio = 255; + + // 割り込みを発生させているトッププライオリティのチャンネルを検索 + for (int ch = 0; ch < channel.size(); ch++) { + chan = &channel[ch]; + + if (chan->int_enable && chan->IsINTR()) { + if (chan->priority < prio) { + prio = chan->priority; + top = ch; + } + } + } + + if (top >= 0) { + chan = &channel[top]; + if ((chan->csr & DMAC::CSR_ERR)) { + return chan->eiv; + } else { + return chan->niv; + } + } + // XXX 誰もいなかったら? + VMPANIC("InterruptAcknowledge no active channels?"); +} + +// +// チャンネル +// + +// CSR を取得する +uint8 +DMACChan::GetCSR() const +{ + uint32 val; + + val = csr & 0xf0; + if (active) { + val |= DMAC::CSR_ACT; + } + // PCT は PCL が High から Low に変わるとセット + if (pcl_prev == true && pcl_pin == false) { + val |= DMAC::CSR_PCT; + } + if (pcl_pin) { + val |= DMAC::CSR_PCS; + } + return val; +} + +// DCR を取得する +uint8 +DMACChan::GetDCR() const +{ + uint8 val; + + val = (xrm << 6); + val |= (dtyp << 4); + val |= (dps << 3); + val |= dcr_pcl; + + return val; +} + +// DCR を設定する +void +DMACChan::SetDCR(uint8 val) +{ + xrm = (val >> 6); + dtyp = (val >> 4) & 3; + dps = (val >> 3) & 1; + dcr_pcl = val & 3; +} + +// OCR を取得する +uint8 +DMACChan::GetOCR() const +{ + uint8 val; + + val = (dir << 7); + val |= (size << 4); + val |= (chain << 2); + val |= reqg; + + return val; +} + +// OCR を設定する +void +DMACChan::SetOCR(uint8 val) +{ + dir = (val >> 7); + size = (val >> 4) & 3; + chain = (val >> 2) & 3; + reqg = val & 3; +} + +// SCR を取得する +uint8 +DMACChan::GetSCR() const +{ + uint8 val; + + val = scr_mac << 2; + val |= scr_dac; + + return val; +} + +// SCR を設定する +void +DMACChan::SetSCR(uint8 val) +{ + scr_mac = (val >> 2) & 3; + scr_dac = val & 3; +} + +// CCR を取得する +uint8 +DMACChan::GetCCR() const +{ + uint8 val = 0; + + if (str) { + val |= DMAC::CCR_STR; + } + if (cnt) { + val |= DMAC::CCR_CNT; + } + if (hlt) { + val |= DMAC::CCR_HLT; + } + if (sab) { + val |= DMAC::CCR_SAB; + } + if (int_enable) { + val |= DMAC::CCR_INT; + } + + return val; +} + +// CCR を設定する (値を置く以上のことはしない) +void +DMACChan::SetCCR(uint8 val) +{ + str = (val & DMAC::CCR_STR); + cnt = (val & DMAC::CCR_CNT); + hlt = (val & DMAC::CCR_HLT); + sab = (val & DMAC::CCR_SAB); + int_enable = (val & DMAC::CCR_INT); +} + +// NIV を設定する +void +DMACChan::SetNIV(uint8 val) +{ + niv = val; +} + +// EIV を設定する +void +DMACChan::SetEIV(uint8 val) +{ + eiv = val; +} + +// MFC を設定する +void +DMACChan::SetMFC(uint8 val) +{ + mfc = val & 0x07; +} + +// CPR を設定する +void +DMACChan::SetCPR(uint8 val) +{ + cpr = val & 0x03; + // 実効プライオリティの上位4bitは cpr に等しい + priority = cpr << 4; +} + +// DFC を設定する +void +DMACChan::SetDFC(uint8 val) +{ + dfc = val & 0x07; +} + +// BFC を設定する +void +DMACChan::SetBFC(uint8 val) +{ + bfc = val & 0x07; }