--- nono/vm/spc.cpp 2026/04/29 17:05:17 1.1.1.14 +++ nono/vm/spc.cpp 2026/04/29 17:05:59 1.1.1.21 @@ -10,39 +10,33 @@ #include "spc.h" #include "config.h" -#include "interrupt.h" +#include "event.h" #include "mainapp.h" -#include "mpu.h" +#include "monitor.h" #include "pedec.h" #include "scheduler.h" +#include "scsidev.h" +#include "scsidomain.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->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) { - SetConfigName("spc0"); - myid = -1; + // 初期値は不定だが表示上困るのでとりあえず。 + myid = 7; - // 汎用イベント - // 名前とコールバック関数は都度セットする。 - event.Regist("SPC Phase"); - - // TC カウンタイベント - event_tc.code = 0; - event_tc.Regist("SPC Timer"); - - monitor.func = ToMonitorCallback(&SPCDevice::MonitorUpdate); + monitor = gMonitorManager->Regist(ID_MONITOR_SPC, this); + monitor->SetCallback(&SPCDevice::MonitorScreen); // サイズは Init で決まる - monitor.Regist(ID_MONITOR_SPC); } // デストラクタ @@ -50,49 +44,77 @@ SPCDevice::~SPCDevice() { } -// 初期化 -bool -SPCDevice::Init() +// ログレベル設定 +void +SPCDevice::SetLogLevel(int loglevel_) { - // inherited::Init() は少し下で呼んでいる + inherited::SetLogLevel(loglevel_); - // 実行中に別インスタンスを頻繁に参照しないようにここで vmtype を覚えておく - vmtype = gMainApp.GetVMType(); - - // 本体 SCSI ID は LUNA も X68030 も 7 固定。 - uint initiator_id = 7; + if (host) { + host->SetLogLevel(loglevel_); + } +} - // モニタには、接続している 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()); - } +bool +SPCDevice::Create() +{ + // ドメイン。 + try { + domain.reset(new SCSIDomain(this, "SPC0", "spc0")); + } catch (...) { } + if ((bool)domain == false) { + warnx("Failed to initialize SCSIDomain at %s", __method__); + return false; } - // この親の Init() は connected_devices 作成後に呼ぶ - if (inherited::Init() == false) { + // バス + try { + bus.reset(new SCSIBus(domain.get())); + } catch (...) { } + if ((bool)bus == false) { + warnx("Failed to initialize SCSIBus at %s", __method__); return false; } + return true; +} + +// 初期化 +bool +SPCDevice::Init() +{ + // 実行中に別インスタンスを頻繁に参照しないようにここで 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) { // 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); } // 割り込み信号線の接続先 @@ -103,7 +125,20 @@ SPCDevice::Init() interrupt = GetInterruptDevice(); } - monitor.SetSize(70, 15 + connected_devices.size()); + auto evman = GetEventManager(); + phase_event = evman->Regist(this, NULL, "SPC Phase"); + timer_event = evman->Regist(this, NULL, "SPC Timer"); + + // モニタに表示するアドレス + if (vmtype == VMType::X68030) { + baseaddr = 0xe96021; + regoffset = 2; + } else { + baseaddr = 0xe1000000; + regoffset = 4; + } + // 「イニシエータ以外のターゲット数」はここでは確定している。 + monitor->SetSize(80, 15 + domain->GetTargetCount() + 1); return true; } @@ -128,192 +163,211 @@ SPCDevice::ResetHard(bool poweron) // このビットは落ちるのではないだろうか spc.sdgc = 0x00; + // キューはリセットされる。 + // mb89351-scsi.pdf (日本語) + spc.dreg.Clear(); + // 内部状態 MakeIntsMask(); + ChangeInterrupt(); prev_ints = (uint32)-1; 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; } -uint64 -SPCDevice::Read(uint32 offset) +busdata +SPCDevice::ReadPort(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; + data = 1U << myid; + 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) { - if (event_tc.IsRunning()) { + uint32 new_ints = data.Data(); + if (timer_event->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) { - if (event_tc.IsRunning()) { + uint32 new_ssts = data.Data(); + if (timer_event->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); + VMPANIC("corrupted offset=%u", offset); } + + data |= read_wait; + data |= BusData::Size1; + return data; } -uint64 -SPCDevice::Write(uint32 offset, uint32 data) +busdata +SPCDevice::WritePort(uint32 offset, uint32 data) { - mpu->AddCycle(write_wait); + busdata r; switch (offset) { case SPC::BDID: + { + uint cur_myid = host->GetMyID(); putlog(3, "BDID <- $%02x", data); - if (myid == data) { + if (data == cur_myid) { // 同じなら何もしない - return 0; + break; } - if (myid == -1) { - // 初回なのでバスに参加 + if ((int)cur_myid < 0) { + // 初回なのでバスに参加。myid はこちらでもコピーを覚えておく。 myid = data; - scsibus->Attach(this, myid); + if (domain->AttachInitiator(bus.get(), myid) == false) { + putlog(0, "BDID %u conflicts with existing devices!", myid); + } + bus->Refresh(); } else { // そうでなければ移動? - putlog(0, "BDID change %d to %d (NOT IMPLEMENTED)", myid, data); - return 0; + putlog(0, "BDID change %u to %u (NOT IMPLEMENTED)", cur_myid, data); } - 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: putlog(3, "DREG <- $%02x", data); - return WriteDREG(data); + r = WriteDREG(data); + break; case SPC::TEMP: spc.temp_out = data; @@ -334,36 +388,40 @@ 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); + VMPANIC("corrupted offset=%u", offset); } + + r |= write_wait; + r |= BusData::Size1; + return r; } -uint64 -SPCDevice::Peek(uint32 offset) +busdata +SPCDevice::PeekPort(uint32 offset) { switch (offset) { case SPC::BDID: - return 1 << myid; + return 1U << myid; case SPC::SCTL: return spc.sctl; @@ -400,7 +458,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); @@ -426,8 +484,14 @@ SPCDevice::Peek(uint32 offset) } } +bool +SPCDevice::PokePort(uint32 offset, uint32 data) +{ + return false; +} + void -SPCDevice::MonitorUpdate(Monitor *, TextScreen& screen) +SPCDevice::MonitorScreen(Monitor *, TextScreen& screen) { int x; int y; @@ -453,33 +517,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), (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); + 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 @@ -559,32 +625,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 = 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"); screen.Puts(x, y++, TA::OnOff(bsy), "BSY"); @@ -600,23 +654,24 @@ 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))) { - screen.Print(x + 4 + 8 - i, y, "%d", i); + if ((bsy & (1U << i))) { + screen.Print(x + 4 + 8 - i, y, "%u", i); } else { screen.Print(x + 4 + 8 - i, y, "_"); } } y++; for (int i = 0; i < 8; i++) { - if ((sel & (1 << i))) { - screen.Print(x + 4 + 8 - i, y, "%d", i); + if ((sel & (1U << i))) { + screen.Print(x + 4 + 8 - i, y, "%u", i); } else { screen.Print(x + 4 + 8 - i, y, "_"); } @@ -624,9 +679,9 @@ SPCDevice::MonitorUpdate(Monitor *, Text // 転送フェーズ中なら現在の XferPhase y += 2; - if (ph == SCSI::Phase::Transfer) { - screen.Puts(x + 5, y, SCSI::GetXferPhaseName(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 @@ -637,20 +692,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 : domain->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 = '-'; @@ -662,29 +717,27 @@ 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; - } + 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 = domain->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 の下請け。 +// レジスタをビットごとに表示する。MonitorScreen の下請け。 void SPCDevice::MonitorReg(TextScreen& screen, int x, int y, uint32 reg, const char * const *names) @@ -693,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]); } } @@ -841,9 +894,9 @@ SPCDevice::WriteINTS(uint32 data) 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->SetCallback(&SPCDevice::SelectionTimeout); + timer_event->time = spc.tc * t_CLF * 2; + scheduler->RestartEvent(timer_event); } } } @@ -895,8 +948,8 @@ SPCDevice::ChangeInterrupt() } // PSNS レジスタ値の取得。 -// Read*() と Peek8() から呼ばれる。 -// scsibus 接続前にもモニタの更新などで呼ばれることに注意。 +// ReadPort() と PeekPort() から呼ばれる。 +// SCSIBus 接続前にもモニタの更新などで呼ばれることに注意。 uint32 SPCDevice::GetPSNS() const { @@ -927,15 +980,15 @@ SPCDevice::GetPSNS() const } // SSTS レジスタ値の取得。 -// Read*() と Peek8() から呼ばれる。 -// scsibus 接続前にもモニタの更新などで呼ばれることに注意。 +// ReadPort() と PeekPort() から呼ばれる。 +// SCSIBus 接続前にもモニタの更新などで呼ばれることに注意。 uint32 SPCDevice::GetSSTS() const { uint32 data; data = spc.ssts & 0xf0; - if (__predict_true(bus != NULL) && GetRST()) { // RST 信号 + if (__predict_true(bus != NULL) && GetRST()) { // RST 信号 data |= SPC::SSTS_RST_IN; } if (spc.tc == 0) { // TC がゼロか @@ -982,23 +1035,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; } } } @@ -1013,10 +1068,10 @@ SPCDevice::ReadDREG() bool q_was_full = spc.dreg.IsFull(); data = spc.dreg.Dequeue(); if (loglevel >= 4) { - putlogn("DREG -> $%02x (q=%d TC=0x%x)", - data, spc.dreg.Length(), spc.tc); + putlogn("DREG -> $%02x (q=%u TC=0x%x)", + 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 +1087,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 +1101,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 +1143,7 @@ SPCDevice::WriteDREG(uint32 data) // XXX 時間は適当 CallAfter(HardwareTransfer, "SPC WriteDREG to Transfer", t_CLF); } - return 0; + return r; } // バスフリーフェーズに移行する @@ -1093,7 +1152,7 @@ SPCDevice::BusFree() { putlog(3, "BusFree"); - // ここは BDID 書き込み前 (まだ scsibus に自身をつなげる前) にも呼ばれる。 + // ここは BDID 書き込み前 (まだ SCSIBus に自身をつなげる前) にも呼ばれる。 // その場合は何もしない。バスフリーのはずでもあるし。 if (bus == NULL) { return; @@ -1137,19 +1196,19 @@ 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 がない - 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()); @@ -1208,9 +1267,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->SetCallback(&SPCDevice::SelectionTimeout); + timer_event->time = (N + 15) * t_CLF * 2; + scheduler->RestartEvent(timer_event); } if ((spc.sctl & SPC::SCTL_ARBIT_EN)) { @@ -1221,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"); @@ -1255,15 +1314,15 @@ SPCDevice::Arbitration1(Event& ev) } 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"); @@ -1273,7 +1332,7 @@ SPCDevice::Arbitration2(Event& ev) // アービトレーションフェーズを使わない場合のセレクションフェーズで、 // 規定時間経過したので SEL を立てるところ。 void -SPCDevice::Selection1(Event& ev) +SPCDevice::Selection1(Event *ev) { putlog(3, "Selection1"); @@ -1290,17 +1349,17 @@ SPCDevice::Selection1(Event& ev) // セレクションコマンドがタイムアウトした時に呼ばれるコールバック。 void -SPCDevice::SelectionTimeout(Event& ev) +SPCDevice::SelectionTimeout(Event *ev) { if (loglevel >= 3) { int tgtid; uint8 data = GetData(); - data &= ~(1 << myid); - tgtid = DecodeID(data); + data &= ~(1U << myid); + tgtid = SCSIBus::DecodeID(data); if (tgtid == -1) { putlogn("Selection timeout"); } else { - putlogn("Selection timeout (#%d)", tgtid); + putlogn("Selection timeout (#%u)", tgtid); } } @@ -1361,21 +1420,20 @@ SPCDevice::TransferCommand() // Transfer コマンド発行時に REQ がアサートされていなければ // アサートされるまで待つ。この場合、ここでは何もすることはなく、 - // TransferReqCB() が呼ばれてそこから転送フェーズが動き始める。 + // OnTransferReq() が呼ばれてそこから転送フェーズが動き始める。 if (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; @@ -1392,19 +1450,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; @@ -1413,9 +1467,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; @@ -1423,7 +1478,7 @@ SPCDevice::TransferReqCB() if (transfer_command_running) { // Transfer Command 実行中なら REQ 待ちだったので // ここでハードウェア転送を開始。 - HardwareTransfer(event); + HardwareTransfer(phase_event); } // データシートでは、マニュアル転送で IN 方向の時データバスの内容を @@ -1441,9 +1496,9 @@ SPCDevice::TransferReqCB() // イベントコールバックとして呼び出すこと。 // REQ がアサートされているのを確認してから呼び出すこと。 // (1バイト送信するとターゲットが再び REQ を上げるなどしてくるので、 -// TransferReqCB() か DREG 書き込みから再びここが呼ばれる) +// OnTransferReq() か DREG 書き込みから再びここが呼ばれる) void -SPCDevice::HardwareTransfer(Event& ev) +SPCDevice::HardwareTransfer(Event *ev) { // SDGC_XFER_ENABLE は転送要求ごとに割り込みを上げる。 // (SPC から見て) 入力操作時は読み込めるデータがある場合、出力操作時は @@ -1491,7 +1546,7 @@ SPCDevice::HardwareTransfer(Event& ev) // 転送完了 spc.tc--; - putlog(4, "Hardware Transfer: %s q=%d tc=%d", + putlog(4, "Hardware Transfer: %s q=%u tc=%u", (GetIO() ? "IN" : "OUT"), spc.dreg.Length(), spc.tc); if (spc.tc == 0) { // どちらでも転送自体はここで完了(?)