--- nono/vm/spc.cpp 2026/04/29 17:04:39 1.1.1.5 +++ nono/vm/spc.cpp 2026/04/29 17:04:42 1.1.1.6 @@ -10,6 +10,7 @@ #include "mpu88xx0.h" #include "mystring.h" #include "scheduler.h" +#include "sram.h" #include "vm.h" // time 時間後に呼び出すコールバックをセットする。 @@ -37,8 +38,6 @@ SPCDevice::SPCDevice() SetConfigName("spc0"); myid = -1; - monitor_size = nnSize(70, 15); - // 汎用イベント // 名前とコールバック関数は都度セットする。 event.dev = this; @@ -71,6 +70,41 @@ SPCDevice::Init() const ConfigItem& item = gConfig->Find("!spc-tCLF"); t_CLF = item.AsInt(); + // アクセスウェイト + if (vmtype == VMTYPE_X68030) { + // InsideOut p.135 + read_wait = 18; + write_wait = 19; + } else { + // 他機種は未調査だけど、判明するまでの間とりあえず + // ノーウェイトでない適当な値を入れておく。 + read_wait = 18; + write_wait = 19; + } + + // 本体の SCSI ID は SPC(MB89352) の BDID レジスタに ID が書き込まれる + // のを待たずに、最初から知っていることにする。本来 (少なくとも真っ当な + // システムでは) イニシエータを含むすべての SCSI 機器の SCSI ID は + // 電源を入れる前に、人間が一々手作業で重複なく設定しているはずなので。 + // X68030 は SRAM $ED0070 下位3ビット。LUNA は 7 固定のようだ。 + uint myid0; + if (vmtype == VMTYPE_X68030) { + myid0 = gSRAM->Peek8(0xed0070) & 7; + } else { + myid0 = 7; + } + + // モニタには、接続している SCSI デバイスだけを表示する。 + // そのため ID ではなく前から何番目のデバイスかのリストが欲しい。 + for (int id = 0; id < 8; id++) { + if (id == myid0) { + connected_devices.push_back(this); + } else if (target[id].get()) { + connected_devices.push_back(target[id].get()); + } + } + monitor_size = nnSize(70, 15 + connected_devices.size()); + return true; } @@ -79,18 +113,17 @@ SPCDevice::Init() void SPCDevice::ResetHard() { - // LUNA は本体回路が分からないのでとりあえずこっち。 - if (vmtype != VMTYPE_X68030) { - Reset(); - } + Reset(); } // MPU の RESET 命令によるリセット void SPCDevice::ResetSoft() { - // X680x0 はこっちだと分かっている。 - if (vmtype == VMTYPE_X68030) { + // X68030 は RESET 命令でもリセットがかかる。 + // LUNA は本体回路が分からないし PROM が何回も RESET 命令を発行するので、 + // とりあえず外しておく。 + if (vmtype != VMTYPE_X68030) { Reset(); } } @@ -127,6 +160,8 @@ SPCDevice::Read(uint32 addr) { uint8 data; + gMPU->AddCycle(read_wait); + switch (addr) { case SPC::BDID: data = 1 << myid; @@ -232,6 +267,8 @@ SPCDevice::Read(uint32 addr) uint64 SPCDevice::Write(uint32 addr, uint32 data) { + gMPU->AddCycle(write_wait); + switch (addr) { case SPC::BDID: putlog(3, "BDID <- $%02x", data); @@ -304,8 +341,8 @@ SPCDevice::Write(uint32 addr, uint32 dat // luna68k の /boot はここで SEL が下げてしまうと期待した動作に // ならないようだ。 // SPC はセレクションタイムアウトしたからと言って、データバスに出した - // ID を自動的に取り下げたりはしないので、手動でデータバスをクリア - // してあげないといけない (TEMP に $00 を書き込む)、だったんでは。 + // ID を自動的に取り下げたりはしないので、TEMP に $00 を書き込むことで + // データバスをクリアする、だったんでは。 if (0 && GetPhase() == SCSI::Phase::Selection) { NegateSEL(); } @@ -603,23 +640,72 @@ SPCDevice::MonitorUpdate(TextScreen& mon monitor.Puts(x + 5, y, SCSI::GetXferPhaseName(GetXfer())); } - // ターゲット + // デバイス一覧 + // 0 1 + // 01234567890123456 + // ID Device Command + // 0: HD(1024MB) $06 Read + // 7: Initiator x = 0; y = 14; + monitor.Print(x, y++, "ID Device Command"); + for (const auto d : connected_devices) { + std::string desc = "?"; + switch (d->GetDevType()) { + case SCSI::DevType::Initiator: + desc = "Initiator"; + break; + + case SCSI::DevType::HD: + { + SCSIHD *hd = dynamic_cast(d); + if (hd) { + desc = string_format("HD(%dMB)", + (int)(hd->GetSize() / 1024 / 1024)); + } + break; + } + + default: + break; + } + + // 本体 ID は BDID が設定される前でもエミュレータ的に知ってはいるが、 + // BDID への書き込みがされなかったりおかしかったりしたら (上の BDID + // 欄でも分かるけど) ここも反映されるようにしておく。 + char idc; + if (d->GetMyID() >= 0) { + idc = '0' + d->GetMyID(); + } else { + idc = '-'; + } + monitor.Print(x, y, "%c: %s", idc, desc.c_str()); + y++; + } + + // 実行中のコマンド + 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) { - const char *cmdname = SCSI::GetCommandName(cmds[0]); - monitor.Print(x, y, "Command: $%02x", cmds[0]); - if (cmdname) { - monitor.Puts(x + 14, y, cmdname); + // この ID のデバイス行に表示したい + // XXX もうちょっとマシにしたい + int i = 0; + for (; i < connected_devices.size(); i++) { + if (d == connected_devices[i]) { + break; + } } + const char *cmdname = SCSI::GetCommandName(cmds[0]); + monitor.Print(x, y + i, "$%02x %s", cmds[0], cmdname ?: ""); } } } + } // レジスタをビットごとに表示する。MonitorUpdate の下請け。 @@ -1092,7 +1178,7 @@ SPCDevice::SelectCommand() // アービトレーションフェーズ開始後、規定時間経過したので SEL を立てるところ。 void -SPCDevice::Arbitration1(int dummy) +SPCDevice::Arbitration1(Event& ev) { putlog(3, "Arbitration1"); @@ -1115,7 +1201,7 @@ SPCDevice::Arbitration1(int dummy) // アービトレーションフェーズ中、SEL を立ててから規定時間経過したので BSY を // 下げるところ。 void -SPCDevice::Arbitration2(int dummy) +SPCDevice::Arbitration2(Event& ev) { putlog(3, "Arbitration2"); @@ -1125,7 +1211,7 @@ SPCDevice::Arbitration2(int dummy) // アービトレーションフェーズを使わない場合のセレクションフェーズで、 // 規定時間経過したので SEL を立てるところ。 void -SPCDevice::Selection1(int dummy) +SPCDevice::Selection1(Event& ev) { putlog(3, "Selection1"); @@ -1139,7 +1225,7 @@ SPCDevice::Selection1(int dummy) // セレクションコマンドがタイムアウトした時に呼ばれるコールバック。 void -SPCDevice::SelectionTimeout(int dummy) +SPCDevice::SelectionTimeout(Event& ev) { if (loglevel >= 3) { int tgtid; @@ -1278,7 +1364,7 @@ SPCDevice::TransferReqCB() // Transfer Command 実行中なら REQ 待ちだったので // ここでハードウェア転送を開始。 - TransferCommandStart(0); + TransferCommandStart(event); } else { // ハードウェア転送中でなくて IN 方向ならここで TEMP に取り込む。 if (GetIO()) { @@ -1290,13 +1376,13 @@ SPCDevice::TransferReqCB() // Transfer Command の実行開始部分。 // TransferCommand() と TransferReqCB() から呼ばれる。 void -SPCDevice::TransferCommandStart(int dummy) +SPCDevice::TransferCommandStart(Event& ev) { // Transfer コマンドの発行と REQ がアサートの両方が成立した時点で、 // SCSI バスの状態と PCTL レジスタの下位3ビットの状態を比較する。 // 一致していれば転送開始、そうでなければ Service Required 割り込み。 if ((GetPCTL() & 7) == GetXfer()) { - HardwareTransfer(0); + HardwareTransfer(event); } else { transfer_command_running = false; SetINTS(SPC::INTS_SERV_REQ); @@ -1309,7 +1395,7 @@ SPCDevice::TransferCommandStart(int dumm // (1バイト送信するとターゲットが再び REQ を上げるなどしてくるので、 // TransferReqCB() か DREG 書き込みから再びここが呼ばれる) void -SPCDevice::HardwareTransfer(int dummy) +SPCDevice::HardwareTransfer(Event& ev) { // SDGC_XFER_ENABLE は転送要求ごとに割り込みを上げる。 // (SPC から見て) 入力操作時は読み込めるデータがある場合、出力操作時は