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

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

unix.superglobalmegacorp.com

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