--- nono/vm/x68kkbd.cpp 2026/04/29 17:05:07 1.1.1.9 +++ nono/vm/x68kkbd.cpp 2026/04/29 17:05:50 1.1.1.19 @@ -4,6 +4,10 @@ // Licensed under nono-license.txt // +// +// X68k キーボード +// + // 送信タイミングについて // キーボードは MFP の USART に繋がっている。 // MFP USART の RR 線 (Receiver Ready) は受信可能なら 'H'、受信中は 'L' に @@ -18,26 +22,22 @@ // SHIFT キーについては keyboard.cpp のコメントを参照。 #include "x68kkbd.h" +#include "event.h" #include "mfp.h" +#include "monitor.h" #include "scc.h" +#include "scheduler.h" #include "sysport.h" +#include "uimessage.h" // コンストラクタ X68030Keyboard::X68030Keyboard() { led.resize(7); - rept_event.func = (DeviceCallback_t)&X68030Keyboard::ReptCallback; - rept_event.time = 0; - rept_event.SetName("Keyboard Repeat"); - - for (int i = 0; i < mouse_event.size(); i++) { - mouse_event[i].dev = this; - mouse_event[i].func = (DeviceCallback_t)&X68030Keyboard::MouseCallback; - // 700us 後から 11bit を 4800 bps の速度で送る - mouse_event[i].time = 700_usec + 1_sec * 11 * (i + 1) / 4800; - mouse_event[i].SetName(string_format("Mouse Data %d", i)); - } + monitor = gMonitorManager->Regist(ID_MONITOR_KEYBOARD, this); + monitor->func = ToMonitorCallback(&X68030Keyboard::MonitorUpdate); + monitor->SetSize(47, 5); } // デストラクタ @@ -45,25 +45,120 @@ X68030Keyboard::~X68030Keyboard() { } -// 本体リセットスイッチによるリセット -void -X68030Keyboard::ResetHard() +// 初期化 +bool +X68030Keyboard::Init() { - inherited::ResetHard(); + if (inherited::Init() == false) { + return false; + } + + mfp = GetMFPDevice(); + scc = GetSCCDevice(); + sysport = GetSysportDevice(); + auto evman = GetEventManager(); + rept_event = evman->Regist(this, + ToEventCallback(&X68030Keyboard::ReptCallback), + "Keyboard Repeat"); + + mouse_event = evman->Regist(this, + ToEventCallback(&X68030Keyboard::MouseCallback), + "Mouse"); + + return true; +} + +// キーボード接続時の MD 固有処理 +void +X68030Keyboard::MDConnect() +{ + rept_delay_data = 3; rept_delay = 500_msec; + rept_time_data = 4; rept_time = 110_msec; rept_key = KC_none; - rept_event.Stop(); + scheduler->StopEvent(rept_event); - for (int i = 0; i < led.size(); i++) { - led[i] = false; - } led_darkness = 0; - for (int i = 0; i < mouse_event.size(); i++) { - mouse_event[i].Stop(); + // XXX マウスの接続取り外しは未定 + scheduler->StopEvent(mouse_event); + mouse_event->code = 0; +} + +// キーボード取り外し時の MD 固有処理 +void +X68030Keyboard::MDDisconnect() +{ + scheduler->StopEvent(rept_event); + scheduler->StopEvent(mouse_event); + mouse_event->code = 0; +} + +// モニター +void +X68030Keyboard::MonitorUpdate(Monitor *, TextScreen& screen) +{ + int y; + + screen.Clear(); + y = 0; + + // 0 1 2 3 4 + // 01234567890123456789012345678901234567890123456 + // Key Enable: % MSCTRL: % + // Ctrl Enable: OPT.2 Enable: % + // Key Repeat: Delay $x(????msec) + // Repeat $x(????msec) + // LED: $xx Zen Hira INS CAPS Code Roma Kana + // LED Darkness: x + + // KEY_EN, CTRL_EN, OPT2_EN, MSCTRL は未実装 + + // キーリピート + screen.Puts(0, y, "Key Repeat:"); + screen.Print(12, y++, "Delay $%x(%4umsec)", + rept_delay_data, (uint)(rept_delay / 1_msec)); + screen.Print(12, y++, "Repeat $%x(%4umsec)", + rept_time_data, (uint)(rept_time / 1_msec)); + + // マウス + int mx = mouse_x; + int my = mouse_y; + if (mx < -999) + mx = -999; + if (mx > 999) + mx = 999; + if (my < -999) + my = -999; + if (my > 999) + my = 999; + + screen.Puts(0, y, "Mouse:"); + screen.Print(12, y, "x=%-4d y=%-4d", mx, my); + screen.Print(27, y, TA::OnOff(mouse_l), "L"); + screen.Print(29, y, TA::OnOff(mouse_r), "R"); + y++; + + // LED + screen.Puts(0, y, "LED:"); + static const char * const ledname[] = { + "Zen", "Hira", "INS", "CAPS", "Code", "Roma", "Kana" + }; + uint val = 0x80; + for (int i = 0; i < 7; i++) { + int n = 6 - i; + screen.Puts(12 + i * 5, y, TA::OnOff(led[n]), ledname[i]); + if (led[n] == 0) { + val |= 1U << n; + } } + screen.Print(5, y, "$%02x", val); + y++; + + // LED の明るさ (というより暗さ) + screen.Print(0, y++, "LED Darkness: %u", led_darkness); } // 共通 keystat を X680x0 キーコードに変換して返す。 @@ -106,12 +201,10 @@ X68030Keyboard::KeyInput(uint keystat) // どのキーコードで問い合わせをされても KC_SHIFT_L と KC_SHIFT_R の OR が // [SHIFT] キーの状態である。 if (keycode == KC_SHIFT_L || keycode == KC_SHIFT_R) { - // KC_SHIFT_L, R に対する pressed は内部の OR 処理用 - pressed[keycode] = ismake; - if (keycode == KC_SHIFT_L && pressed[KC_SHIFT_R]) { - return; - } - if (keycode == KC_SHIFT_R && pressed[KC_SHIFT_L]) { + // それぞれのシフトキーが押されたことは覚えておく。 + SetPressed(keycode, ismake); + // 反対側のキーが押されていたら、いま押した・離したほうは無視する + if (IsPressed(keycode ^ 1)) { return; } keycode = KC_SHIFT; @@ -130,29 +223,28 @@ X68030Keyboard::KeyInput(uint keystat) // ここでキーリピートのイベント開始処理 if (ismake) { // いま起きているリピートは中止 - rept_event.Stop(); rept_key = keycode; - rept_event.time = rept_delay; - rept_event.Start(); + rept_event->time = rept_delay; + scheduler->RestartEvent(rept_event); } else { if (keycode == rept_key) { // 同じキーのブレークでのみリピートを終了する - rept_event.Stop(); + scheduler->StopEvent(rept_event); rept_key = KC_none; } } } void -X68030Keyboard::ReptCallback(Event& ev) +X68030Keyboard::ReptCallback(Event *ev) { // リピート sendqueue.Enqueue(KeyToMDKey(rept_key)); SendStart(); // 二回目以降のリピートは rept_time 間隔で発生 - rept_event.time = rept_time; - rept_event.Start(); + rept_event->time = rept_time; + scheduler->StartEvent(rept_event); } // MFP、システムポートと、キーボードの関係。 @@ -164,11 +256,11 @@ X68030Keyboard::ReptCallback(Event& ev) // <- 本体 || キーボード -> // MFP || // --------+ : || +----- -// | : +--+ || | -// RR|o-------| | || | -// --------+ : | |-----|T0 (Ready) -// +-----| | || | -// Sysport | : +--+ || | +// | : |& || | +// RR|o-------|& || | +// --------+ : |&---------|T0 (Ready) +// +-----|& || | +// Sysport | : |& || | // --------+ | : || | // KeyCtrl|--+ : || +----- // | : || @@ -178,7 +270,7 @@ X68030Keyboard::ReptCallback(Event& ev) void X68030Keyboard::SendStart() { - if (gMFP->RxIsReady()) { + if (mfp->RxIsReady()) { // 本体が Ready ならここで送信 Ready(); } else { @@ -193,7 +285,7 @@ X68030Keyboard::SendStart() void X68030Keyboard::Ready() { - if (gSysport->GetKeyCtrl()) { + if (sysport->GetKeyCtrl()) { // KEYCTRL が有効なら処理する // ここで取り出せるのは MDKey。 @@ -202,12 +294,12 @@ X68030Keyboard::Ready() // あれば送信 // 呼ばれたからには送信できるはず - assert(gMFP->RxIsReady() == true); + assert(mfp->RxIsReady() == true); assert(x68kkey != 0); // 送信 - gMFP->Rx(x68kkey); + mfp->Rx(x68kkey); } else { // なければ何もせず終了 } @@ -238,8 +330,9 @@ X68030Keyboard::MouseSendStart() { uint8 status = 0; - // 最後のバイトを送るまでは、次は受け付けられない - if (mouse_event[2].IsRunning()) { + // 最後のバイトを送るまでは、次は受け付けられない。 + // 送信中かどうかは mouse_event->code で判断している。 + if (mouse_event->code != 0) { return; } @@ -262,23 +355,44 @@ X68030Keyboard::MouseSendStart() status |= mouse_l ? SW_L : 0; status |= mouse_r ? SW_R : 0; - mouse_event[0].code = status; - mouse_event[1].code = mouse_x; - mouse_event[2].code = mouse_y; + // この時点でラッチしてみる(?) + mouse_data[0] = status; + mouse_data[1] = mouse_x; + mouse_data[2] = mouse_y; mouse_x = 0; mouse_y = 0; - mouse_event[0].Start(); - mouse_event[1].Start(); - mouse_event[2].Start(); + // 1バイト目は MSCTRL が立ち下がってから 700us 以上後から送信。 + // さらに1バイトの送信に 2.3msec かかる(このすぐ下参照)。 + mouse_event->time = 700_usec + 2300_usec; + mouse_event->code = 1; + scheduler->RestartEvent(mouse_event); } +// マウスデータのイベントコールバック void -X68030Keyboard::MouseCallback(Event& ev) +X68030Keyboard::MouseCallback(Event *ev) { - // LUNA とオブジェクトが違うだけ - gSCC->Rx(1, ev.code); + // マウスは 4800bps で、ストップビット 2 なので11ビット。 + // 1/4800 * 11 = 2.29166 msec なので、1バイトあたり 2.3 msec にしておく。 + + // code 1 なら1バイト目 (mouse_data[0]) の送信完了時刻が来た。 + // code 2 なら2バイト目 (mouse_data[1]) の送信完了時刻が来た。 + // code 3 なら3バイト目 (mouse_data[2]) の送信完了時刻が来た。 + // 3バイト目を送信してイベントループ終了。 + + scc->Rx(1, mouse_data[mouse_event->code - 1]); + + if (mouse_event->code < 3) { + // 次のイベント + mouse_event->time = 2300_usec; + mouse_event->code++; + scheduler->StartEvent(mouse_event); + } else { + // 3 バイト目の送信が終わった。 + mouse_event->code = 0; + } } // ホストからの制御 @@ -288,60 +402,64 @@ X68030Keyboard::Command(uint32 data) if (data < 0x40) { // $00-$3f TV 制御 // (実装不要) - putlog(2, "Command TV 制御 $%02x", (data & 0x1f)); + putlog(2, "Command TV control $%02x", (data & 0x1f)); } else if (data < 0x48) { // $40-$47 MSCTRL 信号制御 // こいつは頻度が高いのでログレベルを上げておく - putlog(3, "Command MSCTRL 制御 %d", (data & 1)); + putlog(3, "Command MSCTRL control %u", (data & 1)); } else if (data < 0x50) { // $48-$4f キーデータ送出許可 - putlog(1, "Command キー送出許可 %d (未実装)", (data & 1)); + putlog(1, "Command Key send enable %u (NOT IMPLEMENTED)", (data & 1)); } else if (data < 0x54) { // $50-$53 ディスプレイ制御モード選択 // (実装不要) - putlog(2, "Command ディスプレイ制御 X68000モード %d", (data & 1)); + putlog(2, "Command Display control mode %u", (data & 1)); } else if (data < 0x58) { // $54-$57 LED 明るさ選択 // 0 が明るく 3 が暗いので、明るさというより暗さ。 led_darkness = (data & 3); - putlog(1, "Command LED 明るさ %d", led_darkness); + putlog(1, "Command LED brightness %u", led_darkness); + uimessage->Post(UIMessage::KEYBOARD); } else if (data < 0x5c) { // $58-$5b 本体からのディスプレイ制御許可 // (実装不要) - putlog(2, "Command 本体からディスプレイ制御 %d", (data & 1)); + putlog(2, "Command Display control enable %u", (data & 1)); } else if (data < 0x60) { // $5c-$5f OPT.2 によるディスプレイ制御許可 // (実装不要) - putlog(2, "Command OPT2からディスプレイ制御 %d", (data & 1)); + putlog(2, "Command OPT2 control enable %u", (data & 1)); } else if (data < 0x70) { // $60-$6f リピートまでの時間 - rept_delay = 200_msec + (data & 15) * 100_msec; - putlog(1, "Command Repeat Delay %d (%d msec)", (data & 15), - (int)(rept_delay / 1_msec)); + rept_delay_data = data & 15; + rept_delay = 200_msec + rept_delay_data * 100_msec; + putlog(1, "Command Repeat Delay %u (%u msec)", rept_delay_data, + (uint)(rept_delay / 1_msec)); } else if (data < 0x80) { // $70-$7f リピート間隔 - rept_time = 30_msec + (data & 15) * (data & 15) * 5_msec; - putlog(1, "Command Repeat time %d (%d msec)", (data & 15), - (int)(rept_time / 1_msec)); + rept_time_data = data & 15; + rept_time = 30_msec + rept_time_data * rept_time_data * 5_msec; + putlog(1, "Command Repeat time %d (%u msec)", rept_time_data, + (uint)(rept_time / 1_msec)); } else { // $80-$ff LED 制御 data &= 0x7f; - putlog(1, "Command LED 制御 $%02x", data); + putlog(1, "Command LED control $%02x", data); uint32 b = 0x01; for (int i = 0; i < led.size(); i++) { // data は %0 で点灯、led[] は true で点灯 led[i] = ((data & b) == 0); b <<= 1; } + uimessage->Post(UIMessage::KEYBOARD); } } @@ -351,7 +469,7 @@ X68030Keyboard::Command(uint32 data) X68030Keyboard::keycode2x68kkey_table[KC_max] = { NoKey, // [00] X68kKey_BREAK, // [01] - X68kKey_COPY, // [02] + NoKey, // [02] X68kKey_romaji, // [03] X68kKey_code, // [04] X68kKey_kigou, // [05] @@ -369,7 +487,7 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_ESC, // [10] X68kKey_BS, // [11] X68kKey_enter, // [12] - X68kKey_INS, // [13] + NoKey, // [13] X68kKey_space, // [14] X68kKey_DEL, // [15] NoKey, // [16] @@ -383,8 +501,8 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_right, // [1e] X68kKey_down, // [1f] - X68kKey_ROLLUP, // [20] - X68kKey_ROLLDOWN, // [21] + X68kKey_INS, // [20] + X68kKey_COPY, // [21] X68kKey_1, // [22] X68kKey_2, // [23] X68kKey_3, // [24] @@ -400,8 +518,8 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_backslash, // [2e] NoKey, // [2f] - X68kKey_OPT1, // [30] - X68kKey_OPT2, // [31] + NoKey, // [30] + NoKey, // [31] X68kKey_Q, // [32] X68kKey_W, // [33] X68kKey_E, // [34] @@ -417,7 +535,7 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_hira, // [3e] X68kKey_zenkaku, // [3f] - X68kKey_HOME, // [40] + NoKey, // [40] X68kKey_UNDO, // [41] X68kKey_A, // [42] X68kKey_S, // [43] @@ -431,8 +549,8 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_semicolon, // [4b] X68kKey_colon, // [4c] X68kKey_bracketright, // [4d] - NoKey, // [4e] - NoKey, // [4f] + X68kKey_OPT1, // [4e] + X68kKey_OPT2, // [4f] X68kKey_XF1, // [50] X68kKey_XF2, // [51] @@ -451,7 +569,7 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_XF4, // [5e] X68kKey_XF5, // [5f] - NoKey, // [60] + X68kKey_HOME, // [60] X68kKey_PAD_plus, // [61] X68kKey_PAD_minus, // [62] X68kKey_PAD_7, // [63] @@ -468,8 +586,8 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_PAD_enter, // [6e] X68kKey_PAD_CLR, // [6f] - NoKey, // [70] - NoKey, // [71] + X68kKey_ROLLUP, // [70] + X68kKey_ROLLDOWN, // [71] X68kKey_F1, // [72] X68kKey_F2, // [73] X68kKey_F3, // [74]