Annotation of nono/vm/romemu_luna.cpp, revision 1.1.1.20

1.1       root        1: //
                      2: // nono
1.1.1.2   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
1.1.1.13  root        7: //
1.1.1.7   root        8: // LUNA シリーズの ROM エミュレーション共通部分
1.1.1.10  root        9: //
1.1.1.13  root       10: 
1.1.1.10  root       11: // IODevice
1.1.1.16  root       12: //  |
                     13: //  +- ROMDevice (LoadROM()、ウェイト、マスク等を持つ)
                     14: //  |   +- PROMDevice    (LUNA* の PROM)
                     15: //  |   +- IPLROM1Device (X680x0 の IPLROM 後半)
                     16: //  |   +- IPLROM2Device (X680x0 の IPLROM 前半)
                     17: //  |   +- CGROMDevice   (X680x0 の CGROM)
                     18: //  |   |
                     19: //  |   +- ROMEmuDevice
                     20: //  |       |
                     21: //  |       | +-------------------+
                     22: //  |       +-| LunaPROMEmuDevice | (LUNA PROM エミュレーションの共通部分)
                     23: //  |       | +-------------------+
                     24: //  |       |   |
                     25: //  |       |   +- Luna1PROMEmuDevice   (LUNA-I の PROM エミュレーション)
                     26: //  |       |   +- Luna88kPROMEmuDevice (LUNA-88K の PROM エミュレーション)
                     27: //  |       +- NewsROMEmuDevice    (NEWS の ROM エミュレーション)
                     28: //  |       +- ROM30EmuDevice      (X68030 の ROM30 エミュレーション)
                     29: //  |       +- Virt68kROMEmuDevice (virt-m68k の IPLROM 相当の何か)
                     30: //  |
                     31: //  +- PROM0Device   (LUNA* のブートページ切り替え用プロキシ)
                     32: //  +- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ)
1.1.1.2   root       33: 
                     34: #include "romemu_luna.h"
1.1.1.14  root       35: #include "bt45x.h"
1.1.1.15  root       36: #include "builtinrom.h"
1.1.1.20! root       37: #include "dipsw.h"
1.1.1.16  root       38: #include "lance.h"
1.1.1.9   root       39: #include "lunafb.h"
1.1       root       40: #include "mainapp.h"
1.1.1.14  root       41: #include "mainram.h"
1.1       root       42: #include "mk48t02.h"
1.1.1.13  root       43: #include "mpu.h"
1.1.1.14  root       44: #include "pio.h"
1.1.1.13  root       45: #include "prom.h"
1.1.1.19  root       46: #include "rtc.h"
1.1.1.16  root       47: #include "scsidev.h"
1.1.1.18  root       48: #include "scsidomain.h"
1.1       root       49: #include "sio.h"
                     50: #include "spc.h"
1.1.1.7   root       51: #include "ufs.h"
1.1       root       52: 
                     53: // コンストラクタ
                     54: LunaPROMEmuDevice::LunaPROMEmuDevice()
1.1.1.14  root       55:        : inherited(OBJ_PROM)
1.1       root       56: {
1.1.1.10  root       57:        // LUNA では ROM への書き込みは何も起きない
                     58:        write_op = 0;
1.1       root       59: }
                     60: 
                     61: // デストラクタ
                     62: LunaPROMEmuDevice::~LunaPROMEmuDevice()
                     63: {
                     64: }
                     65: 
1.1.1.14  root       66: // 初期化
1.1       root       67: bool
                     68: LunaPROMEmuDevice::Init()
                     69: {
1.1.1.14  root       70:        bt45x = GetBT45xDevice();
1.1.1.16  root       71:        lance = GetLanceDevice();
1.1.1.14  root       72:        lunafb = GetLunafbDevice();
                     73:        mainram = GetMainRAMDevice();
                     74:        nvram = GetMK48T02Device();
                     75:        sio = GetSIODevice();
                     76:        spc = GetSPCDevice();
1.1       root       77: 
                     78:        return true;
                     79: }
                     80: 
1.1.1.16  root       81: // ROMIO から読み出す。
                     82: // ROMIO が応答すべきでなければ (uint64)-1 を返す。
                     83: uint64
                     84: LunaPROMEmuDevice::ReadROMIO(busaddr addr)
1.1       root       85: {
1.1.1.10  root       86:        // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。
1.1.1.16  root       87:        if ((mpu->GetPPC() & 0xff000000) == baseaddr && addr.GetSize() == 4) {
                     88:                switch (addr.Addr()) {
1.1.1.9   root       89:                 case ROMIO_INIT:
1.1.1.16  root       90:                        ROM_Init();
                     91:                        return 0;
1.1       root       92: 
1.1.1.15  root       93:                 case ROMIO_LOAD:
                     94:                        entrypoint = ROM_Load();
                     95:                        putlog(2, "ROMIO_LOAD entrypoint=$%08x", entrypoint);
                     96:                        return 0;
                     97: 
1.1.1.20! root       98:                 case ROMIO_SIOINTR:
        !            99:                        putlog(2, "ROMIO_SIOINTR");
        !           100:                        ROM_SIOIntr();
1.1.1.7   root      101:                        return 0;
1.1       root      102: 
1.1.1.9   root      103:                 case ROMIO_CLOSE:
1.1.1.15  root      104:                        putlog(2, "ROMIO_CLOSE");
1.1.1.7   root      105:                        ROM_Close();
                    106:                        return 0;
1.1       root      107: 
1.1.1.9   root      108:                 case ROMIO_ENTRY:
1.1.1.15  root      109:                 {
                    110:                        uint32 rv = ROM_Entry();
                    111:                        putlog(2, "ROMIO_ENTRY entrypoint=$%08x", rv);
                    112:                        return rv;
                    113:                 }
                    114: 
                    115:                 case ROMIO_AP1:
                    116:                        ROM_AP1();
                    117:                        return 0;
                    118: 
                    119:                 case ROMIO_AP2:
                    120:                        ROM_AP2();
                    121:                        return 0;
                    122: 
                    123:                 case ROMIO_CLKCNT:
                    124:                        return clkcnt;
1.1.1.6   root      125: 
1.1.1.19  root      126:                 case ROMIO_QUITSTOP:
                    127:                        return quitstop;
                    128: 
1.1       root      129:                 default:
                    130:                        break;
                    131:                }
                    132:        }
1.1.1.2   root      133: 
1.1.1.16  root      134:        return (uint64)-1;
1.1       root      135: }
                    136: 
1.1.1.16  root      137: // ROMIO に書き込む。
                    138: // 反応すれば true を返す。そうでなければ false を返す。
                    139: bool
                    140: LunaPROMEmuDevice::WriteROMIO(busaddr addr, uint32 data)
1.1       root      141: {
1.1.1.15  root      142:        // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。
1.1.1.16  root      143:        if ((mpu->GetPPC() & 0xff000000) == baseaddr && addr.GetSize() == 4) {
                    144:                switch (addr.Addr()) {
1.1.1.15  root      145:                 case ROMIO_CLKCNT:
                    146:                        clkcnt = data;
1.1.1.16  root      147:                        return true;
1.1.1.19  root      148: 
                    149:                 case ROMIO_QUITSTOP:
                    150:                        quitstop = data;
                    151:                        return true;
                    152: 
1.1.1.15  root      153:                 default:
                    154:                        break;
                    155:                }
                    156:        }
                    157: 
1.1.1.16  root      158:        return false;
1.1.1.15  root      159: }
1.1.1.14  root      160: 
1.1.1.15  root      161: // ROM 処理の初期化。
1.1.1.16  root      162: void
1.1.1.15  root      163: LunaPROMEmuDevice::ROM_Init()
                    164: {
1.1       root      165:        // 初期パレット
1.1.1.15  root      166:        InitPalette();
1.1       root      167: 
                    168:        // SCSI
                    169:        // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。
1.1.1.16  root      170:        spc->WritePort(SPC::BDID, 7);
                    171: 
                    172:        // LANCE。
                    173:        lance->WritePort(0, 0x00);      // RAP=CSR0
                    174:        lance->WritePort(1, AM7990::CSR0_STOP);
1.1       root      175: 
1.1.1.20! root      176:        // DIP-SW の #1-2 が down(=false) ならシリアル。
        !           177:        auto dipsw = GetDipswDevice();
        !           178:        use_serial = (dipsw->Get(1) == false);
        !           179: 
        !           180:        if (use_serial) {
        !           181:                // シリアル(chA)を初期化
        !           182:                sio->WritePort(1, 0x01);        // CR1
        !           183:                sio->WritePort(1, 0x12);        //    RX Int(all char) | TXIntEnable
        !           184:                sio->WritePort(1, 0x04);        // CR4
        !           185:                sio->WritePort(1, 0x44);        //    Stop 1bit
        !           186:                sio->WritePort(1, 0x03);        // CR3
        !           187:                sio->WritePort(1, 0xc1);        //    RX Enable, 8bit
        !           188:                sio->WritePort(1, 0x05);        // CR5
        !           189:                sio->WritePort(1, 0x68);        //    TX Enable, 8bit
        !           190: 
        !           191:                sio_txq.Clear();
        !           192:        } else {
        !           193:                // キーボード(chB)を初期化
        !           194:                sio->WritePort(3, 0x01);        // CR1
        !           195:                sio->WritePort(3, 0x10);        //    RX Int(all char)
        !           196:                sio->WritePort(3, 0x04);        // CR4
        !           197:                sio->WritePort(3, 0x44);        //    Stop 1bit
        !           198:                sio->WritePort(3, 0x03);        // CR3
        !           199:                sio->WritePort(3, 0xc1);        //    RX Enable, 8bit
        !           200:                sio->WritePort(3, 0x05);        // CR5
        !           201:                sio->WritePort(3, 0x68);        //    TX Enable, 8bit
        !           202: 
        !           203:                // LED を(明示的に)オフ、マウスサンプリングをオフ
        !           204:                sio->WritePort(2, 0x00);
        !           205:                sio->WritePort(2, 0x01);
        !           206:                sio->WritePort(2, 0x20);
        !           207:        }
1.1.1.14  root      208: 
                    209:        // 電源オフをキャンセル。
                    210:        // 実 PROM では、リセット後に PIO のモードセットを行っている。
                    211:        // モードセットを行うと PortC のビットが %0 になる = 電源オフなので、
                    212:        // 即座にこれをキャンセルしている。
                    213:        // ROMEmu ではモードセットを行っていないが、キャンセルだけ発行しておく。
1.1.1.15  root      214:        auto pio1 = GetPIO1Device();
1.1.1.16  root      215:        pio1->WritePort(3, 0x09);
1.1.1.15  root      216: 
                    217:        // 機種別の初期化
                    218:        ROM_InitMD();
                    219: 
                    220:        // スクリーンを初期化
                    221:        InitScreen();
                    222: }
                    223: 
                    224: // 起動時の実行ファイル読み込み処理を行う。
                    225: // 次段実行ファイルの読み込みに成功すれば execute を true にして、
                    226: // エントリポイントを返す。
                    227: // そうでなければ -1 を返す。
                    228: uint32
                    229: LunaPROMEmuDevice::ROM_Load()
                    230: {
                    231:        uint32 entry;
                    232: 
                    233:        entry = -1;
                    234:        execute = false;
                    235: 
                    236:        // 表示環境が整ったところで NVRAM を照合
                    237:        if (VerifyNVRAM()) {
                    238:                // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、
                    239:                // これを真似する。
                    240:                auto pio0 = GetPIO0Device();
                    241:                if (pio0->IsDIPSW11Up()) {
                    242:                        entry = LoadFile("");
1.1.1.16  root      243:                        if (entry != -1) {
                    244:                                execute = true;
                    245:                                return entry;
                    246:                        }
1.1.1.15  root      247:                }
                    248:        } else {
                    249:                // LUNA-88K の PROM 1.20 は初期化するかどうか確認してくるけど、
                    250:                // とりあえず。
                    251:                InitNVRAM();
                    252:                errmsg = "NVRAM Initialized.";
                    253:                // NVRAM をクリアしたら DIPSW によらずプロンプトに降りる
                    254:        }
                    255: 
1.1.1.16  root      256:        PrintTitle();
                    257:        if (errmsg.empty() == false) {
                    258:                Print("** %s\n\n", errmsg.c_str());
1.1.1.15  root      259:        }
1.1.1.16  root      260:        ClearPrompt();
1.1.1.15  root      261: 
                    262:        return entry;
                    263: }
                    264: 
                    265: // 実行ファイルに処理を移すならそのエントリポイントを返す。
                    266: // そうでなければ -1 を返す。
                    267: uint32
                    268: LunaPROMEmuDevice::ROM_Entry()
                    269: {
                    270:        if (execute == false) {
                    271:                return -1;
                    272:        }
                    273:        return entrypoint;
                    274: }
                    275: 
                    276: // パレットを初期化
                    277: void
                    278: LunaPROMEmuDevice::InitPalette()
                    279: {
                    280:        // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。
                    281:        uint8 val = 0xff;
1.1.1.16  root      282:        bt45x->WritePort(0, 0); // select palette
1.1.1.15  root      283:        for (int i = 0; i < 16; i++) {
1.1.1.16  root      284:                bt45x->WritePort(1, val);       // R
                    285:                bt45x->WritePort(1, val);       // G
                    286:                bt45x->WritePort(1, val);       // B
1.1.1.15  root      287:                val ^= 0xff;
                    288:        }
1.1.1.7   root      289: }
1.1.1.2   root      290: 
1.1.1.7   root      291: // スクリーンを初期化
                    292: void
1.1.1.13  root      293: LunaPROMEmuDevice::InitScreen()
1.1.1.7   root      294: {
1.1.1.14  root      295:        lunafb->EmuInitScreen();
1.1.1.13  root      296: 
                    297:        screen_w = 80;
                    298:        screen_h = 32;
                    299:        cursor_x = 0;
                    300:        cursor_y = 0;
1.1.1.15  root      301:        cursor_on = true;
1.1.1.13  root      302: 
                    303:        // 表示範囲はざっくりセンタリングする
                    304:        origin_px = (1280 - screen_w * 12) / 2;
                    305:        origin_py = (1024 - screen_h * 24) / 2;
                    306:        // 横は 48ドット境界から始めておく。
                    307:        // こうすると1(,2)文字目の24ビットが必ずワード境界から始まるため。
                    308:        // 今となっては任意の場所から文字が描画できるようになったので揃える
                    309:        // 必要はなくなったが、依然このほうがアクセス効率はいいので。
                    310:        origin_px = (origin_px / 48) * 48;
                    311: 
1.1.1.15  root      312:        cursor_on = true;
                    313: 
1.1.1.2   root      314:        inputbuf.clear();
                    315:        inputpos = 0;
                    316:        is_shift = false;
1.1.1.3   root      317:        mousecnt = 0;
1.1.1.13  root      318: }
1.1       root      319: 
1.1.1.13  root      320: // タイトルを出力
                    321: void
1.1.1.15  root      322: LunaPROMEmuDevice::PrintTitle()
1.1.1.13  root      323: {
1.1.1.20! root      324:        // シリアルモードなら、画面に何か出しておく。
        !           325:        // 実機 PROM は、LUNA-I (4.22) はカーソルが出るだけ、
        !           326:        // LUNA-88K (1.20) は " Use UART-A.\n" が表示される。
        !           327:        if (use_serial) {
        !           328:                PrintDisplay("Use HostCom0.\n");
        !           329:        }
        !           330: 
1.1.1.16  root      331:        Print("NONO %u.%u.%u Emulated ROM Monitor for %s\n\n",
1.1.1.7   root      332:                NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, machine_name);
1.1.1.13  root      333: }
                    334: 
                    335: // 1文字出力
                    336: void
                    337: LunaPROMEmuDevice::Putc(uint ch)
                    338: {
1.1.1.20! root      339:        if (use_serial) {
        !           340:                PutcSerial(ch);
        !           341:        } else {
        !           342:                PutcDisplay(ch);
        !           343:        }
        !           344: }
        !           345: 
        !           346: // シリアルコンソールに1文字出力。
        !           347: // 実際にはキューに1文字追加して、キュー先頭の1文字を可能なら送信する。
        !           348: void
        !           349: LunaPROMEmuDevice::PutcSerial(uint ch)
        !           350: {
        !           351:        sio_txq.Enqueue(ch);
        !           352:        ProcessSerial();
        !           353: }
        !           354: 
        !           355: // シリアルコンソールにキュー先頭の1文字を可能なら送信する。
        !           356: void
        !           357: LunaPROMEmuDevice::ProcessSerial()
        !           358: {
        !           359:        // コントロールレジスタポインタを確実に 0 にするため読み捨てる。
        !           360:        sio->ReadPort(1);
        !           361: 
        !           362:        // 送信完了なら割り込みを下ろす。
        !           363:        uint sr0 = sio->ReadPort(1);
        !           364:        if ((sr0 & MPSCC::SR0_INTPEND)) {
        !           365:                sio->WritePort(1, 5 << 3);      // Reset TXIntr Pending
        !           366:        }
        !           367: 
        !           368:        if ((sr0 & MPSCC::SR0_TXEMPTY)) {
        !           369:                uint8 ch;
        !           370:                if (sio_txq.Dequeue(&ch)) {
        !           371:                        sio->WritePort(0, ch);
        !           372:                }
        !           373:        }
        !           374: }
        !           375: 
        !           376: // 画面に1文字出力。
        !           377: void
        !           378: LunaPROMEmuDevice::PutcDisplay(uint ch)
        !           379: {
1.1.1.13  root      380:        uint px = origin_px + cursor_x * 12;
                    381:        uint py = origin_py + cursor_y * 24;
                    382: 
1.1.1.15  root      383:        if (cursor_on) {
                    384:                // カーソル位置の反転を元に戻す
                    385:                lunafb->EmuPutc(px, py, ' ', ROP::INV2);
                    386:        }
                    387: 
                    388:        // いくつかの制御コードを勝手に使う。
                    389:        switch (ch) {
                    390:         case 0x0f:     // SI
                    391:                bold = true;
                    392:                break;
                    393:         case 0x0e:     // SO
                    394:                bold = false;
                    395:                break;
                    396: 
1.1.1.19  root      397:         case 0x10: // カラーコード変更
                    398:                lunafb->SetBMSEL(0);
                    399:                break;
1.1.1.15  root      400:         case 0x11:
1.1.1.19  root      401:                lunafb->SetBMSEL(1);
                    402:                break;
1.1.1.15  root      403:         case 0x12:
1.1.1.19  root      404:                lunafb->SetBMSEL(3);
                    405:                break;
1.1.1.15  root      406:         case 0x13:
1.1.1.19  root      407:                lunafb->SetBMSEL(5);
1.1.1.15  root      408:                break;
                    409: 
                    410:         case CR:
                    411:                break;
1.1.1.13  root      412: 
1.1.1.15  root      413:         case LF:
                    414:         put_LF:
1.1.1.13  root      415:                cursor_x = 0;
                    416:                if (cursor_y < screen_h - 1) {
                    417:                        cursor_y++;
                    418:                } else {
                    419:                        // 上スクロールして..
1.1.1.14  root      420:                        lunafb->EmuScrollY(origin_py, origin_py + 24, (screen_h - 1) * 24);
1.1.1.13  root      421:                        // 最終行を消す
1.1.1.14  root      422:                        lunafb->EmuFill(0, py, 1280, 24, 0);
1.1.1.13  root      423:                }
1.1.1.15  root      424:                break;
                    425: 
                    426:         case LEFT:
1.1.1.13  root      427:                if (cursor_x > 0) {
                    428:                        cursor_x--;
                    429:                } else {
                    430:                        cursor_y--;
                    431:                        cursor_x = screen_w - 1;
                    432:                }
1.1.1.15  root      433:                break;
                    434: 
                    435:         case RIGHT:
1.1.1.13  root      436:                if (cursor_x < screen_w - 1) {
                    437:                        cursor_x++;
                    438:                } else {
                    439:                        cursor_y++;
                    440:                        cursor_x = 0;
                    441:                }
1.1.1.15  root      442:                break;
                    443: 
                    444:         default:
                    445:                if (ch < 0x80) {
1.1.1.14  root      446:                        lunafb->EmuPutc(px, py, ch);
1.1.1.15  root      447:                        if (bold) {
                    448:                                for (int i = 1; i < 2; i++) {
                    449:                                        lunafb->EmuPutc(px + i, py, ch, ROP::OR1);
                    450:                                }
                    451:                        }
1.1.1.13  root      452:                } else {
1.1.1.15  root      453:                        lunafb->EmuPutc(px, py, ' ');
                    454:                        lunafb->EmuPutc(px, py + 10, ch);
                    455:                        if (bold) {
                    456:                                for (int i = 1; i < 2; i++) {
                    457:                                        lunafb->EmuPutc(px + i, py + 10, ch, ROP::OR1);
                    458:                                }
                    459:                        }
                    460:                }
                    461:                cursor_x++;
                    462:                if (cursor_x >= screen_w) {
1.1.1.13  root      463:                        goto put_LF;
                    464:                }
1.1.1.15  root      465:                break;
1.1.1.13  root      466:        }
                    467: 
1.1.1.15  root      468:        if (cursor_on) {
                    469:                // カーソル位置を再計算して描画 (文字を反転)
                    470:                px = origin_px + cursor_x * 12;
                    471:                py = origin_py + cursor_y * 24;
                    472:                lunafb->EmuPutc(px, py, ' ', ROP::INV2);
                    473:        }
1.1.1.13  root      474: }
                    475: 
1.1.1.20! root      476: // 文字列 s を画面かシリアルに出力。
1.1.1.13  root      477: void
                    478: LunaPROMEmuDevice::Print(const std::string& s)
                    479: {
                    480:        for (auto *p = s.c_str(); *p; p++) {
                    481:                Putc(*p);
                    482:        }
                    483: }
                    484: 
1.1.1.20! root      485: // 文字列 fmt... を画面かシリアルに出力。
1.1.1.13  root      486: void
                    487: LunaPROMEmuDevice::Print(const char *fmt, ...)
                    488: {
                    489:        char buf[1024];
                    490:        va_list ap;
                    491: 
                    492:        va_start(ap, fmt);
                    493:        vsnprintf(buf, sizeof(buf), fmt, ap);
                    494:        va_end(ap);
                    495: 
                    496:        for (char *p = buf; *p; p++) {
                    497:                Putc(*p);
                    498:        }
1.1       root      499: }
                    500: 
1.1.1.20! root      501: // 文字列 s を画面に出力。
        !           502: void
        !           503: LunaPROMEmuDevice::PrintDisplay(const char *s)
        !           504: {
        !           505:        for (; *s; s++) {
        !           506:                PutcDisplay(*s);
        !           507:        }
        !           508: }
        !           509: 
1.1.1.7   root      510: // ROM 処理のクローズ
                    511: void
1.1.1.2   root      512: LunaPROMEmuDevice::ROM_Close()
1.1       root      513: {
1.1.1.11  root      514:        // SIO をリセットしておく。
                    515:        // SIO のバッファにデータが残っていると、NetBSD のブートローダが
                    516:        // 起動してこないようだ。リセットして RXEN を落としておけば大丈夫ぽい。
1.1.1.14  root      517:        sio->ResetHard(false);
1.1.1.15  root      518: 
                    519:        // 実行ファイルにジャンプ後、NMI でプロンプトにもう一度戻ってこれるので
                    520:        // その時のためにエントリポイント等も初期化しておく。
                    521:        entrypoint = -1;
                    522:        execute = false;
1.1.1.19  root      523:        clkcnt = 0;
                    524:        quitstop = 0;
1.1       root      525: }
                    526: 
1.1.1.20! root      527: // SIO 割り込みハンドラ。
        !           528: // シリアル送信の進行と、シリアルもしくはキーボード(どちらもSIO)からの入力を
        !           529: // 受け取って、コマンド処理を進行させる。
        !           530: // コマンド処理の進行は、キー入力で行を構成し、行が完成すればそのコマンドを
        !           531: // 実行する、実行結果により出力(画面もしくはシリアル)を行ったり、メンバ変数を
        !           532: // 更新したり、までを含む。要するにここがメインループみたいなところ。
1.1.1.7   root      533: void
1.1.1.20! root      534: LunaPROMEmuDevice::ROM_SIOIntr()
1.1       root      535: {
1.1.1.20! root      536:        // シリアル送信を進行。
        !           537:        if (use_serial) {
        !           538:                ProcessSerial();
        !           539:        }
1.1.1.14  root      540: 
1.1.1.20! root      541:        // 入力を処理。
        !           542:        int asciicode = Getc();
1.1.1.15  root      543:        if (cursor_on && asciicode != -1) {
1.1.1.14  root      544:                ProcessChar(asciicode);
                    545:        }
                    546: }
                    547: 
                    548: // キー入力。
1.1.1.20! root      549: int
        !           550: LunaPROMEmuDevice::Getc()
        !           551: {
        !           552:        int asciicode;
        !           553: 
        !           554:        if (use_serial) {
        !           555:                asciicode = GetcSerial();
        !           556:        } else {
        !           557:                asciicode = GetcKeyboard();
        !           558:        }
        !           559: 
        !           560:        return asciicode;
        !           561: }
        !           562: 
        !           563: // シリアルポートからのキー入力。
        !           564: int
        !           565: LunaPROMEmuDevice::GetcSerial()
        !           566: {
        !           567:        // コントロールレジスタポインタを確実に 0 にするため読み捨てる
        !           568:        sio->ReadPort(1);
        !           569: 
        !           570:        uint8 sr0 = sio->ReadPort(1);
        !           571:        if ((sr0 & MPSCC::SR0_RXAVAIL) == 0) {
        !           572:                return -1;
        !           573:        }
        !           574: 
        !           575:        int asciicode = sio->ReadPort(0);
        !           576:        return asciicode;
        !           577: }
        !           578: 
        !           579: // キーボードからのキー入力。
1.1.1.14  root      580: // 押されているキーの ASCII コードを返す。
                    581: // キー入力が無いときは -1 を返す。
                    582: int
1.1.1.20! root      583: LunaPROMEmuDevice::GetcKeyboard()
1.1.1.14  root      584: {
                    585:        // コントロールレジスタポインタを確実に 0 にするため読み捨てる
1.1.1.16  root      586:        sio->ReadPort(3);
1.1.1.14  root      587: 
1.1.1.16  root      588:        uint8 sr0 = sio->ReadPort(3);
1.1.1.14  root      589:        if ((sr0 & MPSCC::SR0_RXAVAIL) == 0) {
                    590:                return -1;
                    591:        }
                    592: 
1.1.1.16  root      593:        uint32 lunakey = sio->ReadPort(2);
1.1       root      594: 
1.1.1.3   root      595:        // マウス入力の 2, 3バイト目は無視
                    596:        if (mousecnt > 0) {
                    597:                mousecnt--;
1.1.1.14  root      598:                return -1;
1.1.1.3   root      599:        }
                    600: 
1.1.1.2   root      601:        // SHIFT キーだけ状態を持つので先に処理
                    602:        if (lunakey == 0x0c || lunakey == 0x0d) {       // 左右SHIFT押下
                    603:                is_shift = true;
1.1.1.14  root      604:                return -1;
1.1.1.2   root      605:        }
                    606:        if (lunakey == 0x8c || lunakey == 0x8d) {       // 左右SHIFT開放
1.1       root      607:                is_shift = false;
1.1.1.14  root      608:                return -1;
1.1.1.2   root      609:        }
1.1.1.3   root      610:        // マウスデータの1バイト目なら、後続2バイトをスキップ
                    611:        if ((lunakey & 0xf8) == 0x80) {
                    612:                mousecnt = 2;
1.1.1.14  root      613:                return -1;
1.1.1.3   root      614:        }
                    615:        // キー開放は無視してよい
1.1.1.2   root      616:        if ((lunakey & 0x80)) {
1.1.1.14  root      617:                return -1;
1.1.1.2   root      618:        }
1.1       root      619: 
1.1.1.9   root      620:        // キーコードから文字を取得
1.1.1.14  root      621:        int asciicode;
1.1.1.9   root      622:        if (is_shift) {
                    623:                asciicode = lunakey2shifttable[lunakey];
                    624:        } else {
                    625:                asciicode = lunakey2asciitable[lunakey];
                    626:        }
1.1.1.14  root      627:        if (asciicode == 0) {
                    628:                return -1;
1.1.1.9   root      629:        }
1.1.1.14  root      630: 
                    631:        return asciicode;
1.1.1.9   root      632: }
                    633: 
                    634: // キー入力ハンドラ (キー入力割り込みによって呼ばれる)。
                    635: // 引数 asciicode は概ね ASCII コード (ただし 0x1c..0x1f が上下左右)。
                    636: void
1.1.1.14  root      637: LunaPROMEmuDevice::ProcessChar(char asciicode)
1.1.1.9   root      638: {
1.1.1.14  root      639:        // LUNA-I の場合、
1.1.1.9   root      640:        // 実機 PROM にはカーソル左右移動の機能はなく、カーソルは常に行末にいる。
                    641:        // そのためかどうか Delete も BackSpace と同じく1文字前を消す動作をして
                    642:        // いる。カーソル位置に文字はないので、Delete をそうするのはまあ分からん
                    643:        // でもない。
                    644:        // 一方、エミュレーション ROM では、カーソルの左右移動をサポートしてみた
                    645:        // ため Delete を「カーソル位置の文字を消す」にすることは出来るが、それは
                    646:        // それで実機と乖離が広がるので、どうしたもんか。
                    647: 
                    648:        switch (asciicode) {
                    649:         case BS:
                    650:                if (inputbuf.empty() == false && inputpos != 0) {
                    651:                        inputbuf.erase(--inputpos, 1);
1.1.1.13  root      652:                        Putc(LEFT);
                    653:                        for (int i = inputpos; i < inputbuf.size(); i++) {
                    654:                                Putc(inputbuf[i]);
                    655:                        }
                    656:                        Putc(' ');
                    657:                        for (int i = 0; i < 1 + inputbuf.size() - inputpos; i++) {
                    658:                                Putc(LEFT);
                    659:                        }
1.1       root      660:                }
1.1.1.2   root      661:                break;
1.1.1.9   root      662: 
                    663:         case LF:
1.1.1.2   root      664:                // 改行
1.1.1.13  root      665:                Putc(asciicode);
                    666:                prompt_y = cursor_y;
1.1.1.9   root      667:                // コマンド実行
1.1.1.7   root      668:                Command();
1.1.1.9   root      669:                // プロンプトを更新
                    670:                ClearPrompt();
1.1.1.2   root      671:                break;
1.1.1.9   root      672: 
                    673:         case LEFT:
1.1.1.2   root      674:                if (inputpos > 0) {
                    675:                        inputpos--;
1.1.1.13  root      676:                        Putc(LEFT);
1.1       root      677:                }
1.1.1.2   root      678:                break;
1.1.1.9   root      679:         case RIGHT:
1.1.1.2   root      680:                if (inputpos < inputbuf.length()) {
1.1       root      681:                        inputpos++;
1.1.1.13  root      682:                        Putc(RIGHT);
1.1       root      683:                }
                    684:                break;
1.1.1.9   root      685: 
                    686:         case UP:
                    687:         case DOWN:
                    688:         case 0:        // キー割り当てなし、これは来ないはずだが一応
                    689:                return;
                    690: 
                    691:         default:       // 通常文字のはず
1.1.1.2   root      692:                inputbuf.insert(inputbuf.begin() + inputpos, asciicode);
1.1.1.13  root      693:                for (int i = inputpos; i < inputbuf.size(); i++) {
                    694:                        Putc(inputbuf[i]);
                    695:                }
                    696:                for (int i = 0; i < inputbuf.size() - inputpos - 1; i++) {
                    697:                        Putc(LEFT);
                    698:                }
1.1.1.2   root      699:                inputpos++;
                    700:                break;
1.1       root      701:        }
1.1.1.2   root      702: }
1.1       root      703: 
1.1.1.9   root      704: // 入力バッファを初期化する。
                    705: void
                    706: LunaPROMEmuDevice::ClearPrompt()
                    707: {
1.1.1.13  root      708:        prompt_y = cursor_y;
1.1.1.9   root      709:        // 入力行をクリア
                    710:        inputbuf.clear();
                    711:        inputpos = 0;
                    712: 
1.1.1.13  root      713:        Print(prompt);
1.1.1.9   root      714: }
                    715: 
                    716: // NVRAM のチェックサムを返す
                    717: uint8
                    718: LunaPROMEmuDevice::CalcNVRAMCsum() const
                    719: {
                    720:        uint8 eor = 0;
                    721:        for (uint32 addr = 0x20; addr < 0x560; addr++) {
1.1.1.16  root      722:                eor ^= nvram->PeekPort(addr);
1.1.1.9   root      723:        }
                    724:        return eor;
                    725: }
                    726: 
                    727: // NVRAM のチェックサムを計算して書き込む
                    728: void
                    729: LunaPROMEmuDevice::WriteNVRAMCsum()
                    730: {
                    731:        uint8 eor = CalcNVRAMCsum();
                    732: 
1.1.1.16  root      733:        nvram->WritePort(0x001c, eor);
                    734:        nvram->WritePort(0x001d, eor);
                    735:        nvram->WritePort(0x001e, eor);
1.1.1.9   root      736: }
                    737: 
                    738: // NVRAM のマジックとチェックサムを照合する
                    739: bool
                    740: LunaPROMEmuDevice::VerifyNVRAM() const
                    741: {
                    742:        if (nvram->PeekString(0x0004) != "<nv>") {
                    743:                return false;
                    744:        }
                    745: 
                    746:        uint8 eor = CalcNVRAMCsum();
                    747:        for (uint32 addr = 0x1c; addr < 0x1f; addr++) {
1.1.1.16  root      748:                if (nvram->PeekPort(addr) != eor) {
1.1.1.9   root      749:                        return false;
                    750:                }
                    751:        }
                    752:        return true;
1.1       root      753: }
                    754: 
1.1.1.15  root      755: static const uint8 __unused test0[] = {
                    756:        0x12, 0x0f, 0x4e, 0x4f, 0x4e, 0x4f, 0x0e, 0x1e, 0x13, 0x00,
                    757: };
                    758: static const uint8 __unused test1[] = {
                    759:        0xcd, 0xd5, 0xcc, 0xd4, 0xc9, 0xad, 0xd2, 0xc9, 0xd3, 0xc3,
                    760:        0x20, 0xd7, 0xcf, 0xd2, 0xcb, 0xd3, 0xd4, 0xc1, 0xd4, 0xc9,
                    761:        0xcf, 0xce, 0x20, 0x11, 0x0f, 0x4c, 0x55, 0x4e, 0x41, 0x2d,
                    762:        0x38, 0x38, 0x4b, 0x00, 0xaa, 0x12, 0x43, 0x58, 0xc2, 0x00,
                    763: };
                    764: static const uint8 __unused test2[] = {
                    765:        0xc8, 0xcf, 0xcc, 0xcf, 0xce, 0xc9, 0xc3, 0x20, 0xd7, 0xcf,
                    766:        0xd2, 0xcb, 0xd3, 0xd4, 0xc1, 0xd4, 0xc9, 0xcf, 0xce, 0x20,
                    767:        0x11, 0x0f, 0x4c, 0x55, 0x4e, 0x41, 0x00, 0x21, 0xd8, 0x67,
                    768: };
                    769: static const uint8 __unused test3[] = {
                    770:        0x0e, 0x1e, 0x13, 0xd3, 0xc5, 0xd2, 0xc9, 0xc5, 0xd3, 0x0a,
                    771:        0x00, 0x0f, 0xd0, 0x4f, 0x7e, 0x40, 0x41, 0xcc, 0xdf, 0x09,
                    772: };
                    773: 
1.1.1.10  root      774: // -X オプションで指定されたホストファイルをロードする。
1.1       root      775: // ロードできればエントリポイントを返す。
1.1.1.15  root      776: // 失敗ならエラーメッセージを errmsg にセットして (uint32)-1 を返す。
1.1.1.10  root      777: // exec_file が指定されている時に呼ぶこと。
1.1       root      778: uint32
                    779: LunaPROMEmuDevice::LoadHostFile()
                    780: {
1.1.1.10  root      781:        assert(gMainApp.exec_file);
1.1       root      782: 
1.1.1.14  root      783:        LoadInfo info(gMainApp.exec_file);
1.1.1.18  root      784:        if (BootLoader::LoadExec(mainram, &info)) {
1.1.1.14  root      785:                return info.entry;
1.1       root      786:        } else {
1.1.1.15  root      787:                errmsg = "Couldn't load specified host program.";
                    788:                return -1;
1.1       root      789:        }
                    790: }
                    791: 
1.1.1.7   root      792: // bootinfo に従ってゲストから起動先をロードする。
1.1.1.15  root      793: // ロードできればエントリポイントを返す。
                    794: // 失敗ならエラーメッセージを errmsg にセットして (uint32)-1 を返す。
1.1       root      795: uint32
1.1.1.7   root      796: LunaPROMEmuDevice::LoadGuestFile(const bootinfo_t& bootinfo)
1.1       root      797: {
1.1.1.7   root      798:        const int& dkpart = bootinfo.dkpart;
                    799:        const int& dkunit = bootinfo.dkunit;
                    800:        const std::string& dkfile = bootinfo.dkfile;
1.1       root      801: 
                    802:        // ユニット (今は SCSI ID=6 決め打ち)
1.1.1.16  root      803:        uint id = 6;
1.1.1.18  root      804:        SCSITarget *target = spc->GetDomain()->GetTarget(id);
1.1       root      805:        if (target == NULL) {
                    806:                errmsg = "No bootable disks";
                    807:                putmsg(0, "%s", errmsg.c_str());
1.1.1.14  root      808:                return -1;
1.1       root      809:        }
1.1.1.3   root      810:        if (target->GetDevType() != SCSI::DevType::HD) {
1.1       root      811:                errmsg = "No bootable disks";
                    812:                putmsg(0, "%s", errmsg.c_str());
1.1.1.14  root      813:                return -1;
1.1       root      814:        }
                    815: 
1.1.1.10  root      816:        SCSIDisk *hd = dynamic_cast<SCSIDisk*>(target);
1.1.1.16  root      817:        putmsg(2, "%s SCSIHD[%u] found", __func__, id);
1.1       root      818: 
                    819:        // +0 セクタ目(512byte)に Sun disklabel
                    820:        scd_dk_label dk;
1.1.1.10  root      821:        if (hd->Peek(&dk, 0, sizeof(dk)) == false) {
1.1.1.16  root      822:                putmsg(0, "SCSIHD[%u] ReadSector failed", id);
                    823:                errmsg = string_format("dk%u Read disklabel failed.", dkunit);
1.1.1.14  root      824:                return -1;
1.1       root      825:        }
                    826: 
                    827:        // +$1fc の MAGIC をチェック
                    828:        uint32 magic = be16toh(dk.magic);
                    829:        if (magic != scd_dk_label::DKL_MAGIC) {
                    830:                putmsg(0, "SCSIHD[%d] Bad magic 0x%04x (!= 0x%x)",
                    831:                        id, magic, scd_dk_label::DKL_MAGIC);
                    832:                errmsg = string_format("dk%d Bad disklabel magic.", dkunit);
1.1.1.14  root      833:                return -1;
1.1       root      834:        }
                    835: 
                    836:        // チェックサム照合は手抜きで省略
                    837: 
                    838:        // 指定パーティションの開始位置とサイズ
                    839:        if (loglevel >= 2) {
                    840:                // デバッグ用に全部表示
                    841:                for (int i = 0; i < 8; i++) {
1.1.1.7   root      842:                        putmsgn("%s partition %c: { start=%8u size=%8u }", __func__,
1.1       root      843:                                'a' + i,
                    844:                                be32toh(dk.map[i].blkno),
                    845:                                be32toh(dk.map[i].nblk));
                    846:                }
                    847:        }
                    848:        uint32 part_start = be32toh(dk.map[dkpart].blkno);
                    849:        uint32 part_size  = be32toh(dk.map[dkpart].nblk);
                    850:        putmsg(1, "%s partition=%c, start=%u size=%u", __func__,
                    851:                dkpart + 'a', part_start, part_size);
                    852: 
                    853:        // ファイルシステムをオープン (マウントっぽいイメージ)
                    854:        Filesys fsys(this);
1.1.1.7   root      855:        if (fsys.Mount(hd, part_start, part_size) == false) {
1.1.1.16  root      856:                errmsg = string_format("dk%u,%c Open ffs failed: %s",
1.1.1.7   root      857:                        dkunit, dkpart + 'a', fsys.errstr.c_str());
                    858:                putmsg(0, "%s: %s", __func__, errmsg.c_str());
1.1.1.14  root      859:                return -1;
1.1       root      860:        }
                    861: 
1.1.1.7   root      862:        // ファイルを探してオープンする
                    863:        inodefile f(this);
                    864:        if (fsys.OpenFile(f, dkfile) == false) {
1.1.1.16  root      865:                errmsg = string_format("dk%u,%c,%s open failed: %s",
1.1.1.7   root      866:                        dkunit, dkpart + 'a', dkfile.c_str(), fsys.errstr.c_str());
                    867:                putmsg(0, "%s: %s", __func__, errmsg.c_str());
1.1.1.14  root      868:                return -1;
1.1       root      869:        }
1.1.1.7   root      870: 
1.1       root      871:        // ファイル本体を読み込み
1.1.1.7   root      872:        fsys.ReadData(f);
1.1       root      873: 
                    874:        // ロード
1.1.1.16  root      875:        std::string name = string_format("SCSIHD %u,%c:%s",
1.1.1.7   root      876:                id, dkpart + 'a', dkfile.c_str());
1.1.1.14  root      877:        LoadInfo info(name.c_str(), f.data.data(), f.data.size());
1.1.1.18  root      878:        if (BootLoader::LoadExec(mainram, &info) == false) {
1.1.1.16  root      879:                errmsg = string_format("dk%u,%c,%s load failed",
1.1.1.14  root      880:                        dkunit, dkpart + 'a', dkfile.c_str());
                    881:                putmsg(0, "%s: %s", __func__, errmsg.c_str());
                    882:                return -1;
                    883:        }
                    884: 
                    885:        if (info.entry == -1) {
1.1.1.16  root      886:                errmsg = string_format("dk%u,%c,%s not executable",
1.1.1.7   root      887:                        dkunit, dkpart + 'a', dkfile.c_str());
1.1.1.14  root      888:                putmsg(0, "%s: %s", __func__, errmsg.c_str());
                    889:                return -1;
1.1       root      890:        }
                    891: 
1.1.1.14  root      892:        return info.entry;
1.1       root      893: }
                    894: 
1.1.1.15  root      895: void
                    896: LunaPROMEmuDevice::ROM_AP1()
                    897: {
                    898:        char buf[100];
                    899:        int n;
                    900: 
                    901:        cursor_on = false;
                    902: 
                    903:        static uint8 ap1pal[] = {
                    904:                0x00, 0x00, 0x00,
                    905:                0x00, 0xf8, 0xf8,
1.1.1.19  root      906:                0, 0, 0,
1.1.1.15  root      907:                0xf8, 0xf8, 0x00,
1.1.1.19  root      908:                0, 0, 0,
1.1.1.15  root      909:                0xf8, 0xf8, 0xf8,
1.1.1.19  root      910:                0, 0, 0,  0, 0, 0,
                    911:                0, 0, 0,  0, 0, 0,
                    912:                0, 0, 0,  0, 0, 0,
                    913:                0, 0, 0,  0, 0, 0,
                    914:                0x00, 0x00, 0x00,
                    915:                0xf8, 0xf8, 0xf8
1.1.1.15  root      916:        };
1.1.1.16  root      917:        bt45x->WritePort(0, 0);
1.1.1.15  root      918:        for (int i = 0; i < countof(ap1pal); i++) {
1.1.1.16  root      919:                bt45x->WritePort(1, ap1pal[i]);
1.1.1.15  root      920:        }
                    921: 
1.1.1.20! root      922:        PrintDisplay((const char *)test0);
1.1.1.15  root      923:        if (gMainApp.IsLUNA88K()) {
1.1.1.20! root      924:                PrintDisplay((const char *)test1);
1.1.1.15  root      925:        } else {
1.1.1.20! root      926:                PrintDisplay((const char *)test2);
1.1.1.15  root      927:        }
1.1.1.20! root      928:        PrintDisplay((const char *)test3);
1.1.1.15  root      929: 
                    930:        n = strlcpy(buf, (const char *)&Builtin::IPLROM30[0x111e3], sizeof(buf));
1.1.1.20! root      931:        snprintf(buf + n, sizeof(buf) - n, "%d.%d.%d '%02d.%02d.%02d\n",
1.1.1.19  root      932:                NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER,
                    933:                GetRTCDevice()->GetYear() % 100, 4, 1);
1.1.1.20! root      934:        PrintDisplay(buf);
1.1.1.15  root      935: 
                    936:        memcpy(buf, &Builtin::IPLROM30[0x111f5], 0x28);
                    937:        memcpy(buf + 0x1e, mpu->GetMPUName(), 7);
                    938:        n = 0x27;
                    939:        n += snprintf(buf + n, sizeof(buf) - n, "%4.1f",
                    940:                (double)mpu->GetClockSpeed() / 1000);
                    941:        strlcpy(buf + n, (const char *)&Builtin::IPLROM30[0x1121f],
                    942:                sizeof(buf) - n);
1.1.1.20! root      943:        PrintDisplay(buf);
1.1.1.15  root      944: 
                    945:        strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11226], sizeof(buf));
                    946:        if (gMainApp.Has(VMCap::M88K)) {
                    947:                memcpy(buf + 0x1e, mpu->GetMPUName(), 7);
                    948:                memcpy(buf + 0x25, &Builtin::IPLROM30[0x11251], 3);
                    949:        }
1.1.1.20! root      950:        PrintDisplay(buf);
1.1.1.15  root      951: 
                    952:        strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11254], sizeof(buf));
                    953:        buf[0x0c] = 'e';
                    954:        if (gMainApp.Has(VMCap::M88K)) {
                    955:                memcpy(buf + 0x1e, mpu->GetMPUName(), 7);
                    956:                memcpy(buf + 0x25, &Builtin::IPLROM30[0x1127d], 3);
                    957:                buf[0x22] = 0x32;
                    958:        }
1.1.1.20! root      959:        PrintDisplay(buf);
1.1.1.15  root      960: 
                    961:        strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11280], sizeof(buf));
                    962:        n = 0x1e;
1.1.1.18  root      963:        n += snprintf(buf + n, sizeof(buf) - n, "%u", mainram->GetSizeMB());
1.1.1.15  root      964:        strlcpy(buf + n, (const char *)&Builtin::IPLROM30[0x1129f],
                    965:                sizeof(buf) - n);
1.1.1.20! root      966:        PrintDisplay(buf);
1.1.1.15  root      967: }
                    968: 
                    969: void
                    970: LunaPROMEmuDevice::ROM_AP2()
                    971: {
                    972:        InitPalette();
                    973:        InitScreen();
                    974: }
                    975: 
1.1.1.7   root      976: // キーコードから文字への変換
                    977: /*static*/ const uint8
                    978: LunaPROMEmuDevice::lunakey2asciitable[] = {
1.1.1.9   root      979:         0,   0,   0,   0,   0,   0,   0,   0,
                    980:         0,   0,   0,   0,   0,   0,   0,   0,
                    981:         0,  BS,  LF,   0, ' ',   0,   0,   0,
                    982:         0,   0,   0,   0,  UP,LEFT,RIGHT,DOWN,
1.1.1.7   root      983:         0,   0,  '1', '2', '3', '4', '5', '6',
                    984:        '7', '8', '9', '0', '-', '^', '\\', 0,
                    985:         0,   0,  'q', 'w', 'e', 'r', 't', 'y',
                    986:        'u', 'i', 'o', 'p', '@', '[',  0,   0,
                    987:         0,   0,  'a', 's', 'd', 'f', 'g', 'h',
                    988:        'j', 'k', 'l', ';', ':', ']',  0,   0,
                    989:         0,   0,  'z', 'x', 'c', 'v', 'b', 'n',
                    990:        'm', ',', '.', '/',  0,   0,   0,   0,
1.1.1.13  root      991:         0,  '+', '-', '7', '8', '9', '4', '5',
                    992:        '6', '1', '2', '3', '0', '.', LF,   0,
                    993:         0,   0,   0,   0,   0,   0,   0,   0,
                    994:         0,   0,   0,   0,  '*', '/', '=', ',',
1.1.1.7   root      995: };
                    996: /*static*/ const uint8
                    997: LunaPROMEmuDevice::lunakey2shifttable[] = {
1.1.1.9   root      998:         0,   0,   0,   0,   0,   0,   0,   0,
                    999:         0,   0,   0,   0,   0,   0,   0,   0,
                   1000:         0,  BS,  LF,   0, ' ',   0,   0,   0,
                   1001:         0,   0,   0,   0,  UP,LEFT,RIGHT,DOWN,
                   1002:         0,   0,  '!', '\"','#', '$', '%', '&',
                   1003:        '\'','(', ')', ' ', '=', '~', '|',  0,          // XXX SHIFT+'0'は?
1.1.1.7   root     1004:         0,   0,  'Q', 'W', 'E', 'R', 'T', 'Y',
                   1005:        'U', 'I', 'O', 'P', '`', '{',  0,   0,
                   1006:         0,   0,  'A', 'S', 'D', 'F', 'G', 'H',
                   1007:        'J', 'K', 'L', '+', '*', '}',  0,   0,
                   1008:         0,   0,  'Z', 'X', 'C', 'V', 'B', 'N',
                   1009:        'M', '<', '>', '?', '_',  0,   0,   0,
1.1.1.13  root     1010:         0,  '+', '-', '7', '8', '9', '4', '5',
                   1011:        '6', '1', '2', '3', '0', '.', LF,   0,
                   1012:         0,   0,   0,   0,   0,   0,   0,   0,
                   1013:         0,   0,   0,   0,  '*', '/', '=', ',',
1.1.1.7   root     1014: };

unix.superglobalmegacorp.com

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