--- nono/vm/x68kkbd.cpp 2026/04/29 17:04:53 1.1.1.5 +++ nono/vm/x68kkbd.cpp 2026/04/29 17:05:46 1.1.1.18 @@ -4,6 +4,10 @@ // Licensed under nono-license.txt // +// +// X68k キーボード +// + // 送信タイミングについて // キーボードは MFP の USART に繋がっている。 // MFP USART の RR 線 (Receiver Ready) は受信可能なら 'H'、受信中は 'L' に @@ -19,12 +23,20 @@ #include "x68kkbd.h" #include "mfp.h" +#include "monitor.h" +#include "scc.h" +#include "scheduler.h" #include "sysport.h" +#include "uimessage.h" // コンストラクタ X68030Keyboard::X68030Keyboard() { led.resize(7); + + monitor = gMonitorManager->Regist(ID_MONITOR_KEYBOARD, this); + monitor->func = ToMonitorCallback(&X68030Keyboard::MonitorUpdate); + monitor->SetSize(47, 5); } // デストラクタ @@ -32,31 +44,137 @@ X68030Keyboard::~X68030Keyboard() { } -// 本体リセットスイッチによるリセット +// 初期化 +bool +X68030Keyboard::Init() +{ + if (inherited::Init() == false) { + return false; + } + + mfp = GetMFPDevice(); + scc = GetSCCDevice(); + sysport = GetSysportDevice(); + + rept_event.func = ToEventCallback(&X68030Keyboard::ReptCallback); + rept_event.time = 0; + rept_event.SetName("Keyboard Repeat"); + scheduler->RegistEvent(rept_event); + + mouse_event.func = ToEventCallback(&X68030Keyboard::MouseCallback); + mouse_event.SetName("Mouse"); + scheduler->RegistEvent(mouse_event); + + 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; + scheduler->StopEvent(rept_event); + + led_darkness = 0; + + // 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::ResetHard() +X68030Keyboard::MonitorUpdate(Monitor *, TextScreen& screen) { - inherited::ResetHard(); + int y; - rept_delay = 500; - rept_time = 110; + screen.Clear(); + y = 0; - for (int i = 0; i < led.size(); i++) { - led[i] = false; + // 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 |= 1 << n; + } } - led_darkness = 0; + screen.Print(5, y, "$%02x", val); + y++; + + // LED の明るさ (というより暗さ) + screen.Print(0, y++, "LED Darkness: %u", led_darkness); } -// 共通キーコードを X680x0 キーコードに変換して返す。 +// 共通 keystat を X680x0 キーコードに変換して返す。 // 対応する X680x0 のキーがなければ 0 を返す。 uint -X68030Keyboard::KeyToMDKey(uint keycode) const +X68030Keyboard::KeyToMDKey(uint keystat) const { + uint keycode = KC_CODE(keystat); if (keycode >= countof(keycode2x68kkey_table)) { return 0; } - return keycode2x68kkey_table[keycode]; + uint mdkey = keycode2x68kkey_table[keycode]; + if (mdkey && KC_IS_BREAK(keystat)) { + mdkey |= 0x80; + } + return mdkey; } // シフト状態なら true を返す @@ -66,48 +184,67 @@ X68030Keyboard::IsShift() const return pressed[KC_SHIFT]; } -// keycode を前処理して返す。 -uint -X68030Keyboard::PreprocessKey(uint keystat) +// キー入力を1つ処理する +void +X68030Keyboard::KeyInput(uint keystat) { + uint keycode = KC_CODE(keystat); + bool ismake = KC_IS_MAKE(keystat); + + assert(keycode < KC_max); + // X68k のキーボードは左右の [SHIFT] の区別がない。 // KC_SHIFT_L はホストの物理キー左 [SHIFT] に対応、 // KC_SHIFT_R はホストの物理キー右 [SHIFT] に対応、 // KC_SHIFT はソフトウェアキーボードからの左右のない [SHIFT] に対応。 // どのキーコードで問い合わせをされても KC_SHIFT_L と KC_SHIFT_R の OR が // [SHIFT] キーの状態である。 - - uint keycode = KC_CODE(keystat); - if (keycode == KC_SHIFT_L || keycode == KC_SHIFT_R) { - // KC_SHIFT_L, R に対する pressed は内部の OR 処理用 - pressed[keycode] = KC_IS_MAKE(keystat); - if (keycode == KC_SHIFT_L && pressed[KC_SHIFT_R]) { - return KC_none; + // それぞれのシフトキーが押されたことは覚えておく。 + SetPressed(keycode, ismake); + // 反対側のキーが押されていたら、いま押した・離したほうは無視する + if (IsPressed(keycode ^ 1)) { + return; } - if (keycode == KC_SHIFT_R && pressed[KC_SHIFT_L]) { - return KC_none; - } - return KC_SHIFT | KC_FLAG(keystat); - } else { - return keystat; + keycode = KC_SHIFT; + + // keycode を変更したので keystat も更新 + keystat = (keystat & 0xff00) | keycode; } -} -#if 0 -// ProcessKey で処理することがいまのところ無い + // 共通処理へ + if (KeyInputCommon(keystat) == false) { + return; + } -// キー処理 (MD固有部分) -bool -X68030Keyboard::ProcessKey(uint keystat) -{ // LED は MPU 側から点灯/消灯指示が来るのでここでは何もしなくてよい - // XXX ここでキーリピート? + // ここでキーリピートのイベント開始処理 + if (ismake) { + // いま起きているリピートは中止 + rept_key = keycode; + rept_event.time = rept_delay; + scheduler->RestartEvent(rept_event); + } else { + if (keycode == rept_key) { + // 同じキーのブレークでのみリピートを終了する + scheduler->StopEvent(rept_event); + rept_key = KC_none; + } + } +} - return true; +void +X68030Keyboard::ReptCallback(Event& ev) +{ + // リピート + sendqueue.Enqueue(KeyToMDKey(rept_key)); + SendStart(); + + // 二回目以降のリピートは rept_time 間隔で発生 + rept_event.time = rept_time; + scheduler->StartEvent(rept_event); } -#endif // MFP、システムポートと、キーボードの関係。 // @@ -118,11 +255,11 @@ X68030Keyboard::ProcessKey(uint keystat) // <- 本体 || キーボード -> // MFP || // --------+ : || +----- -// | : +--+ || | -// RR|o-------| | || | -// --------+ : | |-----|T0 (Ready) -// +-----| | || | -// Sysport | : +--+ || | +// | : |& || | +// RR|o-------|& || | +// --------+ : |&---------|T0 (Ready) +// +-----|& || | +// Sysport | : |& || | // --------+ | : || | // KeyCtrl|--+ : || +----- // | : || @@ -132,7 +269,7 @@ X68030Keyboard::ProcessKey(uint keystat) void X68030Keyboard::SendStart() { - if (gMFP->RxIsReady()) { + if (mfp->RxIsReady()) { // 本体が Ready ならここで送信 Ready(); } else { @@ -147,27 +284,21 @@ X68030Keyboard::SendStart() void X68030Keyboard::Ready() { - if (gSysport->GetKeyCtrl()) { + if (sysport->GetKeyCtrl()) { // KEYCTRL が有効なら処理する - // ここで取り出せるのは keystat。 - uint keystat; - if (sendqueue.Dequeue(&keystat)) { + // ここで取り出せるのは MDKey。 + uint x68kkey; + if (sendqueue.Dequeue(&x68kkey)) { // あれば送信 // 呼ばれたからには送信できるはず - assert(gMFP->RxIsReady() == true); + assert(mfp->RxIsReady() == true); - uint keycode = KC_CODE(keystat); - // ここで X68k キーコードに変換 (変換できないはずはない) - uint x68kkey = KeyToMDKey(keycode); assert(x68kkey != 0); // 送信 - if (KC_IS_BREAK(keystat)) { - x68kkey |= 0x80; - } - gMFP->Rx(x68kkey); + mfp->Rx(x68kkey); } else { // なければ何もせず終了 } @@ -183,6 +314,84 @@ X68030Keyboard::Ready() void X68030Keyboard::MouseInput(int x, int y, bool rb, bool lb, bool mb) { + // ホストからマウスイベントのたびに呼ばれるので、ここで積算。 + // 20msec ごとのサンプリングのタイミングで積算結果を処理する。 + // X68k のマウスの Y 方向は下に動く方向がプラス。 + mouse_x += x; + mouse_y += y; + mouse_r = rb; + mouse_l = lb; +} + +// マウスデータを VM に送信 +void +X68030Keyboard::MouseSendStart() +{ + uint8 status = 0; + + // 最後のバイトを送るまでは、次は受け付けられない。 + // 送信中かどうかは mouse_event.code で判断している。 + if (mouse_event.code != 0) { + return; + } + + if (mouse_x > 127) { + status |= XOVFL; + mouse_x = 127; + } + if (mouse_x < -128) { + status |= XUNFL; + mouse_x = -128; + } + if (mouse_y > 127) { + status |= YOVFL; + mouse_y = 127; + } + if (mouse_y < -128) { + status |= YUNFL; + mouse_y = -128; + } + status |= mouse_l ? SW_L : 0; + status |= mouse_r ? SW_R : 0; + + // この時点でラッチしてみる(?) + mouse_data[0] = status; + mouse_data[1] = mouse_x; + mouse_data[2] = mouse_y; + + mouse_x = 0; + mouse_y = 0; + + // 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) +{ + // マウスは 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; + } } // ホストからの制御 @@ -192,58 +401,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 + (data & 15) * 100; - putlog(1, "Command Repeat Delay %d (%d msec)", (data & 15), rept_delay); + 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 + (data & 15) * (data & 15) * 5; - putlog(1, "Command Repeat time %d (%d msec)", (data & 15), rept_time); + 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); } } @@ -259,7 +474,7 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_kigou, // [05] X68kKey_touroku, // [06] X68kKey_HELP, // [07] - X68kKey_HOME, // [08] + X68kKey_SHIFT, // [08] X68kKey_TAB, // [09] X68kKey_CTRL, // [0a] X68kKey_kana, // [0b] @@ -300,7 +515,7 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_minus, // [2c] X68kKey_circum, // [2d] X68kKey_backslash, // [2e] - X68kKey_UNDO, // [2f] + NoKey, // [2f] X68kKey_OPT1, // [30] X68kKey_OPT2, // [31] @@ -319,8 +534,8 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_hira, // [3e] X68kKey_zenkaku, // [3f] - NoKey, // [40] - NoKey, // [41] + X68kKey_HOME, // [40] + X68kKey_UNDO, // [41] X68kKey_A, // [42] X68kKey_S, // [43] X68kKey_D, // [44] @@ -336,8 +551,8 @@ X68030Keyboard::keycode2x68kkey_table[KC NoKey, // [4e] NoKey, // [4f] - NoKey, // [50] - NoKey, // [51] + X68kKey_XF1, // [50] + X68kKey_XF2, // [51] X68kKey_Z, // [52] X68kKey_X, // [53] X68kKey_C, // [54] @@ -349,9 +564,9 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_period, // [5a] X68kKey_slash, // [5b] X68kKey_underscore, // [5c] - NoKey, // [5d] - NoKey, // [5e] - NoKey, // [5f] + X68kKey_XF3, // [5d] + X68kKey_XF4, // [5e] + X68kKey_XF5, // [5f] NoKey, // [60] X68kKey_PAD_plus, // [61] @@ -386,11 +601,4 @@ X68030Keyboard::keycode2x68kkey_table[KC X68kKey_PAD_divide, // [7d] X68kKey_PAD_equal, // [7e] X68kKey_PAD_comma, // [7f] - - X68kKey_XF1, // [80] - X68kKey_XF2, // [81] - X68kKey_XF3, // [82] - X68kKey_XF4, // [83] - X68kKey_XF5, // [84] - X68kKey_SHIFT, // [85] };