--- nono/vm/romemu_luna.cpp 2026/04/29 17:04:32 1.1 +++ nono/vm/romemu_luna.cpp 2026/04/29 17:05:43 1.1.1.18 @@ -1,701 +1,595 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "aout.h" -#include "bitmap.h" -#include "bt454.h" -#include "ffs_dinode.h" -#include "ffs_dir.h" -#include "ffs_fs.h" +// +// LUNA シリーズの ROM エミュレーション共通部分 +// + +// IODevice +// | +// +- ROMDevice (LoadROM()、ウェイト、マスク等を持つ) +// | +- PROMDevice (LUNA* の PROM) +// | +- IPLROM1Device (X680x0 の IPLROM 後半) +// | +- IPLROM2Device (X680x0 の IPLROM 前半) +// | +- CGROMDevice (X680x0 の CGROM) +// | | +// | +- ROMEmuDevice +// | | +// | | +-------------------+ +// | +-| LunaPROMEmuDevice | (LUNA PROM エミュレーションの共通部分) +// | | +-------------------+ +// | | | +// | | +- Luna1PROMEmuDevice (LUNA-I の PROM エミュレーション) +// | | +- Luna88kPROMEmuDevice (LUNA-88K の PROM エミュレーション) +// | +- NewsROMEmuDevice (NEWS の ROM エミュレーション) +// | +- ROM30EmuDevice (X68030 の ROM30 エミュレーション) +// | +- Virt68kROMEmuDevice (virt-m68k の IPLROM 相当の何か) +// | +// +- PROM0Device (LUNA* のブートページ切り替え用プロキシ) +// +- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ) + +#include "romemu_luna.h" +#include "bt45x.h" +#include "builtinrom.h" +#include "lance.h" +#include "lunafb.h" #include "mainapp.h" -#include "memorystream.h" +#include "mainram.h" #include "mk48t02.h" +#include "mpu.h" #include "pio.h" -#include "romemu_luna.h" -#include "ram.h" +#include "prom.h" +#include "scsidev.h" +#include "scsidomain.h" #include "sio.h" #include "spc.h" - -// ROM エミュレーションの状態遷移 -enum { - STATE_CLOSED = 0, // バックドアは塞がれている - STATE_RESET, // リセット後、アクセス前 - STATE_INIT, // プロンプト初期化中 - STATE_PROMPT, // プロンプト実行中 -}; +#include "ufs.h" // コンストラクタ LunaPROMEmuDevice::LunaPROMEmuDevice() + : inherited(OBJ_PROM) { - mem = NULL; + // LUNA では ROM への書き込みは何も起きない + write_op = 0; } // デストラクタ LunaPROMEmuDevice::~LunaPROMEmuDevice() { - if (mem) { - delete[] mem; - } } +// 初期化 bool LunaPROMEmuDevice::Init() { - // 都度都度 dynamic_cast は面倒なので、ここで一度代入しとく - nvram = dynamic_cast(gRTC.get()); - - mem = new uint8 [devlen]; - - MemoryStreamBE ms(mem); - ms.Write32(0x00002000); // +00: リセットベクタ SP - ms.Write32(0x41000400); // +04: リセットベクタ PC - ms.Write32(0); // +08 - ms.Write32(0x41000500); // +0c: メモリサイズ格納先 - - // リセット時に実行する命令 - ms.SetPtr(mem + 0x400); - ms.Write16(0x2039); // move.l (IPL_INIT),d0 - ms.Write32(ROMIO_IPL_INIT); - ms.Write16(0x660e); // bne _go ; 起動先あればGO - //_prompt: - ms.Write16(0x2039); // move.l (IPL_PROMPT),d0 - ms.Write32(ROMIO_IPL_PROMPT); - ms.Write16(0x6606); // bne _go ; 起動先あればGO - ms.Write32(0x4e722500); // stop #0x2500 ; なければ待つ - ms.Write16(0x60f2); // bra _prompt ; の繰り返し - //_go: - ms.Write16(0x2040); // move.l d0,a0 ; ジャンプ先を a0 にセット - ms.Write16(0x2039); // move.l (IPL_CLOSE),d0 ; 後始末 - ms.Write32(ROMIO_IPL_CLOSE); - ms.Write16(0x4ed0); // jmp (a0) ; ジャンプ - - // Lv6 (SIO) 割り込みハンドラ - // キーボード割り込みが上がったら STOP 命令から戻ってくるだけでいい - ms.SetPtr(mem + 0x600); - ms.Write16(0x4e73); // rte - - // MAC アドレス置き場のマジック - strcpy((char *)&mem[0x1ffd8], "ENADDR"); - - // RAM にベクタを書き込む - // XXX VBR を 41000000 に向けるのとどっちが楽かはある - gRAM->Write32(0x78, 0x41000600); // +78: Lv6 割り込み + bt45x = GetBT45xDevice(); + lance = GetLanceDevice(); + lunafb = GetLunafbDevice(); + mainram = GetMainRAMDevice(); + nvram = GetMK48T02Device(); + sio = GetSIODevice(); + spc = GetSPCDevice(); return true; } -void -LunaPROMEmuDevice::ResetHard() +// ROMIO から読み出す。 +// ROMIO が応答すべきでなければ (uint64)-1 を返す。 +uint64 +LunaPROMEmuDevice::ReadROMIO(busaddr addr) { - ipl_state = STATE_RESET; -} + // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。 + if ((mpu->GetPPC() & 0xff000000) == baseaddr && addr.GetSize() == 4) { + switch (addr.Addr()) { + case ROMIO_INIT: + ROM_Init(); + return 0; -// アドレスデコーダ -inline uint64 -LunaPROMEmuDevice::Decoder(uint32 addr) const -{ - return addr - baseaddr; -} + case ROMIO_LOAD: + entrypoint = ROM_Load(); + putlog(2, "ROMIO_LOAD entrypoint=$%08x", entrypoint); + return 0; -uint64 -LunaPROMEmuDevice::Read32(uint32 addr) -{ - uint64 offset = Decoder(addr); + case ROMIO_KEYIN: + putlog(2, "ROMIO_KEYIN"); + ROM_Keyin(); + return 0; - // ロングワードアクセスでだけ謎の I/O 空間が見える - if (ipl_state != STATE_CLOSED) { - switch (offset) { - case ROMIO_IPL_INIT - baseaddr: - return IPL_Init(); + case ROMIO_CLOSE: + putlog(2, "ROMIO_CLOSE"); + ROM_Close(); + return 0; + + case ROMIO_ENTRY: + { + uint32 rv = ROM_Entry(); + putlog(2, "ROMIO_ENTRY entrypoint=$%08x", rv); + return rv; + } + + case ROMIO_AP1: + ROM_AP1(); + return 0; - case ROMIO_IPL_PROMPT - baseaddr: - return IPL_Prompt(); + case ROMIO_AP2: + ROM_AP2(); + return 0; - case ROMIO_IPL_CLOSE - baseaddr: - return IPL_Close(); + case ROMIO_CLKCNT: + return clkcnt; default: break; } } - return inherited::Read32(addr); + + return (uint64)-1; } -// 起動時の処理を行う。 -// 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。 -// そうでなければ 0 を返すので、プロンプトに処理を移すこと。 -uint32 -LunaPROMEmuDevice::IPL_Init() +// ROMIO に書き込む。 +// 反応すれば true を返す。そうでなければ false を返す。 +bool +LunaPROMEmuDevice::WriteROMIO(busaddr addr, uint32 data) { - MemoryStreamBE ms; - uint32 entry; - - ipl_state = STATE_INIT; - entrypoint = 0; + // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。 + if ((mpu->GetPPC() & 0xff000000) == baseaddr && addr.GetSize() == 4) { + switch (addr.Addr()) { + case ROMIO_CLKCNT: + clkcnt = data; + return true; + default: + break; + } + } - // ROM 0x41000000.L リセットベクタ(SP,PC)もカーネルが参照している - // (これは Init() が用意している) - // ROM 0x4100000c.L メモリサイズ(バイト数)格納先アドレス - // ROM 0x410000b8.L プレーンマスク格納先アドレス - - // メモリサイズはとりあえず 0x41000500 に格納 - ms.SetPtr(mem + 0x500); - ms.Write32(::ram_size); - - // プレーン数(1..4)をプレーンマスク(0x01..0x0f)に - int nplane = gBitmap->GetPlaneCount(); - int planemask = (1 << nplane) - 1; - // 実際は RAM 上を指しているのだが、とりあえずすぐ隣に置いておく。 - ms.SetPtr(mem + 0xb8); - ms.Write32(0x410000bc); - ms.Write32(planemask); + return false; +} +// ROM 処理の初期化。 +void +LunaPROMEmuDevice::ROM_Init() +{ // 初期パレット - gBT454->Write8(0, 14 << 4); // select palette - gBT454->Write8(1, 15 << 4); // R=15 - gBT454->Write8(1, 15 << 4); // G=15 - gBT454->Write8(1, 15 << 4); // B=15 + InitPalette(); // SCSI // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。 - gSPC->Write8(SPC::BDID, 7); + spc->WritePort(SPC::BDID, 7); + + // LANCE。 + lance->WritePort(0, 0x00); // RAP=CSR0 + lance->WritePort(1, AM7990::CSR0_STOP); + + // キーボード(chB)を初期化 + sio->WritePort(3, 0x01); // CR1 + sio->WritePort(3, 0x10); // RX Int(all char) + sio->WritePort(3, 0x04); // CR4 + sio->WritePort(3, 0x44); // Stop 1bit + sio->WritePort(3, 0x03); // CR3 + sio->WritePort(3, 0xc1); // RX Enable, 8bit + sio->WritePort(3, 0x05); // CR5 + sio->WritePort(3, 0x68); // TX Enable, 8bit + + // LED を(明示的に)オフ、マウスサンプリングをオフ + sio->WritePort(2, 0x00); + sio->WritePort(2, 0x01); + sio->WritePort(2, 0x20); + + // 電源オフをキャンセル。 + // 実 PROM では、リセット後に PIO のモードセットを行っている。 + // モードセットを行うと PortC のビットが %0 になる = 電源オフなので、 + // 即座にこれをキャンセルしている。 + // ROMEmu ではモードセットを行っていないが、キャンセルだけ発行しておく。 + auto pio1 = GetPIO1Device(); + pio1->WritePort(3, 0x09); + + // 機種別の初期化 + ROM_InitMD(); // スクリーンを初期化 - text.Init(80, 32); - text.foldmode = true; - gBitmap->EmuConsoleOpen(&text); - - text.Print(0, 0, "NONO %d.%d.%d Emulated ROM Monitor", - NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER); - text.Locate(0, 2); + InitScreen(); +} + +// 起動時の実行ファイル読み込み処理を行う。 +// 次段実行ファイルの読み込みに成功すれば execute を true にして、 +// エントリポイントを返す。 +// そうでなければ -1 を返す。 +uint32 +LunaPROMEmuDevice::ROM_Load() +{ + uint32 entry; + + entry = -1; + execute = false; // 表示環境が整ったところで NVRAM を照合 - if (VerifyNVRAM() == false) { - InitNVRAM(); - text.Print("** NVRAM Initialized.\n"); - text.Print("\n"); - return 0; - } - - // 実際の画面更新は EmuConsoleUpdate() を呼ぶことで行われるので、 - // タイトルを Print してはいるが、エラーなくブートプログラムに制御を - // 渡せた場合にはそれすら表示せずにゲストに処理を移す。 - // 意図して或いは意図せずプロンプトに降りる場合だけ Update が起きて - // 初めて画面が更新される。 - - // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、 - // これを真似する。 - entry = 0; - if ((gPIO0->GetDIPSW1() & 1)) { - if (gMainApp.host_file) { - // ホストのファイルを読み込んでエントリポイントアドレスを - // この Read アクセスの読み出し値として返す。 - // エラーがあればエラーメッセージは呼び出し先側で表示してある。 - entry = LoadHostFile(); - } else { - // NVRAM の設定に従ってブート先を探す。 - errmsg.clear(); - entry = LoadByNVRAM(""); - if (entry == 0) { - text.Print("** %s\n", errmsg.c_str()); + if (VerifyNVRAM()) { + // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、 + // これを真似する。 + auto pio0 = GetPIO0Device(); + if (pio0->IsDIPSW11Up()) { + entry = LoadFile(""); + if (entry != -1) { + execute = true; + return entry; } } + } else { + // LUNA-88K の PROM 1.20 は初期化するかどうか確認してくるけど、 + // とりあえず。 + InitNVRAM(); + errmsg = "NVRAM Initialized."; + // NVRAM をクリアしたら DIPSW によらずプロンプトに降りる + } + + PrintTitle(); + if (errmsg.empty() == false) { + Print("** %s\n\n", errmsg.c_str()); } + ClearPrompt(); return entry; } -// ROM 処理のクローズ。 -// 次段プログラムにジャンプするための処理も含む。 +// 実行ファイルに処理を移すならそのエントリポイントを返す。 +// そうでなければ -1 を返す。 uint32 -LunaPROMEmuDevice::IPL_Close() +LunaPROMEmuDevice::ROM_Entry() { - // ここでバックドアに蓋をする - ipl_state = STATE_CLOSED; + if (execute == false) { + return -1; + } + return entrypoint; +} - // コンソールクローズ - gBitmap->EmuConsoleClose(); +// パレットを初期化 +void +LunaPROMEmuDevice::InitPalette() +{ + // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。 + uint8 val = 0xff; + bt45x->WritePort(0, 0); // select palette + for (int i = 0; i < 16; i++) { + bt45x->WritePort(1, val); // R + bt45x->WritePort(1, val); // G + bt45x->WritePort(1, val); // B + val ^= 0xff; + } +} - // NetBSD のブートローダがカーネルを読み込む時にはいくつかレジスタ - // 渡しのパラメータがあるので、ここで用意する。本当はターゲットが - // NetBSD カーネルの時に限定すべきのような気もするけど、とりあえず。 - // ROM がブートローダを読み込む時にはそのような取り決め事は一切ない - // ので、ブートローダを読む時にも同じ値が入ってしまうが気にしない - // ことにする。 - // 必要なパラメータは - // %d6 に bootdev、NetBSD/luna68k の場合は - // bootdev = $MACUPP00 - // ||||++-- パーティション番号 - // |||+---- SCSI ID (XXX ただしここでは 6 決め打ち) - // ||+----- unit (spc0 なら 0 のやつのはず) - // |+------ アダプタ (spc = 0、lance = 1) - // +------- magic ($a) - // - // %d7 に howto (0 でよい) - // (と %a0 にジャンプ先を入れるがこれは Init() が書き出した ROM - // アセンブラコード側で行っている) - m68kcpu *cpu = gMPU680x0->GetCPU(); - RegD(6) = 0xa0060000; - RegD(7) = 0; - - // 戻り値は使わないのでなんでもいい - return 0; -} - -// ROM プロンプト。 -// VM 側の仮想マシンを回すため、ここでは入力待ちはしない。 -// state が INIT なら1回目なので初期化など初回の処理を行う。 -// state が PROMPT なら1文字入力されたので処理をする。 -// エントリポイントが取得できればそれを、そうでなければ 0 を返す。 -uint32 -LunaPROMEmuDevice::IPL_Prompt() +// スクリーンを初期化 +void +LunaPROMEmuDevice::InitScreen() { - switch (ipl_state) { - case STATE_INIT: - // キーボード(chB)を初期化 - gSIO->Write8(0x51000006, 0x01); // CR1 - gSIO->Write8(0x51000006, 0x10); // RX Int(all char) - gSIO->Write8(0x51000006, 0x03); // CR3 - gSIO->Write8(0x51000006, 0xc1); // RX Enable, 8bit - gSIO->Write8(0x51000006, 0x04); // CR4 - gSIO->Write8(0x51000006, 0x44); // Stop 1bit - gSIO->Write8(0x51000006, 0x05); // CR5 - gSIO->Write8(0x51000006, 0x68); // TX Enable, 8bit - - // バッファを初期化 - inputbuf.clear(); - inputpos = 0; - is_shift = false; + lunafb->EmuInitScreen(); + + screen_w = 80; + screen_h = 32; + cursor_x = 0; + cursor_y = 0; + cursor_on = true; + + // 表示範囲はざっくりセンタリングする + origin_px = (1280 - screen_w * 12) / 2; + origin_py = (1024 - screen_h * 24) / 2; + // 横は 48ドット境界から始めておく。 + // こうすると1(,2)文字目の24ビットが必ずワード境界から始まるため。 + // 今となっては任意の場所から文字が描画できるようになったので揃える + // 必要はなくなったが、依然このほうがアクセス効率はいいので。 + origin_px = (origin_px / 48) * 48; + + cursor_on = true; + + inputbuf.clear(); + inputpos = 0; + is_shift = false; + mousecnt = 0; +} + +// タイトルを出力 +void +LunaPROMEmuDevice::PrintTitle() +{ + Print("NONO %u.%u.%u Emulated ROM Monitor for %s\n\n", + NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, machine_name); +} + +// 1文字出力 +void +LunaPROMEmuDevice::Putc(uint ch) +{ + uint px = origin_px + cursor_x * 12; + uint py = origin_py + cursor_y * 24; - ipl_state = STATE_PROMPT; - prompt_idx = 0; - prompt = default_prompt; + if (cursor_on) { + // カーソル位置の反転を元に戻す + lunafb->EmuPutc(px, py, ' ', ROP::INV2); + } + + // いくつかの制御コードを勝手に使う。 + switch (ch) { + case 0x0f: // SI + bold = true; + break; + case 0x0e: // SO + bold = false; break; - case STATE_PROMPT: - // キー入力があった + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // カラーコード変更 + lunafb->SetBMSEL(ch - 0x10); + break; - uint32 lunakey = gSIO->Read8(0x51000004); + case CR: + break; - // SHIFT キーだけ状態を持つので先に処理 - if (lunakey == 0x0c || lunakey == 0x0d) { // 左右SHIFT押下 - is_shift = true; - return 0; + case LF: + put_LF: + cursor_x = 0; + if (cursor_y < screen_h - 1) { + cursor_y++; + } else { + // 上スクロールして.. + lunafb->EmuScrollY(origin_py, origin_py + 24, (screen_h - 1) * 24); + // 最終行を消す + lunafb->EmuFill(0, py, 1280, 24, 0); } - if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放 - is_shift = false; - return 0; + break; + + case LEFT: + if (cursor_x > 0) { + cursor_x--; + } else { + cursor_y--; + cursor_x = screen_w - 1; } - // 後は最上位ビットが立ってたらキー開放かマウスなので無視 - if ((lunakey & 0x80)) { - return 0; + break; + + case RIGHT: + if (cursor_x < screen_w - 1) { + cursor_x++; + } else { + cursor_y++; + cursor_x = 0; } + break; - switch (lunakey) { - case 0x11: // BS - if (inputpos > 0) { - inputbuf.erase(--inputpos); - } - break; - case 0x12: // Enter - { - // 現在位置のカーソルを消去 - uint16 *rawbuf = text.GetRawBuf(); - int curx = prompt.length() + inputpos; - rawbuf[text.GetY() * text.GetCol() + curx] &= ~TA::On; - // 改行 - text.Putc('\n'); - // コマンド処理。 - // エントリポイントが見付かればもうここは放棄するので帰るだけ。 - uint32 entry = IPL_Command(); - if (entry != 0) { - return entry; - } - inputbuf.clear(); - inputpos = 0; - break; - } - case 0x1d: // ← - if (inputpos > 0) { - inputpos--; - } - break; - case 0x1e: // → - if (inputpos < inputbuf.length()) { - inputpos++; - } - break; - default: // 通常文字 - { - char asciicode; - if (is_shift) { - asciicode = lunakey2shifttable[lunakey]; - } else { - asciicode = lunakey2asciitable[lunakey]; + default: + if (ch < 0x80) { + lunafb->EmuPutc(px, py, ch); + if (bold) { + for (int i = 1; i < 2; i++) { + lunafb->EmuPutc(px + i, py, ch, ROP::OR1); + } } - if (asciicode == 0) { - // 割り当てなし - return 0; + } else { + lunafb->EmuPutc(px, py, ' '); + lunafb->EmuPutc(px, py + 10, ch); + if (bold) { + for (int i = 1; i < 2; i++) { + lunafb->EmuPutc(px + i, py + 10, ch, ROP::OR1); + } } - inputbuf.insert(inputbuf.begin() + inputpos, asciicode); - inputpos++; - break; - } } - putmsg(2, "inputbuf=%zd|%s|", inputbuf.length(), inputbuf.c_str()); + cursor_x++; + if (cursor_x >= screen_w) { + goto put_LF; + } break; } - // 入力行全体を都度更新 (面倒なので行をはみ出さないようにする) - std::string buf = prompt + inputbuf; - text.Print(0, text.GetY(), "%-79s", buf.c_str()); - // カーソルを描画 (生バッファを触って属性を立てる) - uint16 *rawbuf = text.GetRawBuf(); - int curx = prompt.length() + inputpos; - rawbuf[text.GetY() * text.GetCol() + curx] |= TA::On; - // 画面更新 - gBitmap->EmuConsoleUpdate(); - return 0; + if (cursor_on) { + // カーソル位置を再計算して描画 (文字を反転) + px = origin_px + cursor_x * 12; + py = origin_py + cursor_y * 24; + lunafb->EmuPutc(px, py, ' ', ROP::INV2); + } } -// 入力コマンド処理 -uint32 -LunaPROMEmuDevice::IPL_Command() +// 文字列 s を出力 +void +LunaPROMEmuDevice::Print(const std::string& s) { - char buf[inputbuf.length() + 1]; - std::vector arg; - - // 1ワードごとに分解 - strcpy(buf, inputbuf.c_str()); - char *p = buf; - char *s = p; - while (*p != '\0') { - if (*p == ' ') { - *p++ = '\0'; - arg.push_back(std::string(s)); - s = p; - } else { - p++; - } - } - if (p != s) { - arg.push_back(std::string(s)); - } - if (loglevel >= 1) { - for (int i = 0; i < arg.size(); i++) { - putmsg("arg[%d]=|%s|", i, arg[i].c_str()); - } + for (auto *p = s.c_str(); *p; p++) { + Putc(*p); } +} - // "k" コマンド中ならここで先に処理する - if (prompt_idx != 0) { - IPL_CommandK(arg); - return 0; - } - - // 空行 - if (arg.size() == 0) { - return 0; - } - - // 1ワード目がコマンド - if (arg[0] == "h") { - // ヘルプメッセージ - text.Print("Commands\n"); - text.Print( -" g [] : load program\n" -" gh : load host program (specified by -A option)\n" -" h : show this message\n" -" k : show/set boot constants\n" -" x : execute loaded program\n" - ); - return 0; - } - if (arg[0] == "g") { - IPL_CommandG(arg); - return 0; - } - if (arg[0] == "gh") { - IPL_CommandGH(); - return 0; - } - if (arg[0] == "k") { - IPL_CommandK(arg); - return 0; - } - if (arg[0] == "x") { - return IPL_CommandX(); - } - - text.Print("** Unknown command: %s\n", arg[0].c_str()); - return 0; -} - -// "g" コマンド -// g なら NVRAM の情報に従ってプログラムをロード。 -// g なら NVRAM の fname だけ指定の名前を使ってロード。NVRAM には -// 書き込まない。 +// 文字列 fmt... を出力 void -LunaPROMEmuDevice::IPL_CommandG(const std::vector& arg) +LunaPROMEmuDevice::Print(const char *fmt, ...) { - std::string ctlr = nvram->PeekString(0x0030); - if (ctlr == "dk") { // harddisk - std::string fname {}; - - switch (arg.size()) { - case 1: - break; - case 2: - fname = arg[1]; - break; - default: - text.Print("** Invalid argument.\n"); - return; - } - errmsg.clear(); - entrypoint = LoadByNVRAM(fname); - if (entrypoint == 0) { - text.Print("** %s\n", errmsg.c_str()); - } + char buf[1024]; + va_list ap; - } else if (ctlr == "et" || // netboot - ctlr == "sd" || // cassette tape - ctlr == "fl" ) // floppy - { - text.Print("** %s boot not supported.\n", ctlr.c_str()); - return; - } else { - // 起きないはず - text.Print("** Invalid boot controller name.\n"); - return; - } + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); - if (entrypoint) { - text.Print("Loaded. Entry point = $%08x\n", entrypoint); + for (char *p = buf; *p; p++) { + Putc(*p); } } -// "gh" コマンド (勝手に拡張) -// -A オプションで指定されたホストプログラムをロードする。 +// ROM 処理のクローズ void -LunaPROMEmuDevice::IPL_CommandGH() +LunaPROMEmuDevice::ROM_Close() { - entrypoint = 0; - if (gMainApp.host_file) { - // 指定されてたら読み込む - entrypoint = LoadHostFile(); - } else { - text.Print("** Cannot loaded; host program is not specified.\n"); - } -} - -// -A オプションで指定されたホストファイルをロードする。 -// ロードできればエントリポイントを返す。 -// host_file が指定されている時に呼ぶこと。 -uint32 -LunaPROMEmuDevice::LoadHostFile() + // SIO をリセットしておく。 + // SIO のバッファにデータが残っていると、NetBSD のブートローダが + // 起動してこないようだ。リセットして RXEN を落としておけば大丈夫ぽい。 + sio->ResetHard(false); + + // 実行ファイルにジャンプ後、NMI でプロンプトにもう一度戻ってこれるので + // その時のためにエントリポイント等も初期化しておく。 + entrypoint = -1; + execute = false; +} + +// ROM プロンプトのキー入力。 +// ゲストコードの割り込みハンドラから呼ばれて、キー入力で行を構成し、 +// あるいは行が完成すればコマンドを実行する。 +// 実行結果により画面を更新したりメンバ変数を更新する。 +void +LunaPROMEmuDevice::ROM_Keyin() { - assert(gMainApp.host_file); + int asciicode = Getc(); - uint32 entry = gRAM->LoadFile(gMainApp.host_file, AOUT_MID_M68K); - if (entry) { - text.Print("Host program loaded. Entry point = $%08x\n", - entry); - } else { - text.Print("** Couldn't load specified host program.\n"); + if (cursor_on && asciicode != -1) { + ProcessChar(asciicode); } - return entry; } - -// k コマンドのパラメータ表 -struct kinfo { - uint32 offset; // NVRAM 上のオフセット - const char * const name; // 表示名 - int next; // 次インデックス -}; -static const struct kinfo kinfo[] = { - { 0 }, // 0 は通常プロンプトを指すのでここでは使わない - { 0x0030, "controller", 0 }, // [1] BOOT (next は動的判定する) - { 0x0090, "drive unit", 3 }, // [2] DKUNIT - { 0x00b0, "partition ", 4 }, // [3] DKPART - { 0x00d0, "filename ", 0 }, // [4] FLNAME - { 0x0050, "hostname ", 6 }, // [5] HOST - { 0x0070, "servername", 7 }, // [6] SERVER - { 0x0190, "filename ", 8 }, // [7] ETBOOT - { 0x01b0, "", 0 }, // [8] ETFILE (ETBOOT と1行で処理する) -}; - -// "k" コマンド -void -LunaPROMEmuDevice::IPL_CommandK(const std::vector& arg) +// キー入力。 +// 押されているキーの ASCII コードを返す。 +// キー入力が無いときは -1 を返す。 +int +LunaPROMEmuDevice::Getc() { - uint32 offset; - const char *name; - int next; - - // 入力を処理 - if (prompt_idx == 0) { - // ここは1行目表示前なので前行の入力はない。 - next = 1; - } else { - std::string val; - bool rv; - - // 入力があれば更新 - if (arg.size() != 0) { - rv = CommandKInput(arg[0]); - } else { - // [7] の時に入力があれば CommandKInput() の中で更新されるが、 - // 入力がなければそこを通らないので、こっちでやらないといけない。 - // うーん。 - if (prompt_idx == 7) { - prompt_idx = kinfo[prompt_idx].next; - } - rv = true; - } + // コントロールレジスタポインタを確実に 0 にするため読み捨てる + sio->ReadPort(3); - // これ以降は更新後の prompt_idx を使う + uint8 sr0 = sio->ReadPort(3); + if ((sr0 & MPSCC::SR0_RXAVAIL) == 0) { + return -1; + } - // 入力エラーがあった時はこの行を再入力するため - next = prompt_idx; + uint32 lunakey = sio->ReadPort(2); - if (rv == true) { - // 入力があっても空でも次インデックスへ。 - if (prompt_idx == 1) { - // BOOT なら現在値によって次のインデックスが変わる - // "dk" なら [2]、"et" なら [5]。他は未対応 - offset = kinfo[prompt_idx].offset; - val = nvram->PeekString(offset); - if (val == "dk") { - next = 2; - } else if (val == "et") { - next = 5; - } else { - // 起きることはないはず - text.Print("** Inconsistent boot parameter: %s\n", - val.c_str()); - } - } else { - // それ以外はテーブルにかかれている通り - next = kinfo[prompt_idx].next; - } - } + // マウス入力の 2, 3バイト目は無視 + if (mousecnt > 0) { + mousecnt--; + return -1; } - // この行のプロンプト文字列を作成 - prompt_idx = next; - if (prompt_idx == 0) { - // 最終行なら元に戻す - prompt = default_prompt; + // SHIFT キーだけ状態を持つので先に処理 + if (lunakey == 0x0c || lunakey == 0x0d) { // 左右SHIFT押下 + is_shift = true; + return -1; + } + if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放 + is_shift = false; + return -1; + } + // マウスデータの1バイト目なら、後続2バイトをスキップ + if ((lunakey & 0xf8) == 0x80) { + mousecnt = 2; + return -1; + } + // キー開放は無視してよい + if ((lunakey & 0x80)) { + return -1; + } + + // キーコードから文字を取得 + int asciicode; + if (is_shift) { + asciicode = lunakey2shifttable[lunakey]; } else { - // 次のプロンプトに変更 - name = kinfo[prompt_idx].name; - offset = kinfo[prompt_idx].offset; - std::string val = nvram->PeekString(offset); - - // [7] ETBOOT だけ1行で ETBOOT:ETFILE 形式 - if (prompt_idx == 7) { - next = kinfo[prompt_idx].next; // ETFILE - offset = kinfo[next].offset; - val += ":"; - val += nvram->PeekString(offset); - } - - prompt = string_format("%s: %s ?", name, val.c_str()); + asciicode = lunakey2asciitable[lunakey]; } + if (asciicode == 0) { + return -1; + } + + return asciicode; } -// k コマンドの入力値を処理する部分。 -// 成功すれば true を返す。 -// 失敗すれば false を返す。 -// 実機 ROM は入力値にエラーがあると黙って再入力を促す。 -// エラーメッセージくらい表示してもいいかもだがとりあえず真似しておく。 -bool -LunaPROMEmuDevice::CommandKInput(const std::string& val) +// キー入力ハンドラ (キー入力割り込みによって呼ばれる)。 +// 引数 asciicode は概ね ASCII コード (ただし 0x1c..0x1f が上下左右)。 +void +LunaPROMEmuDevice::ProcessChar(char asciicode) { - int offset; - bool rv = true; - - offset = kinfo[prompt_idx].offset; - - switch (prompt_idx) { - case 1: // BOOT - // 正常な入力値は "dk", "et", "sd", "fl" のみ。 - if (val != "dk" && val != "et" && val != "sd" && val != "fl") { - return false; - } - rv = nvram->WriteString(offset, val); - if (rv == false) { - return false; + // LUNA-I の場合、 + // 実機 PROM にはカーソル左右移動の機能はなく、カーソルは常に行末にいる。 + // そのためかどうか Delete も BackSpace と同じく1文字前を消す動作をして + // いる。カーソル位置に文字はないので、Delete をそうするのはまあ分からん + // でもない。 + // 一方、エミュレーション ROM では、カーソルの左右移動をサポートしてみた + // ため Delete を「カーソル位置の文字を消す」にすることは出来るが、それは + // それで実機と乖離が広がるので、どうしたもんか。 + + switch (asciicode) { + case BS: + if (inputbuf.empty() == false && inputpos != 0) { + inputbuf.erase(--inputpos, 1); + Putc(LEFT); + for (int i = inputpos; i < inputbuf.size(); i++) { + Putc(inputbuf[i]); + } + Putc(' '); + for (int i = 0; i < 1 + inputbuf.size() - inputpos; i++) { + Putc(LEFT); + } } break; - case 7: // ETBOOT - { - // こいつだけ ETBOOT:ETFILE 形式で2つのパラメータを同時に処理する - - // ':' で前後に分ける - int sep = val.find(':'); - if (sep == std::string::npos) { - return false; - } - std::string etboot = val.substr(0, sep); - std::string etfile = val.substr(sep + 1, val.size() - sep + 1); - if (etboot.empty() || etfile.empty()) { - return false; - } + case LF: + // 改行 + Putc(asciicode); + prompt_y = cursor_y; + // コマンド実行 + Command(); + // プロンプトを更新 + ClearPrompt(); + break; - // ETBOOT - rv = nvram->WriteString(offset, etboot); - if (rv == false) { - return false; + case LEFT: + if (inputpos > 0) { + inputpos--; + Putc(LEFT); } - - // 次へ - prompt_idx = kinfo[prompt_idx].next; - - // ETFILE - offset = kinfo[prompt_idx].offset; - rv = nvram->WriteString(offset, etfile); - if (rv == false) { - return false; + break; + case RIGHT: + if (inputpos < inputbuf.length()) { + inputpos++; + Putc(RIGHT); } break; - } - default: - rv = nvram->WriteString(offset, val); - if (rv == false) { - return false; + case UP: + case DOWN: + case 0: // キー割り当てなし、これは来ないはずだが一応 + return; + + default: // 通常文字のはず + inputbuf.insert(inputbuf.begin() + inputpos, asciicode); + for (int i = inputpos; i < inputbuf.size(); i++) { + Putc(inputbuf[i]); + } + for (int i = 0; i < inputbuf.size() - inputpos - 1; i++) { + Putc(LEFT); } + inputpos++; break; } - - WriteNVRAMCsum(); - return rv; } -// "x" コマンド -// すでに読み込まれてるはずのエントリポイントにジャンプするだけ。 -uint32 -LunaPROMEmuDevice::IPL_CommandX() +// 入力バッファを初期化する。 +void +LunaPROMEmuDevice::ClearPrompt() { - if (entrypoint == 0) { - text.Print("** Program is not loaded.\n"); - } - return entrypoint; + prompt_y = cursor_y; + // 入力行をクリア + inputbuf.clear(); + inputpos = 0; + + Print(prompt); } // NVRAM のチェックサムを返す uint8 -LunaPROMEmuDevice::CalcNVRAMCsum() +LunaPROMEmuDevice::CalcNVRAMCsum() const { uint8 eor = 0; for (uint32 addr = 0x20; addr < 0x560; addr++) { - eor ^= nvram->Peek(addr); + eor ^= nvram->PeekPort(addr); } return eor; } @@ -706,14 +600,14 @@ LunaPROMEmuDevice::WriteNVRAMCsum() { uint8 eor = CalcNVRAMCsum(); - nvram->Write(0x001c, eor); - nvram->Write(0x001d, eor); - nvram->Write(0x001e, eor); + nvram->WritePort(0x001c, eor); + nvram->WritePort(0x001d, eor); + nvram->WritePort(0x001e, eor); } // NVRAM のマジックとチェックサムを照合する bool -LunaPROMEmuDevice::VerifyNVRAM() +LunaPROMEmuDevice::VerifyNVRAM() const { if (nvram->PeekString(0x0004) != "") { return false; @@ -721,225 +615,83 @@ LunaPROMEmuDevice::VerifyNVRAM() uint8 eor = CalcNVRAMCsum(); for (uint32 addr = 0x1c; addr < 0x1f; addr++) { - if (nvram->Peek(addr) != eor) { + if (nvram->PeekPort(addr) != eor) { return false; } } return true; } -// NVRAM を初期化する -bool -LunaPROMEmuDevice::InitNVRAM() -{ - // 初期値どうしたもんか - nvram->WriteString(0x0004, ""); - nvram->WriteString(0x0020, "BOOT"); - nvram->WriteString(0x0030, "dk"); - nvram->WriteString(0x0040, "HOST"); - nvram->WriteString(0x0050, "nono"); // 偽札的なアレ - nvram->WriteString(0x0060, "SERVER"); - nvram->WriteString(0x0070, "servername"); - nvram->WriteString(0x0080, "DKUNIT"); - nvram->WriteString(0x0090, "0"); - nvram->WriteString(0x00a0, "DKPART"); - nvram->WriteString(0x00b0, "c"); - nvram->WriteString(0x00c0, "DKFILE"); - nvram->WriteString(0x00d0, "vmunix"); - nvram->WriteString(0x00e0, "FLUNIT"); - nvram->WriteString(0x00f0, "2"); - nvram->WriteString(0x0100, "FLFILE"); - nvram->WriteString(0x0110, "vmunix"); - nvram->WriteString(0x0120, "STUNIT"); - nvram->WriteString(0x0130, "0"); - nvram->WriteString(0x0140, "STFLNO"); - nvram->WriteString(0x0150, "1"); - nvram->WriteString(0x0160, "STFILE"); - nvram->WriteString(0x0170, "vmunix"); - nvram->WriteString(0x0180, "ETBOOT"); - nvram->WriteString(0x0190, "server"); - nvram->WriteString(0x01a0, "ETFILE"); - nvram->WriteString(0x01b0, "/vmunix"); - nvram->WriteString(0x0560, "ENADDR"); - nvram->WriteString(0x0570, ""); // どうするか - - // チェックサム再計算 - WriteNVRAMCsum(); - - return true; -} - -// キーコードから文字への変換 -/*static*/ const uint8 -LunaPROMEmuDevice::lunakey2asciitable[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, ' ', 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, '1', '2', '3', '4', '5', '6', - '7', '8', '9', '0', '-', '^', '\\', 0, - 0, 0, 'q', 'w', 'e', 'r', 't', 'y', - 'u', 'i', 'o', 'p', '@', '[', 0, 0, - 0, 0, 'a', 's', 'd', 'f', 'g', 'h', - 'j', 'k', 'l', ';', ':', ']', 0, 0, - 0, 0, 'z', 'x', 'c', 'v', 'b', 'n', - 'm', ',', '.', '/', 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, // XXX テンキー - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, +static const uint8 __unused test0[] = { + 0x12, 0x0f, 0x4e, 0x4f, 0x4e, 0x4f, 0x0e, 0x1e, 0x13, 0x00, }; -/*static*/ const uint8 -LunaPROMEmuDevice::lunakey2shifttable[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, ' ', 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, '1', '2', '3', '4', '5', '6', - '7', '8', '9', '0', '=', '~', '|', 0, - 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', - 'U', 'I', 'O', 'P', '`', '{', 0, 0, - 0, 0, 'A', 'S', 'D', 'F', 'G', 'H', - 'J', 'K', 'L', '+', '*', '}', 0, 0, - 0, 0, 'Z', 'X', 'C', 'V', 'B', 'N', - 'M', '<', '>', '?', '_', 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, // XXX テンキー - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, +static const uint8 __unused test1[] = { + 0xcd, 0xd5, 0xcc, 0xd4, 0xc9, 0xad, 0xd2, 0xc9, 0xd3, 0xc3, + 0x20, 0xd7, 0xcf, 0xd2, 0xcb, 0xd3, 0xd4, 0xc1, 0xd4, 0xc9, + 0xcf, 0xce, 0x20, 0x11, 0x0f, 0x4c, 0x55, 0x4e, 0x41, 0x2d, + 0x38, 0x38, 0x4b, 0x00, 0xaa, 0x12, 0x43, 0x58, 0xc2, 0x00, }; - - -// -// UFS ブート -// - -// ディスクラベル (1セクタ目) -// svnweb.freebsd.org/csrg/sys/luna68k/stand/omron_disklabel.h -struct scd_dk_label -{ - char asciilabel[128]; /* for compatibility */ - char pad[512 - (128 + 8*8 + 11*2 + 4)]; - uint16 badchk; /* checksum of bad track */ - uint32 maxblk; /* # of total logical block */ - uint16 dtype; /* disk drive type */ - uint16 ndisk; /* # of disk drives */ - uint16 ncyl; /* # of data cylinders */ - uint16 acyl; /* # of altenate cylinders */ - uint16 nhead; /* # of heads in this partition */ - uint16 nsect; /* # of 512 byte sectors per track */ - uint16 bhead; /* identifies proper label locations */ - uint16 ppart; /* physical partition # */ - struct { /* logical partitions */ - uint32 blkno; /* starting block */ - uint32 nblk; /* number of blocks */ - } map[8]; - uint16 magic; /* identifies this label format */ - uint16 cksum; /* xor checksum of sector */ - - static const uint16 DKL_MAGIC = 0xdabe; +static const uint8 __unused test2[] = { + 0xc8, 0xcf, 0xcc, 0xcf, 0xce, 0xc9, 0xc3, 0x20, 0xd7, 0xcf, + 0xd2, 0xcb, 0xd3, 0xd4, 0xc1, 0xd4, 0xc9, 0xcf, 0xce, 0x20, + 0x11, 0x0f, 0x4c, 0x55, 0x4e, 0x41, 0x00, 0x21, 0xd8, 0x67, }; -static_assert(sizeof(scd_dk_label) == 512, "size must be 512"); - -// オレオレ inode 情報クラス -class inodefile -{ - public: - inodefile(Device *dev) { - parent = dev; - } - virtual ~inodefile() { } - - // ディレクトリエントリからファイルを検索 - uint32 FindFile(const std::string& name); - - uint32 ino {}; // inode 番号 - struct ufs1_dinode di {}; // disk inode 情報 (ゲストエンディアンで保持) - std::vector data {}; // ファイル(またはディレクトリ)本文 - private: - Device *parent {}; // 親デバイス -}; - -// ファイルシステムハンドルっぽいもの -class Filesys -{ - public: - Filesys(Device *dev) { - parent = dev; - fs = &fs0; - } - virtual ~Filesys() { } - - // ファイルシステムをオープン - bool Open(SCSIHD *hd_, uint32 start, uint32 size); - // inode 情報を読み込む - bool Readi(inodefile& inode); - // データを読み込む - bool ReadData(inodefile& inode); - - std::string errstr{}; // エラーメッセージ - - private: - Device *parent {}; // 親デバイス - SCSIHD *hd {}; // ターゲット SCSI HD - // パーティション情報 - uint32 part_start {}; // パーティションの先頭セクタ - uint32 part_size {}; // パーティションのサイズ(セクタ数) - // スーパーブロック情報 - // fs の(たぶんほとんどの)整数パラメータは読み込み時にホストエンディアン - // に変換してあるのでそのままアクセスしてよい。構造体とかまでは全部対応 - // してないので、必要になったら書き足すこと。 - struct fs *fs; // 体裁を揃えるためポインタを使いたい - struct fs fs0; // こっちが実体 +static const uint8 __unused test3[] = { + 0x0e, 0x1e, 0x13, 0xd3, 0xc5, 0xd2, 0xc9, 0xc5, 0xd3, 0x0a, + 0x00, 0x0f, 0xd0, 0x4f, 0x7e, 0x40, 0x41, 0xcc, 0xdf, 0x09, }; -// NVRAM の設定に従って起動先を探す。 -// fname が指定されればそれをファイル名に使う。empty なら NVRAM の DKFILE -// をファイル名に使う。 -// 成功すればエントリポイントを返す。 -// 失敗ならをエラーメッセージを errmsg にセットして 0 を返す。 +// -X オプションで指定されたホストファイルをロードする。 +// ロードできればエントリポイントを返す。 +// 失敗ならエラーメッセージを errmsg にセットして (uint32)-1 を返す。 +// exec_file が指定されている時に呼ぶこと。 uint32 -LunaPROMEmuDevice::LoadByNVRAM(const std::string& fname) +LunaPROMEmuDevice::LoadHostFile() { - // 起動先情報を NVRAM (mk48t02) に問い合わせる - // DKUNIT はとりあえず無視して SCSI ID=6 - // DKPART は 'a'..'h' でパーティション。 - // DKFILE が読み込むカーネル名 (NetBSD ではブートローダ)。 - int dkunit = nvram->Peek(0x0090) - '0'; - int dkpart = nvram->Peek(0x00b0) - 'a'; - std::string dkfile_name; - if (!fname.empty()) { - dkfile_name = fname; + assert(gMainApp.exec_file); + + LoadInfo info(gMainApp.exec_file); + if (BootLoader::LoadExec(mainram, &info)) { + return info.entry; } else { - dkfile_name = nvram->PeekString(0x00d0); + errmsg = "Couldn't load specified host program."; + return -1; } - putmsg(1, "%s DKUNIT=%d", __func__, dkunit); - putmsg(1, "%s DKPART=%c", __func__, dkpart + 'a'); - putmsg(1, "%s DKFILE=|%s|", __func__, dkfile_name.c_str()); +} + +// bootinfo に従ってゲストから起動先をロードする。 +// ロードできればエントリポイントを返す。 +// 失敗ならエラーメッセージを errmsg にセットして (uint32)-1 を返す。 +uint32 +LunaPROMEmuDevice::LoadGuestFile(const bootinfo_t& bootinfo) +{ + const int& dkpart = bootinfo.dkpart; + const int& dkunit = bootinfo.dkunit; + const std::string& dkfile = bootinfo.dkfile; // ユニット (今は SCSI ID=6 決め打ち) - int id = 6; - SCSITarget *target = gSPC->GetTarget(id); + uint id = 6; + SCSITarget *target = spc->GetDomain()->GetTarget(id); if (target == NULL) { errmsg = "No bootable disks"; putmsg(0, "%s", errmsg.c_str()); - return 0; + return -1; } - if (target->GetDevType() != SCSI::HD) { + if (target->GetDevType() != SCSI::DevType::HD) { errmsg = "No bootable disks"; putmsg(0, "%s", errmsg.c_str()); - return 0; + return -1; } - SCSIHD *hd = dynamic_cast(target); - putmsg(2, "%s SCSIHD[%d] found", __func__, id); + SCSIDisk *hd = dynamic_cast(target); + putmsg(2, "%s SCSIHD[%u] found", __func__, id); // +0 セクタ目(512byte)に Sun disklabel scd_dk_label dk; - if (hd->PeekImage(&dk, 0, sizeof(dk)) == false) { - putmsg(0, "SCSIHD[%d] ReadSector failed", id); - errmsg = string_format("dk%d Read disklabel failed.", dkunit); - return 0; + if (hd->Peek(&dk, 0, sizeof(dk)) == false) { + putmsg(0, "SCSIHD[%u] ReadSector failed", id); + errmsg = string_format("dk%u Read disklabel failed.", dkunit); + return -1; } // +$1fc の MAGIC をチェック @@ -948,7 +700,7 @@ LunaPROMEmuDevice::LoadByNVRAM(const std putmsg(0, "SCSIHD[%d] Bad magic 0x%04x (!= 0x%x)", id, magic, scd_dk_label::DKL_MAGIC); errmsg = string_format("dk%d Bad disklabel magic.", dkunit); - return 0; + return -1; } // チェックサム照合は手抜きで省略 @@ -957,7 +709,7 @@ LunaPROMEmuDevice::LoadByNVRAM(const std if (loglevel >= 2) { // デバッグ用に全部表示 for (int i = 0; i < 8; i++) { - putmsg("%s partition %c: { start=%8u size=%8u }", __func__, + putmsgn("%s partition %c: { start=%8u size=%8u }", __func__, 'a' + i, be32toh(dk.map[i].blkno), be32toh(dk.map[i].nblk)); @@ -970,520 +722,158 @@ LunaPROMEmuDevice::LoadByNVRAM(const std // ファイルシステムをオープン (マウントっぽいイメージ) Filesys fsys(this); - if (fsys.Open(hd, part_start, part_size) == false) { - putmsg(0, "SCSIHD[%d] %c: %s", id, dkpart + 'a', fsys.errstr.c_str()); - errmsg = string_format("dk%d Open ffs failed.", dkunit); - return 0; - } - - // XXX 以降エラーメッセージ全部同じ - errmsg = string_format("dk%d Read ffs failed.", dkunit); - - std::vector dkfile_paths; - int idx = 0; - inodefile dir(this); - inodefile dkfile(this); - - enum { - STATE_REG, // 通常ファイル (終了条件) - STATE_PATH, // パス解析 - STATE_DIR, // ディレクトリ読み込み - STATE_FIND, // ディレクトリからファイルを検索 - STATE_LINK, // シンボリックリンク - } state; - - // DKFILE の先頭に '/' がなければここで足しておく。 - // ファイル名を分解する際にルートディレクトリマークが必要なので。 - if (dkfile_name[0] != '/') { - dkfile_name.insert(0, 1, '/'); - } - state = STATE_PATH; - - while (state != STATE_REG) { - switch (state) { - case STATE_PATH: - // ファイル名をパスに分解 - // dkfile_name にパス名をセットしてからここに来ること。 - putmsg(1, "%s Path dkfile_name=|%s|", __func__, - dkfile_name.c_str()); - if (dkfile_name.empty()) { - putmsg(0, "SCSIHD[%d] %c: Filename empty?", id, dkpart + 'a'); - return 0; - } - dkfile_paths.clear(); - for (int s = 0; s < dkfile_name.size(); ) { - int p = dkfile_name.find('/', s); - if (p == std::string::npos) { - p = dkfile_name.size(); - } - // '/' が連続していなければパスコンポーネント - if (p != s) { - dkfile_paths.push_back(dkfile_name.substr(s, p - s)); - } - s = p + 1; - } - idx = 0; - if (loglevel >= 1) { - for (int i = 0; i < dkfile_paths.size(); i++) { - putmsg("%s dkfile_path[%d]=|%s|", - __func__, i, dkfile_paths[i].c_str()); - } - } - // パスが '/' から始まっていればルートディレクトリの読み込みから。 - // そうでなければ現在のディレクトリでファイルの検索から。 - if (dkfile_name[0] == '/') { - // ルートディレクトリの inode 番号は伝統的に #2 と - // いうことになっている。 - dir.ino = 2; - state = STATE_DIR; - } else { - state = STATE_FIND; - } - break; - - case STATE_DIR: - // inode 番号からディレクトリエントリを取得。 - // dir.ino をセットしてからここに来ること。 - putmsg(1, "%s Readdir ino=%d", __func__, dir.ino); - if (fsys.Readi(dir) == false) { - putmsg(0, "SCSIHD[%d] %c: %s", - id, dkpart + 'a', fsys.errstr.c_str()); - return 0; - } - // 中身(ディレクトリエントリ) を取得 - if (fsys.ReadData(dir) == false) { - putmsg(0, "SCSIHD[%d] %c: %s", - id, dkpart + 'a', fsys.errstr.c_str()); - return 0; - } - state = STATE_FIND; - break; - - case STATE_FIND: - // ディレクトリエントリ dir.data から - // ファイル名 dkfile_paths[idx] を探す。 - putmsg(1, "%s FindFile %s", __func__, dkfile_paths[idx].c_str()); - dkfile.ino = dir.FindFile(dkfile_paths[idx]); - if (dkfile.ino == 0) { - putmsg(0, "SCSIHD[%d] %c: %s", - id, dkpart + 'a', fsys.errstr.c_str()); - return 0; - } - // ファイルの inode 情報を取得。 - if (fsys.Readi(dkfile) == false) { - putmsg(0, "SCSIHD[%d] %c: %s", - id, dkpart + 'a', fsys.errstr.c_str()); - return 0; - } - switch (be16toh(dkfile.di.di_mode) & IFMT) { - case IFREG: // 通常ファイル - state = STATE_REG; - break; - - case IFLNK: // シンボリックリンク - state = STATE_LINK; - break; - - case IFDIR: // ディレクトリ - dir.ino = dkfile.ino; - idx++; - state = STATE_DIR; - break; - - default: // それ以外は対応してないし来ないはず - putmsg(0, "SCSIHD[%d] %c:%s invalid filemode %06o", - id, dkpart + 'a', dkfile_name.c_str(), - be16toh(dkfile.di.di_mode)); - return 0; - } - break; - - case STATE_LINK: - // リンク先ファイル名を取り出す - putmsg(1, "%s Readlink", __func__); - if (fsys.ReadData(dkfile) == false) { - return 0; - } - dkfile_name = std::string((const char *)dkfile.data.data(), - dkfile.data.size()); - // パス解析へ - state = STATE_PATH; - break; - - default: - __unreachable(); - } + if (fsys.Mount(hd, part_start, part_size) == false) { + errmsg = string_format("dk%u,%c Open ffs failed: %s", + dkunit, dkpart + 'a', fsys.errstr.c_str()); + putmsg(0, "%s: %s", __func__, errmsg.c_str()); + return -1; } - // 通常ファイルに到達 - if (idx != dkfile_paths.size() - 1) { - // パスの最後のコンポーネントじゃなければエラー - putmsg(0, "SCSIHD[%d] %c:%s path not found", - id, dkpart + 'a', dkfile_name.c_str()); - return 0; + // ファイルを探してオープンする + inodefile f(this); + if (fsys.OpenFile(f, dkfile) == false) { + errmsg = string_format("dk%u,%c,%s open failed: %s", + dkunit, dkpart + 'a', dkfile.c_str(), fsys.errstr.c_str()); + putmsg(0, "%s: %s", __func__, errmsg.c_str()); + return -1; } + // ファイル本体を読み込み - if (fsys.ReadData(dkfile) == false) { - putmsg(0, "SCSIHD[%d] %c: %s", id, dkpart + 'a', fsys.errstr.c_str()); - return 0; - } + fsys.ReadData(f); // ロード - uint32 entry; - std::string name = string_format("SCSIHD %d,%c:%s", - id, dkpart + 'a', dkfile_name.c_str()); - entry = gRAM->LoadFile(name.c_str(), dkfile.data.data(), - dkfile.data.size(), AOUT_MID_M68K); - - return entry; -} - -// パーティションを FFS としてオープンする(マウントのようなイメージ)。 -// start, size はこのパーティションの開始セクタとセクタ数。 -bool -Filesys::Open(SCSIHD *hd_, uint32 start, uint32 size) -{ - hd = hd_; - part_start = start; - part_size = size; - - // FFS の先頭にはブートブロックがあり、続けてスーパーブロックがある。 - // ブートブロックはブートローダのための領域で、ここもファイルシステムの - // 内側という認識のようだ。 - // - // ブートブロックの大きさはおそらくファイルシステムごとに固定で、 - // FFSv1(?) なら 8KB のようだ。ここは LUNA-I の ROM が解釈できるのと - // 同じファイルシステムだけ扱えればよいので、8KB 固定とする。 - // 続いてスーパーブロックがある。ここにこのファイルシステムのパラメータ - // が書いてある。 - // - // +-------------+ 0 <- part_start (パーティション先頭) - // | BootBlock | - // +-------------+ 8KB - // | SuperBlock | - // | fs_iblkno ---+ - // +-------------+ | - // : : | - // | - // : : | - // +-------------+ <-+ - // | InodeBlock? | - // - // スーパーブロックの構造は の struct fs 参照。 - - // スーパーブロックを読む - hd->PeekImage(&fs0, part_start * 512 + 8192, sizeof(fs0)); - // 読み込んだスーパーブロックはゲストエンディアン(BE) なので - // 使う前に (もう面倒なのでほぼ全部) ホストエンディアンにしておく。 - // ヘッダに定義してあるマクロをそのまま使いたいからだけど、正気かなこれ。 - fs->fs_firstfield = be32toh(fs->fs_firstfield); - fs->fs_sblkno = be32toh(fs->fs_sblkno); - fs->fs_cblkno = be32toh(fs->fs_cblkno); - fs->fs_iblkno = be32toh(fs->fs_iblkno); - fs->fs_dblkno = be32toh(fs->fs_dblkno); - fs->fs_old_cgoffset = be32toh(fs->fs_old_cgoffset); - fs->fs_old_cgmask = be32toh(fs->fs_old_cgmask); - fs->fs_old_time = be32toh(fs->fs_old_time); - fs->fs_old_size = be32toh(fs->fs_old_size); - fs->fs_old_dsize = be32toh(fs->fs_old_dsize); - fs->fs_ncg = be32toh(fs->fs_ncg); - fs->fs_bsize = be32toh(fs->fs_bsize); - fs->fs_fsize = be32toh(fs->fs_fsize); - fs->fs_frag = be32toh(fs->fs_frag); - fs->fs_minfree = be32toh(fs->fs_minfree); - fs->fs_old_rotdelay = be32toh(fs->fs_old_rotdelay); - fs->fs_old_rps = be32toh(fs->fs_old_rps); - fs->fs_bmask = be32toh(fs->fs_bmask); - fs->fs_fmask = be32toh(fs->fs_fmask); - fs->fs_bshift = be32toh(fs->fs_bshift); - fs->fs_fshift = be32toh(fs->fs_fshift); - fs->fs_maxcontig = be32toh(fs->fs_maxcontig); - fs->fs_maxbpg = be32toh(fs->fs_maxbpg); - fs->fs_fragshift = be32toh(fs->fs_fragshift); - fs->fs_fsbtodb = be32toh(fs->fs_fsbtodb); - fs->fs_sbsize = be32toh(fs->fs_sbsize); - //fs->fs_spare1[2] - fs->fs_nindir = be32toh(fs->fs_nindir); - fs->fs_inopb = be32toh(fs->fs_inopb); - fs->fs_old_nspf = be32toh(fs->fs_old_nspf); - fs->fs_optim = be32toh(fs->fs_optim); - fs->fs_old_npsect = be32toh(fs->fs_old_npsect); - fs->fs_old_interleave = be32toh(fs->fs_old_interleave); - fs->fs_old_trackskew = be32toh(fs->fs_old_trackskew); - fs->fs_id[0] = be32toh(fs->fs_id[0]); - fs->fs_id[1] = be32toh(fs->fs_id[1]); - fs->fs_old_csaddr = be32toh(fs->fs_old_csaddr); - fs->fs_cssize = be32toh(fs->fs_cssize); - fs->fs_cgsize = be32toh(fs->fs_cgsize); - fs->fs_spare2 = be32toh(fs->fs_spare2); - fs->fs_old_nsect = be32toh(fs->fs_old_nsect); - fs->fs_old_spc = be32toh(fs->fs_old_spc); - fs->fs_old_ncyl = be32toh(fs->fs_old_ncyl); - fs->fs_old_cpg = be32toh(fs->fs_old_cpg); - fs->fs_ipg = be32toh(fs->fs_ipg); - fs->fs_fpg = be32toh(fs->fs_fpg); - //struct csum fs_old_cstotal - //u_char fs_fsmnt[MAXMNTLEN] - //u_char fs_volname[MAXVOLLEN] - fs->fs_swuid = be64toh(fs->fs_swuid); - fs->fs_pad = be32toh(fs->fs_pad); - fs->fs_cgrotor = be32toh(fs->fs_cgrotor); - //void *fs_ocsp[NOCSPTRS] - //struct csum *fs_csp - //uint32_t *fs_maxcluster - //u_char *fs_active - fs->fs_old_cpc = be32toh(fs->fs_old_cpc); - fs->fs_maxbsize = be32toh(fs->fs_maxbsize); - fs->fs_journal_flags = be32toh(fs->fs_journal_flags); - //uint64_t fs->fs_journallocs[4] - fs->fs_quota_magic = be32toh(fs->fs_quota_magic); - //uint64_t fs->fs_quotafile[2] - //uint64_t fs->fs_sparecon64[9] - fs->fs_sblockloc = be64toh(fs->fs_sblockloc); - //struct csum_total fs_cstotal - fs->fs_time = be64toh(fs->fs_time); - fs->fs_size = be64toh(fs->fs_size); - fs->fs_dsize = be64toh(fs->fs_dsize); - fs->fs_csaddr = be64toh(fs->fs_csaddr); - fs->fs_pendingblocks = be64toh(fs->fs_pendingblocks); - fs->fs_pendinginodes = be32toh(fs->fs_pendinginodes); - // uint32_t fs->fs_snapinum[FSMAXSNAP] - fs->fs_avgfilesize = be32toh(fs->fs_avgfilesize); - fs->fs_avgfpdir = be32toh(fs->fs_avgfpdir); - fs->fs_save_cgsize = be32toh(fs->fs_save_cgsize); - // uint32_t fs->fs_sparecon32[26] - fs->fs_flags = be32toh(fs->fs_flags); - fs->fs_contigsumsize = be32toh(fs->fs_contigsumsize); - fs->fs_maxsymlinklen = be32toh(fs->fs_maxsymlinklen); - fs->fs_old_inodefmt = be32toh(fs->fs_old_inodefmt); - fs->fs_maxfilesize = be64toh(fs->fs_maxfilesize); - fs->fs_qbmask = be64toh(fs->fs_qbmask); - fs->fs_qfmask = be64toh(fs->fs_qfmask); - fs->fs_state = be32toh(fs->fs_state); - fs->fs_old_postblformat = be32toh(fs->fs_old_postblformat); - fs->fs_old_nrpos = be32toh(fs->fs_old_nrpos); - // uint32_t fs->fs_spare5[2] - fs->fs_magic = be32toh(fs->fs_magic); - - // マジック - if (fs->fs_magic != FS_UFS1_MAGIC) { - errstr = string_format("Bad superblock magic 0x%08x (!= 0x%08x)", - fs->fs_magic, FS_UFS1_MAGIC); - return false; - } - - // なんとなく主要なパラメータを表示してみるか - if (parent->loglevel >= 2) { - uint32 sblkno = fs->fs_sblkno; // super block in filesys - uint32 cblkno = fs->fs_cblkno; // cylinder group block in filesys - uint32 iblkno = fs->fs_iblkno; // inode block in filesys - uint32 dblkno = fs->fs_dblkno; // data block in filesys - uint32 bsize = fs->fs_bsize; // size of basic blocks - uint32 ipg = fs->fs_ipg; // inodes per group - uint32 fpg = fs->fs_fpg; // blocks per group x fs_frag - uint32 old_cgoffset = fs->fs_old_cgoffset; // cg. offset in cyl. - - parent->putmsg("%s fs.fs_sblkno=0x%x (0x%x)", - __func__, sblkno, sblkno * 512); - parent->putmsg("%s fs.fs_cblkno=0x%x (0x%x)", - __func__, cblkno, cblkno * 512); - parent->putmsg("%s fs.fs_dblkno=0x%x (0x%x)", - __func__, dblkno, dblkno * 512); - parent->putmsg("%s fs.fs_iblkno=0x%x (0x%x)", - __func__, iblkno, iblkno * 512); - parent->putmsg("%s fs.fs_bsize=0x%x", __func__, bsize); - parent->putmsg("%s fs.fs_ipg=0x%x", __func__, ipg); - parent->putmsg("%s fs.fs_fpg=0x%x", __func__, fpg); - parent->putmsg("%s fs.fs_old_cgoffset=0x%x", __func__, old_cgoffset); + std::string name = string_format("SCSIHD %u,%c:%s", + id, dkpart + 'a', dkfile.c_str()); + LoadInfo info(name.c_str(), f.data.data(), f.data.size()); + if (BootLoader::LoadExec(mainram, &info) == false) { + errmsg = string_format("dk%u,%c,%s load failed", + dkunit, dkpart + 'a', dkfile.c_str()); + putmsg(0, "%s: %s", __func__, errmsg.c_str()); + return -1; + } + + if (info.entry == -1) { + errmsg = string_format("dk%u,%c,%s not executable", + dkunit, dkpart + 'a', dkfile.c_str()); + putmsg(0, "%s: %s", __func__, errmsg.c_str()); + return -1; } - return true; -} - -// inode.ino で指定された disk inode 情報を inode.di に返す -bool -Filesys::Readi(inodefile& inode) -{ - // di は inode のディスク上の記録構造で 128 バイト長。 - // 詳細は の ufs1_dinode 参照。 - // 基本的には inode ブロックにこの dinode が配列のように並んでいるが - // シリンダグループを越えるためには謎のマクロを使う。 - // sys/lib/libsa/ufs.c 参照。 - // - // dinode のうち、ここで関係するかも知れないのは - // di_mode (のうちファイルモード) - // di_size (ファイルサイズ) - // di_db[] (直接ブロック) - // di_ib[] (間接ブロック) - // di_blocks (ブロック数) - // くらいか。 - - uint32 ino = inode.ino; - struct ufs1_dinode *di = &inode.di; - - // ino が含まれるブロックを読み込む - char buf[fs->fs_bsize]; - uint32 ino_sector = FFS_FSBTODB(fs, ino_to_fsba(fs, ino)); - hd->PeekImage(buf, part_start + ino_sector * 512, sizeof(buf)); - - // ino で示される inode を取得 - *di = ((ufs1_dinode *)buf)[ino_to_fsbo(fs, ino)]; - uint16 di_mode = be16toh(di->di_mode); - uint16 di_nlink = be16toh(di->di_nlink); - uint64 di_size = be64toh(di->di_size); - parent->putmsg(2, "%s di[%d].di_mode=%05o di_nlink=%d di_size=%" PRIu64, - __func__, ino, di_mode, di_nlink, di_size); - - return true; + return info.entry; } -// inode.di で指定されたファイル(またはディレクトリ)の本文を -// inode.data に返す -bool -Filesys::ReadData(inodefile& inode) +void +LunaPROMEmuDevice::ROM_AP1() { - // di (ufs1_dinode) にはこの inode (ファイルまたはディレクトリ)の - // データの格納ブロックが記録されている。ディレクトリとは中身が - // ディレクトリエントリであるファイルと同じ構造のもの。 - // di_db[] (直接ブロック) - // di_ib[] (間接ブロック) - // di_blocks (実際に使用しているブロック数) - // - // 直接ブロック di_db[] は12要素の配列で、データブロックの位置が - // 記録されている (未使用なら 0 のようだ)。 - // データブロックは fs->fs_bsize でたぶん基本 4KB のやつ。 - // これもパーティション先頭からのセクタ番号で表記される。なので - // ディスク上で連続しているファイルなら di_db[N] と di_db[N + 1] は - // 8 ずつ増えてるのが見えるはず。 - // 例えば以下のサンプル断片は、直接ブロック2つでセクタ 100 から - // セクタ 115 までの連続する 16KB を所有している様子を示している。 - // - // #100 #101 #102 #107 - // +------+------+------+- .. -+------+ - // di_db[0] = 100 ---> | Data | Data | Data | | Data | - // +------+------+------+- .. -+------+ - // - // #108 #115 - // +------+------+------+- .. -+------+ - // di_db[1] = 108 ---> | Data | Data | Data | | Data | - // +------+------+------+- .. -+------+ - // - // 間接ブロック di_ib[0], [1], [2] はそれぞれ一次、二次、三次の - // 間接記録になっている。一次間接だとこういう感じ。 - // - // #120 - // +--------+ +------+- .. - // di_ib[0] = 120 ---> | #200 ------> #200 | Data | - // | | +------+- .. - // | #208 ------> #208 | Data | - // +--------+ +------+- .. - // - - uint32 ino = inode.ino; - struct ufs1_dinode *di = &inode.di; - std::vector& data = inode.data; - uint64 bsize64 = (uint64)fs->fs_bsize; - - uint64 di_size = be64toh(di->di_size); - uint32 di_blocks = be32toh(di->di_blocks); - // di->di_db[] にファイル(またはディレクトリ)の内容の位置が並んでいる - if (parent->loglevel >= 2) { - parent->putmsg("%s di[%d].di_blocks=%u", __func__, ino, di_blocks); - - std::string dbstr; - for (int i = 0; i < UFS_NDADDR; i++) { - dbstr += string_format(" %u", be32toh(di->di_db[i])); - } - parent->putmsg("%s di[%d].di_db[]=%s", - __func__, ino, dbstr.c_str() + 1); - - std::string distr; - for (int i = 0; i < UFS_NIADDR; i++) { - distr += string_format(" %u", be32toh(di->di_ib[i])); - } - parent->putmsg("%s di[%d].di_ib[]=%s", - __func__, ino, distr.c_str() + 1); - } - - uint64 offset = 0; // 次に読み込む位置 (ここまでに読み込んだバイト数) + char buf[100]; + int n; - // サイズが分かっているのであらかじめ確保しておく - data.resize(di_size); + cursor_on = false; - // 直接ブロック - for (int i = 0; i < countof(di->di_db); i++) { - uint64 remain = di_size - offset; // 残りバイト数 - uint32 nbytes = std::min(remain, bsize64); // 今回読み込み分 - hd->PeekImage(&data[offset], be32toh(di->di_db[i]) * 512, nbytes); - offset += nbytes; - if (offset >= di_size) - break; - } - // 一次間接ブロック - uint32 ib0buf[fs->fs_bsize / sizeof(uint32)]; - hd->PeekImage(ib0buf, be32toh(di->di_ib[0]) * 512, sizeof(ib0buf)); - if (parent->loglevel >= 2) { - std::string distr; - for (int i = 0; i < countof(ib0buf); i++) { - if (ib0buf[i] == 0) - break; - distr += string_format(" %u", be32toh(ib0buf[i])); - } - parent->putmsg("%s di[%d].di_ib[0]=%s", - __func__, ino, distr.c_str() + 1); - } - for (int i = 0; i < countof(ib0buf); i++) { - uint64 remain = di_size - offset; // 残りバイト数 - uint32 nbytes = std::min(remain, bsize64); // 今回読み込み分 - hd->PeekImage(&data[offset], be32toh(ib0buf[i]) * 512, nbytes); - offset += nbytes; - if (offset >= di_size) - break; - } - - // 二次、三次ブロックは未対応 - if (offset < di_size) { - PANIC("di_ib[1] not supported"); + static uint8 ap1pal[] = { + 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xf8, + 0xf8, 0xf8, 0x00, + 0xf8, 0xf8, 0xf8, + }; + bt45x->WritePort(0, 0); + for (int i = 0; i < countof(ap1pal); i++) { + bt45x->WritePort(1, ap1pal[i]); + } + + Print(std::string((const char *)test0)); + if (gMainApp.IsLUNA88K()) { + Print(std::string((const char *)test1)); + } else { + Print(std::string((const char *)test2)); } + Print(std::string((const char *)test3)); - return true; + n = strlcpy(buf, (const char *)&Builtin::IPLROM30[0x111e3], sizeof(buf)); + snprintf(buf + n, sizeof(buf) - n, "%d.%d.%d %s", + NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE); + for (int i = 0; buf[i]; i++) { + if (buf[i] == '/') + buf[i] = '.'; + } + Print("%s\n", buf); + + memcpy(buf, &Builtin::IPLROM30[0x111f5], 0x28); + memcpy(buf + 0x1e, mpu->GetMPUName(), 7); + n = 0x27; + n += snprintf(buf + n, sizeof(buf) - n, "%4.1f", + (double)mpu->GetClockSpeed() / 1000); + strlcpy(buf + n, (const char *)&Builtin::IPLROM30[0x1121f], + sizeof(buf) - n); + Print(std::string(buf)); + + strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11226], sizeof(buf)); + if (gMainApp.Has(VMCap::M88K)) { + memcpy(buf + 0x1e, mpu->GetMPUName(), 7); + memcpy(buf + 0x25, &Builtin::IPLROM30[0x11251], 3); + } + Print(std::string(buf)); + + strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11254], sizeof(buf)); + buf[0x0c] = 'e'; + if (gMainApp.Has(VMCap::M88K)) { + memcpy(buf + 0x1e, mpu->GetMPUName(), 7); + memcpy(buf + 0x25, &Builtin::IPLROM30[0x1127d], 3); + buf[0x22] = 0x32; + } + Print(std::string(buf)); + + strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11280], sizeof(buf)); + n = 0x1e; + n += snprintf(buf + n, sizeof(buf) - n, "%u", mainram->GetSizeMB()); + strlcpy(buf + n, (const char *)&Builtin::IPLROM30[0x1129f], + sizeof(buf) - n); + Print(std::string(buf)); } -// ディレクトリエントリ dirbuf から name で指定されるファイル名に対応する -// inode 番号を返す。見付からなければ 0 を返す。 -uint32 -inodefile::FindFile(const std::string& name) +void +LunaPROMEmuDevice::ROM_AP2() { - // ディレクトリエントリは可変長で並んでいる。 - // d->d_reclen がこの1つ分のエントリの長さなので、次のエントリ位置は - // このエントリの頭から d->d_reclen を足したところになる。 - // 最終エントリの d_reclen を足すとバッファ末尾になるようになっている - // ようだ。 - bool found = false; - const char *dp = (const char *)(data.data()); - const char *dend = dp + data.size(); - uint32 file_ino; - uint16 reclen; - - for (; dp < dend; dp += reclen) { - const struct direct *d = (const struct direct *)dp; - file_ino = be32toh(d->d_fileno); - reclen = be16toh(d->d_reclen); - - std::string str = string_format("ino=%u reclen=%u name=|%s|", - file_ino, reclen, d->d_name); - if (strcmp(name.c_str(), d->d_name) == 0) { - found = true; - parent->putmsg(2, "%s %s found", __func__, str.c_str()); - break; - } else { - parent->putmsg(2, "%s %s skip", __func__, str.c_str()); - } - } - if (found == false) { - parent->putmsg(2, "%s %s not found", __func__, name.c_str()); - return 0; - } - - return file_ino; + InitPalette(); + InitScreen(); } + +// キーコードから文字への変換 +/*static*/ const uint8 +LunaPROMEmuDevice::lunakey2asciitable[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, BS, LF, 0, ' ', 0, 0, 0, + 0, 0, 0, 0, UP,LEFT,RIGHT,DOWN, + 0, 0, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '^', '\\', 0, + 0, 0, 'q', 'w', 'e', 'r', 't', 'y', + 'u', 'i', 'o', 'p', '@', '[', 0, 0, + 0, 0, 'a', 's', 'd', 'f', 'g', 'h', + 'j', 'k', 'l', ';', ':', ']', 0, 0, + 0, 0, 'z', 'x', 'c', 'v', 'b', 'n', + 'm', ',', '.', '/', 0, 0, 0, 0, + 0, '+', '-', '7', '8', '9', '4', '5', + '6', '1', '2', '3', '0', '.', LF, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, '*', '/', '=', ',', +}; +/*static*/ const uint8 +LunaPROMEmuDevice::lunakey2shifttable[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, BS, LF, 0, ' ', 0, 0, 0, + 0, 0, 0, 0, UP,LEFT,RIGHT,DOWN, + 0, 0, '!', '\"','#', '$', '%', '&', + '\'','(', ')', ' ', '=', '~', '|', 0, // XXX SHIFT+'0'は? + 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', + 'U', 'I', 'O', 'P', '`', '{', 0, 0, + 0, 0, 'A', 'S', 'D', 'F', 'G', 'H', + 'J', 'K', 'L', '+', '*', '}', 0, 0, + 0, 0, 'Z', 'X', 'C', 'V', 'B', 'N', + 'M', '<', '>', '?', '_', 0, 0, 0, + 0, '+', '-', '7', '8', '9', '4', '5', + '6', '1', '2', '3', '0', '.', LF, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, '*', '/', '=', ',', +};