--- nono/vm/spc.cpp 2026/04/29 17:05:24 1.1.1.15 +++ nono/vm/spc.cpp 2026/04/29 17:05:28 1.1.1.16 @@ -10,36 +10,26 @@ #include "spc.h" #include "config.h" -#include "interrupt.h" #include "mainapp.h" -#include "mpu.h" #include "pedec.h" #include "scheduler.h" +#include "scsidev.h" // time 時間後に呼び出すコールバックをセットする。 // func の書式が面倒なのを省略して書きたいため。 #define CallAfter(func_, name_, time_) do { \ - event.func = ToEventCallback(&SPCDevice::func_); \ - event.time = time_; \ - event.SetName(name_); \ - scheduler->RestartEvent(event); \ + phase_event.func = ToEventCallback(&SPCDevice::func_); \ + phase_event.time = time_; \ + phase_event.SetName(name_); \ + scheduler->RestartEvent(phase_event); \ } while (0) // コンストラクタ SPCDevice::SPCDevice() : inherited(OBJ_SPC) { - SetConfigName("spc0"); myid = -1; - // 汎用イベント - // 名前とコールバック関数は都度セットする。 - event.Regist("SPC Phase"); - - // TC カウンタイベント - event_tc.code = 0; - event_tc.Regist("SPC Timer"); - monitor.func = ToMonitorCallback(&SPCDevice::MonitorUpdate); // サイズは Init で決まる monitor.Regist(ID_MONITOR_SPC); @@ -50,33 +40,36 @@ SPCDevice::~SPCDevice() { } -// 初期化 bool -SPCDevice::Init() +SPCDevice::Create() { - // inherited::Init() は少し下で呼んでいる - - // 実行中に別インスタンスを頻繁に参照しないようにここで vmtype を覚えておく - vmtype = gMainApp.GetVMType(); - - // 本体 SCSI ID は LUNA も X68030 も 7 固定。 - uint initiator_id = 7; + if (inherited::Create() == false) { + return false; + } - // モニタには、接続している SCSI デバイスだけを表示する。 - // そのため ID ではなく前から何番目のデバイスかのリストが欲しい。 - for (int id = 0; id < 8; id++) { - if (id == initiator_id) { - connected_devices.push_back(this); - } else if (target[id].get()) { - connected_devices.push_back(target[id].get()); - } + scsi.reset(new SCSIHostDevice(this, "spc0")); + if ((bool)scsi == false) { + return false; } + scsi->SetCallback( + ToBusFreeCallback(&SPCDevice::BusFreeCallback), + ToSCSIHostCallback(&SPCDevice::SelectionAckCallback), + ToSCSIHostCallback(&SPCDevice::TransferReqCallback)); - // この親の Init() は connected_devices 作成後に呼ぶ + return true; +} + +// 初期化 +bool +SPCDevice::Init() +{ if (inherited::Init() == false) { return false; } + // 実行中に別インスタンスを頻繁に参照しないようにここで vmtype を覚えておく + vmtype = gMainApp.GetVMType(); + // 入力クロックは機種によって異なる。 // X68k は 5MHz が入っている。 // LUNA-I はおそらく 6.144MHz だと思われる。LUNA-88K は未確認。 @@ -103,7 +96,26 @@ SPCDevice::Init() interrupt = GetInterruptDevice(); } - monitor.SetSize(70, 15 + connected_devices.size()); + // 汎用イベント + // 名前とコールバック関数は都度セットする。 + phase_event.SetName("SPC Phase"); + scheduler->RegistEvent(phase_event); + + // TC カウンタイベント + timer_event.code = 0; + timer_event.SetName("SPC Timer"); + scheduler->RegistEvent(timer_event); + + // モニタに表示するアドレス + if (vmtype == VMType::X68030) { + baseaddr = 0xe96001; + regoffset = 2; + } else { + baseaddr = 0xe1000000; + regoffset = 4; + } + // 「イニシエータ以外のターゲット数」はここでは確定している。 + monitor.SetSize(80, 15 + scsi->GetOwnedBus()->GetTargetCount() + 1); return true; } @@ -135,14 +147,14 @@ SPCDevice::ResetHard(bool poweron) prev_ssts = (uint32)-1; set_atn_in_selection = false; transfer_command_running = false; - scheduler->StopEvent(event); - scheduler->StopEvent(event_tc); + scheduler->StopEvent(phase_event); + scheduler->StopEvent(timer_event); last_busfree_time = 0; } busdata -SPCDevice::Read(uint32 offset) +SPCDevice::ReadPort(uint32 offset) { busdata data; @@ -172,7 +184,7 @@ SPCDevice::Read(uint32 offset) data = GetINTS(); if (loglevel >= 3) { uint32 new_ints = data.Data(); - if (event_tc.IsRunning()) { + if (timer_event.IsRunning()) { // イベント中は値が変わった時だけ表示 if (prev_ints != new_ints) { putlogn("INTS -> $%02x", new_ints); @@ -194,7 +206,7 @@ SPCDevice::Read(uint32 offset) data = GetSSTS(); if (loglevel >= 3) { uint32 new_ssts = data.Data(); - if (event_tc.IsRunning()) { + if (timer_event.IsRunning()) { // イベント中は値が変わった時だけ表示 if (prev_ssts != new_ssts) { putlogn("SSTS -> $%02x", new_ssts); @@ -252,34 +264,40 @@ SPCDevice::Read(uint32 offset) break; default: - VMPANIC("corrupted offset=%d", offset); + VMPANIC("corrupted offset=%u", offset); } data |= read_wait; + data |= BusData::Size1; return data; } busdata -SPCDevice::Write(uint32 offset, uint32 data) +SPCDevice::WritePort(uint32 offset, uint32 data) { busdata r; switch (offset) { case SPC::BDID: + { + uint cur_myid = scsi->GetMyID(); putlog(3, "BDID <- $%02x", data); - if (myid == data) { + if (data == cur_myid) { // 同じなら何もしない break; } - if (myid == -1) { - // 初回なのでバスに参加 + if ((int)cur_myid < 0) { + // 初回なのでバスに参加。myid はこちらでもコピーを覚えておく。 myid = data; - scsibus->Attach(this, myid); + if (scsi->GetOwnedBus()->AttachDevice(scsi.get(), myid) == false) { + putlog(0, "BDID %u conflicts with existing devices!", myid); + } } else { // そうでなければ移動? - putlog(0, "BDID change %d to %d (NOT IMPLEMENTED)", myid, data); + putlog(0, "BDID change %u to %u (NOT IMPLEMENTED)", cur_myid, data); } break; + } case SPC::SCTL: WriteSCTL(data); @@ -319,11 +337,9 @@ SPCDevice::Write(uint32 offset, uint32 d break; case SPC::DREG: - // ここだけ Retry があるので戻り値に注意。 putlog(3, "DREG <- $%02x", data); r = WriteDREG(data); - r |= write_wait; - return r; + break; case SPC::TEMP: spc.temp_out = data; @@ -337,12 +353,12 @@ SPCDevice::Write(uint32 offset, uint32 d // SPC はセレクションタイムアウトしたからと言って、データバスに出した // ID を自動的に取り下げたりはしないので、TEMP に $00 を書き込むことで // データバスをクリアする、だったんでは。 - if (0 && GetPhase() == SCSI::Phase::Selection) { - NegateSEL(); + if (0 && scsi->GetPhase() == SCSI::Phase::Selection) { + scsi->NegateSEL(); } // Out 方向ならバスに出す - if (GetIO() == false) { - SetData(spc.temp_out); + if (scsi->GetIO() == false) { + scsi->SetData(spc.temp_out); } break; @@ -364,14 +380,16 @@ SPCDevice::Write(uint32 offset, uint32 d break; default: - VMPANIC("corrupted offset=%d", offset); + VMPANIC("corrupted offset=%u", offset); } - return write_wait; + r |= write_wait; + r |= BusData::Size1; + return r; } busdata -SPCDevice::Peek(uint32 offset) +SPCDevice::PeekPort(uint32 offset) { switch (offset) { case SPC::BDID: @@ -412,7 +430,7 @@ SPCDevice::Peek(uint32 offset) // すべてバスエラーになってしまう。 if (vmtype == VMType::X68030) { if ((spc.scmd & SPC::SCMD_PROGRAM)) { - return busdata::BusErr; + return BusData::BusErr; } } return spc.dreg.Peek(0); @@ -438,11 +456,18 @@ SPCDevice::Peek(uint32 offset) } } +bool +SPCDevice::PokePort(uint32 offset, uint32 data) +{ + return false; +} + void SPCDevice::MonitorUpdate(Monitor *, TextScreen& screen) { int x; int y; + SCSIBus *bus; uint32 sctl; uint32 scmd; uint32 ints; @@ -455,6 +480,7 @@ SPCDevice::MonitorUpdate(Monitor *, Text screen.Clear(); + bus = scsi->GetOwnedBus(); sctl = spc.sctl; scmd = spc.scmd; ints = GetINTS(); @@ -465,33 +491,35 @@ SPCDevice::MonitorUpdate(Monitor *, Text pctl = GetPCTL(); tc = spc.tc; +#define A(n) (baseaddr + (n) * regoffset) + y = 0; - screen.Print(0, y++, "BDID: $%02x (ID=%d)", (1 << myid), myid); - screen.Print(0, y++, "SCTL: $%02x", sctl); - screen.Print(0, y++, "SCMD: $%02x", scmd); - screen.Print(0, y++, "INTS: $%02x", ints); - screen.Print(0, y++, "PSNS: $%02x", psns); - screen.Print(0, y++, "SDGC: $%02x", sdgc); - screen.Print(0, y++, "SSTS: $%02x", ssts); - screen.Print(0, y++, "SERR: $%02x", serr); - screen.Print(0, y++, "PCTL: $%02x", pctl); - screen.Print(0, y++, "MBC : $XX"); - screen.Print(0, y, "DREG: "); + screen.Print(0, y++, "$%08x BDID: $%02x (ID=%u)", A(0), (1 << myid), myid); + screen.Print(0, y++, "$%08x SCTL: $%02x", A(1), sctl); + screen.Print(0, y++, "$%08x SCMD: $%02x", A(2), scmd); + screen.Print(0, y++, "$%08x INTS: $%02x", A(4), ints); + screen.Print(0, y++, "$%08x PSNS: $%02x", A(5), psns); + screen.Print(0, y++, "$%08x SDGC: $%02x", A(5), sdgc); + screen.Print(0, y++, "$%08x SSTS: $%02x", A(6), ssts); + screen.Print(0, y++, "$%08x SERR: $%02x", A(7), serr); + screen.Print(0, y++, "$%08x PCTL: $%02x", A(8), pctl); + screen.Print(0, y++, "$%08x MBC : $XX", A(9)); + screen.Print(0, y, "$%08x DREG: ", A(10)); { uint i; for (i = 0; i < spc.dreg.Length(); i++) { - screen.Print(6 + i * 4, y, "$%02x", spc.dreg.Peek(i)); + screen.Print(16 + i * 4, y, "$%02x", spc.dreg.Peek(i)); } for (; i < 8; i++) { - screen.Puts(6 + i * 4, y, "---"); + screen.Puts(16 + i * 4, y, "---"); } } y++; - screen.Print(0, y++, "TEMP: $%02x(IN) $%02x(OUT)", + screen.Print(0, y++, "$%08x TEMP: $%02x(IN) $%02x(OUT)", A(11), spc.temp_in, spc.temp_out); - screen.Print(0, y++, "TC : $%06x", tc); + screen.Print(0, y++, "$%08x TC : $%06x", A(12), tc); - x = 10; + x = 20; y = 1; // SCTL @@ -571,32 +599,20 @@ SPCDevice::MonitorUpdate(Monitor *, Text y++; // バス状態 - x = 52; + x = 62; y = 0; - SCSI::Phase ph {}; - uint8 rst = 0; - uint8 bsy = 0; - uint8 sel = 0; - uint8 msg = 0; - uint8 cd = 0; - uint8 io = 0; - uint8 atn = 0; - uint8 req = 0; - uint8 ack = 0; - uint8 data = 0; - if (bus) { - ph = GetPhase(); - rst = GetRST(); - bsy = GetBSY(); - sel = GetSEL(); - msg = GetMSG(); - cd = GetCD(); - io = GetIO(); - atn = GetATN(); - req = GetREQ(); - ack = GetACK(); - data = GetData(); - } + SCSI::Phase ph = bus->GetPhase(); + uint8 rst = bus->GetRST(); + uint8 bsy = bus->GetBSY(); + uint8 sel = bus->GetSEL(); + uint8 msg = bus->GetMSG(); + uint8 cd = bus->GetCD(); + uint8 io = bus->GetIO(); + uint8 atn = bus->GetATN(); + uint8 req = bus->GetREQ(); + uint8 ack = bus->GetACK(); + uint8 data = bus->GetData(); + screen.Print(x, y++, "Phase: %s", SCSI::GetPhaseName(ph)); screen.Puts(x, y++, TA::OnOff(rst), "RST"); screen.Puts(x, y++, TA::OnOff(bsy), "BSY"); @@ -620,7 +636,7 @@ SPCDevice::MonitorUpdate(Monitor *, Text y = 2; for (int i = 0; i < 8; i++) { if ((bsy & (1 << i))) { - screen.Print(x + 4 + 8 - i, y, "%d", i); + screen.Print(x + 4 + 8 - i, y, "%u", i); } else { screen.Print(x + 4 + 8 - i, y, "_"); } @@ -628,7 +644,7 @@ SPCDevice::MonitorUpdate(Monitor *, Text y++; for (int i = 0; i < 8; i++) { if ((sel & (1 << i))) { - screen.Print(x + 4 + 8 - i, y, "%d", i); + screen.Print(x + 4 + 8 - i, y, "%u", i); } else { screen.Print(x + 4 + 8 - i, y, "_"); } @@ -637,7 +653,7 @@ SPCDevice::MonitorUpdate(Monitor *, Text // 転送フェーズ中なら現在の XferPhase y += 2; if (ph == SCSI::Phase::Transfer) { - screen.Puts(x + 5, y, SCSI::GetXferPhaseName(GetXfer())); + screen.Puts(x + 5, y, SCSI::GetXferPhaseName(scsi->GetXfer())); } // デバイス一覧 @@ -649,20 +665,20 @@ SPCDevice::MonitorUpdate(Monitor *, Text x = 0; y = 14; screen.Print(x, y++, "ID Device Command"); - for (const auto d : connected_devices) { + for (const auto d : bus->GetConnectedDevices()) { std::string desc = SCSI::GetDevTypeName(d->GetDevType()); - SCSIDisk *disk = dynamic_cast(d); + const SCSIDisk *disk = dynamic_cast(d); if (disk && disk->IsMediumLoaded()) { - desc += string_format("(%dMB)", - (int)(disk->GetSize() / 1024 / 1024)); + desc += string_format("(%uMB)", + (uint)(disk->GetSize() / 1024 / 1024)); } // 本体 ID は BDID が設定される前でもエミュレータ的に知ってはいるが、 // BDID への書き込みがされなかったりおかしかったりしたら (上の BDID // 欄でも分かるけど) ここも反映されるようにしておく。 char idc; - if (d->GetMyID() >= 0) { + if ((int)d->GetMyID() >= 0) { idc = '0' + d->GetMyID(); } else { idc = '-'; @@ -674,26 +690,23 @@ SPCDevice::MonitorUpdate(Monitor *, Text // 実行中のコマンド x = 14; y = 15; - if (bus) { - SCSIDevice *d = bus->GetConnectedTarget(); - SCSITarget *t = dynamic_cast(d); - if (t) { - std::vector cmds = t->GetCmdSeq(); - if (cmds.size() > 0) { - // この ID のデバイス行に表示したい - // XXX もうちょっとマシにしたい - int i = 0; - for (; i < connected_devices.size(); i++) { - if (d == connected_devices[i]) { - break; - } + SCSITarget *t = bus->GetSelectedTarget(); + if (t) { + std::vector cmds = t->GetCmdSeq(); + if (cmds.size() > 0) { + // この ID のデバイス行に表示したい + // XXX もうちょっとマシにしたい + const auto connected_devices = bus->GetConnectedDevices(); + int i = 0; + for (; i < connected_devices.size(); i++) { + if (t == connected_devices[i]) { + break; } - const char *cmdname = SCSI::GetCommandName(cmds[0]); - screen.Print(x, y + i, "$%02x %s", cmds[0], cmdname ?: ""); } + const char *cmdname = SCSI::GetCommandName(cmds[0]); + screen.Print(x, y + i, "$%02x %s", cmds[0], cmdname ?: ""); } } - } // レジスタをビットごとに表示する。MonitorUpdate の下請け。 @@ -765,16 +778,16 @@ SPCDevice::WriteSCMD(uint32 data) if ((spc.scmd & SPC::SCMD_RST_OUT)) { // 0 -> 1 // すでに RST が立ってたらどうするか - if (GetRST() == false) { + if (scsi->GetRST() == false) { putlog(1, "SCMD RST Out"); BusRelease(); - AssertRST(); + scsi->AssertRST(); SetINTS(SPC::INTS_RESET_COND); } } else { // 1 -> 0 // 特にすることはない? - NegateRST(); + scsi->NegateRST(); } } @@ -807,7 +820,7 @@ SPCDevice::WriteSCMD(uint32 data) putlog(2, "SCMD Reset ACK/REQ command"); // イニシエータなら ACK を、ターゲットなら REQ をリセットする。 // ターゲットモードはサポートしないので、操作するのは ACK のみ。 - NegateACK(); + scsi->NegateACK(); break; case 7: putlog(2, "SCMD Set ACK/REQ command"); @@ -822,7 +835,7 @@ SPCDevice::WriteSCMD(uint32 data) putlog(0, "SCMD Intercept with SetACK (NOT IMPLEMENTED)"); } } - AssertACK(); + scsi->AssertACK(); break; } } @@ -847,15 +860,16 @@ SPCDevice::WriteINTS(uint32 data) // TIMEOUT ビットをクリアした時が if ((ack & SPC::INTS_TIMEOUT)) { // セレクションフェーズなら - if (GetPhase() == SCSI::Phase::Selection) { + if (scsi->GetPhase() == SCSI::Phase::Selection) { // TC がゼロならバスフリー // そうでなければリスタート if (spc.tc == 0) { BusFree(); } else { - event_tc.func = ToEventCallback(&SPCDevice::SelectionTimeout); - event_tc.time = spc.tc * t_CLF * 2; - scheduler->RestartEvent(event_tc); + timer_event.func = + ToEventCallback(&SPCDevice::SelectionTimeout); + timer_event.time = spc.tc * t_CLF * 2; + scheduler->RestartEvent(timer_event); } } } @@ -874,8 +888,8 @@ SPCDevice::SetINTS(uint32 data) spc.ints |= data; // Disconnected 割り込みが起きたら ATN を下げる - if ((spc.ints & SPC::INTS_DISCONNECTED) && GetATN()) { - NegateATN(); + if ((spc.ints & SPC::INTS_DISCONNECTED) && scsi->GetATN()) { + scsi->NegateATN(); } ChangeInterrupt(); @@ -907,39 +921,39 @@ SPCDevice::ChangeInterrupt() } // PSNS レジスタ値の取得。 -// Read*() と Peek8() から呼ばれる。 +// ReadPort() と PeekPort() から呼ばれる。 // scsibus 接続前にもモニタの更新などで呼ばれることに注意。 uint32 SPCDevice::GetPSNS() const { uint32 data = 0; - if (__predict_false(bus == NULL)) { + if (__predict_false(scsi->GetBus() == NULL)) { return 0; } - if (GetREQ()) { + if (scsi->GetREQ()) { data |= SPC::PSNS_REQ; } - if (GetACK()) { + if (scsi->GetACK()) { data |= SPC::PSNS_ACK; } - if (GetATN()) { + if (scsi->GetATN()) { data |= SPC::PSNS_ATN; } - if (GetSEL()) { + if (scsi->GetSEL()) { data |= SPC::PSNS_SEL; } - if (GetBSY()) { + if (scsi->GetBSY()) { data |= SPC::PSNS_BSY; } - data |= (uint32)GetXfer(); + data |= (uint32)scsi->GetXfer(); return data; } // SSTS レジスタ値の取得。 -// Read*() と Peek8() から呼ばれる。 +// ReadPort() と PeekPort() から呼ばれる。 // scsibus 接続前にもモニタの更新などで呼ばれることに注意。 uint32 SPCDevice::GetSSTS() const @@ -947,7 +961,7 @@ SPCDevice::GetSSTS() const uint32 data; data = spc.ssts & 0xf0; - if (__predict_true(bus != NULL) && GetRST()) { // RST 信号 + if (__predict_true(scsi->GetBus() != NULL) && scsi->GetRST()) { // RST 信号 data |= SPC::SSTS_RST_IN; } if (spc.tc == 0) { // TC がゼロか @@ -1027,7 +1041,7 @@ SPCDevice::ReadDREG() bool q_was_full = spc.dreg.IsFull(); data = spc.dreg.Dequeue(); if (loglevel >= 4) { - putlogn("DREG -> $%02x (q=%d TC=0x%x)", + putlogn("DREG -> $%02x (q=%u TC=0x%x)", data.Data(), spc.dreg.Length(), spc.tc); } else { putlog(3, "DREG -> $%02x", data.Data()); @@ -1037,7 +1051,7 @@ SPCDevice::ReadDREG() TransferComplete(); } // この読み出しによってバッファに空きが出来たら転送再開。 - if (transfer_command_running && GetREQ() && q_was_full) { + if (transfer_command_running && scsi->GetREQ() && q_was_full) { // XXX 時間は適当 CallAfter(HardwareTransfer, "SPC ReadDREG to Transfer", t_CLF); } @@ -1098,7 +1112,7 @@ SPCDevice::WriteDREG(uint32 data) spc.dreg.EnqueueForce(data); } // ハードウェア転送中で REQ が来てれば送信。 - if (transfer_command_running && GetREQ()) { + if (transfer_command_running && scsi->GetREQ()) { // XXX 時間は適当 CallAfter(HardwareTransfer, "SPC WriteDREG to Transfer", t_CLF); } @@ -1113,7 +1127,7 @@ SPCDevice::BusFree() // ここは BDID 書き込み前 (まだ scsibus に自身をつなげる前) にも呼ばれる。 // その場合は何もしない。バスフリーのはずでもあるし。 - if (bus == NULL) { + if (scsi->GetBus() == NULL) { return; } @@ -1128,15 +1142,15 @@ SPCDevice::BusRelease() // BSY、SEL だけを落とす。それ以外の信号線は正しくはないけどとりあえず // 一括して SCSIHostDevice::BusFreeCB() が落とすことにしてある。 - switch (GetPhase()) { + switch (scsi->GetPhase()) { case SCSI::Phase::BusFree: case SCSI::Phase::Transfer: break; case SCSI::Phase::Arbitration: case SCSI::Phase::Selection: case SCSI::Phase::Reselection: - NegateSEL(); - NegateBSY(); + scsi->NegateSEL(); + scsi->NegateBSY(); break; } } @@ -1164,10 +1178,10 @@ SPCDevice::SelectCommand() if (data == 0) { // ターゲット ID がない - idstr = string_format("#%d", myid); + idstr = string_format("#%u", myid); } else { - tgtid = DecodeID(data); - idstr = string_format("#%d -> #%d", myid, tgtid); + tgtid = SCSIBus::DecodeID(data); + idstr = string_format("#%u -> #%u", myid, tgtid); } } putlog(2, "Select command (%s)", idstr.c_str()); @@ -1226,9 +1240,9 @@ SPCDevice::SelectCommand() // N が 0 なら無限大なのでタイマーは動かさない。 uint32 N = (spc.tc >> 8) << 8; if (N != 0) { - event_tc.func = ToEventCallback(&SPCDevice::SelectionTimeout); - event_tc.time = (N + 15) * t_CLF * 2; - scheduler->RestartEvent(event_tc); + timer_event.func = ToEventCallback(&SPCDevice::SelectionTimeout); + timer_event.time = (N + 15) * t_CLF * 2; + scheduler->RestartEvent(timer_event); } if ((spc.sctl & SPC::SCTL_ARBIT_EN)) { @@ -1265,13 +1279,13 @@ SPCDevice::Arbitration1(Event& ev) // 区別するため PCTL に設定された IO をバスに出す(p.27)。 // MSG と CD は記述がないけどたぶん一緒に出すだろう。 // タイミングもどこでか分からんのでとりあえずここで。 - SetData(spc.temp_out); - SetXfer(spc.pctl_out); + scsi->SetData(spc.temp_out); + scsi->SetXfer(spc.pctl_out); if (set_atn_in_selection) { - AssertATN(); + scsi->AssertATN(); set_atn_in_selection = false; } - AssertSEL(); + scsi->AssertSEL(); uint64 t_SIDA = 11 * t_CLF - 30; uint64 t_IDBH = 2 * t_CLF - 80; @@ -1285,7 +1299,7 @@ SPCDevice::Arbitration2(Event& ev) { putlog(3, "Arbitration2"); - NegateBSY(); + scsi->NegateBSY(); } // アービトレーションフェーズを使わない場合のセレクションフェーズで、 @@ -1301,9 +1315,9 @@ SPCDevice::Selection1(Event& ev) // ここでは SEL がトリガーになるので SEL のアサートは最後に行う。 // アービトレーションなしなら PCTL に関係なくセレクションフェーズ (IO=0) // を開始する(p.27)。MSG と CD は記述がないけどたぶん一緒に出すだろう。 - SetData(spc.temp_out); - SetXfer(0); - AssertSEL(); + scsi->SetData(spc.temp_out); + scsi->SetXfer(0); + scsi->AssertSEL(); } // セレクションコマンドがタイムアウトした時に呼ばれるコールバック。 @@ -1312,13 +1326,13 @@ SPCDevice::SelectionTimeout(Event& ev) { if (loglevel >= 3) { int tgtid; - uint8 data = GetData(); + uint8 data = scsi->GetData(); data &= ~(1 << myid); - tgtid = DecodeID(data); + tgtid = SCSIBus::DecodeID(data); if (tgtid == -1) { putlogn("Selection timeout"); } else { - putlogn("Selection timeout (#%d)", tgtid); + putlogn("Selection timeout (#%u)", tgtid); } } @@ -1338,7 +1352,7 @@ SPCDevice::SetATNCommand() // Select コマンド前なら、セレクションフェーズで ATN を立てる指示。 // 接続中(転送フェーズ中?) なら即座に ATN を立てる。 - switch (GetPhase()) { + switch (scsi->GetPhase()) { case SCSI::Phase::BusFree: set_atn_in_selection = true; break; @@ -1347,7 +1361,7 @@ SPCDevice::SetATNCommand() case SCSI::Phase::Reselection: // Select Command 実行中なので実質ここで呼ばれることはないはずだが case SCSI::Phase::Transfer: - AssertATN(); + scsi->AssertATN(); break; } } @@ -1370,7 +1384,7 @@ SPCDevice::TransferCommand() // PCTL レジスタに設定してあるフェーズをバスに出す。 // XXX どこでかよく分からんけど、とりあえずこの辺で。 - SetXfer(spc.pctl_out); + scsi->SetXfer(spc.pctl_out); // 転送開始前に DREG キューを初期化する。 // MPU 側が DREG を全部読まずにフェーズを中断とかはたぶん出来るので。 @@ -1379,21 +1393,20 @@ SPCDevice::TransferCommand() // Transfer コマンド発行時に REQ がアサートされていなければ // アサートされるまで待つ。この場合、ここでは何もすることはなく、 - // TransferReqCB() が呼ばれてそこから転送フェーズが動き始める。 - if (GetREQ()) { + // OnTransferReq() が呼ばれてそこから転送フェーズが動き始める。 + if (scsi->GetREQ()) { // コマンドから転送開始までちょっとだけかかる CallAfter(HardwareTransfer, "SPC SCMD to Start", 4 * t_CLF); } } -// バスフリーコールバック (SCSIBus から呼ばれる) +// バスフリーコールバック (SCSIHostDevice から呼ばれる)。 // id はこのバスフリーを行ったデバイス (0..7) void -SPCDevice::BusFreeCB(int id) +SPCDevice::BusFreeCallback(uint id) { putlog(4, "BusFree Callback"); - inherited::BusFreeCB(id); last_busfree_time = scheduler->GetVirtTime(); transfer_command_running = false; @@ -1410,19 +1423,15 @@ SPCDevice::BusFreeCB(int id) } } -// ターゲットがセレクションに応答したコールバック (SCSIBus から呼ばれる) +// ターゲットがセレクションに応答したコールバック +// (SCSIHostDevice から呼ばれる) void -SPCDevice::SelectionAckCB() +SPCDevice::SelectionAckCallback() { - // ターゲットが BSY を立てて応答したので、 - // こちらはデータバスをクリアして SEL を下げる。 - // これによりセレクションフェーズは成功で完了する。 - putlog(4, "SelectionAck Calback"); - SetData(0); - NegateSEL(); + putlog(4, "SelectionAck Callback"); // タイムアウトイベントを停止 - scheduler->StopEvent(event_tc); + scheduler->StopEvent(timer_event); spc.ssts &= ~SPC::SSTS_SPC_BUSY; spc.ssts |= SPC::SSTS_CONN_INIT; @@ -1431,9 +1440,10 @@ SPCDevice::SelectionAckCB() SetINTS(SPC::INTS_COMPLETE); } -// 転送フェーズでターゲットが REQ を立てたコールバック (SCSIBus から呼ばれる) +// 転送フェーズでターゲットが REQ を立てたコールバック +// (SCSIHostDevice から呼ばれる) void -SPCDevice::TransferReqCB() +SPCDevice::TransferReqCallback() { // REQ が立てば Transfer Command 発行前でも Xfer In Progress は立てる spc.ssts |= SPC::SSTS_IN_PROGRESS; @@ -1441,7 +1451,7 @@ SPCDevice::TransferReqCB() if (transfer_command_running) { // Transfer Command 実行中なら REQ 待ちだったので // ここでハードウェア転送を開始。 - HardwareTransfer(event); + HardwareTransfer(phase_event); } // データシートでは、マニュアル転送で IN 方向の時データバスの内容を @@ -1449,8 +1459,8 @@ SPCDevice::TransferReqCB() // HardwareTransfer によって状態が変更される可能性があるため // もう一回チェック。 if (transfer_command_running == false) { - if (GetIO()) { - spc.temp_in = GetData(); + if (scsi->GetIO()) { + spc.temp_in = scsi->GetData(); } } } @@ -1459,7 +1469,7 @@ SPCDevice::TransferReqCB() // イベントコールバックとして呼び出すこと。 // REQ がアサートされているのを確認してから呼び出すこと。 // (1バイト送信するとターゲットが再び REQ を上げるなどしてくるので、 -// TransferReqCB() か DREG 書き込みから再びここが呼ばれる) +// OnTransferReq() か DREG 書き込みから再びここが呼ばれる) void SPCDevice::HardwareTransfer(Event& ev) { @@ -1469,7 +1479,7 @@ SPCDevice::HardwareTransfer(Event& ev) // PCTL のフェーズと SCSI バスのフェーズが一致していれば転送(開始|継続)。 // そうでなければ Service Required 割り込み。 - if (spc.pctl_out != GetXfer()) { + if (spc.pctl_out != scsi->GetXfer()) { transfer_command_running = false; putlog(3, "Hardware Transfer terminated (phase mismatch)"); SetINTS(SPC::INTS_SERV_REQ); @@ -1478,9 +1488,9 @@ SPCDevice::HardwareTransfer(Event& ev) // DREG が送受信不可能ならここでは何もせずに帰るだけでよい。 // その後 DREG アクセスによって送受信可能になればまたこれが呼ばれる。 - if (GetIO()) { + if (scsi->GetIO()) { // Input (ターゲット → イニシエータ) - bool queued = spc.dreg.Enqueue(GetData()); + bool queued = spc.dreg.Enqueue(scsi->GetData()); // SDGC_XFER_ENABLE なら読み込めるデータがある場合に割り込み。 if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) { putlog(3, "SDGC XFER_OUT Interrupt"); @@ -1493,7 +1503,7 @@ SPCDevice::HardwareTransfer(Event& ev) } else { // Output (イニシエータ → ターゲット) if (spc.dreg.Length() != 0) { - SetData(spc.dreg.Dequeue()); + scsi->SetData(spc.dreg.Dequeue()); } else { putlog(4, "Hardware Transfer (DREG empty)"); // SDGC_XFER_ENABLE ならキューに書き込む必要がある場合に割り込み。 @@ -1505,17 +1515,17 @@ SPCDevice::HardwareTransfer(Event& ev) } } - AssertACK(); + scsi->AssertACK(); // 転送完了 spc.tc--; - putlog(4, "Hardware Transfer: %s q=%d tc=%d", - (GetIO() ? "IN" : "OUT"), spc.dreg.Length(), spc.tc); + putlog(4, "Hardware Transfer: %s q=%u tc=%u", + (scsi->GetIO() ? "IN" : "OUT"), spc.dreg.Length(), spc.tc); if (spc.tc == 0) { // どちらでも転送自体はここで完了(?) transfer_command_running = false; - if (GetIO()) { + if (scsi->GetIO()) { // Input 時は TC がゼロになって DREG キューを全部読み出した // ところで完了なので、ここでは何もしない。 } else { @@ -1525,14 +1535,14 @@ SPCDevice::HardwareTransfer(Event& ev) } // メッセージアウトフェーズの最終バイト送信中(?)に ATN を下げる - if (GetXfer() == SCSI::XferPhase::MsgOut && spc.tc == 0) { - NegateATN(); + if (scsi->GetXfer() == SCSI::XferPhase::MsgOut && spc.tc == 0) { + scsi->NegateATN(); } - if (GetXfer() == SCSI::XferPhase::MsgIn && spc.tc == 0) { + if (scsi->GetXfer() == SCSI::XferPhase::MsgIn && spc.tc == 0) { // ただしメッセージインフェーズの最終バイトなら ACK を下げない。 } else { - NegateACK(); + scsi->NegateACK(); } }