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