--- nono/vm/prom.cpp 2026/04/29 17:04:36 1.1 +++ nono/vm/prom.cpp 2026/04/29 17:04:53 1.1.1.6 @@ -43,7 +43,7 @@ PROMDevice::Init() switch (vmtype) { case VMTYPE_LUNA1: - return InitLuna(); + return InitLuna1(); case VMTYPE_LUNA88K: return InitLuna88k(); @@ -55,7 +55,7 @@ PROMDevice::Init() // LUNA-I の場合 bool -PROMDevice::InitLuna() +PROMDevice::InitLuna1() { devlen = 128 * 1024; mask = devlen - 1; @@ -66,20 +66,38 @@ PROMDevice::InitLuna() return false; } - // MAC アドレスは ROM に書き込まれているので、ここで上書き - if (strcmp((const char *)&mem[0x1ffd8], "ENADDR") != 0) { - warnx("ENADDR not found; unknown ROM variants?"); - return false; + // 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; } @@ -105,10 +123,22 @@ PROMDevice::PowerOn() { // パフォーマンス測定に電源オン時の実時刻を記憶。 // Write8() 参照。 - start_time = ::GetRealTime(); + sw.Start(); return true; } +void +PROMDevice::ResetHard() +{ + // PROM への Write (RAM への切り替え) は何度も実行されるので、最初の + // 1回だけ処理するため、こちらでも状態を持つ。 + // Scheduler も同様のステートを持っているけど、あっちはリクエストと + // 現状の2つがあって、リクエスト投げて状態が変わるまでに遅延があるので + // たぶんこっちで処理したほうがいい。 + isrom = true; + gVM->SwitchBootPage(this, true); +} + uint64 PROMDevice::Read8(uint32 addr) { @@ -128,18 +158,18 @@ PROMDevice::Read8(uint32 addr) // のみなので、ここではそれを避けて 0x4200_0004.B を使う。 // 書き込みをバックドアにしたかったが dd で書き込めなさそうだったので // 読み込みを使う。ちゃんと調べてない。 - if (addr == 0x42000004) { - uint64 end, result; - uint64 vt; + if (__predict_false(addr == 0x42000004)) { + uint64 rtime; + uint64 vtime; // 電源オン時からここまでの時間を表示するだけでリセット等はしない。 - end = ::GetRealTime(); - vt = gScheduler->GetVirtTime(); - result = end - start_time; + sw.Stop(); + 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); } // それ以外は通常動作 @@ -149,27 +179,50 @@ PROMDevice::Read8(uint32 addr) uint64 PROMDevice::Write8(uint32 addr, uint32 data) { - // LUNA では PROM への書き込みで 0番地付近を RAM に切り替えている。 - // nono では 0番地付近を ROM に見せること自体をしていないので、 - // ここでは何もしなくてよく、単に無視する。 - // LUNA-I だと最初に $41000000.B への書き込みがあるが、それ以降も - // $41000000.L への書き込みがあるようなので Write32() でも対処している。 + return Write(addr); +} - if (addr == 0x41000000) { - return 0; - } - return inherited::Write8(addr, data); +uint64 +PROMDevice::Write16(uint32 addr, uint32 data) +{ + return Write(addr); } uint64 PROMDevice::Write32(uint32 addr, uint32 data) { - // PROM への書き込みで 0番地付近を RAM に切り替えている。 - // すぐ上の Write8() のコメントも参照。 - // LUNA88K では $4100000c.L への書き込みが最初にある。 - // LUNA-I でも $41000000.L への書き込みがあるので、ここでは両方無視する。 - if (addr < 0x41000010) { - return 0; + 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); + } + + // RAM に切り替え + isrom = false; + gVM->SwitchBootPage(this, false); } - return inherited::Write32(addr, data); + + // XXX ウェイト未調査 + gMPU->AddCycle(wait); + + // バスエラーは起きない + return 0; }