Annotation of nono/vm/fdd.cpp, revision 1.1.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.