--- nono/wx/wxmainframe.cpp 2026/04/29 17:04:51 1.1.1.8 +++ nono/wx/wxmainframe.cpp 2026/04/29 17:04:59 1.1.1.11 @@ -13,43 +13,29 @@ #include "wxmonitor.h" #include "wxmpumonitor.h" #include "wxsoftkey.h" +#include "wxstatuspanel.h" #include "wxtextpanel.h" +#include "wxuimessage.h" #include "wxversion.h" #include "wxvideomonitor.h" -#include "bitmap.h" -#include "bt454.h" #include "debugger.h" -#include "dmac.h" -#include "interrupt.h" -#include "keyboard.h" -#include "lance.h" -#include "lcd.h" #include "mainapp.h" -#include "mfp.h" #include "mpu680x0.h" #include "mpu88xx0.h" #include "netdriver.h" -#include "pio.h" -#include "rtc.h" -#include "scc.h" #include "scheduler.h" -#include "sio.h" #include "spc.h" #include "vm.h" -static void wxmainframe_poweroff_callback(); -static void wxmainframe_halt_callback(void *); - WXMainFrame *gMainFrame; wxBEGIN_EVENT_TABLE(WXMainFrame, inherited) - EVT_CREATE(WXMainFrame::OnCreate) EVT_CLOSE(WXMainFrame::OnClose) EVT_MENU(wxID_EXIT, WXMainFrame::OnExit) EVT_MENU(ID_FULL_SPEED, WXMainFrame::OnFullSpeed) EVT_UPDATE_UI(ID_FULL_SPEED, WXMainFrame::OnFullSpeedUI) - EVT_MENU(ID_RESET_VM, WXMainFrame::OnResetVM) + EVT_MENU(ID_RESTART_VM, WXMainFrame::OnRestartVM) EVT_MENU_RANGE(ID_SCALE_START + 1, ID_SCALE_END - 1, WXMainFrame::OnMenuScale) EVT_UPDATE_UI_RANGE(ID_SCALE_START + 1, ID_SCALE_END - 1, @@ -62,21 +48,33 @@ wxBEGIN_EVENT_TABLE(WXMainFrame, inherit WXMainFrame::OnMenuMonRate) EVT_UPDATE_UI_RANGE(ID_MONRATE_START + 1, ID_MONRATE_END - 1, WXMainFrame::OnMenuMonRateUI) + EVT_MENU(ID_SHOW_STATPANEL, WXMainFrame::OnShowStatusPanel) + EVT_UPDATE_UI(ID_SHOW_STATPANEL, WXMainFrame::OnShowStatusPanelUI) + 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, + WXMainFrame::OnCDInsertUI) + EVT_MENU_RANGE(ID_CD_EJECT_0, ID_CD_EJECT_7, + WXMainFrame::OnCDEject) + EVT_UPDATE_UI_RANGE(ID_CD_EJECT_0, ID_CD_EJECT_7, + WXMainFrame::OnCDEjectUI) + EVT_MENU_RANGE(ID_CD_FORCE_0, ID_CD_FORCE_7, + WXMainFrame::OnCDForce) + EVT_UPDATE_UI_RANGE(ID_CD_FORCE_0, ID_CD_FORCE_7, + WXMainFrame::OnCDForceUI) EVT_MENU(ID_MOUSEMODE, WXMainFrame::OnMouseMode) EVT_UPDATE_UI(ID_MOUSEMODE, WXMainFrame::OnMouseModeUI) - EVT_MENU_RANGE(WinID_Start, WinID_MAX - 1, WXMainFrame::OnWindow) - EVT_UPDATE_UI_RANGE(WinID_Start, WinID_MAX - 1, WXMainFrame::OnWindowUI) + EVT_MENU(ID_RESET_BUTTON, WXMainFrame::OnResetButton) + EVT_MENU_RANGE(0, ID_SUBWIN_END, WXMainFrame::OnWindow) + EVT_UPDATE_UI_RANGE(0, ID_SUBWIN_END, WXMainFrame::OnWindowUI) EVT_MENU(wxID_ABOUT, WXMainFrame::OnAbout) - - EVT_COMMAND(wxID_ANY, WXW_EVT_HALT_NOTIFY, WXMainFrame::OnHaltNotify) wxEND_EVENT_TABLE() WXMainFrame::WXMainFrame(wxWindow *parent) - : wxFrame(parent, wxID_ANY, "nono", wxDefaultPosition, wxDefaultSize, + : inherited(parent, wxID_ANY, "nono", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~wxMAXIMIZE_BOX & ~wxRESIZE_BORDER) { vmtype_t vmtype; - vmtype = gMainApp.GetVMType(); // とりあえずメニュー @@ -85,7 +83,8 @@ WXMainFrame::WXMainFrame(wxWindow *paren wxMenu *menuFile = new wxMenu(); // XXX とりあえず適当なところにおいとく menuFile->Append(ID_FULL_SPEED, _("Full &Speed Mode"), "", wxITEM_CHECK); - menuFile->Append(ID_RESET_VM, _("&Reset VM"), ""); + menuFile->AppendSeparator(); + menuFile->Append(ID_RESTART_VM, _("&Restart VM"), ""); menuFile->Append(wxID_EXIT, _("E&xit")); menubar->Append(menuFile, _("&File")); @@ -104,69 +103,91 @@ WXMainFrame::WXMainFrame(wxWindow *paren menuMonRate->AppendRadioItem(ID_MONRATE_5, _("Middle (&5Hz)")); menuMonRate->AppendRadioItem(ID_MONRATE_1, _("Low (&1Hz)")); menuDisplay->AppendSubMenu(menuMonRate, _("&Monitor Speed")); + menuDisplay->Append(ID_SHOW_STATPANEL, _("Show Status &Panel"), "", + wxITEM_CHECK); menubar->Append(menuDisplay, _("&View")); wxMenu *menuDevice = new wxMenu(); - menuDevice->Append(WinID_SOFTKEY, _("Software &Keyboard"), + CreateRemovableMenu(menuDevice); + menuDevice->Append(ID_SUBWIN_SOFTKEY, _("Software &Keyboard"), "", wxITEM_CHECK); menuDevice->Append(ID_MOUSEMODE, _("&Mouse Mode\tCTRL+F12"), "", wxITEM_CHECK); if (gMainApp.HasVMFeature(VMF_LUNA)) { - menuDevice->Append(WinID_LCD, _("Front &LCD"), + menuDevice->Append(ID_SUBWIN_LCDPANEL, _("Front &LCD"), "", wxITEM_CHECK); } + menuDevice->AppendSeparator(); + wxMenu *menuOper = new wxMenu(); + menuOper->Append(ID_RESET_BUTTON, _("Push &Reset button")); + menuDevice->AppendSubMenu(menuOper, _("Operation")); menubar->Append(menuDevice, _("&Device")); // 機種によってモニタウィンドウを追加する #define AppendIf(menu, id, label) do { \ - int id_ = __CONCAT(WinID_MONITOR_,id); \ - const auto& info_ = monitor_info[(id_)]; \ - if (gMainApp.HasVMFeature(info_.feature)) { \ - (menu)->Append((id_), (label), "", wxITEM_CHECK); \ + vmf_t feature_ = gMonitorManager.GetFeature(id); \ + if (gMainApp.HasVMFeature(feature_)) { \ + (menu)->Append((id), (label), "", wxITEM_CHECK); \ + } \ +} while (0) +#define AppendIfSeparator(menu, feature_) do { \ + if (gMainApp.HasVMFeature(feature_)) { \ + (menu)->AppendSeparator(); \ } \ } while (0) wxMenu *menuMonitor = new wxMenu(); - AppendIf(menuMonitor, SCHEDULER, _("&Scheduler")); - AppendIf(menuMonitor, LOG, _("&Log")); + AppendIf(menuMonitor, ID_MONITOR_SCHEDULER, _("&Scheduler")); + AppendIf(menuMonitor, ID_SUBWIN_LOG, _("&Log")); wxMenu *menuDebug = new wxMenu(); - AppendIf(menuDebug, BPOINT, _("&Breakpoint")); - AppendIf(menuDebug, BRHIST68K, _("Branch &History")); - AppendIf(menuDebug, BRHIST88K, _("Branch &History")); - AppendIf(menuDebug, EXHIST68K, _("&Exception History")); - AppendIf(menuDebug, EXHIST88K, _("&Exception History")); + AppendIf(menuDebug, ID_MONITOR_BREAKPOINT, _("&Breakpoint")); + AppendIf(menuDebug, ID_SUBWIN_BRHIST, _("Branch &History")); + AppendIf(menuDebug, ID_SUBWIN_EXHIST, _("&Exception History")); menuMonitor->AppendSubMenu(menuDebug, _("&Debug")); wxMenu *menuMPU = new wxMenu(); - AppendIf(menuMPU, MPUREG, _("&Register")); - AppendIf(menuMPU, MPUATC, "&ATC"); - AppendIf(menuMPU, CMMU7, "CMMU#7" + _("(Inst)")); - AppendIf(menuMPU, ATC7, "ATC#7"); - AppendIf(menuMPU, CACHE7, _("Cache#7")); - AppendIf(menuMPU, CMMU6, "CMMU#6" + _("(Data)")); - AppendIf(menuMPU, ATC6, "ATC#6"); - AppendIf(menuMPU, CACHE6, _("Cache#6")); - menuMonitor->AppendSubMenu(menuMPU, "MP&U"); - - AppendIf(menuMonitor, MEMDUMP, _("&Memory Dump")); - AppendIf(menuMonitor, INTERRUPT, _("&Interrupt")); - AppendIf(menuMonitor, SPC, "S&PC"); - AppendIf(menuMonitor, RTC, "&RTC"); - AppendIf(menuMonitor, DMAC, "DM&AC"); - AppendIf(menuMonitor, MFP, "M&FP"); - AppendIf(menuMonitor, SCC, "SCC(&Z)"); - AppendIf(menuMonitor, SIO, "SIO(&U)"); - AppendIf(menuMonitor, PIO0, "PIO0"); - AppendIf(menuMonitor, LUNAKEYBOARD, _("&Keyboard/Mouse")); - AppendIf(menuMonitor, LANCE, "Lance(&E)"); - AppendIf(menuMonitor, LCD, "LCD(&J)"); - AppendIf(menuMonitor, BITMAP, _("&Bitmap Control")); - AppendIf(menuMonitor, BITMAP0, _("Bitmap #&0")); + AppendIf(menuMPU, ID_MONITOR_MPUREG, _("&Register")); + AppendIf(menuMPU, ID_MONITOR_MPUATC, "&ATC"); + AppendIfSeparator(menuMPU, VMF_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); + AppendIf(menuMPU, ID_MONITOR_CMMU6, "CMMU#6" + _("(Data)")); + AppendIf(menuMPU, ID_MONITOR_ATC6, "ATC#6"); + AppendIf(menuMPU, ID_SUBWIN_CACHE6, _("Cache") + "#6"); + menuMonitor->AppendSubMenu(menuMPU, "MP&U"); + + wxMenu *menuMemdump = new wxMenu(); + // MEMDUMPn のラベルは UpdateUI で書き直すのでここでは適当に埋めておく。 + // (empty だと怒られる) + // MONITOR_RTC は X68k の RTC、LUNA では MONITOR_MK48T02 だが今の所 + // NVRAM 側のことは表示してないのでラベルも RTC にしてある。 + for (int i = 0; i < MAX_MEMDUMP_MONITOR; i++) { + AppendIf(menuMemdump, ID_MONITOR_MEMDUMP0 + i, "memdump"); + } + menuMonitor->AppendSubMenu(menuMemdump, _("&Memory Dump")); + AppendIf(menuMonitor, ID_MONITOR_INTERRUPT, _("&Interrupt")); + AppendIf(menuMonitor, ID_MONITOR_SPC, "S&PC"); + AppendIf(menuMonitor, ID_MONITOR_SCSIDEVS, _("SCSI Devices")); + AppendIf(menuMonitor, ID_MONITOR_RTC, "&RTC"); + AppendIf(menuMonitor, ID_MONITOR_MK48T02, "&RTC"); + AppendIf(menuMonitor, ID_MONITOR_DMAC, "DM&AC"); + AppendIf(menuMonitor, ID_MONITOR_MFP, "M&FP"); + AppendIf(menuMonitor, ID_MONITOR_SCC, "SCC(&Z)"); + AppendIf(menuMonitor, ID_MONITOR_SIO, "SIO(&U)"); + AppendIf(menuMonitor, ID_MONITOR_PIO0, "PIO0"); + AppendIf(menuMonitor, ID_MONITOR_KEYBOARD, _("&Keyboard/Mouse")); + AppendIf(menuMonitor, ID_MONITOR_LANCE, "Lance(&E)"); + AppendIf(menuMonitor, ID_MONITOR_LCD, "LCD(&J)"); + AppendIf(menuMonitor, ID_MONITOR_SYSCLK, _("S&ystem Clock")); + AppendIf(menuMonitor, ID_MONITOR_LUNAFB, _("&Bitmap Control")); + AppendIf(menuMonitor, ID_SUBWIN_LUNAFB0, _("Bitmap #&0")); wxMenu *menuHost = new wxMenu(); - AppendIf(menuHost, HOSTINFO, _("Host &Information")); - AppendIf(menuHost, NETDRIVER, _("Host &Network")); + AppendIf(menuHost, ID_MONITOR_WXHOSTINFO, _("Host &Information")); + AppendIf(menuHost, ID_MONITOR_HOSTNET, _("Host &Network")); menuMonitor->AppendSubMenu(menuHost, _("&Host")); menubar->Append(menuMonitor, _("&Monitor")); @@ -213,18 +234,30 @@ WXMainFrame::WXMainFrame(wxWindow *paren wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + // WXStatusPanel コンストラクタが gMainView を参照するので + // gMainView のほうを先に作成すること。 gMainView = new WXMainView(this); + gStatusPanel = new WXStatusPanel(this); + vbox->Add(gStatusPanel, 0, wxEXPAND); vbox->Add(gMainView, 1, wxEXPAND | wxFIXED_MINSIZE); + // SHOW イベントを最初にも一度起こしたいため + // ステータスパネルを一旦非表示にする (これで SHOW(false) イベントが飛ぶ) + gStatusPanel->Hide(); + const ConfigItem& item_show = gConfig->Find("show-statuspanel"); + if (item_show.AsInt() != 0) { + // 必要なら改めて表示 (これで SHOW(true) イベントが飛ぶ) + gStatusPanel->Show(); + } + SetSizer(vbox); - Layout(); Fit(); // モニタの更新頻度は初期値だけ設定から読んで、あとはここで管理する。 // 不正値ならデフォルトの 15Hz にしておくか - const ConfigItem& item = gConfig->Find("monitor-rate"); - monitor_rate = item.AsInt(); + const ConfigItem& item_rate = gConfig->Find("monitor-rate"); + monitor_rate = item_rate.AsInt(); if (monitor_rate < 1 || monitor_rate > 60) { monitor_rate = 15; } @@ -236,40 +269,130 @@ WXMainFrame::WXMainFrame(wxWindow *paren Connect(wxEVT_ACTIVATE, wxActivateEventHandler(WXMainView::OnActivate), NULL, gMainView); - POST_CREATE(); + // ウィンドウ表示後に続きの処理をするための単発アイドルイベント。 + Connect(wxEVT_IDLE, wxIdleEventHandler(WXMainFrame::OnIdle)); + + // VM からの通知を接続 + WXUIMessage::Connect(UIMessage::POWEROFF, this, + wxCommandEventHandler(WXMainFrame::OnExit)); + WXUIMessage::Connect(UIMessage::SCSI_MEDIA_FAILED, this, + wxCommandEventHandler(WXMainFrame::OnInsertCDFailed)); + if (gMPU680x0) { + WXUIMessage::Connect(UIMessage::HALT, this, + wxCommandEventHandler(WXMainFrame::OnHaltNotify)); + } } WXMainFrame::~WXMainFrame() { } -// ウィンドウ作成イベント +// リムーバブルデバイス用のメニューを作成して menuDevice に追加する void -WXMainFrame::OnCreate(wxCommandEvent& event) +WXMainFrame::CreateRemovableMenu(wxMenu *menuDevice) { - // ウィンドウを表示 - for (int i = 0; i < monitor_start.size(); i++) { - if (monitor_start[i]) { - DoWindow(i, true); + wxMenu *menu; + bool added = false; + + // FD + + // CD, MO + for (int id = 0; id < 8; id++) { + scsidev[id] = gSPC->GetTarget(id); + if (scsidev[id]) { + menu = CreateSCSIRemovableMenu(id); + if (menu) { + std::string label = string_format("%s (ID%d)", + SCSI::GetDevTypeName(scsidev[id]->GetDevType()), id); + menuDevice->AppendSubMenu(menu, label); + added = true; + } } } - // VM にコールバックを登録 - gVM->SetPowerOffCallback(wxmainframe_poweroff_callback); - if (gMPU680x0) { - gMPU680x0->SetHaltCallback(wxmainframe_halt_callback, this); + // 1つでも追加したらセパレータ + if (added) { + menuDevice->AppendSeparator(); + } +} + +// SCSI リムーバブルデバイス用のメニューを作成して返す。 +// id はデバイスが接続されていること (リムーバブルデバイスであるかは問わない)。 +// リムーバブルデバイスでなければ NULL を返す。 +// メニューバーとポップアップメニュー共通。 +wxMenu * +WXMainFrame::CreateSCSIRemovableMenu(uint id) const +{ + const auto *dev = scsidev[id]; + assert(dev); + + switch (dev->GetDevType()) { + case SCSI::DevType::CD: + case SCSI::DevType::MO: + { + wxMenu *menu = new wxMenu(); + // Insert の実際のラベルは UpdateUI でセットする。 + // ただし何か入れておかないといけないので適当にセット。 + menu->Append(ID_CD_INSERT(id), "Insert", ""); + menu->Append(ID_CD_EJECT(id), _("&Eject"), ""); + menu->Append(ID_CD_FORCE(id), _("&Force Eject"), ""); + return menu; + } + default: + return NULL; } +} + +// ウィンドウ作成イベント (を実現するためここではアイドルイベントを使う) +// +// 初期化(っぽい)もののうちコンストラクタでは早すぎるものは、コンストラクタ +// 直後に起こすこのイベント内で処理する。使えそうなイベントはいくつかあり、 +// 自前の Create (今はもうない) -> WindowCreate -> Idle -> Paint の順で +// 起きるようだ。 +// 自前の Create 時点では早すぎて、ここでサブウィンドウを作成するとサイズが 0 +// みたいなウィンドウが一瞬出来てしまい GTK のワーニングが出る (その後サイズを +// 矯正すれば表示自体は出来る)。 +// WindowCreate 時点ではその問題は起きないが、ここはまだメインウィンドウ表示 +// 前なので、ここでサブウィンドウを表示した後メインウィンドウを表示すると +// (ウィンドウマネージャにもよるかもだが)、メインウィンドウがサブウィンドウを +// 隠してしまう。 +// Idle イベント時点まで来ると上の2つの問題は回避できるようだ。 +// Paint イベントの時点でもタイミング的には問題ないが、環境によっては(?) +// このイベント自体が飛んで来ないことがあるようだ。WXMainFrame の再描画は +// Sizer の子コントロールが行っており、そのため WXMainFrame 自身が再描画 +// しなければならない領域は存在しないからかも知れないが詳細未調査。 +void +WXMainFrame::OnIdle(wxIdleEvent& ev) +{ + Fit(); + + // UIMessage の処理を開始する。 + // 少なくとも WXMainFrame のコンストラクタが完了した後のほうがいいはず。 + UIMessage::Attach(&WXUIMessage::Process); - // VM 実行開始 + // ウィンドウを表示 + for (int i = 0; i < subwindow_start.size(); i++) { + if (subwindow_start[i]) { + DoWindow(i, true); + } + } + + // 電源投入 gVM->PowerButton(); + + // 一度処理したら、このイベントを外す + Disconnect(wxEVT_IDLE, wxIdleEventHandler(WXMainFrame::OnIdle)); } // ウィンドウクローズイベント void WXMainFrame::OnClose(wxCloseEvent& event) { + // ステータスパネルの更新を停止 + gStatusPanel->Close(); + // サブウィンドウを全部閉じる - for (int i = 0; i < WinID_MAX; i++) { + for (int i = 0; i < ID_SUBWIN_MAX; i++) { if ((bool)windows[i]) { windows[i]->Close(); } @@ -304,11 +427,11 @@ WXMainFrame::OnFullSpeedUI(wxUpdateUIEve event.Check(is_fullspeed); } -// メニューの「Reset VM」イベント +// メニューの「Restart VM」イベント void -WXMainFrame::OnResetVM(wxCommandEvent& event) +WXMainFrame::OnRestartVM(wxCommandEvent& event) { - gScheduler->RequestResetHard(); + gScheduler->RequestRestartVM(); } // メニューの「終了」イベント @@ -340,7 +463,6 @@ WXMainFrame::OnMenuScale(wxCommandEvent& gMainView->DoResize(scale); // 再レイアウト - Layout(); Fit(); } @@ -384,13 +506,19 @@ WXMainFrame::OnMenuFontSize(wxCommandEve WXTextPanel::global_fontid = fontid; // アクティブなサブウィンドウ全員に通知 - for (int i = 0; i < WinID_MAX; i++) { + for (int i = 0; i < ID_SUBWIN_MAX; i++) { if ((bool)windows[i]) { WXSubWindow *subwin = dynamic_cast(windows[i].get()); subwin->SetFontSize(fontid); } } + + // ステータスパネルに通知 + gStatusPanel->SetFontSize(fontid); + + // ステータスパネルの大きさが変わるので再レイアウト + Fit(); } } @@ -421,7 +549,7 @@ WXMainFrame::OnMenuMonRate(wxCommandEven monitor_rate = IDtoMonRate[id - (ID_MONRATE_START + 1)]; // アクティブなサブウィンドウのテキストパネル全員に通知 - for (int i = 0; i < WinID_MAX; i++) { + for (int i = 0; i < ID_SUBWIN_MAX; i++) { if ((bool)windows[i]) { auto *subwin = dynamic_cast(windows[i].get()); for (wxWindow *child : subwin->GetChildren()) { @@ -445,6 +573,124 @@ WXMainFrame::OnMenuMonRateUI(wxUpdateUIE event.Check(monitor_rate == event_rate); } +// メニューの「表示 > ステータスパネルを表示」 +void +WXMainFrame::OnShowStatusPanel(wxCommandEvent& event) +{ + // enable ならチェックを付けようとして呼ばれた + bool enable = event.IsChecked(); + + gStatusPanel->Show(enable); + + // 再レイアウト + Fit(); +} + +// メニューの「表示 > ステータスパネルを表示」UI +void +WXMainFrame::OnShowStatusPanelUI(wxUpdateUIEvent& event) +{ + event.Check(gStatusPanel->IsShown()); +} + +// メニューの「デバイス > CD/MO > 挿入」 +void +WXMainFrame::OnCDInsert(wxCommandEvent& event) +{ + std::string title; + std::string wildcard; + + int id = event.GetId() - ID_CD_INSERT_0; + const auto *dev = scsidev[id]; + assert(dev); + + switch (dev->GetDevType()) { + case SCSI::DevType::CD: + title = _("Open a CD image"); + wildcard = "ISO images (*.iso;*.img)|*.iso;*.img;*.ISO;*.IMG"; + break; + case SCSI::DevType::MO: + title = _("Open an MO image"); + wildcard = "MO images (*.img;*.mos)|*.img;*.mos;*.IMG;*.MOS"; + break; + default: + __unreachable(); + } + wildcard += "|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()); + InsertCD(id, path); +} + +// メニューの「デバイス > CD/MO > 挿入」UI +void +WXMainFrame::OnCDInsertUI(wxUpdateUIEvent& event) +{ + int id = event.GetId() - ID_CD_INSERT_0; + SCSIDisk *disk = dynamic_cast(scsidev[id]); + assert(disk); + + if (disk->IsMediumLoaded()) { + event.SetText(_("Ex&change")); + } else { + event.SetText(_("&Insert")); + } +} + +// メニューの「デバイス > CD/MO > 取り出し」 +void +WXMainFrame::OnCDEject(wxCommandEvent& event) +{ + int id = event.GetId() - ID_CD_EJECT_0; + + EjectCD(id, false); +} + +// メニューの「デバイス > CD/MO > 取り出し」UI +void +WXMainFrame::OnCDEjectUI(wxUpdateUIEvent& event) +{ + int id = event.GetId() - ID_CD_EJECT_0; + SCSIDisk *disk = dynamic_cast(scsidev[id]); + assert(disk); + + // 挿入されてて取り出し可能な時のみ有効 + event.Enable(disk->IsMediumLoaded() && + disk->IsMediumRemovalAllowed()); +} + +// メニューの「デバイス > CD/MO > 強制取り出し」 +void +WXMainFrame::OnCDForce(wxCommandEvent& event) +{ + int id = event.GetId() - ID_CD_FORCE_0; + + EjectCD(id, true); +} + +// メニューの「デバイス > CD/MO > 強制取り出し」UI +void +WXMainFrame::OnCDForceUI(wxUpdateUIEvent& event) +{ + int id = event.GetId() - ID_CD_FORCE_0; + SCSIDisk *disk = dynamic_cast(scsidev[id]); + assert(disk); + + // 挿入されてて取り出し禁止の時のみ有効 + event.Enable(disk->IsMediumLoaded() && + disk->IsMediumRemovalPrevented()); +} + // メニューの「デバイス > マウスモード」 void WXMainFrame::OnMouseMode(wxCommandEvent& event) @@ -469,6 +715,14 @@ WXMainFrame::OnMouseModeUI(wxUpdateUIEve event.Check(gMainView->GetMouseMode()); } +// メニューの「デバイス > Push Reset Button」イベント +// こっちは本体リセットボタンによるリセット +void +WXMainFrame::OnResetButton(wxCommandEvent& event) +{ + gScheduler->RequestResetHard(); +} + // メニューのウィンドウ ID 共通イベント // チェックが入る方なら、ウィンドウを作成して表示。 // チェックが消える方なら、ウィンドウを削除。 @@ -483,124 +737,197 @@ WXMainFrame::OnWindow(wxCommandEvent& ev void WXMainFrame::DoWindow(int id, bool enable) { - // モニタウィンドウを作成する共通部分 -#define SetMonitor(name, obj) \ - windows[id].reset(new WXMonitorWindow(this, id, (name), *(obj))) + // モニタウィンドウを作成する共通部分。 + // id はモニター ID であると同時に、windows[] 配列の添字、さらにこの + // モニタウィンドウの wxWindowID としても使っている + // (WXSubWindow::OnClose() 参照)。 +#define SetMonitor(id, name) \ + windows[id].reset(new WXMonitorWindow(this, (name), \ + gMonitorManager.Get(id))) if (enable) { // チェックが入ったのでウィンドウを作成 switch (id) { - case WinID_MONITOR_LOG: - windows[id].reset(new WXLogMonitor(this, id)); - break; - case WinID_MONITOR_SCHEDULER: - SetMonitor(_("Scheduler"), gScheduler.get()); - break; - case WinID_MONITOR_MPUREG: - SetMonitor(_("MPU (Register)"), gMPU.get()); + case ID_MONITOR_ATC0: + case ID_MONITOR_ATC1: + case ID_MONITOR_ATC2: + case ID_MONITOR_ATC3: + case ID_MONITOR_ATC4: + case ID_MONITOR_ATC5: + case ID_MONITOR_ATC6: + case ID_MONITOR_ATC7: + { + int n = id - ID_MONITOR_ATC0; + wxString name(string_format("ATC#%d", n)); + if ((n & 1) == 0) { + name += _("(Data)"); + } else { + name += _("(Inst)"); + } + SetMonitor(id, name); break; - case WinID_MONITOR_MPUATC: - SetMonitor(_("MPU (ATC)"), gMPUATC.get()); + } + + case ID_MONITOR_BREAKPOINT: + SetMonitor(id, _("Breakpoint")); break; - case WinID_MONITOR_BPOINT: - SetMonitor(_("Breakpoint"), gBreakpointMonitor); + + case ID_MONITOR_CMMU0: + case ID_MONITOR_CMMU1: + case ID_MONITOR_CMMU2: + case ID_MONITOR_CMMU3: + case ID_MONITOR_CMMU4: + case ID_MONITOR_CMMU5: + case ID_MONITOR_CMMU6: + case ID_MONITOR_CMMU7: + { + int n = id - ID_MONITOR_CMMU0; + wxString name(string_format("CMMU#%d", n)); + if ((n & 1) == 0) { + name += _("(Data)"); + } else { + name += _("(Inst)"); + } + SetMonitor(id, name); break; - case WinID_MONITOR_BRHIST68K: - windows[id].reset(new WXBranchHistoryMonitor(this, id, - gMPU680x0->GetCPU()->brhist)); + } + + case ID_MONITOR_DMAC: + SetMonitor(id, "DMAC"); break; - case WinID_MONITOR_BRHIST88K: - windows[id].reset(new WXBranchHistoryMonitor(this, id, - gMPU88xx0->GetCPU()->brhist)); + + case ID_MONITOR_INTERRUPT: + SetMonitor(id, _("Interrupt")); break; - case WinID_MONITOR_EXHIST68K: - windows[id].reset(new WXExceptionHistoryMonitor(this, id, - gMPU680x0->GetCPU()->exhist)); + + case ID_MONITOR_KEYBOARD: + SetMonitor(id, _("Keyboard/Mouse")); break; - case WinID_MONITOR_EXHIST88K: - windows[id].reset(new WXExceptionHistoryMonitor(this, id, - gMPU88xx0->GetCPU()->exhist)); + + case ID_MONITOR_LANCE: + SetMonitor(id, "LANCE"); break; - case WinID_MONITOR_CMMU7: - SetMonitor("CMMU#7" + _("(Inst)"), - &gMPU88xx0->GetCPU()->cmmu[0]); + + case ID_MONITOR_LCD: + windows[id].reset(new WXLCDMonitor(this)); break; - case WinID_MONITOR_CMMU6: - SetMonitor("CMMU#6" + _("(Data)"), - &gMPU88xx0->GetCPU()->cmmu[1]); + + case ID_MONITOR_LUNAFB: + SetMonitor(id, _("Bitmap Control")); break; - case WinID_MONITOR_ATC7: - SetMonitor("ATC#7" + _("(Inst)"), gMPU88200ATC[0].get()); + + case ID_MONITOR_MEMDUMP0 ... + (ID_MONITOR_MEMDUMP0 + MAX_MEMDUMP_MONITOR - 1): + { + int n = id - ID_MONITOR_MEMDUMP0; + windows[id].reset(new WXMemdumpWindow(this, n)); break; - case WinID_MONITOR_ATC6: - SetMonitor("ATC#6" + _("(Data)"), gMPU88200ATC[1].get()); + } + + case ID_MONITOR_MFP: + SetMonitor(id, "MFP"); break; - case WinID_MONITOR_CACHE7: - windows[id].reset(new WXCacheWindow(this, id, - _("Cache#7") + _("(Inst)"), &gMPU88xx0->GetCPU()->cmmu[0])); + + case ID_MONITOR_MK48T02: + SetMonitor(id, "MK48T02"); break; - case WinID_MONITOR_CACHE6: - windows[id].reset(new WXCacheWindow(this, id, - _("Cache#6") + _("(Inst)"), &gMPU88xx0->GetCPU()->cmmu[1])); + + case ID_MONITOR_MPUATC: + SetMonitor(id, _("MPU (ATC)")); break; - case WinID_MONITOR_MEMDUMP: - windows[id].reset(new WXMemdumpWindow(this, id)); + + case ID_MONITOR_MPUREG: + SetMonitor(id, _("MPU (Register)")); break; - case WinID_MONITOR_INTERRUPT: - SetMonitor(_("Interrupt"), gInterrupt.get()); + + case ID_MONITOR_HOSTNET: + SetMonitor(id, _("Host Network")); break; - case WinID_MONITOR_SPC: - SetMonitor("SPC", gSPC.get()); + + case ID_MONITOR_PIO0: + SetMonitor(id, "PIO"); break; - case WinID_MONITOR_RTC: - SetMonitor("RTC", gRTC.get()); + + case ID_MONITOR_RTC: + SetMonitor(id, "RTC"); break; - case WinID_MONITOR_DMAC: - SetMonitor("DMAC", gDMAC.get()); + + case ID_MONITOR_SCC: + SetMonitor(id, "SCC"); break; - case WinID_MONITOR_MFP: - SetMonitor("MFP", gMFP.get()); + + case ID_MONITOR_SCHEDULER: + SetMonitor(id, _("Scheduler")); break; - case WinID_MONITOR_SCC: - SetMonitor("SCC", gSCC.get()); + + case ID_MONITOR_SCSIDEVS: + SetMonitor(id, _("SCSI Devices")); break; - case WinID_MONITOR_SIO: - SetMonitor("SIO", gSIO.get()); + + case ID_MONITOR_SIO: + SetMonitor(id, "SIO"); break; - case WinID_MONITOR_PIO0: - SetMonitor("PIO0", gPIO0.get()); + + case ID_MONITOR_SPC: + SetMonitor(id, "SPC"); break; - case WinID_MONITOR_LUNAKEYBOARD: - SetMonitor(_("Keyboard/Mouse"), gKeyboard.get()); + + case ID_MONITOR_SYSCLK: + SetMonitor(id, _("System Clock")); break; - case WinID_MONITOR_LCD: - windows[id].reset(new WXLCDMonitor(this, id)); + + case ID_MONITOR_WXHOSTINFO: + SetMonitor(id, _("Host Information")); break; - case WinID_MONITOR_LANCE: - SetMonitor("Lance", dynamic_cast(gEthernet.get())); + + case ID_SUBWIN_BRHIST: + windows[id].reset(new WXBranchHistoryMonitor(this, + gMonitorManager.Get(id))); break; - case WinID_MONITOR_HOSTINFO: - SetMonitor(_("Host Information"), gHostInfo.get()); + + case ID_SUBWIN_EXHIST: + windows[id].reset(new WXExceptionHistoryMonitor(this, + gMonitorManager.Get(id))); break; - case WinID_MONITOR_NETDRIVER: - SetMonitor(_("Host Network"), gNetDriver.get()); + + case ID_SUBWIN_CACHE0: + case ID_SUBWIN_CACHE1: + case ID_SUBWIN_CACHE2: + case ID_SUBWIN_CACHE3: + case ID_SUBWIN_CACHE4: + case ID_SUBWIN_CACHE5: + case ID_SUBWIN_CACHE6: + case ID_SUBWIN_CACHE7: + { + int n = id - ID_SUBWIN_CACHE0; + wxString name = _("Cache"); + name += string_format("#%d", n); + if ((n & 1) == 0) { + name += _("(Data)"); + } else { + name += _("(Inst)"); + } + windows[id].reset(new WXCacheWindow(this, name, + gMonitorManager.Get(id))); break; - case WinID_MONITOR_BITMAP: - SetMonitor(_("Bitmap Control"), gBitmap.get()); + } + + case ID_SUBWIN_LCDPANEL: + windows[id].reset(new WXLCDWindow(this)); break; - case WinID_MONITOR_BITMAP0: - windows[id].reset(new WXLunaBitmapWindow(this, id, _("Bitmap #0"))); + case ID_SUBWIN_LOG: + windows[id].reset(new WXLogMonitor(this)); break; - case WinID_SOFTKEY: - if (gMainApp.GetVMType() == VMTYPE_X68030) { - windows[id].reset(new WXX68kSoftKeyWindow(this, id)); - } else { - windows[id].reset(new WXLunaSoftKeyWindow(this, id)); - } + case ID_SUBWIN_LUNAFB0: + windows[id].reset(new WXLunaBitmapWindow(this, _("Bitmap #0"))); break; - case WinID_LCD: - windows[id].reset(new WXLCDWindow(this, id)); + + case ID_SUBWIN_SOFTKEY: + windows[id].reset(new WXSoftKeyWindow(this, gMainApp.GetVMType())); break; + + default: + assert(false); } windows[id]->Show(); } else { @@ -618,6 +945,13 @@ WXMainFrame::OnWindowUI(wxUpdateUIEvent& // ウィンドウが存在していればチェックを表示 event.Check((bool)windows[id]); + + if (IS_MONITOR_MEMDUMP(id)) { + // メニューに現在のアドレスを載せる + int n = id - ID_MONITOR_MEMDUMP0; + event.SetText(string_format("%d: $%08x", n, + WXMemdumpWindow::GetStickyAddr(id))); + } } // バージョンダイアログ @@ -628,49 +962,53 @@ WXMainFrame::OnAbout(wxCommandEvent& eve dlg.ShowModal(); } -// ウィンドウをリストから削除する -// (ウィンドウの Close() から呼ばれる) +// CD/MO を挿入する。 void -WXMainFrame::DeleteWindow(wxWindowID id) +WXMainFrame::InsertCD(int id, const std::string& path) { - windows[id].reset(); -} + SCSIDisk *scsidisk = dynamic_cast(scsidev[id]); + assert(scsidisk); -// VM 電源オフ時に呼ばれるコールバック。 -// (VM スレッドからアプリケーションを終了するため) -void -wxmainframe_poweroff_callback() -{ - gMainFrame->PowerOffCallback(); + // VM に依頼 + // (エラーが起きれば別途 UI メッセージが飛んでくる) + scsidisk->LoadDiskUI(path); + + // 次回以降のダイアログのカレントディレクトリをここに更新 + auto pos = path.rfind('/'); + current_dir = path.substr(0, pos); } -// VM からの電源オフ処理 +// CD/MO をイジェクトする。 void -WXMainFrame::PowerOffCallback() +WXMainFrame::EjectCD(int id, bool force) { - wxCommandEvent *ev = new wxCommandEvent(wxEVT_MENU, wxID_EXIT); - QueueEvent(ev); + SCSIDisk *scsidisk = dynamic_cast(scsidev[id]); + assert(scsidisk); + + scsidisk->UnloadDisk(force); } -// m680x0 ダブルバスフォールト時に呼ばれるコールバック。 -// step1. グローバル関数で受けて、クラスメンバ関数を呼ぶだけ。 +// CD/MO の挿入失敗メッセージ void -wxmainframe_halt_callback(void *arg) +WXMainFrame::OnInsertCDFailed(wxCommandEvent& event) { - ((WXMainFrame *)arg)->HaltCallback(); + ::wxMessageBox(_("Cannot load the image file")); } -// m680x0 ダブルバスフォールト処理。 -// step2. クラス内だが VM スレッドなので、ここではイベントをポストするだけ。 +// ウィンドウをリストから削除する +// (ウィンドウの Close() から呼ばれる) void -WXMainFrame::HaltCallback() +WXMainFrame::DeleteWindow(wxWindow *win) { - wxCommandEvent *ev = new wxCommandEvent(WXW_EVT_HALT_NOTIFY); - QueueEvent(ev); + for (int id = 0; id < ID_SUBWIN_MAX; id++) { + if (windows[id].get() == win) { + windows[id].reset(); + break; + } + } } // m680x0 ダブルバスフォールト処理 -// step3. ここが本題。でもダイアログを出すだけ。 void WXMainFrame::OnHaltNotify(wxCommandEvent& event) { @@ -679,45 +1017,5 @@ WXMainFrame::OnHaltNotify(wxCommandEvent wxICON_EXCLAMATION); } -// モニタウィンドウ識別名一覧 -// wxmainframe.h の識別子定義と順序を一致させること。 -// brhist は同じ名前だがフラグが異なるので共存できる。 -/*static*/ struct WXMainFrame::monitor_info -WXMainFrame::monitor_info[WinID_MAX] = { - { "log", VMF_ALL }, // LOG - { "scheduler", VMF_ALL }, // SCHEDULER - { "mpureg", VMF_ALL }, // MPUREG - { "mpuatc", VMF_M68K }, // MPUATC - { "breakpoint", VMF_ALL }, // BPOINT - { "brhist", VMF_M68K }, // BRHIST68K - { "brhist", VMF_M88K }, // BRHIST88K - { "exhist", VMF_M68K }, // EXHIST68K - { "exhist", VMF_M88K }, // EXHIST88K - { "cmmu7", VMF_M88K }, // CMMU#7 - { "cmmu6", VMF_M88K }, // CMMU#6 - { "atc7", VMF_M88K }, // ATC#7 - { "atc6", VMF_M88K }, // ATC#6 - { "cache7", VMF_M88K }, // Cache#7 - { "cache6", VMF_M88K }, // Cache#6 - { "memdump", VMF_ALL }, // MEMDUMP - { "interrupt", VMF_ALL }, // INTERRUPT - { "spc", VMF_ALL }, // SPC - { "rtc", VMF_ALL }, // RTC - { "dmac", VMF_X68K }, // DMAC - { "mfp", VMF_X68K }, // MFP - { "scc", VMF_X68K }, // SCC - { "sio", VMF_LUNA }, // SIO - { "pio0", VMF_LUNA }, // PIO0 - { "keyboard", VMF_LUNA }, // LunaKeyboard - { "lcd", VMF_LUNA }, // LCD (Monitor) - { "lance", VMF_LUNA }, // LANCE - { "bitmap", VMF_LUNA }, // BITMAP - { "bitmap0", VMF_LUNA }, // BITMAP0 - { "hostinfo", VMF_ALL }, // HOSTINFO - { "netdriver", VMF_LUNA }, // NETDRIVER - { "softkey", VMF_ALL }, // SOFTKEY - { "lcdpanel", VMF_LUNA }, // LCD (Panel) -}; - -// 起動時にオープンするモニタウィンドウ (-M オプション) -/*static*/ std::array WXMainFrame::monitor_start; +// 起動時にオープンするサブウィンドウ (-M オプション) +/*static*/ std::array WXMainFrame::subwindow_start;