--- nono/vm/spc.cpp 2026/04/29 17:05:32 1.1.1.17 +++ nono/vm/spc.cpp 2026/04/29 17:05:59 1.1.1.21 @@ -10,30 +10,32 @@ #include "spc.h" #include "config.h" +#include "event.h" #include "mainapp.h" #include "monitor.h" #include "pedec.h" #include "scheduler.h" #include "scsidev.h" +#include "scsidomain.h" // time 時間後に呼び出すコールバックをセットする。 // func の書式が面倒なのを省略して書きたいため。 #define CallAfter(func_, name_, time_) do { \ - phase_event.func = ToEventCallback(&SPCDevice::func_); \ - phase_event.time = time_; \ - phase_event.SetName(name_); \ + phase_event->SetCallback(&SPCDevice::func_); \ + phase_event->time = time_; \ + phase_event->SetName(name_); \ scheduler->RestartEvent(phase_event); \ } while (0) // コンストラクタ SPCDevice::SPCDevice() - : inherited(OBJ_SPC) + : inherited(OBJ_SPC0) { // 初期値は不定だが表示上困るのでとりあえず。 myid = 7; monitor = gMonitorManager->Regist(ID_MONITOR_SPC, this); - monitor->func = ToMonitorCallback(&SPCDevice::MonitorUpdate); + monitor->SetCallback(&SPCDevice::MonitorScreen); // サイズは Init で決まる } @@ -42,21 +44,37 @@ SPCDevice::~SPCDevice() { } +// ログレベル設定 +void +SPCDevice::SetLogLevel(int loglevel_) +{ + inherited::SetLogLevel(loglevel_); + + if (host) { + host->SetLogLevel(loglevel_); + } +} + bool SPCDevice::Create() { - if (inherited::Create() == false) { + // ドメイン。 + try { + domain.reset(new SCSIDomain(this, "SPC0", "spc0")); + } catch (...) { } + if ((bool)domain == false) { + warnx("Failed to initialize SCSIDomain at %s", __method__); return false; } - scsi.reset(new SCSIHostDevice(this, "spc0")); - if ((bool)scsi == false) { + // バス + try { + bus.reset(new SCSIBus(domain.get())); + } catch (...) { } + if ((bool)bus == false) { + warnx("Failed to initialize SCSIBus at %s", __method__); return false; } - scsi->SetCallback( - ToBusFreeCallback(&SPCDevice::BusFreeCallback), - ToSCSIHostCallback(&SPCDevice::SelectionAckCallback), - ToSCSIHostCallback(&SPCDevice::TransferReqCallback)); return true; } @@ -65,18 +83,27 @@ SPCDevice::Create() bool SPCDevice::Init() { - if (inherited::Init() == false) { - return false; - } - // 実行中に別インスタンスを頻繁に参照しないようにここで vmtype を覚えておく vmtype = gMainApp.GetVMType(); + host = domain->GetInitiator(); + host->SetBusCallback( + ToBusFreeCallback(&SPCDevice::BusFreeCallback), + ToSCSIHostCallback(&SPCDevice::SelectionAckCallback), + ToSCSIHostCallback(&SPCDevice::TransferReqCallback)); + + // ターゲットデバイスをバスに接続。 + domain->AttachTargets(bus.get()); + bus->Refresh(); + // 入力クロックは機種によって異なる。 // X68k は 5MHz が入っている。 // LUNA-I はおそらく 6.144MHz だと思われる。LUNA-88K は未確認。 - const ConfigItem& item = gConfig->Find("!spc-tCLF"); - t_CLF = item.AsInt(); + if (vmtype == VMType::X68030) { + t_CLF = 200_nsec; + } else { + t_CLF = 163_nsec; + } // アクセスウェイト if (vmtype == VMType::X68030) { @@ -98,15 +125,9 @@ SPCDevice::Init() interrupt = GetInterruptDevice(); } - // 汎用イベント - // 名前とコールバック関数は都度セットする。 - phase_event.SetName("SPC Phase"); - scheduler->RegistEvent(phase_event); - - // TC カウンタイベント - timer_event.code = 0; - timer_event.SetName("SPC Timer"); - scheduler->RegistEvent(timer_event); + auto evman = GetEventManager(); + phase_event = evman->Regist(this, NULL, "SPC Phase"); + timer_event = evman->Regist(this, NULL, "SPC Timer"); // モニタに表示するアドレス if (vmtype == VMType::X68030) { @@ -117,7 +138,7 @@ SPCDevice::Init() regoffset = 4; } // 「イニシエータ以外のターゲット数」はここでは確定している。 - monitor->SetSize(80, 15 + scsi->GetOwnedBus()->GetTargetCount() + 1); + monitor->SetSize(80, 15 + domain->GetTargetCount() + 1); return true; } @@ -166,7 +187,7 @@ SPCDevice::ReadPort(uint32 offset) switch (offset) { case SPC::BDID: - data = 1 << myid; + data = 1U << myid; putlog(3, "BDID -> $%02x", data.Data()); break; @@ -190,7 +211,7 @@ SPCDevice::ReadPort(uint32 offset) data = GetINTS(); if (loglevel >= 3) { uint32 new_ints = data.Data(); - if (timer_event.IsRunning()) { + if (timer_event->IsRunning()) { // イベント中は値が変わった時だけ表示 if (prev_ints != new_ints) { putlogn("INTS -> $%02x", new_ints); @@ -212,7 +233,7 @@ SPCDevice::ReadPort(uint32 offset) data = GetSSTS(); if (loglevel >= 3) { uint32 new_ssts = data.Data(); - if (timer_event.IsRunning()) { + if (timer_event->IsRunning()) { // イベント中は値が変わった時だけ表示 if (prev_ssts != new_ssts) { putlogn("SSTS -> $%02x", new_ssts); @@ -286,7 +307,7 @@ SPCDevice::WritePort(uint32 offset, uint switch (offset) { case SPC::BDID: { - uint cur_myid = scsi->GetMyID(); + uint cur_myid = host->GetMyID(); putlog(3, "BDID <- $%02x", data); if (data == cur_myid) { // 同じなら何もしない @@ -295,9 +316,10 @@ SPCDevice::WritePort(uint32 offset, uint if ((int)cur_myid < 0) { // 初回なのでバスに参加。myid はこちらでもコピーを覚えておく。 myid = data; - if (scsi->GetOwnedBus()->AttachDevice(scsi.get(), myid) == false) { + if (domain->AttachInitiator(bus.get(), myid) == false) { putlog(0, "BDID %u conflicts with existing devices!", myid); } + bus->Refresh(); } else { // そうでなければ移動? putlog(0, "BDID change %u to %u (NOT IMPLEMENTED)", cur_myid, data); @@ -359,12 +381,12 @@ SPCDevice::WritePort(uint32 offset, uint // SPC はセレクションタイムアウトしたからと言って、データバスに出した // ID を自動的に取り下げたりはしないので、TEMP に $00 を書き込むことで // データバスをクリアする、だったんでは。 - if (0 && scsi->GetPhase() == SCSI::Phase::Selection) { - scsi->NegateSEL(); + if (0 && GetPhase() == SCSI::Phase::Selection) { + NegateSEL(); } // Out 方向ならバスに出す - if (scsi->GetIO() == false) { - scsi->SetData(spc.temp_out); + if (GetIO() == false) { + SetData(spc.temp_out); } break; @@ -399,7 +421,7 @@ SPCDevice::PeekPort(uint32 offset) { switch (offset) { case SPC::BDID: - return 1 << myid; + return 1U << myid; case SPC::SCTL: return spc.sctl; @@ -469,11 +491,10 @@ SPCDevice::PokePort(uint32 offset, uint3 } void -SPCDevice::MonitorUpdate(Monitor *, TextScreen& screen) +SPCDevice::MonitorScreen(Monitor *, TextScreen& screen) { int x; int y; - SCSIBus *bus; uint32 sctl; uint32 scmd; uint32 ints; @@ -486,7 +507,6 @@ SPCDevice::MonitorUpdate(Monitor *, Text screen.Clear(); - bus = scsi->GetOwnedBus(); sctl = spc.sctl; scmd = spc.scmd; ints = GetINTS(); @@ -500,7 +520,7 @@ SPCDevice::MonitorUpdate(Monitor *, Text #define A(n) (baseaddr + (n) * regoffset) y = 0; - screen.Print(0, y++, "$%08x BDID: $%02x (ID=%u)", A(0), (1 << myid), myid); + screen.Print(0, y++, "$%08x BDID: $%02x (ID=%u)", A(0), (1U << 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); @@ -607,17 +627,17 @@ SPCDevice::MonitorUpdate(Monitor *, Text // バス状態 x = 62; y = 0; - 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(); + SCSI::Phase ph = GetPhase(); + uint8 rst = GetRST(); + uint8 bsy = GetBSY(); + uint8 sel = GetSEL(); + uint8 msg = GetMSG(); + uint8 cd = GetCD(); + uint8 io = GetIO(); + uint8 atn = GetATN(); + uint8 req = GetREQ(); + uint8 ack = GetACK(); + uint8 data = GetData(); screen.Print(x, y++, "Phase: %s", SCSI::GetPhaseName(ph)); screen.Puts(x, y++, TA::OnOff(rst), "RST"); @@ -634,14 +654,15 @@ SPCDevice::MonitorUpdate(Monitor *, Text y = 2; screen.Puts(x + 4, y++, "(________)"); screen.Puts(x + 4, y++, "(________)"); - screen.Puts(x + 4, y++, "+"); - screen.Puts(x + 4, y++, "|"); - screen.Puts(x + 4, y++, "+"); + // Shift-JIS は手動で置く必要がある。 + screen.Puts(x + 4, y++, "\x84\xa2"); // "┐" + screen.Puts(x + 4, y++, "\x84\xa0"); // "│" + screen.Puts(x + 4, y++, "\x84\xa3"); // "┘" // BSY, SEL の内訳。"(76543210)" の順。 y = 2; for (int i = 0; i < 8; i++) { - if ((bsy & (1 << i))) { + if ((bsy & (1U << i))) { screen.Print(x + 4 + 8 - i, y, "%u", i); } else { screen.Print(x + 4 + 8 - i, y, "_"); @@ -649,7 +670,7 @@ SPCDevice::MonitorUpdate(Monitor *, Text } y++; for (int i = 0; i < 8; i++) { - if ((sel & (1 << i))) { + if ((sel & (1U << i))) { screen.Print(x + 4 + 8 - i, y, "%u", i); } else { screen.Print(x + 4 + 8 - i, y, "_"); @@ -658,9 +679,9 @@ SPCDevice::MonitorUpdate(Monitor *, Text // 転送フェーズ中なら現在の XferPhase y += 2; - if (ph == SCSI::Phase::Transfer) { - screen.Puts(x + 5, y, SCSI::GetXferPhaseName(scsi->GetXfer())); - } + TA attrxfer = (ph == SCSI::Phase::Transfer) ? TA::Normal : TA::Disable; + screen.Puts(x + 6, y++, attrxfer, "XferPhase:"); + screen.Puts(x + 6, y, attrxfer, SCSI::GetXferPhaseName(GetXfer())); // デバイス一覧 // 0 1 @@ -671,7 +692,7 @@ SPCDevice::MonitorUpdate(Monitor *, Text x = 0; y = 14; screen.Print(x, y++, "ID Device Command"); - for (const auto d : bus->GetConnectedDevices()) { + for (const auto d : domain->GetConnectedDevices()) { std::string desc = SCSI::GetDevTypeName(d->GetDevType()); const SCSIDisk *disk = dynamic_cast(d); @@ -696,13 +717,14 @@ SPCDevice::MonitorUpdate(Monitor *, Text // 実行中のコマンド x = 14; y = 15; - SCSITarget *t = bus->GetSelectedTarget(); + const SCSIDevice *d = bus->GetSelectedTarget(); + const SCSITarget *t = dynamic_cast(d); if (t) { std::vector cmds = t->GetCmdSeq(); if (cmds.size() > 0) { // この ID のデバイス行に表示したい // XXX もうちょっとマシにしたい - const auto connected_devices = bus->GetConnectedDevices(); + const auto connected_devices = domain->GetConnectedDevices(); int i = 0; for (; i < connected_devices.size(); i++) { if (t == connected_devices[i]) { @@ -715,7 +737,7 @@ SPCDevice::MonitorUpdate(Monitor *, Text } } -// レジスタをビットごとに表示する。MonitorUpdate の下請け。 +// レジスタをビットごとに表示する。MonitorScreen の下請け。 void SPCDevice::MonitorReg(TextScreen& screen, int x, int y, uint32 reg, const char * const *names) @@ -724,7 +746,7 @@ SPCDevice::MonitorReg(TextScreen& screen if (names[i][0] == '-') { screen.Puts(x + i * 5, y, TA::Disable, "----"); } else { - bool b = reg & (1 << (7 - i)); + bool b = reg & (1U << (7 - i)); screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]); } } @@ -784,16 +806,16 @@ SPCDevice::WriteSCMD(uint32 data) if ((spc.scmd & SPC::SCMD_RST_OUT)) { // 0 -> 1 // すでに RST が立ってたらどうするか - if (scsi->GetRST() == false) { + if (GetRST() == false) { putlog(1, "SCMD RST Out"); BusRelease(); - scsi->AssertRST(); + AssertRST(); SetINTS(SPC::INTS_RESET_COND); } } else { // 1 -> 0 // 特にすることはない? - scsi->NegateRST(); + NegateRST(); } } @@ -826,7 +848,7 @@ SPCDevice::WriteSCMD(uint32 data) putlog(2, "SCMD Reset ACK/REQ command"); // イニシエータなら ACK を、ターゲットなら REQ をリセットする。 // ターゲットモードはサポートしないので、操作するのは ACK のみ。 - scsi->NegateACK(); + NegateACK(); break; case 7: putlog(2, "SCMD Set ACK/REQ command"); @@ -841,7 +863,7 @@ SPCDevice::WriteSCMD(uint32 data) putlog(0, "SCMD Intercept with SetACK (NOT IMPLEMENTED)"); } } - scsi->AssertACK(); + AssertACK(); break; } } @@ -866,15 +888,14 @@ SPCDevice::WriteINTS(uint32 data) // TIMEOUT ビットをクリアした時が if ((ack & SPC::INTS_TIMEOUT)) { // セレクションフェーズなら - if (scsi->GetPhase() == SCSI::Phase::Selection) { + if (GetPhase() == SCSI::Phase::Selection) { // TC がゼロならバスフリー // そうでなければリスタート if (spc.tc == 0) { BusFree(); } else { - timer_event.func = - ToEventCallback(&SPCDevice::SelectionTimeout); - timer_event.time = spc.tc * t_CLF * 2; + timer_event->SetCallback(&SPCDevice::SelectionTimeout); + timer_event->time = spc.tc * t_CLF * 2; scheduler->RestartEvent(timer_event); } } @@ -894,8 +915,8 @@ SPCDevice::SetINTS(uint32 data) spc.ints |= data; // Disconnected 割り込みが起きたら ATN を下げる - if ((spc.ints & SPC::INTS_DISCONNECTED) && scsi->GetATN()) { - scsi->NegateATN(); + if ((spc.ints & SPC::INTS_DISCONNECTED) && GetATN()) { + NegateATN(); } ChangeInterrupt(); @@ -928,46 +949,46 @@ SPCDevice::ChangeInterrupt() // PSNS レジスタ値の取得。 // ReadPort() と PeekPort() から呼ばれる。 -// scsibus 接続前にもモニタの更新などで呼ばれることに注意。 +// SCSIBus 接続前にもモニタの更新などで呼ばれることに注意。 uint32 SPCDevice::GetPSNS() const { uint32 data = 0; - if (__predict_false(scsi->GetBus() == NULL)) { + if (__predict_false(bus == NULL)) { return 0; } - if (scsi->GetREQ()) { + if (GetREQ()) { data |= SPC::PSNS_REQ; } - if (scsi->GetACK()) { + if (GetACK()) { data |= SPC::PSNS_ACK; } - if (scsi->GetATN()) { + if (GetATN()) { data |= SPC::PSNS_ATN; } - if (scsi->GetSEL()) { + if (GetSEL()) { data |= SPC::PSNS_SEL; } - if (scsi->GetBSY()) { + if (GetBSY()) { data |= SPC::PSNS_BSY; } - data |= (uint32)scsi->GetXfer(); + data |= (uint32)GetXfer(); return data; } // SSTS レジスタ値の取得。 // ReadPort() と PeekPort() から呼ばれる。 -// scsibus 接続前にもモニタの更新などで呼ばれることに注意。 +// SCSIBus 接続前にもモニタの更新などで呼ばれることに注意。 uint32 SPCDevice::GetSSTS() const { uint32 data; data = spc.ssts & 0xf0; - if (__predict_true(scsi->GetBus() != NULL) && scsi->GetRST()) { // RST 信号 + if (__predict_true(bus != NULL) && GetRST()) { // RST 信号 data |= SPC::SSTS_RST_IN; } if (spc.tc == 0) { // TC がゼロか @@ -1057,7 +1078,7 @@ SPCDevice::ReadDREG() TransferComplete(); } // この読み出しによってバッファに空きが出来たら転送再開。 - if (transfer_command_running && scsi->GetREQ() && q_was_full) { + if (transfer_command_running && GetREQ() && q_was_full) { // XXX 時間は適当 CallAfter(HardwareTransfer, "SPC ReadDREG to Transfer", t_CLF); } @@ -1118,7 +1139,7 @@ SPCDevice::WriteDREG(uint32 data) spc.dreg.EnqueueForce(data); } // ハードウェア転送中で REQ が来てれば送信。 - if (transfer_command_running && scsi->GetREQ()) { + if (transfer_command_running && GetREQ()) { // XXX 時間は適当 CallAfter(HardwareTransfer, "SPC WriteDREG to Transfer", t_CLF); } @@ -1131,9 +1152,9 @@ SPCDevice::BusFree() { putlog(3, "BusFree"); - // ここは BDID 書き込み前 (まだ scsibus に自身をつなげる前) にも呼ばれる。 + // ここは BDID 書き込み前 (まだ SCSIBus に自身をつなげる前) にも呼ばれる。 // その場合は何もしない。バスフリーのはずでもあるし。 - if (scsi->GetBus() == NULL) { + if (bus == NULL) { return; } @@ -1148,15 +1169,15 @@ SPCDevice::BusRelease() // BSY、SEL だけを落とす。それ以外の信号線は正しくはないけどとりあえず // 一括して SCSIHostDevice::BusFreeCB() が落とすことにしてある。 - switch (scsi->GetPhase()) { + switch (GetPhase()) { case SCSI::Phase::BusFree: case SCSI::Phase::Transfer: break; case SCSI::Phase::Arbitration: case SCSI::Phase::Selection: case SCSI::Phase::Reselection: - scsi->NegateSEL(); - scsi->NegateBSY(); + NegateSEL(); + NegateBSY(); break; } } @@ -1175,12 +1196,12 @@ SPCDevice::SelectCommand() if (data == 0) { // ID が一切立ってない? idstr = "TEMP==0; No IDs?"; - } else if ((data & (1 << myid)) == 0) { + } else if ((data & (1U << myid)) == 0) { // 自 ID が立ってない? idstr = string_format("TEMP=$%02x; No BDID?", data); } else { // ここで少なくとも自 ID は立っている - data &= ~(1 << myid); + data &= ~(1U << myid); if (data == 0) { // ターゲット ID がない @@ -1246,8 +1267,8 @@ SPCDevice::SelectCommand() // N が 0 なら無限大なのでタイマーは動かさない。 uint32 N = (spc.tc >> 8) << 8; if (N != 0) { - timer_event.func = ToEventCallback(&SPCDevice::SelectionTimeout); - timer_event.time = (N + 15) * t_CLF * 2; + timer_event->SetCallback(&SPCDevice::SelectionTimeout); + timer_event->time = (N + 15) * t_CLF * 2; scheduler->RestartEvent(timer_event); } @@ -1259,21 +1280,21 @@ SPCDevice::SelectCommand() // 必ずバスの使用権を得る。 // バスフリーから規定時間後に SEL (と DB* と ATN) を上げる。 - uint64 t_ARB = 32 * t_CLF - 60; - uint64 t_AWSL = 40; // 適当に中間 + uint64 t_ARB = 32 * t_CLF - 60_nsec; + uint64 t_AWSL = 40_nsec; // 適当に中間 CallAfter(Arbitration1, "SPC Arbitration", wait + t_ARB + t_AWSL); } else { // アービトレーションなしの場合 // バスフリーから規定時間後に SEL (と DB*) を上げる。 - uint64 t_IDSL = 11 * t_CLF - 80; + uint64 t_IDSL = 11 * t_CLF - 80_nsec; CallAfter(Selection1, "SPC Selection", wait + t_IDSL); } } // アービトレーションフェーズ開始後、規定時間経過したので SEL を立てるところ。 void -SPCDevice::Arbitration1(Event& ev) +SPCDevice::Arbitration1(Event *ev) { putlog(3, "Arbitration1"); @@ -1285,33 +1306,33 @@ SPCDevice::Arbitration1(Event& ev) // 区別するため PCTL に設定された IO をバスに出す(p.27)。 // MSG と CD は記述がないけどたぶん一緒に出すだろう。 // タイミングもどこでか分からんのでとりあえずここで。 - scsi->SetData(spc.temp_out); - scsi->SetXfer(spc.pctl_out); + SetData(spc.temp_out); + SetXfer(spc.pctl_out); if (set_atn_in_selection) { - scsi->AssertATN(); + AssertATN(); set_atn_in_selection = false; } - scsi->AssertSEL(); + AssertSEL(); - uint64 t_SIDA = 11 * t_CLF - 30; - uint64 t_IDBH = 2 * t_CLF - 80; + uint64 t_SIDA = 11 * t_CLF - 30_nsec; + uint64 t_IDBH = 2 * t_CLF - 80_nsec; CallAfter(Arbitration2, "SPC Arbitration2", t_SIDA + t_IDBH); } // アービトレーションフェーズ中、SEL を立ててから規定時間経過したので BSY を // 下げるところ。 void -SPCDevice::Arbitration2(Event& ev) +SPCDevice::Arbitration2(Event *ev) { putlog(3, "Arbitration2"); - scsi->NegateBSY(); + NegateBSY(); } // アービトレーションフェーズを使わない場合のセレクションフェーズで、 // 規定時間経過したので SEL を立てるところ。 void -SPCDevice::Selection1(Event& ev) +SPCDevice::Selection1(Event *ev) { putlog(3, "Selection1"); @@ -1321,19 +1342,19 @@ SPCDevice::Selection1(Event& ev) // ここでは SEL がトリガーになるので SEL のアサートは最後に行う。 // アービトレーションなしなら PCTL に関係なくセレクションフェーズ (IO=0) // を開始する(p.27)。MSG と CD は記述がないけどたぶん一緒に出すだろう。 - scsi->SetData(spc.temp_out); - scsi->SetXfer(0); - scsi->AssertSEL(); + SetData(spc.temp_out); + SetXfer(0); + AssertSEL(); } // セレクションコマンドがタイムアウトした時に呼ばれるコールバック。 void -SPCDevice::SelectionTimeout(Event& ev) +SPCDevice::SelectionTimeout(Event *ev) { if (loglevel >= 3) { int tgtid; - uint8 data = scsi->GetData(); - data &= ~(1 << myid); + uint8 data = GetData(); + data &= ~(1U << myid); tgtid = SCSIBus::DecodeID(data); if (tgtid == -1) { putlogn("Selection timeout"); @@ -1358,7 +1379,7 @@ SPCDevice::SetATNCommand() // Select コマンド前なら、セレクションフェーズで ATN を立てる指示。 // 接続中(転送フェーズ中?) なら即座に ATN を立てる。 - switch (scsi->GetPhase()) { + switch (GetPhase()) { case SCSI::Phase::BusFree: set_atn_in_selection = true; break; @@ -1367,7 +1388,7 @@ SPCDevice::SetATNCommand() case SCSI::Phase::Reselection: // Select Command 実行中なので実質ここで呼ばれることはないはずだが case SCSI::Phase::Transfer: - scsi->AssertATN(); + AssertATN(); break; } } @@ -1390,7 +1411,7 @@ SPCDevice::TransferCommand() // PCTL レジスタに設定してあるフェーズをバスに出す。 // XXX どこでかよく分からんけど、とりあえずこの辺で。 - scsi->SetXfer(spc.pctl_out); + SetXfer(spc.pctl_out); // 転送開始前に DREG キューを初期化する。 // MPU 側が DREG を全部読まずにフェーズを中断とかはたぶん出来るので。 @@ -1400,7 +1421,7 @@ SPCDevice::TransferCommand() // Transfer コマンド発行時に REQ がアサートされていなければ // アサートされるまで待つ。この場合、ここでは何もすることはなく、 // OnTransferReq() が呼ばれてそこから転送フェーズが動き始める。 - if (scsi->GetREQ()) { + if (GetREQ()) { // コマンドから転送開始までちょっとだけかかる CallAfter(HardwareTransfer, "SPC SCMD to Start", 4 * t_CLF); } @@ -1465,8 +1486,8 @@ SPCDevice::TransferReqCallback() // HardwareTransfer によって状態が変更される可能性があるため // もう一回チェック。 if (transfer_command_running == false) { - if (scsi->GetIO()) { - spc.temp_in = scsi->GetData(); + if (GetIO()) { + spc.temp_in = GetData(); } } } @@ -1477,7 +1498,7 @@ SPCDevice::TransferReqCallback() // (1バイト送信するとターゲットが再び REQ を上げるなどしてくるので、 // OnTransferReq() か DREG 書き込みから再びここが呼ばれる) void -SPCDevice::HardwareTransfer(Event& ev) +SPCDevice::HardwareTransfer(Event *ev) { // SDGC_XFER_ENABLE は転送要求ごとに割り込みを上げる。 // (SPC から見て) 入力操作時は読み込めるデータがある場合、出力操作時は @@ -1485,7 +1506,7 @@ SPCDevice::HardwareTransfer(Event& ev) // PCTL のフェーズと SCSI バスのフェーズが一致していれば転送(開始|継続)。 // そうでなければ Service Required 割り込み。 - if (spc.pctl_out != scsi->GetXfer()) { + if (spc.pctl_out != GetXfer()) { transfer_command_running = false; putlog(3, "Hardware Transfer terminated (phase mismatch)"); SetINTS(SPC::INTS_SERV_REQ); @@ -1494,9 +1515,9 @@ SPCDevice::HardwareTransfer(Event& ev) // DREG が送受信不可能ならここでは何もせずに帰るだけでよい。 // その後 DREG アクセスによって送受信可能になればまたこれが呼ばれる。 - if (scsi->GetIO()) { + if (GetIO()) { // Input (ターゲット → イニシエータ) - bool queued = spc.dreg.Enqueue(scsi->GetData()); + bool queued = spc.dreg.Enqueue(GetData()); // SDGC_XFER_ENABLE なら読み込めるデータがある場合に割り込み。 if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) { putlog(3, "SDGC XFER_OUT Interrupt"); @@ -1509,7 +1530,7 @@ SPCDevice::HardwareTransfer(Event& ev) } else { // Output (イニシエータ → ターゲット) if (spc.dreg.Length() != 0) { - scsi->SetData(spc.dreg.Dequeue()); + SetData(spc.dreg.Dequeue()); } else { putlog(4, "Hardware Transfer (DREG empty)"); // SDGC_XFER_ENABLE ならキューに書き込む必要がある場合に割り込み。 @@ -1521,17 +1542,17 @@ SPCDevice::HardwareTransfer(Event& ev) } } - scsi->AssertACK(); + AssertACK(); // 転送完了 spc.tc--; putlog(4, "Hardware Transfer: %s q=%u tc=%u", - (scsi->GetIO() ? "IN" : "OUT"), spc.dreg.Length(), spc.tc); + (GetIO() ? "IN" : "OUT"), spc.dreg.Length(), spc.tc); if (spc.tc == 0) { // どちらでも転送自体はここで完了(?) transfer_command_running = false; - if (scsi->GetIO()) { + if (GetIO()) { // Input 時は TC がゼロになって DREG キューを全部読み出した // ところで完了なので、ここでは何もしない。 } else { @@ -1541,14 +1562,14 @@ SPCDevice::HardwareTransfer(Event& ev) } // メッセージアウトフェーズの最終バイト送信中(?)に ATN を下げる - if (scsi->GetXfer() == SCSI::XferPhase::MsgOut && spc.tc == 0) { - scsi->NegateATN(); + if (GetXfer() == SCSI::XferPhase::MsgOut && spc.tc == 0) { + NegateATN(); } - if (scsi->GetXfer() == SCSI::XferPhase::MsgIn && spc.tc == 0) { + if (GetXfer() == SCSI::XferPhase::MsgIn && spc.tc == 0) { // ただしメッセージインフェーズの最終バイトなら ACK を下げない。 } else { - scsi->NegateACK(); + NegateACK(); } }