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