--- nono/vm/romemu_luna.cpp 2026/04/29 17:04:50 1.1.1.7 +++ nono/vm/romemu_luna.cpp 2026/04/29 17:05:06 1.1.1.12 @@ -5,21 +5,29 @@ // // LUNA シリーズの ROM エミュレーション共通部分 - -// IODevice -// | -// +----------+---------------------+ -// v v -// ROMDevice LunaPROMEmuDeivce (ROM エミュレーションの共通部分) -// | | -// | +------------+----------------+ -// v v v -// PROMDevice Luna1PROMEmuDevice Luna88kPROMEmuDevice -// (実ROM) (LUNA-I エミュレーション) (LUNA88K エミュレーション) +// +// IODevice +// | +// +-- ROMDevice (LoadROM()、ウェイト、マスク等を持つ) +// | | +// | +-- PROMDevice (LUNA* の PROM) +// | +-- IPLROM1Device (X680x0 の IPLROM 後半) +// | +-- IPLROM2Device (X680x0 の IPLROM 前半) +// | +-- CGROMDevice (X680x0 の CGROM) +// | | +// | | +-------------------+ +// | +--| LunaPROMEmuDevice | (LUNA PROM エミュレーションの共通部分) +// | +-------------------+ +// | | +// | +-- Luna1PROMEmuDevice (LUNA1 の PROM エミュレーション) +// | +-- Luna88kPROMEmuDevice (LUNA88K の PROM エミュレーション) +// | +// +-- PROM0Device (LUNA* のブートページ切り替え用プロキシ) +// +-- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ) #include "romemu_luna.h" -#include "bitmap.h" #include "bt454.h" +#include "lunafb.h" #include "mainapp.h" #include "mk48t02.h" #include "ram.h" @@ -29,9 +37,10 @@ // コンストラクタ LunaPROMEmuDevice::LunaPROMEmuDevice() + : inherited("PROM") { - logname = "prom"; - devname = "PROM"; + // LUNA では ROM への書き込みは何も起きない + write_op = 0; } // デストラクタ @@ -48,91 +57,33 @@ LunaPROMEmuDevice::Init() return true; } -void -LunaPROMEmuDevice::ResetHard() -{ - 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 (!is_closed) { - switch (offset) { - case ROMIO_INIT - baseaddr: + // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。 + // addr をマスクする前に評価すること。 + if ((gMPU->GetPaddr() & 0xff000000) == baseaddr) { + switch (addr) { + case ROMIO_INIT: return ROM_Init(); - case ROMIO_KEYIN - baseaddr: + case ROMIO_KEYIN: ROM_Keyin(); return 0; - case ROMIO_CLOSE - baseaddr: + case ROMIO_CLOSE: ROM_Close(); return 0; - case ROMIO_ENTRY - baseaddr: + case ROMIO_ENTRY: return ROM_Entry(); default: break; } } - 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)]; + return inherited::Read32(addr); } // デバイスの初期化の共通部分。(継承クラスの ROM_Init() から呼ぶ) @@ -163,14 +114,19 @@ LunaPROMEmuDevice::InitDevice() gSIO->Write(3, 0x44); // Stop 1bit gSIO->Write(3, 0x05); // CR5 gSIO->Write(3, 0x68); // TX Enable, 8bit + + // LED を(明示的に)オフ、マウスサンプリングをオフ + gSIO->Write(2, 0x00); + gSIO->Write(2, 0x01); + gSIO->Write(2, 0x20); } // スクリーンを初期化 void LunaPROMEmuDevice::InitScreen(const char *machine_name) { - text.Init(80, 32); - gBitmap->EmuConsoleOpen(&text); + text.Init(80, 32, TextScreen::Console); + gLunafb->EmuConsoleOpen(&text); inputbuf.clear(); inputpos = 0; is_shift = false; @@ -185,17 +141,13 @@ LunaPROMEmuDevice::InitScreen(const char void LunaPROMEmuDevice::ROM_Close() { - // ここでバックドアに蓋をする - is_closed = true; - - // SIO からキーの Break 入力を取り除いておく。 - // コマンド入力によってここへ来る時は、キーの Make で呼ばれるため - // Break はまだ取り出されておらず SIO のバッファに残っている。 - // コマンド入力によらずここに来た場合でも空読みして問題ないだろう。 - (void)gSIO->Read(2); + // SIO をリセットしておく。 + // SIO のバッファにデータが残っていると、NetBSD のブートローダが + // 起動してこないようだ。リセットして RXEN を落としておけば大丈夫ぽい。 + gSIO->ResetHard(); // コンソールクローズ - gBitmap->EmuConsoleClose(); + gLunafb->EmuConsoleClose(); } // ROM プロンプトのキー入力。 @@ -232,81 +184,176 @@ LunaPROMEmuDevice::ROM_Keyin() return; } - switch (lunakey) { - case 0x11: // BS - if (inputpos > 0) { - inputbuf.erase(--inputpos); + // キーコードから文字を取得 + char asciicode; + if (is_shift) { + asciicode = lunakey2shifttable[lunakey]; + } else { + asciicode = lunakey2asciitable[lunakey]; + } + if (asciicode != 0) { + GetChar(asciicode); + } +} + +// キー入力ハンドラ (キー入力割り込みによって呼ばれる)。 +// 引数 asciicode は概ね ASCII コード (ただし 0x1c..0x1f が上下左右)。 +void +LunaPROMEmuDevice::GetChar(char asciicode) +{ + // LUNA1 の場合、 + // 実機 PROM にはカーソル左右移動の機能はなく、カーソルは常に行末にいる。 + // そのためかどうか Delete も BackSpace と同じく1文字前を消す動作をして + // いる。カーソル位置に文字はないので、Delete をそうするのはまあ分からん + // でもない。 + // 一方、エミュレーション ROM では、カーソルの左右移動をサポートしてみた + // ため Delete を「カーソル位置の文字を消す」にすることは出来るが、それは + // それで実機と乖離が広がるので、どうしたもんか。 + + switch (asciicode) { + case BS: + if (inputbuf.empty() == false && inputpos != 0) { + inputbuf.erase(--inputpos, 1); } break; - case 0x12: // Enter - { - // 現在位置のカーソルを消去 - uint16 *rawbuf = text.GetRawBuf(); - int curx = prompt.length() + inputpos; - rawbuf[text.GetY() * text.GetCol() + curx] &= ~TA::On; + + case LF: + // 改行を起こす前にカーソル(ただの反転)を戻しておく必要がある + text.SetAttr(TA::Normal); // 改行 - text.Putc('\n'); - // コマンド処理。 + text.Putc(asciicode); + prompt_y = text.GetY(); + // コマンド実行 Command(); - inputbuf.clear(); - inputpos = 0; + // プロンプトを更新 + ClearPrompt(); break; - } - case 0x1d: // ← + + case LEFT: if (inputpos > 0) { inputpos--; } break; - case 0x1e: // → + case RIGHT: if (inputpos < inputbuf.length()) { inputpos++; } break; - default: // 通常文字 - { - char asciicode; - if (is_shift) { - asciicode = lunakey2shifttable[lunakey]; - } else { - asciicode = lunakey2asciitable[lunakey]; - } - if (asciicode == 0) { - // 割り当てなし - return; - } + + case UP: + case DOWN: + case 0: // キー割り当てなし、これは来ないはずだが一応 + return; + + default: // 通常文字のはず inputbuf.insert(inputbuf.begin() + inputpos, asciicode); inputpos++; break; - } } - putmsg(2, "inputbuf=%zd|%s|", inputbuf.length(), inputbuf.c_str()); ConsoleUpdate(); } +// 入力バッファを初期化する。 +void +LunaPROMEmuDevice::ClearPrompt() +{ + prompt_y = text.GetY(); + // 入力行をクリア + inputbuf.clear(); + inputpos = 0; +} + // 画面を更新 void LunaPROMEmuDevice::ConsoleUpdate() { - // 入力行全体を都度更新 (面倒なので行をはみ出さないようにする) - 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; + int x; + int y; + + // カーソルを描画する予定の行が最下行を越えていたらスクロール + int len = prompt.length() + inputpos; + y = prompt_y + (len / text.GetCol()); + if (y >= text.GetRow()) { + text.ScrollUp(); + prompt_y--; + } + + // プロンプト行から下を一旦全部クリア + // (手抜きだが、見た目問題ないはず) + text.Mode = TextScreen::Fixed; + for (y = prompt_y; y < text.GetRow(); y++) { + text.Locate(0, y); + for (x = 0; x < text.GetCol(); x++) { + text.Putc(' '); + } + } + text.Locate(0, prompt_y); + text.Mode = TextScreen::Console; + + // プロンプトと現在の入力行を出力 + text.Puts(prompt.c_str()); + text.Puts(inputbuf.c_str()); + + // カーソルを描画 + y = prompt_y + (len / text.GetCol()); + x = len % text.GetCol(); + putmsg(2, "|%s|%s| pos=%d prompt_y=%d cur=(%d,%d)", + prompt.c_str(), inputbuf.c_str(), inputpos, prompt_y, x, y); + text.Locate(x, y); + text.SetAttr(TA::On); + // 画面更新 - gBitmap->EmuConsoleUpdate(); + gLunafb->EmuConsoleUpdate(); +} + +// NVRAM のチェックサムを返す +uint8 +LunaPROMEmuDevice::CalcNVRAMCsum() const +{ + uint8 eor = 0; + for (uint32 addr = 0x20; addr < 0x560; addr++) { + eor ^= nvram->Peek(addr); + } + return eor; +} + +// NVRAM のチェックサムを計算して書き込む +void +LunaPROMEmuDevice::WriteNVRAMCsum() +{ + uint8 eor = CalcNVRAMCsum(); + + nvram->Write(0x001c, eor); + nvram->Write(0x001d, eor); + nvram->Write(0x001e, eor); +} + +// NVRAM のマジックとチェックサムを照合する +bool +LunaPROMEmuDevice::VerifyNVRAM() const +{ + if (nvram->PeekString(0x0004) != "") { + return false; + } + + uint8 eor = CalcNVRAMCsum(); + for (uint32 addr = 0x1c; addr < 0x1f; addr++) { + if (nvram->Peek(addr) != eor) { + return false; + } + } + return true; } -// -A オプションで指定されたホストファイルをロードする。 +// -X オプションで指定されたホストファイルをロードする。 // ロードできればエントリポイントを返す。 -// host_file が指定されている時に呼ぶこと。 +// exec_file が指定されている時に呼ぶこと。 uint32 LunaPROMEmuDevice::LoadHostFile() { - assert(gMainApp.host_file); + assert(gMainApp.exec_file); - uint32 entry = gRAM->LoadFile(gMainApp.host_file); + uint32 entry = gRAM->LoadFile(gMainApp.exec_file); if (entry) { text.Print("Host program loaded. Entry point = $%08x\n", entry); @@ -340,12 +387,12 @@ LunaPROMEmuDevice::LoadGuestFile(const b return 0; } - SCSIHD *hd = dynamic_cast(target); + SCSIDisk *hd = dynamic_cast(target); putmsg(2, "%s SCSIHD[%d] found", __func__, id); // +0 セクタ目(512byte)に Sun disklabel scd_dk_label dk; - if (hd->PeekImage(&dk, 0, sizeof(dk)) == false) { + if (hd->Peek(&dk, 0, sizeof(dk)) == false) { putmsg(0, "SCSIHD[%d] ReadSector failed", id); errmsg = string_format("dk%d Read disklabel failed.", dkunit); return 0; @@ -414,10 +461,10 @@ LunaPROMEmuDevice::LoadGuestFile(const b // キーコードから文字への変換 /*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, 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', @@ -433,12 +480,12 @@ LunaPROMEmuDevice::lunakey2asciitable[] }; /*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, 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',