--- nono/vm/mfp.cpp 2026/04/29 17:04:52 1.1.1.9 +++ nono/vm/mfp.cpp 2026/04/29 17:05:14 1.1.1.13 @@ -4,45 +4,53 @@ // Licensed under nono-license.txt // +// +// MFP (MC68901) +// + #include "mfp.h" #include "interrupt.h" #include "keyboard.h" -#include "mpu680x0.h" +#include "mpu.h" +#include "scheduler.h" #include "x68kkbd.h" -std::unique_ptr gMFP; +// グローバル参照用 +MFPDevice *gMFP; // コンストラクタ MFPDevice::MFPDevice() + : inherited("MFP") { - logname = "mfp"; - devname = "MFP"; devaddr = baseaddr; devlen = 0x2000; - monitor_size = nnSize(77, 26); - for (int ch = 0; ch < countof(event); ch++) { + 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')); + event[ch].Regist(string_format("MFP Timer-%c", ch + 'A')); } // キーボード周りは 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"); + key_event.Regist("MFP USART"); + + monitor.func = ToMonitorCallback(&MFPDevice::MonitorUpdate); + monitor.SetSize(77, 26); + monitor.Regist(ID_MONITOR_MFP); } // デストラクタ MFPDevice::~MFPDevice() { + gMFP = NULL; } -// デバイスリセット +// リセット void -MFPDevice::ResetHard() +MFPDevice::ResetHard(bool poweron) { // All internal registers are cleared expect the TxDR, UDR, and TSR. mfp.aer = 0; @@ -62,10 +70,10 @@ MFPDevice::ResetHard() mfp.tsr = MFP::TSR_BE; // All timers are stopped. - for (int ch = 0; ch < countof(event); ch++) { - event[ch].Stop(); + for (auto& ev : event) { + gScheduler->StopEvent(ev); } - key_event.Stop(); + gScheduler->StopEvent(key_event); // USART RX and TX are disabled. // SO line is placed in HighZ. @@ -77,6 +85,9 @@ MFPDevice::ResetHard() // VR is initialized to $00 (not $0f). mfp.vr_vec = 0; mfp.vr_s = 0; + + // CIRQ | FMIRQ | EXPON | ALARM にしておく + mfp.gpip = 0x6b; } uint64 @@ -175,16 +186,16 @@ MFPDevice::Write(uint32 offset, uint32 d 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, "AER <- $%02x", 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; @@ -259,7 +270,7 @@ MFPDevice::Write(uint32 offset, uint32 d 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: @@ -271,7 +282,10 @@ MFPDevice::Write(uint32 offset, uint32 d 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: @@ -374,18 +388,18 @@ MFPDevice::Peek(uint32 offset) } void -MFPDevice::MonitorUpdate(TextScreen& monitor) +MFPDevice::MonitorUpdate(Monitor *, TextScreen& screen) { int x; int y; - monitor.Clear(); + 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) @@ -393,53 +407,53 @@ 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), + screen.Print(x, y++, "$%06x(TACR): $%02x", A(MFP::TACR), mfp.tcr[0]); + screen.Print(x, y++, "$%06x(TBCR): $%02x", A(MFP::TBCR), mfp.tcr[1]); + screen.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(TADR): $%02x", A(MFP::TADR), GetTDR(0)); + screen.Print(x, y++, "$%06x(TBDR): $%02x", A(MFP::TBDR), GetTDR(1)); + screen.Print(x, y++, "$%06x(TCDR): $%02x", A(MFP::TCDR), GetTDR(2)); + screen.Print(x, y++, "$%06x(TDDR): $%02x", A(MFP::TDDR), GetTDR(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)); + 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) { - monitor.Print(x + 13, y, "$%02x", mfp.udr); + 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%d:%-12s %-6s %-4s %-4s %-4s", i >= 8 ? 'A' : 'B', (i % 8), intrname[i], @@ -450,33 +464,33 @@ MFPDevice::MonitorUpdate(TextScreen& mon } // タイマー関連 - x = 39; + x = 40; y = 9; - monitor.Print(x, y++, " Mode TDR Ini"); + screen.Print(x, y++, " Mode TDR Ini"); for (int ch = 0; ch < 4; ch++) { - monitor.Print(x, y, "Timer-%c", ch + 'A'); + screen.Print(x, y, "Timer-%c", ch + 'A'); if (mfp.tcr[ch] == 0) { - monitor.Print(x + 8, y, "Stopped"); + screen.Print(x + 8, y, "Stopped"); } else if (mfp.tcr[ch] == 8) { - monitor.Print(x + 8, y, "Event"); + screen.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 + 8, y, "Pulse(%s)", prescale_str[mfp.tcr[ch] - 8]); } else { - monitor.Print(x + 8, y, "Delay(%s)", prescale_str[mfp.tcr[ch]]); + screen.Print(x + 8, y, "Delay(%s)", prescale_str[mfp.tcr[ch]]); } - monitor.Print(x + 19, y, "%3d", GetTDR(ch)); - monitor.Print(x + 23, y, "%3d", mfp.initial_tdr[ch]); + screen.Print(x + 20, y, "%3d", GetTDR(ch)); + screen.Print(x + 24, y, "%3d", mfp.initial_tdr[ch]); 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], + screen.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"); @@ -565,18 +579,18 @@ MFPDevice::SetTCR(int ch, uint32 data) 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(); + uint64 now = gScheduler->GetVirtTime(); mfp.tdr[ch] = ((event[ch].vtime - now) + period - 1) / period; // でイベント停止 - event[ch].Stop(); + gScheduler->StopEvent(event[ch]); return; } else if (prev == 8) { // イベントカウントモードから停止しても何も起きないはず @@ -592,12 +606,13 @@ MFPDevice::SetTCR(int ch, uint32 data) 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(); + uint count = mfp.tdr[ch] ?: 256; + event[ch].time = period * count; + gScheduler->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], + count, (uint)period / 1000, (uint)(period / 100) % 10); return; @@ -607,11 +622,11 @@ MFPDevice::SetTCR(int ch, uint32 data) // TDR をロードする mfp.initial_tdr[ch] = mfp.tdr[ch]; - putlog(1, "Timer-%c イベントカウントモード", ch + 'A'); + 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; } @@ -633,7 +648,7 @@ MFPDevice::GetTDR(int ch) const // プリスケーラと終了時刻から算出。 uint64 period = prescale_ns[tcr]; - uint64 now = gMPU->GetVirtTime(); + uint64 now = gScheduler->GetVirtTime(); return ((event[ch].vtime - now) + period - 1) / period; } else { @@ -655,13 +670,13 @@ MFPDevice::SetTDR(int ch, uint32 data) mfp.initial_tdr[ch] = data; } else if (tcr < 8) { - // ディレイモード動作中の書き込み + // ディレイモード動作中の書き込み。 // メインカウンタは影響を受けず、次回値を更新するだけ。 mfp.initial_tdr[ch] = data; } else { // イベントモード、パルス幅測定モードは未対応 - putlog(0, "T%cDR サポートしてないモード中の書き込み $%02x", + putlog(0, "Write T%cDR $%02x during unsupported mode (NOT IMPLEMENTED)", ch + 'A', data); } } @@ -684,12 +699,15 @@ 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(); + ev.time = period * (mfp.tdr[ch] ?: 256); + gScheduler->RestartEvent(ev); +} + +// HSync 入力 +void +MFPDevice::SetHSync(bool hsync) +{ + SetGPIP(MFP::GPIP_HSYNC, hsync); } // VDisp 入力 @@ -716,6 +734,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 @@ -735,7 +767,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) { @@ -752,13 +794,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=%d (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=%d (NOT IMPLEMENTED)", (mfp.ucr >> 3) & 3); } } @@ -798,7 +840,7 @@ MFPDevice::ClearBF() // 実際のハードウェアでは、キーボードが本体 MFP が Ready になるのを待って // いるが、それに相当。 // システムポートの KEYCTRL はキーボード側で処理している。 - X68030Keyboard *x68kkbd = dynamic_cast(gKeyboard.get()); + X68030Keyboard *x68kkbd = dynamic_cast(gKeyboard); x68kkbd->Ready(); } @@ -856,7 +898,7 @@ MFPDevice::Rx(uint32 data) // ので、まだ Buffer Full は立てない。 key_event.code = data; - key_event.Start(); + gScheduler->RestartEvent(key_event); } // キーボードからのデータが受信し終わった頃に呼ばれるイベント。