Annotation of nono/vm/x68kkbd.cpp, revision 1.1.1.10

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: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.