--- nono/vm/spc.cpp 2026/04/29 17:05:17 1.1.1.14 +++ nono/vm/spc.cpp 2026/04/29 17:05:24 1.1.1.15 @@ -86,13 +86,13 @@ SPCDevice::Init() // アクセスウェイト if (vmtype == VMType::X68030) { // InsideOut p.135 - read_wait = 18; - write_wait = 19; + read_wait = busdata::Wait(18 * 40_nsec); + write_wait = busdata::Wait(19 * 40_nsec); } else { // 他機種は未調査だけど、判明するまでの間とりあえず // ノーウェイトでない適当な値を入れておく。 - read_wait = 18; - write_wait = 19; + read_wait = busdata::Wait(18 * 40_nsec); + write_wait = busdata::Wait(19 * 40_nsec); } // 割り込み信号線の接続先 @@ -130,6 +130,7 @@ SPCDevice::ResetHard(bool poweron) // 内部状態 MakeIntsMask(); + ChangeInterrupt(); prev_ints = (uint32)-1; prev_ssts = (uint32)-1; set_atn_in_selection = false; @@ -140,128 +141,135 @@ SPCDevice::ResetHard(bool poweron) last_busfree_time = 0; } -uint64 +busdata SPCDevice::Read(uint32 offset) { - uint8 data; - - mpu->AddCycle(read_wait); + busdata data; switch (offset) { case SPC::BDID: data = 1 << myid; - putlog(3, "BDID -> $%02x", data); - return data; + putlog(3, "BDID -> $%02x", data.Data()); + break; case SPC::SCTL: data = spc.sctl; - putlog(3, "SCTL -> $%02x", data); - return data; + putlog(3, "SCTL -> $%02x", data.Data()); + break; case SPC::SCMD: data = spc.scmd; - putlog(3, "SCMD -> $%02x", data); - return data; + putlog(3, "SCMD -> $%02x", data.Data()); + break; case SPC::TMOD: // MB89352 には存在しないレジスタ。 // 下位2ビットは %0 らしいので意味はないだろうけど真似ておく。 - return 0xfc; + data = 0xfc; + break; case SPC::INTS: data = GetINTS(); if (loglevel >= 3) { + uint32 new_ints = data.Data(); if (event_tc.IsRunning()) { // イベント中は値が変わった時だけ表示 - if (prev_ints != data) { - putlogn("INTS -> $%02x", data); - prev_ints = data; + if (prev_ints != new_ints) { + putlogn("INTS -> $%02x", new_ints); + prev_ints = new_ints; } } else { - putlogn("INTS -> $%02x", data); + putlogn("INTS -> $%02x", new_ints); prev_ints = -1U; } } - return data; + break; case SPC::PSNS: data = GetPSNS(); - putlog(3, "PSNS -> $%02x", data); - return data; + putlog(3, "PSNS -> $%02x", data.Data()); + break; case SPC::SSTS: data = GetSSTS(); if (loglevel >= 3) { + uint32 new_ssts = data.Data(); if (event_tc.IsRunning()) { // イベント中は値が変わった時だけ表示 - if (prev_ssts != data) { - putlogn("SSTS -> $%02x", data); - prev_ssts = data; + if (prev_ssts != new_ssts) { + putlogn("SSTS -> $%02x", new_ssts); + prev_ssts = new_ssts; } } else { - putlogn("SSTS -> $%02x", data); + putlogn("SSTS -> $%02x", new_ssts); prev_ssts = -1U; } } - return data; + break; case SPC::SERR: data = GetSERR(); - putlog(3, "SERR -> $%02x", data); - return data; + putlog(3, "SERR -> $%02x", data.Data()); + break; case SPC::PCTL: data = GetPCTL(); - putlog(3, "PCTL -> $%02x", data); - return data; + putlog(3, "PCTL -> $%02x", data.Data()); + break; case SPC::MBC: putlog(0, "Read MBC (NOT IMPLEMENTED)"); - return 0x0f; + data = 0x0f; + break; case SPC::DREG: - return ReadDREG(); + data = ReadDREG(); + break; case SPC::TEMP: data = spc.temp_in; - putlog(3, "TEMP -> $%02x", data); - return data; + putlog(3, "TEMP -> $%02x", data.Data()); + break; case SPC::TCH: data = spc.tc >> 16; - putlog(3, "TCH -> $%02x", data); - return data; + putlog(3, "TCH -> $%02x", data.Data()); + break; case SPC::TCM: data = (spc.tc >> 8) & 0xff; - putlog(3, "TCM -> $%02x", data); - return data; + putlog(3, "TCM -> $%02x", data.Data()); + break; case SPC::TCL: data = spc.tc & 0xff; - putlog(3, "TCL -> $%02x", data); - return data; + putlog(3, "TCL -> $%02x", data.Data()); + break; case SPC::EXBF: // MB89352 には存在しないレジスタ - return 0xff; + data = 0xff; + break; default: VMPANIC("corrupted offset=%d", offset); } + + data |= read_wait; + return data; } -uint64 +busdata SPCDevice::Write(uint32 offset, uint32 data) { - mpu->AddCycle(write_wait); + busdata r; switch (offset) { case SPC::BDID: putlog(3, "BDID <- $%02x", data); if (myid == data) { // 同じなら何もしない - return 0; + break; } if (myid == -1) { // 初回なのでバスに参加 @@ -270,50 +278,52 @@ SPCDevice::Write(uint32 offset, uint32 d } else { // そうでなければ移動? putlog(0, "BDID change %d to %d (NOT IMPLEMENTED)", myid, data); - return 0; } - return 0; + break; case SPC::SCTL: WriteSCTL(data); - return 0; + break; case SPC::SCMD: WriteSCMD(data); - return 0; + break; case SPC::TMOD: // MB89352 には存在しないレジスタ - return 0; + break; case SPC::INTS: WriteINTS(data); - return 0; + break; case SPC::SDGC: spc.sdgc = data; putlog(3, "SDGC <- $%02x", spc.sdgc); - return 0; + break; case SPC::SSTS: // XXX 書き込みはどうなる? - return 0; + break; case SPC::SERR: // XXX 書き込みはどうなる? - return 0; + break; case SPC::PCTL: WritePCTL(data); - return 0; + break; case SPC::MBC: // XXX 書き込みはどうなる? - return 0; + break; case SPC::DREG: + // ここだけ Retry があるので戻り値に注意。 putlog(3, "DREG <- $%02x", data); - return WriteDREG(data); + r = WriteDREG(data); + r |= write_wait; + return r; case SPC::TEMP: spc.temp_out = data; @@ -334,31 +344,33 @@ SPCDevice::Write(uint32 offset, uint32 d if (GetIO() == false) { SetData(spc.temp_out); } - return 0; + break; case SPC::TCH: spc.tc = (spc.tc & 0x00ffff) | (data << 16); putlog(3, "TCH <- $%02x (TC=$%06x)", data, spc.tc); - return 0; + break; case SPC::TCM: spc.tc = (spc.tc & 0xff00ff) | (data << 8); putlog(3, "TCM <- $%02x (TC=$%06x)", data, spc.tc); - return 0; + break; case SPC::TCL: spc.tc = (spc.tc & 0xffff00) | data; putlog(3, "TCL <- $%02x (TC=$%06x)", data, spc.tc); - return 0; + break; case SPC::EXBF: // MB89352 には存在しないレジスタ - return 0; + break; default: VMPANIC("corrupted offset=%d", offset); } + + return write_wait; } -uint64 +busdata SPCDevice::Peek(uint32 offset) { switch (offset) { @@ -400,7 +412,7 @@ SPCDevice::Peek(uint32 offset) // すべてバスエラーになってしまう。 if (vmtype == VMType::X68030) { if ((spc.scmd & SPC::SCMD_PROGRAM)) { - return (uint64)-1; + return busdata::BusErr; } } return spc.dreg.Peek(0); @@ -982,23 +994,25 @@ SPCDevice::GetPCTL() const // DREG レジスタの読み込み // (副作用があるので Peek 系からは呼ばないこと) -uint64 +busdata SPCDevice::ReadDREG() { - uint32 data; + busdata data; // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、 // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが // すべてバスエラーになってしまう。 if (vmtype == VMType::X68030) { if ((spc.scmd & SPC::SCMD_PROGRAM)) { - return (uint64)-1; + data.SetBusErr(); + return data; } else { // DMA 転送で DREQ が出てないとき DTACK を出さないのを - // -2 を返すことで表現している + // Retry を返すことで表現している。 if (spc.dreg.Length() == 0) { putlog(3, "DREG empty during DMA transferring"); - return (uint64)-2; // DTACK まだ出てないよ + data.SetRetry(); + return data; } } } @@ -1014,9 +1028,9 @@ SPCDevice::ReadDREG() data = spc.dreg.Dequeue(); if (loglevel >= 4) { putlogn("DREG -> $%02x (q=%d TC=0x%x)", - data, spc.dreg.Length(), spc.tc); + data.Data(), spc.dreg.Length(), spc.tc); } else { - putlog(3, "DREG -> $%02x", data); + putlog(3, "DREG -> $%02x", data.Data()); } // この読み出しが最後だったらこれで転送完了。 if (spc.tc == 0 && spc.dreg.Length() == 0) { @@ -1032,7 +1046,7 @@ SPCDevice::ReadDREG() if ((spc.scmd & SPC::SCMD_PROGRAM)) { // XXX プログラム転送中ならどうなる? data = 0xff; - putlog(3, "DREG -> $%02x (empty)", data); + putlog(3, "DREG -> $%02x (empty)", data.Data()); } else { // DMA 転送 // DMA 側でリトライするのでここには来ないはず @@ -1046,20 +1060,24 @@ SPCDevice::ReadDREG() } // DREG レジスタへの書き込み -uint64 +busdata SPCDevice::WriteDREG(uint32 data) { + busdata r; + // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、 // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが // すべてバスエラーになってしまう。 if (vmtype == VMType::X68030) { if ((spc.scmd & SPC::SCMD_PROGRAM)) { - return (uint64)-1; + r.SetBusErr(); + return data; } else { // DMA 転送で DREQ が出てないとき DTACK を出さないのを - // -2 を返すことで表現している + // Retry を返すことで表現している。 if (spc.dreg.IsFull()) { - return (uint64)-2; + r.SetRetry(); + return r; } } } @@ -1084,7 +1102,7 @@ SPCDevice::WriteDREG(uint32 data) // XXX 時間は適当 CallAfter(HardwareTransfer, "SPC WriteDREG to Transfer", t_CLF); } - return 0; + return r; } // バスフリーフェーズに移行する