--- nono/vm/spc.cpp 2026/04/29 17:04:29 1.1.1.2 +++ nono/vm/spc.cpp 2026/04/29 17:04:36 1.1.1.4 @@ -1,10 +1,13 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "spc.h" -#include "mpu.h" +#include "mainapp.h" +#include "mpu680x0.h" +#include "mpu88xx0.h" #include "mystring.h" #include "scheduler.h" #include "vm.h" @@ -16,22 +19,22 @@ event.SetName(name_); \ event.code = 0; \ event.time = time_; \ - gScheduler->AddEvent(&event); \ + event.Start(); \ } while (0) -SPCDevice *gSPC; +std::unique_ptr gSPC; // コンストラクタ -SPCDevice::SPCDevice(uint32 addr) +SPCDevice::SPCDevice() { logname = "spc"; devname = "SPC"; - // X68k と LUNA でアドレスが違う。 - devaddr = addr; - // devlen は XIOSpace に必要なので LUNA ではダミー + // これらは XIOSpace で必要なので、LUNA ではダミー。 + // もうちょっといい方法を考えたほうがいい。 + devaddr = 0xe96000; devlen = 0x2000; - config_name = "spc0"; + SetConfigName("spc0"); myid = -1; monitor.Init(70, 15); @@ -59,16 +62,14 @@ SPCDevice::Init() return false; } - if (gVM->GetType() == VM::TYPE_LUNA) { - regbase = 0xe1000000; - regstride = 4; - // おそらく 6.144MHz が入っていると思われる - t_CLF = 163; - } else { - regbase = 0xe96021; - regstride = 2; - t_CLF = 200; - } + // 実行中に別インスタンスを頻繁に参照しないようにここで vmtype を覚えておく + vmtype = gMainApp.GetVMType(); + + // 入力クロックは機種によって異なる。 + // X68k は 5MHz が入っている。 + // LUNA-I はおそらく 6.144MHz だと思われる。LUNA88K は未確認。 + const ConfigItem& item = gConfig->Find("!spc-tCLF"); + t_CLF = item.AsInt(); return true; } @@ -79,7 +80,7 @@ void SPCDevice::ResetHard() { // LUNA は本体回路が分からないのでとりあえずこっち。 - if (gVM->GetType() == VM::TYPE_LUNA) { + if (vmtype != VMTYPE_X68030) { Reset(); } } @@ -89,7 +90,7 @@ void SPCDevice::ResetSoft() { // X680x0 はこっちだと分かっている。 - if (gVM->GetType() == VM::TYPE_X68030) { + if (vmtype == VMTYPE_X68030) { Reset(); } } @@ -113,29 +114,20 @@ SPCDevice::Reset() spc.sdgc = 0x00; // 内部状態 + prev_ints = (uint32)-1; + prev_ssts = (uint32)-1; set_atn_in_selection = false; transfer_command_running = false; - gScheduler->DelEvent(&event); - gScheduler->DelEvent(&event_tc); + event.Stop(); + event_tc.Stop(); } uint64 -SPCDevice::Read8(uint32 addr) +SPCDevice::Read(uint32 addr) { - uint32 an = addr - regbase; uint8 data; - // regbase を減算するとレジスタは常に +0 のバイトにある。 - // X68k で +1 (偶数アドレス) はバスエラーになる - // LUNA で +1,+2,+3 はミラー - if (gVM->GetType() == VM::TYPE_X68030) { - if ((an % regstride) != 0) { - return (uint64)-1; - } - } - an /= regstride; - - switch (an) { + switch (addr) { case SPC::BDID: data = 1 << myid; putlog(3, "BDID -> $%02x", data); @@ -158,16 +150,15 @@ SPCDevice::Read8(uint32 addr) case SPC::INTS: data = spc.ints; - if (loglevel >= 2) { - static uint32 prev_ints = -1U; - if (event.active) { + if (loglevel >= 3) { + if (event_tc.active) { // イベント中は値が変わった時だけ表示 if (prev_ints != data) { - putlog(3, "INTS -> $%02x", data); + putlog("INTS -> $%02x", data); prev_ints = data; } } else { - putlog(3, "INTS -> $%02x", data); + putlog("INTS -> $%02x", data); prev_ints = -1U; } } @@ -180,16 +171,15 @@ SPCDevice::Read8(uint32 addr) case SPC::SSTS: data = GetSSTS(); - if (loglevel >= 2) { - static uint32 prev_ssts = -1U; - if (event.active) { + if (loglevel >= 3) { + if (event_tc.active) { // イベント中は値が変わった時だけ表示 if (prev_ssts != data) { - putlog(3, "SSTS -> $%02x", data); + putlog("SSTS -> $%02x", data); prev_ssts = data; } } else { - putlog(3, "SSTS -> $%02x", data); + putlog("SSTS -> $%02x", data); prev_ssts = -1U; } } @@ -210,37 +200,7 @@ SPCDevice::Read8(uint32 addr) return 0xff; case SPC::DREG: - // XFER_OUT は DREG アクセスでリセットされる - spc.serr &= ~SPC::SERR_XFER_OUT; - - // 受信データがあれば取り出す - if (__predict_true(spc.dreg.Length() > 0)) { - bool q_was_full = spc.dreg.IsFull(); - data = spc.dreg.Dequeue(); - if (loglevel >= 4) { - putlog("DREG -> $%02x (q=%d TC=%d)", - data, spc.dreg.Length(), spc.tc); - } else { - putlog(3, "DREG -> $%02x", data); - } - // この読み出しが最後だったらここでビジーフラグを下ろす。 - // ProgramTransfer() 内のコメントも参照。 - if (spc.tc == 0 && spc.dreg.Length() == 0) { - transfer_command_running = false; - spc.ssts &= ~(SPC::SSTS_SPC_BUSY | SPC::SSTS_IN_PROGRESS); - SetINTS(SPC::INTS_COMPLETE); - } - // この読み出しによってバッファに空きが出来たら転送再開。 - if (transfer_command_running && GetREQ() && q_was_full) { - // XXX 時間は適当 - CallAfter(ProgramTransfer, "SPC ReadDREG to Transfer", t_CLF); - } - } else { - // XXX どうなる? - data = 0xff; - putlog(3, "DREG -> $%02x (empty)", data); - } - return data; + return ReadDREG(); case SPC::TEMP: data = spc.temp_in; @@ -266,38 +226,13 @@ SPCDevice::Read8(uint32 addr) // MB89352 には存在しないレジスタ return 0xff; } - PANIC("not reached"); -} - -uint64 -SPCDevice::Read16(uint32 addr) -{ - // XXX どうしたもんか - if (gVM->GetType() == VM::TYPE_LUNA) { - // LUNA はミラー - return (Read8(addr) << 8) | Read8(addr + 1); - } else { - // X68k は奇数アドレスのみ - return 0xff00 | Read8(addr + 1); - } + __unreachable(); } uint64 -SPCDevice::Write8(uint32 addr, uint32 data) +SPCDevice::Write(uint32 addr, uint32 data) { - uint32 an = addr - regbase; - - // regbase を減算するとレジスタは常に +0 のバイトにある。 - // X68k で +1 (偶数アドレス) はバスエラーになる - // LUNA で +1,+2,+3 はミラー - if (gVM->GetType() == VM::TYPE_X68030) { - if ((an % regstride) != 0) { - return (uint64)-1; - } - } - an /= regstride; - - switch (an) { + switch (addr) { case SPC::BDID: putlog(3, "BDID <- $%02x", data); if (myid == data) { @@ -314,6 +249,7 @@ SPCDevice::Write8(uint32 addr, uint32 da return 0; } return 0; + case SPC::SCTL: WriteSCTL(data); return 0; @@ -335,6 +271,14 @@ SPCDevice::Write8(uint32 addr, uint32 da putlog(3, "SDGC <- $%02x", spc.sdgc); return 0; + case SPC::SSTS: + // XXX 書き込みはどうなる? + return 0; + + case SPC::SERR: + // XXX 書き込みはどうなる? + return 0; + case SPC::PCTL: data &= SPC::PCTL_MASK; putlog(3, "PCTL <- $%02x", data); @@ -342,19 +286,13 @@ SPCDevice::Write8(uint32 addr, uint32 da SetXfer(data); return 0; + case SPC::MBC: + // XXX 書き込みはどうなる? + return 0; + case SPC::DREG: putlog(3, "DREG <- $%02x", data); - // XFER_OUT は DREG アクセスでリセットされる - spc.serr &= ~SPC::SERR_XFER_OUT; - - // DREG キューに入れて.. - spc.dreg.Enqueue(data); - // プログラム転送中で REQ が来てれば送信。 - if (transfer_command_running && GetREQ()) { - // XXX 時間は適当 - CallAfter(ProgramTransfer, "SPC WriteDREG to Transfer", t_CLF); - } - return 0; + return WriteDREG(data); case SPC::TEMP: spc.temp_out = data; @@ -394,113 +332,73 @@ SPCDevice::Write8(uint32 addr, uint32 da // MB89352 には存在しないレジスタ return 0; } - PANIC("not reached"); -} - -uint64 -SPCDevice::Write16(uint32 addr, uint32 data) -{ - // XXX どうしたもんか - if (gVM->GetType() == VM::TYPE_LUNA) { - // LUNA はミラーでバスエラーは起きない - Write8(addr, data >> 8); - Write8(addr + 1, data); - return 0; - } else { - // X68k は奇数アドレスのみ - return Write8(addr + 1, data); - } + __unreachable(); } uint64 -SPCDevice::Peek8(uint32 addr) +SPCDevice::Peek(uint32 addr) { - uint32 an = addr - regbase; - uint8 data; - - // regbase を減算するとレジスタは常に +0 のバイトにある。 - // X68k で +1 (偶数アドレス) はバスエラーになる - // LUNA では +1,+2,+3 はミラー - if (gVM->GetType() == VM::TYPE_X68030) { - if ((an % regstride) != 0) { - return (uint64)-1; - } - } - an /= regstride; - - switch (an) { + switch (addr) { case SPC::BDID: - data = 1 << myid; - break; + return 1 << myid; case SPC::SCTL: - data = spc.sctl; - break; + return spc.sctl; case SPC::SCMD: - data = spc.scmd; - break; + return spc.scmd; case SPC::TMOD: // MB89352 には存在しないレジスタ。 // 下位2ビットは %0 らしいので意味はないだろうけど真似ておく。 - data = 0xfc; - break; + return 0xfc; case SPC::INTS: - data = spc.ints; - break; + return spc.ints; case SPC::PSNS: - data = GetPSNS(); - break; + return GetPSNS(); case SPC::SSTS: - data = GetSSTS(); - break; + return GetSSTS(); case SPC::SERR: - data = spc.serr; - break; + return spc.serr; case SPC::PCTL: - data = GetPCTL(); - break; + return GetPCTL(); case SPC::MBC: - data = 0xff; - break; + return 0xff; case SPC::DREG: - data = spc.dreg.Peek(0); - break; + // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、 + // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが + // すべてバスエラーになってしまう。 + if (vmtype == VMTYPE_X68030) { + if ((spc.scmd & SPC::SCMD_PROGRAM)) { + return (uint64)-1; + } + } + return spc.dreg.Peek(0); case SPC::TEMP: - data = spc.temp_in; - break; + return spc.temp_in; case SPC::TCH: - data = spc.tc >> 16; - break; + return spc.tc >> 16; case SPC::TCM: - data = (spc.tc >> 8) & 0xff; - break; + return (spc.tc >> 8) & 0xff; case SPC::TCL: - data = spc.tc & 0xff; - break; + return spc.tc & 0xff; case SPC::EXBF: // MB89352 には存在しないレジスタ - data = 0xff; - break; - - default: - // XXX 未調査 - return (uint64)-1; + return 0xff; } - return data; + __unreachable(); } bool @@ -582,7 +480,7 @@ SPCDevice::MonitorUpdate() "(ResetACK/REQ)", "(Set ACK/REQ)", }; - monitor.Print(x, y, "%s", scmd_cmd[scmd >> 5]); + monitor.Puts(x, y, scmd_cmd[scmd >> 5]); y++; // INTS @@ -618,7 +516,7 @@ SPCDevice::MonitorUpdate() "(As Init)", "(undef?)", }; - monitor.Print(x, y, "%s", ssts_conn[ssts >> 6]); + monitor.Puts(x, y, ssts_conn[ssts >> 6]); y++; // SERR @@ -638,7 +536,7 @@ SPCDevice::MonitorUpdate() // バス状態 x = 52; y = 0; - const char *phname = ""; + SCSI::Phase ph {}; uint8 rst = 0; uint8 bsy = 0; uint8 sel = 0; @@ -650,7 +548,7 @@ SPCDevice::MonitorUpdate() uint8 ack = 0; uint8 data = 0; if (bus) { - phname = bus->GetPhaseName(); + ph = GetPhase(); rst = GetRST(); bsy = GetBSY(); sel = GetSEL(); @@ -662,56 +560,63 @@ SPCDevice::MonitorUpdate() ack = GetACK(); data = GetData(); } - monitor.Print(x, y++, "Phase: %s", phname); - monitor.Print(x, y++, "RST :%d", rst); - monitor.Print(x, y++, "BSY :%d(________)", bsy ? 1 : 0); - monitor.Print(x, y++, "SEL :%d(________)", sel ? 1 : 0); - monitor.Print(x, y++, "MSG :%d", msg); - monitor.Print(x, y++, "CD :%d", cd); - monitor.Print(x, y++, "IO :%d", io); - monitor.Print(x, y++, "ATN :%d", atn); - monitor.Print(x, y++, "REQ :%d", req); - monitor.Print(x, y++, "ACK :%d", ack); + monitor.Print(x, y++, "Phase: %s", SCSI::GetPhaseName(ph)); + monitor.Puts(x, y++, TA::OnOff(rst), "RST"); + monitor.Puts(x, y++, TA::OnOff(bsy), "BSY"); + monitor.Puts(x, y++, TA::OnOff(sel), "SEL"); + monitor.Puts(x, y++, TA::OnOff(msg), "MSG"); + monitor.Puts(x, y++, TA::OnOff(cd), "CD"); + monitor.Puts(x, y++, TA::OnOff(io), "IO"); + monitor.Puts(x, y++, TA::OnOff(atn), "ATN"); + monitor.Puts(x, y++, TA::OnOff(req), "REQ"); + monitor.Puts(x, y++, TA::OnOff(ack), "ACK"); monitor.Print(x, y++, "DATA:$%02x", data); + y = 2; + monitor.Puts(x + 4, y++, "(________)"); + monitor.Puts(x + 4, y++, "(________)"); + monitor.Puts(x + 4, y++, "+"); + monitor.Puts(x + 4, y++, "|"); + monitor.Puts(x + 4, y++, "+"); + // BSY, SEL の内訳。"(76543210)" の順。 - x = 59 + 7; y = 2; for (int i = 0; i < 8; i++) { if ((bsy & (1 << i))) { - monitor.Print(x - i, y, "%d", i); + monitor.Print(x + 4 + 8 - i, y, "%d", i); } else { - monitor.Print(x - i, y, "_"); + monitor.Print(x + 4 + 8 - i, y, "_"); } } y++; for (int i = 0; i < 8; i++) { if ((sel & (1 << i))) { - monitor.Print(x - i, y, "%d", i); + monitor.Print(x + 4 + 8 - i, y, "%d", i); } else { - monitor.Print(x - i, y, "_"); + monitor.Print(x + 4 + 8 - i, y, "_"); } } // 転送フェーズ中なら現在の XferPhase - x = 58; y += 2; - if (GetPhase() == SCSI::Phase::Transfer) { - monitor.Print(x, y, "(%s)", SCSI::GetXferPhaseName(GetXfer())); + if (ph == SCSI::Phase::Transfer) { + monitor.Puts(x + 5, y, SCSI::GetXferPhaseName(GetXfer())); } // ターゲット x = 0; y = 14; - SCSIDevice *d = bus->GetConnectedTarget(); - SCSITarget *t = dynamic_cast(d); - if (t) { - std::vector cmds = t->GetCmdSeq(); - if (cmds.size() > 0) { - const char *cmdname = SCSI::GetCommandName(cmds[0]); - monitor.Print(x, y, "Command: $%02x", cmds[0]); - if (cmdname) { - monitor.Print(x + 14, y, "%s", cmdname); + if (bus) { + SCSIDevice *d = bus->GetConnectedTarget(); + SCSITarget *t = dynamic_cast(d); + if (t) { + std::vector cmds = t->GetCmdSeq(); + if (cmds.size() > 0) { + const char *cmdname = SCSI::GetCommandName(cmds[0]); + monitor.Print(x, y, "Command: $%02x", cmds[0]); + if (cmdname) { + monitor.Puts(x + 14, y, cmdname); + } } } } @@ -725,8 +630,7 @@ SPCDevice::MonitorReg(int x, int y, uint { for (int i = 0; i < 8; i++) { bool b = reg & (1 << (7 - i)); - uint attr = b ? TA::On : TA::Off; - monitor.Print(x + i * 5, y, attr, "%s", names[i]); + monitor.Puts(x + i * 5, y, TA::OnOff(b), names[i]); } } @@ -829,6 +733,7 @@ SPCDevice::WriteINTS(uint32 data) // '1' が書き込まれたビットをクリア spc.ints &= ~data; + putlog(3, "INTS <- $%02x (INTS = $%02x)", data, spc.ints); // SPC では INTS でビットを落とす行為は、 // ホストが割り込み要因に対して処理を完了したことを @@ -847,15 +752,12 @@ SPCDevice::WriteINTS(uint32 data) if (spc.tc == 0) { BusFree(); } else { - event.time = spc.tc * t_CLF * 2; - event.func = (DeviceCallback_t)&SPCDevice::SelectionTimeout; - event.SetName("SPC SelectionFailed"); - gScheduler->AddEvent(&event); + event_tc.time = spc.tc * t_CLF * 2; + event_tc.func = (DeviceCallback_t)&SPCDevice::SelectionTimeout; + event_tc.Start(); } } } - - putlog(3, "INTS <- $%02x (INTS = $%02x)", data, spc.ints); } // INTS 設定。 @@ -880,11 +782,11 @@ SPCDevice::SetINTS(uint32 data) // RESET_COND はノンマスカブル割り込み。 if ((spc.sctl & SPC::SCTL_INTR_EN) || (spc.ints & SPC::INTS_RESET_COND)) { putlog(3, "割り込み ints=$%02x up=$%02x", spc.ints, up); - if (gVM->GetType() == VM::TYPE_LUNA) { - gMPU->Interrupt(2); + if (vmtype != VMTYPE_X68030) { + gMPU->Interrupt(this, 2); } else { // とりあえず内蔵だけ - gMPU->Interrupt(1, 0x6c); + gMPU680x0->Interrupt(this, 1, 0x6c); } } } @@ -930,7 +832,7 @@ SPCDevice::GetSSTS() const uint32 data; data = spc.ssts & 0xf0; - if (__predict_true(bus) && GetRST()) { // RST 信号 + if (__predict_true(bus != NULL) && GetRST()) { // RST 信号 data |= SPC::SSTS_RST_IN; } if (spc.tc == 0) { // TC がゼロか @@ -955,7 +857,7 @@ SPCDevice::GetPCTL() const uint32 data; data = 0; - if (__predict_true(bus)) { + if (__predict_true(bus != NULL)) { data |= (uint32)GetXfer(); } if (spc.busfree_intr_enable) { @@ -964,6 +866,85 @@ SPCDevice::GetPCTL() const return data; } +// DREG レジスタの読み込み +// (副作用があるので Peek 系からは呼ばないこと) +uint64 +SPCDevice::ReadDREG() +{ + uint32 data; + + // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、 + // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが + // すべてバスエラーになってしまう。 + if (vmtype == VMTYPE_X68030) { + if ((spc.scmd & SPC::SCMD_PROGRAM)) { + return (uint64)-1; + } + } + + // XFER_OUT は DREG アクセスでリセットされる + spc.serr &= ~SPC::SERR_XFER_OUT; + + // 受信データがあれば取り出す + if (__predict_true(spc.dreg.Length() > 0)) { + bool q_was_full = spc.dreg.IsFull(); + data = spc.dreg.Dequeue(); + if (loglevel >= 4) { + putlog("DREG -> $%02x (q=%d TC=0x%x)", + data, spc.dreg.Length(), spc.tc); + } else { + putlog(3, "DREG -> $%02x", data); + } + // この読み出しが最後だったらこれで転送完了。 + if (spc.tc == 0 && spc.dreg.Length() == 0) { + TransferComplete(); + } + // この読み出しによってバッファに空きが出来たら転送再開。 + if (transfer_command_running && GetREQ() && q_was_full) { + // XXX 時間は適当 + CallAfter(HardwareTransfer, "SPC ReadDREG to Transfer", t_CLF); + } + } else { + // 受信データがない場合 + if ((spc.scmd & SPC::SCMD_PROGRAM)) { + // XXX プログラム転送中ならどうなる? + data = 0xff; + putlog(3, "DREG -> $%02x (empty)", data); + } else { + // DMA 転送 + // XXX どうする? + PANIC("DMA転送中の DREG empty"); + } + } + return data; +} + +// DREG レジスタへの書き込み +uint64 +SPCDevice::WriteDREG(uint32 data) +{ + // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、 + // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが + // すべてバスエラーになってしまう。 + if (vmtype == VMTYPE_X68030) { + if ((spc.scmd & SPC::SCMD_PROGRAM)) { + return (uint64)-1; + } + } + + // XFER_OUT は DREG アクセスでリセットされる + spc.serr &= ~SPC::SERR_XFER_OUT; + + // DREG キューに入れて.. + spc.dreg.Enqueue(data); + // ハードウェア転送中で REQ が来てれば送信。 + if (transfer_command_running && GetREQ()) { + // XXX 時間は適当 + CallAfter(HardwareTransfer, "SPC WriteDREG to Transfer", t_CLF); + } + return 0; +} + // バスフリーフェーズに移行する void SPCDevice::BusFree() @@ -1087,7 +1068,7 @@ SPCDevice::SelectCommand() if (N != 0) { event_tc.time = (N + 15) * t_CLF * 2; event_tc.func = (DeviceCallback_t)&SPCDevice::SelectionTimeout; - gScheduler->AddEvent(&event_tc); + event_tc.Start(); } if ((spc.sctl & SPC::SCTL_ARBIT_EN)) { @@ -1207,7 +1188,7 @@ SPCDevice::SetATNCommand() void SPCDevice::TransferCommand() { - putlog(2, "SCMD Transfer Command (xfer=%s len=%d)", + putlog(2, "SCMD Transfer Command (xfer=%s len=0x%x)", SCSI::GetXferPhaseName(GetPCTL() & 7), spc.tc); // SCMD レジスタ書き込みのうち Transfer コマンドに関わる未実装機能の @@ -1215,9 +1196,6 @@ SPCDevice::TransferCommand() if ((spc.scmd & SPC::SCMD_TERM_MODE)) { putlog(0, "SCMD Padding mode not implemented"); } - if ((spc.scmd & SPC::SCMD_PROGRAM) == 0) { - putlog(0, "SCMD DMA transfer mode not implemented"); - } if ((spc.scmd & SPC::SCMD_INTERCEPT)) { putlog(0, "SCMD Intercept Transfer not implemented"); } @@ -1276,7 +1254,7 @@ SPCDevice::SelectionAckCB() NegateSEL(); // タイムアウトイベントを停止 - gScheduler->DelEvent(&event_tc); + event_tc.Stop(); spc.ssts &= ~SPC::SSTS_SPC_BUSY; spc.ssts |= SPC::SSTS_CONN_INIT; @@ -1293,17 +1271,17 @@ SPCDevice::TransferReqCB() spc.ssts |= SPC::SSTS_IN_PROGRESS; if (transfer_command_running) { - // プログラム転送で SDGC_XFER_ENABLE がセットされていれば、 + // ハードウェア転送で SDGC_XFER_ENABLE がセットされていれば、 // データ要求が来ている時に XFER_OUT をセット(?) if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) { spc.serr |= SPC::SERR_XFER_OUT; } // Transfer Command 実行中なら REQ 待ちだったので - // ここでプログラム転送を開始。 + // ここでハードウェア転送を開始。 TransferCommandStart(0); } else { - // プログラム転送中でなくて IN 方向ならここで TEMP に取り込む。 + // ハードウェア転送中でなくて IN 方向ならここで TEMP に取り込む。 if (GetIO()) { spc.temp_in = GetData(); } @@ -1319,20 +1297,20 @@ SPCDevice::TransferCommandStart(int dumm // SCSI バスの状態と PCTL レジスタの下位3ビットの状態を比較する。 // 一致していれば転送開始、そうでなければ Service Required 割り込み。 if ((GetPCTL() & 7) == GetXfer()) { - ProgramTransfer(0); + HardwareTransfer(0); } else { transfer_command_running = false; SetINTS(SPC::INTS_SERV_REQ); } } -// プログラム転送の1バイト分。 +// ハードウェア転送の1バイト分。 // イベントコールバックとして呼び出すこと。 // REQ がアサートされているのを確認してから呼び出すこと。 // (1バイト送信するとターゲットが再び REQ を上げるなどしてくるので、 // TransferReqCB() か DREG 書き込みから再びここが呼ばれる) void -SPCDevice::ProgramTransfer(int dummy) +SPCDevice::HardwareTransfer(int dummy) { // SDGC_XFER_ENABLE は転送要求ごとに割り込みを上げる。 // (SPC から見て) 入力操作時は読み込めるデータがある場合、出力操作時は @@ -1349,7 +1327,7 @@ SPCDevice::ProgramTransfer(int dummy) SetINTS(0); } if (!queued) { - putlog(4, "Program Transfer (DREG full)"); + putlog(4, "Hardware Transfer (DREG full)"); return; } } else { @@ -1357,7 +1335,7 @@ SPCDevice::ProgramTransfer(int dummy) if (spc.dreg.Length() != 0) { SetData(spc.dreg.Dequeue()); } else { - putlog(4, "Program Transfer (DREG empty)"); + putlog(4, "Hardware Transfer (DREG empty)"); // SDGC_XFER_ENABLE ならキューに書き込む必要がある場合に割り込み。 if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) { putlog(3, "SDGC XFER_OUT Interrupt"); @@ -1368,7 +1346,7 @@ SPCDevice::ProgramTransfer(int dummy) } // SDGC XFER_OUT のログと順序が微妙になるけど、 // DREG full/empty で2行出るのを避けたいので。 - putlog(4, "Program Transfer"); + putlog(4, "Hardware Transfer"); // XXX XFER_OUT はどこで落とすんやろうか。 // データ要求が来たら上げるのだから用が済んだら下ろすのかしら。 @@ -1378,21 +1356,18 @@ SPCDevice::ProgramTransfer(int dummy) // 転送完了 spc.tc--; - putlog(4, "Program Transfer: %s q=%d tc=%d", + putlog(4, "Hardware Transfer: %s q=%d tc=%d", (GetIO() ? "IN" : "OUT"), spc.dreg.Length(), spc.tc); - // TC がゼロになったら完了。 if (spc.tc == 0) { + // どちらでも転送自体はここで完了(?) transfer_command_running = false; - SetINTS(SPC::INTS_COMPLETE); - // SSTS の SPC Busy と Xfer In Progress は、TC==0 だけでは下げずに - // DREG キューが空になるまで待つようだ。 - // この2つのビットはハードウェア転送を実行中の時立てるように読める - // ので転送終わると下げてもよさそうなのだが、ここで下げてしまうと - // LUNA の PROM がステータスフェーズ受信中に SSTS を読んで転送開始 - // 前と誤認してしまうようだ。Read() の DREG のところも参照。 - if (spc.dreg.Length() == 0) { - spc.ssts &= ~(SPC::SSTS_SPC_BUSY | SPC::SSTS_IN_PROGRESS); + if (GetIO()) { + // Input 時は TC がゼロになって DREG キューを全部読み出した + // ところで完了なので、ここでは何もしない。 + } else { + // Output ならここで転送完了。 + TransferComplete(); } } @@ -1407,3 +1382,12 @@ SPCDevice::ProgramTransfer(int dummy) NegateACK(); } } + +// 転送完了。 +// HardwareTransfer() と Read() から呼ばれる。 +void +SPCDevice::TransferComplete() +{ + spc.ssts &= ~(SPC::SSTS_SPC_BUSY | SPC::SSTS_IN_PROGRESS); + SetINTS(SPC::INTS_COMPLETE); +}