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

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

unix.superglobalmegacorp.com

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