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