|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.12 root 7: //
8: // SPC (MB89352)
9: //
10:
1.1 root 11: #include "spc.h"
1.1.1.12 root 12: #include "config.h"
1.1.1.3 root 13: #include "mainapp.h"
1.1.1.7 root 14: #include "pedec.h"
1.1.1.12 root 15: #include "scheduler.h"
1.1.1.16! root 16: #include "scsidev.h"
1.1 root 17:
1.1.1.2 root 18: // time 時間後に呼び出すコールバックをセットする。
19: // func の書式が面倒なのを省略して書きたいため。
20: #define CallAfter(func_, name_, time_) do { \
1.1.1.16! root 21: phase_event.func = ToEventCallback(&SPCDevice::func_); \
! 22: phase_event.time = time_; \
! 23: phase_event.SetName(name_); \
! 24: scheduler->RestartEvent(phase_event); \
1.1.1.2 root 25: } while (0)
26:
27: // コンストラクタ
1.1.1.3 root 28: SPCDevice::SPCDevice()
1.1.1.14 root 29: : inherited(OBJ_SPC)
1.1 root 30: {
1.1.1.2 root 31: myid = -1;
1.1 root 32:
1.1.1.12 root 33: monitor.func = ToMonitorCallback(&SPCDevice::MonitorUpdate);
1.1.1.10 root 34: // サイズは Init で決まる
35: monitor.Regist(ID_MONITOR_SPC);
1.1 root 36: }
37:
1.1.1.2 root 38: // デストラクタ
1.1 root 39: SPCDevice::~SPCDevice()
40: {
41: }
42:
43: bool
1.1.1.16! root 44: SPCDevice::Create()
1.1 root 45: {
1.1.1.16! root 46: if (inherited::Create() == false) {
! 47: return false;
! 48: }
1.1.1.11 root 49:
1.1.1.16! root 50: scsi.reset(new SCSIHostDevice(this, "spc0"));
! 51: if ((bool)scsi == false) {
! 52: return false;
1.1.1.11 root 53: }
1.1.1.16! root 54: scsi->SetCallback(
! 55: ToBusFreeCallback(&SPCDevice::BusFreeCallback),
! 56: ToSCSIHostCallback(&SPCDevice::SelectionAckCallback),
! 57: ToSCSIHostCallback(&SPCDevice::TransferReqCallback));
1.1.1.11 root 58:
1.1.1.16! root 59: return true;
! 60: }
! 61:
! 62: // 初期化
! 63: bool
! 64: SPCDevice::Init()
! 65: {
1.1.1.12 root 66: if (inherited::Init() == false) {
1.1.1.2 root 67: return false;
68: }
69:
1.1.1.16! root 70: // 実行中に別インスタンスを頻繁に参照しないようにここで vmtype を覚えておく
! 71: vmtype = gMainApp.GetVMType();
! 72:
1.1.1.4 root 73: // 入力クロックは機種によって異なる。
74: // X68k は 5MHz が入っている。
1.1.1.14 root 75: // LUNA-I はおそらく 6.144MHz だと思われる。LUNA-88K は未確認。
1.1.1.4 root 76: const ConfigItem& item = gConfig->Find("!spc-tCLF");
77: t_CLF = item.AsInt();
1.1 root 78:
1.1.1.6 root 79: // アクセスウェイト
1.1.1.13 root 80: if (vmtype == VMType::X68030) {
1.1.1.6 root 81: // InsideOut p.135
1.1.1.15 root 82: read_wait = busdata::Wait(18 * 40_nsec);
83: write_wait = busdata::Wait(19 * 40_nsec);
1.1.1.6 root 84: } else {
85: // 他機種は未調査だけど、判明するまでの間とりあえず
86: // ノーウェイトでない適当な値を入れておく。
1.1.1.15 root 87: read_wait = busdata::Wait(18 * 40_nsec);
88: write_wait = busdata::Wait(19 * 40_nsec);
1.1.1.6 root 89: }
90:
1.1.1.7 root 91: // 割り込み信号線の接続先
1.1.1.13 root 92: if (vmtype == VMType::X68030) {
1.1.1.7 root 93: // X68030 は PEDEC (I/O コントローラ) にカスケード
1.1.1.14 root 94: interrupt = GetPEDECDevice();
1.1.1.7 root 95: } else {
1.1.1.14 root 96: interrupt = GetInterruptDevice();
1.1.1.7 root 97: }
98:
1.1.1.16! root 99: // 汎用イベント
! 100: // 名前とコールバック関数は都度セットする。
! 101: phase_event.SetName("SPC Phase");
! 102: scheduler->RegistEvent(phase_event);
! 103:
! 104: // TC カウンタイベント
! 105: timer_event.code = 0;
! 106: timer_event.SetName("SPC Timer");
! 107: scheduler->RegistEvent(timer_event);
! 108:
! 109: // モニタに表示するアドレス
! 110: if (vmtype == VMType::X68030) {
! 111: baseaddr = 0xe96001;
! 112: regoffset = 2;
! 113: } else {
! 114: baseaddr = 0xe1000000;
! 115: regoffset = 4;
! 116: }
! 117: // 「イニシエータ以外のターゲット数」はここでは確定している。
! 118: monitor.SetSize(80, 15 + scsi->GetOwnedBus()->GetTargetCount() + 1);
1.1.1.6 root 119:
1.1 root 120: return true;
121: }
122:
1.1.1.8 root 123: // デバイスリセット
1.1 root 124: void
1.1.1.12 root 125: SPCDevice::ResetHard(bool poweron)
1.1 root 126: {
1.1.1.13 root 127: putlog(1, "Reset");
1.1 root 128:
129: // 初期値 (p.61)
130: spc.sctl = SPC::SCTL_RESET;
131: spc.ints = 0x00;
132: spc.ssts &= 0x0f;
133: spc.serr = 0x00;
1.1.1.11 root 134: // 変更しないレジスタは次の通り (p.60)
135: // BDID, TMOD, TC[HML]?, SCMD, PCTL, TEMP(out)
1.1 root 136:
137: // XXX マニュアルには書いていないけど、
138: // SDGC bit5 の転送ごとに割り込みを発生させるビットが
139: // OFFになることを NetBSD が期待しているし、
140: // このビットは落ちるのではないだろうか
141: spc.sdgc = 0x00;
1.1.1.2 root 142:
143: // 内部状態
1.1.1.7 root 144: MakeIntsMask();
1.1.1.15 root 145: ChangeInterrupt();
1.1.1.3 root 146: prev_ints = (uint32)-1;
147: prev_ssts = (uint32)-1;
1.1.1.2 root 148: set_atn_in_selection = false;
149: transfer_command_running = false;
1.1.1.16! root 150: scheduler->StopEvent(phase_event);
! 151: scheduler->StopEvent(timer_event);
1.1.1.12 root 152:
153: last_busfree_time = 0;
1.1 root 154: }
155:
1.1.1.15 root 156: busdata
1.1.1.16! root 157: SPCDevice::ReadPort(uint32 offset)
1.1 root 158: {
1.1.1.15 root 159: busdata data;
1.1.1.6 root 160:
1.1.1.8 root 161: switch (offset) {
1.1 root 162: case SPC::BDID:
163: data = 1 << myid;
1.1.1.15 root 164: putlog(3, "BDID -> $%02x", data.Data());
165: break;
1.1 root 166:
167: case SPC::SCTL:
168: data = spc.sctl;
1.1.1.15 root 169: putlog(3, "SCTL -> $%02x", data.Data());
170: break;
1.1 root 171:
172: case SPC::SCMD:
1.1.1.2 root 173: data = spc.scmd;
1.1.1.15 root 174: putlog(3, "SCMD -> $%02x", data.Data());
175: break;
1.1 root 176:
177: case SPC::TMOD:
178: // MB89352 には存在しないレジスタ。
179: // 下位2ビットは %0 らしいので意味はないだろうけど真似ておく。
1.1.1.15 root 180: data = 0xfc;
181: break;
1.1 root 182:
183: case SPC::INTS:
1.1.1.7 root 184: data = GetINTS();
1.1.1.3 root 185: if (loglevel >= 3) {
1.1.1.15 root 186: uint32 new_ints = data.Data();
1.1.1.16! root 187: if (timer_event.IsRunning()) {
1.1 root 188: // イベント中は値が変わった時だけ表示
1.1.1.15 root 189: if (prev_ints != new_ints) {
190: putlogn("INTS -> $%02x", new_ints);
191: prev_ints = new_ints;
1.1 root 192: }
193: } else {
1.1.1.15 root 194: putlogn("INTS -> $%02x", new_ints);
1.1 root 195: prev_ints = -1U;
196: }
197: }
1.1.1.15 root 198: break;
1.1 root 199:
200: case SPC::PSNS:
201: data = GetPSNS();
1.1.1.15 root 202: putlog(3, "PSNS -> $%02x", data.Data());
203: break;
1.1 root 204:
205: case SPC::SSTS:
206: data = GetSSTS();
1.1.1.3 root 207: if (loglevel >= 3) {
1.1.1.15 root 208: uint32 new_ssts = data.Data();
1.1.1.16! root 209: if (timer_event.IsRunning()) {
1.1 root 210: // イベント中は値が変わった時だけ表示
1.1.1.15 root 211: if (prev_ssts != new_ssts) {
212: putlogn("SSTS -> $%02x", new_ssts);
213: prev_ssts = new_ssts;
1.1 root 214: }
215: } else {
1.1.1.15 root 216: putlogn("SSTS -> $%02x", new_ssts);
1.1 root 217: prev_ssts = -1U;
218: }
219: }
1.1.1.15 root 220: break;
1.1 root 221:
222: case SPC::SERR:
1.1.1.7 root 223: data = GetSERR();
1.1.1.15 root 224: putlog(3, "SERR -> $%02x", data.Data());
225: break;
1.1 root 226:
227: case SPC::PCTL:
228: data = GetPCTL();
1.1.1.15 root 229: putlog(3, "PCTL -> $%02x", data.Data());
230: break;
1.1 root 231:
232: case SPC::MBC:
1.1.1.13 root 233: putlog(0, "Read MBC (NOT IMPLEMENTED)");
1.1.1.15 root 234: data = 0x0f;
235: break;
1.1 root 236:
237: case SPC::DREG:
1.1.1.15 root 238: data = ReadDREG();
239: break;
1.1 root 240:
241: case SPC::TEMP:
242: data = spc.temp_in;
1.1.1.15 root 243: putlog(3, "TEMP -> $%02x", data.Data());
244: break;
1.1 root 245:
246: case SPC::TCH:
247: data = spc.tc >> 16;
1.1.1.15 root 248: putlog(3, "TCH -> $%02x", data.Data());
249: break;
1.1 root 250:
251: case SPC::TCM:
252: data = (spc.tc >> 8) & 0xff;
1.1.1.15 root 253: putlog(3, "TCM -> $%02x", data.Data());
254: break;
1.1 root 255:
256: case SPC::TCL:
257: data = spc.tc & 0xff;
1.1.1.15 root 258: putlog(3, "TCL -> $%02x", data.Data());
259: break;
1.1 root 260:
261: case SPC::EXBF:
262: // MB89352 には存在しないレジスタ
1.1.1.15 root 263: data = 0xff;
264: break;
1.1.1.12 root 265:
266: default:
1.1.1.16! root 267: VMPANIC("corrupted offset=%u", offset);
1.1 root 268: }
1.1.1.15 root 269:
270: data |= read_wait;
1.1.1.16! root 271: data |= BusData::Size1;
1.1.1.15 root 272: return data;
1.1 root 273: }
274:
1.1.1.15 root 275: busdata
1.1.1.16! root 276: SPCDevice::WritePort(uint32 offset, uint32 data)
1.1 root 277: {
1.1.1.15 root 278: busdata r;
1.1.1.6 root 279:
1.1.1.8 root 280: switch (offset) {
1.1 root 281: case SPC::BDID:
1.1.1.16! root 282: {
! 283: uint cur_myid = scsi->GetMyID();
1.1.1.2 root 284: putlog(3, "BDID <- $%02x", data);
1.1.1.16! root 285: if (data == cur_myid) {
1.1.1.2 root 286: // 同じなら何もしない
1.1.1.15 root 287: break;
1.1.1.2 root 288: }
1.1.1.16! root 289: if ((int)cur_myid < 0) {
! 290: // 初回なのでバスに参加。myid はこちらでもコピーを覚えておく。
1.1.1.2 root 291: myid = data;
1.1.1.16! root 292: if (scsi->GetOwnedBus()->AttachDevice(scsi.get(), myid) == false) {
! 293: putlog(0, "BDID %u conflicts with existing devices!", myid);
! 294: }
1.1.1.2 root 295: } else {
296: // そうでなければ移動?
1.1.1.16! root 297: putlog(0, "BDID change %u to %u (NOT IMPLEMENTED)", cur_myid, data);
1.1.1.2 root 298: }
1.1.1.15 root 299: break;
1.1.1.16! root 300: }
1.1.1.3 root 301:
1.1 root 302: case SPC::SCTL:
303: WriteSCTL(data);
1.1.1.15 root 304: break;
1.1 root 305:
306: case SPC::SCMD:
307: WriteSCMD(data);
1.1.1.15 root 308: break;
1.1 root 309:
310: case SPC::TMOD:
311: // MB89352 には存在しないレジスタ
1.1.1.15 root 312: break;
1.1 root 313:
314: case SPC::INTS:
315: WriteINTS(data);
1.1.1.15 root 316: break;
1.1 root 317:
318: case SPC::SDGC:
319: spc.sdgc = data;
320: putlog(3, "SDGC <- $%02x", spc.sdgc);
1.1.1.15 root 321: break;
1.1 root 322:
1.1.1.3 root 323: case SPC::SSTS:
324: // XXX 書き込みはどうなる?
1.1.1.15 root 325: break;
1.1.1.3 root 326:
327: case SPC::SERR:
328: // XXX 書き込みはどうなる?
1.1.1.15 root 329: break;
1.1.1.3 root 330:
1.1 root 331: case SPC::PCTL:
1.1.1.11 root 332: WritePCTL(data);
1.1.1.15 root 333: break;
1.1 root 334:
1.1.1.3 root 335: case SPC::MBC:
336: // XXX 書き込みはどうなる?
1.1.1.15 root 337: break;
1.1.1.3 root 338:
1.1 root 339: case SPC::DREG:
1.1.1.2 root 340: putlog(3, "DREG <- $%02x", data);
1.1.1.15 root 341: r = WriteDREG(data);
1.1.1.16! root 342: break;
1.1 root 343:
344: case SPC::TEMP:
345: spc.temp_out = data;
346: putlog(3, "TEMP <- $%02x", spc.temp_out);
347:
1.1.1.2 root 348: // Inside (p479) によると、SPC はセレクションタイムアウト時 SEL を
349: // 下げないので、代わりに TEMP に $00 を書き込むことで SEL を下げる、
350: // そうなのだが、データシート PDF には記述が見当たらないし、
351: // luna68k の /boot はここで SEL が下げてしまうと期待した動作に
352: // ならないようだ。
353: // SPC はセレクションタイムアウトしたからと言って、データバスに出した
1.1.1.6 root 354: // ID を自動的に取り下げたりはしないので、TEMP に $00 を書き込むことで
355: // データバスをクリアする、だったんでは。
1.1.1.16! root 356: if (0 && scsi->GetPhase() == SCSI::Phase::Selection) {
! 357: scsi->NegateSEL();
1.1 root 358: }
359: // Out 方向ならバスに出す
1.1.1.16! root 360: if (scsi->GetIO() == false) {
! 361: scsi->SetData(spc.temp_out);
1.1 root 362: }
1.1.1.15 root 363: break;
1.1 root 364:
365: case SPC::TCH:
366: spc.tc = (spc.tc & 0x00ffff) | (data << 16);
1.1.1.2 root 367: putlog(3, "TCH <- $%02x (TC=$%06x)", data, spc.tc);
1.1.1.15 root 368: break;
1.1 root 369: case SPC::TCM:
370: spc.tc = (spc.tc & 0xff00ff) | (data << 8);
1.1.1.2 root 371: putlog(3, "TCM <- $%02x (TC=$%06x)", data, spc.tc);
1.1.1.15 root 372: break;
1.1 root 373: case SPC::TCL:
374: spc.tc = (spc.tc & 0xffff00) | data;
1.1.1.2 root 375: putlog(3, "TCL <- $%02x (TC=$%06x)", data, spc.tc);
1.1.1.15 root 376: break;
1.1 root 377:
378: case SPC::EXBF:
379: // MB89352 には存在しないレジスタ
1.1.1.15 root 380: break;
1.1.1.12 root 381:
382: default:
1.1.1.16! root 383: VMPANIC("corrupted offset=%u", offset);
1.1 root 384: }
1.1.1.15 root 385:
1.1.1.16! root 386: r |= write_wait;
! 387: r |= BusData::Size1;
! 388: return r;
1.1 root 389: }
390:
1.1.1.15 root 391: busdata
1.1.1.16! root 392: SPCDevice::PeekPort(uint32 offset)
1.1 root 393: {
1.1.1.8 root 394: switch (offset) {
1.1 root 395: case SPC::BDID:
1.1.1.3 root 396: return 1 << myid;
1.1 root 397:
398: case SPC::SCTL:
1.1.1.3 root 399: return spc.sctl;
1.1 root 400:
401: case SPC::SCMD:
1.1.1.3 root 402: return spc.scmd;
1.1 root 403:
404: case SPC::TMOD:
405: // MB89352 には存在しないレジスタ。
406: // 下位2ビットは %0 らしいので意味はないだろうけど真似ておく。
1.1.1.3 root 407: return 0xfc;
1.1 root 408:
409: case SPC::INTS:
1.1.1.7 root 410: return GetINTS();
1.1 root 411:
412: case SPC::PSNS:
1.1.1.3 root 413: return GetPSNS();
1.1 root 414:
415: case SPC::SSTS:
1.1.1.3 root 416: return GetSSTS();
1.1 root 417:
418: case SPC::SERR:
1.1.1.7 root 419: return GetSERR();
1.1 root 420:
421: case SPC::PCTL:
1.1.1.3 root 422: return GetPCTL();
1.1 root 423:
424: case SPC::MBC:
1.1.1.13 root 425: return 0x0f;
1.1 root 426:
427: case SPC::DREG:
1.1.1.3 root 428: // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、
429: // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが
430: // すべてバスエラーになってしまう。
1.1.1.13 root 431: if (vmtype == VMType::X68030) {
1.1.1.3 root 432: if ((spc.scmd & SPC::SCMD_PROGRAM)) {
1.1.1.16! root 433: return BusData::BusErr;
1.1.1.3 root 434: }
435: }
436: return spc.dreg.Peek(0);
1.1 root 437:
438: case SPC::TEMP:
1.1.1.3 root 439: return spc.temp_in;
1.1 root 440:
441: case SPC::TCH:
1.1.1.3 root 442: return spc.tc >> 16;
1.1 root 443:
444: case SPC::TCM:
1.1.1.3 root 445: return (spc.tc >> 8) & 0xff;
1.1 root 446:
447: case SPC::TCL:
1.1.1.3 root 448: return spc.tc & 0xff;
1.1 root 449:
450: case SPC::EXBF:
451: // MB89352 には存在しないレジスタ
1.1.1.3 root 452: return 0xff;
1.1.1.12 root 453:
454: default:
1.1.1.14 root 455: return 0xff;
1.1 root 456: }
457: }
458:
1.1.1.16! root 459: bool
! 460: SPCDevice::PokePort(uint32 offset, uint32 data)
! 461: {
! 462: return false;
! 463: }
! 464:
1.1.1.5 root 465: void
1.1.1.10 root 466: SPCDevice::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 467: {
468: int x;
469: int y;
1.1.1.16! root 470: SCSIBus *bus;
1.1 root 471: uint32 sctl;
472: uint32 scmd;
473: uint32 ints;
474: uint32 psns;
475: uint32 sdgc;
476: uint32 ssts;
477: uint32 serr;
478: uint32 pctl;
479: uint32 tc;
480:
1.1.1.10 root 481: screen.Clear();
1.1 root 482:
1.1.1.16! root 483: bus = scsi->GetOwnedBus();
1.1 root 484: sctl = spc.sctl;
1.1.1.2 root 485: scmd = spc.scmd;
1.1.1.7 root 486: ints = GetINTS();
1.1 root 487: psns = GetPSNS();
488: sdgc = spc.sdgc;
489: ssts = GetSSTS();
1.1.1.7 root 490: serr = GetSERR();
1.1 root 491: pctl = GetPCTL();
492: tc = spc.tc;
493:
1.1.1.16! root 494: #define A(n) (baseaddr + (n) * regoffset)
! 495:
1.1 root 496: y = 0;
1.1.1.16! root 497: screen.Print(0, y++, "$%08x BDID: $%02x (ID=%u)", A(0), (1 << myid), myid);
! 498: screen.Print(0, y++, "$%08x SCTL: $%02x", A(1), sctl);
! 499: screen.Print(0, y++, "$%08x SCMD: $%02x", A(2), scmd);
! 500: screen.Print(0, y++, "$%08x INTS: $%02x", A(4), ints);
! 501: screen.Print(0, y++, "$%08x PSNS: $%02x", A(5), psns);
! 502: screen.Print(0, y++, "$%08x SDGC: $%02x", A(5), sdgc);
! 503: screen.Print(0, y++, "$%08x SSTS: $%02x", A(6), ssts);
! 504: screen.Print(0, y++, "$%08x SERR: $%02x", A(7), serr);
! 505: screen.Print(0, y++, "$%08x PCTL: $%02x", A(8), pctl);
! 506: screen.Print(0, y++, "$%08x MBC : $XX", A(9));
! 507: screen.Print(0, y, "$%08x DREG: ", A(10));
1.1 root 508: {
509: uint i;
510: for (i = 0; i < spc.dreg.Length(); i++) {
1.1.1.16! root 511: screen.Print(16 + i * 4, y, "$%02x", spc.dreg.Peek(i));
1.1 root 512: }
513: for (; i < 8; i++) {
1.1.1.16! root 514: screen.Puts(16 + i * 4, y, "---");
1.1 root 515: }
516: }
517: y++;
1.1.1.16! root 518: screen.Print(0, y++, "$%08x TEMP: $%02x(IN) $%02x(OUT)", A(11),
1.1 root 519: spc.temp_in, spc.temp_out);
1.1.1.16! root 520: screen.Print(0, y++, "$%08x TC : $%06x", A(12), tc);
1.1 root 521:
1.1.1.16! root 522: x = 20;
1.1 root 523: y = 1;
524:
525: // SCTL
526: static const char * const sctl_bits[] = {
527: "RST", "CTRL", "DIAG", "ARB", "PARI", "SEL", "RESL", "INTR"
528: };
1.1.1.10 root 529: MonitorReg(screen, x, y, sctl, sctl_bits);
1.1 root 530: y++;
531:
532: // SCMD
533: static const char * const scmd_bits[] = {
1.1.1.11 root 534: "", "", "", "RST", "INTC", "PROG", "----", "TERM"
1.1 root 535: };
1.1.1.10 root 536: MonitorReg(screen, x, y, scmd, scmd_bits);
1.1 root 537: static const char * const scmd_cmd[] = {
538: //12345678901234
539: "(Bus Release)",
540: "(Select)",
541: "(Reset ATN)",
542: "(Set ATN)",
543: "(Transfer)",
544: "(XferPause)",
545: "(ResetACK/REQ)",
546: "(Set ACK/REQ)",
547: };
1.1.1.10 root 548: screen.Puts(x, y, scmd_cmd[scmd >> 5]);
1.1 root 549: y++;
550:
551: // INTS
552: static const char * const ints_bits[] = {
553: "Sel", "Resl", "Disc", "Cmpl", "SReq", "TOut", "HErr", "Rst"
554: };
1.1.1.10 root 555: MonitorReg(screen, x, y, ints, ints_bits);
1.1 root 556: y++;
557:
558: // PSNS
559: static const char * const psns_bits[] = {
560: "REQ", "ACK", "ATN", "SEL", "BSY", "MSG", "C/D", "I/O"
561: };
1.1.1.10 root 562: MonitorReg(screen, x, y, psns, psns_bits);
1.1 root 563: y++;
564:
565: // SDGC
566: static const char * const sdgc_bits[] = {
1.1.1.11 root 567: "-", "-", "XfEn", "-", "-", "-", "-", "-"
1.1 root 568: };
1.1.1.10 root 569: MonitorReg(screen, x, y, sdgc, sdgc_bits);
1.1 root 570: y++;
571:
572: // SSTS
573: static const char * const ssts_bits[] = {
574: "", "", "SBSY", "XInP", "RSTI", "TCZ", "Full", "Empt"
575: };
1.1.1.10 root 576: MonitorReg(screen, x, y, ssts, ssts_bits);
1.1 root 577: static const char * const ssts_conn[] = {
578: //12345678
579: "(NotConn)",
580: "(As Tgt)",
581: "(As Init)",
582: "(undef?)",
583: };
1.1.1.10 root 584: screen.Puts(x, y, ssts_conn[ssts >> 6]);
1.1 root 585: y++;
586:
587: // SERR
588: static const char * const serr_bits[] = {
1.1.1.11 root 589: "-", "-", "XOut", "-", "PErr", "-", "Shrt", "-"
1.1 root 590: };
1.1.1.10 root 591: MonitorReg(screen, x, y, serr, serr_bits);
1.1 root 592: y++;
593:
594: // PCTL
595: static const char * const pctl_bits[] = {
1.1.1.11 root 596: "BIE", "-", "-", "-", "-", "MSG", "C/D", "I/O"
1.1 root 597: };
1.1.1.10 root 598: MonitorReg(screen, x, y, pctl, pctl_bits);
1.1 root 599: y++;
600:
601: // バス状態
1.1.1.16! root 602: x = 62;
1.1 root 603: y = 0;
1.1.1.16! root 604: SCSI::Phase ph = bus->GetPhase();
! 605: uint8 rst = bus->GetRST();
! 606: uint8 bsy = bus->GetBSY();
! 607: uint8 sel = bus->GetSEL();
! 608: uint8 msg = bus->GetMSG();
! 609: uint8 cd = bus->GetCD();
! 610: uint8 io = bus->GetIO();
! 611: uint8 atn = bus->GetATN();
! 612: uint8 req = bus->GetREQ();
! 613: uint8 ack = bus->GetACK();
! 614: uint8 data = bus->GetData();
! 615:
1.1.1.10 root 616: screen.Print(x, y++, "Phase: %s", SCSI::GetPhaseName(ph));
617: screen.Puts(x, y++, TA::OnOff(rst), "RST");
618: screen.Puts(x, y++, TA::OnOff(bsy), "BSY");
619: screen.Puts(x, y++, TA::OnOff(sel), "SEL");
620: screen.Puts(x, y++, TA::OnOff(msg), "MSG");
621: screen.Puts(x, y++, TA::OnOff(cd), "CD");
622: screen.Puts(x, y++, TA::OnOff(io), "IO");
623: screen.Puts(x, y++, TA::OnOff(atn), "ATN");
624: screen.Puts(x, y++, TA::OnOff(req), "REQ");
625: screen.Puts(x, y++, TA::OnOff(ack), "ACK");
626: screen.Print(x, y++, "DATA:$%02x", data);
1.1.1.2 root 627:
1.1.1.3 root 628: y = 2;
1.1.1.10 root 629: screen.Puts(x + 4, y++, "(________)");
630: screen.Puts(x + 4, y++, "(________)");
631: screen.Puts(x + 4, y++, "+");
632: screen.Puts(x + 4, y++, "|");
633: screen.Puts(x + 4, y++, "+");
1.1.1.3 root 634:
1.1.1.2 root 635: // BSY, SEL の内訳。"(76543210)" の順。
636: y = 2;
1.1 root 637: for (int i = 0; i < 8; i++) {
638: if ((bsy & (1 << i))) {
1.1.1.16! root 639: screen.Print(x + 4 + 8 - i, y, "%u", i);
1.1 root 640: } else {
1.1.1.10 root 641: screen.Print(x + 4 + 8 - i, y, "_");
1.1 root 642: }
643: }
644: y++;
645: for (int i = 0; i < 8; i++) {
646: if ((sel & (1 << i))) {
1.1.1.16! root 647: screen.Print(x + 4 + 8 - i, y, "%u", i);
1.1 root 648: } else {
1.1.1.10 root 649: screen.Print(x + 4 + 8 - i, y, "_");
1.1 root 650: }
651: }
652:
1.1.1.2 root 653: // 転送フェーズ中なら現在の XferPhase
654: y += 2;
1.1.1.3 root 655: if (ph == SCSI::Phase::Transfer) {
1.1.1.16! root 656: screen.Puts(x + 5, y, SCSI::GetXferPhaseName(scsi->GetXfer()));
1.1.1.2 root 657: }
658:
1.1.1.6 root 659: // デバイス一覧
660: // 0 1
661: // 01234567890123456
662: // ID Device Command
663: // 0: HD(1024MB) $06 Read
664: // 7: Initiator
1.1 root 665: x = 0;
666: y = 14;
1.1.1.10 root 667: screen.Print(x, y++, "ID Device Command");
1.1.1.16! root 668: for (const auto d : bus->GetConnectedDevices()) {
1.1.1.11 root 669: std::string desc = SCSI::GetDevTypeName(d->GetDevType());
1.1.1.6 root 670:
1.1.1.16! root 671: const SCSIDisk *disk = dynamic_cast<const SCSIDisk*>(d);
1.1.1.11 root 672: if (disk && disk->IsMediumLoaded()) {
1.1.1.16! root 673: desc += string_format("(%uMB)",
! 674: (uint)(disk->GetSize() / 1024 / 1024));
1.1.1.6 root 675: }
676:
677: // 本体 ID は BDID が設定される前でもエミュレータ的に知ってはいるが、
678: // BDID への書き込みがされなかったりおかしかったりしたら (上の BDID
679: // 欄でも分かるけど) ここも反映されるようにしておく。
680: char idc;
1.1.1.16! root 681: if ((int)d->GetMyID() >= 0) {
1.1.1.6 root 682: idc = '0' + d->GetMyID();
683: } else {
684: idc = '-';
685: }
1.1.1.10 root 686: screen.Print(x, y, "%c: %s", idc, desc.c_str());
1.1.1.6 root 687: y++;
688: }
689:
690: // 実行中のコマンド
691: x = 14;
692: y = 15;
1.1.1.16! root 693: SCSITarget *t = bus->GetSelectedTarget();
! 694: if (t) {
! 695: std::vector<uint8> cmds = t->GetCmdSeq();
! 696: if (cmds.size() > 0) {
! 697: // この ID のデバイス行に表示したい
! 698: // XXX もうちょっとマシにしたい
! 699: const auto connected_devices = bus->GetConnectedDevices();
! 700: int i = 0;
! 701: for (; i < connected_devices.size(); i++) {
! 702: if (t == connected_devices[i]) {
! 703: break;
1.1.1.3 root 704: }
1.1.1.2 root 705: }
1.1.1.16! root 706: const char *cmdname = SCSI::GetCommandName(cmds[0]);
! 707: screen.Print(x, y + i, "$%02x %s", cmds[0], cmdname ?: "");
1.1 root 708: }
709: }
710: }
711:
712: // レジスタをビットごとに表示する。MonitorUpdate の下請け。
713: void
1.1.1.10 root 714: SPCDevice::MonitorReg(TextScreen& screen,
1.1.1.5 root 715: int x, int y, uint32 reg, const char * const *names)
1.1 root 716: {
717: for (int i = 0; i < 8; i++) {
1.1.1.11 root 718: if (names[i][0] == '-') {
719: screen.Puts(x + i * 5, y, TA::Disable, "----");
720: } else {
721: bool b = reg & (1 << (7 - i));
722: screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]);
723: }
1.1 root 724: }
725: }
726:
727: // SCTL レジスタへの書き込み
728: void
729: SPCDevice::WriteSCTL(uint32 data)
730: {
731: // リセットの立ち上がり検出が必要
732: uint32 up = ~spc.sctl & data;
733:
734: spc.sctl = data;
735: putlog(3, "SCTL <- $%02x", spc.sctl);
736: if ((up & SPC::SCTL_RESET)) {
737: // リセット
1.1.1.12 root 738: ResetHard(false);
1.1 root 739: } else {
740: // 通常動作
741: }
742: if ((spc.sctl & SPC::SCTL_CTL_RESET)) {
743: putlog(2, "Control Reset");
744: }
745: if ((spc.sctl & SPC::SCTL_DIAG_MODE)) {
1.1.1.13 root 746: putlog(0, "SCTL Diag Mode (NOT IMPLEMENTED)");
1.1 root 747: }
1.1.1.7 root 748:
749: // 割り込みマスクを更新して信号線の状態を変える
750: MakeIntsMask();
751: ChangeInterrupt();
752: }
753:
754: // SCTL_INTR_EN の状態から内部変数 ints_mask を作成する。
755: void
756: SPCDevice::MakeIntsMask()
757: {
758: if ((spc.sctl & SPC::SCTL_INTR_EN)) {
759: spc.ints_mask = SPC::INTS_ENABLE_MASK;
760: } else {
761: spc.ints_mask = SPC::INTS_DISABLE_MASK;
762: }
1.1 root 763: }
764:
765: // SCMD レジスタへの書き込み
766: void
767: SPCDevice::WriteSCMD(uint32 data)
768: {
1.1.1.2 root 769: uint32 chg = spc.scmd ^ data; // 変更点
1.1 root 770: spc.scmd = data;
771: putlog(3, "SCMD <- $%02x", spc.scmd);
772:
1.1.1.7 root 773: // Program, Terminate Mode の未実装判定は転送開始時のみ表示する。
1.1 root 774:
1.1.1.2 root 775: // RST Out 操作は SCTL Reset がオフの時だけ有効
776: // と書いてあるけど、SCTL Reset がオンならほぼ全機能無効なのでは?
777: if ((spc.sctl & SPC::SCTL_RESET) == 0 && (chg & SPC::SCMD_RST_OUT)) {
778: if ((spc.scmd & SPC::SCMD_RST_OUT)) {
779: // 0 -> 1
780: // すでに RST が立ってたらどうするか
1.1.1.16! root 781: if (scsi->GetRST() == false) {
1.1.1.2 root 782: putlog(1, "SCMD RST Out");
783: BusRelease();
1.1.1.16! root 784: scsi->AssertRST();
1.1.1.2 root 785: SetINTS(SPC::INTS_RESET_COND);
786: }
787: } else {
788: // 1 -> 0
789: // 特にすることはない?
1.1.1.16! root 790: scsi->NegateRST();
1.1 root 791: }
792: }
793:
794: switch (spc.scmd >> 5) {
795: case 0:
1.1.1.13 root 796: putlog(2, "SCMD Bus Release command");
1.1.1.7 root 797: if ((spc.scmd & SPC::SCMD_INTERCEPT)) {
798: // BusRelease コマンドの発行と共に Intercept ビットが立っていたら
799: // Service Required 割り込みをリセットする?
1.1.1.13 root 800: putlog(0, "SCMD Intercept with BusRelease (NOT IMPLEMENTED)");
1.1.1.7 root 801: }
1.1 root 802: BusFree();
803: break;
804: case 1:
805: SelectCommand();
806: break;
807: case 2:
1.1.1.13 root 808: putlog(1, "SCMD Reset ATN command (NOT IMPLEMENTED)");
1.1 root 809: break;
810: case 3:
1.1.1.2 root 811: SetATNCommand();
1.1 root 812: break;
813: case 4:
814: TransferCommand();
815: break;
816: case 5:
1.1.1.13 root 817: putlog(2, "SCMD Transfer Pause command (NOT IMPLEMENTED)");
1.1 root 818: break;
819: case 6:
1.1.1.13 root 820: putlog(2, "SCMD Reset ACK/REQ command");
1.1 root 821: // イニシエータなら ACK を、ターゲットなら REQ をリセットする。
822: // ターゲットモードはサポートしないので、操作するのは ACK のみ。
1.1.1.16! root 823: scsi->NegateACK();
1.1 root 824: break;
825: case 7:
1.1.1.13 root 826: putlog(2, "SCMD Set ACK/REQ command");
1.1 root 827: // イニシエータなら ACK を、ターゲットなら REQ をセットする。
828: // ターゲットモードはサポートしないので、操作するのは ACK のみ。
1.1.1.7 root 829: if ((spc.scmd & SPC::SCMD_INTERCEPT)) {
1.1.1.8 root 830: // Intercept なら DREG を維持したまま (たぶん TEMP で) バスを
831: // 直接アクセスするようだが、この DREG 維持あたりは未対応。
832: // これが影響するのは DREG にデータが滞留してる時だけのはず
833: // なので、その時だけログを出す。(X68030 IPLROM、NetBSD/x68k)
834: if (spc.dreg.Length() != 0) {
1.1.1.13 root 835: putlog(0, "SCMD Intercept with SetACK (NOT IMPLEMENTED)");
1.1.1.8 root 836: }
1.1.1.7 root 837: }
1.1.1.16! root 838: scsi->AssertACK();
1.1 root 839: break;
840: }
841: }
842:
843: // INTS レジスタへの書き込み
844: void
845: SPCDevice::WriteINTS(uint32 data)
846: {
847: uint32 ack;
848:
849: // クリアすることになるビットを覚えておく
850: ack = spc.ints & data;
851:
852: // '1' が書き込まれたビットをクリア
853: spc.ints &= ~data;
1.1.1.7 root 854: putlog(3, "INTS <- $%02x (INTS = $%02x)", data, GetINTS());
1.1 root 855:
856: // DISCONNECTED ビットが立っているときは SPC は SCSI から
857: // セレクション、リセレクション要求が来ても応答しない。
858: // DISCONNECTED ビットが下ろされると、応答するようになる。
859:
860: // TIMEOUT ビットをクリアした時が
861: if ((ack & SPC::INTS_TIMEOUT)) {
862: // セレクションフェーズなら
1.1.1.16! root 863: if (scsi->GetPhase() == SCSI::Phase::Selection) {
1.1 root 864: // TC がゼロならバスフリー
865: // そうでなければリスタート
866: if (spc.tc == 0) {
867: BusFree();
868: } else {
1.1.1.16! root 869: timer_event.func =
! 870: ToEventCallback(&SPCDevice::SelectionTimeout);
! 871: timer_event.time = spc.tc * t_CLF * 2;
! 872: scheduler->RestartEvent(timer_event);
1.1 root 873: }
874: }
875: }
1.1.1.7 root 876:
877: // 割り込み信号線の状態を変える
878: ChangeInterrupt();
1.1 root 879: }
880:
1.1.1.2 root 881: // INTS 設定。
1.1.1.7 root 882: // 引数 data は割り込み要因(ここは内部要因なので INTS_XFER_OUT も指定可能)。
1.1.1.2 root 883: // INTS レジスタへの書き込み(割り込み要因のクリア)は WriteINTS() のほう。
1.1 root 884: void
885: SPCDevice::SetINTS(uint32 data)
886: {
887: // 指定された要因を立てる
888: spc.ints |= data;
889:
1.1.1.2 root 890: // Disconnected 割り込みが起きたら ATN を下げる
1.1.1.16! root 891: if ((spc.ints & SPC::INTS_DISCONNECTED) && scsi->GetATN()) {
! 892: scsi->NegateATN();
1.1.1.2 root 893: }
894:
1.1.1.7 root 895: ChangeInterrupt();
896: }
897:
898: // INTS レジスタの値を取得
899: uint32
900: SPCDevice::GetINTS() const
901: {
902: // ints の上位には別の割り込み状態が保持されている。
903: // 下位8bit は INTS レジスタのまま。
904: return spc.ints & 0xff;
905: }
906:
907: // 割り込み信号線の状態を変える
908: void
909: SPCDevice::ChangeInterrupt()
910: {
911: // 割り込み状態は ints に集約してあり、
912: // ints_mask は現在の SCTL_INTR_EN 状態に応じたマスクが用意されているので
913: // 単純に AND するだけでよい。
914: if ((spc.ints & spc.ints_mask) != 0) {
1.1.1.13 root 915: putlog(3, "Interrupt (ints=$%02x)", spc.ints);
1.1.1.7 root 916: interrupt->AssertINT(this);
917: } else {
1.1.1.13 root 918: putlog(4, "Negate Interrupt");
1.1.1.7 root 919: interrupt->NegateINT(this);
1.1 root 920: }
921: }
922:
923: // PSNS レジスタ値の取得。
1.1.1.16! root 924: // ReadPort() と PeekPort() から呼ばれる。
1.1.1.2 root 925: // scsibus 接続前にもモニタの更新などで呼ばれることに注意。
1.1 root 926: uint32
927: SPCDevice::GetPSNS() const
928: {
1.1.1.2 root 929: uint32 data = 0;
930:
1.1.1.16! root 931: if (__predict_false(scsi->GetBus() == NULL)) {
1.1.1.2 root 932: return 0;
933: }
1.1 root 934:
1.1.1.16! root 935: if (scsi->GetREQ()) {
1.1 root 936: data |= SPC::PSNS_REQ;
937: }
1.1.1.16! root 938: if (scsi->GetACK()) {
1.1 root 939: data |= SPC::PSNS_ACK;
940: }
1.1.1.16! root 941: if (scsi->GetATN()) {
1.1 root 942: data |= SPC::PSNS_ATN;
943: }
1.1.1.16! root 944: if (scsi->GetSEL()) {
1.1 root 945: data |= SPC::PSNS_SEL;
946: }
1.1.1.16! root 947: if (scsi->GetBSY()) {
1.1 root 948: data |= SPC::PSNS_BSY;
949: }
1.1.1.16! root 950: data |= (uint32)scsi->GetXfer();
1.1 root 951:
952: return data;
953: }
954:
955: // SSTS レジスタ値の取得。
1.1.1.16! root 956: // ReadPort() と PeekPort() から呼ばれる。
1.1.1.2 root 957: // scsibus 接続前にもモニタの更新などで呼ばれることに注意。
1.1 root 958: uint32
959: SPCDevice::GetSSTS() const
960: {
961: uint32 data;
962:
963: data = spc.ssts & 0xf0;
1.1.1.16! root 964: if (__predict_true(scsi->GetBus() != NULL) && scsi->GetRST()) { // RST 信号
1.1 root 965: data |= SPC::SSTS_RST_IN;
966: }
967: if (spc.tc == 0) { // TC がゼロか
968: data |= SPC::SSTS_TC_ZERO;
969: }
1.1.1.9 root 970: if (spc.dreg.IsFull()) {
1.1 root 971: data |= SPC::SSTS_DREG_FULL;
972: }
1.1.1.9 root 973: if (spc.dreg.Empty()) {
1.1 root 974: data |= SPC::SSTS_DREG_EMPTY;
975: }
976:
977: return data;
978: }
979:
1.1.1.7 root 980: // SERR レジスタ値の取得。
981: uint32
982: SPCDevice::GetSERR() const
983: {
984: // SERR レジスタのうち XFER_OUT ビットだけ ints で保持している。
985: uint32 xfer_out = (spc.ints & SPC::INTS_XFER_OUT) >> 3;
986: return spc.serr | xfer_out;
987: }
988:
1.1.1.11 root 989: // PCTL レジスタへの書き込み。
990: void
991: SPCDevice::WritePCTL(uint32 data)
992: {
993: data &= SPC::PCTL_MASK;
994: putlog(3, "PCTL <- $%02x", data);
995:
996: spc.busfree_intr_enable = (data & SPC::PCTL_BFINT_EN);
997: spc.pctl_out = data & (SPC::PCTL_MSG | SPC::PCTL_CD | SPC::PCTL_IO);
998: // この時点では pctl_out はまだバスに出さない。
999: // バスに出すのは転送開始時のはず。
1000: }
1001:
1.1 root 1002: // PCTL レジスタ値の取得。
1003: uint32
1004: SPCDevice::GetPCTL() const
1005: {
1.1.1.11 root 1006: return spc.busfree_intr_enable | spc.pctl_out;
1.1 root 1007: }
1008:
1.1.1.3 root 1009: // DREG レジスタの読み込み
1010: // (副作用があるので Peek 系からは呼ばないこと)
1.1.1.15 root 1011: busdata
1.1.1.3 root 1012: SPCDevice::ReadDREG()
1013: {
1.1.1.15 root 1014: busdata data;
1.1.1.3 root 1015:
1016: // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、
1017: // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが
1018: // すべてバスエラーになってしまう。
1.1.1.13 root 1019: if (vmtype == VMType::X68030) {
1.1.1.3 root 1020: if ((spc.scmd & SPC::SCMD_PROGRAM)) {
1.1.1.15 root 1021: data.SetBusErr();
1022: return data;
1.1.1.12 root 1023: } else {
1024: // DMA 転送で DREQ が出てないとき DTACK を出さないのを
1.1.1.15 root 1025: // Retry を返すことで表現している。
1.1.1.12 root 1026: if (spc.dreg.Length() == 0) {
1.1.1.13 root 1027: putlog(3, "DREG empty during DMA transferring");
1.1.1.15 root 1028: data.SetRetry();
1029: return data;
1.1.1.12 root 1030: }
1.1.1.3 root 1031: }
1032: }
1033:
1034: // XFER_OUT は DREG アクセスでリセットされる
1.1.1.7 root 1035: spc.ints &= ~SPC::INTS_XFER_OUT;
1.1.1.12 root 1036: // TransferComplete() が割り込み状態を変化させる可能性があるので
1037: // ChangeInterrupt() を最後に呼び出す。
1.1.1.3 root 1038:
1039: // 受信データがあれば取り出す
1040: if (__predict_true(spc.dreg.Length() > 0)) {
1041: bool q_was_full = spc.dreg.IsFull();
1042: data = spc.dreg.Dequeue();
1043: if (loglevel >= 4) {
1.1.1.16! root 1044: putlogn("DREG -> $%02x (q=%u TC=0x%x)",
1.1.1.15 root 1045: data.Data(), spc.dreg.Length(), spc.tc);
1.1.1.3 root 1046: } else {
1.1.1.15 root 1047: putlog(3, "DREG -> $%02x", data.Data());
1.1.1.3 root 1048: }
1049: // この読み出しが最後だったらこれで転送完了。
1050: if (spc.tc == 0 && spc.dreg.Length() == 0) {
1051: TransferComplete();
1052: }
1053: // この読み出しによってバッファに空きが出来たら転送再開。
1.1.1.16! root 1054: if (transfer_command_running && scsi->GetREQ() && q_was_full) {
1.1.1.3 root 1055: // XXX 時間は適当
1056: CallAfter(HardwareTransfer, "SPC ReadDREG to Transfer", t_CLF);
1057: }
1058: } else {
1059: // 受信データがない場合
1060: if ((spc.scmd & SPC::SCMD_PROGRAM)) {
1061: // XXX プログラム転送中ならどうなる?
1062: data = 0xff;
1.1.1.15 root 1063: putlog(3, "DREG -> $%02x (empty)", data.Data());
1.1.1.3 root 1064: } else {
1065: // DMA 転送
1.1.1.12 root 1066: // DMA 側でリトライするのでここには来ないはず
1067: VMPANIC("DMA転送中の DREG empty");
1.1.1.3 root 1068: }
1069: }
1.1.1.12 root 1070:
1071: // 割り込み状態を確定
1072: ChangeInterrupt();
1.1.1.3 root 1073: return data;
1074: }
1075:
1076: // DREG レジスタへの書き込み
1.1.1.15 root 1077: busdata
1.1.1.3 root 1078: SPCDevice::WriteDREG(uint32 data)
1079: {
1.1.1.15 root 1080: busdata r;
1081:
1.1.1.3 root 1082: // X68k は SPC の DREQ 信号から DTACK 信号を作っているため、
1083: // DREQ 信号を出さないプログラム転送モードでは DREG へのアクセスが
1084: // すべてバスエラーになってしまう。
1.1.1.13 root 1085: if (vmtype == VMType::X68030) {
1.1.1.3 root 1086: if ((spc.scmd & SPC::SCMD_PROGRAM)) {
1.1.1.15 root 1087: r.SetBusErr();
1088: return data;
1.1.1.12 root 1089: } else {
1090: // DMA 転送で DREQ が出てないとき DTACK を出さないのを
1.1.1.15 root 1091: // Retry を返すことで表現している。
1.1.1.12 root 1092: if (spc.dreg.IsFull()) {
1.1.1.15 root 1093: r.SetRetry();
1094: return r;
1.1.1.12 root 1095: }
1.1.1.3 root 1096: }
1097: }
1098:
1099: // XFER_OUT は DREG アクセスでリセットされる
1.1.1.7 root 1100: spc.ints &= ~SPC::INTS_XFER_OUT;
1101: ChangeInterrupt();
1.1.1.3 root 1102:
1103: // DREG キューに入れて..
1.1.1.12 root 1104: if (spc.dreg.Enqueue(data) == false) {
1.1.1.14 root 1105: // XXX キューが一杯だったらどうなるのかは書いてないので分からない。
1106: // a) キューを押し出して先頭を破棄する
1107: // b) キューの末尾を破棄して新着データで上書きする
1108: // c) キューの内容は変わらず新着データを破棄する
1109: // どれで実装しても「この時何かしら失われるデータがある」こと自体は
1110: // 再現出来ているはずなので、ここではひとまず a) の動作にしておく。
1111: putlog(0, "DREG Queue overflow");
1112: spc.dreg.EnqueueForce(data);
1.1.1.12 root 1113: }
1.1.1.3 root 1114: // ハードウェア転送中で REQ が来てれば送信。
1.1.1.16! root 1115: if (transfer_command_running && scsi->GetREQ()) {
1.1.1.3 root 1116: // XXX 時間は適当
1117: CallAfter(HardwareTransfer, "SPC WriteDREG to Transfer", t_CLF);
1118: }
1.1.1.15 root 1119: return r;
1.1.1.3 root 1120: }
1121:
1.1 root 1122: // バスフリーフェーズに移行する
1123: void
1124: SPCDevice::BusFree()
1125: {
1.1.1.2 root 1126: putlog(3, "BusFree");
1127:
1128: // ここは BDID 書き込み前 (まだ scsibus に自身をつなげる前) にも呼ばれる。
1129: // その場合は何もしない。バスフリーのはずでもあるし。
1.1.1.16! root 1130: if (scsi->GetBus() == NULL) {
1.1.1.2 root 1131: return;
1132: }
1.1 root 1133:
1.1.1.2 root 1134: BusRelease();
1.1 root 1135: }
1136:
1.1.1.2 root 1137: // バスをリリースする。バスフリーとバスリセットから呼ばれる
1.1 root 1138: void
1139: SPCDevice::BusRelease()
1140: {
1.1.1.2 root 1141: // 自発的にバスをリリースする場合、今のフェーズから自分が変えていい
1142: // BSY、SEL だけを落とす。それ以外の信号線は正しくはないけどとりあえず
1143: // 一括して SCSIHostDevice::BusFreeCB() が落とすことにしてある。
1144:
1.1.1.16! root 1145: switch (scsi->GetPhase()) {
1.1.1.2 root 1146: case SCSI::Phase::BusFree:
1147: case SCSI::Phase::Transfer:
1148: break;
1149: case SCSI::Phase::Arbitration:
1150: case SCSI::Phase::Selection:
1151: case SCSI::Phase::Reselection:
1.1.1.16! root 1152: scsi->NegateSEL();
! 1153: scsi->NegateBSY();
1.1.1.2 root 1154: break;
1155: }
1.1 root 1156: }
1157:
1158: // Select コマンド実行
1159: void
1160: SPCDevice::SelectCommand()
1161: {
1162: std::string idstr;
1163: uint32 data;
1.1.1.2 root 1164: uint32 tgtid;
1.1 root 1165: uint64 wait;
1166:
1.1.1.2 root 1167: // ターゲット ID を表示用に解析
1.1 root 1168: data = spc.temp_out;
1169: if (data == 0) {
1170: // ID が一切立ってない?
1171: idstr = "TEMP==0; No IDs?";
1172: } else if ((data & (1 << myid)) == 0) {
1173: // 自 ID が立ってない?
1174: idstr = string_format("TEMP=$%02x; No BDID?", data);
1175: } else {
1176: // ここで少なくとも自 ID は立っている
1177: data &= ~(1 << myid);
1178:
1179: if (data == 0) {
1180: // ターゲット ID がない
1.1.1.16! root 1181: idstr = string_format("#%u", myid);
1.1 root 1182: } else {
1.1.1.16! root 1183: tgtid = SCSIBus::DecodeID(data);
! 1184: idstr = string_format("#%u -> #%u", myid, tgtid);
1.1 root 1185: }
1186: }
1.1.1.13 root 1187: putlog(2, "Select command (%s)", idstr.c_str());
1.1 root 1188:
1189: // アービトレーションありの場合
1190: //
1191: // BSY X\______/~~~~~~~~~~~~~~~~~~~~~~~~~~\_ .. _/^^^^^^^^^^^^^^^
1192: // |t_BFBL| | |t_BIDH|
1193: // DB* ________<XXXXXXXXXXXXXXXXXXXXXXXXXXXX .. XXXXXXXX>________
1194: // |t_ARB|t_AWSL| | |t_BLSH|
1195: // SEL _____________________/~~~~~~~~~~~~~~~ .. ~~~~~~~~\________
1196: // |t_SIDA|t_IDBH| |
1197: // ATN ____________________________/~~~~~~~~ .. ~~~~~~~~~~~~~~~~~
1198: // |t_SHIR|
1199: // INTR _____________________________________ .. _______________/~
1200: //
1201: // t_BFBL = <Min> (6 + TCL) * t_CLF, <Max> (7 + TCL) * t_CLF + 60
1202: // t_ARB = <Min> 32 * t_CLF - 60
1203: // t_AWSL = <Min> 0, <Max> 80
1204: // t_SIDA = <Min> 11 * t_CLF - 30
1205: // t_IDBH = <Min> 2 * t_CLF - 80
1.1.1.2 root 1206: //
1207: // ここではバスフリー検出から少なくとも t_BFBL 以上 + t_ARB + t_AWSL 後に
1208: // SEL と DB* と ATN を立てて、そこから t_SIDA + t_IDBH 後に BSY を落とす。
1209: //
1210: // アービトレーションなしの場合
1211: //
1212: // BSY X\_______________ .. _/~~~~~~~~~~~~~~~
1213: // |t_FRID| |t_BIDH|
1214: // DB* ________<XXXXXXXX .. XXXXXXXX><XXXXXXX
1215: // |t_IDSL| |t_BLSH|
1216: // SEL _______________/~ .. ~~~~~~~~\________
1217: // |t_SHIR|
1218: // INTR _________________ .. _______________/~
1219: //
1220: // t_FRID = <Min> (6 + TCL) * t_CLF, <Max> (7 + TCL) * t_CLF + 140
1221: // t_IDSL = <Min> 11 * t_CLF - 80
1222: // t_BIDH = <Min> t_BLSH = 2 * t_CLF
1223: // t_SHIR = <Max> 60
1224: //
1.1 root 1225:
1226: // t_FRID/t_BFBL はたぶん今からではなくバスフリーになった時点から起算。
1227: // t_FRID と t_BFBL は同じ式なのでここでは t_BFBL で統一。
1228: uint64 t_BFBL = (6 + spc.GetTCL()) * t_CLF; // Min
1.1.1.14 root 1229: uint64 now = scheduler->GetVirtTime();
1.1.1.2 root 1230: if ((last_busfree_time + t_BFBL) > now) {
1231: wait = (last_busfree_time + t_BFBL) - now;
1.1 root 1232: } else {
1233: wait = 0;
1234: }
1235:
1236: spc.ssts &= ~(SPC::SSTS_CONN_INIT | SPC::SSTS_CONN_TARG);
1237: spc.ssts |= SPC::SSTS_SPC_BUSY;
1238:
1.1.1.2 root 1239: // TCH,TCM によるタイマーを始動。
1240: // N が 0 なら無限大なのでタイマーは動かさない。
1241: uint32 N = (spc.tc >> 8) << 8;
1242: if (N != 0) {
1.1.1.16! root 1243: timer_event.func = ToEventCallback(&SPCDevice::SelectionTimeout);
! 1244: timer_event.time = (N + 15) * t_CLF * 2;
! 1245: scheduler->RestartEvent(timer_event);
1.1.1.2 root 1246: }
1247:
1.1 root 1248: if ((spc.sctl & SPC::SCTL_ARBIT_EN)) {
1249: // アービトレーションありの場合
1250:
1.1.1.2 root 1251: // ここでは複数のデバイスが同時にアービトレーションフェーズに
1252: // 入ることはないため、アービトレーションを開始したデバイスが
1253: // 必ずバスの使用権を得る。
1.1 root 1254:
1.1.1.2 root 1255: // バスフリーから規定時間後に SEL (と DB* と ATN) を上げる。
1.1 root 1256: uint64 t_ARB = 32 * t_CLF - 60;
1257: uint64 t_AWSL = 40; // 適当に中間
1.1.1.2 root 1258: CallAfter(Arbitration1, "SPC Arbitration", wait + t_ARB + t_AWSL);
1.1 root 1259: } else {
1.1.1.2 root 1260: // アービトレーションなしの場合
1261:
1262: // バスフリーから規定時間後に SEL (と DB*) を上げる。
1.1 root 1263: uint64 t_IDSL = 11 * t_CLF - 80;
1.1.1.2 root 1264: CallAfter(Selection1, "SPC Selection", wait + t_IDSL);
1.1 root 1265: }
1.1.1.2 root 1266: }
1.1 root 1267:
1.1.1.2 root 1268: // アービトレーションフェーズ開始後、規定時間経過したので SEL を立てるところ。
1269: void
1.1.1.6 root 1270: SPCDevice::Arbitration1(Event& ev)
1.1.1.2 root 1271: {
1272: putlog(3, "Arbitration1");
1.1 root 1273:
1.1.1.2 root 1274: spc.ssts |= SPC::SSTS_SPC_BUSY;
1.1 root 1275:
1.1.1.2 root 1276: // 本当は DB* -> SEL -> ATN の順でタイミングも規定されてるけど省略。
1277: // ここでは SEL がトリガーになるので SEL のアサートは最後に行う。
1.1.1.11 root 1278: // アービトレーションありなら、セレクション/リセレクションフェーズを
1279: // 区別するため PCTL に設定された IO をバスに出す(p.27)。
1280: // MSG と CD は記述がないけどたぶん一緒に出すだろう。
1281: // タイミングもどこでか分からんのでとりあえずここで。
1.1.1.16! root 1282: scsi->SetData(spc.temp_out);
! 1283: scsi->SetXfer(spc.pctl_out);
1.1.1.2 root 1284: if (set_atn_in_selection) {
1.1.1.16! root 1285: scsi->AssertATN();
1.1.1.2 root 1286: set_atn_in_selection = false;
1287: }
1.1.1.16! root 1288: scsi->AssertSEL();
1.1.1.2 root 1289:
1290: uint64 t_SIDA = 11 * t_CLF - 30;
1291: uint64 t_IDBH = 2 * t_CLF - 80;
1292: CallAfter(Arbitration2, "SPC Arbitration2", t_SIDA + t_IDBH);
1293: }
1.1 root 1294:
1.1.1.2 root 1295: // アービトレーションフェーズ中、SEL を立ててから規定時間経過したので BSY を
1296: // 下げるところ。
1297: void
1.1.1.6 root 1298: SPCDevice::Arbitration2(Event& ev)
1.1.1.2 root 1299: {
1300: putlog(3, "Arbitration2");
1.1 root 1301:
1.1.1.16! root 1302: scsi->NegateBSY();
1.1 root 1303: }
1304:
1.1.1.2 root 1305: // アービトレーションフェーズを使わない場合のセレクションフェーズで、
1306: // 規定時間経過したので SEL を立てるところ。
1.1 root 1307: void
1.1.1.6 root 1308: SPCDevice::Selection1(Event& ev)
1.1 root 1309: {
1.1.1.2 root 1310: putlog(3, "Selection1");
1.1 root 1311:
1.1.1.2 root 1312: spc.ssts |= SPC::SSTS_SPC_BUSY;
1.1 root 1313:
1.1.1.2 root 1314: // 本当は DB* -> SEL の順でタイミングも規定されてるけど省略。
1315: // ここでは SEL がトリガーになるので SEL のアサートは最後に行う。
1.1.1.11 root 1316: // アービトレーションなしなら PCTL に関係なくセレクションフェーズ (IO=0)
1317: // を開始する(p.27)。MSG と CD は記述がないけどたぶん一緒に出すだろう。
1.1.1.16! root 1318: scsi->SetData(spc.temp_out);
! 1319: scsi->SetXfer(0);
! 1320: scsi->AssertSEL();
1.1.1.2 root 1321: }
1.1 root 1322:
1.1.1.2 root 1323: // セレクションコマンドがタイムアウトした時に呼ばれるコールバック。
1324: void
1.1.1.6 root 1325: SPCDevice::SelectionTimeout(Event& ev)
1.1.1.2 root 1326: {
1327: if (loglevel >= 3) {
1328: int tgtid;
1.1.1.16! root 1329: uint8 data = scsi->GetData();
1.1.1.2 root 1330: data &= ~(1 << myid);
1.1.1.16! root 1331: tgtid = SCSIBus::DecodeID(data);
1.1.1.2 root 1332: if (tgtid == -1) {
1.1.1.8 root 1333: putlogn("Selection timeout");
1.1.1.2 root 1334: } else {
1.1.1.16! root 1335: putlogn("Selection timeout (#%u)", tgtid);
1.1.1.2 root 1336: }
1337: }
1.1 root 1338:
1.1.1.2 root 1339: spc.ssts &= ~(SPC::SSTS_CONN_INIT | SPC::SSTS_SPC_BUSY);
1.1 root 1340:
1.1.1.2 root 1341: // TC は実際には減算カウンタのようだが、とりあえず辻褄合わせ
1342: spc.tc = 0;
1.1 root 1343:
1.1.1.2 root 1344: SetINTS(SPC::INTS_TIMEOUT);
1345: }
1.1 root 1346:
1.1.1.2 root 1347: // SetATN コマンド実行
1348: void
1349: SPCDevice::SetATNCommand()
1350: {
1351: putlog(2, "SCMD Set ATN Command");
1.1 root 1352:
1.1.1.2 root 1353: // Select コマンド前なら、セレクションフェーズで ATN を立てる指示。
1354: // 接続中(転送フェーズ中?) なら即座に ATN を立てる。
1.1.1.16! root 1355: switch (scsi->GetPhase()) {
1.1.1.2 root 1356: case SCSI::Phase::BusFree:
1357: set_atn_in_selection = true;
1358: break;
1359: case SCSI::Phase::Arbitration:
1360: case SCSI::Phase::Selection:
1361: case SCSI::Phase::Reselection:
1362: // Select Command 実行中なので実質ここで呼ばれることはないはずだが
1363: case SCSI::Phase::Transfer:
1.1.1.16! root 1364: scsi->AssertATN();
1.1 root 1365: break;
1366: }
1367: }
1368:
1.1.1.2 root 1369: // Transfer コマンド実行
1.1 root 1370: void
1.1.1.2 root 1371: SPCDevice::TransferCommand()
1.1 root 1372: {
1.1.1.3 root 1373: putlog(2, "SCMD Transfer Command (xfer=%s len=0x%x)",
1.1.1.11 root 1374: SCSI::GetXferPhaseName(spc.pctl_out), spc.tc);
1.1.1.2 root 1375:
1376: // SCMD レジスタ書き込みのうち Transfer コマンドに関わる未実装機能の
1377: // ログ表示は SCMD レジスタ書き込み時ではなくここで表示。
1378: if ((spc.scmd & SPC::SCMD_TERM_MODE)) {
1.1.1.13 root 1379: putlog(0, "SCMD Padding mode (NOT IMPLEMENTED)");
1.1.1.2 root 1380: }
1381:
1382: transfer_command_running = true;
1383: spc.ssts |= SPC::SSTS_SPC_BUSY | SPC::SSTS_IN_PROGRESS;
1384:
1.1.1.11 root 1385: // PCTL レジスタに設定してあるフェーズをバスに出す。
1386: // XXX どこでかよく分からんけど、とりあえずこの辺で。
1.1.1.16! root 1387: scsi->SetXfer(spc.pctl_out);
1.1.1.11 root 1388:
1.1.1.2 root 1389: // 転送開始前に DREG キューを初期化する。
1390: // MPU 側が DREG を全部読まずにフェーズを中断とかはたぶん出来るので。
1391: // XXX 転送開始前なのかリセットなのか…
1392: spc.dreg.Clear();
1393:
1394: // Transfer コマンド発行時に REQ がアサートされていなければ
1395: // アサートされるまで待つ。この場合、ここでは何もすることはなく、
1.1.1.16! root 1396: // OnTransferReq() が呼ばれてそこから転送フェーズが動き始める。
! 1397: if (scsi->GetREQ()) {
1.1.1.2 root 1398: // コマンドから転送開始までちょっとだけかかる
1.1.1.11 root 1399: CallAfter(HardwareTransfer, "SPC SCMD to Start", 4 * t_CLF);
1.1 root 1400: }
1401: }
1402:
1.1.1.16! root 1403: // バスフリーコールバック (SCSIHostDevice から呼ばれる)。
1.1.1.2 root 1404: // id はこのバスフリーを行ったデバイス (0..7)
1.1 root 1405: void
1.1.1.16! root 1406: SPCDevice::BusFreeCallback(uint id)
1.1 root 1407: {
1.1.1.2 root 1408: putlog(4, "BusFree Callback");
1.1 root 1409:
1.1.1.14 root 1410: last_busfree_time = scheduler->GetVirtTime();
1.1.1.2 root 1411:
1412: transfer_command_running = false;
1413: spc.ssts &= ~(SPC::SSTS_CONN_INIT | SPC::SSTS_CONN_TARG);
1414: spc.ssts &= ~(SPC::SSTS_SPC_BUSY | SPC::SSTS_IN_PROGRESS);
1.1 root 1415:
1416: // マニュアルが分かりにくいんだけど、
1.1.1.2 root 1417: // o イニシエータとして動作時に、SPC が自らバスフリーにした時
1418: // o BusFree INT enable ビットが立っていて、ターゲットがバスフリーに
1419: // した時
1.1 root 1420: // のいずれかで INTS のバスフリー検出ビットを立てる
1.1.1.2 root 1421: if (id != myid || spc.busfree_intr_enable) {
1.1 root 1422: SetINTS(SPC::INTS_DISCONNECTED);
1423: }
1424: }
1425:
1.1.1.16! root 1426: // ターゲットがセレクションに応答したコールバック
! 1427: // (SCSIHostDevice から呼ばれる)
1.1 root 1428: void
1.1.1.16! root 1429: SPCDevice::SelectionAckCallback()
1.1 root 1430: {
1.1.1.16! root 1431: putlog(4, "SelectionAck Callback");
1.1.1.2 root 1432:
1433: // タイムアウトイベントを停止
1.1.1.16! root 1434: scheduler->StopEvent(timer_event);
1.1 root 1435:
1436: spc.ssts &= ~SPC::SSTS_SPC_BUSY;
1.1.1.2 root 1437: spc.ssts |= SPC::SSTS_CONN_INIT;
1438:
1439: // Command Complete 割り込みを起こす
1.1 root 1440: SetINTS(SPC::INTS_COMPLETE);
1441: }
1442:
1.1.1.16! root 1443: // 転送フェーズでターゲットが REQ を立てたコールバック
! 1444: // (SCSIHostDevice から呼ばれる)
1.1 root 1445: void
1.1.1.16! root 1446: SPCDevice::TransferReqCallback()
1.1 root 1447: {
1.1.1.2 root 1448: // REQ が立てば Transfer Command 発行前でも Xfer In Progress は立てる
1449: spc.ssts |= SPC::SSTS_IN_PROGRESS;
1450:
1451: if (transfer_command_running) {
1452: // Transfer Command 実行中なら REQ 待ちだったので
1.1.1.3 root 1453: // ここでハードウェア転送を開始。
1.1.1.16! root 1454: HardwareTransfer(phase_event);
1.1.1.11 root 1455: }
1456:
1457: // データシートでは、マニュアル転送で IN 方向の時データバスの内容を
1458: // TEMP に取り込むと書いてある(p.78)。
1459: // HardwareTransfer によって状態が変更される可能性があるため
1460: // もう一回チェック。
1461: if (transfer_command_running == false) {
1.1.1.16! root 1462: if (scsi->GetIO()) {
! 1463: spc.temp_in = scsi->GetData();
1.1.1.2 root 1464: }
1465: }
1466: }
1467:
1.1.1.3 root 1468: // ハードウェア転送の1バイト分。
1.1.1.2 root 1469: // イベントコールバックとして呼び出すこと。
1470: // REQ がアサートされているのを確認してから呼び出すこと。
1471: // (1バイト送信するとターゲットが再び REQ を上げるなどしてくるので、
1.1.1.16! root 1472: // OnTransferReq() か DREG 書き込みから再びここが呼ばれる)
1.1.1.2 root 1473: void
1.1.1.6 root 1474: SPCDevice::HardwareTransfer(Event& ev)
1.1.1.2 root 1475: {
1476: // SDGC_XFER_ENABLE は転送要求ごとに割り込みを上げる。
1477: // (SPC から見て) 入力操作時は読み込めるデータがある場合、出力操作時は
1478: // キューにバイトを書き込む必要がある場合に、割り込みを上げる。
1479:
1.1.1.11 root 1480: // PCTL のフェーズと SCSI バスのフェーズが一致していれば転送(開始|継続)。
1481: // そうでなければ Service Required 割り込み。
1.1.1.16! root 1482: if (spc.pctl_out != scsi->GetXfer()) {
1.1.1.11 root 1483: transfer_command_running = false;
1484: putlog(3, "Hardware Transfer terminated (phase mismatch)");
1485: SetINTS(SPC::INTS_SERV_REQ);
1486: return;
1487: }
1488:
1.1.1.2 root 1489: // DREG が送受信不可能ならここでは何もせずに帰るだけでよい。
1490: // その後 DREG アクセスによって送受信可能になればまたこれが呼ばれる。
1.1.1.16! root 1491: if (scsi->GetIO()) {
1.1.1.2 root 1492: // Input (ターゲット → イニシエータ)
1.1.1.16! root 1493: bool queued = spc.dreg.Enqueue(scsi->GetData());
1.1.1.2 root 1494: // SDGC_XFER_ENABLE なら読み込めるデータがある場合に割り込み。
1495: if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) {
1496: putlog(3, "SDGC XFER_OUT Interrupt");
1.1.1.7 root 1497: SetINTS(SPC::INTS_XFER_OUT);
1.1.1.2 root 1498: }
1499: if (!queued) {
1.1.1.3 root 1500: putlog(4, "Hardware Transfer (DREG full)");
1.1.1.2 root 1501: return;
1502: }
1503: } else {
1504: // Output (イニシエータ → ターゲット)
1505: if (spc.dreg.Length() != 0) {
1.1.1.16! root 1506: scsi->SetData(spc.dreg.Dequeue());
1.1 root 1507: } else {
1.1.1.3 root 1508: putlog(4, "Hardware Transfer (DREG empty)");
1.1.1.2 root 1509: // SDGC_XFER_ENABLE ならキューに書き込む必要がある場合に割り込み。
1510: if ((spc.sdgc & SPC::SDGC_XFER_ENABLE)) {
1511: putlog(3, "SDGC XFER_OUT Interrupt");
1.1.1.7 root 1512: SetINTS(SPC::INTS_XFER_OUT);
1.1.1.2 root 1513: }
1514: return;
1.1 root 1515: }
1516: }
1.1.1.2 root 1517:
1.1.1.16! root 1518: scsi->AssertACK();
1.1.1.2 root 1519:
1520: // 転送完了
1521: spc.tc--;
1.1.1.16! root 1522: putlog(4, "Hardware Transfer: %s q=%u tc=%u",
! 1523: (scsi->GetIO() ? "IN" : "OUT"), spc.dreg.Length(), spc.tc);
1.1.1.2 root 1524: if (spc.tc == 0) {
1.1.1.3 root 1525: // どちらでも転送自体はここで完了(?)
1.1.1.2 root 1526: transfer_command_running = false;
1527:
1.1.1.16! root 1528: if (scsi->GetIO()) {
1.1.1.3 root 1529: // Input 時は TC がゼロになって DREG キューを全部読み出した
1530: // ところで完了なので、ここでは何もしない。
1531: } else {
1532: // Output ならここで転送完了。
1533: TransferComplete();
1.1.1.2 root 1534: }
1535: }
1536:
1537: // メッセージアウトフェーズの最終バイト送信中(?)に ATN を下げる
1.1.1.16! root 1538: if (scsi->GetXfer() == SCSI::XferPhase::MsgOut && spc.tc == 0) {
! 1539: scsi->NegateATN();
1.1.1.2 root 1540: }
1541:
1.1.1.16! root 1542: if (scsi->GetXfer() == SCSI::XferPhase::MsgIn && spc.tc == 0) {
1.1.1.2 root 1543: // ただしメッセージインフェーズの最終バイトなら ACK を下げない。
1544: } else {
1.1.1.16! root 1545: scsi->NegateACK();
1.1.1.2 root 1546: }
1.1 root 1547: }
1.1.1.3 root 1548:
1549: // 転送完了。
1550: // HardwareTransfer() と Read() から呼ばれる。
1551: void
1552: SPCDevice::TransferComplete()
1553: {
1554: spc.ssts &= ~(SPC::SSTS_SPC_BUSY | SPC::SSTS_IN_PROGRESS);
1555: SetINTS(SPC::INTS_COMPLETE);
1556: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.