--- 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:11 1.1.1.13 @@ -4,24 +4,36 @@ // Licensed under nono-license.txt // +// // 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 "mpu.h" +#include "prom.h" #include "ram.h" #include "sio.h" #include "spc.h" @@ -29,110 +41,54 @@ // コンストラクタ LunaPROMEmuDevice::LunaPROMEmuDevice() + : inherited("PROM") { - logname = "prom"; - devname = "PROM"; + // LUNA では ROM への書き込みは何も起きない + write_op = 0; } // デストラクタ LunaPROMEmuDevice::~LunaPROMEmuDevice() { + gPROM = NULL; } bool LunaPROMEmuDevice::Init() { // 都度都度 dynamic_cast は面倒なので、ここで一度代入しとく - nvram = dynamic_cast(gRTC.get()); + nvram = dynamic_cast(gRTC); 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() から呼ぶ) @@ -157,45 +113,137 @@ LunaPROMEmuDevice::InitDevice() // キーボード(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, 0x03); // CR3 + gSIO->Write(3, 0xc1); // RX Enable, 8bit 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) +LunaPROMEmuDevice::InitScreen() { - text.Init(80, 32); - gBitmap->EmuConsoleOpen(&text); + gLunafb->EmuInitScreen(); + + screen_w = 80; + screen_h = 32; + cursor_x = 0; + cursor_y = 0; + + // 表示範囲はざっくりセンタリングする + origin_px = (1280 - screen_w * 12) / 2; + origin_py = (1024 - screen_h * 24) / 2; + // 横は 48ドット境界から始めておく。 + // こうすると1(,2)文字目の24ビットが必ずワード境界から始まるため。 + // 今となっては任意の場所から文字が描画できるようになったので揃える + // 必要はなくなったが、依然このほうがアクセス効率はいいので。 + origin_px = (origin_px / 48) * 48; + inputbuf.clear(); inputpos = 0; is_shift = false; mousecnt = 0; +} - text.Print(0, 0, "NONO %d.%d.%d Emulated ROM Monitor for %s", +// タイトルを出力 +void +LunaPROMEmuDevice::PrintTitle(const char *machine_name) +{ + Print("NONO %d.%d.%d Emulated ROM Monitor for %s\n\n", NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, machine_name); - text.Locate(0, 2); } -// ROM 処理のクローズ +// 1文字出力 void -LunaPROMEmuDevice::ROM_Close() +LunaPROMEmuDevice::Putc(uint ch) { - // ここでバックドアに蓋をする - is_closed = true; + uint px = origin_px + cursor_x * 12; + uint py = origin_py + cursor_y * 24; - // SIO からキーの Break 入力を取り除いておく。 - // コマンド入力によってここへ来る時は、キーの Make で呼ばれるため - // Break はまだ取り出されておらず SIO のバッファに残っている。 - // コマンド入力によらずここに来た場合でも空読みして問題ないだろう。 - (void)gSIO->Read(2); + // カーソル位置の反転を元に戻す + gLunafb->EmuPutc(px, py, ' ', ROP::INV2); - // コンソールクローズ - gBitmap->EmuConsoleClose(); + if (ch == LF) { + put_LF: + cursor_x = 0; + if (cursor_y < screen_h - 1) { + cursor_y++; + } else { + // 上スクロールして.. + gLunafb->EmuScrollY(origin_py, origin_py + 24, (screen_h - 1) * 24); + // 最終行を消す + gLunafb->EmuFill(0, py, 1280, 24, 0); + } + } else if (ch == LEFT) { + if (cursor_x > 0) { + cursor_x--; + } else { + cursor_y--; + cursor_x = screen_w - 1; + } + } else if (ch == RIGHT) { + if (cursor_x < screen_w - 1) { + cursor_x++; + } else { + cursor_y++; + cursor_x = 0; + } + } else { + if (cursor_x < screen_w - 1) { + gLunafb->EmuPutc(px, py, ch); + cursor_x++; + } else { + gLunafb->EmuPutc(px, py, ch); + goto put_LF; + } + } + + // カーソル位置を再計算して描画 (文字を反転) + px = origin_px + cursor_x * 12; + py = origin_py + cursor_y * 24; + gLunafb->EmuPutc(px, py, ' ', ROP::INV2); +} + +// 文字列 s を出力 +void +LunaPROMEmuDevice::Print(const std::string& s) +{ + for (auto *p = s.c_str(); *p; p++) { + Putc(*p); + } +} + +// 文字列 fmt... を出力 +void +LunaPROMEmuDevice::Print(const char *fmt, ...) +{ + char buf[1024]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + for (char *p = buf; *p; p++) { + Putc(*p); + } +} + +// ROM 処理のクローズ +void +LunaPROMEmuDevice::ROM_Close() +{ + // SIO をリセットしておく。 + // SIO のバッファにデータが残っていると、NetBSD のブートローダが + // 起動してこないようだ。リセットして RXEN を落としておけば大丈夫ぽい。 + gSIO->ResetHard(false); } // ROM プロンプトのキー入力。 @@ -232,86 +280,152 @@ 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); + 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 0x12: // Enter - { - // 現在位置のカーソルを消去 - uint16 *rawbuf = text.GetRawBuf(); - int curx = prompt.length() + inputpos; - rawbuf[text.GetY() * text.GetCol() + curx] &= ~TA::On; + + case LF: // 改行 - text.Putc('\n'); - // コマンド処理。 + Putc(asciicode); + prompt_y = cursor_y; + // コマンド実行 Command(); - inputbuf.clear(); - inputpos = 0; + // プロンプトを更新 + ClearPrompt(); break; - } - case 0x1d: // ← + + case LEFT: if (inputpos > 0) { inputpos--; + Putc(LEFT); } break; - case 0x1e: // → + case RIGHT: if (inputpos < inputbuf.length()) { inputpos++; + Putc(RIGHT); } break; - default: // 通常文字 - { - char asciicode; - if (is_shift) { - asciicode = lunakey2shifttable[lunakey]; - } else { - asciicode = lunakey2asciitable[lunakey]; + + 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]); } - if (asciicode == 0) { - // 割り当てなし - return; + for (int i = 0; i < inputbuf.size() - inputpos - 1; i++) { + Putc(LEFT); } - inputbuf.insert(inputbuf.begin() + inputpos, asciicode); inputpos++; break; - } } - putmsg(2, "inputbuf=%zd|%s|", inputbuf.length(), inputbuf.c_str()); - ConsoleUpdate(); } -// 画面を更新 +// 入力バッファを初期化する。 +void +LunaPROMEmuDevice::ClearPrompt() +{ + prompt_y = cursor_y; + // 入力行をクリア + inputbuf.clear(); + inputpos = 0; + + Print(prompt); +} + +// NVRAM のチェックサムを返す +uint8 +LunaPROMEmuDevice::CalcNVRAMCsum() const +{ + uint8 eor = 0; + for (uint32 addr = 0x20; addr < 0x560; addr++) { + eor ^= nvram->Peek(addr); + } + return eor; +} + +// NVRAM のチェックサムを計算して書き込む void -LunaPROMEmuDevice::ConsoleUpdate() +LunaPROMEmuDevice::WriteNVRAMCsum() +{ + uint8 eor = CalcNVRAMCsum(); + + nvram->Write(0x001c, eor); + nvram->Write(0x001d, eor); + nvram->Write(0x001e, eor); +} + +// NVRAM のマジックとチェックサムを照合する +bool +LunaPROMEmuDevice::VerifyNVRAM() const { - // 入力行全体を都度更新 (面倒なので行をはみ出さないようにする) - 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(); + 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); + Print("Host program loaded. Entry point = $%08x\n", entry); } else { - text.Print("** Couldn't load specified host program.\n"); + Print("** Couldn't load specified host program.\n"); } return entry; } @@ -340,12 +454,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 +528,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', @@ -426,27 +540,27 @@ LunaPROMEmuDevice::lunakey2asciitable[] '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, + 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, 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', '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, + 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, '*', '/', '=', ',', };