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

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

unix.superglobalmegacorp.com

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