--- nono/vm/romemu_luna.cpp 2026/04/29 17:04:32 1.1 +++ nono/vm/romemu_luna.cpp 2026/04/29 17:04:53 1.1.1.8 @@ -1,43 +1,42 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "aout.h" +// LUNA シリーズの ROM エミュレーション共通部分 + +// IODevice +// | +// +----------+---------------------+ +// v v +// ROMDevice LunaPROMEmuDeivce (ROM エミュレーションの共通部分) +// | | +// | +------------+----------------+ +// v v v +// PROMDevice Luna1PROMEmuDevice Luna88kPROMEmuDevice +// (実ROM) (LUNA-I エミュレーション) (LUNA88K エミュレーション) + +#include "romemu_luna.h" #include "bitmap.h" #include "bt454.h" -#include "ffs_dinode.h" -#include "ffs_dir.h" -#include "ffs_fs.h" #include "mainapp.h" -#include "memorystream.h" #include "mk48t02.h" -#include "pio.h" -#include "romemu_luna.h" #include "ram.h" #include "sio.h" #include "spc.h" - -// ROM エミュレーションの状態遷移 -enum { - STATE_CLOSED = 0, // バックドアは塞がれている - STATE_RESET, // リセット後、アクセス前 - STATE_INIT, // プロンプト初期化中 - STATE_PROMPT, // プロンプト実行中 -}; +#include "ufs.h" // コンストラクタ LunaPROMEmuDevice::LunaPROMEmuDevice() { - mem = NULL; + logname = "prom"; + devname = "PROM"; } // デストラクタ LunaPROMEmuDevice::~LunaPROMEmuDevice() { - if (mem) { - delete[] mem; - } } bool @@ -46,876 +45,300 @@ 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 割り込み - return true; } void LunaPROMEmuDevice::ResetHard() { - ipl_state = STATE_RESET; + is_closed = false; + + // ブートページを ROM に切り替える。 + isrom = true; + gVM->SwitchBootPage(this, true); } // アドレスデコーダ inline uint64 LunaPROMEmuDevice::Decoder(uint32 addr) const { + // 折り返しなし return addr - baseaddr; } uint64 -LunaPROMEmuDevice::Read32(uint32 addr) +LunaPROMEmuDevice::Read8(uint32 addr) { uint64 offset = Decoder(addr); - // ロングワードアクセスでだけ謎の I/O 空間が見える - if (ipl_state != STATE_CLOSED) { - switch (offset) { - case ROMIO_IPL_INIT - baseaddr: - return IPL_Init(); - - case ROMIO_IPL_PROMPT - baseaddr: - return IPL_Prompt(); - - case ROMIO_IPL_CLOSE - baseaddr: - return IPL_Close(); - - default: - break; - } - } - return inherited::Read32(addr); + // 謎の IO 空間はロングワードアクセスのみ + return romdata[offset & (devlen - 1)]; } -// 起動時の処理を行う。 -// 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。 -// そうでなければ 0 を返すので、プロンプトに処理を移すこと。 -uint32 -LunaPROMEmuDevice::IPL_Init() +uint64 +LunaPROMEmuDevice::Read16(uint32 addr) { - MemoryStreamBE ms; - uint32 entry; - - ipl_state = STATE_INIT; - entrypoint = 0; + uint64 offset = Decoder(addr); - // 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); + // 謎の IO 空間はロングワードアクセスのみ + return be16toh(*(uint16 *)&romdata[offset & (devlen - 1)]); +} - // 初期パレット - 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 +uint64 +LunaPROMEmuDevice::Read32(uint32 addr) +{ + uint64 offset = Decoder(addr); - // SCSI - // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。 - gSPC->Write8(SPC::BDID, 7); + // ロングワードアクセスでだけ謎の I/O 空間が見える + if (!is_closed) { + switch (offset) { + case ROMIO_INIT - baseaddr: + return ROM_Init(); - // スクリーンを初期化 - text.Init(80, 32); - text.foldmode = true; - gBitmap->EmuConsoleOpen(&text); + case ROMIO_KEYIN - baseaddr: + ROM_Keyin(); + return 0; - text.Print(0, 0, "NONO %d.%d.%d Emulated ROM Monitor", - NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER); - text.Locate(0, 2); + case ROMIO_CLOSE - baseaddr: + ROM_Close(); + return 0; - // 表示環境が整ったところで NVRAM を照合 - if (VerifyNVRAM() == false) { - InitNVRAM(); - text.Print("** NVRAM Initialized.\n"); - text.Print("\n"); - return 0; - } + case ROMIO_ENTRY - baseaddr: + return ROM_Entry(); - // 実際の画面更新は 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()); - } + default: + break; } } - - return entry; + return be32toh(*(uint32 *)&romdata[offset & (devlen - 1)]); } -// ROM 処理のクローズ。 -// 次段プログラムにジャンプするための処理も含む。 -uint32 -LunaPROMEmuDevice::IPL_Close() +uint64 +LunaPROMEmuDevice::Write8(uint32 addr, uint32 data) { - // ここでバックドアに蓋をする - ipl_state = STATE_CLOSED; - - // コンソールクローズ - gBitmap->EmuConsoleClose(); - - // 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() +uint64 +LunaPROMEmuDevice::Write16(uint32 addr, uint32 data) { - 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; - - ipl_state = STATE_PROMPT; - prompt_idx = 0; - prompt = default_prompt; - break; - - case STATE_PROMPT: - // キー入力があった - - uint32 lunakey = gSIO->Read8(0x51000004); - - // SHIFT キーだけ状態を持つので先に処理 - if (lunakey == 0x0c || lunakey == 0x0d) { // 左右SHIFT押下 - is_shift = true; - return 0; - } - if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放 - is_shift = false; - return 0; - } - // 後は最上位ビットが立ってたらキー開放かマウスなので無視 - if ((lunakey & 0x80)) { - return 0; - } - - 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]; - } - if (asciicode == 0) { - // 割り当てなし - return 0; - } - inputbuf.insert(inputbuf.begin() + inputpos, asciicode); - inputpos++; - break; - } - } - putmsg(2, "inputbuf=%zd|%s|", inputbuf.length(), inputbuf.c_str()); - break; - } + return 0; +} - // 入力行全体を都度更新 (面倒なので行をはみ出さないようにする) - 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(); +uint64 +LunaPROMEmuDevice::Write32(uint32 addr, uint32 data) +{ return 0; } -// 入力コマンド処理 -uint32 -LunaPROMEmuDevice::IPL_Command() +uint64 +LunaPROMEmuDevice::Peek8(uint32 addr) { - char buf[inputbuf.length() + 1]; - std::vector arg; + uint64 offset = Decoder(addr); - // 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()); - } - } + return romdata[offset & (devlen - 1)]; +} - // "k" コマンド中ならここで先に処理する - if (prompt_idx != 0) { - IPL_CommandK(arg); - return 0; +// デバイスの初期化の共通部分。(継承クラスの ROM_Init() から呼ぶ) +void +LunaPROMEmuDevice::InitDevice() +{ + // ブートページを RAM に切り替える。 + if (isrom) { + isrom = false; + gVM->SwitchBootPage(this, false); } - // 空行 - if (arg.size() == 0) { - return 0; + // 初期パレット + // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。 + uint8 val = 0xf0; + gBT454->Write(0, 0); // select palette + for (int i = 0; i < 16; i++) { + gBT454->Write(1, val); // R + gBT454->Write(1, val); // G + gBT454->Write(1, val); // B + val ^= 0xf0; } - // 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(); - } + // SCSI + // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。 + gSPC->Write(SPC::BDID, 7); - text.Print("** Unknown command: %s\n", arg[0].c_str()); - return 0; + // キーボード(chB)を初期化 + gSIO->Write(3, 0x01); // CR1 + gSIO->Write(3, 0x10); // RX Int(all char) + gSIO->Write(3, 0x03); // CR3 + gSIO->Write(3, 0xc1); // RX Enable, 8bit + gSIO->Write(3, 0x04); // CR4 + gSIO->Write(3, 0x44); // Stop 1bit + gSIO->Write(3, 0x05); // CR5 + gSIO->Write(3, 0x68); // TX Enable, 8bit } -// "g" コマンド -// g なら NVRAM の情報に従ってプログラムをロード。 -// g なら NVRAM の fname だけ指定の名前を使ってロード。NVRAM には -// 書き込まない。 +// スクリーンを初期化 void -LunaPROMEmuDevice::IPL_CommandG(const std::vector& arg) +LunaPROMEmuDevice::InitScreen(const char *machine_name) { - 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()); - } - - } 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; - } + text.Init(80, 32); + gBitmap->EmuConsoleOpen(&text); + inputbuf.clear(); + inputpos = 0; + is_shift = false; + mousecnt = 0; - if (entrypoint) { - text.Print("Loaded. Entry point = $%08x\n", entrypoint); - } + text.Print(0, 0, "NONO %d.%d.%d Emulated ROM Monitor for %s", + NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, machine_name); + text.Locate(0, 2); } -// "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"); - } -} + // ここでバックドアに蓋をする + is_closed = true; -// -A オプションで指定されたホストファイルをロードする。 -// ロードできればエントリポイントを返す。 -// host_file が指定されている時に呼ぶこと。 -uint32 -LunaPROMEmuDevice::LoadHostFile() -{ - assert(gMainApp.host_file); + // SIO からキーの Break 入力を取り除いておく。 + // コマンド入力によってここへ来る時は、キーの Make で呼ばれるため + // Break はまだ取り出されておらず SIO のバッファに残っている。 + // コマンド入力によらずここに来た場合でも空読みして問題ないだろう。 + (void)gSIO->Read(2); - 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"); - } - return entry; + // コンソールクローズ + gBitmap->EmuConsoleClose(); } - -// 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" コマンド +// ROM プロンプトのキー入力。 +// ゲストコードの割り込みハンドラから呼ばれて、キー入力で行を構成し、 +// あるいは行が完成すればコマンドを実行する。 +// 実行結果により画面を更新したりメンバ変数を更新する。 void -LunaPROMEmuDevice::IPL_CommandK(const std::vector& arg) +LunaPROMEmuDevice::ROM_Keyin() { - 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; - } - - // これ以降は更新後の prompt_idx を使う - - // 入力エラーがあった時はこの行を再入力するため - next = prompt_idx; + uint32 lunakey = gSIO->Read(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; } - // この行のプロンプト文字列を作成 - prompt_idx = next; - if (prompt_idx == 0) { - // 最終行なら元に戻す - prompt = default_prompt; - } 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()); + // SHIFT キーだけ状態を持つので先に処理 + if (lunakey == 0x0c || lunakey == 0x0d) { // 左右SHIFT押下 + is_shift = true; + return; + } + if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放 + is_shift = false; + return; + } + // マウスデータの1バイト目なら、後続2バイトをスキップ + if ((lunakey & 0xf8) == 0x80) { + mousecnt = 2; + return; + } + // キー開放は無視してよい + if ((lunakey & 0x80)) { + return; } -} - -// k コマンドの入力値を処理する部分。 -// 成功すれば true を返す。 -// 失敗すれば false を返す。 -// 実機 ROM は入力値にエラーがあると黙って再入力を促す。 -// エラーメッセージくらい表示してもいいかもだがとりあえず真似しておく。 -bool -LunaPROMEmuDevice::CommandKInput(const std::string& val) -{ - 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; + switch (lunakey) { + case 0x11: // BS + if (inputpos > 0) { + inputbuf.erase(--inputpos); } break; - - case 7: // ETBOOT + case 0x12: // Enter { - // こいつだけ ETBOOT:ETFILE 形式で2つのパラメータを同時に処理する - - // ':' で前後に分ける - int sep = val.find(':'); - if (sep == std::string::npos) { - return false; + // 現在位置のカーソルを消去 + int curx = prompt.length() + inputpos; + // TA::On かそうでないかしか使っていないので + // とりあえずこうしておく + text.Locate(curx, text.GetY()); + text.SetAttr(TA::Normal); + // 改行 + text.Putc('\n'); + // コマンド処理。 + Command(); + inputbuf.clear(); + inputpos = 0; + break; + } + case 0x1d: // ← + if (inputpos > 0) { + inputpos--; } - 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; + break; + case 0x1e: // → + if (inputpos < inputbuf.length()) { + inputpos++; } - - // ETBOOT - rv = nvram->WriteString(offset, etboot); - if (rv == false) { - return false; + break; + default: // 通常文字 + { + char asciicode; + if (is_shift) { + asciicode = lunakey2shifttable[lunakey]; + } else { + asciicode = lunakey2asciitable[lunakey]; } - - // 次へ - prompt_idx = kinfo[prompt_idx].next; - - // ETFILE - offset = kinfo[prompt_idx].offset; - rv = nvram->WriteString(offset, etfile); - if (rv == false) { - return false; + if (asciicode == 0) { + // 割り当てなし + return; } + inputbuf.insert(inputbuf.begin() + inputpos, asciicode); + inputpos++; break; } - - default: - rv = nvram->WriteString(offset, val); - if (rv == false) { - return false; - } - break; } - - WriteNVRAMCsum(); - return rv; + putmsg(2, "inputbuf=%zd|%s|", inputbuf.length(), inputbuf.c_str()); + ConsoleUpdate(); } -// "x" コマンド -// すでに読み込まれてるはずのエントリポイントにジャンプするだけ。 -uint32 -LunaPROMEmuDevice::IPL_CommandX() -{ - if (entrypoint == 0) { - text.Print("** Program is not loaded.\n"); - } - return entrypoint; -} - -// NVRAM のチェックサムを返す -uint8 -LunaPROMEmuDevice::CalcNVRAMCsum() -{ - uint8 eor = 0; - for (uint32 addr = 0x20; addr < 0x560; addr++) { - eor ^= nvram->Peek(addr); - } - return eor; -} - -// NVRAM のチェックサムを計算して書き込む +// 画面を更新 void -LunaPROMEmuDevice::WriteNVRAMCsum() +LunaPROMEmuDevice::ConsoleUpdate() { - uint8 eor = CalcNVRAMCsum(); - - nvram->Write(0x001c, eor); - nvram->Write(0x001d, eor); - nvram->Write(0x001e, eor); + // 入力行全体を都度更新 (面倒なので行をはみ出さないようにする) + std::string buf = prompt + inputbuf; + text.Print(0, text.GetY(), "%-79s", buf.c_str()); + // カーソルを描画 + int curx = prompt.length() + inputpos; + // TA::On かそうでないかしか使っていないので + // とりあえずこうしておく + text.Locate(curx, text.GetY()); + text.SetAttr(TA::On); + // 画面更新 + gBitmap->EmuConsoleUpdate(); } -// NVRAM のマジックとチェックサムを照合する -bool -LunaPROMEmuDevice::VerifyNVRAM() +// -A オプションで指定されたホストファイルをロードする。 +// ロードできればエントリポイントを返す。 +// host_file が指定されている時に呼ぶこと。 +uint32 +LunaPROMEmuDevice::LoadHostFile() { - if (nvram->PeekString(0x0004) != "") { - return false; - } + assert(gMainApp.host_file); - uint8 eor = CalcNVRAMCsum(); - for (uint32 addr = 0x1c; addr < 0x1f; addr++) { - if (nvram->Peek(addr) != eor) { - return false; - } + uint32 entry = gRAM->LoadFile(gMainApp.host_file); + if (entry) { + text.Print("Host program loaded. Entry point = $%08x\n", + entry); + } else { + text.Print("** Couldn't load specified host program.\n"); } - 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; + return entry; } -// キーコードから文字への変換 -/*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 -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, -}; - - -// -// 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_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; // こっちが実体 -}; - -// NVRAM の設定に従って起動先を探す。 -// fname が指定されればそれをファイル名に使う。empty なら NVRAM の DKFILE -// をファイル名に使う。 +// bootinfo に従ってゲストから起動先をロードする。 // 成功すればエントリポイントを返す。 // 失敗ならをエラーメッセージを errmsg にセットして 0 を返す。 uint32 -LunaPROMEmuDevice::LoadByNVRAM(const std::string& fname) +LunaPROMEmuDevice::LoadGuestFile(const bootinfo_t& bootinfo) { - // 起動先情報を 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; - } else { - dkfile_name = nvram->PeekString(0x00d0); - } - 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()); + const int& dkpart = bootinfo.dkpart; + const int& dkunit = bootinfo.dkunit; + const std::string& dkfile = bootinfo.dkfile; // ユニット (今は SCSI ID=6 決め打ち) int id = 6; @@ -925,7 +348,7 @@ LunaPROMEmuDevice::LoadByNVRAM(const std putmsg(0, "%s", errmsg.c_str()); return 0; } - if (target->GetDevType() != SCSI::HD) { + if (target->GetDevType() != SCSI::DevType::HD) { errmsg = "No bootable disks"; putmsg(0, "%s", errmsg.c_str()); return 0; @@ -957,7 +380,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 +393,74 @@ 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); + if (fsys.Mount(hd, part_start, part_size) == false) { + errmsg = string_format("dk%d,%c Open ffs failed: %s", + dkunit, dkpart + 'a', fsys.errstr.c_str()); + putmsg(0, "%s: %s", __func__, errmsg.c_str()); 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 (idx != dkfile_paths.size() - 1) { - // パスの最後のコンポーネントじゃなければエラー - putmsg(0, "SCSIHD[%d] %c:%s path not found", - id, dkpart + 'a', dkfile_name.c_str()); + // ファイルを探してオープンする + inodefile f(this); + if (fsys.OpenFile(f, dkfile) == false) { + errmsg = string_format("dk%d,%c,%s open failed: %s", + dkunit, dkpart + 'a', dkfile.c_str(), fsys.errstr.c_str()); + putmsg(0, "%s: %s", __func__, errmsg.c_str()); return 0; } + // ファイル本体を読み込み - 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; + id, dkpart + 'a', dkfile.c_str()); + entry = gRAM->LoadFile(name.c_str(), f.data.data(), f.data.size()); + if (entry == 0) { + errmsg = string_format("dk%d,%c,%s not executable", + dkunit, dkpart + 'a', dkfile.c_str()); } - // なんとなく主要なパラメータを表示してみるか - 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); - } - - 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; -} - -// inode.di で指定されたファイル(またはディレクトリ)の本文を -// inode.data に返す -bool -Filesys::ReadData(inodefile& inode) -{ - // 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; // 次に読み込む位置 (ここまでに読み込んだバイト数) - - // サイズが分かっているのであらかじめ確保しておく - data.resize(di_size); - - // 直接ブロック - 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"); - } - - return true; + return entry; } -// ディレクトリエントリ dirbuf から name で指定されるファイル名に対応する -// inode 番号を返す。見付からなければ 0 を返す。 -uint32 -inodefile::FindFile(const std::string& name) -{ - // ディレクトリエントリは可変長で並んでいる。 - // 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; -} +// キーコードから文字への変換 +/*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 +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, '!', '\"', '#', '$', '%', '&', + '\'', '(', ')', ' ', '=', '~', '|', 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, 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, +};