Annotation of nono/vm/spc.cpp, revision 1.1.1.15

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.