--- nono/vm/prom.cpp 2026/04/29 17:04:53 1.1.1.6 +++ nono/vm/prom.cpp 2026/04/29 17:05:37 1.1.1.16 @@ -4,31 +4,137 @@ // Licensed under nono-license.txt // -// PROM (LUNA-I, LUNA88K 共通) +// +// PROM (LUNA-I, LUNA-88K 共通) +// -// IODevice -// | -// +----------------------------------------+ -// | | -// v v -// ROMDevice Luna*PROMEmuDeivce -// | (ROM エミュレーションはここ) -// +---------+---------------+ -// v v -// PROMDevice CGROMDevice など -// (LUNA* の実ROM共通) (X68k の各種ROM) +// 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 "prom.h" #include "config.h" +#include "ethernet.h" #include "mainapp.h" -#include "scheduler.h" +#include "mainbus.h" +#include "mpu.h" + +// +// PROM0 +// +// PROM0 は PROM のブートページ担当。 +// これ自身はメモリ領域を持たず PROM へのプロキシとして動作する。 +// +// リセット後最初の ROM 領域への書き込みアクセスでブートページを RAM に +// 切り替える。 +// LUNA のブートページのアドレス範囲は不明なので、とりあえず devtable の +// 1区画である 16MB ($0000'0000..$00ff'ffff) とする。実害はない。 + +// コンストラクタ +PROM0Device::PROM0Device() + : inherited(OBJ_PROM_BOOT) +{ + // 通常ここからログを出すことはない + ClearAlias(); +} + +// デストラクタ +PROM0Device::~PROM0Device() +{ +} + +// 初期化 +bool +PROM0Device::Init() +{ + prom = GetPROMDevice(); + + return true; +} + +// リセット +void +PROM0Device::ResetHard(bool poweron) +{ + auto mainbus = GetMainbusDevice(); + mainbus->SwitchBootPageToROM(); +} + +// アドレスデコーダ +/*static*/ inline uint32 +PROM0Device::Decoder(uint32 addr) +{ + // ここはブートページか ROM かの比較だけで十分 (それ以外はここに来ない) + if (addr < 0x0100'0000) { + addr += 0x4100'0000; + } + return addr; +} + +busdata +PROM0Device::Read(busaddr addr) +{ + addr.ChangeAddr(Decoder(addr.Addr())); + return prom->Read(addr); +} + +// LUNA では PROM へのライトアクセスでブートページを RAM に切り替えている。 +// - LUNA-I では最初に $41000000.B への書き込みがあるが、 +// それ以降も $41000000.L への書き込みが(複数回)あるようだ。 +// - LUNA-88K では $4100000c.L への書き込みがある。 +// +// よってアドレスを定めず PROM 全域への書き込みで最初の1回だけ発動する +// ようにしておく。サイズも今の所ワード(16bit)アクセスは確認してないけど +// 統一的に対処しておく。 +busdata +PROM0Device::Write(busaddr addr, uint32 data) +{ + auto mainbus = GetMainbusDevice(); + mainbus->SwitchBootPageToRAM(); + + // バスエラーは起きない + busdata r = 0; + r |= busdata::Size(addr.GetSize()); + return r; +} + +busdata +PROM0Device::Peek1(uint32 addr) +{ + addr = Decoder(addr); + return prom->Peek1(addr); +} + + +// +// PROM +// // コンストラクタ PROMDevice::PROMDevice() + : inherited(OBJ_PROM) { - logname = "prom"; - devname = "PROM"; - devaddr = 0x41000000; + // LUNA では ROM への書き込みは何も起きない + write_op = 0; } // デストラクタ @@ -36,20 +142,25 @@ PROMDevice::~PROMDevice() { } +// 初期化 bool PROMDevice::Init() { - vmtype_t vmtype = gMainApp.GetVMType(); + VMType vmtype = gMainApp.GetVMType(); switch (vmtype) { - case VMTYPE_LUNA1: - return InitLuna1(); + case VMType::LUNA1: + if (gMainApp.msxdos_mode) { + return true; + } else { + return InitLuna1(); + } - case VMTYPE_LUNA88K: + case VMType::LUNA88K: return InitLuna88k(); default: - PANIC("unknown vmtype %d", vmtype); + PANIC("unknown vmtype %u", (uint)vmtype); } } @@ -57,7 +168,7 @@ PROMDevice::Init() bool PROMDevice::InitLuna1() { - devlen = 128 * 1024; + uint devlen = 128 * 1024; mask = devlen - 1; // ファイル名が存在してる時しかここに来ない @@ -66,6 +177,9 @@ PROMDevice::InitLuna1() return false; } + // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく + wait = busdata::Wait(1 * mpu->GetClock_nsec()); + // ROM バージョンを確認。確認されてるのは // V4.02 (Wed Oct 12 17:07:11 1988) // V4.22 (Thu Jul 27 11:45:42 1989) @@ -77,8 +191,17 @@ PROMDevice::InitLuna1() //00d3a0 3a 34 32 20 31 39 38 39 00 00 00 00 4e 56 fd f8 |:42 1989....NV..| if (strcmp((const char *)&mem[0xd390], "Thu Jul 27 11:45:42 1989") == 0) { - // V4.22 - putlog(1, "V4.22 detected"); + romver = 422; + } + + if (romver == 0) { + putmsg(0, "Unknown PROM version"); + } else { + putmsg(1, "v%u.%02u detected", romver / 100, romver % 100); + } + + if (romver == 422) { + // V4.22。他の ROM は何も分からないので何もしない。 // MAC アドレスは ROM に書き込まれているので、ここで上書き if (strcmp((const char *)&mem[0x1ffd8], "ENADDR") != 0) { @@ -88,25 +211,49 @@ PROMDevice::InitLuna1() // MAC アドレスは12桁の16進数文字列として書き込まれている。例えば // 前半の 00:00:0A 部分は 30 30 30 30 30 41 "00000A" という感じ。 - const macaddr_t macaddr = gEthernet->GetMacAddr(); - memcpy(&mem[0x1ffe0], macaddr.to_string().c_str(), 12); + MacAddr macaddr {}; + if (EthernetDevice::GetConfigMacAddr(0, &macaddr, true) == false) { + // エラーメッセージは表示済み + return false; + } + + const char *msg; + if (macaddr.Empty()) { + // "rom" なら元イメージのままにする。 + msg = "ROM"; + } else { + // 指定されたら上書き。 + memcpy(&imagebuf[0x1ffe0], macaddr.ToString().c_str(), 12); + msg = "config"; + } + putmsg(1, "macaddr=%c%c:%c%c:%c%c:%c%c:%c%c:%c%c (from %s)", + std::tolower(imagebuf[0x1ffe0]), + std::tolower(imagebuf[0x1ffe1]), + std::tolower(imagebuf[0x1ffe2]), + std::tolower(imagebuf[0x1ffe3]), + std::tolower(imagebuf[0x1ffe4]), + std::tolower(imagebuf[0x1ffe5]), + std::tolower(imagebuf[0x1ffe6]), + std::tolower(imagebuf[0x1ffe7]), + std::tolower(imagebuf[0x1ffe8]), + std::tolower(imagebuf[0x1ffe9]), + std::tolower(imagebuf[0x1ffea]), + std::tolower(imagebuf[0x1ffeb]), + msg); // SCSI デバッグビット bool scsi_debug_bit = gConfig->Find(".prom-scsi-debug").AsInt(); - mem[0x4100981d - 0x41000000] = (scsi_debug_bit) ? 0x01 : 0; - } else { - // 他の ROM は何も分からないので何もしない。 - putlog(0, "Unknown PROM Version"); + imagebuf[0x4100981d - 0x41000000] = (scsi_debug_bit) ? 0x01 : 0; } return true; } -// LUNA88K の場合 +// LUNA-88K の場合 bool PROMDevice::InitLuna88k() { - devlen = 256 * 1024; + uint devlen = 256 * 1024; mask = devlen - 1; // ファイル名が存在してる時しかここに来ない @@ -115,114 +262,41 @@ PROMDevice::InitLuna88k() return false; } - return true; -} - -bool -PROMDevice::PowerOn() -{ - // パフォーマンス測定に電源オン時の実時刻を記憶。 - // Write8() 参照。 - sw.Start(); - return true; -} - -void -PROMDevice::ResetHard() -{ - // PROM への Write (RAM への切り替え) は何度も実行されるので、最初の - // 1回だけ処理するため、こちらでも状態を持つ。 - // Scheduler も同様のステートを持っているけど、あっちはリクエストと - // 現状の2つがあって、リクエスト投げて状態が変わるまでに遅延があるので - // たぶんこっちで処理したほうがいい。 - isrom = true; - gVM->SwitchBootPage(this, true); -} - -uint64 -PROMDevice::Read8(uint32 addr) -{ - // パフォーマンス測定のためのバックドア。 - // 0x42000004 番地をバイト読み込みを行うと VM 電源オン時から現在までの - // 実時間をホスト (ログ) に出力する。NetBSD のマルチユーザブートが - // あらかた終わってログインプロンプトが出る少し前に実行される - // /etc/rc.local に - // dd if=/dev/mem skip=1107296260 bs=1 count=1 - // と書いておくことでここまでにかかる時間を表示できる。ただしこの値は - // ホスト性能に依存するため、同一環境下での相対的な比較のみが可能。 - // 実機ではこの動作では PROM のミラーが読めるだけなので副作用はないはず。 - // PROM は仕様では 0x4100_0000 から 128KB なので、ここでは通常アクセス - // するはずのない 0x4200_0004 からの1バイトの読み込みをバックドアに使う。 - // 0x4100_0000..0x41ff_ffff への書き込みは 0番地付近を ROM から RAM に - // 切り替えるらしいが実際に PROM がアクセスするのは 0x4100_0000.[BL] - // のみなので、ここではそれを避けて 0x4200_0004.B を使う。 - // 書き込みをバックドアにしたかったが dd で書き込めなさそうだったので - // 読み込みを使う。ちゃんと調べてない。 - if (__predict_false(addr == 0x42000004)) { - uint64 rtime; - uint64 vtime; - - // 電源オン時からここまでの時間を表示するだけでリセット等はしない。 - sw.Stop(); - rtime = sw.Elapsed(); - vtime = gMPU->GetVirtTime(); - putlog(0, "電源オンから %d.%03d 秒経過 %3.3f%%", - (int)(rtime / (1000 * 1000 * 1000)), - (int)((rtime / (1000 * 1000)) % 1000), - (double)vtime / rtime * 100); - } - - // それ以外は通常動作 - return inherited::Read8(addr); -} - -uint64 -PROMDevice::Write8(uint32 addr, uint32 data) -{ - return Write(addr); -} - -uint64 -PROMDevice::Write16(uint32 addr, uint32 data) -{ - return Write(addr); -} - -uint64 -PROMDevice::Write32(uint32 addr, uint32 data) -{ - return Write(addr); -} - -// Write{8,16,32} 共通。 -// LUNA では書き込みでバスエラーは起きない。 -// 電源オン後最初の書き込みでブートページを RAM に切り替える。 -uint64 -PROMDevice::Write(uint32 addr) -{ - if (__predict_false(isrom)) { - // LUNA では PROM への書き込みでブートページを RAM に切り替えている。 - // - LUNA-I では最初に $41000000.B への書き込みがあるが、 - // それ以降も $41000000.L への書き込みが(複数回)あるようだ。 - // - LUNA88K では $4100000c.L への書き込みがある。 - // よってアドレスを定めず PROM 全域への書き込みで最初の1回だけ発動 - // するようにしておく。サイズも今の所ワード(16bit)アクセスは確認して - // ないけど統一的に対処しておく。 - - // XXX PROM がブートページを担当してる期間に RAM (のはずのアドレス) - // への書き込みは起きるんだろうか? - if (addr < 0x41000000) { - putlogn("%s addr=$%08x", __func__, addr); - } + // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく + wait = busdata::Wait(3 * mpu->GetClock_nsec()); - // RAM に切り替え - isrom = false; - gVM->SwitchBootPage(this, false); + // ROM バージョンを照合。どこかに日付が埋まってるっぽい。 + // + // 1.20: + //02c190 00 00 04 b0 28 0d 2f e1 28 0d 2f e1 54 68 75 20 |....(./.(./.Thu | + //02c1a0 41 70 72 20 31 38 20 31 34 3a 33 34 3a 32 35 20 |Apr 18 14:34:25 | + //02c1b0 31 39 39 31 00 00 00 00 41 02 c1 9c 00 00 00 00 |1991....A.......| + // + // 1.31: + //03c940 00 00 05 1e 2a 0f 28 78 2a 0f 28 78 54 75 65 20 |....*.(x*.(xTue | + //03c950 4d 61 79 20 31 32 20 31 31 3a 30 37 3a 35 32 20 |May 12 11:07:52 | + //03c960 31 39 39 32 00 00 00 00 41 03 c9 4c 00 00 00 00 |1992....A..L....| + // + // 1.37: + //03cf30 00 00 05 5a 2b 5a 4a 7d 2b 5a 4a 7d 4d 6f 6e 20 |...Z+ZJ}+ZJ}Mon | + //03cf40 4a 61 6e 20 31 38 20 31 35 3a 31 33 3a 31 37 20 |Jan 18 15:13:17 | + //03cf50 31 39 39 33 00 00 00 00 41 03 cf 3c 00 00 00 00 |1993....A..<....| + + if (strcmp((const char *)&mem[0x2c19c], "Thu Apr 18 14:34:25 1991") == 0) { + romver = 120; + } else + if (strcmp((const char *)&mem[0x3c94c], "Tue May 12 11:07:52 1992") == 0) { + romver = 131; + } else + if (strcmp((const char *)&mem[0x3cf3c], "Mon Jan 18 15:13:17 1993") == 0) { + romver = 137; } - // XXX ウェイト未調査 - gMPU->AddCycle(wait); + if (romver == 0) { + putmsg(0, "Unknown PROM version"); + } else { + putmsg(1, "v%u.%02u detected", romver / 100, romver % 100); + } - // バスエラーは起きない - return 0; + return true; }