--- nono/vm/romemu_luna88k.cpp 2026/04/29 17:04:53 1.1.1.6 +++ nono/vm/romemu_luna88k.cpp 2026/04/29 17:04:55 1.1.1.7 @@ -18,10 +18,11 @@ // (実ROM) (LUNA-I エミュレーション) (LUNA88K エミュレーション) #include "romemu_luna88k.h" -#include "bitmap.h" #include "iodevstream.h" +#include "lunafb.h" #include "mainapp.h" #include "memorystream.h" +#include "mk48t02.h" #include "pio.h" #include "ram.h" #include "sysctlr.h" @@ -37,9 +38,7 @@ Luna88kPROMEmuDevice::Luna88kPROMEmuDevi romdata.reset(new uint8 [devlen]); // プロンプトを初期化。 - // 本物は "O>" というプロンプトでよく分からんけど、こっちは nono の - // 頭文字ということにしておく。 - prompt = "N>"; + prompt = default_prompt; } // デストラクタ @@ -183,6 +182,7 @@ Luna88kPROMEmuDevice::ROM_Init() // 表示環境が整ったところで NVRAM を照合 if (VerifyNVRAM() == false) { + // PROM 1.20 は初期化するかどうか確認してくるけど、とりあえず。 InitNVRAM(); text.Print("** NVRAM Initialized.\n"); text.Print("\n"); @@ -218,6 +218,7 @@ Luna88kPROMEmuDevice::ROM_Init() // ConsoleUpdate を呼ぶことでタイトルを含めて画面を表示する。 done: if (entry == 0) { + ClearPrompt(); ConsoleUpdate(); } @@ -235,6 +236,45 @@ Luna88kPROMEmuDevice::ROM_Entry() } } +void +Luna88kPROMEmuDevice::GetChar(char asciicode) +{ + if (prompt_idx == 0) { + // 行入力モード (画面更新もしてある) + inherited::GetChar(asciicode); + } else { + // 0 以外ならノンブロッキングモード + + // 入力されたキーを表示 + text.SetAttr(TA::Normal); + if (' ' <= asciicode && asciicode < 0x7f) { + text.Putc(asciicode); + } + + switch (prompt_idx) { + case PROMPT_NVRAM_UPDATE: + CommandNvramUpdate2(asciicode); + break; + + case PROMPT_NVRAM_NEW: + CommandNvramNew2(asciicode); + break; + + case PROMPT_NVRAM_INIT: + CommandNvramInit2(asciicode); + break; + } + + // プロンプトを戻す + prompt = default_prompt; + prompt_idx = 0; + prompt_y = text.GetY(); + + // こっちは画面更新をする必要がある + ConsoleUpdate(); + } +} + // 入力コマンド処理 (入力行が揃ったところで呼ばれる)。 // // b 現在のパラメータで起動 @@ -272,31 +312,54 @@ Luna88kPROMEmuDevice::Command() // 1ワード目がコマンド if (arg[0] == "h") { - // ヘルプメッセージ - text.Print( -" *** Help Message ***\n" -" b : load and execute program\n" -" h : show this message\n" -#if 0 -" nvram : show/set nvram parameters\n" -#endif - ); + CommandH(arg); return; } if (arg[0] == "b") { CommandB(arg); return; } -#if 0 if (arg[0] == "nvram") { CommandNvram(arg); return; } -#endif text.Print("** Unknown command: %s\n", arg[0].c_str()); } +// "h" コマンド +// 引数なしなら、全体のヘルプメッセージ。 +// 引数ありなら、個別のヘルプメッセージ。 +void +Luna88kPROMEmuDevice::CommandH(const std::vector& arg) +{ + if (arg.size() < 2) { + // ヘルプメッセージ + mainhelp: + text.Print( +" *** Help Message ***\n" +" b : load and execute program\n" +" h : show this message\n" +" nvram : show/set nvram parameters\n" + ); + return; + } + + const std::string& key = arg[1]; + if (key == "nvram") { + text.Print( +" *** Help Message ***\n" +" nvram [] : show\n" +" nvram : set\n" +" nvram - : delete\n" +" nvram -- : initialize\n" + ); + return; + } else { + goto mainhelp; + } +} + // "b" コマンド // 引数なしなら NVRAM の情報に従ってプログラムをロードして実行。 // 引数ありのほうは未実装。 @@ -327,45 +390,306 @@ Luna88kPROMEmuDevice::CommandBH() b_entrypoint = LoadHostFile(); } +// "nvram" コマンド +// nvram .. 全表示 +// nvram .. 表示 +// nvram .. 追加/更新 +// nvram - .. 削除 +// nvram -- .. 初期化 void Luna88kPROMEmuDevice::CommandNvram(const std::vector& arg) { - printf("%s\n", __func__); + switch (arg.size()) { + case 1: + // 引数なし + CommandNvramShowAll(); + break; + case 2: + // 引数1つ + if (arg[1] == "--") { + CommandNvramInit(); + } else { + CommandNvramShow(arg); + } + break; + case 3: + // 引数2つ + if (arg[1] == "-") { + CommandNvramDelete(arg); + } else { + CommandNvramUpdate(arg); + } + break; + default: + text.Print("** Syntax error\n"); + } +} + +// NVRAM は 32バイトを1エントリとする。 +// [0] はヘッダに相当し、フォーマットは別記。 +// [1] 以降が key (16バイト)、val(16バイト) の並び。こんな感じ。 +// +// 00000020 62 6f 6f 74 5f 64 65 76 69 63 65 00 00 00 00 00 |boot_device.....| +// 00000030 73 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |sd..............| +// +// key, val とも '\0' で終端もしくは16文字で終端のようだ。 +// val に17文字を設定しようとすると怒られる。 +// key が "" ならこのエントリは空きで val は無視、 +// 空きエントリは無視して次へ (FAT のディレクトリエントリみたいな感じ)。 +// val が "" は有効。 +// +// オフセット 0x540 から始まるエントリが最後で、これ以上追加しようとすると +// "nvram set error: no area" (PROM 1.20) と言われる。 +// LUNA88K にはないが LUNA-I では 0x560 にぽつんと ENADDR があったので、 +// その辺りとの互換性だろうか。 + +// NVRAM 全項目表示 +void +Luna88kPROMEmuDevice::CommandNvramShowAll() +{ + putmsg(1, "nvram ShowAll"); + for (uint32 offset = 0x20; offset < 0x560; offset += 0x20) { + std::string key = nvram->PeekString(offset); + if (key.empty()) { + continue; + } + std::string val = nvram->PeekString(offset + 16); + text.Print("%-15s : %s\n", key.c_str(), val.c_str()); + } +} + +// NVRAM 指定項目を表示 +void +Luna88kPROMEmuDevice::CommandNvramShow(const std::vector& arg) +{ + assert(arg.size() >= 2); + const std::string& key = arg[1]; + + putmsg(1, "nvram Show |%s|", key.c_str()); + uint32 offset = FindNVRAM(key); + if (offset > 0) { + std::string val = nvram->PeekString(offset + 16); + text.Print("%-15s : %s\n", key.c_str(), val.c_str()); + } else { + text.Print("** Not found\n"); + } +} + +// NVRAM 項目を追加/更新 +void +Luna88kPROMEmuDevice::CommandNvramUpdate(const std::vector& arg) +{ + // キーがあれば更新、なければ追加。 + // いずれも事前にプロンプトで確認する。 + + assert(arg.size() >= 3); + nvram_key = arg[1]; + nvram_val = arg[2]; + putmsg(1, "nvram Update |%s|%s|", nvram_key.c_str(), nvram_val.c_str()); + + if (nvram_key.size() > 16) { + text.Print("** Key too long\n"); + return; + } + if (nvram_val.size() > 16) { + text.Print("** Val too long\n"); + return; + } + + nvram_offset = FindNVRAM(nvram_key); + if (nvram_offset > 0) { + // キーがすでにあれば更新。 + // Update2() に申し送りするのは nvram_offset と nvram_val のみでいい。 + + std::string oldval = nvram->PeekString(nvram_offset + 16); + prompt = string_format("Update %s : \"%s\" -> \"%s\" (Y/[N]):", + nvram_key.c_str(), oldval.c_str(), nvram_val.c_str()); + prompt_idx = PROMPT_NVRAM_UPDATE; + } else { + // キーがなければ追加 + // New2() に申し送りするのは nvram_offset, nvram_key, nvram_val の3つ。 + + // 先に空き領域をチェックしている + nvram_offset = FindNVRAM(""); + if (nvram_offset == 0) { + text.Print("** No area available\n"); + return; + } + + prompt = string_format("Set new %s : \"%s\" (Y/[N]):", + nvram_key.c_str(), nvram_val.c_str()); + prompt_idx = PROMPT_NVRAM_NEW; + } + + // 入力待ちの状態で戻る + // (prompt_idx != 0 なのでキー入力後に指定の関数へ飛んで続きを実行する) +} + +// NVRAM 項目を更新 (キー入力後) +void +Luna88kPROMEmuDevice::CommandNvramUpdate2(char asciicode) +{ + assert(prompt_idx == PROMPT_NVRAM_UPDATE); + assert(nvram_offset > 0); + assert(nvram_val.size() <= 16); + + text.CRLF(); + + if (asciicode != 'y' && asciicode != 'Y') { + text.Print("** Not updated\n"); + return; + } + + if (nvram->WriteString(nvram_offset + 16, nvram_val) == false) { + text.Print("** Write error\n"); + return; + } + WriteNVRAMCsum(); + + text.Print("Updated\n"); +} + +// NVRAM 項目を追加 (キー入力後) +void +Luna88kPROMEmuDevice::CommandNvramNew2(char asciicode) +{ + assert(prompt_idx == PROMPT_NVRAM_NEW); + assert(nvram_offset > 0); + assert(!nvram_key.empty()); + assert(nvram_key.size() <= 16); + assert(nvram_val.size() <= 16); + + text.CRLF(); + + if (asciicode != 'y' && asciicode != 'Y') { + text.Print("** Not added\n"); + return; + } + + if (nvram->WriteString(nvram_offset, nvram_key) == false + || nvram->WriteString(nvram_offset + 16, nvram_val) == false) { + text.Print("** Write error\n"); + return; + } + WriteNVRAMCsum(); + + text.Print("Added\n"); } -// NVRAM のチェックサムを返す -uint8 -Luna88kPROMEmuDevice::CalcNVRAMCsum() +// NVRAM 項目削除 +// +// 削除する時は確認がないらしい… (PROM 1.20) +// 成功しても何も言わないらしい… (PROM 1.20) +// 追加削除に比べてなんか雑じゃない? +void +Luna88kPROMEmuDevice::CommandNvramDelete(const std::vector& arg) +{ + assert(arg.size() >= 3); + assert(arg[1] == "-"); + const std::string& key = arg[2]; + putmsg(1, "nvram Delete |%s|", key.c_str()); + + if (key.size() > 16) { + text.Print("** key too long\n"); + return; + } + + uint32 offset = FindNVRAM(key); + if (offset == 0) { + text.Print("** Not found\n"); + return; + } + + // キーを消せば削除になる (値のほうは消してないようだ) + nvram->WriteString(offset, ""); + WriteNVRAMCsum(); +} + +// NVRAM から key を探してそのエントリのオフセットを返す。 +// key == "" なら空きエントリを探す。 +// 見付からなければ 0 を返す (オフセット 0 はパラメータには使わないため) +uint32 +Luna88kPROMEmuDevice::FindNVRAM(const std::string& key) const { + for (uint32 offset = 0x20; offset < 0x560; offset += 0x20) { + std::string k = nvram->PeekString(offset); + if (k == key) { + return offset; + } + // XXX 同じキーが複数あったらどうなるか + } return 0; } -// NVRAM のチェックサムを計算して書き込む +// NVRAM 初期化 void -Luna88kPROMEmuDevice::WriteNVRAMCsum() +Luna88kPROMEmuDevice::CommandNvramInit() { + putmsg(1, "nvram Init"); + + prompt = "Initialize (Y/[N]):"; + prompt_idx = PROMPT_NVRAM_INIT; + + // 入力待ちの状態で戻る } -// NVRAM のマジックとチェックサムを照合する -bool -Luna88kPROMEmuDevice::VerifyNVRAM() +// NVRAM 初期化 (キー入力後) +void +Luna88kPROMEmuDevice::CommandNvramInit2(char asciicode) { - return true; + assert(prompt_idx == PROMPT_NVRAM_INIT); + + text.CRLF(); + + if (asciicode != 'y' && asciicode != 'Y') { + text.Print("** Not initialized\n"); + return; + } + + InitNVRAM(); + text.Print("** Initialized\n"); } // NVRAM を初期化する bool Luna88kPROMEmuDevice::InitNVRAM() { - return false; + nvram->ClearAll(); + + nvram->WriteString(0x0004, ""); + nvram->WriteString(0x0020, "boot_device"); + nvram->WriteString(0x0030, "sd"); + nvram->WriteString(0x0040, "boot_spec"); + nvram->WriteString(0x0050, "0"); + nvram->WriteString(0x0060, "boot_unit"); + nvram->WriteString(0x0070, "0"); + nvram->WriteString(0x0080, "boot_partition"); + nvram->WriteString(0x0090, "0"); + nvram->WriteString(0x00a0, "boot_filename"); + nvram->WriteString(0x00b0, "vmunix"); + nvram->WriteString(0x00c0, "main_mem_check"); + nvram->WriteString(0x00d0, "1"); + nvram->WriteString(0x00e0, "3port_mem_check"); + nvram->WriteString(0x00f0, "1"); + + // チェックサム再計算 + WriteNVRAMCsum(); + + return true; } // NVRAM のブート設定を bootinfo に取得する。 void Luna88kPROMEmuDevice::GetNVRAM(bootinfo_t& bootinfo) { - // XXX NVRAM 未調査につき ID=6 a: boot 固定 - bootinfo.dkunit = 0; - bootinfo.dkpart = 0; - bootinfo.dkfile = "boot"; + // dkunit (装置番号) はたぶん boot_unit (0..) + // dkpart (パーティション) は boot_partition (0..) + // dkfile (ファイル名) は boot_filename + // sd 以外のデバイスは未対応 + bootinfo.dkunit = nvram->Peek(0x0070) - '0'; + bootinfo.dkpart = nvram->Peek(0x0090) - '0'; + bootinfo.dkfile = nvram->PeekString(0x00b0); + putmsg(1, "%s unit=%d", __func__, bootinfo.dkunit); + putmsg(1, "%s part=%c", __func__, bootinfo.dkpart + 'a'); + putmsg(1, "%s file=|%s|", __func__, bootinfo.dkfile.c_str()); }