--- nono/vm/romemu_luna.cpp 2026/04/29 17:04:32 1.1 +++ nono/vm/romemu_luna.cpp 2026/04/29 17:04:36 1.1.1.2 @@ -1,19 +1,33 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // +// LUNA-I の ROM エミュレーション + +// IODevice +// | +// +----------+----------+ +// v v +// ROMDevice LunaPROMEmuDeivce (ROM エミュレーション) +// | +// v +// PROMDevice (実ROM) + +#include "romemu_luna.h" #include "aout.h" #include "bitmap.h" #include "bt454.h" +#include "ethernet.h" #include "ffs_dinode.h" #include "ffs_dir.h" #include "ffs_fs.h" #include "mainapp.h" #include "memorystream.h" #include "mk48t02.h" +#include "mpu680x0.h" #include "pio.h" -#include "romemu_luna.h" #include "ram.h" #include "sio.h" #include "spc.h" @@ -29,15 +43,15 @@ enum { // コンストラクタ LunaPROMEmuDevice::LunaPROMEmuDevice() { - mem = NULL; + // LUNA-I の ROM の特定アドレスを読み出す人がいるので、 + // サイズは実 ROM と同じにしておく必要がある。 + devlen = 128 * 1024; + romdata.reset(new uint8 [devlen]); } // デストラクタ LunaPROMEmuDevice::~LunaPROMEmuDevice() { - if (mem) { - delete[] mem; - } } bool @@ -46,38 +60,37 @@ LunaPROMEmuDevice::Init() // 都度都度 dynamic_cast は面倒なので、ここで一度代入しとく nvram = dynamic_cast(gRTC.get()); - mem = new uint8 [devlen]; - - MemoryStreamBE ms(mem); + MemoryStreamBE ms(romdata.get()); 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 + ms.SetPtr(&romdata[0x400]); + ms.Write16(0x2039); // move.l (ROMIO_INIT),d0 + ms.Write32(ROMIO_INIT); + ms.Write16(0x660c); // 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 ; の繰り返し + ms.Write32(0x4e722500); // stop #0x2500 ; キー入力を待つ + ms.Write16(0x2039); // move.l (ROMIO_KEYIN),d0 + ms.Write32(ROMIO_KEYIN); // ; キー入力処理 + ms.Write16(0x67f4); // beq _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(0x2039); // move.l (ROMIO_CLOSE),d0 + ms.Write32(ROMIO_CLOSE); // ; 後始末 ms.Write16(0x4ed0); // jmp (a0) ; ジャンプ // Lv6 (SIO) 割り込みハンドラ // キーボード割り込みが上がったら STOP 命令から戻ってくるだけでいい - ms.SetPtr(mem + 0x600); + ms.SetPtr(&romdata[0x600]); ms.Write16(0x4e73); // rte - // MAC アドレス置き場のマジック - strcpy((char *)&mem[0x1ffd8], "ENADDR"); + // MAC アドレス + const macaddr_t macaddr = gEthernet->GetMacAddr(); + strcpy((char *)&romdata[0x1ffd8], "ENADDR"); + memcpy(&romdata[0x1ffe0], macaddr.to_string().c_str(), 12); // RAM にベクタを書き込む // XXX VBR を 41000000 に向けるのとどっちが楽かはある @@ -89,50 +102,94 @@ LunaPROMEmuDevice::Init() void LunaPROMEmuDevice::ResetHard() { - ipl_state = STATE_RESET; + is_closed = false; } // アドレスデコーダ inline uint64 LunaPROMEmuDevice::Decoder(uint32 addr) const { + // 折り返しなし return addr - baseaddr; } uint64 +LunaPROMEmuDevice::Read8(uint32 addr) +{ + uint64 offset = Decoder(addr); + + // 謎の IO 空間はロングワードアクセスのみ + return romdata[offset & (devlen - 1)]; +} + +uint64 +LunaPROMEmuDevice::Read16(uint32 addr) +{ + uint64 offset = Decoder(addr); + + // 謎の IO 空間はロングワードアクセスのみ + return be16toh(*(uint16 *)&romdata[offset & (devlen - 1)]); +} + +uint64 LunaPROMEmuDevice::Read32(uint32 addr) { uint64 offset = Decoder(addr); // ロングワードアクセスでだけ謎の I/O 空間が見える - if (ipl_state != STATE_CLOSED) { + if (!is_closed) { switch (offset) { - case ROMIO_IPL_INIT - baseaddr: - return IPL_Init(); + case ROMIO_INIT - baseaddr: + return ROM_Init(); - case ROMIO_IPL_PROMPT - baseaddr: - return IPL_Prompt(); + case ROMIO_KEYIN - baseaddr: + return ROM_Keyin(); - case ROMIO_IPL_CLOSE - baseaddr: - return IPL_Close(); + case ROMIO_CLOSE - baseaddr: + return ROM_Close(); default: break; } } - return inherited::Read32(addr); + return be32toh(*(uint32 *)&romdata[offset & (devlen - 1)]); +} + +uint64 +LunaPROMEmuDevice::Write8(uint32 addr, uint32 data) +{ + return 0; +} + +uint64 +LunaPROMEmuDevice::Write16(uint32 addr, uint32 data) +{ + return 0; +} + +uint64 +LunaPROMEmuDevice::Write32(uint32 addr, uint32 data) +{ + return 0; +} + +uint64 +LunaPROMEmuDevice::Peek8(uint32 addr) +{ + uint64 offset = Decoder(addr); + + return romdata[offset & (devlen - 1)]; } // 起動時の処理を行う。 // 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。 // そうでなければ 0 を返すので、プロンプトに処理を移すこと。 uint32 -LunaPROMEmuDevice::IPL_Init() +LunaPROMEmuDevice::ROM_Init() { MemoryStreamBE ms; uint32 entry; - ipl_state = STATE_INIT; entrypoint = 0; // ROM 0x41000000.L リセットベクタ(SP,PC)もカーネルが参照している @@ -141,14 +198,14 @@ LunaPROMEmuDevice::IPL_Init() // ROM 0x410000b8.L プレーンマスク格納先アドレス // メモリサイズはとりあえず 0x41000500 に格納 - ms.SetPtr(mem + 0x500); + ms.SetPtr(&romdata[0x500]); ms.Write32(::ram_size); // プレーン数(1..4)をプレーンマスク(0x01..0x0f)に int nplane = gBitmap->GetPlaneCount(); int planemask = (1 << nplane) - 1; // 実際は RAM 上を指しているのだが、とりあえずすぐ隣に置いておく。 - ms.SetPtr(mem + 0xb8); + ms.SetPtr(&romdata[0xb8]); ms.Write32(0x410000bc); ms.Write32(planemask); @@ -162,10 +219,25 @@ LunaPROMEmuDevice::IPL_Init() // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。 gSPC->Write8(SPC::BDID, 7); - // スクリーンを初期化 + // 内蔵 ROM: キーボード(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 + + // 内蔵 ROM: スクリーンを初期化 text.Init(80, 32); text.foldmode = true; gBitmap->EmuConsoleOpen(&text); + inputbuf.clear(); + inputpos = 0; + is_shift = false; + prompt_idx = 0; + prompt = default_prompt; text.Print(0, 0, "NONO %d.%d.%d Emulated ROM Monitor", NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER); @@ -204,16 +276,20 @@ LunaPROMEmuDevice::IPL_Init() } } + if (entry == 0) { + ConsoleUpdate(); + } + return entry; } // ROM 処理のクローズ。 // 次段プログラムにジャンプするための処理も含む。 uint32 -LunaPROMEmuDevice::IPL_Close() +LunaPROMEmuDevice::ROM_Close() { // ここでバックドアに蓋をする - ipl_state = STATE_CLOSED; + is_closed = true; // コンソールクローズ gBitmap->EmuConsoleClose(); @@ -244,110 +320,88 @@ LunaPROMEmuDevice::IPL_Close() return 0; } -// ROM プロンプト。 +// ROM プロンプトのキー入力。 // VM 側の仮想マシンを回すため、ここでは入力待ちはしない。 -// state が INIT なら1回目なので初期化など初回の処理を行う。 -// state が PROMPT なら1文字入力されたので処理をする。 // エントリポイントが取得できればそれを、そうでなければ 0 を返す。 uint32 -LunaPROMEmuDevice::IPL_Prompt() +LunaPROMEmuDevice::ROM_Keyin() { - 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 + uint32 lunakey = gSIO->Read8(0x51000004); - // バッファを初期化 - inputbuf.clear(); - inputpos = 0; + // 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; + } - 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; + switch (lunakey) { + case 0x11: // BS + if (inputpos > 0) { + inputbuf.erase(--inputpos); } - if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放 - is_shift = false; - return 0; + 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 = Command(); + if (entry != 0) { + return entry; } - // 後は最上位ビットが立ってたらキー開放かマウスなので無視 - if ((lunakey & 0x80)) { - return 0; + inputbuf.clear(); + inputpos = 0; + break; + } + case 0x1d: // ← + if (inputpos > 0) { + inputpos--; } - - 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); + break; + case 0x1e: // → + if (inputpos < inputbuf.length()) { inputpos++; - break; - } } - putmsg(2, "inputbuf=%zd|%s|", inputbuf.length(), inputbuf.c_str()); 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()); + ConsoleUpdate(); + return 0; +} +// 画面を更新 +void +LunaPROMEmuDevice::ConsoleUpdate() +{ // 入力行全体を都度更新 (面倒なので行をはみ出さないようにする) std::string buf = prompt + inputbuf; text.Print(0, text.GetY(), "%-79s", buf.c_str()); @@ -357,19 +411,18 @@ LunaPROMEmuDevice::IPL_Prompt() rawbuf[text.GetY() * text.GetCol() + curx] |= TA::On; // 画面更新 gBitmap->EmuConsoleUpdate(); - return 0; } // 入力コマンド処理 uint32 -LunaPROMEmuDevice::IPL_Command() +LunaPROMEmuDevice::Command() { - char buf[inputbuf.length() + 1]; + std::vector buf(inputbuf.length() + 1); std::vector arg; // 1ワードごとに分解 - strcpy(buf, inputbuf.c_str()); - char *p = buf; + strcpy(&buf[0], inputbuf.c_str()); + char *p = &buf[0]; char *s = p; while (*p != '\0') { if (*p == ' ') { @@ -391,7 +444,7 @@ LunaPROMEmuDevice::IPL_Command() // "k" コマンド中ならここで先に処理する if (prompt_idx != 0) { - IPL_CommandK(arg); + CommandK(arg); return 0; } @@ -414,19 +467,19 @@ LunaPROMEmuDevice::IPL_Command() return 0; } if (arg[0] == "g") { - IPL_CommandG(arg); + CommandG(arg); return 0; } if (arg[0] == "gh") { - IPL_CommandGH(); + CommandGH(); return 0; } if (arg[0] == "k") { - IPL_CommandK(arg); + CommandK(arg); return 0; } if (arg[0] == "x") { - return IPL_CommandX(); + return CommandX(); } text.Print("** Unknown command: %s\n", arg[0].c_str()); @@ -438,7 +491,7 @@ LunaPROMEmuDevice::IPL_Command() // g なら NVRAM の fname だけ指定の名前を使ってロード。NVRAM には // 書き込まない。 void -LunaPROMEmuDevice::IPL_CommandG(const std::vector& arg) +LunaPROMEmuDevice::CommandG(const std::vector& arg) { std::string ctlr = nvram->PeekString(0x0030); if (ctlr == "dk") { // harddisk @@ -480,7 +533,7 @@ LunaPROMEmuDevice::IPL_CommandG(const st // "gh" コマンド (勝手に拡張) // -A オプションで指定されたホストプログラムをロードする。 void -LunaPROMEmuDevice::IPL_CommandGH() +LunaPROMEmuDevice::CommandGH() { entrypoint = 0; if (gMainApp.host_file) { @@ -530,7 +583,7 @@ static const struct kinfo kinfo[] = { // "k" コマンド void -LunaPROMEmuDevice::IPL_CommandK(const std::vector& arg) +LunaPROMEmuDevice::CommandK(const std::vector& arg) { uint32 offset; const char *name; @@ -681,7 +734,7 @@ LunaPROMEmuDevice::CommandKInput(const s // "x" コマンド // すでに読み込まれてるはずのエントリポイントにジャンプするだけ。 uint32 -LunaPROMEmuDevice::IPL_CommandX() +LunaPROMEmuDevice::CommandX() { if (entrypoint == 0) { text.Print("** Program is not loaded.\n"); @@ -842,13 +895,13 @@ struct scd_dk_label static_assert(sizeof(scd_dk_label) == 512, "size must be 512"); // オレオレ inode 情報クラス -class inodefile +class inodefile final { public: inodefile(Device *dev) { parent = dev; } - virtual ~inodefile() { } + ~inodefile() { } // ディレクトリエントリからファイルを検索 uint32 FindFile(const std::string& name); @@ -861,14 +914,14 @@ class inodefile }; // ファイルシステムハンドルっぽいもの -class Filesys +class Filesys final { public: Filesys(Device *dev) { parent = dev; fs = &fs0; } - virtual ~Filesys() { } + ~Filesys() { } // ファイルシステムをオープン bool Open(SCSIHD *hd_, uint32 start, uint32 size); @@ -1322,12 +1375,12 @@ Filesys::Readi(inodefile& inode) struct ufs1_dinode *di = &inode.di; // ino が含まれるブロックを読み込む - char buf[fs->fs_bsize]; + std::vector 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)); + hd->PeekImage(&buf[0], part_start + ino_sector * 512, buf.size()); // ino で示される inode を取得 - *di = ((ufs1_dinode *)buf)[ino_to_fsbo(fs, ino)]; + *di = ((ufs1_dinode *)(&buf[0]))[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); @@ -1420,22 +1473,23 @@ Filesys::ReadData(inodefile& inode) break; } // 一次間接ブロック - uint32 ib0buf[fs->fs_bsize / sizeof(uint32)]; - hd->PeekImage(ib0buf, be32toh(di->di_ib[0]) * 512, sizeof(ib0buf)); + std::vector ib0buf(fs->fs_bsize / sizeof(uint32)); + hd->PeekImage(&ib0buf[0], be32toh(di->di_ib[0]) * 512, + ib0buf.size() * sizeof(uint32)); if (parent->loglevel >= 2) { std::string distr; - for (int i = 0; i < countof(ib0buf); i++) { - if (ib0buf[i] == 0) + for (const auto& x : ib0buf) { + if (x == 0) break; - distr += string_format(" %u", be32toh(ib0buf[i])); + distr += string_format(" %u", be32toh(x)); } parent->putmsg("%s di[%d].di_ib[0]=%s", __func__, ino, distr.c_str() + 1); } - for (int i = 0; i < countof(ib0buf); i++) { + for (const auto& x : ib0buf) { uint64 remain = di_size - offset; // 残りバイト数 uint32 nbytes = std::min(remain, bsize64); // 今回読み込み分 - hd->PeekImage(&data[offset], be32toh(ib0buf[i]) * 512, nbytes); + hd->PeekImage(&data[offset], be32toh(x) * 512, nbytes); offset += nbytes; if (offset >= di_size) break; @@ -1459,7 +1513,6 @@ inodefile::FindFile(const std::string& n // このエントリの頭から d->d_reclen を足したところになる。 // 最終エントリの d_reclen を足すとバッファ末尾になるようになっている // ようだ。 - bool found = false; const char *dp = (const char *)(data.data()); const char *dend = dp + data.size(); uint32 file_ino; @@ -1473,17 +1526,12 @@ inodefile::FindFile(const std::string& n 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; + return file_ino; } 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; + parent->putmsg(2, "%s %s not found", __func__, name.c_str()); + return 0; }