--- nono/vm/dmac.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/dmac.cpp 2026/04/29 17:05:14 1.1.1.11 @@ -1,136 +1,251 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// DMAC (HD63450) // #include "dmac.h" +#include "adpcm.h" +#include "bus.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) + +// グローバル参照用 +DMACDevice *gDMAC; +// コンストラクタ DMACDevice::DMACDevice() + : inherited("DMAC") { - logname = "dmac"; - devname = "DMAC"; devaddr = baseaddr; devlen = 0x2000; + + 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::Read8(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 + + uint32 offset = addr & 0xff; + int ch = offset / 0x40; + int n = offset % 0x40; + DMACChan *chan = &channel[ch]; + switch (n) { case DMAC::CSR: - data = chan->csr; + data = chan->GetCSR(); + putlog(3, "#%d CSR -> $%02x", ch, data); break; case DMAC::CER: data = chan->cer; + putlog(3, "#%d CER -> $%02x", ch, data); break; case DMAC::DCR: - data = chan->dcr; + data = chan->GetDCR(); + putlog(3, "#%d DCR -> $%02x", ch, data); break; case DMAC::OCR: - data = chan->ocr; + data = chan->GetOCR(); + putlog(3, "#%d OCR -> $%02x", ch, data); break; case DMAC::SCR: - data = chan->scr; + data = chan->GetSCR(); + putlog(3, "#%d SCR -> $%02x", ch, data); break; case DMAC::CCR: - data = chan->ccr; + data = chan->GetCCR(); + putlog(3, "#%d CCR -> $%02x", ch, data); break; case DMAC::MTC: data = chan->mtc >> 8; + putlog(3, "#%d MTC:H -> $%02x", ch, data); break; case DMAC::MTC + 1: data = chan->mtc & 0xff; + putlog(3, "#%d MTC:L -> $%02x", ch, data); break; case DMAC::MAR: data = chan->mar >> 24; + putlog(3, "#%d MAR:0 -> $%02x", ch, data); break; case DMAC::MAR + 1: data = (chan->mar >> 16) & 0xff; + putlog(3, "#%d MAR:1 -> $%02x", ch, data); break; case DMAC::MAR + 2: data = (chan->mar >> 8) & 0xff; + putlog(3, "#%d MAR:2 -> $%02x", ch, data); break; case DMAC::MAR + 3: data = chan->mar & 0xff; + putlog(3, "#%d MAR:3 -> $%02x", ch, data); break; case DMAC::DAR: data = chan->dar >> 24; + putlog(3, "#%d DAR:0 -> $%02x", ch, data); break; case DMAC::DAR + 1: data = (chan->dar >> 16) & 0xff; + putlog(3, "#%d DAR:1 -> $%02x", ch, data); break; case DMAC::DAR + 2: data = (chan->dar >> 8) & 0xff; + putlog(3, "#%d DAR:2 -> $%02x", ch, data); break; case DMAC::DAR + 3: data = chan->dar & 0xff; + putlog(3, "#%d DAR:3 -> $%02x", ch, data); break; case DMAC::BTC: data = chan->btc >> 8; + putlog(3, "#%d BTC:H -> $%02x", ch, data); break; case DMAC::BTC + 1: data = chan->btc & 0xff; + putlog(3, "#%d BTC:L -> $%02x", ch, data); break; case DMAC::BAR: data = chan->bar >> 24; + putlog(3, "#%d BAR:0 -> $%02x", ch, data); break; case DMAC::BAR + 1: data = (chan->bar >> 16) & 0xff; + putlog(3, "#%d BAR:1 -> $%02x", ch, data); break; case DMAC::BAR + 2: data = (chan->bar >> 8) & 0xff; + putlog(3, "#%d BAR:2 -> $%02x", ch, data); break; case DMAC::BAR + 3: data = chan->bar & 0xff; + putlog(3, "#%d BAR:3 -> $%02x", ch, data); break; case DMAC::NIV: data = chan->niv; + putlog(3, "#%d NIV -> $%02x", ch, data); break; case DMAC::EIV: data = chan->eiv; + putlog(3, "#%d EIV -> $%02x", ch, data); break; case DMAC::MFC: data = chan->mfc; + putlog(3, "#%d MFC -> $%02x", ch, data); break; case DMAC::CPR: data = chan->cpr; + putlog(3, "#%d CPR -> $%02x", ch, data); break; case DMAC::DFC: data = chan->dfc; + putlog(3, "#%d DFC -> $%02x", ch, data); break; case DMAC::BFC: data = chan->bfc; + putlog(3, "#%d BFC -> $%02x", ch, data); break; case DMAC::GCR: if (ch == 3) { - data = dmac.gcr; + data = gcr; + putlog(3, "GCR -> $%02x", data); break; } else { data = 0xff; @@ -146,16 +261,151 @@ DMACDevice::Read8(uint32 addr) } uint64 +DMACDevice::Read16(uint32 addr) +{ + 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) { - int ch; + gMPU->AddCycle(31); // InsideOut p.135 + + uint32 offset = addr & 0xff; + int ch = offset / 0x40; + int n = offset % 0x40; + DMACChan *chan = &channel[ch]; - ch = (addr - baseaddr) / 0x40; - struct DMAC::channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; switch (n) { case DMAC::CSR: - write_csr(ch, data); + WriteCSR(chan, data); break; case DMAC::CER: @@ -163,101 +413,279 @@ DMACDevice::Write8(uint32 addr, uint32 d break; case DMAC::DCR: - chan->dcr = data & 0xfb; + putlog(2, "#%d DCR <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetDCR(data); break; case DMAC::OCR: - chan->ocr = data; + putlog(2, "#%d OCR <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetOCR(data); break; case DMAC::SCR: - chan->scr = data & 0x0f; + putlog(2, "#%d SCR <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetSCR(data); break; case DMAC::CCR: - write_ccr(ch, 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(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(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(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(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(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(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(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(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(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(2, "#%d DAR:3 <- $%02x (DAR=$%08x)", ch, data, chan->dar); break; case DMAC::BTC: chan->btc = (chan->btc & 0x00ff) | (data << 8); + putlog(2, "#%d BTC:H <- $%02x (BTC=$%04x)", ch, data, chan->btc); break; case DMAC::BTC + 1: chan->btc = (chan->btc & 0xff00) | data; + putlog(2, "#%d BTC:L <- $%02x (BTC=$%04x)", ch, data, chan->btc); break; case DMAC::BAR: chan->bar = (chan->bar & 0x00ffffff) | (data << 24); + 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(2, "#%d BAR:1 <- $%02x (BAR=$%08x)", ch, data, chan->bar); break; case DMAC::BAR + 2: chan->bar = (chan->bar & 0xffff00ff) | (data << 8); + putlog(2, "#%d BAR:2 <- $%02x (BAR=$%08x)", ch, data, chan->bar); break; case DMAC::BAR + 3: chan->bar = (chan->bar & 0xffffff00) | data; + putlog(2, "#%d BAR:3 <- $%02x (BAR=$%08x)", ch, data, chan->bar); break; case DMAC::NIV: - chan->niv = data; + putlog(2, "#%d NIV <- $%02x", ch, data); + chan->SetNIV(data); break; case DMAC::EIV: - chan->eiv = data; + putlog(2, "#%d EIV <- $%02x", ch, data); + chan->SetEIV(data); break; case DMAC::MFC: - chan->mfc = data & 7; + putlog(2, "#%d MFC <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetMFC(data); break; case DMAC::CPR: - chan->cpr = data & 3; + putlog(2, "#%d CPR <- $%02x", ch, data); + chan->SetCPR(data); break; case DMAC::DFC: - chan->dfc = data & 7; + putlog(2, "#%d DFC <- $%02x", ch, data); + TIMING_ERR_IF_RUNNING; + chan->SetDFC(data); break; case DMAC::BFC: - chan->bfc = data & 7; + putlog(2, "#%d BFC <- $%02x", ch, data); + chan->SetBFC(data); break; case DMAC::GCR: if (ch == 3) { - dmac.gcr = data; + WriteGCR(data); + } + break; + + default: + break; + } + return 0; +} + +uint64 +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; @@ -271,14 +699,15 @@ uint64 DMACDevice::Peek8(uint32 addr) { uint8 data; - int ch; - ch = (addr - baseaddr) / 0x40; - struct DMAC::channel *chan = &dmac.chan[ch]; - int n = (addr - baseaddr) % 0x40; + uint32 offset = addr & 0xff; + int ch = offset / 0x40; + int n = offset % 0x40; + DMACChan *chan = &channel[ch]; + switch (n) { case DMAC::CSR: - data = chan->csr; + data = chan->GetCSR(); break; case DMAC::CER: @@ -286,19 +715,19 @@ DMACDevice::Peek8(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->ccr; + data = chan->GetCCR(); break; case DMAC::MTC: @@ -380,8 +809,7 @@ DMACDevice::Peek8(uint32 addr) case DMAC::GCR: if (ch == 3) { - data = dmac.gcr; - break; + data = gcr; } else { data = 0xff; } @@ -396,14 +824,1145 @@ DMACDevice::Peek8(uint32 addr) } void -DMACDevice::write_csr(int ch, uint32 data) +DMACDevice::MonitorUpdate(Monitor *, TextScreen& screen) +{ + int x; + int y; + + screen.Clear(); + + y = 1; + screen.Puts(0, y++, "BaseAddress"); + screen.Print(0, y++, "+$%02x CSR:", DMAC::CSR); + y += 2; + 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; + 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++) { + DMACChan *chan = &channel[ch]; + int val; + x = 13 + ch * 17; + y = 0; + + // 地味だけど有効なチャンネルをハイライトしてみる + 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->GetCSR(); + screen.Print(x, y++, "$%02x", val); + static const char * const csrname[] = { + "COC", "BTC", "NDT", "ERR", "ACT", "DIT", "PCT", "PCS", + }; + MonitorReg4(screen, x, y++, val >> 4, &csrname[0]); + MonitorReg4(screen, x, y++, val & 0xff, &csrname[4]); + + // CER + val = chan->cer; + 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 + screen.Print(x, y++, "$%02x", chan->GetDCR()); + + // DCR:XRM + static const char * const dcr_xrm[] = { + //1234567890123 + "Burst", + "undefined", + "CycleW/O Hold", + "CycleWithHold", + }; + val = chan->GetXRM(); + 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->GetDTYP(); + screen.Print(x, y++, "%d:%s", val, dcr_dtyp[val]); + + // DCR:DPS + static const char * const dcr_dps[] = { + //1234567890123 + "8bit", + "16bit", + }; + val = chan->GetDPS(); + screen.Print(x, y++, "%d:%s", val, dcr_dps[val]); + + // DCR: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[] = { + //1234567890123 + "MemoryToDevice", + "DeviceToMemory", + }; + val = chan->GetDIR(); + screen.Print(x, y++, "%d:%s", val, ocr_dir[val]); + + // OCR:SIZE + static const char * const ocr_size[] = { + //1234567890123 + "8bit (Packed)", + "16bit", + "32bit", + "8bit Unpacked", + }; + val = chan->GetSIZE(); + 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->GetCHAIN(); + 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->GetREQG(); + screen.Print(x, y++, "%d:%s", val, ocr_reqg[val]); + + // SCR + screen.Print(x, y++, "$%02x", chan->GetSCR()); + + // SCR:MAC,DAC + static const char * const scr_xac[] = { + //1234567890123 + "NoCount", + "CountUp", + "CountDown", + "undefined", + }; + 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->GetCCR(); + 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 - 10, y++, "+$3f GCR: $%02x", gcr); + } + } +} + +// 4ビット分を表示する。MonitorUpdate の下請け +void +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 +}; + +// CSR への書き込み。 +void +DMACDevice::WriteCSR(DMACChan *chan, uint32 data) +{ + // 上位3ビットと DIT は %1 の書き込みでクリア + chan->csr &= ~(data & 0xe4); + + // ERR も %1 の書き込みでクリア。 + // このとき CER もクリアする。 + if ((data & DMAC::CSR_ERR)) { + chan->csr &= ~DMAC::CSR_ERR; + chan->cer = 0; + } + + // PCT も %1 の書き込みでクリア。 + // PCT は PCL が High → Low の時セットなので pcl_prev を下げればよい + if ((data & DMAC::CSR_PCT)) { + chan->pcl_prev = false; + } + + putlog(2, "#%d CSR <- $%02x (CSR = $%02x)", chan->ch, data, chan->GetCSR()); + + ChangeInterrupt(); +} + +// GCR への書き込み。 +void +DMACDevice::WriteGCR(uint8 data) +{ + putlog(2, "GCR <- $%02x", data); + gcr = data & 0x0f; +} + +// CCR への書き込み。これだけいろいろ変則的なので注意。 +// up は data のうち %0 -> %1 に変化したビット。 +// SetCCR() 実行済み、ログは出力済み。 +void +DMACDevice::WriteCCR(DMACChan *chan, uint32 data, uint8 up) +{ + // コンフィギュレーションエラーのチェック。 + // データシート 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 ((up & DMAC::CCR_SAB)) { + AbortTransfer(chan); + } + + // HLT (Halt Operation) + if ((up & DMAC::CCR_HLT)) { + putlog(0, "CCR HLT (NOT IMPLEMENTED)"); + } + + // CNT (Continue Operation) + 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 ((up & DMAC::CCR_STR)) { + StartTransfer(chan); + } + + ChangeInterrupt(); +} + +// 転送開始 +void +DMACDevice::StartTransfer(DMACChan* chan) +{ + // Operation Timing Error (ii) + // CSR の ACT,COC,BTC,NDT,ERR が立ってたら開始しない + if (chan->active || (chan->csr & 0xf0) != 0) { + Error(chan, DMAC::CER_TIMING); + return; + } + + // ACT を立てたら STR を下げる。 + chan->active = true; + chan->str = false; + + // 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; + } + + chan->data.Clear(); + + // 転送モード + switch (chan->GetREQG()) { + case DMAC::REQG_AUTO_LIM: + VMPANIC("REQG_AUTO_LIM 未実装"); + break; + + case DMAC::REQG_AUTO_MAX: + // 自発的に転送を開始する + chan->req = true; + break; + + case DMAC::REQG_EXTERNAL: + // 外部リクエスト転送。ここでは何もしなくてよい + break; + + 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); + } +} + +// 今回転送するチャンネルを決定して返す。なければ NULL を返す。 +DMACChan * +DMACDevice::SelectChannel() +{ + 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; + } +} + +// 転送開始 +void +DMACDevice::StartCallback(Event& ev) +{ + // チャンネルを決定 + auto chan = SelectChannel(); + if (chan == NULL) { + return; + } + + // 実効プライオリティを同順位の最後に回す + chan->priority |= 0x08; + + chan->retry = 0; + + // XXX バスアービトレーションとか + + // 転送シーケンス決定 + 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 { + // 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(); + } + + // デバイスアクセスなら ACK#n 信号と DONE 信号のドライブ + switch (op) { + case RD_D8: + case RD_D16: + case WR_D8: + case WR_D16: + NegateACK(chan); + break; + default: + break; + } + + // バスエラーか。ACK を下ろした後で行う。 + if ((int64)data < 0) { + TransferError(chan, op, data); + return; + } + + // 読み込んだデータをキューに投入。バスエラー判定後に行う。 + 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; + default: + break; + } + + // カウンタを更新。バスエラー判定後に行う。 + 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 NOP: + break; + default: + __unreachable(); + } + + // シーケンス完了で 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 ディレイはあとから見直す + CallAfter(TransferCallback, 4); + } +} + +// 転送エラー +void +DMACDevice::TransferError(DMACChan *chan, int op, uint64 r) { - putlog(1, "write_csr[%d] <- $%02x 未実装書き込み", ch, data); + 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; + } + // タイムアウトしたらバスエラーにフォールスルー + } + + // バスエラー + 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(); + } } +// エラー発生 void -DMACDevice::write_ccr(int ch, uint32 data) +DMACDevice::Error(DMACChan *chan, uint8 errcode) { - putlog(1, "write_ccr[%d] <- $%02x 未実装書き込み", ch, data); + // データシート 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); } +// ACK#n 信号線をアサート +void +DMACDevice::AssertACK(DMACChan *chan) +{ + switch (chan->ch) { + case 0: + gFDC->AssertDACK(chan->mtc == 1); + break; + case 1: + // 接続されていない + break; + case 3: + gADPCM->AssertDACK(chan->mtc == 1); + break; + default: + VMPANIC("未実装"); + } +} + +// 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("未実装"); + } +} + +// 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::NegateREQ(int ch) +{ + assert(0 <= ch && ch < 4); + + 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 ビットのセットを省略。 + + chan->str = false; + chan->cnt = false; + chan->active = false; + chan->cer = DMAC::CER_SOFT_ABORT; + // 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; +}