--- nono/vm/fdd.cpp 2026/04/29 17:05:14 1.1.1.1 +++ nono/vm/fdd.cpp 2026/04/29 17:05:51 1.1.1.6 @@ -13,8 +13,12 @@ // トラックは 8~ 個のセクタで構成される。 // セクタは 128~ バイトで構成される。 +// 割り込み状態はこちらでは保持しておらず PEDEC 側で持っている。 +// PEDEC への割り込みは AssertINT() するだけで使っている。 + #include "fdd.h" #include "config.h" +#include "event.h" #include "fdc.h" #include "mainapp.h" #include "pedec.h" @@ -25,13 +29,34 @@ #define MFM_SECTOR_HEADER (12 + 3 + 1 + 4 + 2 + 22 + 12 + 3 + 1 + 2) #define MFM_TRACK_RAW_SIZE (10416) -// トラックデータ追加 +// +// フロッピーディスクの 1 トラック +// + +// トラックデータの記録開始 +void +FDTrack::Begin() +{ + markpos.push_back(buf.size()); +} + +// トラックデータの記録終了 +void +FDTrack::End() +{ + assert(buf.size() == MFM_TRACK_RAW_SIZE); + + markpos.push_back(buf.size()); +} + +// トラックデータに data を追加 void FDTrack::Append(uint8 data) { buf.push_back(data); } +// トラックデータに data を count 個追加 void FDTrack::Append(uint8 data, int count) { @@ -49,22 +74,6 @@ FDTrack::AppendMark(uint8 data) Append(data); } -// トラックデータの記録開始 -void -FDTrack::Begin() -{ - markpos.push_back(buf.size()); -} - -// トラックデータの記録終了 -void -FDTrack::End() -{ - assert(buf.size() == MFM_TRACK_RAW_SIZE); - - markpos.push_back(buf.size()); -} - void FDTrack::Clear() { @@ -73,20 +82,18 @@ FDTrack::Clear() } - - -FDDDevice::FDDDevice(int unit_) - : inherited("FDD") +// +// フロッピードライブ +// + +// コンストラクタ +FDDDevice::FDDDevice(uint unit_) + : inherited(OBJ_FDD(unit_)) { unit = unit_; - SetName(string_format("FDD%d", unit)); - ClearAlias(); - AddAlias(GetName()); - - event.func = ToEventCallback(&FDDDevice::EventCallback); - event.Regist(GetName()); } +// デストラクタ FDDDevice::~FDDDevice() { } @@ -95,20 +102,26 @@ FDDDevice::~FDDDevice() bool FDDDevice::Init() { - const std::string keybody = string_format("fd%d-", unit); + fdc = GetFDCDevice(); + pedec = GetPEDECDevice(); + + const std::string keybody = string_format("fd%u-", unit); const std::string imgkey = keybody + "image"; const std::string wikey = keybody + "writeignore"; - // デバイス種別(v[0])とパス(v[1])に分解。 - // デバイス種別はすでにチェックしてあるのでここでは不要。 const ConfigItem& itemimg = gConfig->Find(imgkey); const std::string& filename = itemimg.AsString(); // UI からの通知を受け取るイベントを用意 - gScheduler->ConnectMessage(MessageID::FDD_LOAD(unit), this, - ToMessageCallback(&FDDDevice::LoadCallback)); - gScheduler->ConnectMessage(MessageID::FDD_UNLOAD(unit), this, - ToMessageCallback(&FDDDevice::UnloadCallback)); + scheduler->ConnectMessage(MessageID::FDD_LOAD(unit), this, + ToMessageCallback(&FDDDevice::LoadMessage)); + scheduler->ConnectMessage(MessageID::FDD_UNLOAD(unit), this, + ToMessageCallback(&FDDDevice::UnloadMessage)); + + auto evman = GetEventManager(); + event = evman->Regist(this, + ToEventCallback(&FDDDevice::EventCallback), + GetName()); // オープン前に writeignore をチェック。 // この後パス名を処理するとすぐに UI に通知を出すため、それより前で @@ -118,21 +131,10 @@ FDDDevice::Init() // 設定時点でファイルが指定されていればオープン if (filename.empty() == false) { - std::string path; - if (filename[0] == '/') { - path = filename; - } else { - // 相対パスなら VM ディレクトリから - path = gMainApp.GetVMDir() + filename; - } - + std::string path = gMainApp.NormalizePath(filename); if (LoadDisk(path) == false) { return false; } - } else { - // メディアがロードされないままでも起動時は必ず通知を投げる。 - // ステータスパネルはこの UIMessage でのみ状態を更新するため。 - MediaChanged(); } return true; @@ -154,7 +156,7 @@ FDDDevice::ResetHard(bool poweron) // y = 11 以降が各ドライブの状態で、このうち x = 26 以降が FDD の担当。 // y = 16 以降は FDD のアクセスマップ。 void -FDDDevice::MonitorUpdateFDD(TextScreen& screen, int hd) +FDDDevice::MonitorUpdateFDD(TextScreen& screen, uint hd) { /* 1 2 3 4 5 6 7 0123456789012345678901234567890123456789012345678901234567890123456789012345678 @@ -200,14 +202,14 @@ XX>01234567890123456 01234567890123456| if (medium_loaded) { // フォーマットと CHRN - screen.Print(ux + 20, uy, "%4s %2d %d %2d %d(%d)", + screen.Print(ux + 20, uy, "%4s %2u %u %2u %u(%u)", format_name.c_str(), ncyl, nhead, nsect, format_n, sectsize); } uy = 16 + (unit / 2) * 10; ux = (unit % 2) * 41; - screen.Print(ux, uy++, "Unit %d", unit); + screen.Print(ux, uy++, "Unit %u", unit); // 横ガイド for (int i = 0; i < 8; i++) { screen.Puts(ux + 2, uy + i, "|"); @@ -220,7 +222,7 @@ XX>01234567890123456 01234567890123456| } TA selattr = selected ? TA::Em : TA::Normal; - screen.Print(ux, uy - 1, selattr, "Unit %d: HD=%d cyl=%2d/%d pos=%5d/%d", + screen.Print(ux, uy - 1, selattr, "Unit %u: HD=%u cyl=%2u/%u pos=%5u/%u", unit, hd, cylinder, ncyl, pos, MFM_TRACK_RAW_SIZE); // ヘッドの現在位置。 @@ -240,10 +242,8 @@ XX>01234567890123456 01234567890123456| } // ヘッド位置 - screen.Print(ux, uy + heady, selattr, "%2d>", cylinder); + screen.Print(ux, uy + heady, selattr, "%2u>", cylinder); screen.Puts(ux + hd * 18 + 3 + headx, uy + heady, selattr, "#"); - // 裏のヘッド位置 - screen.Puts(ux + (1 - hd) * 18 + 3 + headx, uy + heady, selattr, "+"); } // READY 信号 @@ -296,7 +296,7 @@ FDDDevice::SetMotorOn(bool motor_on_) } void -FDDDevice::EventCallback(Event& ev) +FDDDevice::EventCallback(Event *ev) { // モータが回転している間は1バイトずつ進める pos++; @@ -306,36 +306,36 @@ FDDDevice::EventCallback(Event& ev) switch (motor_state) { case READY: - gScheduler->StartEvent(ev); + scheduler->StartEvent(ev); break; case SPIN_UP: // 増速中 - if (event.time == 16_usec) { + if (event->time == 16_usec) { motor_state = READY; // READY になったので FDC に通知 - gFDC->ReadyChanged(unit, true); + fdc->ReadyChanged(unit, true); } else { - if (event.time < 16_usec) { + if (event->time < 16_usec) { // オーバーシュート対応 - event.time = 16_usec; + event->time = 16_usec; } else { - event.time /= 2; + event->time /= 2; } - putlog(4, "SPIN_UP next=%uusec", (uint)(event.time / 1_usec)); + putlog(4, "SPIN_UP next=%uusec", (uint)(event->time / 1_usec)); } - gScheduler->StartEvent(ev); + scheduler->StartEvent(ev); break; case SPIN_DOWN: - if (event.time >= 262144_usec) { + if (event->time >= 262144_usec) { motor_state = STOPPED; // イベント停止、ここでは time も 0 クリアしておく。 - event.time = 0; + event->time = 0; } else { - event.time *= 2; - gScheduler->StartEvent(ev); - putlog(4, "SPIN_DOWN next=%uusec", (uint)(event.time / 1_usec)); + event->time *= 2; + scheduler->StartEvent(ev); + putlog(4, "SPIN_DOWN next=%uusec", (uint)(event->time / 1_usec)); } break; @@ -388,16 +388,16 @@ FDDDevice::DoMotorOn() // 仕様では 500msec で READY になるとあるが、ここでは 2 のべき乗を // 使っているので、約 524msec かかる。細かいことは気にしない。 motor_state = SPIN_UP; - event.time = 262144_usec; - putlog(4, "SPIN_UP next=%uusec", (uint)(event.time / 1_usec)); + event->time = 262144_usec; + putlog(4, "SPIN_UP next=%uusec", (uint)(event->time / 1_usec)); } else if (motor_state == SPIN_DOWN) { // 細かいことは無視して、増速に転じる motor_state = SPIN_UP; } else { - VMPANIC("DoMotorOn called on motor_state=%d", (int)motor_state); + VMPANIC("DoMotorOn called on motor_state=%u", (uint)motor_state); } - gScheduler->RestartEvent(event); + scheduler->RestartEvent(event); } void @@ -409,22 +409,22 @@ FDDDevice::DoMotorOff() // 物理的には多少モータは慣性回転するはずではあるが、ディスクは // もうないので。 motor_state = STOPPED; - gFDC->ReadyChanged(unit, false); + fdc->ReadyChanged(unit, false); } else if (motor_state == READY) { // 減速に転じる。 // 細かいことは気にせず増速と同様に倍々で減速していくので停止まで // 約 524msec かかる。細かいことは気にしない。 motor_state = SPIN_DOWN; // 定常回転 (READY) ではなくなった - gFDC->ReadyChanged(unit, false); + fdc->ReadyChanged(unit, false); } else if (motor_state == SPIN_UP) { // 細かいことは無視して、減速に転じる。 motor_state = SPIN_DOWN; } else { - VMPANIC("DoMotorOff called on motor_state=%d", (int)motor_state); + VMPANIC("DoMotorOff called on motor_state=%u", (uint)motor_state); } - gScheduler->RestartEvent(event); + scheduler->RestartEvent(event); } // ドライブコントロールのセット @@ -456,7 +456,7 @@ FDDDevice::SetDriveCtrl(uint8 data) if (islog(is_blink == false)) { putlogn("LED Blink"); } - blink_start = gScheduler->GetVirtTime(); + blink_start = scheduler->GetVirtTime(); is_blink = true; } else { if (islog(is_blink == true)) { @@ -640,7 +640,7 @@ FDDDevice::LoadDisk(const std::string& p { bool ejected; off_t size; - bool w_ok; + int w_ok; bool read_only; // すでにあればクローズ (ここでは通知は行わない) @@ -652,10 +652,29 @@ FDDDevice::LoadDisk(const std::string& p goto done; } - if (image.GetInfo(&size, &w_ok) == false) { + w_ok = image.IsWriteable(); + if (w_ok < 0) { + goto done; + } + + // 書き込みモードをここで確定 + if (w_ok) { + if (write_ignore) { + write_mode = RW::WriteIgnore; + } else { + write_mode = RW::Writeable; + } + } else { + write_mode = RW::WriteProtect; + } + + // ここでオープン + read_only = (GetWriteMode() != RW::Writeable); + if (image.Open(read_only, write_ignore) == false) { goto done; } + size = image.GetSize(); if (size == 1261568) { // Human68k 2HD フォーマットとみなす // 77track, 2side, 8sector, 1024bytes/sector @@ -685,23 +704,6 @@ FDDDevice::LoadDisk(const std::string& p gap4b = (MFM_TRACK_RAW_SIZE - MFM_TRACK_HEADER) - (sectsize + MFM_SECTOR_HEADER + gap3) * nsect; - // 書き込みモードをここで確定 - if (w_ok) { - if (write_ignore) { - write_mode = RW::WriteIgnore; - } else { - write_mode = RW::Writeable; - } - } else { - write_mode = RW::WriteProtect; - } - - // ここでオープン - read_only = (GetWriteMode() != RW::Writeable); - if (image.Open(read_only, write_ignore) == false) { - goto done; - } - // 書き込み無視なら一応ログ出力 if (GetWriteMode() == RW::WriteIgnore) { putmsg(0, "write is ignored"); @@ -723,7 +725,7 @@ FDDDevice::LoadDisk(const std::string& p // 変化があれば if (ejected || medium_loaded) { ChangeStatus(); - gPEDEC->AssertINT(this); + pedec->AssertINT(this); // 通知 MediaChanged(); } @@ -754,7 +756,7 @@ FDDDevice::UnloadDisk_internal() return false; } - gPEDEC->AssertINT(this); + pedec->AssertINT(this); Clear(); @@ -783,7 +785,7 @@ FDDDevice::Clear() void FDDDevice::MediaChanged() const { - UIMessage::Post(UIMessage::FDD_MEDIA_CHANGE, unit); + gMainApp.GetUIMessage()->Post(UIMessage::FDD_MEDIA_CHANGE, unit); } // メディアを挿入する。 @@ -792,7 +794,7 @@ void FDDDevice::LoadDiskUI(const std::string& pathname_) { new_pathname = pathname_; - gScheduler->SendMessage(MessageID::FDD_LOAD(unit)); + scheduler->SendMessage(MessageID::FDD_LOAD(unit)); } // メディアを排出する。 @@ -800,22 +802,22 @@ FDDDevice::LoadDiskUI(const std::string& void FDDDevice::UnloadDiskUI(bool force) { - gScheduler->SendMessage(MessageID::FDD_UNLOAD(unit), force); + scheduler->SendMessage(MessageID::FDD_UNLOAD(unit), force); } // メディア挿入メッセージコールバック void -FDDDevice::LoadCallback(MessageID msgid, uint32 arg) +FDDDevice::LoadMessage(MessageID msgid, uint32 arg) { if (LoadDisk(new_pathname) == false) { // 失敗したら UI に通知 - UIMessage::Post(UIMessage::FDD_MEDIA_FAILED); + gMainApp.GetUIMessage()->Post(UIMessage::FDD_MEDIA_FAILED); } } // メディア排出メッセージコールバック void -FDDDevice::UnloadCallback(MessageID msgid, uint32 arg) +FDDDevice::UnloadMessage(MessageID msgid, uint32 arg) { bool force = arg; UnloadDisk(force); @@ -830,7 +832,7 @@ FDDDevice::GetAccessLED() const // 点滅状態はここで作成。 // 点滅開始から 0.5秒は点灯、次の 0.5秒を消灯とする。 // XXX 間隔は適当 - uint64 time = gScheduler->GetVirtTime() - blink_start; + uint64 time = scheduler->GetVirtTime() - blink_start; time %= 1_sec; if (time < 500_msec) { return LED::GREEN;