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