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