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

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

unix.superglobalmegacorp.com

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