--- nono/vm/romemu_luna.cpp 2026/04/29 17:05:18 1.1.1.14 +++ nono/vm/romemu_luna.cpp 2026/04/29 17:05:51 1.1.1.19 @@ -9,26 +9,32 @@ // // IODevice -// | -// +-- ROMDevice (LoadROM()、ウェイト、マスク等を持つ) -// | | -// | +-- PROMDevice (LUNA* の PROM) -// | +-- IPLROM1Device (X680x0 の IPLROM 後半) -// | +-- IPLROM2Device (X680x0 の IPLROM 前半) -// | +-- CGROMDevice (X680x0 の CGROM) -// | | -// | | +-------------------+ -// | +--| LunaPROMEmuDevice | (LUNA PROM エミュレーションの共通部分) -// | +-------------------+ -// | | -// | +-- Luna1PROMEmuDevice (LUNA-I の PROM エミュレーション) -// | +-- Luna88kPROMEmuDevice (LUNA-88K の PROM エミュレーション) -// | -// +-- PROM0Device (LUNA* のブートページ切り替え用プロキシ) -// +-- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ) +// | +// +- ROMDevice (LoadROM()、ウェイト、マスク等を持つ) +// | +- PROMDevice (LUNA* の PROM) +// | +- IPLROM1Device (X680x0 の IPLROM 後半) +// | +- IPLROM2Device (X680x0 の IPLROM 前半) +// | +- CGROMDevice (X680x0 の CGROM) +// | | +// | +- ROMEmuDevice +// | | +// | | +-------------------+ +// | +-| LunaPROMEmuDevice | (LUNA PROM エミュレーションの共通部分) +// | | +-------------------+ +// | | | +// | | +- Luna1PROMEmuDevice (LUNA-I の PROM エミュレーション) +// | | +- Luna88kPROMEmuDevice (LUNA-88K の PROM エミュレーション) +// | +- NewsROMEmuDevice (NEWS の ROM エミュレーション) +// | +- ROM30EmuDevice (X68030 の ROM30 エミュレーション) +// | +- Virt68kROMEmuDevice (virt-m68k の IPLROM 相当の何か) +// | +// +- PROM0Device (LUNA* のブートページ切り替え用プロキシ) +// +- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ) #include "romemu_luna.h" #include "bt45x.h" +#include "builtinrom.h" +#include "lance.h" #include "lunafb.h" #include "mainapp.h" #include "mainram.h" @@ -36,6 +42,9 @@ #include "mpu.h" #include "pio.h" #include "prom.h" +#include "rtc.h" +#include "scsidev.h" +#include "scsidomain.h" #include "sio.h" #include "spc.h" #include "ufs.h" @@ -57,11 +66,8 @@ LunaPROMEmuDevice::~LunaPROMEmuDevice() bool LunaPROMEmuDevice::Init() { - if (inherited::Init() == false) { - return false; - } - bt45x = GetBT45xDevice(); + lance = GetLanceDevice(); lunafb = GetLunafbDevice(); mainram = GetMainRAMDevice(); nvram = GetMK48T02Device(); @@ -71,77 +77,196 @@ LunaPROMEmuDevice::Init() return true; } +// ROMIO から読み出す。 +// ROMIO が応答すべきでなければ (uint64)-1 を返す。 uint64 -LunaPROMEmuDevice::Read32(uint32 addr) +LunaPROMEmuDevice::ReadROMIO(busaddr addr) { // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。 - // addr をマスクする前に評価すること。 - if ((mpu->GetPaddr() & 0xff000000) == baseaddr) { - switch (addr) { + if ((mpu->GetPPC() & 0xff000000) == baseaddr && addr.GetSize() == 4) { + switch (addr.Addr()) { case ROMIO_INIT: - return ROM_Init(); + ROM_Init(); + return 0; + + case ROMIO_LOAD: + entrypoint = ROM_Load(); + putlog(2, "ROMIO_LOAD entrypoint=$%08x", entrypoint); + return 0; case ROMIO_KEYIN: + putlog(2, "ROMIO_KEYIN"); ROM_Keyin(); return 0; case ROMIO_CLOSE: + putlog(2, "ROMIO_CLOSE"); ROM_Close(); return 0; case ROMIO_ENTRY: - return ROM_Entry(); + { + uint32 rv = ROM_Entry(); + putlog(2, "ROMIO_ENTRY entrypoint=$%08x", rv); + return rv; + } + + case ROMIO_AP1: + ROM_AP1(); + return 0; + + case ROMIO_AP2: + ROM_AP2(); + return 0; + + case ROMIO_CLKCNT: + return clkcnt; + + case ROMIO_QUITSTOP: + return quitstop; default: break; } } - return inherited::Read32(addr); + return (uint64)-1; } -// デバイスの初期化の共通部分。(継承クラスの ROM_Init() から呼ぶ) -void -LunaPROMEmuDevice::InitDevice() +// ROMIO に書き込む。 +// 反応すれば true を返す。そうでなければ false を返す。 +bool +LunaPROMEmuDevice::WriteROMIO(busaddr addr, uint32 data) { - auto pio1 = GetPIO1Device(); + // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。 + if ((mpu->GetPPC() & 0xff000000) == baseaddr && addr.GetSize() == 4) { + switch (addr.Addr()) { + case ROMIO_CLKCNT: + clkcnt = data; + return true; + + case ROMIO_QUITSTOP: + quitstop = data; + return true; - // 初期パレット - // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。 - uint8 val = 0xff; - bt45x->Write(0, 0); // select palette - for (int i = 0; i < 16; i++) { - bt45x->Write(1, val); // R - bt45x->Write(1, val); // G - bt45x->Write(1, val); // B - val ^= 0xff; + default: + break; + } } + return false; +} + +// ROM 処理の初期化。 +void +LunaPROMEmuDevice::ROM_Init() +{ + // 初期パレット + InitPalette(); + // SCSI // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。 - spc->Write(SPC::BDID, 7); + spc->WritePort(SPC::BDID, 7); + + // LANCE。 + lance->WritePort(0, 0x00); // RAP=CSR0 + lance->WritePort(1, AM7990::CSR0_STOP); // キーボード(chB)を初期化 - sio->Write(3, 0x01); // CR1 - sio->Write(3, 0x10); // RX Int(all char) - sio->Write(3, 0x04); // CR4 - sio->Write(3, 0x44); // Stop 1bit - sio->Write(3, 0x03); // CR3 - sio->Write(3, 0xc1); // RX Enable, 8bit - sio->Write(3, 0x05); // CR5 - sio->Write(3, 0x68); // TX Enable, 8bit + sio->WritePort(3, 0x01); // CR1 + sio->WritePort(3, 0x10); // RX Int(all char) + sio->WritePort(3, 0x04); // CR4 + sio->WritePort(3, 0x44); // Stop 1bit + sio->WritePort(3, 0x03); // CR3 + sio->WritePort(3, 0xc1); // RX Enable, 8bit + sio->WritePort(3, 0x05); // CR5 + sio->WritePort(3, 0x68); // TX Enable, 8bit // LED を(明示的に)オフ、マウスサンプリングをオフ - sio->Write(2, 0x00); - sio->Write(2, 0x01); - sio->Write(2, 0x20); + sio->WritePort(2, 0x00); + sio->WritePort(2, 0x01); + sio->WritePort(2, 0x20); // 電源オフをキャンセル。 // 実 PROM では、リセット後に PIO のモードセットを行っている。 // モードセットを行うと PortC のビットが %0 になる = 電源オフなので、 // 即座にこれをキャンセルしている。 // ROMEmu ではモードセットを行っていないが、キャンセルだけ発行しておく。 - pio1->Write(3, 0x09); + auto pio1 = GetPIO1Device(); + pio1->WritePort(3, 0x09); + + // 機種別の初期化 + ROM_InitMD(); + + // スクリーンを初期化 + InitScreen(); +} + +// 起動時の実行ファイル読み込み処理を行う。 +// 次段実行ファイルの読み込みに成功すれば execute を true にして、 +// エントリポイントを返す。 +// そうでなければ -1 を返す。 +uint32 +LunaPROMEmuDevice::ROM_Load() +{ + uint32 entry; + + entry = -1; + execute = false; + + // 表示環境が整ったところで NVRAM を照合 + if (VerifyNVRAM()) { + // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、 + // これを真似する。 + auto pio0 = GetPIO0Device(); + if (pio0->IsDIPSW11Up()) { + entry = LoadFile(""); + if (entry != -1) { + execute = true; + return entry; + } + } + } else { + // LUNA-88K の PROM 1.20 は初期化するかどうか確認してくるけど、 + // とりあえず。 + InitNVRAM(); + errmsg = "NVRAM Initialized."; + // NVRAM をクリアしたら DIPSW によらずプロンプトに降りる + } + + PrintTitle(); + if (errmsg.empty() == false) { + Print("** %s\n\n", errmsg.c_str()); + } + ClearPrompt(); + + return entry; +} + +// 実行ファイルに処理を移すならそのエントリポイントを返す。 +// そうでなければ -1 を返す。 +uint32 +LunaPROMEmuDevice::ROM_Entry() +{ + if (execute == false) { + return -1; + } + return entrypoint; +} + +// パレットを初期化 +void +LunaPROMEmuDevice::InitPalette() +{ + // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。 + uint8 val = 0xff; + bt45x->WritePort(0, 0); // select palette + for (int i = 0; i < 16; i++) { + bt45x->WritePort(1, val); // R + bt45x->WritePort(1, val); // G + bt45x->WritePort(1, val); // B + val ^= 0xff; + } } // スクリーンを初期化 @@ -154,6 +279,7 @@ LunaPROMEmuDevice::InitScreen() screen_h = 32; cursor_x = 0; cursor_y = 0; + cursor_on = true; // 表示範囲はざっくりセンタリングする origin_px = (1280 - screen_w * 12) / 2; @@ -164,6 +290,8 @@ LunaPROMEmuDevice::InitScreen() // 必要はなくなったが、依然このほうがアクセス効率はいいので。 origin_px = (origin_px / 48) * 48; + cursor_on = true; + inputbuf.clear(); inputpos = 0; is_shift = false; @@ -172,9 +300,9 @@ LunaPROMEmuDevice::InitScreen() // タイトルを出力 void -LunaPROMEmuDevice::PrintTitle(const char *machine_name) +LunaPROMEmuDevice::PrintTitle() { - Print("NONO %d.%d.%d Emulated ROM Monitor for %s\n\n", + Print("NONO %u.%u.%u Emulated ROM Monitor for %s\n\n", NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, machine_name); } @@ -185,11 +313,38 @@ LunaPROMEmuDevice::Putc(uint ch) uint px = origin_px + cursor_x * 12; uint py = origin_py + cursor_y * 24; - // カーソル位置の反転を元に戻す - lunafb->EmuPutc(px, py, ' ', ROP::INV2); + if (cursor_on) { + // カーソル位置の反転を元に戻す + lunafb->EmuPutc(px, py, ' ', ROP::INV2); + } - if (ch == LF) { - put_LF: + // いくつかの制御コードを勝手に使う。 + switch (ch) { + case 0x0f: // SI + bold = true; + break; + case 0x0e: // SO + bold = false; + break; + + case 0x10: // カラーコード変更 + lunafb->SetBMSEL(0); + break; + case 0x11: + lunafb->SetBMSEL(1); + break; + case 0x12: + lunafb->SetBMSEL(3); + break; + case 0x13: + lunafb->SetBMSEL(5); + break; + + case CR: + break; + + case LF: + put_LF: cursor_x = 0; if (cursor_y < screen_h - 1) { cursor_y++; @@ -199,34 +354,56 @@ LunaPROMEmuDevice::Putc(uint ch) // 最終行を消す lunafb->EmuFill(0, py, 1280, 24, 0); } - } else if (ch == LEFT) { + break; + + case LEFT: if (cursor_x > 0) { cursor_x--; } else { cursor_y--; cursor_x = screen_w - 1; } - } else if (ch == RIGHT) { + break; + + case RIGHT: if (cursor_x < screen_w - 1) { cursor_x++; } else { cursor_y++; cursor_x = 0; } - } else { - if (cursor_x < screen_w - 1) { + break; + + default: + if (ch < 0x80) { lunafb->EmuPutc(px, py, ch); - cursor_x++; + if (bold) { + for (int i = 1; i < 2; i++) { + lunafb->EmuPutc(px + i, py, ch, ROP::OR1); + } + } } else { - lunafb->EmuPutc(px, py, ch); + lunafb->EmuPutc(px, py, ' '); + lunafb->EmuPutc(px, py + 10, ch); + if (bold) { + for (int i = 1; i < 2; i++) { + lunafb->EmuPutc(px + i, py + 10, ch, ROP::OR1); + } + } + } + cursor_x++; + if (cursor_x >= screen_w) { goto put_LF; } + break; } - // カーソル位置を再計算して描画 (文字を反転) - px = origin_px + cursor_x * 12; - py = origin_py + cursor_y * 24; - lunafb->EmuPutc(px, py, ' ', ROP::INV2); + if (cursor_on) { + // カーソル位置を再計算して描画 (文字を反転) + px = origin_px + cursor_x * 12; + py = origin_py + cursor_y * 24; + lunafb->EmuPutc(px, py, ' ', ROP::INV2); + } } // 文字列 s を出力 @@ -262,6 +439,13 @@ LunaPROMEmuDevice::ROM_Close() // SIO のバッファにデータが残っていると、NetBSD のブートローダが // 起動してこないようだ。リセットして RXEN を落としておけば大丈夫ぽい。 sio->ResetHard(false); + + // 実行ファイルにジャンプ後、NMI でプロンプトにもう一度戻ってこれるので + // その時のためにエントリポイント等も初期化しておく。 + entrypoint = -1; + execute = false; + clkcnt = 0; + quitstop = 0; } // ROM プロンプトのキー入力。 @@ -273,7 +457,7 @@ LunaPROMEmuDevice::ROM_Keyin() { int asciicode = Getc(); - if (asciicode != -1) { + if (cursor_on && asciicode != -1) { ProcessChar(asciicode); } } @@ -285,14 +469,14 @@ int LunaPROMEmuDevice::Getc() { // コントロールレジスタポインタを確実に 0 にするため読み捨てる - sio->Read(3); + sio->ReadPort(3); - uint8 sr0 = sio->Read(3); + uint8 sr0 = sio->ReadPort(3); if ((sr0 & MPSCC::SR0_RXAVAIL) == 0) { return -1; } - uint32 lunakey = sio->Read(2); + uint32 lunakey = sio->ReadPort(2); // マウス入力の 2, 3バイト目は無視 if (mousecnt > 0) { @@ -421,7 +605,7 @@ LunaPROMEmuDevice::CalcNVRAMCsum() const { uint8 eor = 0; for (uint32 addr = 0x20; addr < 0x560; addr++) { - eor ^= nvram->Peek(addr); + eor ^= nvram->PeekPort(addr); } return eor; } @@ -432,9 +616,9 @@ LunaPROMEmuDevice::WriteNVRAMCsum() { uint8 eor = CalcNVRAMCsum(); - nvram->Write(0x001c, eor); - nvram->Write(0x001d, eor); - nvram->Write(0x001e, eor); + nvram->WritePort(0x001c, eor); + nvram->WritePort(0x001d, eor); + nvram->WritePort(0x001e, eor); } // NVRAM のマジックとチェックサムを照合する @@ -447,15 +631,35 @@ LunaPROMEmuDevice::VerifyNVRAM() const uint8 eor = CalcNVRAMCsum(); for (uint32 addr = 0x1c; addr < 0x1f; addr++) { - if (nvram->Peek(addr) != eor) { + if (nvram->PeekPort(addr) != eor) { return false; } } return true; } +static const uint8 __unused test0[] = { + 0x12, 0x0f, 0x4e, 0x4f, 0x4e, 0x4f, 0x0e, 0x1e, 0x13, 0x00, +}; +static const uint8 __unused test1[] = { + 0xcd, 0xd5, 0xcc, 0xd4, 0xc9, 0xad, 0xd2, 0xc9, 0xd3, 0xc3, + 0x20, 0xd7, 0xcf, 0xd2, 0xcb, 0xd3, 0xd4, 0xc1, 0xd4, 0xc9, + 0xcf, 0xce, 0x20, 0x11, 0x0f, 0x4c, 0x55, 0x4e, 0x41, 0x2d, + 0x38, 0x38, 0x4b, 0x00, 0xaa, 0x12, 0x43, 0x58, 0xc2, 0x00, +}; +static const uint8 __unused test2[] = { + 0xc8, 0xcf, 0xcc, 0xcf, 0xce, 0xc9, 0xc3, 0x20, 0xd7, 0xcf, + 0xd2, 0xcb, 0xd3, 0xd4, 0xc1, 0xd4, 0xc9, 0xcf, 0xce, 0x20, + 0x11, 0x0f, 0x4c, 0x55, 0x4e, 0x41, 0x00, 0x21, 0xd8, 0x67, +}; +static const uint8 __unused test3[] = { + 0x0e, 0x1e, 0x13, 0xd3, 0xc5, 0xd2, 0xc9, 0xc5, 0xd3, 0x0a, + 0x00, 0x0f, 0xd0, 0x4f, 0x7e, 0x40, 0x41, 0xcc, 0xdf, 0x09, +}; + // -X オプションで指定されたホストファイルをロードする。 // ロードできればエントリポイントを返す。 +// 失敗ならエラーメッセージを errmsg にセットして (uint32)-1 を返す。 // exec_file が指定されている時に呼ぶこと。 uint32 LunaPROMEmuDevice::LoadHostFile() @@ -463,18 +667,17 @@ LunaPROMEmuDevice::LoadHostFile() assert(gMainApp.exec_file); LoadInfo info(gMainApp.exec_file); - if (mainram->LoadFromFile(&info)) { - Print("Host program loaded. Entry point = $%08x\n", info.entry); + if (BootLoader::LoadExec(mainram, &info)) { return info.entry; } else { - Print("** Couldn't load specified host program.\n"); - return 0; + errmsg = "Couldn't load specified host program."; + return -1; } } // bootinfo に従ってゲストから起動先をロードする。 -// 成功すればエントリポイントを返す。 -// 失敗ならをエラーメッセージを errmsg にセットして (uint32)-1 を返す。 +// ロードできればエントリポイントを返す。 +// 失敗ならエラーメッセージを errmsg にセットして (uint32)-1 を返す。 uint32 LunaPROMEmuDevice::LoadGuestFile(const bootinfo_t& bootinfo) { @@ -483,8 +686,8 @@ LunaPROMEmuDevice::LoadGuestFile(const b const std::string& dkfile = bootinfo.dkfile; // ユニット (今は SCSI ID=6 決め打ち) - int id = 6; - SCSITarget *target = spc->GetTarget(id); + uint id = 6; + SCSITarget *target = spc->GetDomain()->GetTarget(id); if (target == NULL) { errmsg = "No bootable disks"; putmsg(0, "%s", errmsg.c_str()); @@ -497,13 +700,13 @@ LunaPROMEmuDevice::LoadGuestFile(const b } SCSIDisk *hd = dynamic_cast(target); - putmsg(2, "%s SCSIHD[%d] found", __func__, id); + putmsg(2, "%s SCSIHD[%u] found", __func__, id); // +0 セクタ目(512byte)に Sun disklabel scd_dk_label dk; if (hd->Peek(&dk, 0, sizeof(dk)) == false) { - putmsg(0, "SCSIHD[%d] ReadSector failed", id); - errmsg = string_format("dk%d Read disklabel failed.", dkunit); + putmsg(0, "SCSIHD[%u] ReadSector failed", id); + errmsg = string_format("dk%u Read disklabel failed.", dkunit); return -1; } @@ -536,7 +739,7 @@ LunaPROMEmuDevice::LoadGuestFile(const b // ファイルシステムをオープン (マウントっぽいイメージ) Filesys fsys(this); if (fsys.Mount(hd, part_start, part_size) == false) { - errmsg = string_format("dk%d,%c Open ffs failed: %s", + errmsg = string_format("dk%u,%c Open ffs failed: %s", dkunit, dkpart + 'a', fsys.errstr.c_str()); putmsg(0, "%s: %s", __func__, errmsg.c_str()); return -1; @@ -545,7 +748,7 @@ LunaPROMEmuDevice::LoadGuestFile(const b // ファイルを探してオープンする inodefile f(this); if (fsys.OpenFile(f, dkfile) == false) { - errmsg = string_format("dk%d,%c,%s open failed: %s", + errmsg = string_format("dk%u,%c,%s open failed: %s", dkunit, dkpart + 'a', dkfile.c_str(), fsys.errstr.c_str()); putmsg(0, "%s: %s", __func__, errmsg.c_str()); return -1; @@ -555,18 +758,18 @@ LunaPROMEmuDevice::LoadGuestFile(const b fsys.ReadData(f); // ロード - std::string name = string_format("SCSIHD %d,%c:%s", + std::string name = string_format("SCSIHD %u,%c:%s", id, dkpart + 'a', dkfile.c_str()); LoadInfo info(name.c_str(), f.data.data(), f.data.size()); - if (mainram->LoadFromData(&info) == false) { - errmsg = string_format("dk%d,%c,%s load failed", + if (BootLoader::LoadExec(mainram, &info) == false) { + errmsg = string_format("dk%u,%c,%s load failed", dkunit, dkpart + 'a', dkfile.c_str()); putmsg(0, "%s: %s", __func__, errmsg.c_str()); return -1; } if (info.entry == -1) { - errmsg = string_format("dk%d,%c,%s not executable", + errmsg = string_format("dk%u,%c,%s not executable", dkunit, dkpart + 'a', dkfile.c_str()); putmsg(0, "%s: %s", __func__, errmsg.c_str()); return -1; @@ -575,6 +778,87 @@ LunaPROMEmuDevice::LoadGuestFile(const b return info.entry; } +void +LunaPROMEmuDevice::ROM_AP1() +{ + char buf[100]; + int n; + + cursor_on = false; + + static uint8 ap1pal[] = { + 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xf8, + 0, 0, 0, + 0xf8, 0xf8, 0x00, + 0, 0, 0, + 0xf8, 0xf8, 0xf8, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0x00, 0x00, 0x00, + 0xf8, 0xf8, 0xf8 + }; + bt45x->WritePort(0, 0); + for (int i = 0; i < countof(ap1pal); i++) { + bt45x->WritePort(1, ap1pal[i]); + } + + Print(std::string((const char *)test0)); + if (gMainApp.IsLUNA88K()) { + Print(std::string((const char *)test1)); + } else { + Print(std::string((const char *)test2)); + } + Print(std::string((const char *)test3)); + + n = strlcpy(buf, (const char *)&Builtin::IPLROM30[0x111e3], sizeof(buf)); + snprintf(buf + n, sizeof(buf) - n, "%d.%d.%d '%02d.%02d.%02d", + NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, + GetRTCDevice()->GetYear() % 100, 4, 1); + Print("%s\n", buf); + + memcpy(buf, &Builtin::IPLROM30[0x111f5], 0x28); + memcpy(buf + 0x1e, mpu->GetMPUName(), 7); + n = 0x27; + n += snprintf(buf + n, sizeof(buf) - n, "%4.1f", + (double)mpu->GetClockSpeed() / 1000); + strlcpy(buf + n, (const char *)&Builtin::IPLROM30[0x1121f], + sizeof(buf) - n); + Print(std::string(buf)); + + strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11226], sizeof(buf)); + if (gMainApp.Has(VMCap::M88K)) { + memcpy(buf + 0x1e, mpu->GetMPUName(), 7); + memcpy(buf + 0x25, &Builtin::IPLROM30[0x11251], 3); + } + Print(std::string(buf)); + + strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11254], sizeof(buf)); + buf[0x0c] = 'e'; + if (gMainApp.Has(VMCap::M88K)) { + memcpy(buf + 0x1e, mpu->GetMPUName(), 7); + memcpy(buf + 0x25, &Builtin::IPLROM30[0x1127d], 3); + buf[0x22] = 0x32; + } + Print(std::string(buf)); + + strlcpy(buf, (const char *)&Builtin::IPLROM30[0x11280], sizeof(buf)); + n = 0x1e; + n += snprintf(buf + n, sizeof(buf) - n, "%u", mainram->GetSizeMB()); + strlcpy(buf + n, (const char *)&Builtin::IPLROM30[0x1129f], + sizeof(buf) - n); + Print(std::string(buf)); +} + +void +LunaPROMEmuDevice::ROM_AP2() +{ + InitPalette(); + InitScreen(); +} + // キーコードから文字への変換 /*static*/ const uint8 LunaPROMEmuDevice::lunakey2asciitable[] = {