--- nono/wx/wxmainframe.cpp 2026/04/29 17:04:28 1.1 +++ nono/wx/wxmainframe.cpp 2026/04/29 17:05:04 1.1.1.12 @@ -1,40 +1,45 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "vm.h" -#include "bitmap.h" -#include "bt454.h" -#include "lance.h" -#include "lcd.h" -#include "mfp.h" -#include "mpu.h" -#include "netdriver.h" -#include "rtc.h" -#include "scheduler.h" -#include "sio.h" -#include "spc.h" -#include "wxheader.h" +#include "wxmainframe.h" #include "wxdumpmonitor.h" +#include "wxhistmonitor.h" +#include "wxlcdwindow.h" #include "wxlogmonitor.h" -#include "wxmainframe.h" #include "wxmainview.h" #include "wxmonitor.h" +#include "wxmpumonitor.h" #include "wxsoftkey.h" +#include "wxstatuspanel.h" #include "wxtextpanel.h" +#include "wxuimessage.h" +#include "wxversion.h" #include "wxvideomonitor.h" - -static void wxmainframe_poweroff_callback(); +#include "debugger.h" +#include "mainapp.h" +#include "mpu680x0.h" +#include "mpu88xx0.h" +#include "netdriver.h" +#include "scheduler.h" +#include "spc.h" +#include "vm.h" WXMainFrame *gMainFrame; wxBEGIN_EVENT_TABLE(WXMainFrame, inherited) - EVT_CREATE(WXMainFrame::OnCreate) + EVT_CLOSE(WXMainFrame::OnClose) EVT_MENU(wxID_EXIT, WXMainFrame::OnExit) + EVT_MENU(ID_RESTART_VM, WXMainFrame::OnRestartVM) EVT_MENU(ID_FULL_SPEED, WXMainFrame::OnFullSpeed) EVT_UPDATE_UI(ID_FULL_SPEED, WXMainFrame::OnFullSpeedUI) + EVT_MENU_RANGE(ID_INPUT_START + 1, ID_INPUT_END - 1, + WXMainFrame::OnMenuInput) + EVT_UPDATE_UI_RANGE(ID_INPUT_START + 1, ID_INPUT_END -1, + WXMainFrame::OnMenuInputUI) 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, @@ -43,91 +48,190 @@ wxBEGIN_EVENT_TABLE(WXMainFrame, inherit WXMainFrame::OnMenuFontSize) EVT_UPDATE_UI_RANGE(ID_FONTSIZE_START + 1, ID_FONTSIZE_END - 1, WXMainFrame::OnMenuFontSizeUI) - EVT_MENU_RANGE(WinID_Start, WinID_MAX - 1, WXMainFrame::OnWindow) - EVT_UPDATE_UI_RANGE(WinID_Start, WinID_MAX - 1, WXMainFrame::OnWindowUI) + EVT_MENU_RANGE(ID_MONRATE_START + 1, ID_MONRATE_END - 1, + 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(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) wxEND_EVENT_TABLE() WXMainFrame::WXMainFrame(wxWindow *parent) - : wxFrame(parent, wxID_ANY, "mm") + : inherited(parent, wxID_ANY, "nono", wxDefaultPosition, wxDefaultSize, + wxDEFAULT_FRAME_STYLE & ~wxMAXIMIZE_BOX & ~wxRESIZE_BORDER) { - int vmtype; + vmtype_t vmtype; + vmtype = gMainApp.GetVMType(); - vmtype = gVM->GetType(); - - // とりあえずメニュー - // XXX すぐにメニューファクトリーになる + // メニュー wxMenuBar *menubar = new wxMenuBar(); + // メニュー > ファイル wxMenu *menuFile = new wxMenu(); - // XXX とりあえず適当なところにおいとく - menuFile->Append(ID_FULL_SPEED, wxT("高速モード"), wxT("高速モード"), - wxITEM_CHECK); - menuFile->Append(wxID_EXIT, wxT("終了")); - menubar->Append(menuFile, wxT("ファイル")); + menuFile->Append(ID_RESTART_VM, _("&Restart VM"), ""); + menuFile->Append(wxID_EXIT, _("E&xit")); + menubar->Append(menuFile, _("&File")); + + // メニュー > 設定 + wxMenu *menuSet = new wxMenu(); + menuSet->Append(ID_FULL_SPEED, _("Full &Speed Mode"), "", wxITEM_CHECK); + wxMenu *menuSetInput = new wxMenu(); + menuSetInput->AppendRadioItem(ID_INPUT_CHAR, _("By &Character")); + menuSetInput->AppendRadioItem(ID_INPUT_JP, _("&JP Keyboard")); + menuSet->AppendSubMenu(menuSetInput, _("&Key Input")); + menubar->Append(menuSet, _("&Setting")); + // メニュー > 表示 wxMenu *menuDisplay = new wxMenu(); wxMenu *menuViewScale = new wxMenu(); - menuViewScale->AppendRadioItem(ID_SCALE_1_00, wxT("x1.0"), wxT("x1.0")); - menuViewScale->AppendRadioItem(ID_SCALE_0_75, wxT("x0.75"), wxT("x0.75")); - menuViewScale->AppendRadioItem(ID_SCALE_0_50, wxT("x0.5"), wxT("x0.5")); - menuDisplay->AppendSubMenu(menuViewScale, wxT("スケール"), wxT("スケール")); + menuViewScale->Append(ID_SCALE_1_00, _("x1.&0"), "", wxITEM_CHECK); + menuViewScale->Append(ID_SCALE_0_75, _("x0.&75"), "", wxITEM_CHECK); + menuViewScale->Append(ID_SCALE_0_50, _("x0.&5"), "", wxITEM_CHECK); + menuDisplay->AppendSubMenu(menuViewScale, _("&Scale")); wxMenu *menuFontSize = new wxMenu(); - menuFontSize->AppendRadioItem(ID_FONTSIZE_12, wxT("12"), wxT("12")); - menuFontSize->AppendRadioItem(ID_FONTSIZE_16, wxT("16"), wxT("16")); - menuFontSize->AppendRadioItem(ID_FONTSIZE_24, wxT("24"), wxT("24")); - menuDisplay->AppendSubMenu(menuFontSize, wxT("フォントサイズ"), - wxT("フォントサイズ")); - menubar->Append(menuDisplay, wxT("表示")); + menuFontSize->AppendRadioItem(ID_FONTSIZE_12, "1&2"); + menuFontSize->AppendRadioItem(ID_FONTSIZE_16, "1&6"); + menuDisplay->AppendSubMenu(menuFontSize, _("&Font Size")); + wxMenu *menuMonRate = new wxMenu(); + menuMonRate->AppendRadioItem(ID_MONRATE_20, _("High (&20Hz)")); + 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, wxT("ソフトウェアキーボード"), - wxT("ソフトウェアキーボード"), wxITEM_CHECK); - menubar->Append(menuDevice, wxT("デバイス")); + 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(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 { \ + 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(); - menuMonitor->Append(WinID_MONITOR_SCHEDULER, wxT("スケジューラ"), - wxT("スケジューラ"), wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_LOG, wxT("ログ"), wxT("ログ"), - wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_MPUREG, wxT("MPU レジスタ"), - wxT("MPU レジスタ"), wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_MPUATC, wxT("MPU(ATC)"), wxT("MPU(ATC)"), - wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_MPUBRHIST, wxT("MPU(ブランチ履歴)"), - wxT("MPU(ブランチ履歴)"), wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_MEMDUMP, wxT("メモリダンプ"), - wxT("メモリダンプ"), wxITEM_CHECK); - if (vmtype == VM::TYPE_X68030) { - menuMonitor->Append(WinID_MONITOR_MFP, wxT("MFP"), wxT("MFP"), - wxITEM_CHECK); + AppendIf(menuMonitor, ID_MONITOR_SCHEDULER, _("&Scheduler")); + AppendIf(menuMonitor, ID_SUBWIN_LOG, _("&Log")); + + wxMenu *menuDebug = new wxMenu(); + 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, 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->Append(WinID_MONITOR_SPC, wxT("SPC"), wxT("SPC"), - wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_RTC, wxT("RTC"), wxT("RTC"), - wxITEM_CHECK); - if (vmtype == VM::TYPE_LUNA) { - menuMonitor->Append(WinID_MONITOR_SIO, wxT("SIO"), wxT("SIO"), - wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_LCD, wxT("LCD"), wxT("LCD"), - wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_LANCE, wxT("Lance"), wxT("Lance"), - wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_BITMAP, wxT("ビットマップ制御"), - wxT("ビットマップ制御"), wxITEM_CHECK); - menuMonitor->Append(WinID_MONITOR_BITMAP0, wxT("ビットマップ#0"), - wxT("ビットマップ#0"), wxITEM_CHECK); - } - menuMonitor->Append(WinID_MONITOR_NETDRIVER, wxT("ホストネットワーク"), - wxT("ホストネットワーク"), wxITEM_CHECK); - menubar->Append(menuMonitor, wxT("モニター")); + 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, ID_MONITOR_WXHOSTINFO, _("Host &Information")); + AppendIf(menuHost, ID_MONITOR_HOSTNET, _("Host &Network")); + menuMonitor->AppendSubMenu(menuHost, _("&Host")); + + menubar->Append(menuMonitor, _("&Monitor")); + + // メニュー > ヘルプ +#if defined(__WXOSX__) + // OSX では wxID_ABOUT はリンゴメニューに入れるしきたりになっている。 + // Append() すると Quit より下に来てしまうので、Prepend() で上に追加。 + // 今のところヘルプメニューから About ダイアログを抜くとメニューが空に + // なってしまうので、ヘルプメニューの追加ごと抑制する。 + menubar->OSXGetAppleMenu()->Prepend(wxID_ABOUT, _("About nono")); +#else + wxMenu *menuHelp = new wxMenu(); + menuHelp->Append(wxID_ABOUT, _("About nono")); + menubar->Append(menuHelp, _("&Help")); +#endif + SetMenuBar(menubar); wxString title("nono"); switch (vmtype) { - case VM::TYPE_LUNA: + case VMTYPE_X68030: + title += "(X68030)"; + break; + case VMTYPE_LUNA1: title += "(LUNA-I)"; break; - case VM::TYPE_X68030: - title += "(X68030)"; + case VMTYPE_LUNA88K: + title += "(LUNA88K)"; + break; + default: break; } SetTitle(title); @@ -144,39 +248,203 @@ 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(); - POST_CREATE(); + // モニタの更新頻度は初期値だけ設定から読んで、あとはここで管理する。 + // 不正値ならデフォルトの 15Hz にしておくか + const ConfigItem& item_rate = gConfig->Find("monitor-rate"); + monitor_rate = item_rate.AsInt(); + if (monitor_rate < 1 || monitor_rate > 60) { + monitor_rate = 15; + } + + // Activate イベントは、このトップレベルウィンドウにウィンドウフォーカスが + // 来た or 外れたことを示すので、wxFrame (というかたぶん wxTopLevelWindow) + // を継承している WXMainFrameに飛んでくるのだが、処理の都合上 WXMainView で + // 扱いたいので、そちらに回す。 + Connect(wxEVT_ACTIVATE, wxActivateEventHandler(WXMainView::OnActivate), + NULL, gMainView); + + // ウィンドウ表示後に続きの処理をするための単発アイドルイベント。 + 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) { + 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; + } + } + } + + // 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; + } +} + +// (設定反映などの) 初期化。 +// 成功すれば true、失敗すれば false を返す。 +bool +WXMainFrame::Init() +{ + if (!gMainView->Init()) { + return false; + } + + return true; +} + +// ウィンドウ作成イベント (を実現するためここではアイドルイベントを使う) +// +// 初期化(っぽい)もののうちコンストラクタでは早すぎるものは、コンストラクタ +// 直後に起こすこのイベント内で処理する。使えそうなイベントはいくつかあり、 +// 自前の 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); + // ウィンドウを表示 - for (int i = 0; i < monitor_start.size(); i++) { - if (monitor_start[i]) { + for (int i = 0; i < subwindow_start.size(); i++) { + if (subwindow_start[i]) { DoWindow(i, true); } } - // 時刻開始? - // なるべく VM が開始する直前にしたい。 - gRTC->InitRTC(); - - // VM 実行開始 - gVM->SetPowerOffCallback(wxmainframe_poweroff_callback); + // 電源投入 gVM->PowerButton(); + + // 一度処理したら、このイベントを外す + Disconnect(wxEVT_IDLE, wxIdleEventHandler(WXMainFrame::OnIdle)); +} + +// ウィンドウクローズイベント +void +WXMainFrame::OnClose(wxCloseEvent& event) +{ + // ステータスパネルの更新を停止 + gStatusPanel->Close(); + + // サブウィンドウを全部閉じる + for (int i = 0; i < ID_SUBWIN_MAX; i++) { + if ((bool)windows[i]) { + windows[i]->Close(); + } + } + + // VM を解放 + gVM->Dispose(); + gVM.reset(); + + // 後はデフォルト処理 + event.Skip(); +} + +// メニューの「Restart VM」イベント +void +WXMainFrame::OnRestartVM(wxCommandEvent& event) +{ + gScheduler->RequestRestartVM(); +} + +// メニューの「終了」イベント +void +WXMainFrame::OnExit(wxCommandEvent& event) +{ + // ウィンドウを閉じる + Close(); } // メニューの「高速モード」イベント @@ -200,12 +468,26 @@ WXMainFrame::OnFullSpeedUI(wxUpdateUIEve event.Check(is_fullspeed); } -// メニューの「終了」イベント +// メニューの「設定 > 入力 > *」の共通イベント void -WXMainFrame::OnExit(wxCommandEvent& event) +WXMainFrame::OnMenuInput(wxCommandEvent& event) { - // ウィンドウを閉じる - Close(); + bool char_mode = (event.GetId() == ID_INPUT_CHAR); + + gMainView->SetCharInputMode(char_mode); +} + +// メニューの「設定 > 入力 > *」の共通 UI +void +WXMainFrame::OnMenuInputUI(wxUpdateUIEvent& event) +{ + bool char_mode = gMainView->GetCharInputMode(); + + if (char_mode) { + event.Check(event.GetId() == ID_INPUT_CHAR); + } else { + event.Check(event.GetId() == ID_INPUT_JP); + } } // メニューの「表示 > スケール > *」の共通イベント @@ -229,7 +511,6 @@ WXMainFrame::OnMenuScale(wxCommandEvent& gMainView->DoResize(scale); // 再レイアウト - Layout(); Fit(); } @@ -238,57 +519,54 @@ void WXMainFrame::OnMenuScaleUI(wxUpdateUIEvent& event) { double current_scale = gMainView->GetScale(); - double event_scale; - switch (event.GetId()) { - case ID_SCALE_1_00: - event_scale = 1.0; - break; - case ID_SCALE_0_75: - event_scale = 0.75; - break; - case ID_SCALE_0_50: - event_scale = 0.50; - break; - default: - event_scale = 0; - break; + if (current_scale == 1.0) { + event.Check(event.GetId() == ID_SCALE_1_00); + } else if (current_scale == 0.75) { + event.Check(event.GetId() == ID_SCALE_0_75); + } else if (current_scale == 0.5) { + event.Check(event.GetId() == ID_SCALE_0_50); } - event.Check(current_scale == event_scale); } // メニューの「表示 > フォントサイズ > *」の共通イベント void WXMainFrame::OnMenuFontSize(wxCommandEvent& event) { - fontsize_t fontsize; + FontId fontid; - assert(ID_FONTSIZE_12 <= event.GetId() && event.GetId() <= ID_FONTSIZE_24); + assertmsg( + ID_FONTSIZE_12 <= event.GetId() && event.GetId() <= ID_FONTSIZE_16, + "event.GetId()=%d", event.GetId()); switch (event.GetId()) { case ID_FONTSIZE_12: - fontsize = FONT_6x12; + fontid = FontId::_6x12; break; case ID_FONTSIZE_16: - fontsize = FONT_8x16; - break; - case ID_FONTSIZE_24: - fontsize = FONT_12x24; + fontid = FontId::_8x16; break; default: __unreachable(); break; } - if (fontsize != WXTextPanel::global_fontsize) { - WXTextPanel::global_fontsize = fontsize; + if (fontid != WXTextPanel::global_fontid) { + WXTextPanel::global_fontid = fontid; // アクティブなサブウィンドウ全員に通知 - for (int i = 0; i < WinID_MAX; i++) { - if (windows[i] != NULL) { - WXSubWindow *subwin = dynamic_cast(windows[i]); - subwin->SetFontSize(fontsize); + 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(); } } @@ -296,12 +574,203 @@ WXMainFrame::OnMenuFontSize(wxCommandEve void WXMainFrame::OnMenuFontSizeUI(wxUpdateUIEvent& event) { - fontsize_t current_size = WXTextPanel::global_fontsize; - fontsize_t event_size = (fontsize_t)(event.GetId() - (ID_FONTSIZE_START+1)); + FontId current_size = WXTextPanel::global_fontid; + FontId event_size = (FontId)(event.GetId() - (ID_FONTSIZE_START+1)); event.Check(current_size == event_size); } +// 更新頻度 ID -> 数値 (メニューのテキスト、ID と一致させること) +/*static*/ const int +WXMainFrame::IDtoMonRate[] = { + 20, // ID_MONRATE_20 + 5, // ID_MONRATE_5 + 1, // ID_MONRATE_1 +}; + +// メニューの「表示 > モニタ更新頻度 > *」の共通イベント +void +WXMainFrame::OnMenuMonRate(wxCommandEvent& event) +{ + int id = event.GetId(); + assert(ID_MONRATE_START < id && id < ID_MONRATE_END); + monitor_rate = IDtoMonRate[id - (ID_MONRATE_START + 1)]; + + // アクティブなサブウィンドウのテキストパネル全員に通知 + 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()) { + auto *panel = dynamic_cast(child); + if (panel) { + panel->SetRate(monitor_rate); + } + } + } + } +} + +// メニューの「表示 > モニタ更新頻度 > *」共通 UI +void +WXMainFrame::OnMenuMonRateUI(wxUpdateUIEvent& event) +{ + int id = event.GetId(); + assert(ID_MONRATE_START < id && id < ID_MONRATE_END); + int event_rate = IDtoMonRate[id - (ID_MONRATE_START + 1)]; + + 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) +{ + // enable ならチェックを付けようとして呼ばれた + bool enable = event.IsChecked(); + + if (enable) { + // メッセージを表示したい + } else { + // メッセージを表示したい + } + + // マウスモードを設定 + gMainView->SetMouseMode(enable); +} + +// メニューの「デバイス > マウスモード」UI +void +WXMainFrame::OnMouseModeUI(wxUpdateUIEvent& event) +{ + event.Check(gMainView->GetMouseMode()); +} + +// メニューの「デバイス > Push Reset Button」イベント +// こっちは本体リセットボタンによるリセット +void +WXMainFrame::OnResetButton(wxCommandEvent& event) +{ + gScheduler->RequestResetHard(); +} + // メニューのウィンドウ ID 共通イベント // チェックが入る方なら、ウィンドウを作成して表示。 // チェックが消える方なら、ウィンドウを削除。 @@ -316,61 +785,197 @@ WXMainFrame::OnWindow(wxCommandEvent& ev void WXMainFrame::DoWindow(int id, bool enable) { + // モニタウィンドウを作成する共通部分。 + // 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] = new WXLogMonitor(this, id); + 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 ID_MONITOR_BREAKPOINT: + SetMonitor(id, _("Breakpoint")); + break; + + 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_SCHEDULER: - windows[id] = new WXMonitorWindow(this, id, "スケジューラ", - gScheduler); + } + + case ID_MONITOR_DMAC: + SetMonitor(id, "DMAC"); break; - case WinID_MONITOR_MPUREG: - windows[id] = new WXMonitorWindow(this, id, "MPU レジスタ", gMPU); + + case ID_MONITOR_INTERRUPT: + SetMonitor(id, _("Interrupt")); break; - case WinID_MONITOR_MPUATC: - windows[id] = new WXMonitorWindow(this, id, "MPU (ATC)", gMPUATC); + + case ID_MONITOR_KEYBOARD: + SetMonitor(id, _("Keyboard/Mouse")); break; - case WinID_MONITOR_MPUBRHIST: - windows[id] = new WXMonitorWindow(this, id, "MPU (ブランチ履歴)", - gMPUBrHist); + + case ID_MONITOR_LANCE: + SetMonitor(id, "LANCE"); break; - case WinID_MONITOR_MEMDUMP: - windows[id] = new WXMemdumpWindow(this, id); + + case ID_MONITOR_LCD: + windows[id].reset(new WXLCDMonitor(this)); break; - case WinID_MONITOR_MFP: - windows[id] = new WXMonitorWindow(this, id, "MFP", gMFP); + + case ID_MONITOR_LUNAFB: + SetMonitor(id, _("Bitmap Control")); break; - case WinID_MONITOR_SPC: - windows[id] = new WXMonitorWindow(this, id, "SPC", gSPC); + + 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_RTC: - windows[id] = new WXMonitorWindow(this, id, "RTC", gRTC); + } + + case ID_MONITOR_MFP: + SetMonitor(id, "MFP"); break; - case WinID_MONITOR_SIO: - windows[id] = new WXMonitorWindow(this, id, "SIO", gSIO); + + case ID_MONITOR_MK48T02: + SetMonitor(id, "MK48T02"); break; - case WinID_MONITOR_LCD: - windows[id] = new WXMonitorWindow(this, id, "LCD", gLCD); + + case ID_MONITOR_MPUATC: + SetMonitor(id, _("MPU (ATC)")); break; - case WinID_MONITOR_LANCE: - windows[id] = new WXMonitorWindow(this, id, "Lance", gLance); + + case ID_MONITOR_MPUREG: + SetMonitor(id, _("MPU (Register)")); break; - case WinID_MONITOR_NETDRIVER: - windows[id] = new WXMonitorWindow(this, id, "ホストネットワーク", - gNetDriver); + + case ID_MONITOR_HOSTNET: + SetMonitor(id, _("Host Network")); break; - case WinID_MONITOR_BITMAP: - windows[id] = new WXMonitorWindow(this, id, "ビットマップ制御", - gBitmap); + + case ID_MONITOR_PIO0: + SetMonitor(id, "PIO"); break; - case WinID_MONITOR_BITMAP0: - windows[id] = new WXLunaBitmapWindow(this, id, "ビットマップ#0"); + + case ID_MONITOR_RTC: + SetMonitor(id, "RTC"); break; - case WinID_SOFTKEY: - windows[id] = new WXLunaSoftKeyWindow(this, id); + + case ID_MONITOR_SCC: + SetMonitor(id, "SCC"); break; + + case ID_MONITOR_SCHEDULER: + SetMonitor(id, _("Scheduler")); + break; + + case ID_MONITOR_SCSIDEVS: + SetMonitor(id, _("SCSI Devices")); + break; + + case ID_MONITOR_SIO: + SetMonitor(id, "SIO"); + break; + + case ID_MONITOR_SPC: + SetMonitor(id, "SPC"); + break; + + case ID_MONITOR_SYSCLK: + SetMonitor(id, _("System Clock")); + break; + + case ID_MONITOR_WXHOSTINFO: + SetMonitor(id, _("Host Information")); + break; + + case ID_SUBWIN_BRHIST: + windows[id].reset(new WXBranchHistoryMonitor(this, + gMonitorManager.Get(id))); + break; + + case ID_SUBWIN_EXHIST: + windows[id].reset(new WXExceptionHistoryMonitor(this, + gMonitorManager.Get(id))); + break; + + 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 ID_SUBWIN_LCDPANEL: + windows[id].reset(new WXLCDWindow(this)); + break; + case ID_SUBWIN_LOG: + windows[id].reset(new WXLogMonitor(this)); + break; + case ID_SUBWIN_LUNAFB0: + windows[id].reset(new WXLunaBitmapWindow(this, _("Bitmap #0"))); + break; + + case ID_SUBWIN_SOFTKEY: + windows[id].reset(new WXSoftKeyWindow(this, gMainApp.GetVMType())); + break; + + default: + assert(false); } windows[id]->Show(); } else { @@ -387,54 +992,78 @@ WXMainFrame::OnWindowUI(wxUpdateUIEvent& int id = event.GetId(); // ウィンドウが存在していればチェックを表示 - event.Check((windows[id] != NULL)); + 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))); + } } -// ウィンドウをリストから削除する -// (ウィンドウの Close() から呼ばれる) +// バージョンダイアログ void -WXMainFrame::DeleteWindow(wxWindowID id) +WXMainFrame::OnAbout(wxCommandEvent& event) { - delete windows[id]; - windows[id] = NULL; + WXVersionDlg dlg(this); + dlg.ShowModal(); } -// VM 電源オフ時に呼ばれるコールバック。 -// (VM スレッドからアプリケーションを終了するため) +// CD/MO を挿入する。 void -wxmainframe_poweroff_callback() +WXMainFrame::InsertCD(int id, const std::string& path) { - gMainFrame->PowerOffCallback(); + SCSIDisk *scsidisk = dynamic_cast(scsidev[id]); + assert(scsidisk); + + // 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); } -// モニタウィンドウ識別名一覧 -/*static*/ const char * -WXMainFrame::monitor_names[WinID_MAX] = { - "log", // LOG - "scheduler", // SCHEDULER - "mpureg", // MPUREG - "mpuatc", // MPUATC - "mpuhist", // MPUBRHIST - "memdump", // MEMDUMP - "mfp", // MFP - "spc", // SPC - "rtc", // RTC - "sio", // SIO - "lcd", // LCD - "lance", // LANCE - "bitmap", // BITMAP - "bitmap0", // BITMAP0 - "netdriver", // NETDRIVER - "softkey", // SOFTKEY -}; +// CD/MO の挿入失敗メッセージ +void +WXMainFrame::OnInsertCDFailed(wxCommandEvent& event) +{ + ::wxMessageBox(_("Cannot load the image file")); +} + +// ウィンドウをリストから削除する +// (ウィンドウの Close() から呼ばれる) +void +WXMainFrame::DeleteWindow(wxWindow *win) +{ + for (int id = 0; id < ID_SUBWIN_MAX; id++) { + if (windows[id].get() == win) { + windows[id].reset(); + break; + } + } +} + +// m680x0 ダブルバスフォールト処理 +void +WXMainFrame::OnHaltNotify(wxCommandEvent& event) +{ + ::wxMessageBox(_("Double bus fault has occurred in VM"), + wxEmptyString, + wxICON_EXCLAMATION); +} -// 起動時にオープンするモニタウィンドウ (-M オプション) -/*static*/ std::array WXMainFrame::monitor_start; +// 起動時にオープンするサブウィンドウ (-M オプション) +/*static*/ std::array WXMainFrame::subwindow_start;