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