--- nono/vm/spc.cpp 2026/04/29 17:05:09 1.1.1.12 +++ nono/vm/spc.cpp 2026/04/29 17:05:17 1.1.1.14 @@ -15,7 +15,6 @@ #include "mpu.h" #include "pedec.h" #include "scheduler.h" -#include "sram.h" // time 時間後に呼び出すコールバックをセットする。 // func の書式が面倒なのを省略して書きたいため。 @@ -23,27 +22,19 @@ event.func = ToEventCallback(&SPCDevice::func_); \ event.time = time_; \ event.SetName(name_); \ - gScheduler->RestartEvent(event); \ + scheduler->RestartEvent(event); \ } while (0) -// グローバル参照用 -SPCDevice *gSPC; - // コンストラクタ SPCDevice::SPCDevice() - : inherited("SPC") + : inherited(OBJ_SPC) { - // これらは XIOSpace で必要なので、LUNA ではダミー。 - // もうちょっといい方法を考えたほうがいい。 - devaddr = 0xe96000; - devlen = 0x2000; - SetConfigName("spc0"); myid = -1; // 汎用イベント // 名前とコールバック関数は都度セットする。 - event.Regist("SPC phase"); + event.Regist("SPC Phase"); // TC カウンタイベント event_tc.code = 0; @@ -57,31 +48,24 @@ SPCDevice::SPCDevice() // デストラクタ SPCDevice::~SPCDevice() { - gSPC = NULL; } +// 初期化 bool SPCDevice::Init() { + // inherited::Init() は少し下で呼んでいる + // 実行中に別インスタンスを頻繁に参照しないようにここで vmtype を覚えておく vmtype = gMainApp.GetVMType(); - // 本体の SCSI ID は SPC(MB89352) の BDID レジスタに ID が書き込まれる - // のを待たずに、最初から知っていることにする。本来 (少なくとも真っ当な - // システムでは) イニシエータを含むすべての SCSI 機器の SCSI ID は - // 電源を入れる前に、人間が一々手作業で重複なく設定しているはずなので。 - // X68030 は SRAM $ED0070 下位3ビット。LUNA は 7 固定のようだ。 - uint myid0; - if (vmtype == VMTYPE_X68030) { - myid0 = gSRAM->Peek8(0xed0070) & 7; - } else { - myid0 = 7; - } + // 本体 SCSI ID は LUNA も X68030 も 7 固定。 + uint initiator_id = 7; // モニタには、接続している SCSI デバイスだけを表示する。 // そのため ID ではなく前から何番目のデバイスかのリストが欲しい。 for (int id = 0; id < 8; id++) { - if (id == myid0) { + if (id == initiator_id) { connected_devices.push_back(this); } else if (target[id].get()) { connected_devices.push_back(target[id].get()); @@ -95,12 +79,12 @@ SPCDevice::Init() // 入力クロックは機種によって異なる。 // X68k は 5MHz が入っている。 - // LUNA-I はおそらく 6.144MHz だと思われる。LUNA88K は未確認。 + // LUNA-I はおそらく 6.144MHz だと思われる。LUNA-88K は未確認。 const ConfigItem& item = gConfig->Find("!spc-tCLF"); t_CLF = item.AsInt(); // アクセスウェイト - if (vmtype == VMTYPE_X68030) { + if (vmtype == VMType::X68030) { // InsideOut p.135 read_wait = 18; write_wait = 19; @@ -112,11 +96,11 @@ SPCDevice::Init() } // 割り込み信号線の接続先 - if (vmtype == VMTYPE_X68030) { + if (vmtype == VMType::X68030) { // X68030 は PEDEC (I/O コントローラ) にカスケード - interrupt = gPEDEC; + interrupt = GetPEDECDevice(); } else { - interrupt = gInterrupt; + interrupt = GetInterruptDevice(); } monitor.SetSize(70, 15 + connected_devices.size()); @@ -128,7 +112,7 @@ SPCDevice::Init() void SPCDevice::ResetHard(bool poweron) { - putlog(1, "リセット"); + putlog(1, "Reset"); // 初期値 (p.61) spc.sctl = SPC::SCTL_RESET; @@ -150,8 +134,8 @@ SPCDevice::ResetHard(bool poweron) prev_ssts = (uint32)-1; set_atn_in_selection = false; transfer_command_running = false; - gScheduler->StopEvent(event); - gScheduler->StopEvent(event_tc); + scheduler->StopEvent(event); + scheduler->StopEvent(event_tc); last_busfree_time = 0; } @@ -161,7 +145,7 @@ SPCDevice::Read(uint32 offset) { uint8 data; - gMPU->AddCycle(read_wait); + mpu->AddCycle(read_wait); switch (offset) { case SPC::BDID: @@ -232,8 +216,8 @@ SPCDevice::Read(uint32 offset) return data; case SPC::MBC: - putlog(0, "MBC 未実装読み込み"); - return 0xff; + putlog(0, "Read MBC (NOT IMPLEMENTED)"); + return 0x0f; case SPC::DREG: return ReadDREG(); @@ -263,14 +247,14 @@ SPCDevice::Read(uint32 offset) return 0xff; default: - __unreachable(); + VMPANIC("corrupted offset=%d", offset); } } uint64 SPCDevice::Write(uint32 offset, uint32 data) { - gMPU->AddCycle(write_wait); + mpu->AddCycle(write_wait); switch (offset) { case SPC::BDID: @@ -285,7 +269,7 @@ SPCDevice::Write(uint32 offset, uint32 d scsibus->Attach(this, myid); } else { // そうでなければ移動? - putlog(0, "BDID 変更 %d to %d not implemented", myid, data); + putlog(0, "BDID change %d to %d (NOT IMPLEMENTED)", myid, data); return 0; } return 0; @@ -370,7 +354,7 @@ SPCDevice::Write(uint32 offset, uint32 d return 0; default: - __unreachable(); + VMPANIC("corrupted offset=%d", offset); } } @@ -408,13 +392,13 @@ SPCDevice::Peek(uint32 offset) return GetPCTL(); case SPC::MBC: - return 0xff; + return 0x0f; case SPC::DREG: // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、 // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが // すべてバスエラーになってしまう。 - if (vmtype == VMTYPE_X68030) { + if (vmtype == VMType::X68030) { if ((spc.scmd & SPC::SCMD_PROGRAM)) { return (uint64)-1; } @@ -438,7 +422,7 @@ SPCDevice::Peek(uint32 offset) return 0xff; default: - __unreachable(); + return 0xff; } } @@ -734,7 +718,7 @@ SPCDevice::WriteSCTL(uint32 data) putlog(2, "Control Reset"); } if ((spc.sctl & SPC::SCTL_DIAG_MODE)) { - putlog(0, "SCTL 未実装 Diag Mode"); + putlog(0, "SCTL Diag Mode (NOT IMPLEMENTED)"); } // 割り込みマスクを更新して信号線の状態を変える @@ -784,11 +768,11 @@ SPCDevice::WriteSCMD(uint32 data) switch (spc.scmd >> 5) { case 0: - putlog(2, "SCMD Bus Release コマンド"); + putlog(2, "SCMD Bus Release command"); if ((spc.scmd & SPC::SCMD_INTERCEPT)) { // BusRelease コマンドの発行と共に Intercept ビットが立っていたら // Service Required 割り込みをリセットする? - putlog(0, "SCMD Intercept with BusRelease not implemented"); + putlog(0, "SCMD Intercept with BusRelease (NOT IMPLEMENTED)"); } BusFree(); break; @@ -796,7 +780,7 @@ SPCDevice::WriteSCMD(uint32 data) SelectCommand(); break; case 2: - putlog(1, "SCMD Reset ATN コマンド (未実装)"); + putlog(1, "SCMD Reset ATN command (NOT IMPLEMENTED)"); break; case 3: SetATNCommand(); @@ -805,16 +789,16 @@ SPCDevice::WriteSCMD(uint32 data) TransferCommand(); break; case 5: - putlog(2, "SCMD Transfer Pause コマンド (未実装)"); + putlog(2, "SCMD Transfer Pause command (NOT IMPLEMENTED)"); break; case 6: - putlog(2, "SCMD Reset ACK/REQ コマンド"); + putlog(2, "SCMD Reset ACK/REQ command"); // イニシエータなら ACK を、ターゲットなら REQ をリセットする。 // ターゲットモードはサポートしないので、操作するのは ACK のみ。 NegateACK(); break; case 7: - putlog(2, "SCMD Set ACK/REQ コマンド"); + putlog(2, "SCMD Set ACK/REQ command"); // イニシエータなら ACK を、ターゲットなら REQ をセットする。 // ターゲットモードはサポートしないので、操作するのは ACK のみ。 if ((spc.scmd & SPC::SCMD_INTERCEPT)) { @@ -823,7 +807,7 @@ SPCDevice::WriteSCMD(uint32 data) // これが影響するのは DREG にデータが滞留してる時だけのはず // なので、その時だけログを出す。(X68030 IPLROM、NetBSD/x68k) if (spc.dreg.Length() != 0) { - putlog(0, "SCMD Intercept with SetACK not implemented"); + putlog(0, "SCMD Intercept with SetACK (NOT IMPLEMENTED)"); } } AssertACK(); @@ -859,7 +843,7 @@ SPCDevice::WriteINTS(uint32 data) } else { event_tc.func = ToEventCallback(&SPCDevice::SelectionTimeout); event_tc.time = spc.tc * t_CLF * 2; - gScheduler->RestartEvent(event_tc); + scheduler->RestartEvent(event_tc); } } } @@ -902,10 +886,10 @@ SPCDevice::ChangeInterrupt() // ints_mask は現在の SCTL_INTR_EN 状態に応じたマスクが用意されているので // 単純に AND するだけでよい。 if ((spc.ints & spc.ints_mask) != 0) { - putlog(3, "割り込み ints=$%02x", spc.ints); + putlog(3, "Interrupt (ints=$%02x)", spc.ints); interrupt->AssertINT(this); } else { - putlog(4, "割り込み解除"); + putlog(4, "Negate Interrupt"); interrupt->NegateINT(this); } } @@ -1006,14 +990,14 @@ SPCDevice::ReadDREG() // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、 // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが // すべてバスエラーになってしまう。 - if (vmtype == VMTYPE_X68030) { + if (vmtype == VMType::X68030) { if ((spc.scmd & SPC::SCMD_PROGRAM)) { return (uint64)-1; } else { // DMA 転送で DREQ が出てないとき DTACK を出さないのを // -2 を返すことで表現している if (spc.dreg.Length() == 0) { - putlog(3, "DMA転送中の DREG empty"); + putlog(3, "DREG empty during DMA transferring"); return (uint64)-2; // DTACK まだ出てないよ } } @@ -1068,7 +1052,7 @@ SPCDevice::WriteDREG(uint32 data) // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、 // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが // すべてバスエラーになってしまう。 - if (vmtype == VMTYPE_X68030) { + if (vmtype == VMType::X68030) { if ((spc.scmd & SPC::SCMD_PROGRAM)) { return (uint64)-1; } else { @@ -1086,7 +1070,14 @@ SPCDevice::WriteDREG(uint32 data) // DREG キューに入れて.. if (spc.dreg.Enqueue(data) == false) { - VMPANIC("DREG キューオーバーフロー"); + // XXX キューが一杯だったらどうなるのかは書いてないので分からない。 + // a) キューを押し出して先頭を破棄する + // b) キューの末尾を破棄して新着データで上書きする + // c) キューの内容は変わらず新着データを破棄する + // どれで実装しても「この時何かしら失われるデータがある」こと自体は + // 再現出来ているはずなので、ここではひとまず a) の動作にしておく。 + putlog(0, "DREG Queue overflow"); + spc.dreg.EnqueueForce(data); } // ハードウェア転送中で REQ が来てれば送信。 if (transfer_command_running && GetREQ()) { @@ -1161,7 +1152,7 @@ SPCDevice::SelectCommand() idstr = string_format("#%d -> #%d", myid, tgtid); } } - putlog(2, "Select コマンド (%s)", idstr.c_str()); + putlog(2, "Select command (%s)", idstr.c_str()); // アービトレーションありの場合 // @@ -1203,7 +1194,7 @@ SPCDevice::SelectCommand() // t_FRID/t_BFBL はたぶん今からではなくバスフリーになった時点から起算。 // t_FRID と t_BFBL は同じ式なのでここでは t_BFBL で統一。 uint64 t_BFBL = (6 + spc.GetTCL()) * t_CLF; // Min - uint64 now = gScheduler->GetVirtTime(); + uint64 now = scheduler->GetVirtTime(); if ((last_busfree_time + t_BFBL) > now) { wait = (last_busfree_time + t_BFBL) - now; } else { @@ -1219,7 +1210,7 @@ SPCDevice::SelectCommand() if (N != 0) { event_tc.func = ToEventCallback(&SPCDevice::SelectionTimeout); event_tc.time = (N + 15) * t_CLF * 2; - gScheduler->RestartEvent(event_tc); + scheduler->RestartEvent(event_tc); } if ((spc.sctl & SPC::SCTL_ARBIT_EN)) { @@ -1353,7 +1344,7 @@ SPCDevice::TransferCommand() // SCMD レジスタ書き込みのうち Transfer コマンドに関わる未実装機能の // ログ表示は SCMD レジスタ書き込み時ではなくここで表示。 if ((spc.scmd & SPC::SCMD_TERM_MODE)) { - putlog(0, "SCMD Padding mode not implemented"); + putlog(0, "SCMD Padding mode (NOT IMPLEMENTED)"); } transfer_command_running = true; @@ -1385,7 +1376,7 @@ SPCDevice::BusFreeCB(int id) putlog(4, "BusFree Callback"); inherited::BusFreeCB(id); - last_busfree_time = gScheduler->GetVirtTime(); + last_busfree_time = scheduler->GetVirtTime(); transfer_command_running = false; spc.ssts &= ~(SPC::SSTS_CONN_INIT | SPC::SSTS_CONN_TARG); @@ -1413,7 +1404,7 @@ SPCDevice::SelectionAckCB() NegateSEL(); // タイムアウトイベントを停止 - gScheduler->StopEvent(event_tc); + scheduler->StopEvent(event_tc); spc.ssts &= ~SPC::SSTS_SPC_BUSY; spc.ssts |= SPC::SSTS_CONN_INIT;