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

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

unix.superglobalmegacorp.com

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