--- nono/vm/prom.cpp 2026/04/29 17:04:36 1.1 +++ nono/vm/prom.cpp 2026/04/29 17:05:06 1.1.1.9 @@ -5,30 +5,162 @@ // // PROM (LUNA-I, LUNA88K 共通) - -// 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) +// | +-- LunaPROMEmuDevice (LUNA PROM エミュレーションの共通部分) +// | | +// | +-- Luna1PROMEmuDevice (LUNA1 の PROM エミュレーション) +// | +-- Luna88kPROMEmuDevice (LUNA88K の PROM エミュレーション) +// | +// | +-------------+ +// +-- | PROM0Device | (LUNA* のブートページ切り替え用プロキシ) +// | +-------------+ +// | +// +-- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ) #include "prom.h" #include "config.h" +#include "ethernet.h" #include "mainapp.h" #include "scheduler.h" +std::unique_ptr gPROM0; +std::unique_ptr gPROM; + +// +// PROM0 +// +// PROM0 は PROM のブートページ担当。 +// これ自身はメモリ領域を持たず PROM へのプロキシとして動作する。 +// +// リセット後最初の ROM 領域への書き込みアクセスでブートページを RAM に +// 切り替える。 +// LUNA のブートページのアドレス範囲は不明なので、とりあえず devtable の +// 1区画である 16MB ($0000'0000..$00ff'ffff) とする。実害はない。 + +// コンストラクタ +PROM0Device::PROM0Device() + : inherited("PROM(0)") +{ + ClearAlias(); + AddAlias("PROM0"); +} + +// デストラクタ +PROM0Device::~PROM0Device() +{ +} + +bool +PROM0Device::Init() +{ + prom = gPROM.get(); + return true; +} + +void +PROM0Device::ResetHard() +{ + gVM->SwitchBootPageToROM(this); +} + +// アドレスデコーダ +/*static*/ inline uint32 +PROM0Device::Decoder(uint32 addr) +{ + // ここはブートページか ROM かの比較だけで十分 (それ以外はここに来ない) + if (addr < 0x0100'0000) { + addr += 0x4100'0000; + } + return addr; +} + +uint64 +PROM0Device::Read8(uint32 addr) +{ + addr = Decoder(addr); + return prom->Read8(addr); +} + +uint64 +PROM0Device::Read16(uint32 addr) +{ + addr = Decoder(addr); + return prom->Read16(addr); +} + +uint64 +PROM0Device::Read32(uint32 addr) +{ + addr = Decoder(addr); + return prom->Read32(addr); +} + +// LUNA では PROM へのライトアクセスでブートページを RAM に切り替えている。 +// - LUNA-I では最初に $41000000.B への書き込みがあるが、 +// それ以降も $41000000.L への書き込みが(複数回)あるようだ。 +// - LUNA88K では $4100000c.L への書き込みがある。 +// +// よってアドレスを定めず PROM 全域への書き込みで最初の1回だけ発動する +// ようにしておく。サイズも今の所ワード(16bit)アクセスは確認してないけど +// 統一的に対処しておく。 +uint64 +PROM0Device::Write() +{ + gVM->SwitchBootPageToRAM(this); + + // バスエラーは起きない + return 0; +} + +uint64 +PROM0Device::Write8(uint32 addr, uint32 data) +{ + return Write(); +} + +uint64 +PROM0Device::Write16(uint32 addr, uint32 data) +{ + return Write(); +} + +uint64 +PROM0Device::Write32(uint32 addr, uint32 data) +{ + return Write(); +} + +uint64 +PROM0Device::Peek8(uint32 addr) +{ + addr = Decoder(addr); + return prom->Peek8(addr); +} + + +// +// PROM +// + // コンストラクタ PROMDevice::PROMDevice() + : inherited("PROM") { - logname = "prom"; - devname = "PROM"; devaddr = 0x41000000; + + // LUNA では ROM への書き込みは何も起きない + write_op = 0; } // デストラクタ @@ -43,7 +175,7 @@ PROMDevice::Init() switch (vmtype) { case VMTYPE_LUNA1: - return InitLuna(); + return InitLuna1(); case VMTYPE_LUNA88K: return InitLuna88k(); @@ -55,7 +187,7 @@ PROMDevice::Init() // LUNA-I の場合 bool -PROMDevice::InitLuna() +PROMDevice::InitLuna1() { devlen = 128 * 1024; mask = devlen - 1; @@ -66,20 +198,41 @@ PROMDevice::InitLuna() return false; } - // MAC アドレスは ROM に書き込まれているので、ここで上書き - if (strcmp((const char *)&mem[0x1ffd8], "ENADDR") != 0) { - warnx("ENADDR not found; unknown ROM variants?"); - return false; + // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく + wait = 1; + + // ROM バージョンを確認。確認されてるのは + // V4.02 (Wed Oct 12 17:07:11 1988) + // V4.22 (Thu Jul 27 11:45:42 1989) + // V4.25 + // + // とりあえず V4.22 は +$00d390 からにタイムスタンプ文字列があるので + // それで判別してみる。 + //00d390 54 68 75 20 4a 75 6c 20 32 37 20 31 31 3a 34 35 |Thu Jul 27 11:45| + //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"); + + // MAC アドレスは ROM に書き込まれているので、ここで上書き + if (strcmp((const char *)&mem[0x1ffd8], "ENADDR") != 0) { + warnx("ENADDR not found; unknown ROM V4.22 variants?"); + return false; + } + + // 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); + + // 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"); } - // 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); - - // SCSI デバッグビット - // XXX ROM を確認してから適用したほうがいい - bool scsi_debug_bit = gConfig->Find(".prom-scsi-debug").AsInt(); - mem[0x4100981d - 0x41000000] = (scsi_debug_bit) ? 0x01 : 0; return true; } @@ -97,6 +250,9 @@ PROMDevice::InitLuna88k() return false; } + // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく + wait = 3; + return true; } @@ -104,8 +260,8 @@ bool PROMDevice::PowerOn() { // パフォーマンス測定に電源オン時の実時刻を記憶。 - // Write8() 参照。 - start_time = ::GetRealTime(); + // Read8() 参照。 + sw.Start(); return true; } @@ -113,63 +269,29 @@ uint64 PROMDevice::Read8(uint32 addr) { // パフォーマンス測定のためのバックドア。 - // 0x42000004 番地をバイト読み込みを行うと VM 電源オン時から現在までの + // 0x42e576f7 番地をバイト読み込みを行うと VM 電源オン時から現在までの // 実時間をホスト (ログ) に出力する。NetBSD のマルチユーザブートが // あらかた終わってログインプロンプトが出る少し前に実行される // /etc/rc.local に - // dd if=/dev/mem skip=1107296260 bs=1 count=1 + // dd if=/dev/mem skip=1122334455 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 (addr == 0x42000004) { - uint64 end, result; - uint64 vt; + // 出来れば書き込みをバックドアにしたかったが、たぶんカーネルレベルで + // 弾いてあるので、読み込みを使う。 + if (__predict_false(addr == 0x42e576f7)) { + uint64 rtime; + uint64 vtime; // 電源オン時からここまでの時間を表示するだけでリセット等はしない。 - end = ::GetRealTime(); - vt = gScheduler->GetVirtTime(); - result = end - start_time; + rtime = sw.Elapsed(); + vtime = gMPU->GetVirtTime(); putlog(0, "電源オンから %d.%03d 秒経過 %3.3f%%", - (int)(result / (1000 * 1000 * 1000)), - (int)((result / (1000 * 1000)) % 1000), - (double)vt / result * 100); + (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) -{ - // LUNA では PROM への書き込みで 0番地付近を RAM に切り替えている。 - // nono では 0番地付近を ROM に見せること自体をしていないので、 - // ここでは何もしなくてよく、単に無視する。 - // LUNA-I だと最初に $41000000.B への書き込みがあるが、それ以降も - // $41000000.L への書き込みがあるようなので Write32() でも対処している。 - - if (addr == 0x41000000) { - return 0; - } - return inherited::Write8(addr, data); -} - -uint64 -PROMDevice::Write32(uint32 addr, uint32 data) -{ - // PROM への書き込みで 0番地付近を RAM に切り替えている。 - // すぐ上の Write8() のコメントも参照。 - // LUNA88K では $4100000c.L への書き込みが最初にある。 - // LUNA-I でも $41000000.L への書き込みがあるので、ここでは両方無視する。 - if (addr < 0x41000010) { - return 0; - } - return inherited::Write32(addr, data); -}