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