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

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

unix.superglobalmegacorp.com

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