--- nono/wx/wxmainframe.cpp 2026/04/29 17:05:09 1.1.1.13 +++ nono/wx/wxmainframe.cpp 2026/04/29 17:05:12 1.1.1.14 @@ -11,21 +11,22 @@ #include "wxmainframe.h" #include "fontmanager.h" #include "wxdumpmonitor.h" -#include "wxhistmonitor.h" #include "wxlcdwindow.h" #include "wxlogmonitor.h" #include "wxlogsetting.h" #include "wxmainview.h" #include "wxmonitor.h" #include "wxmpumonitor.h" +#include "wxplanemonitor.h" +#include "wxromwindow.h" #include "wxsoftkey.h" #include "wxstatuspanel.h" #include "wxuimessage.h" #include "wxversion.h" -#include "wxvectormonitor.h" -#include "wxvideomonitor.h" #include "config.h" #include "debugger.h" +#include "fdc.h" +#include "fdd.h" #include "mainapp.h" #include "mpu680x0.h" #include "mpu88xx0.h" @@ -93,6 +94,24 @@ enum { ID_SHOW_STATPANEL, + // FD + ID_FD_INSERT_0, + ID_FD_INSERT_1, + ID_FD_INSERT_2, + ID_FD_INSERT_3, + ID_FD_MISINSERT_0, + ID_FD_MISINSERT_1, + ID_FD_MISINSERT_2, + ID_FD_MISINSERT_3, + ID_FD_EJECT_0, + ID_FD_EJECT_1, + ID_FD_EJECT_2, + ID_FD_EJECT_3, + ID_FD_FORCE_0, + ID_FD_FORCE_1, + ID_FD_FORCE_2, + ID_FD_FORCE_3, + // CD/MO (リムーバブルデバイス) ID_CD_INSERT_0, ID_CD_INSERT_1, @@ -120,14 +139,19 @@ enum { ID_CD_FORCE_7, ID_MOUSEMODE, + ID_POWER_BUTTON, ID_RESET_BUTTON, ID_NMI_BUTTON, ID_KEYBOARD_CONNECT, ID_local_end, // 最後に置く (チェック用) }; -static_assert(ID_local_end <= (int)IDGROUP_MAINFRAME_END, "ID exceeded"); +static_assert(ID_local_end - 1 <= (int)IDGROUP_MAINFRAME_END, "ID exceeded"); +#define ID_FD_INSERT(id) (ID_FD_INSERT_0 + (id)) +#define ID_FD_MISINSERT(id) (ID_FD_MISINSERT_0 + (id)) +#define ID_FD_EJECT(id) (ID_FD_EJECT_0 + (id)) +#define ID_FD_FORCE(id) (ID_FD_FORCE_0 + (id)) #define ID_CD_INSERT(id) (ID_CD_INSERT_0 + (id)) #define ID_CD_EJECT(id) (ID_CD_EJECT_0 + (id)) #define ID_CD_FORCE(id) (ID_CD_FORCE_0 + (id)) @@ -159,6 +183,18 @@ wxBEGIN_EVENT_TABLE(WXMainFrame, inherit WXMainFrame::OnMenuMonRateUI) EVT_MENU(ID_SHOW_STATPANEL, WXMainFrame::OnShowStatusPanel) EVT_UPDATE_UI(ID_SHOW_STATPANEL, WXMainFrame::OnShowStatusPanelUI) + EVT_MENU_RANGE(ID_FD_INSERT_0, ID_FD_INSERT_3, + WXMainFrame::OnFDInsert) + EVT_UPDATE_UI_RANGE(ID_FD_INSERT_0, ID_FD_INSERT_3, + WXMainFrame::OnFDInsertUI) + EVT_MENU_RANGE(ID_FD_EJECT_0, ID_FD_EJECT_3, + WXMainFrame::OnFDEject) + EVT_UPDATE_UI_RANGE(ID_FD_EJECT_0, ID_FD_EJECT_3, + WXMainFrame::OnFDEjectUI) + EVT_MENU_RANGE(ID_FD_FORCE_0, ID_FD_FORCE_3, + WXMainFrame::OnFDForce) + EVT_UPDATE_UI_RANGE(ID_FD_FORCE_0, ID_FD_FORCE_3, + WXMainFrame::OnFDForceUI) EVT_MENU_RANGE(ID_CD_INSERT_0, ID_CD_INSERT_7, WXMainFrame::OnCDInsert) EVT_UPDATE_UI_RANGE(ID_CD_INSERT_0, ID_CD_INSERT_7, @@ -173,6 +209,8 @@ wxBEGIN_EVENT_TABLE(WXMainFrame, inherit WXMainFrame::OnCDForceUI) EVT_MENU(ID_MOUSEMODE, WXMainFrame::OnMouseMode) EVT_UPDATE_UI(ID_MOUSEMODE, WXMainFrame::OnMouseModeUI) + EVT_MENU(ID_POWER_BUTTON, WXMainFrame::OnPowerButton) + EVT_UPDATE_UI(ID_POWER_BUTTON, WXMainFrame::OnPowerButtonUI) EVT_MENU(ID_RESET_BUTTON, WXMainFrame::OnResetButton) EVT_MENU(ID_NMI_BUTTON, WXMainFrame::OnNMIButton) EVT_MENU(ID_KEYBOARD_CONNECT, WXMainFrame::OnKeyboardConnect) @@ -186,7 +224,7 @@ WXMainFrame::WXMainFrame(wxWindow *paren : inherited(parent, wxID_ANY, "nono", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~wxMAXIMIZE_BOX & ~wxRESIZE_BORDER) { - vmtype_t vmtype; + VMType vmtype; vmtype = gMainApp.GetVMType(); // メニュー @@ -233,14 +271,22 @@ WXMainFrame::WXMainFrame(wxWindow *paren "", wxITEM_CHECK); menuDevice->Append(ID_MOUSEMODE, _("&Mouse Mode\tCTRL+F12"), "", wxITEM_CHECK); - if (gMainApp.HasVMFeature(VMF_LUNA)) { + if (gMainApp.Has(VMCap::LUNA)) { menuDevice->Append(ID_SUBWIN_LCDPANEL, _("Front &LCD"), "", wxITEM_CHECK); } menuDevice->AppendSeparator(); wxMenu *menuOper = new wxMenu(); + wxItemKind power_button_kind; + if (gMainApp.Has(VMCap::LUNA)) { + power_button_kind = wxITEM_NORMAL; + } else { + power_button_kind = wxITEM_CHECK; + } + menuOper->Append(ID_POWER_BUTTON, _("Push &Power button"), + "", power_button_kind); menuOper->Append(ID_RESET_BUTTON, _("Push &Reset button")); - if (gMainApp.HasVMFeature(VMF_LUNA)) { + if (gMainApp.Has(VMCap::LUNA)) { menuOper->Append(ID_NMI_BUTTON, _("Push &ABORT button")); } else { menuOper->Append(ID_NMI_BUTTON, _("Push &Interrupt button")); @@ -253,13 +299,13 @@ WXMainFrame::WXMainFrame(wxWindow *paren // 機種によってモニタウィンドウを追加する #define AppendIf(menu, id, label) do { \ - vmf_t feature_ = gMonitorManager->GetFeature(id); \ - if (gMainApp.HasVMFeature(feature_)) { \ + VMCap vmcap_ = gMonitorManager->GetVMCap(id); \ + if (gMainApp.Has(vmcap_)) { \ (menu)->Append((id), (label), "", wxITEM_CHECK); \ } \ } while (0) -#define AppendIfSeparator(menu, feature_) do { \ - if (gMainApp.HasVMFeature(feature_)) { \ +#define AppendIfSeparator(menu, vmcap_) do { \ + if (gMainApp.Has(vmcap_)) { \ (menu)->AppendSeparator(); \ } \ } while (0) @@ -281,11 +327,11 @@ WXMainFrame::WXMainFrame(wxWindow *paren wxMenu *menuMPU = new wxMenu(); AppendIf(menuMPU, ID_MONITOR_MPUREG, _("&Register")); AppendIf(menuMPU, ID_MONITOR_MPUATC, "&ATC"); - AppendIfSeparator(menuMPU, VMF_M88K); + AppendIfSeparator(menuMPU, VMCap::M88K); AppendIf(menuMPU, ID_MONITOR_CMMU7, "CMMU#7" + _("(Inst)")); AppendIf(menuMPU, ID_MONITOR_ATC7, "ATC#7"); AppendIf(menuMPU, ID_SUBWIN_CACHE7, _("Cache") + "#7"); - AppendIfSeparator(menuMPU, VMF_M88K); + AppendIfSeparator(menuMPU, VMCap::M88K); AppendIf(menuMPU, ID_MONITOR_CMMU6, "CMMU#6" + _("(Data)")); AppendIf(menuMPU, ID_MONITOR_ATC6, "ATC#6"); AppendIf(menuMPU, ID_SUBWIN_CACHE6, _("Cache") + "#6"); @@ -307,6 +353,7 @@ WXMainFrame::WXMainFrame(wxWindow *paren AppendIf(menuMonitor, ID_MONITOR_MK48T02, "&RTC"); AppendIf(menuMonitor, ID_MONITOR_DMAC, "DM&AC"); AppendIf(menuMonitor, ID_MONITOR_MFP, "M&FP"); + AppendIf(menuMonitor, ID_MONITOR_FDC, "F&DC"); AppendIf(menuMonitor, ID_MONITOR_SCC, "SCC(&Z)"); AppendIf(menuMonitor, ID_MONITOR_SIO, "SIO(&U)"); AppendIf(menuMonitor, ID_MONITOR_PIO0, "PIO0"); @@ -318,6 +365,8 @@ WXMainFrame::WXMainFrame(wxWindow *paren AppendIf(menuMonitor, ID_MONITOR_LUNAVC, _("&Video Control")); AppendIf(menuMonitor, ID_MONITOR_RENDERER, _("Renderer")); AppendIf(menuMonitor, ID_SUBWIN_LUNAFB, _("&Bitmap Plane")); + AppendIf(menuMonitor, ID_SUBWIN_TVRAM, _("&Text VRAM")); + AppendIf(menuMonitor, ID_SUBWIN_ROM, "ROM"); wxMenu *menuHost = new wxMenu(); AppendIf(menuHost, ID_MONITOR_WXHOSTINFO, _("Host &Information")); @@ -344,13 +393,13 @@ WXMainFrame::WXMainFrame(wxWindow *paren wxString title("nono"); switch (vmtype) { - case VMTYPE_X68030: + case VMType::X68030: title += "(X68030)"; break; - case VMTYPE_LUNA1: + case VMType::LUNA1: title += "(LUNA-I)"; break; - case VMTYPE_LUNA88K: + case VMType::LUNA88K: title += "(LUNA88K)"; break; default: @@ -407,7 +456,9 @@ WXMainFrame::WXMainFrame(wxWindow *paren WXUIMessage::Connect(UIMessage::APPEXIT, this, wxCommandEventHandler(WXMainFrame::OnExit)); WXUIMessage::Connect(UIMessage::SCSI_MEDIA_FAILED, this, - wxCommandEventHandler(WXMainFrame::OnInsertCDFailed)); + wxCommandEventHandler(WXMainFrame::OnInsertFailed)); + WXUIMessage::Connect(UIMessage::FDD_MEDIA_FAILED, this, + wxCommandEventHandler(WXMainFrame::OnInsertFailed)); if (gMPU680x0) { WXUIMessage::Connect(UIMessage::HALT, this, wxCommandEventHandler(WXMainFrame::OnHaltNotify)); @@ -419,7 +470,9 @@ WXMainFrame::~WXMainFrame() WXUIMessage::Disconnect(UIMessage::APPEXIT, wxCommandEventHandler(WXMainFrame::OnExit)); WXUIMessage::Disconnect(UIMessage::SCSI_MEDIA_FAILED, - wxCommandEventHandler(WXMainFrame::OnInsertCDFailed)); + wxCommandEventHandler(WXMainFrame::OnInsertFailed)); + WXUIMessage::Disconnect(UIMessage::FDD_MEDIA_FAILED, + wxCommandEventHandler(WXMainFrame::OnInsertFailed)); if (gMPU680x0) { WXUIMessage::Disconnect(UIMessage::HALT, wxCommandEventHandler(WXMainFrame::OnHaltNotify)); @@ -434,6 +487,18 @@ WXMainFrame::CreateRemovableMenu(wxMenu bool added = false; // FD + if (gFDC) { + const std::vector& fdd_list = gFDC->GetFDD(); + for (const auto fdd : fdd_list) { + int unit = fdd->GetUnitNo(); + fdd_array[unit] = fdd; + + menu = CreateFDMenu(unit); + std::string label = string_format("FD%d", unit); + menuDevice->AppendSubMenu(menu, label); + added = true; + } + } // CD, MO for (int id = 0; id < 8; id++) { @@ -455,6 +520,21 @@ WXMainFrame::CreateRemovableMenu(wxMenu } } +// FD 用のメニューを作成して返す。 +wxMenu * +WXMainFrame::CreateFDMenu(int unit) const +{ + wxMenu *menu = new wxMenu(); + // Insert の実際のラベルは UpdateUI でセットする。 + // ただし何か入れておかないといけないので適当にセット。 + menu->Append(ID_FD_INSERT(unit), "Insert", ""); + // XXX 未実装 + //menu->Append(ID_FD_MISINSERT(unit), _("Insert incorrectly"), ""); + menu->Append(ID_FD_EJECT(unit), _("&Eject"), ""); + menu->Append(ID_FD_FORCE(unit), _("&Force Eject"), ""); + return menu; +} + // SCSI リムーバブルデバイス用のメニューを作成して返す。 // id はデバイスが接続されていること (リムーバブルデバイスであるかは問わない)。 // リムーバブルデバイスでなければ NULL を返す。 @@ -557,7 +637,7 @@ WXMainFrame::OnIdle(wxIdleEvent& ev) } // 電源投入 - gVM->PowerButton(); + gPower->PowerButton(); // 一度処理したら、このイベントを外す Disconnect(wxEVT_IDLE, wxIdleEventHandler(WXMainFrame::OnIdle)); @@ -754,7 +834,7 @@ WXMainFrame::OnMenuMonRate(wxCommandEven if ((bool)windows[i]) { auto *subwin = dynamic_cast(windows[i].get()); for (wxWindow *child : subwin->GetChildren()) { - auto *panel = dynamic_cast(child); + auto *panel = dynamic_cast(child); if (panel) { panel->SetRate(monitor_rate); } @@ -794,6 +874,90 @@ WXMainFrame::OnShowStatusPanelUI(wxUpdat event.Check(gStatusPanel->IsShown()); } +// メニューの「デバイス > FD > 挿入」 +void +WXMainFrame::OnFDInsert(wxCommandEvent& event) +{ + int unit = event.GetId() - ID_FD_INSERT_0; + const auto *fdd = fdd_array[unit]; + assert(fdd); + + std::string title(_("Open a FD image")); + std::string wildcard("FD images (*.xdf;*.img;*.fs)" + "|*.xdf;*.XDF;*.img;*.IMG;*.fs;*.FS" + "|All files (*.*)|*.*"); + + // ディレクトリは、まだ一度も使ってなければ VM ディレクトリにする + if (current_dir.empty()) { + current_dir = gMainApp.GetVMDir(); + } + + wxFileDialog dialog(this, title, current_dir, "", wildcard, + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() == wxID_CANCEL) { + return; + } + auto path = std::string(dialog.GetPath()); + InsertFD(unit, path); +} + +// メニューの「デバイス > FD > 挿入」UI +void +WXMainFrame::OnFDInsertUI(wxUpdateUIEvent& event) +{ + int unit = event.GetId() - ID_FD_INSERT_0; + const auto *fdd = fdd_array[unit]; + assert(fdd); + + if (fdd->IsMediumLoaded()) { + event.SetText(_("Ex&change")); + } else { + event.SetText(_("&Insert")); + } +} + +// メニューの「デバイス > FD > 取り出し」 +void +WXMainFrame::OnFDEject(wxCommandEvent& event) +{ + int unit = event.GetId() - ID_FD_EJECT_0; + + EjectFD(unit, false); +} + +// メニューの「デバイス > FD > 取り出し」UI +void +WXMainFrame::OnFDEjectUI(wxUpdateUIEvent& event) +{ + int unit = event.GetId() - ID_FD_EJECT_0; + FDDDevice *fdd = fdd_array[unit]; + assert(fdd); + + // 挿入されてて取り出し可能な時のみ有効 + event.Enable(fdd->IsMediumLoaded() && fdd->IsEjectEnable()); +} + +// メニューの「デバイス > FD > 強制取り出し」 +void +WXMainFrame::OnFDForce(wxCommandEvent& event) +{ + int unit = event.GetId() - ID_FD_FORCE_0; + + EjectFD(unit, true); +} + +// メニューの「デバイス > FD > 強制取り出し」UI +void +WXMainFrame::OnFDForceUI(wxUpdateUIEvent& event) +{ + int unit = event.GetId() - ID_FD_FORCE_0; + FDDDevice *fdd = fdd_array[unit]; + assert(fdd); + + // 挿入されてて取り出し禁止の時のみ有効 + event.Enable(fdd->IsMediumLoaded() && fdd->IsEjectEnable() == false); +} + // メニューの「デバイス > CD/MO > 挿入」 void WXMainFrame::OnCDInsert(wxCommandEvent& event) @@ -916,6 +1080,31 @@ WXMainFrame::OnMouseModeUI(wxUpdateUIEve event.Check(gMainView->GetMouseMode()); } +// メニューの「デバイス > Push Power Button」イベント +void +WXMainFrame::OnPowerButton(wxCommandEvent& event) +{ + // LUNA はモーメンタリ動作、X68030 はオルタネート動作のスイッチ。 + // この違いは VM 側で吸収するのでここはボタンを押したという動作だけ + // 通知する。 + gPower->PowerButton(); +} + +// メニューの「デバイス > Push Power Button」UI +void +WXMainFrame::OnPowerButtonUI(wxUpdateUIEvent& event) +{ + if (gMainApp.Has(VMCap::LUNA)) { + // LUNA の電源ボタンはモーメンタリ動作で、電源オフ->オンの機能しかなく + // さらに今の所電源オフ状態はないので、実質無効。 + event.Enable(false); + } else { + // X68030 の電源ボタンはオルタネート動作なので、 + // チェックで状態を表す。 + event.Check(gPower->IsPowerPressed()); + } +} + // メニューの「デバイス > Push Reset Button」イベント // こっちは本体リセットボタンによるリセット void @@ -1026,6 +1215,10 @@ WXMainFrame::DoWindow(int id, bool enabl SetMonitor(id, "DMAC"); break; + case ID_MONITOR_FDC: + SetMonitor(id, "FDC"); + break; + case ID_MONITOR_HOSTCOM: SetMonitor(id, _("Host Serial Port")); break; @@ -1119,13 +1312,13 @@ WXMainFrame::DoWindow(int id, bool enabl break; case ID_SUBWIN_BRHIST: - windows[id].reset(new WXBranchHistoryMonitor(this, - gMonitorManager->Get(id))); + windows[id].reset(new WXScrollMonitorWindow(this, + _("Branch History"), gMonitorManager->Get(id))); break; case ID_SUBWIN_EXHIST: - windows[id].reset(new WXExceptionHistoryMonitor(this, - gMonitorManager->Get(id))); + windows[id].reset(new WXScrollMonitorWindow(this, + _("Exception History"), gMonitorManager->Get(id))); break; case ID_SUBWIN_CACHE0: @@ -1159,8 +1352,23 @@ WXMainFrame::DoWindow(int id, bool enabl case ID_SUBWIN_LOGSETTING: windows[id].reset(new WXLogSettingWindow(this)); break; + case ID_SUBWIN_LUNAFB: - windows[id].reset(new WXLunaBitmapWindow(this, _("Bitmap Plane"))); + case ID_SUBWIN_TVRAM: + { + // 呼び方が違うので名前と ID が違う + wxString name; + if (id == ID_SUBWIN_LUNAFB) { + name = _("Bitmap Plane"); + } else { + name = _("Text VRAM"); + } + windows[id].reset(new WXPlaneVRAMWindow(this, name)); + break; + } + + case ID_SUBWIN_ROM: + windows[id].reset(new WXROMWindow(this)); break; case ID_SUBWIN_SOFTKEY: @@ -1168,7 +1376,8 @@ WXMainFrame::DoWindow(int id, bool enabl break; case ID_SUBWIN_VECTOR: - windows[id].reset(new WXVectorMonitor(this)); + windows[id].reset(new WXScrollMonitorWindow(this, + _("Vector Table"), gMonitorManager->Get(id))); break; default: @@ -1207,6 +1416,32 @@ WXMainFrame::OnAbout(wxCommandEvent& eve dlg.ShowModal(); } +// FD を挿入する。 +void +WXMainFrame::InsertFD(int unit, const std::string& path) +{ + FDDDevice *fdd = fdd_array[unit]; + assert(fdd); + + // VM に依頼 + // (エラーが起きれば別途 UI メッセージが飛んでくる) + fdd->LoadDiskUI(path); + + // 次回以降のダイアログのカレントディレクトリをここに更新 + auto pos = path.rfind('/'); + current_dir = path.substr(0, pos); +} + +// FD をイジェクトする。 +void +WXMainFrame::EjectFD(int unit, bool force) +{ + FDDDevice *fdd = fdd_array[unit]; + assert(fdd); + + fdd->UnloadDiskUI(force); +} + // CD/MO を挿入する。 void WXMainFrame::InsertCD(int id, const std::string& path) @@ -1233,9 +1468,10 @@ WXMainFrame::EjectCD(int id, bool force) scsidisk->UnloadDiskUI(force); } -// CD/MO の挿入失敗メッセージ +// CD/MO/FD の挿入失敗メッセージ。 +// CD/MO と FD でメッセージが同じなので共用する。 void -WXMainFrame::OnInsertCDFailed(wxCommandEvent& event) +WXMainFrame::OnInsertFailed(wxCommandEvent& event) { ::wxMessageBox(_("Cannot load the image file")); }