--- nono/vm/mfp.h 2026/04/29 17:04:50 1.1.1.7 +++ nono/vm/mfp.h 2026/04/29 17:06:00 1.1.1.20 @@ -4,37 +4,46 @@ // Licensed under nono-license.txt // +// +// MFP (MC68901) +// + #pragma once #include "device.h" -#include "scheduler.h" +#include + +class InterruptDevice; +class Syncer; +class X68030Keyboard; struct MFP { - static const int GPIP = 0; // $E88001 - static const int AER = 1; // $E88003 - static const int DDR = 2; // $E88005 - static const int IERA = 3; // $E88007 - static const int IERB = 4; // $E88009 - static const int IPRA = 5; // $E8800B - static const int IPRB = 6; // $E8800D - static const int ISRA = 7; // $E8800F - static const int ISRB = 8; // $E88011 - static const int IMRA = 9; // $E88013 - static const int IMRB = 10; // $E88015 - static const int VR = 11; // $E88017 - static const int TACR = 12; // $E88019 - static const int TBCR = 13; // $E8801B - static const int TCDCR = 14; // $E8801D - static const int TADR = 15; // $E8801F - static const int TBDR = 16; // $E88021 - static const int TCDR = 17; // $E88023 - static const int TDDR = 18; // $E88025 - static const int SCR = 19; // $E88027 - static const int UCR = 20; // $E88029 - static const int RSR = 21; // $E8802B - static const int TSR = 22; // $E8802D - static const int UDR = 23; // $E8802F + static const uint GPIP = 0; // $E88001 + static const uint AER = 1; // $E88003 + static const uint DDR = 2; // $E88005 + static const uint IERA = 3; // $E88007 + static const uint IERB = 4; // $E88009 + static const uint IPRA = 5; // $E8800B + static const uint IPRB = 6; // $E8800D + static const uint ISRA = 7; // $E8800F + static const uint ISRB = 8; // $E88011 + static const uint IMRA = 9; // $E88013 + static const uint IMRB = 10; // $E88015 + static const uint VR = 11; // $E88017 + static const uint TACR = 12; // $E88019 + static const uint TBCR = 13; // $E8801B + static const uint TCDCR = 14; // $E8801D + static const uint TADR = 15; // $E8801F + static const uint TBDR = 16; // $E88021 + static const uint TCDR = 17; // $E88023 + static const uint TDDR = 18; // $E88025 + static const uint SCR = 19; // $E88027 + static const uint UCR = 20; // $E88029 + static const uint RSR = 21; // $E8802B + static const uint TSR = 22; // $E8802D + static const uint UDR = 23; // $E8802F + static const uint RegMax = 24; union ir16 { uint16 w; @@ -52,14 +61,14 @@ struct MFP uint8 gpip; uint8 aer; uint8 ddr; - static const int GPIP_HSYNC = 7; - static const int GPIP_CIRQ = 6; + static const uint GPIP_HSYNC = 7; + static const uint GPIP_CIRQ = 6; // 5 は常に '1' - static const int GPIP_VDISP = 4; - static const int GPIP_FMIRQ = 3; - static const int GPIP_POWSW = 2; - static const int GPIP_EXPON = 1; - static const int GPIP_ALARM = 0; + static const uint GPIP_VDISP = 4; + static const uint GPIP_FMIRQ = 3; + static const uint GPIP_POWSW = 2; + static const uint GPIP_EXPON = 1; + static const uint GPIP_ALARM = 0; static const uint INTR_RTC_ALARM = 0; static const uint INTR_EXPON = 1; @@ -82,40 +91,60 @@ struct MFP ir16 isr; ir16 imr; - // - // | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | + // b7 b6 b5 b4 b3 b2 b1 b0 + // +-----+-----+-----+-----+-----+-----+-----+-----+ // VR | V7 | V6 | V5 | V4 | S | --- | --- | --- | + // +-----+-----+-----+-----+-----+-----+-----+-----+ + // | | | | | // | | | | +------------ In-Service Register Enable // +-----+-----+-----+------------------ Vector Number uint8 vr_vec; // ベクタの上位4ビット uint8 vr_s; // %1:ソフトウェアEOI(ISR有効)、%0:自動EOI(ISR無効) uint8 GetVR() const { return vr_vec | vr_s; } - // TDR のカウンタをメインカウンタと呼び、このメインカウンタを - // 1減らすためのカウンタをプリスケーラカウンタと呼ぶ。 - // 実際のハードウェアは 4MHz のクロック入力ごとにプリスケーラカウンタを - // カウントしていき指定回数カウントすればメインカウンタを1減らすのだが、 - // この実装ではどちらのカウンタも持たず、タイマー終了時刻と現在時刻で - // 管理する。そのためメインカウンタの値はこの時間差から求める。 - - // tcr は書き込み値、現在のモードを保持する。 - uint8 tcr[4]; // --- --- --- RST AC3 AC2 AC1 AC0 : TACR - // --- --- --- RST BC3 BC2 BC1 BC0 : TBCR - // --- CC2 CC1 CC0 --- DC2 DC1 DC0 : TCDCR - - // tdr はカウンタの現在値。 - uint8 tdr[4]; - - // initial_tdr はカウントダウン開始時点での TxDR の値。 - // カウンタが 1 から 0 になるタイミングで tdr の値をリロードする。 - // tdr に $00 を書き込むと 256 カウントになるため、 - // initial_tdr は 1..256 の値をとる。 - uint initial_tdr[4]; + // タイマー + struct Timer { + // タイマー停止中は、カウンタの現在値 (1-256) を保持。 + // タイマー動作中は、現在カウントダウン中のカウントの初期値(1-256)。 + // TxDR レジスタ値際は、タイマー動作中/停止中どちらでも + // これではなく GetTDR() を使うこと。 + uint32 tdr; + + // 次回の TxDR のリロード値 (1-256)。 + uint32 next_tdr; + + // タイマーの開始時刻。 + uint64 start; + + // tcr は書き込み値、現在のモードを保持する。 + // TACR: --- --- --- RST AC3 AC2 AC1 AC0 + // TBCR: --- --- --- RST BC3 BC2 BC1 BC0 + // TCDCR: --- CC2 CC1 CC0 --- DC2 DC1 DC0 + uint8 tcr; + + bool IsStopped() const noexcept { return (tcr == 0); } + bool IsDelay() const noexcept { return (0 < tcr && tcr < 8); } + bool IsEvent() const noexcept { return (tcr == 8); } + bool IsPulseWidth() const noexcept { return (tcr > 8); } + + // 現在時刻 now とメインカウンタ周期 period から + // 現在のメインカウンタの値を求める。 + // タイマー動作中のみ呼ぶこと。 + uint8 GetCounter(uint64 now, uint64 period) const; + } timer[4]; + + // TCDCR の値 + uint8 GetTCDCR() const { + return (timer[2].tcr << 4) | timer[3].tcr; + } uint8 scr; - // | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | + // b7 b6 b5 b4 b3 b2 b1 b0 + // +-----+-----+-----+-----+-----+-----+-----+-----+ // UCR | CLK | WL | ST | PE | E/O | --- | + // +-----+-----+-----+-----+-----+-----+-----+-----+ + // | | | | | // | | | | +--------- %1で偶数パリティ // | | | +--------------- %1でパリティエラー // | | +------------------- スタート/ストップビット @@ -132,8 +161,11 @@ struct MFP static const uint32 UCR_EO = 0x02; uint8 ucr; - // | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | + // b7 b6 b5 b4 b3 b2 b1 b0 + // +-----+-----+-----+-----+-----+-----+-----+-----+ // RSR | BF | OE | PE | FE | B | CIP | SS | RE | + // +-----+-----+-----+-----+-----+-----+-----+-----+ + // | | | | | | | | // | | | | | | | +--- %1 RX Enable // | | | | | | +--------- Sync Stop // | | | | | +--------------- 非同期なら @@ -154,8 +186,11 @@ struct MFP static const uint32 RSR_RE = 0x01; uint8 rsr; - // | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | + // b7 b6 b5 b4 b3 b2 b1 b0 + // +-----+-----+-----+-----+-----+-----+-----+-----+ // TSR | BE | UE | AT | END | B | H | L | TE | + // +-----+-----+-----+-----+-----+-----+-----+-----+ + // | | | | // | | | +--- %1 TX Enable // | | +--------------------------- %1 TX Disabled // | +--------------------------------------- %1 Underrun Error @@ -175,31 +210,39 @@ struct MFP uint16 udr; }; -class MFPDevice - : public IODevice +class MFPDevice : public IODevice { using inherited = IODevice; - static const int baseaddr = 0xe88000; + static const uint32 baseaddr = 0xe88000; public: MFPDevice(); - virtual ~MFPDevice() override; + ~MFPDevice() override; - void ResetHard() override; - void MonitorUpdate(TextScreen&) override; + bool Init() override; + void ResetHard(bool poweron) override; // 割り込みアクノリッジ - int InterruptAcknowledge(); + busdata InterruptAcknowledge(); + + // HSync 入力 + void SetHSync(bool hsync); // VDisp 入力 void SetVDisp(bool vdisp); + // 電源ボタン状態入力 + void SetPowSW(bool powsw); + + // ALARM 信号入力 + void SetAlarmOut(bool alarm); + // KEY CTRL 状態を設定する void SetKeyCtrl(bool ctrl); - // RR 線の状態を返す - bool IsRR() const; + // キーボードからの送信が可能なら true を返す。 + bool RxIsReady() const; // キーボードからデータを受信 void Rx(uint32 data); @@ -207,14 +250,19 @@ class MFPDevice protected: // BusIO インタフェース static const uint32 NPORT = 0x20; - uint64 Read(uint32 offset); - uint64 Write(uint32 offset, uint32 data); - uint64 Peek(uint32 offset); + busdata ReadPort(uint32 offset); + busdata WritePort(uint32 offset, uint32 data); + busdata PeekPort(uint32 offset); + bool PokePort(uint32 offset, uint32 data); private: + void SetIER(uint16 data); void SetTCR(int ch, uint32 data); uint8 GetTDR(int ch) const; void SetTDR(int ch, uint32 data); + void RestartTimerEvent(int ch); + void StopTimerEvent(int ch); + bool IsTimerIntrEnable(int ch) const; // GPIP の信号状態をセットする void SetGPIP(int num, bool val); @@ -223,9 +271,15 @@ class MFPDevice void SetRSR(uint32 data); void SetUDR(uint32 data); + // RSR::BF ビットの状態を変える + void SetBF(); + void ClearBF(); + // MFP の RR 線の状態を返す + bool IsRR() const; + // イベントコールバック - void TimerCallback(Event& ev); - void KeyCallback(Event& ev); + void TimerCallback(Event *); + void KeyCallback(Event *); // 必要なら割り込みを上げる void SetInterrupt(uint ch); @@ -233,21 +287,36 @@ class MFPDevice // 割り込み信号線の状態を変える void ChangeInterrupt(); + DECLARE_MONITOR_SCREEN(MonitorScreen); + struct MFP mfp {}; - Event event[4] {}; - Event key_event {}; + std::array event {}; + Event *key_event {}; + + // 各タイマーが刻んでいる時刻 (時間軸) + std::array stime {}; + + // 各タイマーの周期 + std::array rt_period {}; + + // タイマーを実時間に追従する場合は true。 + bool sync_rt {}; - // Receiver Ready 線の状態 - bool rr {}; + Monitor *monitor {}; - // システムポート#4 の KEY CTRL 状態 - bool key_ctrl {}; + InterruptDevice *interrupt {}; + Syncer *syncer {}; + X68030Keyboard *keyboard {}; + static const busdata wait; static const char *intrname[]; static const char *gpipname[]; - static const int prescale_ns[8]; + static const uint64 prescale_tsec[8]; static const char *prescale_str[8]; - static const int timer_vector[4]; + static const uint timer_vector[4]; + static const char *regname[MFP::RegMax]; }; -extern std::unique_ptr gMFP; +inline MFPDevice *GetMFPDevice() { + return Object::GetObject(OBJ_MFP); +}