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

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

unix.superglobalmegacorp.com

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