--- nono/vm/spc.cpp 2026/04/29 17:04:36 1.1.1.4 +++ nono/vm/spc.cpp 2026/04/29 17:04:56 1.1.1.10 @@ -5,11 +5,12 @@ // #include "spc.h" +#include "interrupt.h" #include "mainapp.h" #include "mpu680x0.h" #include "mpu88xx0.h" -#include "mystring.h" -#include "scheduler.h" +#include "pedec.h" +#include "sram.h" #include "vm.h" // time 時間後に呼び出すコールバックをセットする。 @@ -26,9 +27,8 @@ std::unique_ptr gSPC; // コンストラクタ SPCDevice::SPCDevice() + : inherited("SPC") { - logname = "spc"; - devname = "SPC"; // これらは XIOSpace で必要なので、LUNA ではダミー。 // もうちょっといい方法を考えたほうがいい。 devaddr = 0xe96000; @@ -37,17 +37,17 @@ SPCDevice::SPCDevice() SetConfigName("spc0"); myid = -1; - monitor.Init(70, 15); - // 汎用イベント // 名前とコールバック関数は都度セットする。 - event.dev = this; event.SetName("SPC phase"); // TC カウンタイベント - event_tc.dev = this; event_tc.code = 0; event_tc.SetName("SPC Timer"); + + monitor.func = (MonitorCallback_t)&SPCDevice::MonitorUpdate; + // サイズは Init で決まる + monitor.Regist(ID_MONITOR_SPC); } // デストラクタ @@ -71,33 +71,55 @@ SPCDevice::Init() const ConfigItem& item = gConfig->Find("!spc-tCLF"); t_CLF = item.AsInt(); - return true; -} - + // アクセスウェイト + if (vmtype == VMTYPE_X68030) { + // InsideOut p.135 + read_wait = 18; + write_wait = 19; + } else { + // 他機種は未調査だけど、判明するまでの間とりあえず + // ノーウェイトでない適当な値を入れておく。 + read_wait = 18; + write_wait = 19; + } -// 本体リセットスイッチによるリセット -void -SPCDevice::ResetHard() -{ - // LUNA は本体回路が分からないのでとりあえずこっち。 - if (vmtype != VMTYPE_X68030) { - Reset(); + // 割り込み信号線の接続先 + if (vmtype == VMTYPE_X68030) { + // X68030 は PEDEC (I/O コントローラ) にカスケード + interrupt = gPEDEC.get(); + } else { + interrupt = gInterrupt.get(); } -} -// MPU の RESET 命令によるリセット -void -SPCDevice::ResetSoft() -{ - // X680x0 はこっちだと分かっている。 + // 本体の SCSI ID は SPC(MB89352) の BDID レジスタに ID が書き込まれる + // のを待たずに、最初から知っていることにする。本来 (少なくとも真っ当な + // システムでは) イニシエータを含むすべての SCSI 機器の SCSI ID は + // 電源を入れる前に、人間が一々手作業で重複なく設定しているはずなので。 + // X68030 は SRAM $ED0070 下位3ビット。LUNA は 7 固定のようだ。 + uint myid0; if (vmtype == VMTYPE_X68030) { - Reset(); + 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.SetSize(70, 15 + connected_devices.size()); + + return true; } -// SPC リセット +// デバイスリセット void -SPCDevice::Reset() +SPCDevice::ResetHard() { putlog(1, "リセット"); @@ -114,6 +136,7 @@ SPCDevice::Reset() spc.sdgc = 0x00; // 内部状態 + MakeIntsMask(); prev_ints = (uint32)-1; prev_ssts = (uint32)-1; set_atn_in_selection = false; @@ -123,11 +146,13 @@ SPCDevice::Reset() } uint64 -SPCDevice::Read(uint32 addr) +SPCDevice::Read(uint32 offset) { uint8 data; - switch (addr) { + gMPU->AddCycle(read_wait); + + switch (offset) { case SPC::BDID: data = 1 << myid; putlog(3, "BDID -> $%02x", data); @@ -149,16 +174,16 @@ SPCDevice::Read(uint32 addr) return 0xfc; case SPC::INTS: - data = spc.ints; + data = GetINTS(); if (loglevel >= 3) { if (event_tc.active) { // イベント中は値が変わった時だけ表示 if (prev_ints != data) { - putlog("INTS -> $%02x", data); + putlogn("INTS -> $%02x", data); prev_ints = data; } } else { - putlog("INTS -> $%02x", data); + putlogn("INTS -> $%02x", data); prev_ints = -1U; } } @@ -175,18 +200,18 @@ SPCDevice::Read(uint32 addr) if (event_tc.active) { // イベント中は値が変わった時だけ表示 if (prev_ssts != data) { - putlog("SSTS -> $%02x", data); + putlogn("SSTS -> $%02x", data); prev_ssts = data; } } else { - putlog("SSTS -> $%02x", data); + putlogn("SSTS -> $%02x", data); prev_ssts = -1U; } } return data; case SPC::SERR: - data = spc.serr; + data = GetSERR(); putlog(3, "SERR -> $%02x", data); return data; @@ -230,9 +255,11 @@ SPCDevice::Read(uint32 addr) } uint64 -SPCDevice::Write(uint32 addr, uint32 data) +SPCDevice::Write(uint32 offset, uint32 data) { - switch (addr) { + gMPU->AddCycle(write_wait); + + switch (offset) { case SPC::BDID: putlog(3, "BDID <- $%02x", data); if (myid == data) { @@ -304,8 +331,8 @@ SPCDevice::Write(uint32 addr, uint32 dat // luna68k の /boot はここで SEL が下げてしまうと期待した動作に // ならないようだ。 // SPC はセレクションタイムアウトしたからと言って、データバスに出した - // ID を自動的に取り下げたりはしないので、手動でデータバスをクリア - // してあげないといけない (TEMP に $00 を書き込む)、だったんでは。 + // ID を自動的に取り下げたりはしないので、TEMP に $00 を書き込むことで + // データバスをクリアする、だったんでは。 if (0 && GetPhase() == SCSI::Phase::Selection) { NegateSEL(); } @@ -336,9 +363,9 @@ SPCDevice::Write(uint32 addr, uint32 dat } uint64 -SPCDevice::Peek(uint32 addr) +SPCDevice::Peek(uint32 offset) { - switch (addr) { + switch (offset) { case SPC::BDID: return 1 << myid; @@ -354,7 +381,7 @@ SPCDevice::Peek(uint32 addr) return 0xfc; case SPC::INTS: - return spc.ints; + return GetINTS(); case SPC::PSNS: return GetPSNS(); @@ -363,7 +390,7 @@ SPCDevice::Peek(uint32 addr) return GetSSTS(); case SPC::SERR: - return spc.serr; + return GetSERR(); case SPC::PCTL: return GetPCTL(); @@ -401,8 +428,8 @@ SPCDevice::Peek(uint32 addr) __unreachable(); } -bool -SPCDevice::MonitorUpdate() +void +SPCDevice::MonitorUpdate(Monitor *, TextScreen& screen) { int x; int y; @@ -416,43 +443,43 @@ SPCDevice::MonitorUpdate() uint32 pctl; uint32 tc; - monitor.Clear(); + screen.Clear(); sctl = spc.sctl; scmd = spc.scmd; - ints = spc.ints; + ints = GetINTS(); psns = GetPSNS(); sdgc = spc.sdgc; ssts = GetSSTS(); - serr = spc.serr; + serr = GetSERR(); pctl = GetPCTL(); tc = spc.tc; y = 0; - monitor.Print(0, y++, "BDID: $%02x (ID=%d)", (1 << myid), myid); - monitor.Print(0, y++, "SCTL: $%02x", sctl); - monitor.Print(0, y++, "SCMD: $%02x", scmd); - monitor.Print(0, y++, "INTS: $%02x", ints); - monitor.Print(0, y++, "PSNS: $%02x", psns); - monitor.Print(0, y++, "SDGC: $%02x", sdgc); - monitor.Print(0, y++, "SSTS: $%02x", ssts); - monitor.Print(0, y++, "SERR: $%02x", serr); - monitor.Print(0, y++, "PCTL: $%02x", pctl); - monitor.Print(0, y++, "MBC : $XX"); - monitor.Print(0, y, "DREG: "); + 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: "); { uint i; for (i = 0; i < spc.dreg.Length(); i++) { - monitor.Print(6 + i * 4, y, "$%02x", spc.dreg.Peek(i)); + screen.Print(6 + i * 4, y, "$%02x", spc.dreg.Peek(i)); } for (; i < 8; i++) { - monitor.Print(6 + i * 4, y, TA::Disable, "---"); + screen.Print(6 + i * 4, y, TA::Disable, "---"); } } y++; - monitor.Print(0, y++, "TEMP: $%02x(IN) $%02x(OUT)", + screen.Print(0, y++, "TEMP: $%02x(IN) $%02x(OUT)", spc.temp_in, spc.temp_out); - monitor.Print(0, y++, "TC : $%06x", tc); + screen.Print(0, y++, "TC : $%06x", tc); x = 10; y = 1; @@ -461,14 +488,14 @@ SPCDevice::MonitorUpdate() static const char * const sctl_bits[] = { "RST", "CTRL", "DIAG", "ARB", "PARI", "SEL", "RESL", "INTR" }; - MonitorReg(x, y, sctl, sctl_bits); + MonitorReg(screen, x, y, sctl, sctl_bits); y++; // SCMD static const char * const scmd_bits[] = { "", "", "", "RST", "INTC", "PROG", "", "TERM" }; - MonitorReg(x, y, scmd, scmd_bits); + MonitorReg(screen, x, y, scmd, scmd_bits); static const char * const scmd_cmd[] = { //12345678901234 "(Bus Release)", @@ -480,35 +507,35 @@ SPCDevice::MonitorUpdate() "(ResetACK/REQ)", "(Set ACK/REQ)", }; - monitor.Puts(x, y, scmd_cmd[scmd >> 5]); + screen.Puts(x, y, scmd_cmd[scmd >> 5]); y++; // INTS static const char * const ints_bits[] = { "Sel", "Resl", "Disc", "Cmpl", "SReq", "TOut", "HErr", "Rst" }; - MonitorReg(x, y, ints, ints_bits); + MonitorReg(screen, x, y, ints, ints_bits); y++; // PSNS static const char * const psns_bits[] = { "REQ", "ACK", "ATN", "SEL", "BSY", "MSG", "C/D", "I/O" }; - MonitorReg(x, y, psns, psns_bits); + MonitorReg(screen, x, y, psns, psns_bits); y++; // SDGC static const char * const sdgc_bits[] = { "", "", "XfEn", "", "", "", "", "" }; - MonitorReg(x, y, sdgc, sdgc_bits); + MonitorReg(screen, x, y, sdgc, sdgc_bits); y++; // SSTS static const char * const ssts_bits[] = { "", "", "SBSY", "XInP", "RSTI", "TCZ", "Full", "Empt" }; - MonitorReg(x, y, ssts, ssts_bits); + MonitorReg(screen, x, y, ssts, ssts_bits); static const char * const ssts_conn[] = { //12345678 "(NotConn)", @@ -516,21 +543,21 @@ SPCDevice::MonitorUpdate() "(As Init)", "(undef?)", }; - monitor.Puts(x, y, ssts_conn[ssts >> 6]); + screen.Puts(x, y, ssts_conn[ssts >> 6]); y++; // SERR static const char * const serr_bits[] = { "", "", "XOut", "", "PErr", "", "Shrt", "" }; - MonitorReg(x, y, serr, serr_bits); + MonitorReg(screen, x, y, serr, serr_bits); y++; // PCTL static const char * const pctl_bits[] = { "BIE", "", "", "", "", "MSG", "C/D", "I/O" }; - MonitorReg(x, y, pctl, pctl_bits); + MonitorReg(screen, x, y, pctl, pctl_bits); y++; // バス状態 @@ -560,77 +587,125 @@ SPCDevice::MonitorUpdate() ack = GetACK(); data = GetData(); } - 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); + 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"); + screen.Puts(x, y++, TA::OnOff(sel), "SEL"); + screen.Puts(x, y++, TA::OnOff(msg), "MSG"); + screen.Puts(x, y++, TA::OnOff(cd), "CD"); + screen.Puts(x, y++, TA::OnOff(io), "IO"); + screen.Puts(x, y++, TA::OnOff(atn), "ATN"); + screen.Puts(x, y++, TA::OnOff(req), "REQ"); + screen.Puts(x, y++, TA::OnOff(ack), "ACK"); + screen.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++, "+"); + 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++, "+"); // BSY, SEL の内訳。"(76543210)" の順。 y = 2; for (int i = 0; i < 8; i++) { if ((bsy & (1 << i))) { - monitor.Print(x + 4 + 8 - i, y, "%d", i); + screen.Print(x + 4 + 8 - i, y, "%d", i); } else { - monitor.Print(x + 4 + 8 - i, y, "_"); + screen.Print(x + 4 + 8 - i, y, "_"); } } y++; for (int i = 0; i < 8; i++) { if ((sel & (1 << i))) { - monitor.Print(x + 4 + 8 - i, y, "%d", i); + screen.Print(x + 4 + 8 - i, y, "%d", i); } else { - monitor.Print(x + 4 + 8 - i, y, "_"); + screen.Print(x + 4 + 8 - i, y, "_"); } } // 転送フェーズ中なら現在の XferPhase y += 2; if (ph == SCSI::Phase::Transfer) { - monitor.Puts(x + 5, y, SCSI::GetXferPhaseName(GetXfer())); + screen.Puts(x + 5, y, SCSI::GetXferPhaseName(GetXfer())); } - // ターゲット + // デバイス一覧 + // 0 1 + // 01234567890123456 + // ID Device Command + // 0: HD(1024MB) $06 Read + // 7: Initiator x = 0; y = 14; + screen.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 = '-'; + } + screen.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]); + screen.Print(x, y + i, "$%02x %s", cmds[0], cmdname ?: ""); } } } - return true; } // レジスタをビットごとに表示する。MonitorUpdate の下請け。 void -SPCDevice::MonitorReg(int x, int y, uint32 reg, const char * const *names) +SPCDevice::MonitorReg(TextScreen& screen, + int x, int y, uint32 reg, const char * const *names) { for (int i = 0; i < 8; i++) { bool b = reg & (1 << (7 - i)); - monitor.Puts(x + i * 5, y, TA::OnOff(b), names[i]); + screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]); } } @@ -645,7 +720,7 @@ SPCDevice::WriteSCTL(uint32 data) putlog(3, "SCTL <- $%02x", spc.sctl); if ((up & SPC::SCTL_RESET)) { // リセット - Reset(); + ResetHard(); } else { // 通常動作 } @@ -655,6 +730,21 @@ SPCDevice::WriteSCTL(uint32 data) if ((spc.sctl & SPC::SCTL_DIAG_MODE)) { putlog(0, "SCTL 未実装 Diag Mode"); } + + // 割り込みマスクを更新して信号線の状態を変える + MakeIntsMask(); + ChangeInterrupt(); +} + +// SCTL_INTR_EN の状態から内部変数 ints_mask を作成する。 +void +SPCDevice::MakeIntsMask() +{ + if ((spc.sctl & SPC::SCTL_INTR_EN)) { + spc.ints_mask = SPC::INTS_ENABLE_MASK; + } else { + spc.ints_mask = SPC::INTS_DISABLE_MASK; + } } // SCMD レジスタへの書き込み @@ -665,8 +755,7 @@ SPCDevice::WriteSCMD(uint32 data) spc.scmd = data; putlog(3, "SCMD <- $%02x", spc.scmd); - // Intercept, Program, Terminate Mode の未実装判定は - // 転送開始時のみ表示する。 + // Program, Terminate Mode の未実装判定は転送開始時のみ表示する。 // RST Out 操作は SCTL Reset がオフの時だけ有効 // と書いてあるけど、SCTL Reset がオンならほぼ全機能無効なのでは? @@ -690,6 +779,11 @@ SPCDevice::WriteSCMD(uint32 data) switch (spc.scmd >> 5) { case 0: putlog(2, "SCMD Bus Release コマンド"); + if ((spc.scmd & SPC::SCMD_INTERCEPT)) { + // BusRelease コマンドの発行と共に Intercept ビットが立っていたら + // Service Required 割り込みをリセットする? + putlog(0, "SCMD Intercept with BusRelease not implemented"); + } BusFree(); break; case 1: @@ -717,6 +811,15 @@ SPCDevice::WriteSCMD(uint32 data) putlog(2, "SCMD Set ACK/REQ コマンド"); // イニシエータなら ACK を、ターゲットなら REQ をセットする。 // ターゲットモードはサポートしないので、操作するのは ACK のみ。 + if ((spc.scmd & SPC::SCMD_INTERCEPT)) { + // Intercept なら DREG を維持したまま (たぶん TEMP で) バスを + // 直接アクセスするようだが、この DREG 維持あたりは未対応。 + // これが影響するのは DREG にデータが滞留してる時だけのはず + // なので、その時だけログを出す。(X68030 IPLROM、NetBSD/x68k) + if (spc.dreg.Length() != 0) { + putlog(0, "SCMD Intercept with SetACK not implemented"); + } + } AssertACK(); break; } @@ -733,11 +836,7 @@ SPCDevice::WriteINTS(uint32 data) // '1' が書き込まれたビットをクリア spc.ints &= ~data; - putlog(3, "INTS <- $%02x (INTS = $%02x)", data, spc.ints); - - // SPC では INTS でビットを落とす行為は、 - // ホストが割り込み要因に対して処理を完了したことを - // SPC に通知する意味が含まれている。 + putlog(3, "INTS <- $%02x (INTS = $%02x)", data, GetINTS()); // DISCONNECTED ビットが立っているときは SPC は SCSI から // セレクション、リセレクション要求が来ても応答しない。 @@ -758,18 +857,17 @@ SPCDevice::WriteINTS(uint32 data) } } } + + // 割り込み信号線の状態を変える + ChangeInterrupt(); } // INTS 設定。 -// INTS にビットを立てる要因が起きた時に data にそれを指定して呼び出す。 -// この関数は INTS を書き換えて、必要なら割り込みを上げる。 +// 引数 data は割り込み要因(ここは内部要因なので INTS_XFER_OUT も指定可能)。 // INTS レジスタへの書き込み(割り込み要因のクリア)は WriteINTS() のほう。 void SPCDevice::SetINTS(uint32 data) { - // 立ち上がりでだけ割り込みがかかるわけではないが表示のため。 - uint32 up = ~spc.ints & data; - // 指定された要因を立てる spc.ints |= data; @@ -778,16 +876,30 @@ SPCDevice::SetINTS(uint32 data) NegateATN(); } - // 割り込み許可なら割り込み。 - // 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 (vmtype != VMTYPE_X68030) { - gMPU->Interrupt(this, 2); - } else { - // とりあえず内蔵だけ - gMPU680x0->Interrupt(this, 1, 0x6c); - } + ChangeInterrupt(); +} + +// INTS レジスタの値を取得 +uint32 +SPCDevice::GetINTS() const +{ + // ints の上位には別の割り込み状態が保持されている。 + // 下位8bit は INTS レジスタのまま。 + return spc.ints & 0xff; +} + +// 割り込み信号線の状態を変える +void +SPCDevice::ChangeInterrupt() +{ + // 割り込み状態は ints に集約してあり、 + // ints_mask は現在の SCTL_INTR_EN 状態に応じたマスクが用意されているので + // 単純に AND するだけでよい。 + if ((spc.ints & spc.ints_mask) != 0) { + putlog(3, "割り込み ints=$%02x", spc.ints); + interrupt->AssertINT(this); + } else { + interrupt->NegateINT(this); } } @@ -838,16 +950,25 @@ SPCDevice::GetSSTS() const if (spc.tc == 0) { // TC がゼロか data |= SPC::SSTS_TC_ZERO; } - if (spc.dreg.Length() == 8) { + if (spc.dreg.IsFull()) { data |= SPC::SSTS_DREG_FULL; } - if (spc.dreg.Length() == 0) { + if (spc.dreg.Empty()) { data |= SPC::SSTS_DREG_EMPTY; } return data; } +// SERR レジスタ値の取得。 +uint32 +SPCDevice::GetSERR() const +{ + // SERR レジスタのうち XFER_OUT ビットだけ ints で保持している。 + uint32 xfer_out = (spc.ints & SPC::INTS_XFER_OUT) >> 3; + return spc.serr | xfer_out; +} + // PCTL レジスタ値の取得。 // Read*() と Peek8() から呼ばれる。 // scsibus 接続前にもモニタの更新などで呼ばれることに注意。 @@ -883,14 +1004,15 @@ SPCDevice::ReadDREG() } // XFER_OUT は DREG アクセスでリセットされる - spc.serr &= ~SPC::SERR_XFER_OUT; + spc.ints &= ~SPC::INTS_XFER_OUT; + ChangeInterrupt(); // 受信データがあれば取り出す 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)", + putlogn("DREG -> $%02x (q=%d TC=0x%x)", data, spc.dreg.Length(), spc.tc); } else { putlog(3, "DREG -> $%02x", data); @@ -933,7 +1055,8 @@ SPCDevice::WriteDREG(uint32 data) } // XFER_OUT は DREG アクセスでリセットされる - spc.serr &= ~SPC::SERR_XFER_OUT; + spc.ints &= ~SPC::INTS_XFER_OUT; + ChangeInterrupt(); // DREG キューに入れて.. spc.dreg.Enqueue(data); @@ -1052,7 +1175,7 @@ SPCDevice::SelectCommand() // t_FRID/t_BFBL はたぶん今からではなくバスフリーになった時点から起算。 // t_FRID と t_BFBL は同じ式なのでここでは t_BFBL で統一。 uint64 t_BFBL = (6 + spc.GetTCL()) * t_CLF; // Min - uint64 now = gScheduler->GetVirtTime(); + uint64 now = gMPU->GetVirtTime(); if ((last_busfree_time + t_BFBL) > now) { wait = (last_busfree_time + t_BFBL) - now; } else { @@ -1093,7 +1216,7 @@ SPCDevice::SelectCommand() // アービトレーションフェーズ開始後、規定時間経過したので SEL を立てるところ。 void -SPCDevice::Arbitration1(int dummy) +SPCDevice::Arbitration1(Event& ev) { putlog(3, "Arbitration1"); @@ -1116,7 +1239,7 @@ SPCDevice::Arbitration1(int dummy) // アービトレーションフェーズ中、SEL を立ててから規定時間経過したので BSY を // 下げるところ。 void -SPCDevice::Arbitration2(int dummy) +SPCDevice::Arbitration2(Event& ev) { putlog(3, "Arbitration2"); @@ -1126,7 +1249,7 @@ SPCDevice::Arbitration2(int dummy) // アービトレーションフェーズを使わない場合のセレクションフェーズで、 // 規定時間経過したので SEL を立てるところ。 void -SPCDevice::Selection1(int dummy) +SPCDevice::Selection1(Event& ev) { putlog(3, "Selection1"); @@ -1140,7 +1263,7 @@ SPCDevice::Selection1(int dummy) // セレクションコマンドがタイムアウトした時に呼ばれるコールバック。 void -SPCDevice::SelectionTimeout(int dummy) +SPCDevice::SelectionTimeout(Event& ev) { if (loglevel >= 3) { int tgtid; @@ -1148,9 +1271,9 @@ SPCDevice::SelectionTimeout(int dummy) data &= ~(1 << myid); tgtid = DecodeID(data); if (tgtid == -1) { - putlog("Selection timeout"); + putlogn("Selection timeout"); } else { - putlog("Selection timeout (#%d)", tgtid); + putlogn("Selection timeout (#%d)", tgtid); } } @@ -1196,9 +1319,6 @@ SPCDevice::TransferCommand() if ((spc.scmd & SPC::SCMD_TERM_MODE)) { putlog(0, "SCMD Padding mode not implemented"); } - if ((spc.scmd & SPC::SCMD_INTERCEPT)) { - putlog(0, "SCMD Intercept Transfer not implemented"); - } transfer_command_running = true; spc.ssts |= SPC::SSTS_SPC_BUSY | SPC::SSTS_IN_PROGRESS; @@ -1226,7 +1346,7 @@ SPCDevice::BusFreeCB(int id) putlog(4, "BusFree Callback"); inherited::BusFreeCB(id); - last_busfree_time = gScheduler->GetVirtTime(); + last_busfree_time = gMPU->GetVirtTime(); transfer_command_running = false; spc.ssts &= ~(SPC::SSTS_CONN_INIT | SPC::SSTS_CONN_TARG); @@ -1271,15 +1391,9 @@ SPCDevice::TransferReqCB() spc.ssts |= SPC::SSTS_IN_PROGRESS; if (transfer_command_running) { - // ハードウェア転送で SDGC_XFER_ENABLE がセットされていれば、 - // データ要求が来ている時に XFER_OUT をセット(?) - if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) { - spc.serr |= SPC::SERR_XFER_OUT; - } - // Transfer Command 実行中なら REQ 待ちだったので // ここでハードウェア転送を開始。 - TransferCommandStart(0); + TransferCommandStart(event); } else { // ハードウェア転送中でなくて IN 方向ならここで TEMP に取り込む。 if (GetIO()) { @@ -1291,13 +1405,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); @@ -1310,7 +1424,7 @@ SPCDevice::TransferCommandStart(int dumm // (1バイト送信するとターゲットが再び REQ を上げるなどしてくるので、 // TransferReqCB() か DREG 書き込みから再びここが呼ばれる) void -SPCDevice::HardwareTransfer(int dummy) +SPCDevice::HardwareTransfer(Event& ev) { // SDGC_XFER_ENABLE は転送要求ごとに割り込みを上げる。 // (SPC から見て) 入力操作時は読み込めるデータがある場合、出力操作時は @@ -1324,7 +1438,7 @@ SPCDevice::HardwareTransfer(int dummy) // SDGC_XFER_ENABLE なら読み込めるデータがある場合に割り込み。 if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) { putlog(3, "SDGC XFER_OUT Interrupt"); - SetINTS(0); + SetINTS(SPC::INTS_XFER_OUT); } if (!queued) { putlog(4, "Hardware Transfer (DREG full)"); @@ -1339,7 +1453,7 @@ SPCDevice::HardwareTransfer(int dummy) // SDGC_XFER_ENABLE ならキューに書き込む必要がある場合に割り込み。 if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) { putlog(3, "SDGC XFER_OUT Interrupt"); - SetINTS(0); + SetINTS(SPC::INTS_XFER_OUT); } return; } @@ -1348,10 +1462,6 @@ SPCDevice::HardwareTransfer(int dummy) // DREG full/empty で2行出るのを避けたいので。 putlog(4, "Hardware Transfer"); - // XXX XFER_OUT はどこで落とすんやろうか。 - // データ要求が来たら上げるのだから用が済んだら下ろすのかしら。 - spc.serr &= ~SPC::SERR_XFER_OUT; - AssertACK(); // 転送完了