--- nono/vm/scsibus.cpp 2026/04/29 17:04:42 1.1.1.4 +++ nono/vm/scsibus.cpp 2026/04/29 17:04:56 1.1.1.8 @@ -21,11 +21,8 @@ // コンストラクタ SCSIBus::SCSIBus() + : inherited("SCSIBus") { - logname = "scsibus"; - devname = "SCSIBus"; - - event.dev = this; event.SetName("SCSIBus"); // 複数出来たら名前どうするべか } @@ -34,6 +31,28 @@ SCSIBus::~SCSIBus() { } +// 電源オン +bool +SCSIBus::PowerOn() +{ + phase = SCSI::Phase::BusFree; + rst = false; + req = false; + ack = false; + atn = false; + sel = 0; + bsy = 0; + xfer = 0; + data = 0; + + last_req_time = 0; + req_wait = 0; + init_id = -1; + target_id = -1; + + return true; +} + // リセット void SCSIBus::ResetHard() @@ -162,8 +181,29 @@ SCSIBus::AssertSEL(uint id) init_id = id; } else { - // どうする? - PANIC("AssertSEL in %s phase\n", GetPhaseName()); + // それ以外(転送フェーズ)中に SEL が立てられたらどうすればいいか。 + // これは転送中に LUNA をリセットすると起きる。LUNA の PROM は SPC に + // 対してバスリセットを発行せずに、セレクションを行おうとするため。 + // SCSI 本には載ってないので分からないが、このような規約違反はたいてい + // イニシエータが検出してバスフリーかバスリセットを行うことが意図 + // されているんじゃないかと想像するけど、今回そのイニシエータが違反 + // してるので、どうするんだろうね。 + // 賢いターゲットデバイスだとこれを検出して自主的にバスフリーへ落ちる + // だろうか。賢くないターゲットデバイスは検出せずにそのまま (そして + // デッドロック) になるだろうか。 + +#if 1 + // XXX とりあえず LUNA で SCSI がなんか刺さった時に本体リセットでも + // 直らない状況を真似するため、ターゲットは何もしないでおく。 +#else + // ターゲットデバイスは賢いので、全員イニシエータ様に忖度して + // 即座にバスフリーへ移行するというのはどうか。 + for (uint i = 0; i < 8; i++) { + if (i != id) { + NegateBSY(i); + } + } +#endif } } @@ -180,7 +220,17 @@ SCSIBus::NegateSEL(uint id) sel &= ~(1 << id); - if (GetPhase() == SCSI::Phase::Selection) { + if (GetPhase() == SCSI::Phase::BusFree) { + // バスフリーフェーズで SEL を下げる + + // 通常は起きないはずだけど、転送フェーズ中に LUNA をリセットすると + // -> LUNA の PROM が SEL を上げる (プロトコル違反) + // -> ターゲットは転送フェーズ中なのに SEL が上がったのを検出して + // BSY を取り下げてバスフリーに移行する + // -> バスフリーがイニシエータに通知され、イニシエータが SEL を下げる + // となるので、このケースはスルーしてよい。 + + } else if (GetPhase() == SCSI::Phase::Selection) { // セレクションフェーズで SEL を下げる if (bsy) { // BSY が立っていれば、ターゲットがセレクションに応答したので @@ -195,7 +245,7 @@ SCSIBus::NegateSEL(uint id) BusFree(id); } } else { - // どうする? + // 下げる分には無視してもいいはずだけど、どうする? PANIC("NegateSEL in %s phase\n", GetPhaseName()); } } @@ -308,7 +358,7 @@ SCSIBus::AssertREQ() // 通知はイベントにしないといけないので。 uint64 after = 0; - uint64 now = gScheduler->GetVirtTime(); + uint64 now = gMPU->GetVirtTime(); if (now < last_req_time + 1_usec) { after = (last_req_time + 1_usec) - now; }