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