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

1.1       root        1: //
                      2: // nono
1.1.1.4   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
1.1.1.9   root        7: //
1.1.1.10  root        8: // FDC (uPD72065B) + I/O コントローラ
1.1.1.9   root        9: //
                     10: 
1.1.1.10  root       11: // MOTOR ON 信号
                     12: //
                     13: // X68030 の回路図によれば、簡略すると以下のように接続されている。
                     14: //
                     15: // PEDEC           SED9420A               FDD
                     16: // -------+     +---------------+      +-----------
                     17: //  ~TRIG |-----| TRIG    TMOUT |------| ~MOTOR ON
                     18: //
                     19: // SED9420A の TRIG は内蔵単安定マルチバイブレータのトリガ端子となっている。
                     20: // TRIG がアサートされている間 TMOUT をアサートし、TRIG がネゲートされると
                     21: // 一定時間後に TMOUT をネゲートする。
                     22: // その時間は SED9420A の CR 端子に接続された CR 回路によって決まってくるが
                     23: // データシートには時間の計算方法が記されていない。X68030 の回路図では
                     24: //
                     25: // SED9420A
                     26: // ---------+
                     27: //       CR |-----+---820kOhm---VCC1
                     28: //                |
                     29: //               47uF
                     30: //                |
                     31: //               GND
                     32: //
                     33: // となっているので時定数は 38.54 秒である。
                     34: // この時定数と TMOUT の維持時間の正確な関係は分からないが、他の単安定マルチ
                     35: // では時定数時間とパルス幅が同じになるように調整されたもの(74HC123)もある
                     36: // ので、この時定数時間を TMOUT (MOTOR ON) 信号の維持時間と考えることにする。
                     37: //
                     38: // 参考文献
                     39: //  最新フロッピ・ディスク装置とその応用ノウハウ  高橋昇司  p.175
                     40: //  https://archive.org/details/recent_floppy_disk_system_and_know_how/
                     41: //    page/n175
                     42: //
                     43: //
                     44: // DRIVE SEL.x 信号とアクセスドライブセレクトレジスタ
                     45: //
                     46: // アクセスドライブセレクトレジスタ($e94007) の MOTOR_ON ビットが 1 の
                     47: // ときは、アクセスドライブ番号フィールドにより指定された DRIVE SEL.x 信号が
                     48: // アサートされる。MOTOR_ON ビットが 0 のときは、すべての DRIVE SEL.x 信号は
                     49: // ネゲートされて、どのドライブも選択されていない状態になる。
                     50: // 「ドライブセレクトイネーブル」とあるのは、こういう意味である。
                     51: //
                     52: //
                     53: // DRIVE SEL.x 信号と各信号の関係
                     54: //
                     55: // MOTOR ON 信号と DRIVE SEL.x 信号の考察を踏まえると、MOTOR ON 信号は
                     56: // ドライブが選択されているかどうかに関わらず、全ドライブが受け取らなければ
                     57: // ならない。そうでないとタイマーを入れている意味がない。
                     58: // FDD は自分が選択されているかどうかに関係なく、MOTOR ON 信号がアサート
                     59: // されればモータを回そうとし、ネゲートされればモータを止める。
                     60: // また、アクセスドライブセレクトレジスタの 2HD/2DD ビット、およびその
                     61: // 関係する信号である DISK TYPE SEL, MIN/STD, MFM/FM 周りも同様と考えられる。
                     62: // これらの信号は SED9420A に接続されるため、ドライブごとに扱うように
                     63: // なっていないからである。
                     64: // また、FDDINT についても、どこかのドライブで状態が変わったことを CPU に
                     65: // 通知する、という機能が要求されるべきであるから、OP.SEL にもよらず
                     66: // 割り込みがかかる、と推定される。
                     67: //
                     68: // DRIVE SEL.x でドライブごとに有効になる信号
                     69: //   READY, INDEX, READ DATA, WRITE PROTECT, TRK00, WRITE GATE, WRITE DATA,
                     70: //   STEP, DIRECTION
                     71: // ドライブに共通の信号
                     72: //   MOTOR ON, DISK TYPE SEL, FDDINT
                     73: // どちらでも良い信号
                     74: //   SIDE SELECT (たぶんドライブごと有効と思われる)
                     75: // OP.SEL.x でドライブごとに有効になる信号
                     76: //   EJECT, EJECT MASK, LED BLINK, DISK IN, ERR DISK
                     77: 
1.1.1.15  root       78: // ドライブセレクトイネーブルがネゲートされていて、
                     79: // どのドライブも選択されていない状態になったとき、
                     80: // 各入力信号はプルアップされているため、
                     81: // 負論理の信号はすべてネゲート状態となる。
                     82: 
1.1       root       83: #include "fdc.h"
1.1.1.10  root       84: #include "bitops.h"
                     85: #include "config.h"
                     86: #include "dmac.h"
1.1.1.17! root       87: #include "event.h"
1.1.1.10  root       88: #include "fdd.h"
1.1.1.14  root       89: #include "monitor.h"
1.1.1.6   root       90: #include "pedec.h"
1.1       root       91: #include "scheduler.h"
                     92: 
1.1.1.10  root       93: // time [nsec] 後に呼び出す phase_event のコールバックをセットする。
                     94: // func の書式が面倒なのを省略して書きたいため。
                     95: #define CallAfter(func_, time_)        do { \
1.1.1.17! root       96:        phase_event->func = ToEventCallback(&FDCDevice::func_); \
        !            97:        phase_event->time = time_; \
1.1.1.11  root       98:        scheduler->RestartEvent(phase_event); \
1.1.1.10  root       99: } while (0)
                    100: 
                    101: 
1.1       root      102: // コンストラクタ
                    103: FDCDevice::FDCDevice()
1.1.1.11  root      104:        : inherited(OBJ_FDC)
1.1       root      105: {
1.1.1.14  root      106:        monitor = gMonitorManager->Regist(ID_MONITOR_FDC, this);
                    107:        monitor->func = ToMonitorCallback(&FDCDevice::MonitorUpdate);
1.1.1.10  root      108:        // サイズは Create() 時に確定する
1.1       root      109: }
                    110: 
                    111: // デストラクタ
                    112: FDCDevice::~FDCDevice()
                    113: {
                    114: }
                    115: 
1.1.1.10  root      116: bool
                    117: FDCDevice::Create()
                    118: {
                    119:        // ドライブ数。
                    120:        // fdd_list はランダムアクセスできる配列なので、歯抜け (#0 がなくて
                    121:        // #1 があるみたいなの) もサポートするが、今の所設定方法がない。
                    122:        const ConfigItem& item = gConfig->Find("fd-drive");
                    123:        ndrive = item.AsInt();
                    124: 
                    125:        // X68k では上限4台。(LUNA はひとまず放置)
                    126:        if (ndrive > 4) {
                    127:                item.Err();
                    128:                return false;
                    129:        }
                    130: 
                    131:        // モニタサイズはここで決定。FDD は横に2台で、それ以上は縦に伸ばす。
1.1.1.14  root      132:        monitor->SetSize(80, 15 + (ndrive / 2) * 10);
1.1.1.10  root      133: 
                    134:        // vector<unique_ptr> は面倒なので一旦 unique_ptr[] に確保して
                    135:        // 生ポインタの vector を作る。うーん。
                    136:        for (int i = 0; i < ndrive; i++) {
1.1.1.16  root      137:                try {
                    138:                        fdd_list[i].reset(new FDDDevice(i));
                    139:                } catch (...) { }
                    140:                if ((bool)fdd_list[i] == false) {
                    141:                        warnx("Failed to initialize FDDDevice(%u) at %s", i, __method__);
                    142:                        return false;
                    143:                }
1.1.1.10  root      144:        }
                    145:        for (int i = 0; i < ndrive; i++) {
                    146:                if ((bool)fdd_list[i]) {
                    147:                        fdd_vector.push_back(fdd_list[i].get());
                    148:                } else {
                    149:                        fdd_vector.push_back(NULL);
                    150:                }
                    151:        }
                    152: 
                    153:        return true;
                    154: }
                    155: 
1.1.1.11  root      156: // 初期化
                    157: bool
                    158: FDCDevice::Init()
                    159: {
                    160:        dmac = GetDMACDevice();
                    161:        pedec = GetPEDECDevice();
                    162: 
1.1.1.17! root      163:        auto evman = GetEventManager();
        !           164:        phase_event = evman->Regist(this, NULL, "FDC Phase");
1.1.1.13  root      165: 
1.1.1.17! root      166:        poll_event = evman->Regist(this,
        !           167:                ToEventCallback(&FDCDevice::PollEventCallback),
        !           168:                "FDC Polling");
        !           169:        poll_event->time = 1024_usec;
        !           170: 
        !           171:        motor_event = evman->Regist(this,
        !           172:                ToEventCallback(&FDCDevice::MotorEventCallback),
        !           173:                "FDC SED9420A TMOUT");
        !           174:        motor_event->time = 38.54_sec;
1.1.1.13  root      175: 
1.1.1.11  root      176:        return true;
                    177: }
                    178: 
1.1.1.9   root      179: // リセット
1.1       root      180: void
1.1.1.9   root      181: FDCDevice::ResetHard(bool poweron)
1.1       root      182: {
1.1.1.10  root      183:        if (poweron) {
                    184:                // READY ポーリングはリセットの影響を受けないはず
                    185:                initialized = false;
1.1.1.11  root      186:                scheduler->StopEvent(motor_event);
1.1.1.10  root      187:        }
                    188: 
1.1.1.12  root      189:        // uPD765 マニュアルによると、リセット信号によるリセットでは
                    190:        // SRT, HUT, HLT は影響を受けない。
                    191: 
                    192:        // シリンダ位置 pcn はとりあえずわからないので放置。
                    193: 
                    194:        // US0,US1 を low (FDD0) に。
                    195:        us = 0;
                    196:        // 割り込み (と DRQ) をネゲート。
                    197:        msr.ndm = false;
                    198:        st0.ic = ST0_IC_IC;
                    199:        st0.se = false;
                    200:        st0.ec = false;
                    201:        st0.nr = false;
                    202:        st0.hd = false;
                    203:        st0.us = 0;
                    204:        ChangeInterrupt();
                    205: 
1.1.1.11  root      206:        scheduler->StopEvent(phase_event);
                    207:        scheduler->StopEvent(poll_event);
1.1.1.10  root      208:        CommandPhase();
1.1       root      209: }
                    210: 
1.1.1.12  root      211: busdata
1.1.1.13  root      212: FDCDevice::ReadPort(uint32 offset)
1.1       root      213: {
1.1.1.12  root      214:        busdata data;
1.1.1.5   root      215: 
1.1.1.7   root      216:        switch (offset) {
1.1.1.4   root      217:         case 0:
1.1.1.10  root      218:                if (dack) {
                    219:                        // DACK が立っているときは A0 によらずデータレジスタをアクセス
1.1.1.12  root      220:                        data = ReadData();
                    221:                } else {
                    222:                        data = GetMSR();
                    223:                        putlog(4, "STAT -> $%02x", data.Data());
1.1.1.10  root      224:                }
1.1.1.12  root      225:                break;
1.1.1.4   root      226:         case 1:
1.1.1.12  root      227:                data = ReadData();
                    228:                break;
1.1.1.4   root      229:         case 2:
1.1.1.12  root      230:                data = ReadDriveStatus();
                    231:                break;
1.1.1.4   root      232:         case 3:
1.1.1.12  root      233:                data = 0xff;
                    234:                break;
1.1.1.11  root      235: 
                    236:         default:
1.1.1.13  root      237:                VMPANIC("corrupted offset=%u", offset);
1.1       root      238:        }
1.1.1.12  root      239: 
                    240:        // InsideOut p.135
                    241:        const busdata wait = busdata::Wait(19 * 40_nsec);
                    242:        data |= wait;
                    243: 
1.1.1.13  root      244:        data |= BusData::Size1;
1.1.1.12  root      245:        return data;
1.1       root      246: }
                    247: 
1.1.1.12  root      248: busdata
1.1.1.13  root      249: FDCDevice::WritePort(uint32 offset, uint32 data)
1.1       root      250: {
1.1.1.7   root      251:        switch (offset) {
1.1.1.4   root      252:         case 0:
1.1.1.10  root      253:                if (dack) {
                    254:                        // DACK が立っているときは A0 によらずデータレジスタをアクセス
                    255:                        WriteDataReg(data);
1.1.1.12  root      256:                        break;
1.1.1.10  root      257:                }
                    258: 
                    259:                // ここはフェーズに関わらず 0x34, 0x35, 0x36 のコマンドだけを
                    260:                // いつでも受け付ける。RQM などの信号制御とは関係なく受け付ける、
                    261:                // となっているはずなので直接ディスパッチする。
                    262:                switch (data) {
                    263:                 case 0x34:
                    264:                        CmdResetStandby();
                    265:                        break;
                    266:                 case 0x35:
                    267:                        CmdSetStandby();
                    268:                        break;
                    269:                 case 0x36:
                    270:                        CmdSoftwareReset();
                    271:                        break;
                    272:                 default:
                    273:                        putlog(0, "Write DATA: command $%02x (NOT IMPLEMENTED)",
                    274:                                data);
                    275:                        break;
                    276:                }
1.1.1.12  root      277:                break;
1.1       root      278: 
1.1.1.4   root      279:         case 1:
1.1       root      280:                WriteCmd(data);
1.1.1.12  root      281:                break;
1.1.1.4   root      282: 
                    283:         case 2:
1.1.1.10  root      284:         {
                    285:                // ドライブコントロールレジスタ
                    286:                putlog(1, "Drive Control <- $%02x", data);
                    287: 
                    288:                uint8 bit = 0x01;
                    289:                for (const auto f : fdd_vector) {
1.1.1.15  root      290:                        // ドライブセレクタのビットが %1 -> %0 になると動作する。
1.1.1.10  root      291:                        if (f && bit_falling(drivectrl, data, bit)) {
                    292:                                f->SetDriveCtrl(data & 0xe0);
1.1.1.15  root      293: 
                    294:                                // FDD 割り込みのクリアは詳細不明だが、とりあえず
                    295:                                // 同じタイミングで動作するようにしておく。
                    296:                                pedec->NegateINT(f);
1.1.1.10  root      297:                        }
                    298:                        bit <<= 1;
1.1       root      299:                }
1.1.1.10  root      300:                drivectrl = data;
1.1.1.12  root      301:                break;
1.1.1.10  root      302:         }
1.1       root      303: 
1.1.1.4   root      304:         case 3:
1.1.1.10  root      305:         {
                    306:                bool is2DD = ((data & 0x10) != 0);
                    307:                uint accdrive = data & 3;
                    308:                bool motor_on_bit_new = data & 0x80;
                    309: 
                    310:                if (motor_on_bit_new) {
1.1.1.13  root      311:                        putlog(1, "Access Drive <- $%02x (MotorOn %s #%u)", data,
1.1.1.10  root      312:                                is2DD ? "2DD" : "2HD",
                    313:                                accdrive);
                    314: 
                    315:                        if (motor_on_bit == false) {
                    316:                                // MOTOR_ON ビットが立ち上がったら、全ドライブに通知
                    317:                                // motor_event は止める。
1.1.1.11  root      318:                                scheduler->StopEvent(motor_event);
1.1.1.10  root      319:                                for (const auto f : fdd_vector) {
                    320:                                        if (f) {
                    321:                                                f->SetMotorOn(true);
                    322:                                        }
                    323:                                }
                    324:                        }
1.1       root      325:                } else {
1.1.1.10  root      326:                        putlog(1, "Access Drive <- $%02x (MotorOff %s)", data,
                    327:                                is2DD ? "2DD" : "2HD");
                    328: 
                    329:                        // motor_on_bit はドライブセレクトイネーブルの意味も持つ
                    330: 
                    331:                        // 全ドライブの選択を解除
                    332:                        accdrive = MAX_DRIVE;
                    333: 
                    334:                        if (motor_on_bit) {
                    335:                                // MOTOR_ON ビットが立ち下がったら、一定時間後に MOTOR_ON
                    336:                                // 信号をネゲートする。
1.1.1.11  root      337:                                scheduler->RestartEvent(motor_event);
1.1.1.10  root      338:                        }
                    339:                }
                    340: 
                    341:                // FDD 割り込みクリア(要実機確認)
1.1.1.11  root      342:                pedec->NegateINT(fdd_vector[0]);
1.1.1.10  root      343: 
                    344:                motor_on_bit = motor_on_bit_new;
                    345: 
                    346:                DriveSelect(accdrive);
                    347:                // 全ドライブに通知
                    348:                for (const auto f : fdd_vector) {
                    349:                        if (f) {
                    350:                                f->SetDriveSelect(accdrive, is2DD);
                    351:                        }
1.1       root      352:                }
1.1.1.12  root      353:                break;
1.1.1.10  root      354:         }
1.1.1.11  root      355:         default:
1.1.1.13  root      356:                VMPANIC("corrupted offset=%u", offset);
1.1       root      357:        }
1.1.1.12  root      358: 
                    359:        // InsideOut p.135
                    360:        const busdata wait = busdata::Wait(19 * 40_nsec);
                    361: 
1.1.1.13  root      362:        busdata r = wait;
                    363:        r |= BusData::Size1;
                    364:        return r;
1.1       root      365: }
                    366: 
1.1.1.12  root      367: busdata
1.1.1.13  root      368: FDCDevice::PeekPort(uint32 offset)
1.1       root      369: {
1.1.1.10  root      370:        switch (offset) {
                    371:         case 0:
                    372:                if (dack == false) {
                    373:                        return GetMSR();
                    374:                }
                    375:                FALLTHROUGH;
                    376: 
                    377:         case 1:
                    378:         {
                    379:                // 副作用なくデータを読み出す
                    380:                switch (phase) {
                    381:                 case PHASE_COMMAND:
                    382:                        return 0xff;
                    383: 
                    384:                 case PHASE_EXEC:
                    385:                        if (dreg.Empty()) {
                    386:                                return 0xff;    // XXX 何が読めるか
                    387:                        }
                    388:                        return dreg.Peek(0);
                    389: 
                    390:                 case PHASE_RESULT:
                    391:                        return resbuf.Peek();
                    392: 
                    393:                 default:
1.1.1.11  root      394:                        return 0xff;
1.1.1.10  root      395:                }
                    396:         }
                    397: 
                    398:         case 2:
                    399:                return GetDriveStatus();
                    400: 
                    401:         case 3:
                    402:                return 0xff;
                    403: 
                    404:         default:
1.1.1.11  root      405:                return 0xff;
1.1.1.10  root      406:        }
1.1       root      407: }
                    408: 
1.1.1.13  root      409: bool
                    410: FDCDevice::PokePort(uint32 offset, uint32 data)
                    411: {
                    412:        return false;
                    413: }
                    414: 
1.1.1.10  root      415: // モニタ更新
                    416: void
                    417: FDCDevice::MonitorUpdate(Monitor *, TextScreen& screen)
                    418: {
                    419:        int x;
                    420:        int y;
                    421:        uint8 local_msr;
                    422:        uint8 local_st0;
                    423:        uint8 local_st1;
                    424:        uint8 local_st2;
                    425:        uint8 drivestat;
                    426:        FDDDevice *f;
                    427: 
                    428:        local_msr = GetMSR();
                    429:        local_st0 = GetST0();
                    430:        local_st1 = GetST1();
                    431:        local_st2 = GetST2();
                    432:        drivestat = GetDriveStatus();
                    433: 
                    434:        screen.Clear();
                    435:        y = 0;
                    436: 
                    437:        // 0         1         2         3         4         5         6
                    438:        // 01234567890123456789012345678901234567890123456789012345678901234567890
                    439:        // $ee99xx MSR:$00 --
                    440:        //         ST0:
                    441:        // Command: $00 $11 $22 $33 $44 $55 $66 $77 $88 $99 (READ DATA)
                    442: 
                    443:        // MSR
                    444:        screen.Print(0, y, "$%06x  MSR:$%02x", (baseaddr + 1),  local_msr);
                    445:        static const char * const msr_bits[] = {
                    446:                "RQM", "DIO", "NDM", "CB", "D3B", "D2B", "D1B", "D0B"
                    447:        };
                    448:        MonitorReg(screen, 17, y, local_msr, msr_bits);
                    449:        y++;
                    450: 
                    451:        // ST0
                    452:        screen.Print(9, y, "ST0:$%02x", local_st0);
                    453:        static const char * const st0_bits[] = {
                    454:                "",    "",    "SE",  "EC",  "NR",  "HD",  "US1", "US0"
                    455:        };
                    456:        MonitorReg(screen, 17, y, local_st0, st0_bits);
                    457:        static const char * const ic_str[] = {
                    458:                "NT",
                    459:                "AT",
                    460:                "IC",
                    461:                "AI",
                    462:        };
                    463:        screen.Print(17, y, "IC=%s", ic_str[local_st0 >> 6]);
                    464:        y++;
                    465: 
                    466:        // ST1
                    467:        screen.Print(9, y, "ST1:$%02x", local_st1);
                    468:        static const char * const st1_bits[] = {
                    469:                "EN", "-", "DE", "OR", "-", "ND", "NW", "MA"
                    470:        };
                    471:        MonitorReg(screen, 17, y, local_st1, st1_bits);
                    472:        y++;
                    473: 
                    474:        // ST2
                    475:        screen.Print(9, y, "ST2:$%02x", local_st2);
                    476:        static const char * const st2_bits[] = {
                    477:                "-", "CM", "DD", "NC", "SH", "SN", "BC", "MD"
                    478:        };
                    479:        MonitorReg(screen, 17, y, local_st2, st2_bits);
                    480:        y++;
                    481: 
                    482:        // ドライブコントロール
                    483:        screen.Print(0, y, "$e94005 CTRL:$%02x", drivectrl);
                    484:        static const char * const dctrl_bits[] = {
                    485:                "LED", "EJM", "EJO", "-", "D#3", "D#2", "D#1", "D#0"
                    486:        };
                    487:        MonitorReg(screen, 17, y, drivectrl, dctrl_bits);
                    488:        y++;
                    489: 
                    490:        // ドライブステータス
                    491:        screen.Print(0, y, "        STAT:$%02x", drivestat);
                    492:        static const char * const dstat_bits[] = {
                    493:                "DIN", "ERR", "-", "-", "-", "-", "-", "-"
                    494:        };
                    495:        MonitorReg(screen, 17, y, drivestat, dstat_bits);
                    496:        y++;
                    497: 
                    498:        x = 52;
                    499:        y = 0;
1.1.1.13  root      500:        screen.Print(x, y++, "HD=%u", hd);
                    501:        screen.Print(x, y++, "US=%u", us);
                    502:        screen.Print(x, y++, "TC=%u", tc);
                    503:        screen.Print(x, y++, "DACK=%u", dack);
1.1.1.10  root      504: 
                    505:        x = 60;
                    506:        y = 0;
1.1.1.13  root      507:        screen.Print(x, y++, "srt=%u (seek=%u)", srt, seek_srt);
                    508:        screen.Print(x, y++, "hut=%u", hut);
                    509:        screen.Print(x, y++, "hlt=%u", hlt);
                    510:        screen.Print(x, y++, "nd=%u", nd);
1.1.1.10  root      511: 
                    512:        x = 52;
                    513:        y = 5;
                    514:        screen.Puts(x, y, "<OPM>");
                    515:        screen.Print(x + 6, y, TA::OnOff(force_ready), "CT2");
                    516: 
                    517:        // フェーズ、コマンド
                    518:        x = 0;
                    519:        y = 7;
                    520:        static const char * const phase_str[] = {
                    521:                "Command",
                    522:                "Execute",
                    523:                "Result",
                    524:        };
                    525:        screen.Print(x, y++, "Phase:   %s", phase_str[(int)phase]);
                    526:        if (cmdbuf.len > 0) {
                    527:                screen.Puts(x, y, "Command:");
1.1.1.13  root      528:                for (uint i = 0; i < cmdbuf.len; i++) {
1.1.1.10  root      529:                        screen.Print(x + 9 + i * 4, y, "$%02x", cmdbuf.buf[i]);
                    530:                }
                    531:                screen.Print(x + 49, y, "(%s)", cmd->name);
                    532:        }
                    533: 
                    534:        x = 1;
                    535:        y = 10;
                    536:        // 存在するかに関わらず 4台分表示する (LUNA は知らん)
                    537:        screen.Puts(0, y, "<FDD> PCN NCN Ready Poll");
                    538:        screen.Puts(26, y, "Motor AccLED Eject   FMT C  H  R  N"); 
                    539:        y++;
                    540:        for (int i = 0; i < MAX_DRIVE; i++) {
                    541:                UnitInfo& u = unitinfo[i];
                    542: 
                    543:                if (i < fdd_vector.size()) {
                    544:                        f = fdd_vector[i];
                    545:                } else {
                    546:                        f = NULL;
                    547:                }
                    548: 
1.1.1.13  root      549:                screen.Print(x, y, "#%u", i);
1.1.1.10  root      550:                if (f) {
1.1.1.13  root      551:                        screen.Print(x + 5, y, "%3d %3u", u.pcn, u.ncn);
1.1.1.10  root      552:                        screen.Print(x + 13, y, TA::OnOff(u.ready), "Ready");
                    553:                        static const char * state_str[] = {
                    554:                                "Idle",
                    555:                                "Seek",
                    556:                                "Recal",
                    557:                        };
                    558:                        if ((uint)u.state < countof(state_str)) {
                    559:                                screen.Puts(x + 19, y, state_str[(int)u.state]);
                    560:                        } else {
                    561:                                screen.Puts(x + 19, y, "state?");
                    562:                        }
                    563:                } else {
                    564:                        screen.Puts(x + 5, y, TA::Disable, "Not connected");
                    565:                }
                    566: 
                    567:                y++;
                    568:        }
                    569: 
                    570:        // FDD の状況も同じモニタ内に表示する。
                    571:        for (int i = 0; i < fdd_vector.size(); i++) {
                    572:                f = fdd_vector[i];
                    573:                if (f) {
                    574:                        f->MonitorUpdateFDD(screen, hd);
                    575:                }
                    576:        }
                    577: }
                    578: 
                    579: // レジスタをビットごとに表示する。MonitorUpdate の下請け。
                    580: void
                    581: FDCDevice::MonitorReg(TextScreen& screen,
                    582:        int x, int y, uint8 reg, const char * const *names)
                    583: {
1.1.1.13  root      584:        for (uint i = 0; i < 8; i++) {
1.1.1.10  root      585:                if (names[i][0] == '-') {
                    586:                        screen.Puts(x + i * 4, y, TA::Disable, "---");
                    587:                } else {
1.1.1.17! root      588:                        bool b = reg & (1U << (7 - i));
1.1.1.10  root      589:                        screen.Puts(x + i * 4, y, TA::OnOff(b), names[i]);
                    590:                }
                    591:        }
                    592: }
                    593: 
                    594: // メインステータスレジスタ(MSR)取得
                    595: uint8
                    596: FDCDevice::GetMSR() const
1.1       root      597: {
                    598:        uint8 stat = 0;
                    599: 
1.1.1.10  root      600:        if (msr.rqm) {
                    601:                stat |= MSR_RQM;
1.1       root      602:        }
1.1.1.10  root      603:        if (msr.dio == DIOtoHost) {
                    604:                stat |= MSR_DIO;
1.1       root      605:        }
1.1.1.10  root      606:        if (msr.ndm) {
                    607:                stat |= MSR_NDM;
1.1       root      608:        }
1.1.1.10  root      609:        if (msr.cb) {
                    610:                stat |= MSR_CB;
1.1       root      611:        }
1.1.1.10  root      612:        for (int i = 0; i < msr.db.size(); i++) {
                    613:                stat |= (msr.db[i] ? 1 : 0) << i;
1.1       root      614:        }
                    615: 
                    616:        return stat;
                    617: }
                    618: 
1.1.1.10  root      619: // ST0 レジスタ取得
                    620: uint8
                    621: FDCDevice::GetST0() const
                    622: {
                    623:        uint8 data;
                    624: 
                    625:        data = st0.ic;
                    626:        if (st0.se) {
                    627:                data |= ST0_SE;
                    628:        }
                    629:        if (st0.ec) {
                    630:                data |= ST0_EC;
                    631:        }
                    632:        if (st0.nr) {
                    633:                data |= ST0_NR;
                    634:        }
                    635:        if (st0.hd) {
                    636:                data |= ST0_HD;
                    637:        }
                    638:        data |= st0.us;
                    639: 
                    640:        return data;
                    641: }
                    642: 
                    643: // ST1 レジスタ取得
                    644: uint8
                    645: FDCDevice::GetST1() const
                    646: {
                    647:        uint8 data = 0;
                    648: 
                    649:        if (st1.en) {
                    650:                data |= ST1_EN;
                    651:        }
                    652:        if (st1.de) {
                    653:                data |= ST1_DE;
                    654:        }
                    655:        if (st1.overrun) {
                    656:                data |= ST1_OR;
                    657:        }
                    658:        if (st1.nd) {
                    659:                data |= ST1_ND;
                    660:        }
                    661:        if (st1.nw) {
                    662:                data |= ST1_NW;
                    663:        }
                    664:        if (st1.ma) {
                    665:                data |= ST1_MA;
                    666:        }
                    667:        return data;
                    668: }
                    669: 
                    670: // ST2 レジスタ取得
                    671: uint8
                    672: FDCDevice::GetST2() const
                    673: {
                    674:        uint8 data = 0;
                    675: 
                    676:        if (st2.cm) {
                    677:                data |= ST2_CM;
                    678:        }
                    679:        if (st2.dd) {
                    680:                data |= ST2_DD;
                    681:        }
                    682:        if (st2.nc) {
                    683:                data |= ST2_NC;
                    684:        }
                    685:        if (st2.sh) {
                    686:                data |= ST2_SH;
                    687:        }
                    688:        if (st2.sn) {
                    689:                data |= ST2_SN;
                    690:        }
                    691:        if (st2.bc) {
                    692:                data |= ST2_BC;
                    693:        }
                    694:        if (st2.md) {
                    695:                data |= ST2_MD;
                    696:        }
                    697:        return data;
                    698: }
                    699: 
1.1       root      700: // FDC データレジスタ読み込み
                    701: uint32
                    702: FDCDevice::ReadData()
                    703: {
                    704:        uint8 data;
                    705: 
1.1.1.10  root      706:        // TODO: RQM, DIO 処理
                    707: 
                    708:        switch (phase) {
                    709:         case PHASE_COMMAND:
                    710:                // 規定フェーズ外の読み込み
                    711:                // XXX ほんと?
                    712:                putlog(1, "Read DATA: in irregular phase");
                    713:                data = 0xff;
                    714:                return data;
                    715: 
                    716:         case PHASE_EXEC:
                    717:                if (dreg.Dequeue(&data) == false) {
                    718:                        // エラー
                    719:                        return 0xff;
                    720:                }
                    721:                putlog(4, "DATA -> $%02x", data);
                    722:                return data;
                    723: 
                    724:         case PHASE_RESULT:
                    725:                data = resbuf.Take();
                    726:                putlog(2, "DATA -> $%02x", data);
                    727:                if (resbuf.pos == 1) {
                    728:                        // 1 バイトを読んだところで
                    729:                        // ステータスをレポートするとクリアされるようだ
                    730:                        st0.ic = ST0_IC_IC;
                    731:                        st0.se = false;
                    732:                        st0.ec = false;
                    733:                        st0.nr = false;
                    734:                        st0.hd = false;
                    735:                        st0.us = 0;
                    736:                        ChangeInterrupt();
                    737:                }
                    738:                if (resbuf.pos == resbuf.len) {
                    739:                        CommandPhase();
                    740:                }
                    741:                return data;
                    742: 
                    743:         default:
1.1.1.13  root      744:                VMPANIC("corrupted phase=$%x", (uint)phase);
1.1       root      745:        }
1.1.1.10  root      746: }
1.1       root      747: 
1.1.1.10  root      748: // ドライブステータスレジスタ取得
                    749: uint32
                    750: FDCDevice::GetDriveStatus() const
                    751: {
                    752:        uint8 data = 0;
                    753: 
                    754:        // ドライブコントロールのドライブ選択が %1 になっているドライブを調べる。
                    755:        // 誤挿入は未対応。
                    756:        uint8 bit = 0x01;
                    757:        for (const auto f : fdd_vector) {
                    758:                if ((drivectrl & bit)) {
                    759:                        if (f && f->IsMediumLoaded()) {
                    760:                                data |= 0x80;
                    761:                        }
                    762:                }
                    763:                bit <<= 1;
1.1       root      764:        }
1.1.1.10  root      765: 
1.1       root      766:        return data;
                    767: }
                    768: 
                    769: // ドライブステータスレジスタ読み込み
                    770: uint32
                    771: FDCDevice::ReadDriveStatus()
                    772: {
1.1.1.10  root      773:        uint8 data = GetDriveStatus();
                    774:        putlog(1, "Drive Status -> $%02x", data);
1.1       root      775:        return data;
                    776: }
                    777: 
                    778: // FDC コマンドレジスタ書き込み
                    779: void
                    780: FDCDevice::WriteCmd(uint32 data)
                    781: {
1.1.1.10  root      782:        switch (phase) {
                    783:         case PHASE_COMMAND:
                    784:                if (msr.rqm == false) {
                    785:                        // リクエストしていないコマンド書き込み
                    786:                        putlog(1, "CMD <- $%02x: not requested", data);
1.1       root      787:                        break;
1.1.1.10  root      788:                }
                    789:                msr.rqm = false;
1.1       root      790: 
1.1.1.13  root      791:                putlog(2, "CMD[%u] <- $%02x", cmdbuf.len, data);
1.1.1.10  root      792:                cmdbuf.buf[cmdbuf.len++] = data;
                    793:                // コマンドの1バイト目でディスパッチ
                    794:                if (cmdbuf.len == 1) {
                    795:                        // 見付からなければリスト末尾の Invalid にマッチする
                    796:                        for (auto& c : cmd_list) {
                    797:                                if ((cmdbuf.code & c.mask) == c.code) {
                    798:                                        cmd = &c;
                    799:                                        break;
                    800:                                }
                    801:                        }
                    802:                        putlog(1, "Command %s accepted", cmd->name);
1.1       root      803:                }
1.1.1.10  root      804:                CallAfter(CommandCallback, 2_usec);
                    805:                break;
                    806: 
                    807:         case PHASE_EXEC:
                    808:                if (msr.dio == DIOtoFDC) {
                    809:                        WriteDataReg(data);
                    810:                } else {
                    811:                        putlog(1, "CMD <- $%02x: unexpected data write (DIO)", data);
                    812:                }
                    813:                break;
                    814: 
                    815:         case PHASE_RESULT:
                    816:                putlog(1, "CMD <- $%02x: unexpected data write (result phase)", data);
                    817:                break;
1.1       root      818: 
1.1.1.10  root      819:         default:
1.1.1.13  root      820:                VMPANIC("corrupted phase=$%x", (uint)phase);
1.1.1.10  root      821:        }
                    822: }
                    823: 
                    824: // FDC データレジスタ書き込み
                    825: void
                    826: FDCDevice::WriteDataReg(uint32 data)
                    827: {
                    828:        dreg.EnqueueForce(data);
                    829: }
                    830: 
                    831: // Command フェーズに移行
                    832: void
                    833: FDCDevice::CommandPhase()
                    834: {
                    835:        putlog(2, "Command Phase");
                    836:        phase = PHASE_COMMAND;
                    837:        msr.rqm = true;
                    838:        msr.dio = DIOtoFDC;
                    839:        msr.ndm = false;
                    840:        msr.cb = false;
                    841:        cmdbuf.Clear();
                    842:        resbuf.Clear();
                    843: }
                    844: 
                    845: // Exec フェーズに移行
                    846: void
                    847: FDCDevice::ExecPhase()
                    848: {
                    849:        putlog(2, "Exec Phase: %s", cmd->name);
                    850:        phase = PHASE_EXEC;
                    851:        msr.cb = true;
                    852:        if (nd) {
                    853:                msr.ndm = true; // XXX ?
                    854:        }
                    855: }
                    856: 
                    857: // Result フェーズに移行
                    858: void
                    859: FDCDevice::ResultPhase()
                    860: {
                    861:        putlog(2, "Result Phase");
                    862:        phase = PHASE_RESULT;
                    863:        msr.rqm = true;
                    864:        msr.dio = DIOtoHost;
                    865:        msr.ndm = false;
                    866:        msr.cb = true;
                    867: }
                    868: 
                    869: void
1.1.1.17! root      870: FDCDevice::ResultCallback(Event *ev)
1.1.1.10  root      871: {
                    872:        ResultPhase();
                    873: }
                    874: 
                    875: void
1.1.1.17! root      876: FDCDevice::CommandCallback(Event *ev)
1.1.1.10  root      877: {
                    878:        msr.cb = true;
                    879:        if (cmdbuf.len == cmd->cmdlen) {
                    880:                putlog(2, "Command %s execute", cmd->name);
                    881:                (this->*(cmd->func))();
1.1       root      882:        } else {
1.1.1.10  root      883:                msr.rqm = true;
1.1       root      884:        }
1.1.1.10  root      885: }
1.1       root      886: 
1.1.1.10  root      887: // 割り込み信号線の状態を変える
                    888: void
                    889: FDCDevice::ChangeInterrupt()
                    890: {
                    891:        if (GetST0() != 0x80 || (msr.ndm && 0/* XXX DRQ*/)) {
                    892:                putlog(4, "AssertINT");
1.1.1.11  root      893:                pedec->AssertINT(this);
1.1.1.10  root      894:        } else {
                    895:                putlog(4, "NegateINT");
1.1.1.11  root      896:                pedec->NegateINT(this);
1.1       root      897:        }
                    898: }
                    899: 
1.1.1.10  root      900: // READY 信号が変化したことの通知 (FDD から呼ばれる)
1.1       root      901: void
1.1.1.10  root      902: FDCDevice::ReadyChanged(int unit, bool is_ready)
1.1       root      903: {
1.1.1.10  root      904:        // 実際は READY 信号は SPECIFY コマンドが呼ばれた後 1.024 msec 間隔で
                    905:        // 常にポーリングしているが、エミュレータでは無駄なので、変化があった
                    906:        // ことの通知を受けてポーリングループを起動し、どのユニットも変化が
                    907:        // なくなったらポーリングを止めるようにしておく。
                    908:        // uPD765 マニュアル POLLING FEATURE OF THE uPD765 (p.433)
1.1.1.16  root      909:        StartPoll();
1.1.1.10  root      910: 
                    911:        // 転送の同期を取るために、READY になった時刻を覚えておく
                    912:        if (is_ready) {
1.1.1.11  root      913:                unitinfo[unit].ready_time = scheduler->GetVirtTime();
1.1.1.10  root      914:        }
1.1       root      915: }
                    916: 
1.1.1.10  root      917: // ポーリング開始
1.1       root      918: void
1.1.1.10  root      919: FDCDevice::StartPoll()
1.1       root      920: {
1.1.1.16  root      921:        // SPECIFY コマンドで有効になる
                    922:        if (initialized == false) {
                    923:                return;
                    924:        }
1.1.1.17! root      925:        if (poll_event->IsRunning() == false) {
1.1.1.10  root      926:                putlog(3, "Start polling");
1.1.1.11  root      927:                scheduler->StartEvent(poll_event);
1.1.1.10  root      928:        }
                    929: }
1.1       root      930: 
1.1.1.10  root      931: // ポーリングイベントコールバック
                    932: void
1.1.1.17! root      933: FDCDevice::PollEventCallback(Event *ev)
1.1.1.10  root      934: {
                    935:        bool is_poll = false;
1.1       root      936: 
1.1.1.10  root      937:        // READY ポーリング。
                    938:        // コマンドフェーズにいてコマンド未発行のときだけ実行。
                    939:        // Inside で言う「シーク中の E-PHASE」も FDC としては
                    940:        // コマンドフェーズなので同じ状態になっている。
                    941:        if (phase == PHASE_COMMAND && cmdbuf.len == 0) {
                    942:                for (int unit_no = 0; unit_no < MAX_DRIVE; unit_no++) {
                    943:                        is_poll |= PollReady(unit_no);
1.1.1.9   root      944:                }
1.1.1.10  root      945:        }
1.1       root      946: 
1.1.1.10  root      947:        // シーク
                    948:        seek_srt++;
                    949:        if (seek_srt == 16) {
                    950:                seek_srt = srt;
                    951:                for (int unit_no = 0; unit_no < MAX_DRIVE; unit_no++) {
                    952:                        is_poll |= ExecSeek(unit_no);
                    953:                }
                    954:        } else {
                    955:                is_poll = true;
                    956:        }
1.1       root      957: 
1.1.1.10  root      958:        if (is_poll) {
1.1.1.11  root      959:                scheduler->RestartEvent(ev);
1.1.1.10  root      960:        } else {
                    961:                putlog(3, "Stop polling");
                    962:        }
                    963: }
1.1       root      964: 
1.1.1.10  root      965: // READY ポーリングの実行。
                    966: // ポーリングを継続する必要があるときは true を返す。
                    967: bool
                    968: FDCDevice::PollReady(int unit_no)
                    969: {
                    970:        auto& unit = unitinfo[unit_no];
                    971: 
                    972:        UnitSelect(unit_no);
                    973:        bool r = IsReady();
                    974:        if (unit.ready != r) {
                    975:                // READY が変化したので、ドライブ状態が Idle でかつ、
                    976:                // 割り込み発行中でなければ状態を更新して割り込みを発行
                    977:                if (unit.state == UnitState::Idle && st0.ic == ST0_IC_IC) {
                    978:                        unit.ready = r;
                    979:                        st0.ic = ST0_IC_AI;
                    980:                        st0.nr = !r;
                    981:                        st0.us = unit_no;
                    982:                        ChangeInterrupt();
                    983:                        return false;
                    984:                } else {
                    985:                        // 割り込み発生をペンディングしているのでポーリング続行
                    986:                        return true;
                    987:                }
1.1       root      988:        }
1.1.1.10  root      989:        return false;
1.1       root      990: }
                    991: 
1.1.1.10  root      992: // リキャリブレートまたはシークの実行。
                    993: // ポーリングを継続する必要があるときは true を返す。
                    994: bool
                    995: FDCDevice::ExecSeek(int unit_no)
                    996: {
                    997:        auto& unit = unitinfo[unit_no];
                    998: 
                    999:        if (unit.state == UnitState::Idle) {
                   1000:                return false;
                   1001:        }
                   1002: 
                   1003:        UnitSelect(unit_no);
                   1004: 
                   1005:        if (IsReady() == false) {
                   1006:                unit.state = UnitState::Idle;
                   1007:                st0.nr = true;
                   1008:                st0.ic = ST0_IC_AT;
                   1009:                st0.hd = false;
                   1010:                st0.us = us;
                   1011:                ChangeInterrupt();
                   1012:                CommandPhase();
                   1013:                return false;
                   1014:        }
                   1015:        unit.ready = true;
                   1016: 
                   1017:        if (unit.state == UnitState::Recalibrate) {
                   1018:                if (fdd && fdd->GetTrack0()) {
                   1019:                        putlog(2, "Track0 detected");
                   1020:                        unit.pcn = 0;
                   1021:                        SeekEnd();
                   1022:                        return false;
                   1023:                } else if (unit.pcn < 0) {
                   1024:                        putlog(2, "Track0 not detected");
                   1025:                        st0.ec = true;
                   1026:                        SeekEnd();
                   1027:                        return false;
                   1028:                }
                   1029:        } else {
                   1030:                if (unit.pcn == unit.ncn) {
                   1031:                        SeekEnd();
                   1032:                        return false;
                   1033:                }
                   1034:        }
                   1035: 
                   1036:        // シークを FDD に発行
                   1037:        int dir;
                   1038:        if (unit.pcn < unit.ncn) {
                   1039:                dir = 1;
                   1040:        } else {
                   1041:                dir = -1;
                   1042:        }
                   1043:        unit.pcn += dir;
                   1044:        if (fdd) {
                   1045:                fdd->DoSeek(dir);
                   1046:        }
                   1047:        return true;
                   1048: }
                   1049: 
                   1050: // 強制レディ信号の設定 (OPM から呼ばれる)
                   1051: void
                   1052: FDCDevice::SetForceReady(bool force_ready_)
                   1053: {
                   1054:        force_ready = force_ready_;
                   1055:        putlog(2, "Force Ready %s", force_ready ? "On" : "Off");
1.1.1.16  root     1056:        StartPoll();
1.1.1.10  root     1057: }
                   1058: 
                   1059: // DACK 信号をアサートする (DMAC から呼ばれる)
1.1       root     1060: void
1.1.1.10  root     1061: FDCDevice::AssertDACK(bool tc_)
1.1       root     1062: {
1.1.1.10  root     1063:        dack = true;
                   1064:        tc = tc_;
                   1065:        // DRQ を下げる
1.1.1.11  root     1066:        dmac->NegateREQ(0);
1.1       root     1067: }
                   1068: 
1.1.1.10  root     1069: // DACK 信号をネゲートする (DMAC から呼ばれる)
1.1       root     1070: void
1.1.1.10  root     1071: FDCDevice::NegateDACK()
1.1       root     1072: {
1.1.1.10  root     1073:        dack = false;
                   1074: }
                   1075: 
                   1076: // 内部データレジスタに data を投入
                   1077: void
                   1078: FDCDevice::PushDreg(uint8 data)
                   1079: {
                   1080:        if (dreg.Enqueue(data)) {
                   1081:                // FDC DRQ をアサートして DMAC REQ#0 を送る
                   1082:                RequestDataByte();
                   1083:        } else {
                   1084:                // OverRun
                   1085:                putlog(1, "PushDreg overrun detected");
                   1086:                st1.overrun = true;
                   1087:                st0.ic = ST0_IC_AT;
                   1088:                ChangeInterrupt();
                   1089:        }
                   1090: }
                   1091: 
                   1092: // 内部データレジスタから data を取り出す。
1.1.1.13  root     1093: // 空なら (uint32)-1 を返す。
                   1094: uint32
1.1.1.10  root     1095: FDCDevice::PopDreg()
                   1096: {
                   1097:        uint8 data;
                   1098: 
                   1099:        if (dreg.Dequeue(&data)) {
                   1100:                return data;
                   1101:        } else {
                   1102:                // OverRun
                   1103:                putlog(1, "PopDreg overrun detected");
                   1104:                st1.overrun = true;
                   1105:                st0.ic = ST0_IC_AT;
                   1106:                ChangeInterrupt();
1.1.1.13  root     1107:                return (uint32)-1;
1.1       root     1108:        }
                   1109: }
                   1110: 
1.1.1.10  root     1111: // CPU または DMAC に読み書きデータを要求する
1.1       root     1112: void
1.1.1.10  root     1113: FDCDevice::RequestDataByte()
1.1       root     1114: {
1.1.1.10  root     1115:        if (nd) {
                   1116:                putlog(0, "CPU Transfer mode (NOT IMPLEMENTED)");
                   1117:        } else {
1.1.1.11  root     1118:                dmac->AssertREQ(0);
1.1.1.10  root     1119:        }
1.1.1.9   root     1120: }
                   1121: 
1.1.1.10  root     1122: // ユニット US0,US1 信号線の状態を変える。
1.1.1.9   root     1123: void
1.1.1.10  root     1124: FDCDevice::UnitSelect(uint8 us_)
1.1.1.9   root     1125: {
1.1.1.10  root     1126:        // こちらは FDC 側の US0, US1 信号線の出力に相当。
                   1127:        // アクセスドライブは X68k では I/O コントローラ側で決まる。
                   1128:        // LUNA では XP 側の PSG の I/O と US 信号線の組み合わせで決まる。
                   1129:        us = us_ & 0x03;
                   1130: 
1.1.1.13  root     1131:        putlog(3, "UnitSelect US:%u", us);
1.1.1.10  root     1132: 
                   1133:        // いまのところ LUNA のことは忘れる
1.1       root     1134: }
                   1135: 
1.1.1.10  root     1136: // ドライブセレクト信号線の状態を変える。
                   1137: void
                   1138: FDCDevice::DriveSelect(uint8 drive)
                   1139: {
                   1140:        // こちらは I/O コントローラにあるアクセスドライブセレクトのほう
                   1141:        if (drive < ndrive) {
                   1142:                fdd = fdd_vector[drive];
                   1143:        } else {
                   1144:                fdd = NULL;
                   1145:        }
                   1146: 
1.1.1.15  root     1147:        // ドライブセレクト信号線が変わったら READY が変わるかもしれないので
                   1148:        // ポーリングを再開する必要がある。
1.1.1.16  root     1149:        StartPoll();
1.1.1.15  root     1150: 
1.1.1.10  root     1151:        // いまのところ LUNA のことは忘れる
                   1152: }
                   1153: 
                   1154: // 一定時間後に MOTOR_ON 信号をネゲートするイベント。
                   1155: // SED9420 の TMOUT 信号に相当。
                   1156: void
1.1.1.17! root     1157: FDCDevice::MotorEventCallback(Event *ev)
1.1.1.10  root     1158: {
                   1159:        // 全ドライブに通知
                   1160:        for (const auto f : fdd_vector) {
                   1161:                if (f) {
                   1162:                        f->SetMotorOn(false);
                   1163:                }
                   1164:        }
                   1165: }
                   1166: 
                   1167: // FDC コマンド
                   1168: 
                   1169: void
                   1170: FDCDevice::CmdReadDiagnostic()
                   1171: {
                   1172:        CmdNotImplemented();
                   1173: }
                   1174: 
                   1175: void
                   1176: FDCDevice::CmdSpecify()
                   1177: {
                   1178:        srt = cmdbuf.specify.srt_hut >> 4;
                   1179:        hut = cmdbuf.specify.srt_hut & 15;
                   1180:        hlt = cmdbuf.specify.hlt_nd >> 1;
                   1181:        nd  = cmdbuf.specify.hlt_nd & 1;
1.1.1.13  root     1182:        putlog(2, " SRT:%u HUT:%u HLT:%u ND:%u", srt, hut, hlt, nd);
1.1.1.10  root     1183: 
                   1184:        // 初期化完了、ポーリング開始
                   1185:        initialized = true;
                   1186: 
                   1187:        CommandPhase();
                   1188: }
                   1189: 
                   1190: void
                   1191: FDCDevice::CmdSenseDeviceStatus()
                   1192: {
                   1193:        UnitSelect(cmdbuf.sense_device_status.hd_us);
                   1194: 
                   1195:        uint8 st3 = 0;
                   1196:        // FT = fdd.GetFault();
                   1197:        if (fdd && fdd->GetWriteProtect()) {
                   1198:                st3 |= ST3_WP;
                   1199:        }
                   1200:        if (IsReady()) {
                   1201:                st3 |= ST3_RY;
                   1202:        }
                   1203:        if (fdd && fdd->GetTrack0()) {
                   1204:                st3 |= ST3_T0;
                   1205:        }
                   1206:        // TS = fdd.GetTwoSide();
                   1207:        // HD, US
                   1208:        st3 |= (cmdbuf.sense_device_status.hd_us & 0x07);
                   1209: 
                   1210:        putlog(2, " ST3=$%02x", st3);
                   1211:        resbuf.device_status.st3 = st3;
                   1212:        resbuf.len = sizeof(resbuf.device_status);
                   1213:        // 1.024 msec は READY のポーリング間隔から推定。
                   1214:        // 正確な数字は実機調査すること。
                   1215:        CallAfter(ResultCallback, 1024_usec);
                   1216: }
                   1217: 
                   1218: void
                   1219: FDCDevice::CmdWriteData()
                   1220: {
                   1221:        UnitSelect(cmdbuf.data.hd_us);
                   1222: 
                   1223:        mt = cmdbuf.code & 0x80;
                   1224:        mf = cmdbuf.code & 0x40;
                   1225:        sk = 0;
                   1226: 
                   1227:        hd = (cmdbuf.data.hd_us & 0x04) >> 2;
                   1228: 
                   1229:        if (loglevel >= 2) {
1.1.1.13  root     1230:                putlogn(" MT:%u MF:%u SK:%u HD:%u", (uint)mt, (uint)mf, (uint)sk, hd);
                   1231:                putlogn(" C:%2u H:%u R:%2u N:%u",
1.1.1.10  root     1232:                        cmdbuf.data.c,
                   1233:                        cmdbuf.data.h,
                   1234:                        cmdbuf.data.r,
                   1235:                        cmdbuf.data.n);
1.1.1.13  root     1236:                putlogn(" EOT:%2u GSL:$%02x DTL:$%02x",
1.1.1.10  root     1237:                        cmdbuf.data.eot,
                   1238:                        cmdbuf.data.gsl,
                   1239:                        cmdbuf.data.dtl);
                   1240:        }
                   1241: 
                   1242:        XferStart(true);
                   1243: }
                   1244: 
                   1245: void
                   1246: FDCDevice::CmdReadData()
                   1247: {
                   1248:        UnitSelect(cmdbuf.data.hd_us);
                   1249: 
                   1250:        mt = cmdbuf.code & 0x80;
                   1251:        mf = cmdbuf.code & 0x40;
                   1252:        sk = cmdbuf.code & 0x20;
                   1253: 
                   1254:        hd = (cmdbuf.data.hd_us & 0x04) >> 2;
                   1255: 
                   1256:        if (loglevel >= 2) {
1.1.1.13  root     1257:                putlogn(" MT:%u MF:%u SK:%u HD:%u", (uint)mt, (uint)mf, (uint)sk, hd);
                   1258:                putlogn(" C:%2u H:%u R:%2u N:%u",
1.1.1.10  root     1259:                        cmdbuf.data.c,
                   1260:                        cmdbuf.data.h,
                   1261:                        cmdbuf.data.r,
                   1262:                        cmdbuf.data.n);
1.1.1.13  root     1263:                putlogn(" EOT:%2u GSL:$%02x DTL:$%02x",
1.1.1.10  root     1264:                        cmdbuf.data.eot,
                   1265:                        cmdbuf.data.gsl,
                   1266:                        cmdbuf.data.dtl);
                   1267:        }
                   1268: 
                   1269:        XferStart(false);
                   1270: }
                   1271: 
                   1272: void
                   1273: FDCDevice::XferStart(bool is_write)
                   1274: {
                   1275:        resbuf.len = sizeof(resbuf.data);
                   1276: 
                   1277:        if (IsReady() == false) {
                   1278:                st0.nr = true;
                   1279:                st0.ic = ST0_IC_AT;
                   1280:                CallAfter(XferEndCallback, 0);
                   1281:                return;
                   1282:        }
                   1283:        unitinfo[us].ready = true;
                   1284:        assert(fdd);
                   1285: 
                   1286:        tc = false;
                   1287:        dack = false;
                   1288:        dreg.Clear();
                   1289:        xfer_start = false;
                   1290:        xfer_enable = false;
                   1291:        xfer_write = is_write;
                   1292:        index_detected = 0;
                   1293: 
                   1294:        fdd->ReadTrack(hd);
                   1295: 
                   1296:        ExecPhase();
                   1297: 
                   1298:        // FDD がバイトを読み書きするちょうど真ん中のタイミングで動くように
                   1299:        // 同期してみる。
1.1.1.11  root     1300:        uint64 now = scheduler->GetVirtTime();
1.1.1.10  root     1301:        uint64 t = 16_usec - ((now - unitinfo[us].ready_time) % 16_usec);
                   1302:        t += 8_usec;
                   1303: 
                   1304:        CallAfter(XferStartCallback, t);
                   1305: }
                   1306: 
                   1307: void
1.1.1.17! root     1308: FDCDevice::XferStartCallback(Event *ev)
1.1.1.10  root     1309: {
                   1310:        // マーク位置まで待つ。
                   1311:        // マークを読んでみてディスパッチ。
                   1312:        // IAM ならなにもしない。
                   1313:        // IDAM なら CHRN を読み出す。データ転送が開始されていない時は
                   1314:        // R を検査して、一致したら転送開始状態にしておく。
                   1315:        // DAM/DDAM のときは、転送開始状態でなければ何もしない。
                   1316:        // 転送開始状態の時はデータ転送またはエラー処理。
                   1317:        // 転送が終わったら CHR を進めて EOT になったら
                   1318:        //  MT=1 で HD=0 のときは裏に行く。
                   1319:        //  そうでなければ EN エラー。
                   1320: 
                   1321:        assert(fdd);
                   1322: 
                   1323:        int dist = fdd->GetDistanceToMark();
                   1324:        if (dist < 0) {
                   1325:                VMPANIC("dist < 0");
                   1326:        }
                   1327:        if (dist == 0) {
                   1328:                uint8 mark = fdd->ReadByte();
1.1.1.17! root     1329:                ev->time = 1 * 16_usec;
1.1.1.10  root     1330:                if (mark == MARK_IDAM) {
                   1331:                        // CHRN と CRC の合計時間を加算
1.1.1.17! root     1332:                        ev->time += (4 + 2) * 16_usec;
1.1.1.10  root     1333: 
                   1334:                        resbuf.data.c = fdd->PeekByte(1);
                   1335:                        resbuf.data.h = fdd->PeekByte(2);
                   1336:                        resbuf.data.r = fdd->PeekByte(3);
                   1337:                        resbuf.data.n = fdd->PeekByte(4);
1.1.1.13  root     1338:                        putlog(2, "IDAM C:%2u H:%u R:%2u N:%u",
1.1.1.10  root     1339:                                resbuf.data.c,
                   1340:                                resbuf.data.h,
                   1341:                                resbuf.data.r,
                   1342:                                resbuf.data.n);
                   1343: 
                   1344:                        // READ ID は最初に発見した ID を返す。
                   1345:                        if ((cmdbuf.code & 0xbf) == 0x0a) {
                   1346:                                putlog(2, "READ ID complete");
                   1347:                                st0.ic = ST0_IC_NT;
1.1.1.17! root     1348:                                CallAfter(XferEndCallback, ev->time);
1.1.1.10  root     1349:                                return;
                   1350:                        }
                   1351: 
                   1352:                        // uPD765 マニュアル pXX に R だけを一致検査すると書いてある。
                   1353:                        // 72065B でどうかは未調査だが、CHRN 4つ比較することにする。
                   1354:                        if ((resbuf.data.c == cmdbuf.data.c) &&
                   1355:                            (resbuf.data.h == cmdbuf.data.h) &&
                   1356:                            (resbuf.data.r == cmdbuf.data.r) &&
                   1357:                            (resbuf.data.n == cmdbuf.data.n))
                   1358:                        {
                   1359:                                putlog(3, "Transfer decided");
                   1360:                                xfer_start = true;
1.1.1.17! root     1361:                                sect_remain = 1U << (7 + resbuf.data.n);
1.1.1.10  root     1362:                                if (cmdbuf.data.n == 0) {
                   1363:                                        xfer_remain = cmdbuf.data.dtl;
                   1364:                                        if (xfer_remain > 128) {
                   1365:                                                xfer_remain = 128;
                   1366:                                        }
                   1367:                                } else {
                   1368:                                        xfer_remain = sect_remain;
                   1369:                                }
                   1370:                                // CRC のぶん
                   1371:                                sect_remain += 2;
                   1372:                        }
                   1373:                } else if (mark == MARK_DAM) {
                   1374:                        xfer_enable = xfer_start;
                   1375:                        if (xfer_enable) {
                   1376:                                putlog(3, "DAM detected. Sector data will be transferred");
                   1377:                                if (xfer_write) {
                   1378:                                        RequestDataByte();
                   1379:                                }
1.1.1.17! root     1380:                                CallAfter(XferDataCallback, ev->time);
1.1.1.10  root     1381:                                return;
                   1382:                        } else {
                   1383:                                putlog(3, "DAM detected. Sector data will be skipped");
                   1384:                        }
                   1385:                } else if (mark == MARK_DDAM) {
                   1386:                        VMPANIC("DDAM (NOT IMPLEMENTED)");
                   1387:                } else if (mark == MARK_IAM) {
                   1388:                        // IAM は何もすることがない
                   1389:                } else {
                   1390:                        // インデックスホールを都合3回検出したらエラー終了
                   1391:                        if (fdd->IsIndex()) {
                   1392:                                index_detected++;
                   1393:                                if (index_detected == 3) {
                   1394:                                        st0.ic = ST0_IC_AT;
                   1395:                                        if (xfer_start) {
                   1396:                                                st1.nd = true;
                   1397:                                        } else {
                   1398:                                                st1.ma = true;
                   1399:                                        }
1.1.1.17! root     1400:                                        CallAfter(XferEndCallback, ev->time);
1.1.1.10  root     1401:                                        return;
                   1402:                                }
                   1403:                        }
                   1404:                }
                   1405:        } else {
                   1406:                // 次のマークまで待つ
1.1.1.17! root     1407:                ev->time = dist * 16_usec;
1.1.1.10  root     1408:        }
1.1.1.11  root     1409:        scheduler->StartEvent(ev);
1.1.1.10  root     1410: }
                   1411: 
                   1412: void
1.1.1.17! root     1413: FDCDevice::XferDataCallback(Event *ev)
1.1.1.10  root     1414: {
                   1415:        assert(fdd);
                   1416: 
1.1.1.17! root     1417:        ev->time = 1 * 16_usec;
1.1.1.10  root     1418: 
                   1419:        // 変態フォーマットをサポートするときはこのへんに追加が必要。
                   1420: 
                   1421:        if (xfer_remain > 0) {
                   1422:                if (__predict_false(xfer_write)) {
1.1.1.13  root     1423:                        uint32 data = PopDreg();
                   1424:                        if ((int32)data >= 0) {
1.1.1.10  root     1425:                                fdd->WriteByte(data);
                   1426:                        } else {
                   1427:                                // XXX: こうじゃなくてエラービットだけ立ててセクタ終了時処理かも
                   1428:                                return;
                   1429:                        }
                   1430:                } else {
                   1431:                        PushDreg(fdd->ReadByte());
                   1432:                }
                   1433:                xfer_remain--;
                   1434:        }
                   1435: 
                   1436:        bool is_eot = false;
                   1437: 
                   1438:        if (sect_remain > 0) {
                   1439:                sect_remain--;
                   1440:        } else {
                   1441:                // セクタデータ転送完了
                   1442:                putlog(3, "Sector data transfer completed");
                   1443:                // TODO: Data CRC チェック
                   1444: 
                   1445:                xfer_start = false;
                   1446:                xfer_enable = false;
                   1447: 
                   1448:                is_eot = (resbuf.data.r >= cmdbuf.data.eot);
                   1449: 
                   1450:                // マルチトラックの処理
                   1451:                if (is_eot) {
                   1452:                        if (xfer_write) {
                   1453:                                fdd->WriteTrack();
                   1454:                        }
                   1455:                        resbuf.data.r = 1;
                   1456:                        if (mt && hd == 0) {
                   1457:                                is_eot = false;
                   1458: 
                   1459:                                // HD=1 の読み込み
                   1460:                                fdd->ReadTrack(1);
                   1461:                        } else {
                   1462:                                resbuf.data.c += 1;
                   1463:                        }
                   1464:                        if (mt) {
                   1465:                                // HD=1 から MT を立てて読み終わると HD=0 になっている
                   1466:                                hd ^= 1;
                   1467:                                resbuf.data.h = hd;
                   1468:                        }
                   1469:                } else {
                   1470:                        resbuf.data.r += 1;
                   1471:                }
                   1472: 
                   1473:                // EOT チェック
                   1474:                if (tc == false) {
                   1475:                        if (is_eot) {
                   1476:                                putlog(3, "EOT reached");
                   1477:                                st1.en = true;
                   1478:                                st0.ic = ST0_IC_AT;
                   1479:                                CallAfter(XferEndCallback, 16_usec);
                   1480:                                return;
                   1481:                        } else {
                   1482:                                // 次のセクタの ID へ継続
                   1483:                                putlog(3, "Multi sector; continue to transfer");
                   1484:                                cmdbuf.data.c = resbuf.data.c;
                   1485:                                cmdbuf.data.h = resbuf.data.h;
                   1486:                                cmdbuf.data.r = resbuf.data.r;
                   1487:                                cmdbuf.data.n = resbuf.data.n;
                   1488:                                CallAfter(XferStartCallback, 16_usec);
                   1489:                                return;
                   1490:                        }
                   1491:                }
                   1492:        }
                   1493: 
                   1494:        if (tc) {
                   1495:                putlog(3, "Done by TC signal");
                   1496:                if (xfer_write) {
                   1497:                        fdd->WriteTrack();
                   1498:                }
                   1499:                st0.ic = ST0_IC_NT;
                   1500:                CallAfter(XferEndCallback, 16_usec);
                   1501:                return;
                   1502:        }
                   1503: 
                   1504:        if (xfer_write && xfer_remain > 0) {
                   1505:                RequestDataByte();
                   1506:        }
1.1.1.11  root     1507:        scheduler->StartEvent(ev);
1.1.1.10  root     1508: }
                   1509: 
                   1510: void
1.1.1.17! root     1511: FDCDevice::XferEndCallback(Event *ev)
1.1.1.10  root     1512: {
                   1513:        st0.hd = hd;
                   1514:        st0.us = us;
                   1515:        resbuf.data.st0 = GetST0();
                   1516:        resbuf.data.st1 = GetST1();
                   1517:        resbuf.data.st2 = GetST2();
                   1518:        ChangeInterrupt();
                   1519:        ResultPhase();
                   1520: }
                   1521: 
                   1522: // READY 入力端子
                   1523: bool
                   1524: FDCDevice::IsReady() const
                   1525: {
                   1526:        return force_ready || (fdd && fdd->GetReady());
                   1527: }
                   1528: 
                   1529: void
                   1530: FDCDevice::CmdRecalibrate()
                   1531: {
                   1532:        UnitSelect(cmdbuf.recalibrate.us);
                   1533: 
                   1534:        if (IsReady() == false) {
                   1535:                st0.nr = true;
                   1536:                st0.ic = ST0_IC_AT;
                   1537:                st0.hd = false;
                   1538:                st0.us = us;
                   1539:                ChangeInterrupt();
                   1540:                CommandPhase();
                   1541:                return;
                   1542:        }
                   1543:        unitinfo[us].ready = true;
                   1544: 
                   1545:        msr.db[us] = true;
                   1546:        unitinfo[us].pcn = 255;
                   1547:        unitinfo[us].ncn = 0;
                   1548:        unitinfo[us].state = UnitState::Recalibrate;
                   1549:        CommandPhase();
                   1550:        StartPoll();
                   1551: }
                   1552: 
                   1553: // シーク終了処理
                   1554: void
                   1555: FDCDevice::SeekEnd()
                   1556: {
                   1557:        putlog(2, "Seek completed");
                   1558:        st0.ic = ST0_IC_NT;
                   1559:        st0.se = true;
                   1560:        st0.us = us;
                   1561:        unitinfo[us].state = UnitState::Idle;
                   1562:        ChangeInterrupt();
                   1563:        CommandPhase();
                   1564: }
1.1       root     1565: 
1.1.1.10  root     1566: void
                   1567: FDCDevice::CmdSenseInterruptStatus()
1.1       root     1568: {
1.1.1.10  root     1569:        // XXX とりあえずここで割り込みを下げる。
                   1570:        //     実際には割り込み状態をラッチして信号線に出すというコマンドで
                   1571:        //     割り込み要因がオフになっていればそれによってネゲートされる
                   1572:        //     ということのようだ。
                   1573: 
                   1574:        // XXX 割り込みが起きていないときは INVALID と同じらしい
                   1575: 
                   1576:        if (st0.ic != ST0_IC_IC) {
                   1577:                // シーク完了割り込みを発生させた Unit のトラックをレポート
                   1578:                resbuf.interrupt_status.pcn = unitinfo[st0.us].pcn;
                   1579:                msr.db[st0.us] = false;
                   1580:                resbuf.len = sizeof(resbuf.interrupt_status);
1.1       root     1581:        } else {
1.1.1.10  root     1582:                resbuf.len = sizeof(resbuf.invalid);
1.1       root     1583:        }
1.1.1.10  root     1584:        resbuf.interrupt_status.st0 = GetST0();
                   1585:        putlog(2, " ST0=$%02x", resbuf.interrupt_status.st0);
                   1586:        ResultPhase();
                   1587: }
                   1588: 
                   1589: void
                   1590: FDCDevice::CmdWriteDeletedData()
                   1591: {
                   1592:        CmdNotImplemented();
                   1593: }
                   1594: 
                   1595: void
                   1596: FDCDevice::CmdReadID()
                   1597: {
                   1598:        UnitSelect(cmdbuf.data.hd_us);
                   1599: 
                   1600:        mf = cmdbuf.code & 0x40;
                   1601:        hd = (cmdbuf.data.hd_us & 0x04) >> 2;
1.1.1.13  root     1602:        putlog(2, " MF:%u HD:%u", (uint)mf, hd);
1.1.1.10  root     1603: 
                   1604:        XferStart(false);
1.1       root     1605: }
1.1.1.10  root     1606: 
                   1607: void
                   1608: FDCDevice::CmdReadDeletedData()
                   1609: {
                   1610:        CmdNotImplemented();
                   1611: }
                   1612: 
                   1613: void
                   1614: FDCDevice::CmdWriteID()
                   1615: {
                   1616:        CmdNotImplemented();
                   1617: }
                   1618: 
                   1619: void
                   1620: FDCDevice::CmdSeek()
                   1621: {
                   1622:        putlog(2, "SEEK start");
                   1623: 
                   1624:        UnitSelect(cmdbuf.seek.us);
                   1625: 
                   1626:        if (IsReady() == false) {
                   1627:                st0.nr = true;
                   1628:                st0.ic = ST0_IC_AT;
                   1629:                st0.hd = false;
                   1630:                st0.us = us;
                   1631:                ChangeInterrupt();
                   1632:                CommandPhase();
                   1633:                return;
                   1634:        }
                   1635:        unitinfo[us].ready = true;
                   1636: 
                   1637:        msr.db[us] = true;
                   1638:        unitinfo[us].ncn = cmdbuf.seek.ncn;
                   1639:        unitinfo[us].state = UnitState::Seek;
1.1.1.13  root     1640:        putlog(2, " NCN:%u", unitinfo[us].ncn);
1.1.1.10  root     1641:        CommandPhase();
                   1642:        StartPoll();
                   1643: }
                   1644: 
                   1645: void
                   1646: FDCDevice::CmdVersion()
                   1647: {
                   1648:        CmdNotImplemented();
                   1649: }
                   1650: 
                   1651: void
                   1652: FDCDevice::CmdScanEqual()
                   1653: {
                   1654:        CmdNotImplemented();
                   1655: }
                   1656: 
                   1657: void
                   1658: FDCDevice::CmdScanLowOrEqual()
                   1659: {
                   1660:        CmdNotImplemented();
                   1661: }
                   1662: 
                   1663: void
                   1664: FDCDevice::CmdScanHighOrEqual()
                   1665: {
                   1666:        CmdNotImplemented();
                   1667: }
                   1668: 
                   1669: void
                   1670: FDCDevice::CmdResetStandby()
                   1671: {
                   1672:        CmdNotImplemented();
                   1673: }
                   1674: 
                   1675: void
                   1676: FDCDevice::CmdSetStandby()
                   1677: {
                   1678:        CmdNotImplemented();
                   1679: }
                   1680: 
                   1681: void
                   1682: FDCDevice::CmdSoftwareReset()
                   1683: {
                   1684:        ResetHard(false);
                   1685: }
                   1686: 
                   1687: void
                   1688: FDCDevice::CmdInvalid()
                   1689: {
                   1690:        CmdNotImplemented();
                   1691: }
                   1692: 
                   1693: void
                   1694: FDCDevice::CmdNotImplemented()
                   1695: {
                   1696:        UnitSelect(cmdbuf.data.hd_us);
                   1697:        hd = (cmdbuf.data.hd_us & 0x04) >> 2;
                   1698: 
                   1699:        putlog(0, "Command %s NOT IMPLEMENTED", cmd->name);
                   1700:        // とりあえずエラー
                   1701:        st0.ic = ST0_IC_AT;
                   1702:        st0.hd = hd;
                   1703:        st0.us = us;
                   1704:        ChangeInterrupt();
                   1705:        CommandPhase();
                   1706: }
                   1707: 
                   1708: // コマンド
                   1709: #define CMD(x) &FDCDevice::Cmd##x
                   1710: /*static*/ const std::vector<FDCDevice::FDCCmd>
                   1711: FDCDevice::cmd_list {
                   1712:        // code mask cmdlen func                                        name
                   1713:        { 0x02, 0xbf, 9, CMD(ReadDiagnostic),           "READ DIAGNOSTIC" },
                   1714:        { 0x03, 0xff, 3, CMD(Specify),                          "SPECIFY" },
                   1715:        { 0x04, 0xff, 2, CMD(SenseDeviceStatus),        "SENSE DEVICE STATUS" },
                   1716:        { 0x05, 0x3f, 9, CMD(WriteData),                        "WRITE DATA" },
                   1717:        { 0x06, 0x1f, 9, CMD(ReadData),                         "READ DATA" },
                   1718:        { 0x07, 0xff, 2, CMD(Recalibrate),                      "RECALIBRATE" },
                   1719:        { 0x08, 0xff, 1, CMD(SenseInterruptStatus),     "SENSE INTERRUPT STATUS" },
                   1720:        { 0x09, 0x3f, 9, CMD(WriteDeletedData),         "WRITE DELETED DATA" },
                   1721:        { 0x0a, 0xbf, 2, CMD(ReadID),                           "READ ID" },
                   1722:        { 0x0c, 0x1f, 9, CMD(ReadDeletedData),          "READ DELETED DATA" },
                   1723:        { 0x0d, 0xbf, 6, CMD(WriteID),                          "WRITE ID" },
                   1724:        { 0x0f, 0xff, 3, CMD(Seek),                                     "SEEK" },
                   1725:        { 0x10, 0x1f, 1, CMD(Version),                          "VERSION" },
                   1726:        { 0x11, 0x1f, 9, CMD(ScanEqual),                        "SCAN EQUAL" },
                   1727:        { 0x19, 0x1f, 9, CMD(ScanLowOrEqual),           "SCAN LOW OR EQUAL" },
                   1728:        { 0x1d, 0x1f, 9, CMD(ScanHighOrEqual),          "SCAN HIGH OR EQUAL" },
                   1729:        { 0x34, 0xff, 1, CMD(ResetStandby),                     "RESET STANDBY" },
                   1730:        { 0x35, 0xff, 1, CMD(SetStandby),                       "SET STANDBY" },
                   1731:        { 0x36, 0xff, 1, CMD(SoftwareReset),            "SOFTWARE RESET" },
                   1732: 
                   1733:        // リストの最後に Invalid を置く。
                   1734:        // code=0x00, mask=0x00 なので必ず一致する。
                   1735:        { 0x00, 0x00, 1, CMD(Invalid),                          "invalid" },
                   1736: };

unix.superglobalmegacorp.com

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