--- nono/vm/mfp.cpp 2026/04/29 17:04:50 1.1.1.8 +++ nono/vm/mfp.cpp 2026/04/29 17:04:52 1.1.1.9 @@ -8,6 +8,7 @@ #include "interrupt.h" #include "keyboard.h" #include "mpu680x0.h" +#include "x68kkbd.h" std::unique_ptr gMFP; @@ -76,9 +77,6 @@ MFPDevice::ResetHard() // VR is initialized to $00 (not $0f). mfp.vr_vec = 0; mfp.vr_s = 0; - - // 信号線 - rr = false; } uint64 @@ -159,7 +157,9 @@ MFPDevice::Read(uint32 offset) data = mfp.tsr; break; case MFP::UDR: + // UDR からデータを読み出したら Buffer Full をクリア data = mfp.udr; + ClearBF(); break; default: data = 0xff; @@ -772,15 +772,36 @@ 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 はキーボード側で処理している。 + X68030Keyboard *x68kkbd = dynamic_cast(gKeyboard.get()); + x68kkbd->Ready(); +} + // UDR への書き込み void MFPDevice::SetUDR(uint32 data) @@ -790,28 +811,49 @@ MFPDevice::SetUDR(uint32 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(); @@ -821,11 +863,21 @@ MFPDevice::Rx(uint32 data) 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); }