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

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

unix.superglobalmegacorp.com

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