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

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

unix.superglobalmegacorp.com

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