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