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