diff --git a/core/ARM/cpu.h b/core/ARM/cpu.h index d94754e..1323bf1 100644 --- a/core/ARM/cpu.h +++ b/core/ARM/cpu.h @@ -40,4 +40,9 @@ struct CPU { LOG_INFO(ARM, "X{} = {}", reg, x(reg)); // X0 = 0... } } + + void get_state(u64* out_regs, u64& out_pc) const { + std::memcpy(out_regs, regs, sizeof(regs)); + out_pc = pc; + } }; diff --git a/core/main.cpp b/core/main.cpp index 335c331..09c0d90 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -14,76 +14,106 @@ #include "gui/panels/CPUPanel.h" #include "gui/panels/PerformancePanel.h" +std::shared_ptr console_panel; +std::shared_ptr cpu_panel; +std::shared_ptr performance_panel; // CPU test function -void cpuTest() { +void cpuTest() +{ CPU cpu; cpu.pc = 0; - // Simple ARMv8 program in memory (MOVZ X0, #5; ADD X0, X0, #3; RET) - // These are placeholders; real encoding will be parsed later cpu.write_byte(0, 0x05); // MOVZ placeholder cpu.write_byte(4, 0x03); // ADD placeholder cpu.write_byte(8, 0xFF); // RET placeholder - LOG_INFO(ARM, "{}", cpu.read_byte(0)); - JIT jit; jit.translate_and_run(cpu); - cpu.print_debug_information(); - LOG_INFO(ARM, "X0 = {}", cpu.x(0)); + + if (cpu_panel) + cpu_panel->UpdateState(cpu); } -int main() { +void initGUI(Pound::GUI::GUIManager *gui_manager) +{ + gui_manager->AddPanel(console_panel); + gui_manager->AddPanel(cpu_panel); + gui_manager->AddPanel(performance_panel); + + Pound::GUI::TabBar *file_menu = gui_manager->AddTabs("File"); + Pound::GUI::TabBar *emulation_menu = gui_manager->AddTabs("Emulation"); + Pound::GUI::TabBar *view_menu = gui_manager->AddTabs("View"); + Pound::GUI::TabBar *help_menu = gui_manager->AddTabs("Help"); + + gui_manager->AddSubTab(file_menu, "Open ROM...", []() {}); + gui_manager->AddSubTab(file_menu, "Exit", []() + { + LOG_INFO(Render, "Exiting Pound Emulator"); + std::exit(0); }); + + gui_manager->AddSubTab(emulation_menu, "Run CPU Test", []() + { cpuTest(); }); + gui_manager->AddSubTab(emulation_menu, "Pause", []() + { LOG_INFO(Render, "Pausing emulation (not implemented yet)"); }); + gui_manager->AddSubTab(emulation_menu, "Resume", []() + { LOG_INFO(Render, "Resuming emulation (not implemented yet)"); }); + + gui_manager->AddSubTab(view_menu, "Console", console_panel->IsVisible(), + [console_panel](bool is_checked) + { + console_panel->SetVisible(is_checked); + }); + + gui_manager->AddSubTab(view_menu, "CPU State", cpu_panel->IsVisible(), + [cpu_panel](bool is_checked) + { + cpu_panel->SetVisible(is_checked); + }); + + gui_manager->AddSubTab(view_menu, "Performance", performance_panel->IsVisible(), + [performance_panel](bool is_checked) + { + performance_panel->SetVisible(is_checked); + }); + + gui_manager->AddSubTab(help_menu, "About", []() + { LOG_INFO(Render, "Pound Emulator is a pre-alpha project. Visit our GitHub for more information."); }); + + + cpu_panel->SetCPUTestCallback(cpuTest); + console_panel->AddLog("[INFO] Pound Emulator started"); + console_panel->AddLog("[INFO] Version: Pre-Alpha"); +} + +int main() +{ Base::Log::Initialize(); Base::Log::Start(); const auto config_dir = Base::FS::GetUserPath(Base::FS::PathType::BinaryDir); Config::Load(config_dir / "config.toml"); - // Create GUI manager auto gui_manager = std::make_unique(); - - // Initialize GUI - if (!gui_manager->Initialize("Pound Emulator", Config::windowWidth(), Config::windowHeight())) { + if (!gui_manager->Initialize("Pound Emulator", Config::windowWidth(), Config::windowHeight())) + { LOG_ERROR(Render, "Failed to initialize GUI"); return -1; } - // Create and add panels - auto console_panel = std::make_shared(); - auto cpu_panel = std::make_shared(); - auto performance_panel = std::make_shared(); - - gui_manager->AddPanel(console_panel); - gui_manager->AddPanel(cpu_panel); - gui_manager->AddPanel(performance_panel); - - // Set up callbacks - auto cpu_test_callback = [console_panel]() { - console_panel->AddLog("[INFO] Running CPU test..."); - cpuTest(); - console_panel->AddLog("[INFO] CPU test completed. Check terminal for details."); - }; - - gui_manager->SetCPUTestCallback(cpu_test_callback); - cpu_panel->SetCPUTestCallback(cpu_test_callback); - - // Add initial console message - console_panel->AddLog("[INFO] Pound Emulator started"); - console_panel->AddLog("[INFO] Version: Pre-Alpha"); - - // Main loop - while (gui_manager->IsRunning()) { + console_panel = std::make_shared(); + cpu_panel = std::make_shared(); + performance_panel = std::make_shared(); + + initGUI(gui_manager.get()); + + while (gui_manager->IsRunning()) + { gui_manager->RunFrame(); - - // Small delay to prevent excessive CPU usage std::this_thread::sleep_for(std::chrono::milliseconds(5)); } - - // Cleanup gui_manager->Shutdown(); - + return 0; -} \ No newline at end of file +} diff --git a/gui/GUIManager.cpp b/gui/GUIManager.cpp index 39f50f7..3d1225d 100644 --- a/gui/GUIManager.cpp +++ b/gui/GUIManager.cpp @@ -34,6 +34,7 @@ namespace Pound::GUI ImGui::CreateContext(); ImGuiIO &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + // io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Setup style ApplyTheme(); @@ -69,11 +70,8 @@ namespace Pound::GUI window->ProcessEvents(); BeginFrame(); + RenderTabBars(); - // Render menu bar - RenderMainMenuBar(); - - // Render all panels for (auto &panel : panels) { if (panel->IsVisible()) @@ -82,7 +80,6 @@ namespace Pound::GUI } } - // Demo window for debugging if (show_demo_window) { ImGui::ShowDemoWindow(&show_demo_window); @@ -112,85 +109,6 @@ namespace Pound::GUI window->SwapBuffers(); } - void GUIManager::RenderMainMenuBar() - { - if (ImGui::BeginMainMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - if (ImGui::MenuItem("Load ROM...", "Ctrl+O")) - { - // TODO: Implement ROM loading - } - ImGui::Separator(); - if (ImGui::MenuItem("Exit", "Alt+F4")) - { - window->SetShouldClose(true); - } - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Emulation")) - { - if (ImGui::MenuItem("Run CPU Test")) - { - if (cpu_test_callback) - { - cpu_test_callback(); - } - } - ImGui::Separator(); - if (ImGui::MenuItem("Pause", "F5")) - { - // TODO: Implement pause - } - if (ImGui::MenuItem("Reset", "F6")) - { - // TODO: Implement reset - } - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("View")) - { - for (auto &panel : panels) - { - bool visible = panel->IsVisible(); - if (ImGui::MenuItem(panel->GetName().c_str(), nullptr, &visible)) - { - panel->SetVisible(visible); - } - } - ImGui::Separator(); - if (ImGui::MenuItem("ImGui Demo", nullptr, &show_demo_window)) - { - // Toggle handled by flag - } - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Tools")) - { - if (ImGui::MenuItem("Settings", "Ctrl+,")) - { - // TODO: Open settings panel - } - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Help")) - { - if (ImGui::MenuItem("About")) - { - // TODO: Show about dialog - } - ImGui::EndMenu(); - } - - ImGui::EndMainMenuBar(); - } - } - void GUIManager::ApplyTheme() { ImGuiStyle &style = ImGui::GetStyle(); @@ -272,4 +190,114 @@ namespace Pound::GUI }), panels.end()); } + + TabBar *GUIManager::AddTabs(const std::string &name) + { + auto new_bar = std::make_unique(); + new_bar->id = name; + m_tab_bars.push_back(std::move(new_bar)); + return m_tab_bars.back().get(); + } + + void GUIManager::AddSubTab(TabBar *parent_bar, const std::string &name, std::function callback) + { + if (!parent_bar) + { + LOG_WARNING(Render, "Trying to add a sub-tab to a null TabBar."); + return; + } + + TabItem new_item; + new_item.name = name; + new_item.render_callback = std::move(callback); + parent_bar->items.push_back(std::move(new_item)); + } + + void GUIManager::AddSubTab(TabBar *parent_bar, const std::string &name, const std::string &shortcut, std::function callback) + { + if (!parent_bar) + { + LOG_WARNING(Render, "Trying to add a sub-tab to a null TabBar."); + return; + } + + TabItem new_item; + new_item.name = name; + new_item.shortcut = shortcut; + new_item.render_callback = std::move(callback); + parent_bar->items.push_back(std::move(new_item)); + } + + void GUIManager::AddSubTab(TabBar *parent_bar, const std::string &name, bool *p_selected, std::function callback) + { + if (!parent_bar) + { + LOG_WARNING(Render, "Trying to add a sub-tab to a null TabBar."); + return; + } + + TabItem new_item; + new_item.name = name; + new_item.p_selected = p_selected; + new_item.checked_callback = std::move(callback); + parent_bar->items.push_back(std::move(new_item)); + } + + void GUIManager::AddSubTab(TabBar *parent_bar, const std::string &name, bool *p_selected, const std::string &shortcut, std::function callback) + { + if (!parent_bar) + { + LOG_WARNING(Render, "Trying to add a sub-tab to a null TabBar."); + return; + } + + TabItem new_item; + new_item.name = name; + new_item.shortcut = shortcut; + new_item.p_selected = p_selected; + new_item.checked_callback = std::move(callback); + parent_bar->items.push_back(std::move(new_item)); + } + + void GUIManager::RenderTabBarContents(TabBar &bar) + { + for (const auto &item : bar.items) + { + if (item.nested_tabs) + { + if (ImGui::BeginMenu(item.name.c_str())) + { + RenderTabBarContents(*item.nested_tabs); + ImGui::EndMenu(); + } + } + else + { + const char *shortcut = item.shortcut.empty() ? nullptr : item.shortcut.c_str(); + if (ImGui::MenuItem(item.name.c_str(), shortcut, item.p_selected)) + { + if (item.p_selected && item.checked_callback) + { + item.checked_callback(*item.p_selected); + } + } + } + } + } + + void GUIManager::RenderTabBars() + { + if (ImGui::BeginMainMenuBar()) + { + for (const auto &bar_ptr : m_tab_bars) + { + if (ImGui::BeginMenu(bar_ptr->id.c_str())) + { + RenderTabBarContents(*bar_ptr); + ImGui::EndMenu(); + } + } + ImGui::EndMainMenuBar(); + } + } } \ No newline at end of file diff --git a/gui/GUIManager.h b/gui/GUIManager.h index 5a8ec67..b8e2512 100644 --- a/gui/GUIManager.h +++ b/gui/GUIManager.h @@ -7,38 +7,63 @@ #include "Window.h" #include "Panel.h" -namespace Pound::GUI { +namespace Pound::GUI +{ -class GUIManager { -public: - GUIManager(); - ~GUIManager(); + struct TabBar; - bool Initialize(const std::string& title, int width, int height); - void Shutdown(); - - void RunFrame(); - bool IsRunning() const { return running && window && !window->ShouldClose(); } - - void AddPanel(std::shared_ptr panel); - void RemovePanel(const std::string& name); - - // Callback for external systems - void SetCPUTestCallback(std::function callback) { cpu_test_callback = callback; } - -private: - void BeginFrame(); - void EndFrame(); - void RenderMainMenuBar(); - void ApplyTheme(); - - std::unique_ptr window; - std::vector> panels; - bool running = false; - bool show_demo_window = false; - - // Callbacks - std::function cpu_test_callback; -}; + struct TabItem + { + std::string name; + std::string shortcut; + bool *p_selected = nullptr; + std::function render_callback; + std::function checked_callback; + std::unique_ptr nested_tabs = nullptr; + }; + + struct TabBar + { + std::string id; + std::vector items; + bool is_visible = true; + }; + + class GUIManager + { + public: + GUIManager(); + ~GUIManager(); + + bool Initialize(const std::string &title, int width, int height); + void Shutdown(); + + void RunFrame(); + bool IsRunning() const { return running && window && !window->ShouldClose(); } + + Window *GetWindow() const { return window.get(); } + + void AddPanel(std::shared_ptr panel); + void RemovePanel(const std::string &name); + + TabBar *AddTabs(const std::string &name); + void AddSubTab(TabBar *parent_bar, const std::string &name, std::function callback = nullptr); + void AddSubTab(TabBar *parent_bar, const std::string &name, const std::string &shortcut, std::function callback); + void AddSubTab(TabBar *parent_bar, const std::string &name, bool *p_selected, std::function callback = nullptr); + void AddSubTab(TabBar *parent_bar, const std::string &name, bool *p_selected, const std::string &shortcut, std::function callback = nullptr); + + private: + void BeginFrame(); + void EndFrame(); + void ApplyTheme(); + void RenderTabBars(); + void RenderTabBarContents(TabBar &bar); + + std::unique_ptr window; + std::vector> panels; + bool running = false; + bool show_demo_window = false; + std::vector> m_tab_bars; + }; } // namespace Pound::GUI \ No newline at end of file diff --git a/gui/Panel.h b/gui/Panel.h index 87c6269..cd2ddc6 100644 --- a/gui/Panel.h +++ b/gui/Panel.h @@ -16,7 +16,7 @@ namespace Pound::GUI virtual void Render() = 0; const std::string &GetName() const { return name; } - bool IsVisible() const { return visible; } + bool* IsVisible() { return &visible; } void SetVisible(bool vis) { visible = vis; } protected: diff --git a/gui/panels/CPUPanel.cpp b/gui/panels/CPUPanel.cpp index 3a8f057..75f939c 100644 --- a/gui/panels/CPUPanel.cpp +++ b/gui/panels/CPUPanel.cpp @@ -1,143 +1,86 @@ -// Copyright 2025 Pound Emulator Project. All rights reserved. +// In gui/panels/CPUPanel.cpp #include "CPUPanel.h" #include "../Colors.h" -#include -#include +#include +#include -namespace Pound::GUI { +namespace Pound::GUI +{ + CPUPanel::CPUPanel() : Panel("CPU Debug") {} -CPUPanel::CPUPanel() : Panel("CPU Debug") {} + void CPUPanel::UpdateState(const CPU& cpu) + { + cpu.get_state(cpu_state.registers.data(), cpu_state.pc); + } -void CPUPanel::Render() { - if (!ImGui::Begin(name.c_str(), &visible, ImGuiWindowFlags_NoCollapse)) { - ImGui::End(); - return; + void CPUPanel::SetCPUTestCallback(std::function callback) + { + cpu_test_callback = std::move(callback); } - // Control buttons with custom colors - ImGui::PushStyleColor(ImGuiCol_Button, Colors::WithAlpha(Colors::Primary, 0.40f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, Colors::PrimaryHover); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, Colors::PrimaryActive); - - if (ImGui::Button("Run CPU Test", ImVec2(120, 0))) { - if (cpu_test_callback) { - cpu_test_callback(); - show_test_result = true; + void CPUPanel::Render() + { + if (!visible) { + return; } - } - - ImGui::SameLine(); - if (ImGui::Button("Step", ImVec2(60, 0))) { - // TODO: Implement step functionality - } - - ImGui::SameLine(); - - // Reset button with secondary color - ImGui::PushStyleColor(ImGuiCol_Button, Colors::WithAlpha(Colors::Secondary, 0.40f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, Colors::SecondaryHover); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, Colors::SecondaryActive); - - if (ImGui::Button("Reset", ImVec2(60, 0))) { - // TODO: Implement reset functionality - } - - ImGui::PopStyleColor(6); // Pop all 6 color changes - - ImGui::Separator(); - - // Tabs for different views - if (ImGui::BeginTabBar("CPUTabBar")) { - if (ImGui::BeginTabItem("Registers")) { - // General purpose registers - ImGui::Text("General Purpose Registers:"); - ImGui::Separator(); - - if (ImGui::BeginTable("RegisterTable", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { - ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed, 60.0f); - ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 120.0f); - ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed, 60.0f); - ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 120.0f); - ImGui::TableHeadersRow(); - - for (int i = 0; i < 16; i++) { - ImGui::TableNextRow(); - - // Left column - ImGui::TableSetColumnIndex(0); - ImGui::Text("X%d", i); - ImGui::TableSetColumnIndex(1); - ImGui::Text("0x%016llX", cpu_state.registers[i]); - - // Right column - if (i + 16 < 32) { - ImGui::TableSetColumnIndex(2); - ImGui::Text("X%d", i + 16); - ImGui::TableSetColumnIndex(3); - ImGui::Text("0x%016llX", cpu_state.registers[i + 16]); - } - } - - ImGui::EndTable(); + if (!ImGui::Begin(name.c_str(), &visible, ImGuiWindowFlags_NoCollapse)) + { + ImGui::End(); + return; + } + + ImGui::PushStyleColor(ImGuiCol_Button, Colors::WithAlpha(Colors::Primary, 0.40f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, Colors::PrimaryHover); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, Colors::PrimaryActive); + if (ImGui::Button("Run CPU Test", ImVec2(120, 0))) + { + if (cpu_test_callback) { + cpu_test_callback(); + show_test_result = true; } - - ImGui::Spacing(); - ImGui::Text("Program Counter: 0x%016llX", cpu_state.pc); - ImGui::Text("Flags: 0x%08X", cpu_state.flags); - - ImGui::EndTabItem(); } - - if (ImGui::BeginTabItem("Memory")) { - static char addr_input[17] = "0000000000000000"; - ImGui::Text("Memory Viewer"); - ImGui::Separator(); - - ImGui::InputText("Address", addr_input, sizeof(addr_input), - ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - - // TODO: Implement memory viewer - ImGui::Text("Memory viewer will be implemented here"); - - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("Disassembly")) { - ImGui::Text("Disassembly View"); - ImGui::Separator(); - - // TODO: Implement disassembly view - ImGui::Text("Disassembly will be shown here"); - - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } - - // Test result popup - if (show_test_result) { - ImGui::OpenPopup("CPU Test Result"); - } - - if (ImGui::BeginPopupModal("CPU Test Result", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("The CPU test has been executed successfully!"); - ImGui::Text("Check the console for detailed output."); + ImGui::PopStyleColor(3); ImGui::Separator(); - ImGui::Text("Note: Pound is still in pre-alpha state."); - - ImGui::Spacing(); - - if (ImGui::Button("OK", ImVec2(120, 0))) { - show_test_result = false; - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } + if (ImGui::BeginTabBar("CPUTabBar")) + { + if (ImGui::BeginTabItem("Registers")) + { + ImGui::Text("General Purpose Registers:"); + ImGui::Separator(); + if (ImGui::BeginTable("RegisterTable", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) + { + ImGui::TableSetupColumn("Register"); ImGui::TableSetupColumn("Value"); + ImGui::TableSetupColumn("Register"); ImGui::TableSetupColumn("Value"); + ImGui::TableHeadersRow(); + + for (int i = 0; i < 16; i++) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); ImGui::Text("X%d", i); + ImGui::TableSetColumnIndex(1); ImGui::Text("0x%016" PRIX64, cpu_state.registers[i]); - ImGui::End(); -} + if (i + 16 < 31) + { + ImGui::TableSetColumnIndex(2); ImGui::Text("X%d", i + 16); + ImGui::TableSetColumnIndex(3); ImGui::Text("0x%016" PRIX64, cpu_state.registers[i + 16]); + } + } + ImGui::EndTable(); + } + + ImGui::Spacing(); + ImGui::Text("Program Counter: 0x%016" PRIX64, cpu_state.pc); + ImGui::Text("Flags (PState): 0x%08X", cpu_state.flags); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Memory")) { /*...*/ ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("Disassembly")) { /*...*/ ImGui::EndTabItem(); } + + ImGui::EndTabBar(); + } + ImGui::End(); + } } // namespace Pound::GUI \ No newline at end of file diff --git a/gui/panels/CPUPanel.h b/gui/panels/CPUPanel.h index 2e92f03..605567b 100644 --- a/gui/panels/CPUPanel.h +++ b/gui/panels/CPUPanel.h @@ -1,32 +1,31 @@ -// Copyright 2025 Pound Emulator Project. All rights reserved. #pragma once #include "../Panel.h" +#include "ARM/cpu.h" #include +#include +#include namespace Pound::GUI { + struct CPUState + { + std::array registers{}; + uint64_t pc = 0; + uint32_t flags = 0; + }; class CPUPanel : public Panel { public: CPUPanel(); - void Render() override; - void SetCPUTestCallback(std::function callback) { cpu_test_callback = callback; } - void ShowTestResult(bool show = true) { show_test_result = show; } + void SetCPUTestCallback(std::function callback); + void UpdateState(const CPU& cpu); private: - std::function cpu_test_callback; + CPUState cpu_state; bool show_test_result = false; - - // CPU state display (placeholder for future integration) - struct CPUState - { - uint64_t registers[32] = {0}; - uint64_t pc = 0; - uint32_t flags = 0; - } cpu_state; + std::function cpu_test_callback; }; - -} // namespace Pound::GUI \ No newline at end of file +} \ No newline at end of file diff --git a/gui/panels/ConsolePanel.cpp b/gui/panels/ConsolePanel.cpp index 31b9e28..2cf63d4 100644 --- a/gui/panels/ConsolePanel.cpp +++ b/gui/panels/ConsolePanel.cpp @@ -14,6 +14,10 @@ namespace Pound::GUI void ConsolePanel::Render() { + if (!visible) { + return; + } + if (!ImGui::Begin(name.c_str(), &visible)) { ImGui::End(); diff --git a/gui/panels/PerformancePanel.cpp b/gui/panels/PerformancePanel.cpp index 80cb0e8..a02cf4c 100644 --- a/gui/panels/PerformancePanel.cpp +++ b/gui/panels/PerformancePanel.cpp @@ -13,6 +13,10 @@ namespace Pound::GUI void PerformancePanel::Render() { + if (!visible) { + return; + } + if (!ImGui::Begin(name.c_str(), &visible)) { ImGui::End();