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

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

unix.superglobalmegacorp.com

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