--- nono/vm/mfp.cpp 2026/04/29 17:04:29 1.1.1.2 +++ nono/vm/mfp.cpp 2026/04/29 17:04:42 1.1.1.6 @@ -1,14 +1,16 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // +#include "keyboard.h" #include "mfp.h" -#include "mpu.h" +#include "mpu680x0.h" #include "mystring.h" #include "scheduler.h" -MFPDevice *gMFP; +std::unique_ptr gMFP; // コンストラクタ MFPDevice::MFPDevice() @@ -17,7 +19,7 @@ MFPDevice::MFPDevice() devname = "MFP"; devaddr = baseaddr; devlen = 0x2000; - monitor.Init(65, 17); + monitor_size = nnSize(65, 17); for (int ch = 0; ch < countof(event); ch++) { event[ch].dev = this; @@ -25,6 +27,12 @@ MFPDevice::MFPDevice() event[ch].code = ch; event[ch].SetName(string_format("MFP Timer-%c", ch + 'A')); } + + // キーボード周りは x68kkbd.cpp のコメント参照 + key_event.dev = this; + key_event.func = (DeviceCallback_t)&MFPDevice::KeyCallback; + key_event.time = 3750_usec; + key_event.SetName("MFP USART"); } // デストラクタ @@ -32,37 +40,70 @@ MFPDevice::~MFPDevice() { } +// 本体リセットスイッチによるリセット +void +MFPDevice::ResetHard() +{ + Reset(); +} + // MPU の RESET 命令によるリセット void MFPDevice::ResetSoft() { - // TxDR, UDR, TSR はクリアされない - - // ? - mfp.tsr = 0x80; + Reset(); +} - // それ以外のすべてのレジスタはクリア? - mfp.vr = 0; +// リセット(共通) +void +MFPDevice::Reset() +{ + // All internal registers are cleared expect the TxDR, UDR, and TSR. + 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; + mfp.timer_start[i] = 0; + } + mfp.scr = 0; + mfp.ucr = 0; + mfp.rsr = 0; + // XXX TSR は変化しないがたぶんバッファはクリアされるので BE は立つ + // ということかな。 + mfp.tsr = MFP::TSR_BE; - // タイマーはすべてストップなのでイベントを削除 + // All timers are stopped. for (int ch = 0; ch < countof(event); ch++) { - gScheduler->DelEvent(&event[ch]); + event[ch].Stop(); } + key_event.Stop(); - // ペンディング割り込みクリア + // USART RX and TX are disabled. + // SO line is placed in HighZ. + // The interrupt channels are disabled, and + // any pending interrupts are cleared. + // GPIO lines are placed in HighZ. + // timer outputs are driven low. + // External signals are nagated. + // VR is initialized to $00 (not $0f). + mfp.vr = 0; + // 信号線 + rr = false; } uint64 -MFPDevice::Read8(uint32 addr) +MFPDevice::Read(uint32 addr) { uint8 data; - if (addr % 2 == 0) - return (uint64)-1; + gMPU->AddCycle(24); // InsideOut p.135 - int n = (addr - baseaddr) / 2; - switch (n) { + switch (addr) { case MFP::GPIP: data = mfp.gpip; break; @@ -136,25 +177,18 @@ MFPDevice::Read8(uint32 addr) data = mfp.udr; break; default: - return (uint64)-1; + data = 0xff; + break; } return data; } uint64 -MFPDevice::Read16(uint32 addr) +MFPDevice::Write(uint32 addr, uint32 data) { - return (0xff00 | Read8(addr + 1)); -} + gMPU->AddCycle(24); // InsideOut p.135 -uint64 -MFPDevice::Write8(uint32 addr, uint32 data) -{ - if (addr % 2 == 0) - return (uint64)-1; - - int n = (addr - baseaddr) / 2; - switch (n) { + switch (addr) { case MFP::GPIP: putlog(0, "GPIP <- $%02x 未実装書き込み", data); mfp.gpip = data; @@ -239,43 +273,38 @@ MFPDevice::Write8(uint32 addr, uint32 da mfp.scr = data; break; case MFP::UCR: - putlog(0, "UCR <- $%02x 未実装書き込み", data); - mfp.ucr = data & 0xfe; + putlog(2, "UCR <- $%02x", data); + SetUCR(data); break; case MFP::RSR: - putlog(0, "RSR <- $%02x 未実装書き込み", data); - mfp.rsr = data; + putlog(2, "RSR <- $%02x", data); + SetRSR(data); break; case MFP::TSR: putlog(0, "TSR <- $%02x 未実装書き込み", data); mfp.tsr = (mfp.tsr & 0xf0) | (data & 0x0f); break; case MFP::UDR: - putlog(0, "UDR <- $%02x 未実装書き込み", data); - mfp.udr = data; + if (data == 0x40 || data == 0x41) { + // MSCTRL だけ頻度が高いのでログレベルを上げておく + putlog(3, "UDR <- $%02x", data); + } else { + putlog(2, "UDR <- $%02x", data); + } + SetUDR(data); break; default: - return (uint64)-1; + break; } return 0; } uint64 -MFPDevice::Write16(uint32 addr, uint32 data) -{ - return Write8(addr + 1, data); -} - -uint64 -MFPDevice::Peek8(uint32 addr) +MFPDevice::Peek(uint32 addr) { uint8 data; - if (addr % 2 == 0) { - return (uint64)-1; - } - int n = (addr - baseaddr) / 2; - switch (n) { + switch (addr) { case MFP::GPIP: data = mfp.gpip; break; @@ -350,13 +379,12 @@ MFPDevice::Peek8(uint32 addr) break; default: data = 0xff; - break; } return data; } -bool -MFPDevice::MonitorUpdate() +void +MFPDevice::MonitorUpdate(TextScreen& monitor) { int x; int y; @@ -407,7 +435,7 @@ MFPDevice::MonitorUpdate() // GPIP 関連 x = 39; y = 8; - monitor.Print(x, y++, "GPIP Name GPIP AER DDR"); + monitor.Puts(x, y++, "GPIP Name 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], @@ -415,8 +443,6 @@ MFPDevice::MonitorUpdate() (mfp.aer & mask) ? 1 : 0, (mfp.ddr & mask) ? "OUT" : "IN"); } - - return true; } /*static*/ const char * @@ -478,13 +504,18 @@ MFPDevice::prescale_str[8] = { // タイマーのチャンネルからベクタ(ビット番号) に変換する /*static*/ const int -MFPDevice::timer_vector[4] = { 13, 8, 5, 4 }; +MFPDevice::timer_vector[4] = { + MFP::INTR_TIMER_A, + MFP::INTR_TIMER_B, + MFP::INTR_TIMER_C, + MFP::INTR_TIMER_D, +}; // このタイマーで割り込みを上げる必要があるかどうかを返す bool MFPDevice::IsTimerIntrEnable(uint ch) const { - assert(ch < 4); + assertmsg(ch < 4, "ch=%u", ch); __assume(ch < 4); // 割り込みが無効なら false @@ -530,7 +561,7 @@ MFPDevice::SetTCR(int ch, uint32 data) putlog(1, "Timer-%c 停止", ch + 'A'); } mfp.timer_start[ch] = 0; - gScheduler->DelEvent(&event[ch]); + event[ch].Stop(); return; } else { @@ -577,7 +608,7 @@ MFPDevice::SetTCR(int ch, uint32 data) // 他には何もしなくてよい。Timer-B 対策。 if (IsTimerIntrEnable(ch)) { event[ch].time = mfp.initial_tdr[ch] * scale; - gScheduler->AddEvent(&event[ch]); + event[ch].Start(); } putlog(1, "Timer-%c ディレイモード開始(%d.%d x %d usec)", @@ -593,7 +624,7 @@ MFPDevice::SetTCR(int ch, uint32 data) // タイマー開始時刻と減算量が分かっているので読み出しのたびに求める。 // この操作に副作用はないので Peek/Monitor でも使用可能。 uint8 -MFPDevice::GetTDR(int ch) +MFPDevice::GetTDR(int ch) const { uint64 ellapsed; uint64 count; @@ -667,15 +698,20 @@ MFPDevice::SetTDR(int ch, uint32 data) ellapsed = gScheduler->GetVirtTime() - mfp.timer_start[ch]; // プリスケールカウント時間 (上でいう1カウントの時間) scale = prescale_ns[mfp.tcr[ch]]; - // 1タイマー期間(上でいう50カウントに相当する時間) - period = scale * mfp.initial_tdr[ch]; - // タイマー開始時から次のタイムアップ時刻までの時間 - timer_end = ((ellapsed + period - 1) / period) * period; - // タイムアップ時刻 - timer_end = mfp.timer_start[ch] + timer_end; + if (__predict_true(scale > 0)) { + // 1タイマー期間(上でいう50カウントに相当する時間) + period = scale * mfp.initial_tdr[ch]; + // タイマー開始時から次のタイムアップ時刻までの時間 + timer_end = ((ellapsed + period - 1) / period) * period; + // タイムアップ時刻 + timer_end = mfp.timer_start[ch] + timer_end; + } else { + // タイムアップ時刻 + timer_end = mfp.timer_start[ch]; + } // 現在から次のタイムアップ時刻まで event[ch].time = timer_end - gScheduler->GetVirtTime(); - gScheduler->AddEvent(&event[ch]); + event[ch].Start(); putlog(3, "Timer-%c TDR 更新予約 %d.%09d", ch + 'A', (int)(event[ch].time / (1000 * 1000 * 1000)), @@ -685,9 +721,9 @@ MFPDevice::SetTDR(int ch, uint32 data) // イベントコールバック void -MFPDevice::TimerCallback(int arg) +MFPDevice::TimerCallback(Event& ev) { - int ch = arg; + int& ch = ev.code; int prevtdr; uint64 ellapsed; uint64 scale; @@ -717,11 +753,11 @@ MFPDevice::TimerCallback(int arg) // 1タイマー期間を新しい TDR で再計算 period = prescale_ns[mfp.tcr[ch]] * mfp.initial_tdr[ch]; // イベント再設定 - event[ch].time = period; - gScheduler->AddEvent(&event[ch]); + ev.time = period; + ev.Start(); // 割り込みを上げる - gMPU->Interrupt(6, mfp.vr + timer_vector[ch]); + gMPU680x0->Interrupt(this, 6, mfp.vr + timer_vector[ch]); } if (mfp.initial_tdr[ch] != prevtdr) { @@ -775,7 +811,7 @@ MFPDevice::SetGPIP(int num, bool val) // 1 0 1 1 // 1 1 0 0 // 1 1 1 0 - if ((old != val) & (aer == val)) { + if ((old != val) && (aer == val)) { // XXX ここで割り込みを上げる } @@ -785,3 +821,93 @@ MFPDevice::SetGPIP(int num, bool val) mfp.gpip &= ~(1 << num); } } + +// UCR への書き込み +void +MFPDevice::SetUCR(uint32 data) +{ + mfp.ucr = data; + + if ((mfp.ucr & MFP::UCR_CLK) == 0) { + putlog(0, "UCR CLK=0 未実装"); + } + if (((mfp.ucr & MFP::UCR_WL) >> 5) != 0) { + putlog(0, "UCR WL=%d 未実装", (mfp.ucr >> 5) & 3); + } + if (((mfp.ucr & MFP::UCR_ST) >> 3) != 1) { + putlog(0, "UCR ST=%d 未実装", (mfp.ucr >> 3) & 3); + } +} + +// RSR への書き込み +void +MFPDevice::SetRSR(uint32 data) +{ + uint32 old = mfp.rsr; + + mfp.rsr = 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 に + } +} + +// UDR への書き込み +void +MFPDevice::SetUDR(uint32 data) +{ + if ((mfp.tsr & MFP::TSR_TE)) { + gKeyboard->Command(data); + } +} + +// システムポート#4 の KEY CTRL の状態を受け取る。 +void +MFPDevice::SetKeyCtrl(bool val) +{ + key_ctrl = val; + putlog(1, "KEYCTRL = %d", key_ctrl); +} + +// RR (Receiver Ready) 線の状態を返す。true なら Ready。 +// これはシステムポート#4 の KEY CTRL の状態も加味したもの。 +bool +MFPDevice::IsRR() const +{ + return rr && key_ctrl; +} + +// キーボードからデータを受信 +void +MFPDevice::Rx(uint32 data) +{ + // Ready を下げる + rr = false; + + key_event.code = data; + key_event.Start(); +} + +// キーボードからのデータが受信し終わった頃に呼ばれるイベント。 +void +MFPDevice::KeyCallback(Event& ev) +{ + // Ready を元に戻す + rr = true; + + mfp.udr = ev.code; + mfp.rsr |= MFP::RSR_BF; + + // XXX この辺要再考察 + mfp.isr.a |= 0x10; + // 割り込み有効でマスクされてない時だけ + if ((mfp.ier.a & 0x10) && (mfp.imr.a & 0x10)) { + gMPU680x0->Interrupt(this, 6, mfp.vr + MFP::INTR_RXFULL); + } +}