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