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

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

unix.superglobalmegacorp.com

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