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