--- nono/vm/mfp.cpp 2026/04/29 17:04:45 1.1.1.7 +++ nono/vm/mfp.cpp 2026/04/29 17:05:33 1.1.1.17 @@ -4,69 +4,81 @@ // Licensed under nono-license.txt // +// +// MFP (MC68901) +// + #include "mfp.h" #include "interrupt.h" #include "keyboard.h" -#include "mpu680x0.h" -#include "mystring.h" +#include "monitor.h" +#include "mpu.h" +#include "scheduler.h" +#include "x68kkbd.h" -std::unique_ptr gMFP; +// InsideOut p.135 +static const busdata wait = busdata::Wait(24 * 40_nsec); // コンストラクタ MFPDevice::MFPDevice() + : inherited(OBJ_MFP) { - logname = "mfp"; - devname = "MFP"; - devaddr = baseaddr; - devlen = 0x2000; - monitor_size = nnSize(77, 26); + monitor = gMonitorManager->Regist(ID_MONITOR_MFP, this); + monitor->func = ToMonitorCallback(&MFPDevice::MonitorUpdate); + monitor->SetSize(77, 26); +} + +// デストラクタ +MFPDevice::~MFPDevice() +{ +} + +// 初期化 +bool +MFPDevice::Init() +{ + if (inherited::Init() == false) { + return false; + } - for (int ch = 0; ch < countof(event); ch++) { + interrupt = GetInterruptDevice(); + keyboard = dynamic_cast(GetKeyboard()); + + for (int ch = 0; ch < event.size(); ch++) { event[ch].dev = this; - event[ch].func = (DeviceCallback_t)&MFPDevice::TimerCallback; + event[ch].func = ToEventCallback(&MFPDevice::TimerCallback); event[ch].code = ch; event[ch].SetName(string_format("MFP Timer-%c", ch + 'A')); + scheduler->RegistEvent(event[ch]); } // キーボード周りは x68kkbd.cpp のコメント参照 - key_event.dev = this; - key_event.func = (DeviceCallback_t)&MFPDevice::KeyCallback; + key_event.func = ToEventCallback(&MFPDevice::KeyCallback); key_event.time = 3750_usec; key_event.SetName("MFP USART"); -} - -// デストラクタ -MFPDevice::~MFPDevice() -{ -} + scheduler->RegistEvent(key_event); -// 本体リセットスイッチによるリセット -void -MFPDevice::ResetHard() -{ - Reset(); -} - -// MPU の RESET 命令によるリセット -void -MFPDevice::ResetSoft() -{ - Reset(); + return true; } -// リセット(共通) +// リセット void -MFPDevice::Reset() +MFPDevice::ResetHard(bool poweron) { + // 3.3 RESET OPERATION には // All internal registers are cleared expect the TxDR, UDR, and TSR. + // と書いてあるが、6.2.1 には TxDR はリセット時に $00 と書いてある。 + // 内部カウンタは影響を受けずに next_tdr だけリセットされるという + // 意味だろうか。 mfp.aer = 0; mfp.ddr = 0; mfp.ier.w = 0; mfp.ipr.w = 0; mfp.isr.w = 0; mfp.imr.w = 0; - for (int i = 0; i < countof(mfp.tcr); i++) { - mfp.tcr[i] = 0; + for (int i = 0; i < countof(mfp.timer); i++) { + mfp.timer[i].tcr = 0; + mfp.timer[i].next_tdr = 256; } mfp.scr = 0; mfp.ucr = 0; @@ -76,10 +88,10 @@ MFPDevice::Reset() mfp.tsr = MFP::TSR_BE; // All timers are stopped. - for (int ch = 0; ch < countof(event); ch++) { - event[ch].Stop(); + for (auto& ev : event) { + scheduler->StopEvent(ev); } - key_event.Stop(); + scheduler->StopEvent(key_event); // USART RX and TX are disabled. // SO line is placed in HighZ. @@ -92,18 +104,18 @@ MFPDevice::Reset() mfp.vr_vec = 0; mfp.vr_s = 0; - // 信号線 - rr = false; + // CIRQ | FMIRQ | EXPON | ALARM にしておく + mfp.gpip = 0x6b; + + ChangeInterrupt(); } -uint64 -MFPDevice::Read(uint32 addr) +busdata +MFPDevice::ReadPort(uint32 offset) { - uint8 data; - - gMPU->AddCycle(24); // InsideOut p.135 + busdata data; - switch (addr) { + switch (offset) { case MFP::GPIP: data = mfp.gpip; break; @@ -141,13 +153,13 @@ MFPDevice::Read(uint32 addr) data = mfp.GetVR(); break; case MFP::TACR: - data = mfp.tcr[0]; + data = mfp.timer[0].tcr; break; case MFP::TBCR: - data = mfp.tcr[1]; + data = mfp.timer[1].tcr; break; case MFP::TCDCR: - data = (mfp.tcr[2] << 4) | mfp.tcr[3]; + data = mfp.GetTCDCR(); break; case MFP::TADR: data = GetTDR(0); @@ -174,142 +186,162 @@ MFPDevice::Read(uint32 addr) data = mfp.tsr; break; case MFP::UDR: + // UDR からデータを読み出したら Buffer Full をクリア data = mfp.udr; + ClearBF(); break; default: data = 0xff; break; } + + if (__predict_false(loglevel >= 3)) { + if (offset == MFP::TCDR) { + putlog(4, "%s -> $%02x", regname[offset], data.Data()); + } else if (offset < MFP::RegMax) { + putlogn("%s -> $%02x", regname[offset], data.Data()); + } else { + putlogn("$%06x -> $%02x", mpu->GetPaddr(), data.Data()); + } + } + data |= wait; + data |= BusData::Size1; return data; } -uint64 -MFPDevice::Write(uint32 addr, uint32 data) +busdata +MFPDevice::WritePort(uint32 offset, uint32 data) { - gMPU->AddCycle(24); // InsideOut p.135 - - switch (addr) { + switch (offset) { case MFP::GPIP: - putlog(0, "GPIP <- $%02x 未実装書き込み", data); + putlog(0, "Write GPIP <- $%02x (NOT IMPLEMENTED)", data); mfp.gpip = data; break; case MFP::AER: - putlog(0, "AER <- $%02x 未実装書き込み", data); + putlog(2, "%s <- $%02x", regname[offset], data); mfp.aer = data; break; case MFP::DDR: if (data != 0) { - putlog(0, "DDR <- $%02x 未実装書き込み", data); + putlog(0, "Write DDR <- $%02x (NOT IMPLEMENTED)", data); } mfp.ddr = data; break; case MFP::IERA: - putlog(2, "IERA <- $%02x", data); - mfp.ier.a = data; + putlog(2, "%s <- $%02x", regname[offset], data); + SetIER(mfp.ier.a, mfp.ipr.a, data); break; case MFP::IERB: - putlog(2, "IERB <- $%02x", data); - mfp.ier.b = data; + putlog(2, "%s <- $%02x", regname[offset], data); + SetIER(mfp.ier.b, mfp.ipr.b, data); break; case MFP::IPRA: - putlog(2, "IPRA <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); mfp.ipr.a = data; ChangeInterrupt(); break; case MFP::IPRB: - putlog(2, "IPRB <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); mfp.ipr.b = data; ChangeInterrupt(); break; case MFP::ISRA: - putlog(2, "ISRA <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); mfp.isr.a = data; break; case MFP::ISRB: - putlog(2, "ISRB <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); mfp.isr.b = data; break; case MFP::IMRA: - putlog(2, "IMRA <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); mfp.imr.a = data; ChangeInterrupt(); break; case MFP::IMRB: - putlog(2, "IMRB <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); mfp.imr.b = data; ChangeInterrupt(); break; case MFP::VR: mfp.vr_vec = data & 0xf0; mfp.vr_s = data & 0x08; - putlog(2, "VR <- $%02x", mfp.GetVR()); + putlog(2, "%s <- $%02x", regname[offset], mfp.GetVR()); break; case MFP::TACR: - putlog(2, "TACR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetTCR(0, data); break; case MFP::TBCR: - putlog(2, "TBCR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetTCR(1, data); break; case MFP::TCDCR: - putlog(2, "TCDCR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetTCR(2, data >> 4); SetTCR(3, data & 7); break; case MFP::TADR: - putlog(2, "TADR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetTDR(0, data); break; case MFP::TBDR: - putlog(2, "TBDR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetTDR(1, data); break; case MFP::TCDR: - putlog(2, "TCDR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetTDR(2, data); break; case MFP::TDDR: - putlog(2, "TDDR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetTDR(3, data); break; case MFP::SCR: - putlog(0, "SCR <- $%02x 未実装書き込み", data); + putlog(0, "SCR <- $%02x (NOT IMPLEMENTED)", data); mfp.scr = data; break; case MFP::UCR: - putlog(2, "UCR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetUCR(data); break; case MFP::RSR: - putlog(2, "RSR <- $%02x", data); + putlog(2, "%s <- $%02x", regname[offset], data); SetRSR(data); break; case MFP::TSR: - putlog(0, "TSR <- $%02x 未実装書き込み", data); + if ((data & 0x0e)) { + // SO 端子関係の B, H, L ビットは未実装 + putlog(0, "TSR <- $%02x (B,H,L bit NOT IMPLEMENTED)", data); + } mfp.tsr = (mfp.tsr & 0xf0) | (data & 0x0f); break; case MFP::UDR: - if (data == 0x40 || data == 0x41) { - // MSCTRL だけ頻度が高いのでログレベルを上げておく - putlog(3, "UDR <- $%02x", data); - } else { - putlog(2, "UDR <- $%02x", data); + if (__predict_false(loglevel >= 2)) { + if (data == 0x40 || data == 0x41) { + // MSCTRL だけ頻度が高いのでログレベルを上げておく + putlog(3, "%s <- $%02x", regname[offset], data); + } else { + putlogn("%s <- $%02x", regname[offset], data); + } } SetUDR(data); break; default: break; } - return 0; + + busdata r = wait; + r |= BusData::Size1; + return r; } -uint64 -MFPDevice::Peek(uint32 addr) +busdata +MFPDevice::PeekPort(uint32 offset) { uint8 data; - switch (addr) { + switch (offset) { case MFP::GPIP: data = mfp.gpip; break; @@ -347,13 +379,13 @@ MFPDevice::Peek(uint32 addr) data = mfp.GetVR(); break; case MFP::TACR: - data = mfp.tcr[0]; + data = mfp.timer[0].tcr; break; case MFP::TBCR: - data = mfp.tcr[1]; + data = mfp.timer[1].tcr; break; case MFP::TCDCR: - data = (mfp.tcr[2] << 4) | mfp.tcr[3]; + data = mfp.GetTCDCR(); break; case MFP::TADR: data = GetTDR(0); @@ -388,19 +420,33 @@ MFPDevice::Peek(uint32 addr) return data; } +bool +MFPDevice::PokePort(uint32 offset, uint32 data) +{ + return false; +} + void -MFPDevice::MonitorUpdate(TextScreen& monitor) +MFPDevice::MonitorUpdate(Monitor *, TextScreen& screen) { + MFP mfp_; + std::array tdr_; int x; int y; - monitor.Clear(); + // ローカルにコピー。 + memcpy(&mfp_, &mfp, sizeof(mfp)); + for (int ch = 0; ch < tdr_.size(); ch++) { + tdr_[ch] = GetTDR(ch); + } + + screen.Clear(); // 0 1 2 3 4 5 6 // 01234567890123456789012345678901234567890123456789012345678901234567890 // $e88001(GPIP):$00 $e88007(IERA):$00 $e88019(TACR):$00 $e88027 - // IER IMR IPR ISR Mode TDR Cnt - // An:MPSC TxEmpty Enable Mask Pend Serv Timer-A Delay(50us) 255 255 + // IER IMR IPR ISR Mode TDR Cnt + // An:MPSC TxEmpty Enable Mask Pend Serv Timer-A Delay(50us) 255 255 #define A(x) (baseaddr + (x * 2) + 1) @@ -408,93 +454,93 @@ MFPDevice::MonitorUpdate(TextScreen& mon // 1列目(GPIP 関連と VR) x = 0; y = 0; - monitor.Print(x, y++, "$%06x(GPIP):$%02x", A(MFP::GPIP), mfp.gpip); - monitor.Print(x, y++, "$%06x(AER): $%02x", A(MFP::AER), mfp.aer); - monitor.Print(x, y++, "$%06x(DDR): $%02x", A(MFP::DDR), mfp.ddr); - monitor.Print(x, y++, "$%06x(VR): $%02x", A(MFP::VR), mfp.GetVR()); + screen.Print(x, y++, "$%06x(GPIP):$%02x", A(MFP::GPIP), mfp_.gpip); + screen.Print(x, y++, "$%06x(AER): $%02x", A(MFP::AER), mfp_.aer); + screen.Print(x, y++, "$%06x(DDR): $%02x", A(MFP::DDR), mfp_.ddr); + screen.Print(x, y++, "$%06x(VR): $%02x", A(MFP::VR), mfp_.GetVR()); // 2列目(割り込み関連) x = 20; y = 0; - monitor.Print(x, y++, "$%06x(IERA):$%02x", A(MFP::IERA), mfp.ier.a); - monitor.Print(x, y++, "$%06x(IERB):$%02x", A(MFP::IERB), mfp.ier.b); - monitor.Print(x, y++, "$%06x(IPRA):$%02x", A(MFP::IPRA), mfp.ipr.a); - monitor.Print(x, y++, "$%06x(IPRB):$%02x", A(MFP::IPRB), mfp.ipr.b); - monitor.Print(x, y++, "$%06x(ISRA):$%02x", A(MFP::ISRA), mfp.isr.a); - monitor.Print(x, y++, "$%06x(ISRB):$%02x", A(MFP::ISRB), mfp.isr.b); - monitor.Print(x, y++, "$%06x(IMRA):$%02x", A(MFP::IMRA), mfp.imr.a); - monitor.Print(x, y++, "$%06x(IMRB):$%02x", A(MFP::IMRB), mfp.imr.b); + screen.Print(x, y++, "$%06x(IERA):$%02x", A(MFP::IERA), mfp_.ier.a); + screen.Print(x, y++, "$%06x(IERB):$%02x", A(MFP::IERB), mfp_.ier.b); + screen.Print(x, y++, "$%06x(IPRA):$%02x", A(MFP::IPRA), mfp_.ipr.a); + screen.Print(x, y++, "$%06x(IPRB):$%02x", A(MFP::IPRB), mfp_.ipr.b); + screen.Print(x, y++, "$%06x(ISRA):$%02x", A(MFP::ISRA), mfp_.isr.a); + screen.Print(x, y++, "$%06x(ISRB):$%02x", A(MFP::ISRB), mfp_.isr.b); + screen.Print(x, y++, "$%06x(IMRA):$%02x", A(MFP::IMRA), mfp_.imr.a); + screen.Print(x, y++, "$%06x(IMRB):$%02x", A(MFP::IMRB), mfp_.imr.b); // 3列目(タイマー関連) x = 40; y = 0; - monitor.Print(x, y++, "$%06x(TACR): $%02x", A(MFP::TACR), mfp.tcr[0]); - monitor.Print(x, y++, "$%06x(TBCR): $%02x", A(MFP::TBCR), mfp.tcr[1]); - monitor.Print(x, y++, "$%06x(TCDCR):$%02x", A(MFP::TCDCR), - (mfp.tcr[2] << 4) | mfp.tcr[3]); - monitor.Print(x, y++, "$%06x(TADR): $%02x", A(MFP::TADR), GetTDR(0)); - monitor.Print(x, y++, "$%06x(TBDR): $%02x", A(MFP::TBDR), GetTDR(1)); - monitor.Print(x, y++, "$%06x(TCDR): $%02x", A(MFP::TCDR), GetTDR(2)); - monitor.Print(x, y++, "$%06x(TDDR): $%02x", A(MFP::TDDR), GetTDR(3)); + screen.Print(x, y++, "$%06x(TACR): $%02x", A(MFP::TACR), mfp_.timer[0].tcr); + screen.Print(x, y++, "$%06x(TBCR): $%02x", A(MFP::TBCR), mfp_.timer[1].tcr); + screen.Print(x, y++, "$%06x(TCDCR):$%02x", A(MFP::TCDCR), mfp_.GetTCDCR()); + screen.Print(x, y++, "$%06x(TADR): $%02x", A(MFP::TADR), tdr_[0]); + screen.Print(x, y++, "$%06x(TBDR): $%02x", A(MFP::TBDR), tdr_[1]); + screen.Print(x, y++, "$%06x(TCDR): $%02x", A(MFP::TCDR), tdr_[2]); + screen.Print(x, y++, "$%06x(TDDR): $%02x", A(MFP::TDDR), tdr_[3]); // 4列目(USART 関連) x = 61; y = 0; - monitor.Print(x, y++, "$%06x(SCR):$%02x", A(MFP::SCR), mfp.scr); - monitor.Print(x, y++, "$%06x(UCR):$%02x", A(MFP::UCR), mfp.ucr); - monitor.Print(x, y++, "$%06x(RSR):$%02x", A(MFP::RSR), mfp.rsr); - monitor.Print(x, y++, "$%06x(TSR):$%02x", A(MFP::TSR), mfp.tsr); - monitor.Print(x, y, "$%06x(UDR):", A(MFP::UDR)); - if ((int16)mfp.udr >= 0) { - monitor.Print(x + 13, y, "$%02x", mfp.udr); + screen.Print(x, y++, "$%06x(SCR):$%02x", A(MFP::SCR), mfp_.scr); + screen.Print(x, y++, "$%06x(UCR):$%02x", A(MFP::UCR), mfp_.ucr); + screen.Print(x, y++, "$%06x(RSR):$%02x", A(MFP::RSR), mfp_.rsr); + screen.Print(x, y++, "$%06x(TSR):$%02x", A(MFP::TSR), mfp_.tsr); + screen.Print(x, y, "$%06x(UDR):", A(MFP::UDR)); + if ((int16)mfp_.udr >= 0) { + screen.Print(x + 13, y, "$%02x", mfp_.udr); } else { - monitor.Print(x + 13, y, "---"); + screen.Print(x + 13, y, "---"); } // 割り込み関連 y = 9; - monitor.Print(0, y++, " %-6s %-4s %-4s %-4s", + screen.Print(0, y++, " %-6s %-4s %-4s %-4s", "IER", "IMR", "IPR", "ISR"); for (int i = 15; i >= 0; i--, y++) { uint16 mask = (1 << i); - monitor.Print(0, y, "%c%d:%-12s %-6s %-4s %-4s %-4s", + screen.Print(0, y, "%c%u:%-12s %-6s %-4s %-4s %-4s", i >= 8 ? 'A' : 'B', (i % 8), intrname[i], - (mfp.ier.w & mask) ? "Enable" : "", - (mfp.imr.w & mask) ? "" : "Mask", // %0 でマスクする - (mfp.ipr.w & mask) ? "Pend" : "", - (mfp.isr.w & mask) ? "Serv" : ""); + (mfp_.ier.w & mask) ? "Enable" : "", + (mfp_.imr.w & mask) ? "" : "Mask", // %0 でマスクする + (mfp_.ipr.w & mask) ? "Pend" : "", + (mfp_.isr.w & mask) ? "Serv" : ""); } // タイマー関連 - x = 39; + x = 40; y = 9; - monitor.Print(x, y++, " Mode TDR Ini"); + screen.Print(x, y++, " Mode TDR Next"); for (int ch = 0; ch < 4; ch++) { - monitor.Print(x, y, "Timer-%c", ch + 'A'); - if (mfp.tcr[ch] == 0) { - monitor.Print(x + 8, y, "Stopped"); - } else if (mfp.tcr[ch] == 8) { - monitor.Print(x + 8, y, "Event"); - } else if (mfp.tcr[ch] > 8) { - monitor.Print(x + 8, y, "Pulse(%s)", prescale_str[mfp.tcr[ch] - 8]); + screen.Print(x, y, "Timer-%c", ch + 'A'); + uint8 tcr = mfp_.timer[ch].tcr; + if (tcr == 0) { + screen.Print(x + 8, y, "Stopped"); + } else if (tcr == 8) { + screen.Print(x + 8, y, "Event"); + } else if (tcr > 8) { + screen.Print(x + 8, y, "Pulse(%s)", prescale_str[tcr - 8]); } else { - monitor.Print(x + 8, y, "Delay(%s)", prescale_str[mfp.tcr[ch]]); + screen.Print(x + 8, y, "Delay(%s)", prescale_str[tcr]); } - monitor.Print(x + 19, y, "%3d", GetTDR(ch)); - monitor.Print(x + 23, y, "%3d", mfp.initial_tdr[ch]); + screen.Print(x + 20, y, "%3u", tdr_[ch]); + screen.Print(x + 25, y, "%3u", mfp_.timer[ch].next_tdr & 0xff); y++; } // GPIP 関連 - x = 39; + x = 40; y = 9 + 8; - monitor.Puts(x, y++, " GPIP AER DDR"); + screen.Puts(x, y++, " GPIP AER DDR"); for (int i = 7; i >= 0; i--, y++) { uint mask = (1 << i); - monitor.Print(x, y, "b%d %-6s %d %d %s", i, gpipname[i], - (mfp.gpip & mask) ? 1 : 0, - (mfp.aer & mask) ? 1 : 0, - (mfp.ddr & mask) ? "OUT" : "IN"); + screen.Print(x, y, "b%u %-6s %u %u %s", i, gpipname[i], + (mfp_.gpip & mask) ? 1 : 0, + (mfp_.aer & mask) ? 1 : 0, + (mfp_.ddr & mask) ? "OUT" : "IN"); } } @@ -531,7 +577,7 @@ MFPDevice::gpipname[] = { }; // TxCR の設定値から分周期間というかメインカウンタ1回分の時間を返す -/*static*/ const int +/*static*/ const uint MFPDevice::prescale_ns[8] = { 0, 1000, // /4 @@ -556,7 +602,7 @@ MFPDevice::prescale_str[8] = { }; // タイマーのチャンネルから MFP 割り込みチャンネル番号に変換する -/*static*/ const int +/*static*/ const uint MFPDevice::timer_vector[4] = { MFP::INTR_TIMER_A, MFP::INTR_TIMER_B, @@ -564,34 +610,49 @@ MFPDevice::timer_vector[4] = { MFP::INTR_TIMER_D, }; +// IER[AB] に data をセットする +void +MFPDevice::SetIER(uint8& ier, uint8& ipr, uint32 data) +{ + // clr は、data によって %1 -> %0 に変化するビットだけを立てたもの。 + uint8 clr = ier & (ier ^ data); + + // IER を下げたところは IPR も下げる。(紙資料 p4-4) + ipr &= ~clr; + + // どちらにしても新しい IER によって信号線の状態は変える。 + ier = data; + ChangeInterrupt(); +} + // TxCR をセットする void MFPDevice::SetTCR(int ch, uint32 data) { uint32 prev; - prev = mfp.tcr[ch]; + prev = mfp.timer[ch].tcr; data &= 0x0f; - mfp.tcr[ch] = data; + mfp.timer[ch].tcr = data; if (data == 0) { if (prev == 0) { // 0 -> 0 なら何も起きない return; } else if (prev < 8) { // タイマー停止 - putlog(1, "Timer-%c 停止", ch + 'A'); + putlog(1, "Timer-%c Stop", ch + 'A'); // ここで TDR を確定させる。 // タイマー動作中の TDR の参照はイベント終了時刻から現在値を逆算 // していたが、イベントを停止すると求められなくなるので、イベント // 停止前に求めておく。 uint64 period = prescale_ns[prev]; - uint64 now = gMPU->GetVirtTime(); - mfp.tdr[ch] = ((event[ch].vtime - now) + period - 1) / period; + uint64 now = scheduler->GetVirtTime(); + mfp.timer[ch].tdr = ((event[ch].vtime - now) + period - 1) / period; // でイベント停止 - event[ch].Stop(); + scheduler->StopEvent(event[ch]); return; } else if (prev == 8) { // イベントカウントモードから停止しても何も起きないはず @@ -603,16 +664,14 @@ MFPDevice::SetTCR(int ch, uint32 data) } else if (data < 8) { // タイマー開始 - // TDR をリロード(?) - mfp.tdr[ch] = mfp.initial_tdr[ch]; - // タイマースタート - uint64 period = prescale_ns[mfp.tcr[ch]]; - event[ch].time = period * (mfp.tdr[ch] ?: 256); - event[ch].Start(); + // TDR は触らずイベントを開始。 + uint64 period = prescale_ns[mfp.timer[ch].tcr]; + event[ch].time = period * mfp.timer[ch].tdr; + scheduler->RestartEvent(event[ch]); - putlog(1, "Timer-%c ディレイモード開始(%u x %u.%uusec)", + putlog(1, "Timer-%c Start Delay mode(%u x %u.%uusec)", ch + 'A', - mfp.initial_tdr[ch], + mfp.timer[ch].tdr, (uint)period / 1000, (uint)(period / 100) % 10); return; @@ -621,12 +680,12 @@ MFPDevice::SetTCR(int ch, uint32 data) // イベントカウントモード // TDR をロードする - mfp.initial_tdr[ch] = mfp.tdr[ch]; - putlog(1, "Timer-%c イベントカウントモード", ch + 'A'); + mfp.timer[ch].next_tdr = mfp.timer[ch].tdr; + putlog(1, "Timer-%c Event count mode", ch + 'A'); return; } else { - putlog(0, "T%cCR パルス幅測定モード $%02x 未実装", + putlog(0, "T%cCR Pulse-Width Measurement Mode $%02x (NOT IMPLEMENTED)", ch + 'A', data); return; } @@ -637,23 +696,24 @@ MFPDevice::SetTCR(int ch, uint32 data) uint8 MFPDevice::GetTDR(int ch) const { - uint8 tcr = mfp.tcr[ch]; + uint8 tcr = mfp.timer[ch].tcr; if (tcr == 0) { // 停止中なら tdr に現在値が保持されている - return mfp.tdr[ch]; + return mfp.timer[ch].tdr & 0xff; } else if (tcr < 8) { // ディレイモード動作中なら現在値は保持していないので、 // プリスケーラと終了時刻から算出。 uint64 period = prescale_ns[tcr]; - uint64 now = gMPU->GetVirtTime(); - return ((event[ch].vtime - now) + period - 1) / period; + uint64 now = scheduler->GetVirtTime(); + uint8 data = ((event[ch].vtime - now) + period - 1) / period; + return data; } else { // イベントモード、パルス幅測定モードは未対応 - return mfp.tdr[ch]; + return mfp.timer[ch].tdr & 0xff; } } @@ -662,22 +722,25 @@ MFPDevice::GetTDR(int ch) const void MFPDevice::SetTDR(int ch, uint32 data) { - uint8& tcr = mfp.tcr[ch]; + uint8& tcr = mfp.timer[ch].tcr; + + if (data == 0) { + data = 256; + } + mfp.timer[ch].next_tdr = data; if (tcr == 0) { - // 停止中の書き込みは両方更新 - mfp.tdr[ch] = data; - mfp.initial_tdr[ch] = data; + // 停止中の書き込みはメインカウンタも更新。 + mfp.timer[ch].tdr = data; } else if (tcr < 8) { - // ディレイモード動作中の書き込み - // メインカウンタは影響を受けず、次回値を更新するだけ。 - mfp.initial_tdr[ch] = data; + // ディレイモード動作中の書き込みは + // メインカウンタは影響を受けない。 } else { // イベントモード、パルス幅測定モードは未対応 - putlog(0, "T%cDR サポートしてないモード中の書き込み $%02x", - ch + 'A', data); + putlog(0, "Write T%cDR $%02x during unsupported mode (NOT IMPLEMENTED)", + ch + 'A', mfp.timer[ch].next_tdr); } } @@ -688,7 +751,7 @@ MFPDevice::TimerCallback(Event& ev) int& ch = ev.code; // TDR がゼロになったところで呼ばれるので、TDR をリロードする - mfp.tdr[ch] = mfp.initial_tdr[ch]; + mfp.timer[ch].tdr = mfp.timer[ch].next_tdr; // 割り込みを上げる SetInterrupt(timer_vector[ch]); @@ -698,13 +761,16 @@ MFPDevice::TimerCallback(Event& ev) // period が1メインパルス期間 // time が1タイムアウトパルス期間 - uint64 period = prescale_ns[mfp.tcr[ch]]; - uint64 time = period * (mfp.tdr[ch] ?: 256); - // 次のタイムアウトパルスは、今回のタイムアウト時刻から数える - // (現在時刻はそれを通り過ぎた任意の時刻なので) - uint64 now = gMPU->GetVirtTime(); - ev.time = ev.vtime + time - now; - ev.Start(); + uint64 period = prescale_ns[mfp.timer[ch].tcr]; + ev.time = period * mfp.timer[ch].tdr; + scheduler->RestartEvent(ev); +} + +// HSync 入力 +void +MFPDevice::SetHSync(bool hsync) +{ + SetGPIP(MFP::GPIP_HSYNC, hsync); } // VDisp 入力 @@ -712,15 +778,15 @@ void MFPDevice::SetVDisp(bool vdisp) { // TAI への入力はイベントカウントモードの時のみ - if (mfp.tcr[0] == 8) { + if (mfp.timer[0].tcr == 8) { // AER が示す方向と同じエッジ方向の時 bool aer = (mfp.aer & (1 << MFP::GPIP_VDISP)); if (aer == vdisp) { // カウンタを減算 - mfp.tdr[0]--; - if (mfp.tdr[0] == 0) { + mfp.timer[0].tdr--; + if (mfp.timer[0].tdr == 0) { // 0 になったらリロード - mfp.tdr[0] = mfp.initial_tdr[0]; + mfp.timer[0].tdr = mfp.timer[0].next_tdr; SetInterrupt(MFP::INTR_TIMER_A); } @@ -731,6 +797,20 @@ MFPDevice::SetVDisp(bool vdisp) SetGPIP(MFP::GPIP_VDISP, vdisp); } +// 電源ボタン状態入力 (PowerDevice から呼ばれる) +void +MFPDevice::SetPowSW(bool powsw) +{ + SetGPIP(MFP::GPIP_POWSW, powsw); +} + +// ALARM 信号入力 (RTC の ALARM_OUT 出力端子) +void +MFPDevice::SetAlarmOut(bool alarm) +{ + SetGPIP(MFP::GPIP_ALARM, alarm); +} + // GPIP への入力。 // num はビット番号(0..7)。 void @@ -750,7 +830,17 @@ MFPDevice::SetGPIP(int num, bool val) // 1 1 0 0 // 1 1 1 0 if ((old != val) && (aer == val)) { - // XXX ここで割り込みを上げる + // ここで割り込みを上げる + // XXX GPIP ビット番号と INTR の対応どうなってんの + int intr; + if (num < 4) { + intr = num; + } else if (num < 6) { + intr = num + 2; + } else { + intr = num - MFP::GPIP_CIRQ + MFP::INTR_CRTC_IRQ; + } + SetInterrupt(intr); } if (val) { @@ -767,13 +857,13 @@ MFPDevice::SetUCR(uint32 data) mfp.ucr = data; if ((mfp.ucr & MFP::UCR_CLK) == 0) { - putlog(0, "UCR CLK=0 未実装"); + putlog(0, "UCR CLK=0 (NOT IMPLEMENTED)"); } if (((mfp.ucr & MFP::UCR_WL) >> 5) != 0) { - putlog(0, "UCR WL=%d 未実装", (mfp.ucr >> 5) & 3); + putlog(0, "UCR WL=%u (NOT IMPLEMENTED)", (mfp.ucr >> 5) & 3); } if (((mfp.ucr & MFP::UCR_ST) >> 3) != 1) { - putlog(0, "UCR ST=%d 未実装", (mfp.ucr >> 3) & 3); + putlog(0, "UCR ST=%u (NOT IMPLEMENTED)", (mfp.ucr >> 3) & 3); } } @@ -787,12 +877,34 @@ MFPDevice::SetRSR(uint32 data) if ((old & MFP::RSR_RE) == 0 && (mfp.rsr & MFP::RSR_RE)) { // RE 0 -> 1 - rr = true; } if ((old & MFP::RSR_RE) && (mfp.rsr & MFP::RSR_RE) == 0) { // RE 1 -> 0 - rr = false; - mfp.rsr &= 0x0f; // 上位4ビットのステータスを %0 に + // 上位4ビットのステータスを %0 に + mfp.rsr &= ~(MFP::RSR_OE | MFP::RSR_PE | MFP::RSR_FE); + ClearBF(); + } +} + +// RSR::BF (Buffer Full) をセットする +void +MFPDevice::SetBF() +{ + mfp.rsr |= MFP::RSR_BF; +} + +// RSR::BF (Buffer Full) をクリアする +void +MFPDevice::ClearBF() +{ + mfp.rsr &= ~MFP::RSR_BF; + + // バッファが空いたのでキーボードに次の転送を催促。 + // 実際のハードウェアでは、キーボードが本体 MFP が Ready になるのを待って + // いるが、それに相当。 + // システムポートの KEYCTRL はキーボード側で処理している。 + if (RxIsReady()) { + keyboard->Ready(); } } @@ -801,46 +913,77 @@ void MFPDevice::SetUDR(uint32 data) { if ((mfp.tsr & MFP::TSR_TE)) { - gKeyboard->Command(data); + keyboard->Command(data); } } -// システムポート#4 の KEY CTRL の状態を受け取る。 -void -MFPDevice::SetKeyCtrl(bool val) +// __ +// RR 線の状態を返す。アサートなら true。 +bool +MFPDevice::IsRR() const { - key_ctrl = val; - putlog(1, "KEYCTRL = %d", key_ctrl); + // __ + // 実際の RR 信号線は、BF が立っていて、PE | FE がクリアされている場合に + // アサートされる。ただしエミュレータでは Parity Error も Frame Error も + // 起きないので、この2ビットは無視する。 + return ((mfp.rsr & MFP::RSR_BF) != 0); } -// RR (Receiver Ready) 線の状態を返す。true なら Ready。 -// これはシステムポート#4 の KEY CTRL の状態も加味したもの。 +// キーボードからの受信が可能なら true を返す。 bool -MFPDevice::IsRR() const +MFPDevice::RxIsReady() const { - return rr && key_ctrl; + // __ + // RR は本来 MFP から CPU/DMAC など上位に対しての Ready で、 + // UDR にデータが用意できたので引き取ってほしいの意。 + // X680x0 ではこれを CPU/DMAC (上位) 向けではなく、下位のキーボードからの + // フロー制御に流用しており、 + // 上位への(本来の)「UDR にデータが用意できた」(IsRR() = true) は、 + // キーボードに対しては「UDR にデータがあるので送信禁止」、 + // 上位への(本来の)「UDR にデータが用意できていない」(IsRR() = false) は、 + // キーボードに対しては「UDR は空いているので送信してよい」と + // ここで意味が論理反転することに注意。 + // + // また、システムポートの KEYCTRL は X680x0Keyboard 側で処理しているので + // こちらでは不要。 + // + // UDR が空いていてキーボードからデータが送られてきている間は RR は Ready + // だが、そもそもキーボードは送出作業中で、新たな送信はできないはずなので + // この場合、つまり key_event の動作中も false (送信不可) にする。 + + return !IsRR() && !key_event.IsRunning(); } // キーボードからデータを受信 void MFPDevice::Rx(uint32 data) { - // Ready を下げる - rr = false; + // ここは受信したビットをシフトレジスタへ入れ始めたところに相当する + // ので、まだ Buffer Full は立てない。 key_event.code = data; - key_event.Start(); + scheduler->RestartEvent(key_event); } // キーボードからのデータが受信し終わった頃に呼ばれるイベント。 void MFPDevice::KeyCallback(Event& ev) { - // Ready を元に戻す - rr = true; - + // UDR にデータを置いて Buffer Full をセット mfp.udr = ev.code; - mfp.rsr |= MFP::RSR_BF; + SetBF(); + + // Manual 7-5 7.2.1 Receiver Interrupt Channels より + // | 割り込みは1文字受信ごとに1回しか発生しませんが、2つの専用割り込み + // | チャネルにより、正常な受信状態と異常な受信状態に別々のベクター番号を + // | 割り当てることができます。受信したワードにエラーがあり、エラー割り込み + // | チャネルが有効な場合は、エラーチャネルのみに割り込みが発生します。 + // | しかし、エラーチャネルが無効の場合、エラー状態の割り込みは、 + // | バッファフル状態の割り込みと一緒にバッファフル割り込みチャネルに + // | 生成されます。どのエラー条件で割り込みが発生したかを判断するためには、 + // | 常にRSRを読み出す必要があります。 + // XXX: まだエラー状態は実装されていない、なおエラーは起きないので + // メモだけのままかも知れない。 SetInterrupt(MFP::INTR_RXFULL); } @@ -874,28 +1017,74 @@ MFPDevice::ChangeInterrupt() // ペンディングの割り込みがあればアサートされる、 // IPR をクリアするか IMR をクリアすると /IRQ はネゲートされる。 bool irq = ((mfp.ipr.w & mfp.imr.w) != 0); - gInterrupt->ChangeINT(this, irq); + interrupt->ChangeINT(this, irq); } // 割り込みアクノリッジ -int +busdata MFPDevice::InterruptAcknowledge() { + busdata data; + + // IPR が誰も立ってなければ、無視するっぽい? + // 実際には MFP がバスエラーを出しているのではなく、MFP は俺知らない + // と思って放置してると上位回路 (たぶん SAKI ちゃん) がタイムアウトして + // MPU にバスエラー応答する。 uint32 pending = mfp.ipr.w; + if (__predict_false(pending == 0)) { + data.SetBusErr(); + } else { + // ch はこの時点で IPR に立ってる一番優先度の高いチャンネルのビット + int ch = 31 - __builtin_clz(pending); + uint b = (1U << ch); - // ch はこの時点で IPR に立ってる一番優先度の高いチャンネルのビット - // XXX IPR にビット立ってなかったらどうなる? - int ch = 31 - __builtin_clz(pending); - uint b = (1U << ch); + // MPU にベクタを引き渡したので Pending をクリア + mfp.ipr.w &= ~b; + ChangeInterrupt(); - // MPU にベクタを引き渡したので Pending をクリア - mfp.ipr.w &= ~b; - ChangeInterrupt(); + // ソフトウェア EOI なら In Service を立てる + if (mfp.vr_s) { + mfp.isr.w |= b; + } - // ソフトウェア EOI なら In Service を立てる - if (mfp.vr_s) { - mfp.isr.w |= b; + data = mfp.vr_vec + ch; } - return mfp.vr_vec + ch; + // /IEI ネゲート時の /IACK と /IEI の関係がいまいち分からないけど、 + // とりあえず /IACK がアサートされてから 3 MFP クロック後に + // データバスにベクタを供給、次の 1 MFP クロックで DTACK 応答。 + // で次の 1 MFP クロック以内に CPU が /IACK を下げるとして、 + // 計 5 MFP クロック (= 1.25_usec) としておく。 + const busdata intack_wait = busdata::Wait(5 * 250_nsec); + data |= intack_wait; + + return data; } + +/*static*/ const char * +MFPDevice::regname[MFP::RegMax] = { + "GPIP", + "AER", + "DDR", + "IERA", + "IERB", + "IPRA", + "IPRB", + "ISRA", + "ISRB", + "IMRA", + "IMRB", + "VR", + "TACR", + "TBCR", + "TCDCR", + "TADR", + "TBDR", + "TCDR", + "TDDR", + "SCR", + "UCR", + "RSR", + "TSR", + "UDR", +};