--- nono/vm/prom.cpp 2026/04/29 17:05:28 1.1.1.15 +++ nono/vm/prom.cpp 2026/04/29 17:06:00 1.1.1.19 @@ -66,10 +66,6 @@ PROM0Device::~PROM0Device() bool PROM0Device::Init() { - if (inherited::Init() == false) { - return false; - } - prom = GetPROMDevice(); return true; @@ -150,19 +146,14 @@ PROMDevice::~PROMDevice() bool PROMDevice::Init() { - if (inherited::Init() == false) { - return false; - } - VMType vmtype = gMainApp.GetVMType(); switch (vmtype) { case VMType::LUNA1: if (gMainApp.msxdos_mode) { return true; - } else { - return InitLuna1(); } + return InitLuna1(); case VMType::LUNA88K: return InitLuna88k(); @@ -186,7 +177,7 @@ PROMDevice::InitLuna1() } // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく - wait = busdata::Wait(1 * mpu->GetClock_nsec()); + wait = busdata::Wait(1 * mpu->GetClock_tsec()); // ROM バージョンを確認。確認されてるのは // V4.02 (Wed Oct 12 17:07:11 1988) @@ -219,7 +210,7 @@ PROMDevice::InitLuna1() // MAC アドレスは12桁の16進数文字列として書き込まれている。例えば // 前半の 00:00:0A 部分は 30 30 30 30 30 41 "00000A" という感じ。 - macaddr_t macaddr {}; + MacAddr macaddr {}; if (EthernetDevice::GetConfigMacAddr(0, &macaddr, true) == false) { // エラーメッセージは表示済み return false; @@ -250,7 +241,7 @@ PROMDevice::InitLuna1() msg); // SCSI デバッグビット - bool scsi_debug_bit = gConfig->Find(".prom-scsi-debug").AsInt(); + bool scsi_debug_bit = gConfig->Find(".prom-scsi-debug").AsBool(); imagebuf[0x4100981d - 0x41000000] = (scsi_debug_bit) ? 0x01 : 0; } @@ -271,7 +262,7 @@ PROMDevice::InitLuna88k() } // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく - wait = busdata::Wait(3 * mpu->GetClock_nsec()); + wait = busdata::Wait(3 * mpu->GetClock_tsec()); // ROM バージョンを照合。どこかに日付が埋まってるっぽい。 // @@ -306,5 +297,62 @@ PROMDevice::InitLuna88k() putmsg(1, "v%u.%02u detected", romver / 100, romver % 100); } + if (romver == 120) { + // m88100 コアのパイプラインが未実装なのをごまかすハック。 + // + // PROM 1.20 のプローブルーチン(かな?) はこうなっている。 + // 410005f8: f5421400 ld r10,r2, r0 + // 410005fc: f4405800 or r2, r0, r0 + // 41000600: c0000002 br 0x41000608 + // 41000604: 58400001 or r2, r0, #0x0001 + // 41000608: f1a0e800 tcnd ne0,r0, #0x000 + // : + // ここで 410005f8 の ld 命令がバスエラーになると以下の例外ハンドラに + // 飛ぶ。 + // 0000102c: 80008060 stcr r0, ssbr ; Clear SSBR + // 00001030: 80204080 ldcr r1, sxip + // 00001034: 800180c1 stcr r1, sfip ; sfip <- sxip + // 00001038: f0218021 clr r1, r1, 1<1> ; &= ~VALID + // 0000103c: 80018081 stcr r1, sxip ; ただし sxip は readonly + // 00001040: 800080a0 stcr r0, snip ; snip <- 0 (Invalidate) + // 00001044: 80204260 ldcr r1, sr2 + // 00001048: f400fc00 rte + // + // 例外ハンドラは特に何もせず sxip の位置から実行を再開しようとする。 + // そのまま読むと例外発生前に実行した命令(ld 命令)を再実行するように + // 読めるが、ld 命令直後の OR 命令は整数ユニット担当であり更に ld + // 命令の rD と OR 命令の rD, rS は依存関係もないため、データユニット + // が ld 命令のバス応答を待っている間に整数ユニットが OR 命令を実行 + // できる。もっと言えばバスの応答時間次第で少なくとも同期命令である + // tcnd 命令の直前までは進むことが出来る。 + // この例外ハンドラはおそらくこれを踏まえて、例外発生時の sxip が + // 少なくとも ld 命令よりも進んでいることを前提にしているようだ。 + // + // ところがうちではこのパイプラインを実装しておらず、xip が ld 命令を + // 指したまま例外が発生するため、このままでは rte 命令で再び ld 命令に + // 戻って行ってしまい無限ループになる。 + // + // この DAE ハンドラは OpenBSD のブートローダからも呼ばれるので、 + // MPU 側で呼び出し元を特定して動作を書き換えるハックでは十分でない。 + // そのため PROM 1.20 であれば ROM のコードを変更して snip から + // 再実行するようにする。 + // + // OpenBSD (カーネルのほう) でも例外発生時の sxip が実機よりも遅い + // ところを指している (可能性がある) という現象は変わらないが、 + // これによる齟齬は発生しないはず。 + // ld/st 命令中の例外なら、xip がどこを指していても xip は実行済み + // で snip から再実行することには変わりないので問題は起きないはず。 + // xmem 命令中の例外なら、xmem は同期命令なので xip はここに留まる + // ためおそらく実機と同じ動作になってるはず。 + // + // ROM上 RAMコピー後 + // 410016c0: 00001030: 80204080 ldcr r1,sxip + // ↓ + // 410016c0: 00001030: 802040c0 ldcr r1,snip + if (be32toh(*(const uint32 *)&imagebuf[0x16c0]) == 0x80204080) { + imagebuf[0x16c3] = 0xa0; + } + } + return true; }