Annotation of nono/vm/fdd.cpp, revision 1.1

1.1     ! root        1: //
        !             2: // nono
        !             3: // Copyright (C) 2022 nono project
        !             4: // Licensed under nono-license.txt
        !             5: //
        !             6: 
        !             7: //
        !             8: // FDD
        !             9: //
        !            10: 
        !            11: // ディスクメディアは 77~ 個のシリンダで構成される。
        !            12: // シリンダは両面の場合 2 本のトラックで構成される。
        !            13: // トラックは 8~ 個のセクタで構成される。
        !            14: // セクタは 128~ バイトで構成される。
        !            15: 
        !            16: #include "fdd.h"
        !            17: #include "config.h"
        !            18: #include "fdc.h"
        !            19: #include "mainapp.h"
        !            20: #include "pedec.h"
        !            21: #include "scheduler.h"
        !            22: #include "uimessage.h"
        !            23: 
        !            24: #define MFM_TRACK_HEADER       (80 + 12 + 3 + 1 + 50)
        !            25: #define MFM_SECTOR_HEADER      (12 + 3 + 1 + 4 + 2 + 22 + 12 + 3 + 1 + 2)
        !            26: #define MFM_TRACK_RAW_SIZE     (10416)
        !            27: 
        !            28: // トラックデータ追加
        !            29: void
        !            30: FDTrack::Append(uint8 data)
        !            31: {
        !            32:        buf.push_back(data);
        !            33: }
        !            34: 
        !            35: void
        !            36: FDTrack::Append(uint8 data, int count)
        !            37: {
        !            38:        for (int i = 0; i < count; i++) {
        !            39:                Append(data);
        !            40:        }
        !            41: }
        !            42: 
        !            43: // トラックデータにマークを追加
        !            44: void
        !            45: FDTrack::AppendMark(uint8 data)
        !            46: {
        !            47:        // マーク位置を記録
        !            48:        markpos.push_back(buf.size());
        !            49:        Append(data);
        !            50: }
        !            51: 
        !            52: // トラックデータの記録開始
        !            53: void
        !            54: FDTrack::Begin()
        !            55: {
        !            56:        markpos.push_back(buf.size());
        !            57: }
        !            58: 
        !            59: // トラックデータの記録終了
        !            60: void
        !            61: FDTrack::End()
        !            62: {
        !            63:        assert(buf.size() == MFM_TRACK_RAW_SIZE);
        !            64: 
        !            65:        markpos.push_back(buf.size());
        !            66: }
        !            67: 
        !            68: void
        !            69: FDTrack::Clear()
        !            70: {
        !            71:        buf.clear();
        !            72:        markpos.clear();
        !            73: }
        !            74: 
        !            75: 
        !            76: 
        !            77: 
        !            78: FDDDevice::FDDDevice(int unit_)
        !            79:        : inherited("FDD")
        !            80: {
        !            81:        unit = unit_;
        !            82:        SetName(string_format("FDD%d", unit));
        !            83:        ClearAlias();
        !            84:        AddAlias(GetName());
        !            85: 
        !            86:        event.func = ToEventCallback(&FDDDevice::EventCallback);
        !            87:        event.Regist(GetName());
        !            88: }
        !            89: 
        !            90: FDDDevice::~FDDDevice()
        !            91: {
        !            92: }
        !            93: 
        !            94: // 初期化
        !            95: bool
        !            96: FDDDevice::Init()
        !            97: {
        !            98:        const std::string keybody = string_format("fd%d-", unit);
        !            99:        const std::string imgkey = keybody + "image";
        !           100:        const std::string wikey  = keybody + "writeignore";
        !           101: 
        !           102:        // デバイス種別(v[0])とパス(v[1])に分解。
        !           103:        // デバイス種別はすでにチェックしてあるのでここでは不要。
        !           104:        const ConfigItem& itemimg = gConfig->Find(imgkey);
        !           105:        const std::string& filename = itemimg.AsString();
        !           106: 
        !           107:        // UI からの通知を受け取るイベントを用意
        !           108:        gScheduler->ConnectMessage(MessageID::FDD_LOAD(unit), this,
        !           109:                ToMessageCallback(&FDDDevice::LoadCallback));
        !           110:        gScheduler->ConnectMessage(MessageID::FDD_UNLOAD(unit), this,
        !           111:                ToMessageCallback(&FDDDevice::UnloadCallback));
        !           112: 
        !           113:        // オープン前に writeignore をチェック。
        !           114:        // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
        !           115:        // 行わないといけない。うーん。
        !           116:        const ConfigItem& wi = gConfig->Find(wikey);
        !           117:        write_ignore = wi.AsInt();
        !           118: 
        !           119:        // 設定時点でファイルが指定されていればオープン
        !           120:        if (filename.empty() == false) {
        !           121:                std::string path;
        !           122:                if (filename[0] == '/') {
        !           123:                        path = filename;
        !           124:                } else {
        !           125:                        // 相対パスなら VM ディレクトリから
        !           126:                        path = gMainApp.GetVMDir() + filename;
        !           127:                }
        !           128: 
        !           129:                if (LoadDisk(path) == false) {
        !           130:                        return false;
        !           131:                }
        !           132:        } else {
        !           133:                // メディアがロードされないままでも起動時は必ず通知を投げる。
        !           134:                // ステータスパネルはこの UIMessage でのみ状態を更新するため。
        !           135:                MediaChanged();
        !           136:        }
        !           137: 
        !           138:        return true;
        !           139: }
        !           140: 
        !           141: // リセット
        !           142: void
        !           143: FDDDevice::ResetHard(bool poweron)
        !           144: {
        !           145:        if (poweron) {
        !           146:                motor_state = STOPPED;
        !           147:        }
        !           148: 
        !           149:        // XXX どこでやる?
        !           150:        eject_enable = true;
        !           151: }
        !           152: 
        !           153: // モニタの下請け (fdc から呼ばれる)。
        !           154: // y = 11 以降が各ドライブの状態で、このうち x = 26 以降が FDD の担当。
        !           155: // y = 16 以降は FDD のアクセスマップ。
        !           156: void
        !           157: FDDDevice::MonitorUpdateFDD(TextScreen& screen, int hd)
        !           158: {
        !           159: /*        1         2         3         4         5         6         7
        !           160: 0123456789012345678901234567890123456789012345678901234567890123456789012345678
        !           161:                           ready accled eject   fmt C  H  R  N
        !           162:                           stop  blink  disable 2HC 99 1 99 2(1024)
        !           163: Unit0: HD=1 cyl=nn/nn pos=12345/12345
        !           164: XX>01234567890123456 01234567890123456| XX>01234567890123456 01234567890123456|
        !           165: */
        !           166: 
        !           167:        int uy = 11 + unit;
        !           168:        int ux = 26;
        !           169: 
        !           170:        // モータ状態
        !           171:        const char * const motor_state_str[] = {
        !           172:                "Stop",
        !           173:                "Down",
        !           174:                "Up",
        !           175:                "Ready",
        !           176:        };
        !           177:        screen.Print(ux, uy, "%s", motor_state_str[(int)motor_state]);
        !           178: 
        !           179:        // アクセス LED 状態
        !           180:        const char * const access_led_str[] = {
        !           181:                "Off",
        !           182:                "Green",
        !           183:                "Red",
        !           184:                "Blink",
        !           185:        };
        !           186:        screen.Print(ux + 6, uy, "%s", access_led_str[(int)access_led]);
        !           187: 
        !           188:        // イジェクト可否 (LED 状態)
        !           189:        const char *eject_str;
        !           190:        if (medium_loaded) {
        !           191:                if (eject_enable) {
        !           192:                        eject_str = "Enable";
        !           193:                } else {
        !           194:                        eject_str = "Disable";
        !           195:                }
        !           196:        } else {
        !           197:                eject_str = "Off";
        !           198:        }
        !           199:        screen.Puts(ux + 13, uy, eject_str);
        !           200: 
        !           201:        if (medium_loaded) {
        !           202:                // フォーマットと CHRN
        !           203:                screen.Print(ux + 20, uy, "%4s %2d %d %2d %d(%d)",
        !           204:                        format_name.c_str(), ncyl, nhead, nsect, format_n, sectsize);
        !           205:        }
        !           206: 
        !           207:        uy = 16 + (unit / 2) * 10;
        !           208:        ux = (unit % 2) * 41;
        !           209: 
        !           210:        screen.Print(ux, uy++, "Unit %d", unit);
        !           211:        // 横ガイド
        !           212:        for (int i = 0; i < 8; i++) {
        !           213:                screen.Puts(ux + 2,  uy + i, "|");
        !           214:                screen.Puts(ux + 38, uy + i, "|");
        !           215:        }
        !           216: 
        !           217:        // メディアが入ってなければここで終わり
        !           218:        if (IsMediumLoaded() == false) {
        !           219:                return;
        !           220:        }
        !           221: 
        !           222:        TA selattr = selected ? TA::Em : TA::Normal;
        !           223:        screen.Print(ux, uy - 1, selattr, "Unit %d: HD=%d cyl=%2d/%d pos=%5d/%d",
        !           224:                unit, hd, cylinder, ncyl, pos, MFM_TRACK_RAW_SIZE);
        !           225: 
        !           226:        // ヘッドの現在位置。
        !           227:        // 横は全長を当分割、縦は1行で10トラック固定にする
        !           228:        int headx = (double)pos / MFM_TRACK_RAW_SIZE * 17;
        !           229:        int heady = (double)cylinder / 10;
        !           230: 
        !           231:        // 片面ディスクはサポートしていないが一応
        !           232:        for (int h = 0; h < nhead; h++) {
        !           233:                for (int i = 0; i < 8; i++) {
        !           234:                        TA trkattr = (__predict_false(selected && i == heady && h == hd))
        !           235:                                ? TA::Em : TA::Normal;
        !           236:                        screen.Puts(ux + h * 18 + 3, uy + i, trkattr,
        !           237:                        //   01234567890123456
        !           238:                                ".................");
        !           239:                }
        !           240:        }
        !           241: 
        !           242:        // ヘッド位置
        !           243:        screen.Print(ux, uy + heady, selattr, "%2d>", cylinder);
        !           244:        screen.Puts(ux + hd * 18 + 3 + headx, uy + heady, selattr, "#");
        !           245:        // 裏のヘッド位置
        !           246:        screen.Puts(ux + (1 - hd) * 18 + 3 + headx, uy + heady, selattr, "+");
        !           247: }
        !           248: 
        !           249: // READY 信号
        !           250: bool
        !           251: FDDDevice::GetReady() const
        !           252: {
        !           253:        return (motor_state == READY);
        !           254: }
        !           255: 
        !           256: // TRACK00 信号
        !           257: bool
        !           258: FDDDevice::GetTrack0() const
        !           259: {
        !           260:        return (cylinder == 0);
        !           261: }
        !           262: 
        !           263: bool
        !           264: FDDDevice::GetWriteProtect() const
        !           265: {
        !           266:        return write_mode == RW::WriteProtect;
        !           267: }
        !           268: 
        !           269: void
        !           270: FDDDevice::DoSeek(int dir)
        !           271: {
        !           272:        if ((dir >= 0 && cylinder < 80) || (dir < 0 && cylinder > 0)) {
        !           273:                cylinder += dir;
        !           274:        }
        !           275: }
        !           276: 
        !           277: // ドライブセレクト入力 (DRIVE_SELECT, DISK_TYPE_SELECT 信号)
        !           278: void
        !           279: FDDDevice::SetDriveSelect(uint sel, bool is_2dd_)
        !           280: {
        !           281:        if (is_2dd_) {
        !           282:                putlog(0, "Select 2DD (NOT IMPLEMENTED)");
        !           283:        }
        !           284: 
        !           285:        selected = (sel == unit);
        !           286: 
        !           287:        ChangeStatus();
        !           288: }
        !           289: 
        !           290: // MOTOR_ON 信号入力
        !           291: void
        !           292: FDDDevice::SetMotorOn(bool motor_on_)
        !           293: {
        !           294:        motor_on = motor_on_;
        !           295:        ChangeStatus();
        !           296: }
        !           297: 
        !           298: void
        !           299: FDDDevice::EventCallback(Event& ev)
        !           300: {
        !           301:        // モータが回転している間は1バイトずつ進める
        !           302:        pos++;
        !           303:        if (pos >= MFM_TRACK_RAW_SIZE) {
        !           304:                pos = 0;
        !           305:        }
        !           306: 
        !           307:        switch (motor_state) {
        !           308:         case READY:
        !           309:                gScheduler->StartEvent(ev);
        !           310:                break;
        !           311: 
        !           312:         case SPIN_UP:
        !           313:                // 増速中
        !           314:                if (event.time == 16_usec) {
        !           315:                        motor_state = READY;
        !           316:                        // READY になったので FDC に通知
        !           317:                        gFDC->ReadyChanged(unit, true);
        !           318:                } else {
        !           319:                        if (event.time < 16_usec) {
        !           320:                                // オーバーシュート対応
        !           321:                                event.time = 16_usec;
        !           322:                        } else {
        !           323:                                event.time /= 2;
        !           324:                        }
        !           325:                        putlog(4, "SPIN_UP next=%uusec", (uint)(event.time / 1_usec));
        !           326:                }
        !           327:                gScheduler->StartEvent(ev);
        !           328:                break;
        !           329: 
        !           330:         case SPIN_DOWN:
        !           331:                if (event.time >= 262144_usec) {
        !           332:                        motor_state = STOPPED;
        !           333:                        // イベント停止、ここでは time も 0 クリアしておく。
        !           334:                        event.time = 0;
        !           335:                } else {
        !           336:                        event.time *= 2;
        !           337:                        gScheduler->StartEvent(ev);
        !           338:                        putlog(4, "SPIN_DOWN next=%uusec", (uint)(event.time / 1_usec));
        !           339:                }
        !           340:                break;
        !           341: 
        !           342:         default:
        !           343:                break;
        !           344:        }
        !           345: }
        !           346: 
        !           347: // ドライブの状態が変わった。
        !           348: // メディア挿入、排出、ドライブセレクト変更など。
        !           349: void
        !           350: FDDDevice::ChangeStatus()
        !           351: {
        !           352:        bool active = medium_loaded && motor_on;
        !           353: 
        !           354:        // モータ動作の決定
        !           355:        if (IsMotorActive() == false && active) {
        !           356:                // ディスクが挿入されていて、オフ -> オン
        !           357:                DoMotorOn();
        !           358:        } else if (IsMotorActive() && active == false) {
        !           359:                // オン -> オフ
        !           360:                DoMotorOff();
        !           361:        }
        !           362: 
        !           363:        // アクセス LED の決定
        !           364:        if (active && selected) {
        !           365:                access_led = LED::RED;
        !           366:        } else {
        !           367:                if (medium_loaded) {
        !           368:                        access_led = LED::GREEN;
        !           369:                } else {
        !           370:                        if (is_blink) {
        !           371:                                access_led = LED::BLINK;
        !           372:                        } else {
        !           373:                                access_led = LED::OFF;
        !           374:                        }
        !           375:                }
        !           376:        }
        !           377: }
        !           378: 
        !           379: void
        !           380: FDDDevice::DoMotorOn()
        !           381: {
        !           382:        putlog(1, "Motor On");
        !           383: 
        !           384:        if (motor_state == STOPPED) {
        !           385:                // 始動。
        !           386:                // 262144_usec/1バイトの回転速度から2倍ずつ速くしていって
        !           387:                // 16_usec/1バイトまで上げると定常状態とする。
        !           388:                // 仕様では 500msec で READY になるとあるが、ここでは 2 のべき乗を
        !           389:                // 使っているので、約 524msec かかる。細かいことは気にしない。
        !           390:                motor_state = SPIN_UP;
        !           391:                event.time = 262144_usec;
        !           392:                putlog(4, "SPIN_UP next=%uusec", (uint)(event.time / 1_usec));
        !           393:        } else if (motor_state == SPIN_DOWN) {
        !           394:                // 細かいことは無視して、増速に転じる
        !           395:                motor_state = SPIN_UP;
        !           396:        } else {
        !           397:                VMPANIC("DoMotorOn called on motor_state=%d", (int)motor_state);
        !           398:        }
        !           399: 
        !           400:        gScheduler->RestartEvent(event);
        !           401: }
        !           402: 
        !           403: void
        !           404: FDDDevice::DoMotorOff()
        !           405: {
        !           406:        putlog(1, "Motor Off");
        !           407: 
        !           408:        if (medium_loaded == false) {
        !           409:                // 物理的には多少モータは慣性回転するはずではあるが、ディスクは
        !           410:                // もうないので。
        !           411:                motor_state = STOPPED;
        !           412:                gFDC->ReadyChanged(unit, false);
        !           413:        } else if (motor_state == READY) {
        !           414:                // 減速に転じる。
        !           415:                // 細かいことは気にせず増速と同様に倍々で減速していくので停止まで
        !           416:                // 約 524msec かかる。細かいことは気にしない。
        !           417:                motor_state = SPIN_DOWN;
        !           418:                // 定常回転 (READY) ではなくなった
        !           419:                gFDC->ReadyChanged(unit, false);
        !           420:        } else if (motor_state == SPIN_UP) {
        !           421:                // 細かいことは無視して、減速に転じる。
        !           422:                motor_state = SPIN_DOWN;
        !           423:        } else {
        !           424:                VMPANIC("DoMotorOff called on motor_state=%d", (int)motor_state);
        !           425:        }
        !           426: 
        !           427:        gScheduler->RestartEvent(event);
        !           428: }
        !           429: 
        !           430: // ドライブコントロールのセット
        !           431: // DRIVE # が '1' -> '0' に変化したときのみ呼び出される。
        !           432: void
        !           433: FDDDevice::SetDriveCtrl(uint8 data)
        !           434: {
        !           435:        putlog(2, "Drive Control <- $%02x", data);
        !           436: 
        !           437:        // ログレベルが 1 なら変化時だけ、2 以上なら試行を全部表示?
        !           438:        auto islog = [&](bool expr) {
        !           439:                if (loglevel >= 2) {
        !           440:                        return true;
        !           441:                } else if (loglevel == 1) {
        !           442:                        if (expr) {
        !           443:                                return true;
        !           444:                        }
        !           445:                }
        !           446:                return false;
        !           447:        };
        !           448: 
        !           449:        // アクセスランプ点滅 (メディアが入っていない時のみ)
        !           450:        if (medium_loaded == false) {
        !           451:                // LED を点滅させておいてディスクを入れたら LED は点灯になるはず。
        !           452:                // そのままの状態でディスクを抜いたとき LED が点滅に戻るのか
        !           453:                // 消灯するのかは未調査。現状は点滅に戻るように実装してある。
        !           454: 
        !           455:                if ((data & 0x80)) {
        !           456:                        if (islog(is_blink == false)) {
        !           457:                                putlogn("LED Blink");
        !           458:                        }
        !           459:                        blink_start = gScheduler->GetVirtTime();
        !           460:                        is_blink = true;
        !           461:                } else {
        !           462:                        if (islog(is_blink == true)) {
        !           463:                                putlogn("LED Off");
        !           464:                        }
        !           465:                        is_blink = false;
        !           466:                }
        !           467:        } else {
        !           468:                if ((data & 0x40)) {
        !           469:                        if (islog(eject_enable == true)) {
        !           470:                                putlogn("Eject button disabled");
        !           471:                        }
        !           472:                        eject_enable = false;
        !           473:                } else {
        !           474:                        if (islog(eject_enable == false)) {
        !           475:                                putlogn("Eject button enabled");
        !           476:                        }
        !           477:                        eject_enable = true;
        !           478:                }
        !           479: 
        !           480:                if ((data & 0x20)) {
        !           481:                        if (islog(medium_loaded == true)) {
        !           482:                                putlogn("Eject");
        !           483:                        }
        !           484:                        UnloadDisk(true);
        !           485:                }
        !           486:        }
        !           487:        ChangeStatus();
        !           488: }
        !           489: 
        !           490: // 1 バイト取得
        !           491: uint8
        !           492: FDDDevice::ReadByte()
        !           493: {
        !           494:        return trackbuf.buf[pos];
        !           495: }
        !           496: 
        !           497: // 1 バイト書き込み
        !           498: void
        !           499: FDDDevice::WriteByte(uint8 data)
        !           500: {
        !           501:        trackbuf.buf[pos] = data;
        !           502: }
        !           503: 
        !           504: // 折り返してはいけない
        !           505: uint8
        !           506: FDDDevice::PeekByte(int offset) const
        !           507: {
        !           508:        return trackbuf.buf[pos + offset];
        !           509: }
        !           510: 
        !           511: // 次のマークまたはインデックスホールまでのバイト距離を返す。
        !           512: // 見つからないときは -1 を返す。
        !           513: int
        !           514: FDDDevice::GetDistanceToMark() const
        !           515: {
        !           516:        if (pos == 0) {
        !           517:                return 0;
        !           518:        }
        !           519:        for (int i = 0; i < trackbuf.markpos.size(); i++) {
        !           520:                if (pos <= trackbuf.markpos[i]) {
        !           521:                        return trackbuf.markpos[i] - pos;
        !           522:                }
        !           523:        }
        !           524:        return -1;
        !           525: }
        !           526: 
        !           527: // 現在のシリンダ位置の、hd で指定したトラックを読み込む
        !           528: void
        !           529: FDDDevice::ReadTrack(int hd)
        !           530: {
        !           531:        uint32 lba = (cylinder * nhead + hd) * nsect;
        !           532: 
        !           533:        uint32 start = lba * sectsize;
        !           534:        uint32 len = nsect * sectsize;
        !           535: 
        !           536:        std::vector<uint8> buf(len);
        !           537:        image.Read(buf.data(), start, len);
        !           538: 
        !           539:        int bufpos = 0;
        !           540: 
        !           541:        // MFM のみサポート
        !           542: 
        !           543:        trackbuf.Clear();
        !           544:        trackbuf.lba = lba;
        !           545: 
        !           546:        // トラックデータ
        !           547:        // 本当は FDC が SYNC を検出しているのだが、
        !           548:        // AppendSync() によって SYNC の位置を別途記録することで対応している。
        !           549: 
        !           550:        trackbuf.Begin();
        !           551: 
        !           552:        // トラックヘッダ
        !           553:        trackbuf.Append(0x4e, 80);              // Gap4a
        !           554:        trackbuf.Append(0x00, 12);              // SYNC
        !           555:        trackbuf.Append(0xc2, 3);               // IAM
        !           556:        trackbuf.AppendMark(MARK_IAM);  // IAM マーク位置
        !           557:        trackbuf.Append(0x4e, 50);              // Gap1
        !           558: 
        !           559:        for (int i = 0; i < nsect; i++) {
        !           560:                uint8 c = cylinder;                     // C は 0 始まり
        !           561:                uint8 h = hd;                           // H は 0 か 1
        !           562:                uint8 r = i + 1;                        // R は 1 始まり
        !           563:                uint8 n = format_n;
        !           564:                uint8 crc[2] {};                        // XXX CRC は未実装
        !           565:                uint8 datacrc[2] {};
        !           566: 
        !           567:                trackbuf.Append(0x00, 12);              // SYNC
        !           568:                trackbuf.Append(0xa1, 3);               // IDAM
        !           569:                trackbuf.AppendMark(MARK_IDAM);
        !           570:                trackbuf.Append(c);
        !           571:                trackbuf.Append(h);
        !           572:                trackbuf.Append(r);
        !           573:                trackbuf.Append(n);
        !           574:                trackbuf.Append(crc[0]);
        !           575:                trackbuf.Append(crc[1]);
        !           576:                trackbuf.Append(0x4e, 22);      // Gap2
        !           577: 
        !           578:                trackbuf.Append(0x00, 12);              // SYNC
        !           579:                trackbuf.Append(0xa1, 3);       // DAM/DDAM
        !           580:                if (true/*dam*/) {
        !           581:                        trackbuf.AppendMark(MARK_DAM);
        !           582:                } else {
        !           583:                        trackbuf.AppendMark(MARK_DDAM);
        !           584:                }
        !           585: 
        !           586:                for (int j = 0; j < sectsize; j++) {
        !           587:                        trackbuf.Append(buf[bufpos++]);
        !           588:                }
        !           589:                trackbuf.Append(datacrc[0]);
        !           590:                trackbuf.Append(datacrc[1]);
        !           591: 
        !           592:                trackbuf.Append(0, gap3);       // Gap3
        !           593:        }
        !           594:        trackbuf.Append(0, gap4b);              // Gap4b
        !           595: 
        !           596:        trackbuf.End();                 // トラック終わり
        !           597: }
        !           598: 
        !           599: // トラックバッファをイメージにフラッシュ
        !           600: void
        !           601: FDDDevice::WriteTrack()
        !           602: {
        !           603:        uint32 lba = trackbuf.lba;
        !           604: 
        !           605:        uint32 start = lba * sectsize;
        !           606:        uint32 len = nsect * sectsize;
        !           607: 
        !           608:        std::vector<uint8> buf(len);
        !           609: 
        !           610:        // MFM のみサポート
        !           611: 
        !           612:        int bufpos = 0;
        !           613: 
        !           614:        // トラックデータからデータを再構成
        !           615: 
        !           616:        for (int i = 0; i < trackbuf.markpos.size(); i++) {
        !           617:                int p = trackbuf.markpos[i];
        !           618:                if (p < trackbuf.buf.size()) {
        !           619:                        uint8 d = trackbuf.buf[p];
        !           620:                        if (d == MARK_DAM || d == MARK_DDAM) {
        !           621:                                p++;
        !           622:                                for (int j = 0; j < sectsize; j++) {
        !           623:                                        buf[bufpos++] = trackbuf.buf[p++];
        !           624:                                }
        !           625:                        }
        !           626:                }
        !           627:        }
        !           628:        if (bufpos != len) {
        !           629:                VMPANIC("invalid track");
        !           630:        }
        !           631: 
        !           632:        image.Write(buf.data(), start, len);
        !           633: }
        !           634: 
        !           635: // バックエンドのディスクイメージを開く。
        !           636: // 成功すれば true、失敗すれば false を返す。(ロードされれば true を返す
        !           637: // のではないが、medium_loaded が同じ値になるので使いまわしている)
        !           638: bool
        !           639: FDDDevice::LoadDisk(const std::string& pathname_)
        !           640: {
        !           641:        bool ejected;
        !           642:        off_t size;
        !           643:        bool w_ok;
        !           644:        bool read_only;
        !           645: 
        !           646:        // すでにあればクローズ (ここでは通知は行わない)
        !           647:        ejected = UnloadDisk_internal();
        !           648: 
        !           649:        // 新しいイメージをオープン(する準備)
        !           650:        pathname = pathname_;
        !           651:        if (image.CreateHandler(pathname) == false) {
        !           652:                goto done;
        !           653:        }
        !           654: 
        !           655:        if (image.GetInfo(&size, &w_ok) == false) {
        !           656:                goto done;
        !           657:        }
        !           658: 
        !           659:        if (size == 1261568) {
        !           660:                // Human68k 2HD フォーマットとみなす
        !           661:                // 77track, 2side, 8sector, 1024bytes/sector
        !           662:                format_name = "2HD";
        !           663:                ncyl = 77;
        !           664:                nhead = 2;
        !           665:                nsect = 8;
        !           666:                sectsize = 1024;
        !           667:                format_n = 3;
        !           668:                gap3 = 0x74;
        !           669:        } else if (size == 1228800) {
        !           670:                // NetBSD 2HC フォーマットとみなす
        !           671:                // 80track, 2side, 15sector, 512bytes/sector
        !           672:                format_name = "2HC";
        !           673:                ncyl = 80;
        !           674:                nhead = 2;
        !           675:                nsect = 15;
        !           676:                sectsize = 512;
        !           677:                format_n = 2;
        !           678:                gap3 = 0x54;
        !           679:        } else {
        !           680:                warnx("%s: Unsuppoted image, size(%ju)",
        !           681:                        pathname.c_str(), (uintmax_t)size);
        !           682:                goto done;
        !           683:        }
        !           684: 
        !           685:        gap4b = (MFM_TRACK_RAW_SIZE - MFM_TRACK_HEADER)
        !           686:                - (sectsize + MFM_SECTOR_HEADER + gap3) * nsect;
        !           687: 
        !           688:        // 書き込みモードをここで確定
        !           689:        if (w_ok) {
        !           690:                if (write_ignore) {
        !           691:                        write_mode = RW::WriteIgnore;
        !           692:                } else {
        !           693:                        write_mode = RW::Writeable;
        !           694:                }
        !           695:        } else {
        !           696:                write_mode = RW::WriteProtect;
        !           697:        }
        !           698: 
        !           699:        // ここでオープン
        !           700:        read_only = (GetWriteMode() != RW::Writeable);
        !           701:        if (image.Open(read_only, write_ignore) == false) {
        !           702:                goto done;
        !           703:        }
        !           704: 
        !           705:        // 書き込み無視なら一応ログ出力
        !           706:        if (GetWriteMode() == RW::WriteIgnore) {
        !           707:                putmsg(0, "write is ignored");
        !           708:        }
        !           709: 
        !           710:        medium_loaded = true;
        !           711:        putmsg(0, "%s format", format_name.c_str());
        !           712:        putlog(1, "Medium loaded");
        !           713: 
        !           714:        // オートリキャリブレート
        !           715:        cylinder = 0;
        !           716: 
        !           717:  done:
        !           718:        if (medium_loaded == false) {
        !           719:                // ここで状態をクリアする
        !           720:                Clear();
        !           721:        }
        !           722: 
        !           723:        // 変化があれば
        !           724:        if (ejected || medium_loaded) {
        !           725:                ChangeStatus();
        !           726:                gPEDEC->AssertINT(this);
        !           727:                // 通知
        !           728:                MediaChanged();
        !           729:        }
        !           730: 
        !           731:        return medium_loaded;
        !           732: }
        !           733: 
        !           734: // バックエンドのディスクイメージを閉じる。
        !           735: // オープンされてなければ何もしない。
        !           736: void
        !           737: FDDDevice::UnloadDisk(bool force)
        !           738: {
        !           739:        if (eject_enable || force) {
        !           740:                if (UnloadDisk_internal()) {
        !           741:                        // 実際に閉じたら通知
        !           742:                        MediaChanged();
        !           743:                }
        !           744:        }
        !           745: }
        !           746: 
        !           747: // バックエンドのディスクイメージを閉じる (内部用)。
        !           748: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
        !           749: // ここでは通知は行わない。
        !           750: bool
        !           751: FDDDevice::UnloadDisk_internal()
        !           752: {
        !           753:        if ((bool)image == false) {
        !           754:                return false;
        !           755:        }
        !           756: 
        !           757:        gPEDEC->AssertINT(this);
        !           758: 
        !           759:        Clear();
        !           760: 
        !           761:        // 取り出し禁止はされてても解除する
        !           762:        if (eject_enable == false) {
        !           763:                eject_enable = true;
        !           764:        }
        !           765: 
        !           766:        putlog(1, "Medium unloaded");
        !           767:        return true;
        !           768: }
        !           769: 
        !           770: // ディスク状態をクリアする
        !           771: // (メディアを取り外した時など)
        !           772: void
        !           773: FDDDevice::Clear()
        !           774: {
        !           775:        medium_loaded = false;
        !           776:        ChangeStatus();
        !           777:        image.Close();
        !           778:        pathname.clear();
        !           779:        write_mode = RW::Writeable;
        !           780: }
        !           781: 
        !           782: // メディア状態変更を UI に通知する
        !           783: void
        !           784: FDDDevice::MediaChanged() const
        !           785: {
        !           786:        UIMessage::Post(UIMessage::FDD_MEDIA_CHANGE, unit);
        !           787: }
        !           788: 
        !           789: // メディアを挿入する。
        !           790: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
        !           791: void
        !           792: FDDDevice::LoadDiskUI(const std::string& pathname_)
        !           793: {
        !           794:        new_pathname = pathname_;
        !           795:        gScheduler->SendMessage(MessageID::FDD_LOAD(unit));
        !           796: }
        !           797: 
        !           798: // メディアを排出する。
        !           799: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
        !           800: void
        !           801: FDDDevice::UnloadDiskUI(bool force)
        !           802: {
        !           803:        gScheduler->SendMessage(MessageID::FDD_UNLOAD(unit), force);
        !           804: }
        !           805: 
        !           806: // メディア挿入メッセージコールバック
        !           807: void
        !           808: FDDDevice::LoadCallback(MessageID msgid, uint32 arg)
        !           809: {
        !           810:        if (LoadDisk(new_pathname) == false) {
        !           811:                // 失敗したら UI に通知
        !           812:                UIMessage::Post(UIMessage::FDD_MEDIA_FAILED);
        !           813:        }
        !           814: }
        !           815: 
        !           816: // メディア排出メッセージコールバック
        !           817: void
        !           818: FDDDevice::UnloadCallback(MessageID msgid, uint32 arg)
        !           819: {
        !           820:        bool force = arg;
        !           821:        UnloadDisk(force);
        !           822: }
        !           823: 
        !           824: // アクセス LED の状態を取得。
        !           825: // ここで返すのは OFF, GREEN, RED の3通り。
        !           826: FDDDevice::LED
        !           827: FDDDevice::GetAccessLED() const
        !           828: {
        !           829:        if (access_led == LED::BLINK) {
        !           830:                // 点滅状態はここで作成。
        !           831:                // 点滅開始から 0.5秒は点灯、次の 0.5秒を消灯とする。
        !           832:                // XXX 間隔は適当
        !           833:                uint64 time = gScheduler->GetVirtTime() - blink_start;
        !           834:                time %= 1_sec;
        !           835:                if (time < 500_msec) {
        !           836:                        return LED::GREEN;
        !           837:                } else {
        !           838:                        return LED::OFF;
        !           839:                }
        !           840:        } else {
        !           841:                // それ以外はそのまま返せる
        !           842:                return access_led;
        !           843:        }
        !           844: }
        !           845: 
        !           846: // イジェクト LED の状態を取得。点灯なら true
        !           847: bool
        !           848: FDDDevice::GetEjectLED() const
        !           849: {
        !           850:        // メディアが挿入されていて、イジェクト許可なら点灯。
        !           851:        return (medium_loaded && eject_enable);
        !           852: }

unix.superglobalmegacorp.com

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