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

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_;
                     94: }
                     95: 
1.1.1.2   root       96: // デストラクタ
1.1       root       97: FDDDevice::~FDDDevice()
                     98: {
                     99: }
                    100: 
                    101: // 初期化
                    102: bool
                    103: FDDDevice::Init()
                    104: {
1.1.1.2   root      105:        fdc = GetFDCDevice();
                    106:        pedec = GetPEDECDevice();
                    107: 
1.1.1.4   root      108:        const std::string keybody = string_format("fd%u-", unit);
1.1       root      109:        const std::string imgkey = keybody + "image";
                    110:        const std::string wikey  = keybody + "writeignore";
                    111: 
                    112:        const ConfigItem& itemimg = gConfig->Find(imgkey);
                    113:        const std::string& filename = itemimg.AsString();
                    114: 
                    115:        // UI からの通知を受け取るイベントを用意
1.1.1.2   root      116:        scheduler->ConnectMessage(MessageID::FDD_LOAD(unit), this,
1.1.1.3   root      117:                ToMessageCallback(&FDDDevice::LoadMessage));
1.1.1.2   root      118:        scheduler->ConnectMessage(MessageID::FDD_UNLOAD(unit), this,
1.1.1.3   root      119:                ToMessageCallback(&FDDDevice::UnloadMessage));
1.1       root      120: 
1.1.1.6 ! root      121:        auto evman = GetEventManager();
        !           122:        event = evman->Regist(this,
        !           123:                ToEventCallback(&FDDDevice::EventCallback),
        !           124:                GetName());
1.1.1.4   root      125: 
1.1       root      126:        // オープン前に writeignore をチェック。
                    127:        // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
                    128:        // 行わないといけない。うーん。
                    129:        const ConfigItem& wi = gConfig->Find(wikey);
                    130:        write_ignore = wi.AsInt();
                    131: 
                    132:        // 設定時点でファイルが指定されていればオープン
                    133:        if (filename.empty() == false) {
1.1.1.3   root      134:                std::string path = gMainApp.NormalizePath(filename);
1.1       root      135:                if (LoadDisk(path) == false) {
                    136:                        return false;
                    137:                }
                    138:        }
                    139: 
                    140:        return true;
                    141: }
                    142: 
                    143: // リセット
                    144: void
                    145: FDDDevice::ResetHard(bool poweron)
                    146: {
                    147:        if (poweron) {
                    148:                motor_state = STOPPED;
                    149:        }
                    150: 
                    151:        // XXX どこでやる?
                    152:        eject_enable = true;
                    153: }
                    154: 
                    155: // モニタの下請け (fdc から呼ばれる)。
                    156: // y = 11 以降が各ドライブの状態で、このうち x = 26 以降が FDD の担当。
                    157: // y = 16 以降は FDD のアクセスマップ。
                    158: void
1.1.1.4   root      159: FDDDevice::MonitorUpdateFDD(TextScreen& screen, uint hd)
1.1       root      160: {
                    161: /*        1         2         3         4         5         6         7
                    162: 0123456789012345678901234567890123456789012345678901234567890123456789012345678
                    163:                           ready accled eject   fmt C  H  R  N
                    164:                           stop  blink  disable 2HC 99 1 99 2(1024)
                    165: Unit0: HD=1 cyl=nn/nn pos=12345/12345
                    166: XX>01234567890123456 01234567890123456| XX>01234567890123456 01234567890123456|
                    167: */
                    168: 
                    169:        int uy = 11 + unit;
                    170:        int ux = 26;
                    171: 
                    172:        // モータ状態
                    173:        const char * const motor_state_str[] = {
                    174:                "Stop",
                    175:                "Down",
                    176:                "Up",
                    177:                "Ready",
                    178:        };
                    179:        screen.Print(ux, uy, "%s", motor_state_str[(int)motor_state]);
                    180: 
                    181:        // アクセス LED 状態
                    182:        const char * const access_led_str[] = {
                    183:                "Off",
                    184:                "Green",
                    185:                "Red",
                    186:                "Blink",
                    187:        };
                    188:        screen.Print(ux + 6, uy, "%s", access_led_str[(int)access_led]);
                    189: 
                    190:        // イジェクト可否 (LED 状態)
                    191:        const char *eject_str;
                    192:        if (medium_loaded) {
                    193:                if (eject_enable) {
                    194:                        eject_str = "Enable";
                    195:                } else {
                    196:                        eject_str = "Disable";
                    197:                }
                    198:        } else {
                    199:                eject_str = "Off";
                    200:        }
                    201:        screen.Puts(ux + 13, uy, eject_str);
                    202: 
                    203:        if (medium_loaded) {
                    204:                // フォーマットと CHRN
1.1.1.4   root      205:                screen.Print(ux + 20, uy, "%4s %2u %u %2u %u(%u)",
1.1       root      206:                        format_name.c_str(), ncyl, nhead, nsect, format_n, sectsize);
                    207:        }
                    208: 
                    209:        uy = 16 + (unit / 2) * 10;
                    210:        ux = (unit % 2) * 41;
                    211: 
1.1.1.4   root      212:        screen.Print(ux, uy++, "Unit %u", unit);
1.1       root      213:        // 横ガイド
                    214:        for (int i = 0; i < 8; i++) {
                    215:                screen.Puts(ux + 2,  uy + i, "|");
                    216:                screen.Puts(ux + 38, uy + i, "|");
                    217:        }
                    218: 
                    219:        // メディアが入ってなければここで終わり
                    220:        if (IsMediumLoaded() == false) {
                    221:                return;
                    222:        }
                    223: 
                    224:        TA selattr = selected ? TA::Em : TA::Normal;
1.1.1.4   root      225:        screen.Print(ux, uy - 1, selattr, "Unit %u: HD=%u cyl=%2u/%u pos=%5u/%u",
1.1       root      226:                unit, hd, cylinder, ncyl, pos, MFM_TRACK_RAW_SIZE);
                    227: 
                    228:        // ヘッドの現在位置。
                    229:        // 横は全長を当分割、縦は1行で10トラック固定にする
                    230:        int headx = (double)pos / MFM_TRACK_RAW_SIZE * 17;
                    231:        int heady = (double)cylinder / 10;
                    232: 
                    233:        // 片面ディスクはサポートしていないが一応
                    234:        for (int h = 0; h < nhead; h++) {
                    235:                for (int i = 0; i < 8; i++) {
                    236:                        TA trkattr = (__predict_false(selected && i == heady && h == hd))
                    237:                                ? TA::Em : TA::Normal;
                    238:                        screen.Puts(ux + h * 18 + 3, uy + i, trkattr,
                    239:                        //   01234567890123456
                    240:                                ".................");
                    241:                }
                    242:        }
                    243: 
                    244:        // ヘッド位置
1.1.1.4   root      245:        screen.Print(ux, uy + heady, selattr, "%2u>", cylinder);
1.1       root      246:        screen.Puts(ux + 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
1.1.1.6 ! root      299: FDDDevice::EventCallback(Event *ev)
1.1       root      300: {
                    301:        // モータが回転している間は1バイトずつ進める
                    302:        pos++;
                    303:        if (pos >= MFM_TRACK_RAW_SIZE) {
                    304:                pos = 0;
                    305:        }
                    306: 
                    307:        switch (motor_state) {
                    308:         case READY:
1.1.1.2   root      309:                scheduler->StartEvent(ev);
1.1       root      310:                break;
                    311: 
                    312:         case SPIN_UP:
                    313:                // 増速中
1.1.1.6 ! root      314:                if (event->time == 16_usec) {
1.1       root      315:                        motor_state = READY;
                    316:                        // READY になったので FDC に通知
1.1.1.2   root      317:                        fdc->ReadyChanged(unit, true);
1.1       root      318:                } else {
1.1.1.6 ! root      319:                        if (event->time < 16_usec) {
1.1       root      320:                                // オーバーシュート対応
1.1.1.6 ! root      321:                                event->time = 16_usec;
1.1       root      322:                        } else {
1.1.1.6 ! root      323:                                event->time /= 2;
1.1       root      324:                        }
1.1.1.6 ! root      325:                        putlog(4, "SPIN_UP next=%uusec", (uint)(event->time / 1_usec));
1.1       root      326:                }
1.1.1.2   root      327:                scheduler->StartEvent(ev);
1.1       root      328:                break;
                    329: 
                    330:         case SPIN_DOWN:
1.1.1.6 ! root      331:                if (event->time >= 262144_usec) {
1.1       root      332:                        motor_state = STOPPED;
                    333:                        // イベント停止、ここでは time も 0 クリアしておく。
1.1.1.6 ! root      334:                        event->time = 0;
1.1       root      335:                } else {
1.1.1.6 ! root      336:                        event->time *= 2;
1.1.1.2   root      337:                        scheduler->StartEvent(ev);
1.1.1.6 ! root      338:                        putlog(4, "SPIN_DOWN next=%uusec", (uint)(event->time / 1_usec));
1.1       root      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;
1.1.1.6 ! root      391:                event->time = 262144_usec;
        !           392:                putlog(4, "SPIN_UP next=%uusec", (uint)(event->time / 1_usec));
1.1       root      393:        } else if (motor_state == SPIN_DOWN) {
                    394:                // 細かいことは無視して、増速に転じる
                    395:                motor_state = SPIN_UP;
                    396:        } else {
1.1.1.4   root      397:                VMPANIC("DoMotorOn called on motor_state=%u", (uint)motor_state);
1.1       root      398:        }
                    399: 
1.1.1.2   root      400:        scheduler->RestartEvent(event);
1.1       root      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;
1.1.1.2   root      412:                fdc->ReadyChanged(unit, false);
1.1       root      413:        } else if (motor_state == READY) {
                    414:                // 減速に転じる。
                    415:                // 細かいことは気にせず増速と同様に倍々で減速していくので停止まで
                    416:                // 約 524msec かかる。細かいことは気にしない。
                    417:                motor_state = SPIN_DOWN;
                    418:                // 定常回転 (READY) ではなくなった
1.1.1.2   root      419:                fdc->ReadyChanged(unit, false);
1.1       root      420:        } else if (motor_state == SPIN_UP) {
                    421:                // 細かいことは無視して、減速に転じる。
                    422:                motor_state = SPIN_DOWN;
                    423:        } else {
1.1.1.4   root      424:                VMPANIC("DoMotorOff called on motor_state=%u", (uint)motor_state);
1.1       root      425:        }
                    426: 
1.1.1.2   root      427:        scheduler->RestartEvent(event);
1.1       root      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:                        }
1.1.1.2   root      459:                        blink_start = scheduler->GetVirtTime();
1.1       root      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;
1.1.1.4   root      643:        int w_ok;
1.1       root      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: 
1.1.1.4   root      655:        w_ok = image.IsWriteable();
                    656:        if (w_ok < 0) {
                    657:                goto done;
                    658:        }
                    659: 
                    660:        // 書き込みモードをここで確定
                    661:        if (w_ok) {
                    662:                if (write_ignore) {
                    663:                        write_mode = RW::WriteIgnore;
                    664:                } else {
                    665:                        write_mode = RW::Writeable;
                    666:                }
                    667:        } else {
                    668:                write_mode = RW::WriteProtect;
                    669:        }
                    670: 
                    671:        // ここでオープン
                    672:        read_only = (GetWriteMode() != RW::Writeable);
                    673:        if (image.Open(read_only, write_ignore) == false) {
1.1       root      674:                goto done;
                    675:        }
                    676: 
1.1.1.4   root      677:        size = image.GetSize();
1.1       root      678:        if (size == 1261568) {
                    679:                // Human68k 2HD フォーマットとみなす
                    680:                // 77track, 2side, 8sector, 1024bytes/sector
                    681:                format_name = "2HD";
                    682:                ncyl = 77;
                    683:                nhead = 2;
                    684:                nsect = 8;
                    685:                sectsize = 1024;
                    686:                format_n = 3;
                    687:                gap3 = 0x74;
                    688:        } else if (size == 1228800) {
                    689:                // NetBSD 2HC フォーマットとみなす
                    690:                // 80track, 2side, 15sector, 512bytes/sector
                    691:                format_name = "2HC";
                    692:                ncyl = 80;
                    693:                nhead = 2;
                    694:                nsect = 15;
                    695:                sectsize = 512;
                    696:                format_n = 2;
                    697:                gap3 = 0x54;
                    698:        } else {
                    699:                warnx("%s: Unsuppoted image, size(%ju)",
                    700:                        pathname.c_str(), (uintmax_t)size);
                    701:                goto done;
                    702:        }
                    703: 
                    704:        gap4b = (MFM_TRACK_RAW_SIZE - MFM_TRACK_HEADER)
                    705:                - (sectsize + MFM_SECTOR_HEADER + gap3) * nsect;
                    706: 
                    707:        // 書き込み無視なら一応ログ出力
                    708:        if (GetWriteMode() == RW::WriteIgnore) {
                    709:                putmsg(0, "write is ignored");
                    710:        }
                    711: 
                    712:        medium_loaded = true;
                    713:        putmsg(0, "%s format", format_name.c_str());
                    714:        putlog(1, "Medium loaded");
                    715: 
                    716:        // オートリキャリブレート
                    717:        cylinder = 0;
                    718: 
                    719:  done:
                    720:        if (medium_loaded == false) {
                    721:                // ここで状態をクリアする
                    722:                Clear();
                    723:        }
                    724: 
                    725:        // 変化があれば
                    726:        if (ejected || medium_loaded) {
                    727:                ChangeStatus();
1.1.1.2   root      728:                pedec->AssertINT(this);
1.1       root      729:                // 通知
                    730:                MediaChanged();
                    731:        }
                    732: 
                    733:        return medium_loaded;
                    734: }
                    735: 
                    736: // バックエンドのディスクイメージを閉じる。
                    737: // オープンされてなければ何もしない。
                    738: void
                    739: FDDDevice::UnloadDisk(bool force)
                    740: {
                    741:        if (eject_enable || force) {
                    742:                if (UnloadDisk_internal()) {
                    743:                        // 実際に閉じたら通知
                    744:                        MediaChanged();
                    745:                }
                    746:        }
                    747: }
                    748: 
                    749: // バックエンドのディスクイメージを閉じる (内部用)。
                    750: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
                    751: // ここでは通知は行わない。
                    752: bool
                    753: FDDDevice::UnloadDisk_internal()
                    754: {
                    755:        if ((bool)image == false) {
                    756:                return false;
                    757:        }
                    758: 
1.1.1.2   root      759:        pedec->AssertINT(this);
1.1       root      760: 
                    761:        Clear();
                    762: 
                    763:        // 取り出し禁止はされてても解除する
                    764:        if (eject_enable == false) {
                    765:                eject_enable = true;
                    766:        }
                    767: 
                    768:        putlog(1, "Medium unloaded");
                    769:        return true;
                    770: }
                    771: 
                    772: // ディスク状態をクリアする
                    773: // (メディアを取り外した時など)
                    774: void
                    775: FDDDevice::Clear()
                    776: {
                    777:        medium_loaded = false;
                    778:        ChangeStatus();
                    779:        image.Close();
                    780:        pathname.clear();
                    781:        write_mode = RW::Writeable;
                    782: }
                    783: 
                    784: // メディア状態変更を UI に通知する
                    785: void
                    786: FDDDevice::MediaChanged() const
                    787: {
1.1.1.6 ! root      788:        gMainApp.GetUIMessage()->Post(UIMessage::FDD_MEDIA_CHANGE, unit);
1.1       root      789: }
                    790: 
                    791: // メディアを挿入する。
                    792: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
                    793: void
                    794: FDDDevice::LoadDiskUI(const std::string& pathname_)
                    795: {
                    796:        new_pathname = pathname_;
1.1.1.2   root      797:        scheduler->SendMessage(MessageID::FDD_LOAD(unit));
1.1       root      798: }
                    799: 
                    800: // メディアを排出する。
                    801: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
                    802: void
                    803: FDDDevice::UnloadDiskUI(bool force)
                    804: {
1.1.1.2   root      805:        scheduler->SendMessage(MessageID::FDD_UNLOAD(unit), force);
1.1       root      806: }
                    807: 
                    808: // メディア挿入メッセージコールバック
                    809: void
1.1.1.3   root      810: FDDDevice::LoadMessage(MessageID msgid, uint32 arg)
1.1       root      811: {
                    812:        if (LoadDisk(new_pathname) == false) {
                    813:                // 失敗したら UI に通知
1.1.1.6 ! root      814:                gMainApp.GetUIMessage()->Post(UIMessage::FDD_MEDIA_FAILED);
1.1       root      815:        }
                    816: }
                    817: 
                    818: // メディア排出メッセージコールバック
                    819: void
1.1.1.3   root      820: FDDDevice::UnloadMessage(MessageID msgid, uint32 arg)
1.1       root      821: {
                    822:        bool force = arg;
                    823:        UnloadDisk(force);
                    824: }
                    825: 
                    826: // アクセス LED の状態を取得。
                    827: // ここで返すのは OFF, GREEN, RED の3通り。
                    828: FDDDevice::LED
                    829: FDDDevice::GetAccessLED() const
                    830: {
                    831:        if (access_led == LED::BLINK) {
                    832:                // 点滅状態はここで作成。
                    833:                // 点滅開始から 0.5秒は点灯、次の 0.5秒を消灯とする。
                    834:                // XXX 間隔は適当
1.1.1.2   root      835:                uint64 time = scheduler->GetVirtTime() - blink_start;
1.1       root      836:                time %= 1_sec;
                    837:                if (time < 500_msec) {
                    838:                        return LED::GREEN;
                    839:                } else {
                    840:                        return LED::OFF;
                    841:                }
                    842:        } else {
                    843:                // それ以外はそのまま返せる
                    844:                return access_led;
                    845:        }
                    846: }
                    847: 
                    848: // イジェクト LED の状態を取得。点灯なら true
                    849: bool
                    850: FDDDevice::GetEjectLED() const
                    851: {
                    852:        // メディアが挿入されていて、イジェクト許可なら点灯。
                    853:        return (medium_loaded && eject_enable);
                    854: }

unix.superglobalmegacorp.com

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