|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.10! root 7: //
! 8: // X68k キーボード
! 9: //
! 10:
1.1.1.5 root 11: // 送信タイミングについて
12: // キーボードは MFP の USART に繋がっている。
13: // MFP USART の RR 線 (Receiver Ready) は受信可能なら 'H'、受信中は 'L' に
14: // なっている。キーボードは RR が 'H' の時だけデータを送信する。また、
15: // システムポート#4 の KEY CTRL = 0 (キーデータ送信不可) はこの RR を
16: // 'L' に落とす AND 回路になっている。キーボードからの送信は 2400bps、
17: // ストップビット1、パリティなしなので、1バイトの送信に 3.75 msec かかる。
18: // 実装では、キーボードは MFP の RR が 'H' なら MFP にキーデータを送信。
19: // MFP 側で RR を 'L' にして 3.75 msec をイベントで待った後、キーを処理
20: // して RR を 'H' に戻すことにする。
21:
22: // SHIFT キーについては keyboard.cpp のコメントを参照。
1.1.1.4 root 23:
1.1 root 24: #include "x68kkbd.h"
25: #include "mfp.h"
1.1.1.7 root 26: #include "scc.h"
1.1.1.10! root 27: #include "scheduler.h"
1.1.1.5 root 28: #include "sysport.h"
1.1 root 29:
30: // コンストラクタ
31: X68030Keyboard::X68030Keyboard()
32: {
1.1.1.4 root 33: led.resize(7);
1.1.1.6 root 34:
1.1.1.10! root 35: rept_event.func = ToEventCallback(&X68030Keyboard::ReptCallback);
1.1.1.6 root 36: rept_event.time = 0;
1.1.1.10! root 37: rept_event.Regist("Keyboard Repeat");
1.1.1.7 root 38:
1.1.1.10! root 39: mouse_event.func = ToEventCallback(&X68030Keyboard::MouseCallback);
! 40: mouse_event.Regist("Mouse");
1.1.1.3 root 41: }
42:
43: // デストラクタ
44: X68030Keyboard::~X68030Keyboard()
45: {
46: }
47:
1.1.1.10! root 48: // キーボード接続時の MD 固有処理
1.1.1.4 root 49: void
1.1.1.10! root 50: X68030Keyboard::MDConnect()
1.1.1.3 root 51: {
1.1.1.6 root 52: rept_delay = 500_msec;
53: rept_time = 110_msec;
54: rept_key = KC_none;
1.1.1.10! root 55: gScheduler->StopEvent(rept_event);
1.1.1.4 root 56:
57: led_darkness = 0;
1.1.1.7 root 58:
1.1.1.10! root 59: // XXX マウスの接続取り外しは未定
! 60: gScheduler->StopEvent(mouse_event);
! 61: mouse_event.code = 0;
! 62: }
! 63:
! 64: // キーボード取り外し時の MD 固有処理
! 65: void
! 66: X68030Keyboard::MDDisconnect()
! 67: {
! 68: gScheduler->StopEvent(rept_event);
! 69: gScheduler->StopEvent(mouse_event);
! 70: mouse_event.code = 0;
1.1 root 71: }
72:
1.1.1.9 root 73: // 共通 keystat を X680x0 キーコードに変換して返す。
1.1.1.5 root 74: // 対応する X680x0 のキーがなければ 0 を返す。
75: uint
1.1.1.9 root 76: X68030Keyboard::KeyToMDKey(uint keystat) const
1.1 root 77: {
1.1.1.9 root 78: uint keycode = KC_CODE(keystat);
1.1.1.5 root 79: if (keycode >= countof(keycode2x68kkey_table)) {
80: return 0;
81: }
1.1 root 82:
1.1.1.9 root 83: uint mdkey = keycode2x68kkey_table[keycode];
84: if (mdkey && KC_IS_BREAK(keystat)) {
85: mdkey |= 0x80;
86: }
87: return mdkey;
1.1.1.5 root 88: }
89:
90: // シフト状態なら true を返す
91: bool
92: X68030Keyboard::IsShift() const
93: {
94: return pressed[KC_SHIFT];
95: }
1.1 root 96:
1.1.1.6 root 97: // キー入力を1つ処理する
98: void
99: X68030Keyboard::KeyInput(uint keystat)
1.1.1.5 root 100: {
1.1.1.6 root 101: uint keycode = KC_CODE(keystat);
102: bool ismake = KC_IS_MAKE(keystat);
103:
104: assert(keycode < KC_max);
105:
1.1.1.5 root 106: // X68k のキーボードは左右の [SHIFT] の区別がない。
107: // KC_SHIFT_L はホストの物理キー左 [SHIFT] に対応、
108: // KC_SHIFT_R はホストの物理キー右 [SHIFT] に対応、
109: // KC_SHIFT はソフトウェアキーボードからの左右のない [SHIFT] に対応。
110: // どのキーコードで問い合わせをされても KC_SHIFT_L と KC_SHIFT_R の OR が
111: // [SHIFT] キーの状態である。
112: if (keycode == KC_SHIFT_L || keycode == KC_SHIFT_R) {
113: // KC_SHIFT_L, R に対する pressed は内部の OR 処理用
1.1.1.6 root 114: pressed[keycode] = ismake;
1.1.1.5 root 115: if (keycode == KC_SHIFT_L && pressed[KC_SHIFT_R]) {
1.1.1.6 root 116: return;
1.1.1.5 root 117: }
118: if (keycode == KC_SHIFT_R && pressed[KC_SHIFT_L]) {
1.1.1.6 root 119: return;
1.1.1.5 root 120: }
1.1.1.6 root 121: keycode = KC_SHIFT;
1.1.1.8 root 122:
1.1.1.6 root 123: // keycode を変更したので keystat も更新
1.1.1.8 root 124: keystat = (keystat & 0xff00) | keycode;
1.1 root 125: }
1.1.1.5 root 126:
1.1.1.6 root 127: // 共通処理へ
128: if (KeyInputCommon(keystat) == false) {
129: return;
130: }
1.1 root 131:
1.1.1.4 root 132: // LED は MPU 側から点灯/消灯指示が来るのでここでは何もしなくてよい
1.1 root 133:
1.1.1.6 root 134: // ここでキーリピートのイベント開始処理
135: if (ismake) {
136: // いま起きているリピートは中止
137: rept_key = keycode;
138: rept_event.time = rept_delay;
1.1.1.10! root 139: gScheduler->RestartEvent(rept_event);
1.1.1.6 root 140: } else {
141: if (keycode == rept_key) {
142: // 同じキーのブレークでのみリピートを終了する
1.1.1.10! root 143: gScheduler->StopEvent(rept_event);
1.1.1.6 root 144: rept_key = KC_none;
145: }
146: }
147: }
1.1.1.5 root 148:
1.1.1.6 root 149: void
150: X68030Keyboard::ReptCallback(Event& ev)
151: {
152: // リピート
1.1.1.9 root 153: sendqueue.Enqueue(KeyToMDKey(rept_key));
1.1.1.6 root 154: SendStart();
155:
156: // 二回目以降のリピートは rept_time 間隔で発生
157: rept_event.time = rept_time;
1.1.1.10! root 158: gScheduler->StartEvent(rept_event);
1.1 root 159: }
1.1.1.5 root 160:
161: // MFP、システムポートと、キーボードの関係。
162: //
163: // キーボードの Ready 信号は MFP の RR とシステムポートの KeyCtrl の
164: // AND で作られているが、この AND 部分はキーボードが担当することにする。
165: // そのほうが都合がいいため。
166: //
167: // <- 本体 || キーボード ->
168: // MFP ||
169: // --------+ : || +-----
1.1.1.10! root 170: // | : |& || |
! 171: // RR|o-------|& || |
! 172: // --------+ : |&---------|T0 (Ready)
! 173: // +-----|& || |
! 174: // Sysport | : |& || |
1.1.1.5 root 175: // --------+ | : || |
176: // KeyCtrl|--+ : || +-----
177: // | : ||
178: // :エミュレータでは点線から右をキーボードが担当
1.1 root 179:
1.1.1.5 root 180: // キーデータを送信
1.1 root 181: void
1.1.1.5 root 182: X68030Keyboard::SendStart()
1.1 root 183: {
1.1.1.5 root 184: if (gMFP->RxIsReady()) {
185: // 本体が Ready ならここで送信
186: Ready();
187: } else {
188: // そうでない場合、ここでは処理は不要。
189: // MFP 側が受信可能になると Ready() がコールされ、そっちで処理される
1.1 root 190: }
191: }
192:
1.1.1.5 root 193: // MFP からの Ready 通知
194: // 実際にはキーボード側が MFP (を含む本体側) が Ready になるのを待っている
195: // のだが、待っているを作るのは大変なので。
1.1.1.2 root 196: void
1.1.1.5 root 197: X68030Keyboard::Ready()
1.1.1.2 root 198: {
1.1.1.5 root 199: if (gSysport->GetKeyCtrl()) {
200: // KEYCTRL が有効なら処理する
201:
1.1.1.9 root 202: // ここで取り出せるのは MDKey。
203: uint x68kkey;
204: if (sendqueue.Dequeue(&x68kkey)) {
1.1.1.5 root 205: // あれば送信
206:
207: // 呼ばれたからには送信できるはず
208: assert(gMFP->RxIsReady() == true);
209:
210: assert(x68kkey != 0);
211:
212: // 送信
213: gMFP->Rx(x68kkey);
214: } else {
215: // なければ何もせず終了
216: }
217: } else {
218: // KEYCTRL が無効なら何もしないだけでなく、キューをクリア。
219: // KEYCTRL が無効から有効になった時に、無効だった期間のキーデータを
220: // 吐き出すのはおかしいので。
221: sendqueue.Clear();
222: }
1.1.1.2 root 223: }
224:
1.1.1.5 root 225: // マウス入力
1.1.1.4 root 226: void
1.1.1.5 root 227: X68030Keyboard::MouseInput(int x, int y, bool rb, bool lb, bool mb)
1.1.1.4 root 228: {
1.1.1.7 root 229: // ホストからマウスイベントのたびに呼ばれるので、ここで積算。
230: // 20msec ごとのサンプリングのタイミングで積算結果を処理する。
231: // X68k のマウスの Y 方向は下に動く方向がプラス。
232: mouse_x += x;
233: mouse_y += y;
234: mouse_r = rb;
235: mouse_l = lb;
236: }
237:
238: // マウスデータを VM に送信
239: void
240: X68030Keyboard::MouseSendStart()
241: {
242: uint8 status = 0;
243:
1.1.1.10! root 244: // 最後のバイトを送るまでは、次は受け付けられない。
! 245: // 送信中かどうかは mouse_event.code で判断している。
! 246: if (mouse_event.code != 0) {
1.1.1.7 root 247: return;
248: }
249:
250: if (mouse_x > 127) {
251: status |= XOVFL;
252: mouse_x = 127;
253: }
254: if (mouse_x < -128) {
255: status |= XUNFL;
256: mouse_x = -128;
257: }
258: if (mouse_y > 127) {
259: status |= YOVFL;
260: mouse_y = 127;
261: }
262: if (mouse_y < -128) {
263: status |= YUNFL;
264: mouse_y = -128;
265: }
266: status |= mouse_l ? SW_L : 0;
267: status |= mouse_r ? SW_R : 0;
268:
1.1.1.10! root 269: // この時点でラッチしてみる(?)
! 270: mouse_data[0] = status;
! 271: mouse_data[1] = mouse_x;
! 272: mouse_data[2] = mouse_y;
1.1.1.7 root 273:
274: mouse_x = 0;
275: mouse_y = 0;
276:
1.1.1.10! root 277: // 1バイト目は MSCTRL が立ち下がってから 700us 以上後から送信。
! 278: mouse_event.time = 700_usec;
! 279: mouse_event.code = 1;
! 280: gScheduler->RestartEvent(mouse_event);
1.1.1.7 root 281: }
282:
1.1.1.10! root 283: // マウスデータのイベントコールバック
1.1.1.7 root 284: void
285: X68030Keyboard::MouseCallback(Event& ev)
286: {
1.1.1.10! root 287: // マウスは 4800bps で、ストップビット 2 なので11ビット。
! 288: // 1/4800 * 11 = 2.29166 msec なので、1バイトあたり 2.3 msec にしておく。
! 289:
! 290: // code は 1 なら1バイト目 ([0])
! 291: // :
! 292: // code が 4 なら送らずに終了
! 293:
! 294: if (mouse_event.code < 4) {
! 295: // 1, 2, 3 バイト目送信
! 296: gSCC->Rx(1, mouse_data[mouse_event.code - 1]);
! 297:
! 298: // 次のイベント
! 299: mouse_event.time = 2300_usec;
! 300: mouse_event.code++;
! 301: gScheduler->StartEvent(mouse_event);
! 302: } else {
! 303: // 3 バイト目の送信が終わって、1文字送信分の時間が経過したので
! 304: // ここで code を 0 に戻す。
! 305: // XXX 実際のタイミングがどうかは知らない。
! 306: mouse_event.code = 0;
! 307: }
1.1.1.4 root 308: }
309:
310: // ホストからの制御
311: void
312: X68030Keyboard::Command(uint32 data)
313: {
314: if (data < 0x40) {
315: // $00-$3f TV 制御
316: // (実装不要)
317: putlog(2, "Command TV 制御 $%02x", (data & 0x1f));
318:
319: } else if (data < 0x48) {
320: // $40-$47 MSCTRL 信号制御
321: // こいつは頻度が高いのでログレベルを上げておく
322: putlog(3, "Command MSCTRL 制御 %d", (data & 1));
323:
324: } else if (data < 0x50) {
325: // $48-$4f キーデータ送出許可
326: putlog(1, "Command キー送出許可 %d (未実装)", (data & 1));
327:
328: } else if (data < 0x54) {
329: // $50-$53 ディスプレイ制御モード選択
330: // (実装不要)
331: putlog(2, "Command ディスプレイ制御 X68000モード %d", (data & 1));
332:
333: } else if (data < 0x58) {
334: // $54-$57 LED 明るさ選択
335: // 0 が明るく 3 が暗いので、明るさというより暗さ。
336: led_darkness = (data & 3);
337: putlog(1, "Command LED 明るさ %d", led_darkness);
338:
339: } else if (data < 0x5c) {
340: // $58-$5b 本体からのディスプレイ制御許可
341: // (実装不要)
342: putlog(2, "Command 本体からディスプレイ制御 %d", (data & 1));
343:
344: } else if (data < 0x60) {
345: // $5c-$5f OPT.2 によるディスプレイ制御許可
346: // (実装不要)
347: putlog(2, "Command OPT2からディスプレイ制御 %d", (data & 1));
348:
349: } else if (data < 0x70) {
350: // $60-$6f リピートまでの時間
1.1.1.6 root 351: rept_delay = 200_msec + (data & 15) * 100_msec;
352: putlog(1, "Command Repeat Delay %d (%d msec)", (data & 15),
353: (int)(rept_delay / 1_msec));
1.1.1.4 root 354:
355: } else if (data < 0x80) {
356: // $70-$7f リピート間隔
1.1.1.6 root 357: rept_time = 30_msec + (data & 15) * (data & 15) * 5_msec;
358: putlog(1, "Command Repeat time %d (%d msec)", (data & 15),
359: (int)(rept_time / 1_msec));
1.1.1.4 root 360:
361: } else {
362: // $80-$ff LED 制御
363: data &= 0x7f;
364: putlog(1, "Command LED 制御 $%02x", data);
365: uint32 b = 0x01;
366: for (int i = 0; i < led.size(); i++) {
367: // data は %0 で点灯、led[] は true で点灯
368: led[i] = ((data & b) == 0);
369: b <<= 1;
370: }
371: }
372: }
373:
1.1 root 374: // キーコード変換表
375: // 共通キーコードから X680x0 キーコードに変換する。
376: /*static*/ const uint
1.1.1.5 root 377: X68030Keyboard::keycode2x68kkey_table[KC_max] = {
1.1 root 378: NoKey, // [00]
379: X68kKey_BREAK, // [01]
380: X68kKey_COPY, // [02]
381: X68kKey_romaji, // [03]
1.1.1.4 root 382: X68kKey_code, // [04]
1.1 root 383: X68kKey_kigou, // [05]
384: X68kKey_touroku, // [06]
385: X68kKey_HELP, // [07]
1.1.1.9 root 386: X68kKey_SHIFT, // [08]
1.1 root 387: X68kKey_TAB, // [09]
388: X68kKey_CTRL, // [0a]
389: X68kKey_kana, // [0b]
390: X68kKey_SHIFT, // [0c]
1.1.1.5 root 391: X68kKey_SHIFT, // [0d]
1.1 root 392: X68kKey_CAPS, // [0e]
393: NoKey, // [0f]
394:
395: X68kKey_ESC, // [10]
396: X68kKey_BS, // [11]
397: X68kKey_enter, // [12]
398: X68kKey_INS, // [13]
399: X68kKey_space, // [14]
400: X68kKey_DEL, // [15]
401: NoKey, // [16]
402: NoKey, // [17]
403: NoKey, // [18]
404: NoKey, // [19]
405: NoKey, // [1a]
406: NoKey, // [1b]
407: X68kKey_up, // [1c]
408: X68kKey_left, // [1d]
409: X68kKey_right, // [1e]
410: X68kKey_down, // [1f]
411:
412: X68kKey_ROLLUP, // [20]
413: X68kKey_ROLLDOWN, // [21]
414: X68kKey_1, // [22]
415: X68kKey_2, // [23]
416: X68kKey_3, // [24]
417: X68kKey_4, // [25]
418: X68kKey_5, // [26]
419: X68kKey_6, // [27]
420: X68kKey_7, // [28]
421: X68kKey_8, // [29]
422: X68kKey_9, // [2a]
423: X68kKey_0, // [2b]
424: X68kKey_minus, // [2c]
425: X68kKey_circum, // [2d]
426: X68kKey_backslash, // [2e]
1.1.1.9 root 427: NoKey, // [2f]
1.1 root 428:
429: X68kKey_OPT1, // [30]
430: X68kKey_OPT2, // [31]
431: X68kKey_Q, // [32]
432: X68kKey_W, // [33]
433: X68kKey_E, // [34]
434: X68kKey_R, // [35]
435: X68kKey_T, // [36]
436: X68kKey_Y, // [37]
437: X68kKey_U, // [38]
438: X68kKey_I, // [39]
439: X68kKey_O, // [3a]
440: X68kKey_P, // [3b]
441: X68kKey_at, // [3c]
442: X68kKey_bracketleft, // [3d]
443: X68kKey_hira, // [3e]
444: X68kKey_zenkaku, // [3f]
445:
1.1.1.9 root 446: X68kKey_HOME, // [40]
447: X68kKey_UNDO, // [41]
1.1 root 448: X68kKey_A, // [42]
449: X68kKey_S, // [43]
450: X68kKey_D, // [44]
451: X68kKey_F, // [45]
452: X68kKey_G, // [46]
453: X68kKey_H, // [47]
454: X68kKey_J, // [48]
455: X68kKey_K, // [49]
456: X68kKey_L, // [4a]
457: X68kKey_semicolon, // [4b]
458: X68kKey_colon, // [4c]
459: X68kKey_bracketright, // [4d]
460: NoKey, // [4e]
461: NoKey, // [4f]
462:
1.1.1.9 root 463: X68kKey_XF1, // [50]
464: X68kKey_XF2, // [51]
1.1 root 465: X68kKey_Z, // [52]
466: X68kKey_X, // [53]
467: X68kKey_C, // [54]
468: X68kKey_V, // [55]
469: X68kKey_B, // [56]
1.1.1.5 root 470: X68kKey_N, // [57]
471: X68kKey_M, // [58]
1.1 root 472: X68kKey_comma, // [59]
473: X68kKey_period, // [5a]
474: X68kKey_slash, // [5b]
475: X68kKey_underscore, // [5c]
1.1.1.9 root 476: X68kKey_XF3, // [5d]
477: X68kKey_XF4, // [5e]
478: X68kKey_XF5, // [5f]
1.1 root 479:
480: NoKey, // [60]
481: X68kKey_PAD_plus, // [61]
482: X68kKey_PAD_minus, // [62]
483: X68kKey_PAD_7, // [63]
484: X68kKey_PAD_8, // [64]
485: X68kKey_PAD_9, // [65]
486: X68kKey_PAD_4, // [66]
487: X68kKey_PAD_5, // [67]
488: X68kKey_PAD_6, // [68]
489: X68kKey_PAD_1, // [69]
490: X68kKey_PAD_2, // [6a]
491: X68kKey_PAD_3, // [6b]
492: X68kKey_PAD_0, // [6c]
493: X68kKey_PAD_decimal, // [6d]
494: X68kKey_PAD_enter, // [6e]
495: X68kKey_PAD_CLR, // [6f]
496:
497: NoKey, // [70]
498: NoKey, // [71]
499: X68kKey_F1, // [72]
500: X68kKey_F2, // [73]
501: X68kKey_F3, // [74]
502: X68kKey_F4, // [75]
503: X68kKey_F5, // [76]
504: X68kKey_F6, // [77]
505: X68kKey_F7, // [78]
506: X68kKey_F8, // [79]
507: X68kKey_F9, // [7a]
508: X68kKey_F10, // [7b]
509: X68kKey_PAD_multiply, // [7c]
510: X68kKey_PAD_divide, // [7d]
511: X68kKey_PAD_equal, // [7e]
512: X68kKey_PAD_comma, // [7f]
513: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.