--- 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:43 1.1.1.18 @@ -4,34 +4,56 @@ // 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) +// | | +// | +- 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 "bitmap.h" -#include "bt454.h" +#include "bt45x.h" +#include "builtinrom.h" +#include "lance.h" +#include "lunafb.h" #include "mainapp.h" +#include "mainram.h" #include "mk48t02.h" -#include "ram.h" +#include "mpu.h" +#include "pio.h" +#include "prom.h" +#include "scsidev.h" +#include "scsidomain.h" #include "sio.h" #include "spc.h" #include "ufs.h" // コンストラクタ LunaPROMEmuDevice::LunaPROMEmuDevice() + : inherited(OBJ_PROM) { - logname = "prom"; - devname = "PROM"; + // LUNA では ROM への書き込みは何も起きない + write_op = 0; } // デストラクタ @@ -39,163 +61,375 @@ LunaPROMEmuDevice::~LunaPROMEmuDevice() { } +// 初期化 bool LunaPROMEmuDevice::Init() { - // 都度都度 dynamic_cast は面倒なので、ここで一度代入しとく - nvram = dynamic_cast(gRTC.get()); + bt45x = GetBT45xDevice(); + lance = GetLanceDevice(); + lunafb = GetLunafbDevice(); + mainram = GetMainRAMDevice(); + nvram = GetMK48T02Device(); + sio = GetSIODevice(); + spc = GetSPCDevice(); return true; } -void -LunaPROMEmuDevice::ResetHard() -{ - is_closed = false; -} - -// アドレスデコーダ -inline uint64 -LunaPROMEmuDevice::Decoder(uint32 addr) const -{ - // 折り返しなし - return addr - baseaddr; -} - +// ROMIO から読み出す。 +// ROMIO が応答すべきでなければ (uint64)-1 を返す。 uint64 -LunaPROMEmuDevice::Read8(uint32 addr) +LunaPROMEmuDevice::ReadROMIO(busaddr addr) { - uint64 offset = Decoder(addr); - - // 謎の IO 空間はロングワードアクセスのみ - return romdata[offset & (devlen - 1)]; -} + // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。 + if ((mpu->GetPPC() & 0xff000000) == baseaddr && addr.GetSize() == 4) { + switch (addr.Addr()) { + case ROMIO_INIT: + ROM_Init(); + return 0; -uint64 -LunaPROMEmuDevice::Read16(uint32 addr) -{ - uint64 offset = Decoder(addr); + case ROMIO_LOAD: + entrypoint = ROM_Load(); + putlog(2, "ROMIO_LOAD entrypoint=$%08x", entrypoint); + return 0; - // 謎の IO 空間はロングワードアクセスのみ - return be16toh(*(uint16 *)&romdata[offset & (devlen - 1)]); -} + case ROMIO_KEYIN: + putlog(2, "ROMIO_KEYIN"); + ROM_Keyin(); + return 0; -uint64 -LunaPROMEmuDevice::Read32(uint32 addr) -{ - uint64 offset = Decoder(addr); + case ROMIO_CLOSE: + putlog(2, "ROMIO_CLOSE"); + ROM_Close(); + return 0; - // ロングワードアクセスでだけ謎の I/O 空間が見える - if (!is_closed) { - switch (offset) { - case ROMIO_INIT - baseaddr: - return ROM_Init(); + case ROMIO_ENTRY: + { + uint32 rv = ROM_Entry(); + putlog(2, "ROMIO_ENTRY entrypoint=$%08x", rv); + return rv; + } - case ROMIO_KEYIN - baseaddr: - ROM_Keyin(); + case ROMIO_AP1: + ROM_AP1(); return 0; - case ROMIO_CLOSE - baseaddr: - ROM_Close(); + case ROMIO_AP2: + ROM_AP2(); return 0; - case ROMIO_ENTRY - baseaddr: - return ROM_Entry(); + case ROMIO_CLKCNT: + return clkcnt; default: break; } } - return be32toh(*(uint32 *)&romdata[offset & (devlen - 1)]); + + return (uint64)-1; } -uint64 -LunaPROMEmuDevice::Write8(uint32 addr, uint32 data) +// ROMIO に書き込む。 +// 反応すれば true を返す。そうでなければ false を返す。 +bool +LunaPROMEmuDevice::WriteROMIO(busaddr addr, uint32 data) { - return 0; + // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。 + if ((mpu->GetPPC() & 0xff000000) == baseaddr && addr.GetSize() == 4) { + switch (addr.Addr()) { + case ROMIO_CLKCNT: + clkcnt = data; + return true; + default: + break; + } + } + + return false; } -uint64 -LunaPROMEmuDevice::Write16(uint32 addr, uint32 data) +// ROM 処理の初期化。 +void +LunaPROMEmuDevice::ROM_Init() { - return 0; -} + // 初期パレット + InitPalette(); -uint64 -LunaPROMEmuDevice::Write32(uint32 addr, uint32 data) + // SCSI + // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。 + spc->WritePort(SPC::BDID, 7); + + // LANCE。 + lance->WritePort(0, 0x00); // RAP=CSR0 + lance->WritePort(1, AM7990::CSR0_STOP); + + // キーボード(chB)を初期化 + 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->WritePort(2, 0x00); + sio->WritePort(2, 0x01); + sio->WritePort(2, 0x20); + + // 電源オフをキャンセル。 + // 実 PROM では、リセット後に PIO のモードセットを行っている。 + // モードセットを行うと PortC のビットが %0 になる = 電源オフなので、 + // 即座にこれをキャンセルしている。 + // ROMEmu ではモードセットを行っていないが、キャンセルだけ発行しておく。 + auto pio1 = GetPIO1Device(); + pio1->WritePort(3, 0x09); + + // 機種別の初期化 + ROM_InitMD(); + + // スクリーンを初期化 + InitScreen(); +} + +// 起動時の実行ファイル読み込み処理を行う。 +// 次段実行ファイルの読み込みに成功すれば execute を true にして、 +// エントリポイントを返す。 +// そうでなければ -1 を返す。 +uint32 +LunaPROMEmuDevice::ROM_Load() { - return 0; + 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; } -uint64 -LunaPROMEmuDevice::Peek8(uint32 addr) +// 実行ファイルに処理を移すならそのエントリポイントを返す。 +// そうでなければ -1 を返す。 +uint32 +LunaPROMEmuDevice::ROM_Entry() { - uint64 offset = Decoder(addr); - - return romdata[offset & (devlen - 1)]; + if (execute == false) { + return -1; + } + return entrypoint; } -// デバイスの初期化の共通部分。(継承クラスの ROM_Init() から呼ぶ) +// パレットを初期化 void -LunaPROMEmuDevice::InitDevice() +LunaPROMEmuDevice::InitPalette() { - // 初期パレット // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。 - uint8 val = 0xf0; - gBT454->Write(0, 0); // select palette + uint8 val = 0xff; + bt45x->WritePort(0, 0); // select palette for (int i = 0; i < 16; i++) { - gBT454->Write(1, val); // R - gBT454->Write(1, val); // G - gBT454->Write(1, val); // B - val ^= 0xf0; + bt45x->WritePort(1, val); // R + bt45x->WritePort(1, val); // G + bt45x->WritePort(1, val); // B + val ^= 0xff; } - - // SCSI - // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。 - gSPC->Write(SPC::BDID, 7); - - // キーボード(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, 0x05); // CR5 - gSIO->Write(3, 0x68); // TX Enable, 8bit } // スクリーンを初期化 void -LunaPROMEmuDevice::InitScreen(const char *machine_name) +LunaPROMEmuDevice::InitScreen() { - text.Init(80, 32); - gBitmap->EmuConsoleOpen(&text); + lunafb->EmuInitScreen(); + + screen_w = 80; + screen_h = 32; + cursor_x = 0; + cursor_y = 0; + cursor_on = true; + + // 表示範囲はざっくりセンタリングする + origin_px = (1280 - screen_w * 12) / 2; + origin_py = (1024 - screen_h * 24) / 2; + // 横は 48ドット境界から始めておく。 + // こうすると1(,2)文字目の24ビットが必ずワード境界から始まるため。 + // 今となっては任意の場所から文字が描画できるようになったので揃える + // 必要はなくなったが、依然このほうがアクセス効率はいいので。 + origin_px = (origin_px / 48) * 48; + + cursor_on = true; + 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() +{ + Print("NONO %u.%u.%u 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); + if (cursor_on) { + // カーソル位置の反転を元に戻す + lunafb->EmuPutc(px, py, ' ', ROP::INV2); + } - // コンソールクローズ - gBitmap->EmuConsoleClose(); + // いくつかの制御コードを勝手に使う。 + switch (ch) { + case 0x0f: // SI + bold = true; + break; + case 0x0e: // SO + bold = false; + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // カラーコード変更 + lunafb->SetBMSEL(ch - 0x10); + break; + + case CR: + break; + + case LF: + put_LF: + cursor_x = 0; + if (cursor_y < screen_h - 1) { + cursor_y++; + } else { + // 上スクロールして.. + lunafb->EmuScrollY(origin_py, origin_py + 24, (screen_h - 1) * 24); + // 最終行を消す + lunafb->EmuFill(0, py, 1280, 24, 0); + } + break; + + case LEFT: + if (cursor_x > 0) { + cursor_x--; + } else { + cursor_y--; + cursor_x = screen_w - 1; + } + break; + + case RIGHT: + if (cursor_x < screen_w - 1) { + cursor_x++; + } else { + cursor_y++; + cursor_x = 0; + } + break; + + default: + if (ch < 0x80) { + lunafb->EmuPutc(px, py, ch); + if (bold) { + for (int i = 1; i < 2; i++) { + lunafb->EmuPutc(px + i, py, ch, ROP::OR1); + } + } + } else { + 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; + } + + if (cursor_on) { + // カーソル位置を再計算して描画 (文字を反転) + px = origin_px + cursor_x * 12; + py = origin_py + cursor_y * 24; + lunafb->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 を落としておけば大丈夫ぽい。 + sio->ResetHard(false); + + // 実行ファイルにジャンプ後、NMI でプロンプトにもう一度戻ってこれるので + // その時のためにエントリポイント等も初期化しておく。 + entrypoint = -1; + execute = false; } // ROM プロンプトのキー入力。 @@ -205,120 +439,229 @@ LunaPROMEmuDevice::ROM_Close() void LunaPROMEmuDevice::ROM_Keyin() { - uint32 lunakey = gSIO->Read(2); + int asciicode = Getc(); + + if (cursor_on && asciicode != -1) { + ProcessChar(asciicode); + } +} + +// キー入力。 +// 押されているキーの ASCII コードを返す。 +// キー入力が無いときは -1 を返す。 +int +LunaPROMEmuDevice::Getc() +{ + // コントロールレジスタポインタを確実に 0 にするため読み捨てる + sio->ReadPort(3); + + uint8 sr0 = sio->ReadPort(3); + if ((sr0 & MPSCC::SR0_RXAVAIL) == 0) { + return -1; + } + + uint32 lunakey = sio->ReadPort(2); // マウス入力の 2, 3バイト目は無視 if (mousecnt > 0) { mousecnt--; - return; + return -1; } // SHIFT キーだけ状態を持つので先に処理 if (lunakey == 0x0c || lunakey == 0x0d) { // 左右SHIFT押下 is_shift = true; - return; + return -1; } if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放 is_shift = false; - return; + return -1; } // マウスデータの1バイト目なら、後続2バイトをスキップ if ((lunakey & 0xf8) == 0x80) { mousecnt = 2; - return; + return -1; } // キー開放は無視してよい if ((lunakey & 0x80)) { - return; + return -1; } - switch (lunakey) { - case 0x11: // BS - if (inputpos > 0) { - inputbuf.erase(--inputpos); + // キーコードから文字を取得 + int asciicode; + if (is_shift) { + asciicode = lunakey2shifttable[lunakey]; + } else { + asciicode = lunakey2asciitable[lunakey]; + } + if (asciicode == 0) { + return -1; + } + + return asciicode; +} + +// キー入力ハンドラ (キー入力割り込みによって呼ばれる)。 +// 引数 asciicode は概ね ASCII コード (ただし 0x1c..0x1f が上下左右)。 +void +LunaPROMEmuDevice::ProcessChar(char asciicode) +{ + // LUNA-I の場合、 + // 実機 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::ConsoleUpdate() +LunaPROMEmuDevice::ClearPrompt() { - // 入力行全体を都度更新 (面倒なので行をはみ出さないようにする) - 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(); + 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->PeekPort(addr); + } + return eor; +} + +// NVRAM のチェックサムを計算して書き込む +void +LunaPROMEmuDevice::WriteNVRAMCsum() +{ + uint8 eor = CalcNVRAMCsum(); + + nvram->WritePort(0x001c, eor); + nvram->WritePort(0x001d, eor); + nvram->WritePort(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->PeekPort(addr) != eor) { + return false; + } + } + return true; } -// -A オプションで指定されたホストファイルをロードする。 +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 オプションで指定されたホストファイルをロードする。 // ロードできればエントリポイントを返す。 -// host_file が指定されている時に呼ぶこと。 +// 失敗ならエラーメッセージを errmsg にセットして (uint32)-1 を返す。 +// exec_file が指定されている時に呼ぶこと。 uint32 LunaPROMEmuDevice::LoadHostFile() { - assert(gMainApp.host_file); + assert(gMainApp.exec_file); - uint32 entry = gRAM->LoadFile(gMainApp.host_file); - if (entry) { - text.Print("Host program loaded. Entry point = $%08x\n", - entry); + LoadInfo info(gMainApp.exec_file); + if (BootLoader::LoadExec(mainram, &info)) { + return info.entry; } else { - text.Print("** Couldn't load specified host program.\n"); + errmsg = "Couldn't load specified host program."; + return -1; } - return entry; } // bootinfo に従ってゲストから起動先をロードする。 -// 成功すればエントリポイントを返す。 -// 失敗ならをエラーメッセージを errmsg にセットして 0 を返す。 +// ロードできればエントリポイントを返す。 +// 失敗ならエラーメッセージを errmsg にセットして (uint32)-1 を返す。 uint32 LunaPROMEmuDevice::LoadGuestFile(const bootinfo_t& bootinfo) { @@ -327,28 +670,28 @@ LunaPROMEmuDevice::LoadGuestFile(const b const std::string& dkfile = bootinfo.dkfile; // ユニット (今は SCSI ID=6 決め打ち) - int id = 6; - SCSITarget *target = gSPC->GetTarget(id); + uint id = 6; + SCSITarget *target = spc->GetDomain()->GetTarget(id); if (target == NULL) { errmsg = "No bootable disks"; putmsg(0, "%s", errmsg.c_str()); - return 0; + return -1; } if (target->GetDevType() != SCSI::DevType::HD) { errmsg = "No bootable disks"; putmsg(0, "%s", errmsg.c_str()); - return 0; + return -1; } - SCSIHD *hd = dynamic_cast(target); - putmsg(2, "%s SCSIHD[%d] found", __func__, id); + SCSIDisk *hd = dynamic_cast(target); + putmsg(2, "%s SCSIHD[%u] found", __func__, id); // +0 セクタ目(512byte)に Sun disklabel scd_dk_label dk; - if (hd->PeekImage(&dk, 0, sizeof(dk)) == false) { - putmsg(0, "SCSIHD[%d] ReadSector failed", id); - errmsg = string_format("dk%d Read disklabel failed.", dkunit); - return 0; + if (hd->Peek(&dk, 0, sizeof(dk)) == false) { + putmsg(0, "SCSIHD[%u] ReadSector failed", id); + errmsg = string_format("dk%u Read disklabel failed.", dkunit); + return -1; } // +$1fc の MAGIC をチェック @@ -357,7 +700,7 @@ LunaPROMEmuDevice::LoadGuestFile(const b putmsg(0, "SCSIHD[%d] Bad magic 0x%04x (!= 0x%x)", id, magic, scd_dk_label::DKL_MAGIC); errmsg = string_format("dk%d Bad disklabel magic.", dkunit); - return 0; + return -1; } // チェックサム照合は手抜きで省略 @@ -380,44 +723,128 @@ 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 0; + return -1; } // ファイルを探してオープンする 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 0; + return -1; } // ファイル本体を読み込み fsys.ReadData(f); // ロード - uint32 entry; - std::string name = string_format("SCSIHD %d,%c:%s", + std::string name = string_format("SCSIHD %u,%c:%s", id, dkpart + 'a', dkfile.c_str()); - entry = gRAM->LoadFile(name.c_str(), f.data.data(), f.data.size()); - if (entry == 0) { - errmsg = string_format("dk%d,%c,%s not executable", + LoadInfo info(name.c_str(), f.data.data(), f.data.size()); + 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; } - return entry; + if (info.entry == -1) { + 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; + } + + return info.entry; +} + +void +LunaPROMEmuDevice::ROM_AP1() +{ + char buf[100]; + int n; + + cursor_on = false; + + static uint8 ap1pal[] = { + 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xf8, + 0xf8, 0xf8, 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 %s", + NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE); + for (int i = 0; buf[i]; i++) { + if (buf[i] == '/') + buf[i] = '.'; + } + 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[] = { - 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 +853,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, '*', '/', '=', ',', };