--- nono/vm/romemu_luna.cpp 2026/04/29 17:05:03 1.1.1.11 +++ nono/vm/romemu_luna.cpp 2026/04/29 17:06:01 1.1.1.20 @@ -4,40 +4,55 @@ // Licensed under nono-license.txt // +// // LUNA シリーズの ROM エミュレーション共通部分 // + // 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 のブートページ切り替え用プロキシ) +// | +// +- 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 "bt454.h" +#include "bt45x.h" +#include "builtinrom.h" +#include "dipsw.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 "rtc.h" +#include "scsidev.h" +#include "scsidomain.h" #include "sio.h" #include "spc.h" #include "ufs.h" // コンストラクタ LunaPROMEmuDevice::LunaPROMEmuDevice() - : inherited("PROM") + : inherited(OBJ_PROM) { // LUNA では ROM への書き込みは何も起きない write_op = 0; @@ -48,88 +63,448 @@ 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; } +// ROMIO から読み出す。 +// ROMIO が応答すべきでなければ (uint64)-1 を返す。 uint64 -LunaPROMEmuDevice::Read32(uint32 addr) +LunaPROMEmuDevice::ReadROMIO(busaddr addr) { // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える。 - // addr をマスクする前に評価すること。 - if ((gMPU->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_KEYIN: - ROM_Keyin(); + case ROMIO_LOAD: + entrypoint = ROM_Load(); + putlog(2, "ROMIO_LOAD entrypoint=$%08x", entrypoint); + return 0; + + case ROMIO_SIOINTR: + putlog(2, "ROMIO_SIOINTR"); + ROM_SIOIntr(); 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() から呼ぶ) +// ROMIO に書き込む。 +// 反応すれば true を返す。そうでなければ false を返す。 +bool +LunaPROMEmuDevice::WriteROMIO(busaddr addr, uint32 data) +{ + // 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; + + default: + break; + } + } + + return false; +} + +// ROM 処理の初期化。 void -LunaPROMEmuDevice::InitDevice() +LunaPROMEmuDevice::ROM_Init() { // 初期パレット - // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。 - uint8 val = 0xf0; - gBT454->Write(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; - } + InitPalette(); // SCSI // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。 - gSPC->Write(SPC::BDID, 7); + spc->WritePort(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 + // LANCE。 + lance->WritePort(0, 0x00); // RAP=CSR0 + lance->WritePort(1, AM7990::CSR0_STOP); + + // DIP-SW の #1-2 が down(=false) ならシリアル。 + auto dipsw = GetDipswDevice(); + use_serial = (dipsw->Get(1) == false); + + if (use_serial) { + // シリアル(chA)を初期化 + sio->WritePort(1, 0x01); // CR1 + sio->WritePort(1, 0x12); // RX Int(all char) | TXIntEnable + sio->WritePort(1, 0x04); // CR4 + sio->WritePort(1, 0x44); // Stop 1bit + sio->WritePort(1, 0x03); // CR3 + sio->WritePort(1, 0xc1); // RX Enable, 8bit + sio->WritePort(1, 0x05); // CR5 + sio->WritePort(1, 0x68); // TX Enable, 8bit + + sio_txq.Clear(); + } else { + // キーボード(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() +{ + 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; + } } // スクリーンを初期化 void -LunaPROMEmuDevice::InitScreen(const char *machine_name) +LunaPROMEmuDevice::InitScreen() { - text.Init(80, 32, TextScreen::Console); - gLunafb->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() +{ + // シリアルモードなら、画面に何か出しておく。 + // 実機 PROM は、LUNA-I (4.22) はカーソルが出るだけ、 + // LUNA-88K (1.20) は " Use UART-A.\n" が表示される。 + if (use_serial) { + PrintDisplay("Use HostCom0.\n"); + } + + 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); +} + +// 1文字出力 +void +LunaPROMEmuDevice::Putc(uint ch) +{ + if (use_serial) { + PutcSerial(ch); + } else { + PutcDisplay(ch); + } +} + +// シリアルコンソールに1文字出力。 +// 実際にはキューに1文字追加して、キュー先頭の1文字を可能なら送信する。 +void +LunaPROMEmuDevice::PutcSerial(uint ch) +{ + sio_txq.Enqueue(ch); + ProcessSerial(); +} + +// シリアルコンソールにキュー先頭の1文字を可能なら送信する。 +void +LunaPROMEmuDevice::ProcessSerial() +{ + // コントロールレジスタポインタを確実に 0 にするため読み捨てる。 + sio->ReadPort(1); + + // 送信完了なら割り込みを下ろす。 + uint sr0 = sio->ReadPort(1); + if ((sr0 & MPSCC::SR0_INTPEND)) { + sio->WritePort(1, 5 << 3); // Reset TXIntr Pending + } + + if ((sr0 & MPSCC::SR0_TXEMPTY)) { + uint8 ch; + if (sio_txq.Dequeue(&ch)) { + sio->WritePort(0, ch); + } + } +} + +// 画面に1文字出力。 +void +LunaPROMEmuDevice::PutcDisplay(uint ch) +{ + uint px = origin_px + cursor_x * 12; + uint py = origin_py + cursor_y * 24; + + if (cursor_on) { + // カーソル位置の反転を元に戻す + lunafb->EmuPutc(px, py, ' ', ROP::INV2); + } + + // いくつかの制御コードを勝手に使う。 + 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++; + } 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); + } +} + +// 文字列 s を画面に出力。 +void +LunaPROMEmuDevice::PrintDisplay(const char *s) +{ + for (; *s; s++) { + PutcDisplay(*s); + } } // ROM 処理のクローズ @@ -139,64 +514,129 @@ LunaPROMEmuDevice::ROM_Close() // SIO をリセットしておく。 // SIO のバッファにデータが残っていると、NetBSD のブートローダが // 起動してこないようだ。リセットして RXEN を落としておけば大丈夫ぽい。 - gSIO->ResetHard(); + sio->ResetHard(false); + + // 実行ファイルにジャンプ後、NMI でプロンプトにもう一度戻ってこれるので + // その時のためにエントリポイント等も初期化しておく。 + entrypoint = -1; + execute = false; + clkcnt = 0; + quitstop = 0; +} + +// SIO 割り込みハンドラ。 +// シリアル送信の進行と、シリアルもしくはキーボード(どちらもSIO)からの入力を +// 受け取って、コマンド処理を進行させる。 +// コマンド処理の進行は、キー入力で行を構成し、行が完成すればそのコマンドを +// 実行する、実行結果により出力(画面もしくはシリアル)を行ったり、メンバ変数を +// 更新したり、までを含む。要するにここがメインループみたいなところ。 +void +LunaPROMEmuDevice::ROM_SIOIntr() +{ + // シリアル送信を進行。 + if (use_serial) { + ProcessSerial(); + } - // コンソールクローズ - gLunafb->EmuConsoleClose(); + // 入力を処理。 + int asciicode = Getc(); + if (cursor_on && asciicode != -1) { + ProcessChar(asciicode); + } } -// ROM プロンプトのキー入力。 -// ゲストコードの割り込みハンドラから呼ばれて、キー入力で行を構成し、 -// あるいは行が完成すればコマンドを実行する。 -// 実行結果により画面を更新したりメンバ変数を更新する。 -void -LunaPROMEmuDevice::ROM_Keyin() +// キー入力。 +int +LunaPROMEmuDevice::Getc() +{ + int asciicode; + + if (use_serial) { + asciicode = GetcSerial(); + } else { + asciicode = GetcKeyboard(); + } + + return asciicode; +} + +// シリアルポートからのキー入力。 +int +LunaPROMEmuDevice::GetcSerial() { - uint32 lunakey = gSIO->Read(2); + // コントロールレジスタポインタを確実に 0 にするため読み捨てる + sio->ReadPort(1); + + uint8 sr0 = sio->ReadPort(1); + if ((sr0 & MPSCC::SR0_RXAVAIL) == 0) { + return -1; + } + + int asciicode = sio->ReadPort(0); + return asciicode; +} + +// キーボードからのキー入力。 +// 押されているキーの ASCII コードを返す。 +// キー入力が無いときは -1 を返す。 +int +LunaPROMEmuDevice::GetcKeyboard() +{ + // コントロールレジスタポインタを確実に 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; } // キーコードから文字を取得 - char asciicode; + int asciicode; if (is_shift) { asciicode = lunakey2shifttable[lunakey]; } else { asciicode = lunakey2asciitable[lunakey]; } - if (asciicode != 0) { - GetChar(asciicode); + if (asciicode == 0) { + return -1; } + + return asciicode; } // キー入力ハンドラ (キー入力割り込みによって呼ばれる)。 // 引数 asciicode は概ね ASCII コード (ただし 0x1c..0x1f が上下左右)。 void -LunaPROMEmuDevice::GetChar(char asciicode) +LunaPROMEmuDevice::ProcessChar(char asciicode) { - // LUNA1 の場合、 + // LUNA-I の場合、 // 実機 PROM にはカーソル左右移動の機能はなく、カーソルは常に行末にいる。 // そのためかどうか Delete も BackSpace と同じく1文字前を消す動作をして // いる。カーソル位置に文字はないので、Delete をそうするのはまあ分からん @@ -209,15 +649,21 @@ LunaPROMEmuDevice::GetChar(char asciicod 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 LF: - // 改行を起こす前にカーソル(ただの反転)を戻しておく必要がある - text.SetAttr(TA::Normal); // 改行 - text.Putc(asciicode); - prompt_y = text.GetY(); + Putc(asciicode); + prompt_y = cursor_y; // コマンド実行 Command(); // プロンプトを更新 @@ -227,11 +673,13 @@ LunaPROMEmuDevice::GetChar(char asciicod case LEFT: if (inputpos > 0) { inputpos--; + Putc(LEFT); } break; case RIGHT: if (inputpos < inputbuf.length()) { inputpos++; + Putc(RIGHT); } break; @@ -242,63 +690,27 @@ LunaPROMEmuDevice::GetChar(char asciicod default: // 通常文字のはず inputbuf.insert(inputbuf.begin() + inputpos, asciicode); + for (int i = inputpos; i < inputbuf.size(); i++) { + Putc(inputbuf[i]); + } + for (int i = 0; i < inputbuf.size() - inputpos - 1; i++) { + Putc(LEFT); + } inputpos++; break; } - ConsoleUpdate(); } // 入力バッファを初期化する。 void LunaPROMEmuDevice::ClearPrompt() { - prompt_y = text.GetY(); + prompt_y = cursor_y; // 入力行をクリア inputbuf.clear(); inputpos = 0; -} - -// 画面を更新 -void -LunaPROMEmuDevice::ConsoleUpdate() -{ - 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); - - // 画面更新 - gLunafb->EmuConsoleUpdate(); + Print(prompt); } // NVRAM のチェックサムを返す @@ -307,7 +719,7 @@ LunaPROMEmuDevice::CalcNVRAMCsum() const { uint8 eor = 0; for (uint32 addr = 0x20; addr < 0x560; addr++) { - eor ^= nvram->Peek(addr); + eor ^= nvram->PeekPort(addr); } return eor; } @@ -318,9 +730,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 のマジックとチェックサムを照合する @@ -333,34 +745,53 @@ 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() { assert(gMainApp.exec_file); - uint32 entry = gRAM->LoadFile(gMainApp.exec_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) { @@ -369,28 +800,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; } 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); - return 0; + putmsg(0, "SCSIHD[%u] ReadSector failed", id); + errmsg = string_format("dk%u Read disklabel failed.", dkunit); + return -1; } // +$1fc の MAGIC をチェック @@ -399,7 +830,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; } // チェックサム照合は手抜きで省略 @@ -422,35 +853,124 @@ 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, + 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]); + } + + PrintDisplay((const char *)test0); + if (gMainApp.IsLUNA88K()) { + PrintDisplay((const char *)test1); + } else { + PrintDisplay((const char *)test2); + } + PrintDisplay((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\n", + NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, + GetRTCDevice()->GetYear() % 100, 4, 1); + PrintDisplay(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); + PrintDisplay(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); + } + PrintDisplay(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; + } + PrintDisplay(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); + PrintDisplay(buf); +} + +void +LunaPROMEmuDevice::ROM_AP2() +{ + InitPalette(); + InitScreen(); } // キーコードから文字への変換 @@ -468,10 +988,10 @@ 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[] = { @@ -487,8 +1007,8 @@ LunaPROMEmuDevice::lunakey2shifttable[] '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, '*', '/', '=', ',', };