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