summaryrefslogtreecommitdiffstats
path: root/gearboy/platforms/desktop-shared/gui_debug.cpp
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2022-06-02 15:28:40 +0200
committerLouis Burda <quent.burda@gmail.com>2022-06-02 15:28:40 +0200
commit5bc16063c29aa4d3d287ebd163ccdbcbf54c4f9f (patch)
treec131f947a37b3af2d14d41e9eda098bdec2d061c /gearboy/platforms/desktop-shared/gui_debug.cpp
parent78a5f810b22f0d8cafa05f638b0cb2e889824859 (diff)
downloadcscg2022-gearboy-master.tar.gz
cscg2022-gearboy-master.zip
Added submodule filesHEADmaster
Diffstat (limited to 'gearboy/platforms/desktop-shared/gui_debug.cpp')
-rw-r--r--gearboy/platforms/desktop-shared/gui_debug.cpp2060
1 files changed, 2060 insertions, 0 deletions
diff --git a/gearboy/platforms/desktop-shared/gui_debug.cpp b/gearboy/platforms/desktop-shared/gui_debug.cpp
new file mode 100644
index 00000000..b2bfd8e0
--- /dev/null
+++ b/gearboy/platforms/desktop-shared/gui_debug.cpp
@@ -0,0 +1,2060 @@
+/*
+ * Gearboy - Nintendo Game Boy Emulator
+ * Copyright (C) 2012 Ignacio Sanchez
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/
+ *
+ */
+
+#include <math.h>
+#include "imgui/imgui.h"
+#include "imgui/imgui_memory_editor.h"
+#include "FileBrowser/ImGuiFileBrowser.h"
+#include "config.h"
+#include "emu.h"
+#include "renderer.h"
+#include "../../src/gearboy.h"
+#include "gui.h"
+#include "gui_debug_constants.h"
+
+#define GUI_DEBUG_IMPORT
+#include "gui_debug.h"
+
+struct DebugSymbol
+{
+ int bank;
+ u16 address;
+ std::string text;
+};
+
+struct DisassmeblerLine
+{
+ bool is_symbol;
+ bool is_breakpoint;
+ Memory::stDisassembleRecord* record;
+ std::string symbol;
+};
+
+static MemoryEditor mem_edit;
+static ImVec4 cyan = ImVec4(0.0f,1.0f,1.0f,1.0f);
+static ImVec4 magenta = ImVec4(1.0f,0.502f,0.957f,1.0f);
+static ImVec4 yellow = ImVec4(1.0f,1.0f,0.0f,1.0f);
+static ImVec4 red = ImVec4(1.0f,0.149f,0.447f,1.0f);
+static ImVec4 green = ImVec4(0.0f,1.0f,0.0f,1.0f);
+static ImVec4 white = ImVec4(1.0f,1.0f,1.0f,1.0f);
+static ImVec4 gray = ImVec4(0.5f,0.5f,0.5f,1.0f);
+static ImVec4 dark_gray = ImVec4(0.1f,0.1f,0.1f,1.0f);
+static std::vector<DebugSymbol> symbols;
+static Memory::stDisassembleRecord* selected_record = NULL;
+static char brk_address_cpu[8] = "";
+static char brk_address_mem[10] = "";
+static bool brk_new_mem_read = true;
+static bool brk_new_mem_write = true;
+static char goto_address[5] = "";
+static bool goto_address_requested = false;
+static u16 goto_address_target = 0;
+static bool goto_back_requested = false;
+static int goto_back = 0;
+
+static void debug_window_processor(void);
+static void debug_window_io(void);
+static void debug_window_audio(void);
+static void debug_window_memory(void);
+static void debug_window_disassembler(void);
+static void debug_window_vram(void);
+static void debug_window_vram_background(void);
+static void debug_window_vram_tiles(void);
+static void debug_window_vram_oam(void);
+static void debug_window_vram_palettes(void);
+static void add_symbol(const char* line);
+static void add_breakpoint_cpu(void);
+static void add_breakpoint_mem(void);
+static void request_goto_address(u16 addr);
+static ImVec4 color_565_to_float(u16 color);
+
+void gui_debug_windows(void)
+{
+ if (config_debug.debug)
+ {
+ if (config_debug.show_processor)
+ debug_window_processor();
+ if (config_debug.show_memory)
+ debug_window_memory();
+ if (config_debug.show_disassembler)
+ debug_window_disassembler();
+ if (config_debug.show_iomap)
+ debug_window_io();
+ if (config_debug.show_audio)
+ debug_window_audio();
+ if (config_debug.show_video)
+ debug_window_vram();
+
+ //ImGui::ShowDemoWindow(&config_debug.debug);
+ }
+}
+
+
+void gui_debug_reset(void)
+{
+ gui_debug_reset_breakpoints_cpu();
+ gui_debug_reset_breakpoints_mem();
+ gui_debug_reset_symbols();
+ selected_record = NULL;
+}
+
+void gui_debug_reset_symbols(void)
+{
+ symbols.clear();
+
+ for (int i = 0; i < gui_debug_symbols_count; i++)
+ add_symbol(gui_debug_symbols[i]);
+}
+
+void gui_debug_load_symbols_file(const char* path)
+{
+ Log("Loading symbol file %s", path);
+
+ std::ifstream file(path);
+
+ if (file.is_open())
+ {
+ std::string line;
+
+ while (std::getline(file, line))
+ {
+ add_symbol(line.c_str());
+ }
+
+ file.close();
+ }
+}
+
+void gui_debug_toggle_breakpoint(void)
+{
+ if (IsValidPointer(selected_record))
+ {
+ bool found = false;
+ std::vector<Memory::stDisassembleRecord*>* breakpoints = emu_get_core()->GetMemory()->GetBreakpointsCPU();
+
+ for (long unsigned int b = 0; b < breakpoints->size(); b++)
+ {
+ if ((*breakpoints)[b] == selected_record)
+ {
+ found = true;
+ InitPointer((*breakpoints)[b]);
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ breakpoints->push_back(selected_record);
+ }
+ }
+}
+
+void gui_debug_runtocursor(void)
+{
+ if (IsValidPointer(selected_record))
+ {
+ emu_get_core()->GetMemory()->SetRunToBreakpoint(selected_record);
+ emu_debug_continue();
+ }
+}
+
+void gui_debug_reset_breakpoints_cpu(void)
+{
+ emu_get_core()->GetMemory()->GetBreakpointsCPU()->clear();
+ brk_address_cpu[0] = 0;
+}
+
+void gui_debug_reset_breakpoints_mem(void)
+{
+ emu_get_core()->GetMemory()->GetBreakpointsMem()->clear();
+ brk_address_mem[0] = 0;
+}
+
+void gui_debug_go_back(void)
+{
+ goto_back_requested = true;
+}
+
+static void debug_window_memory(void)
+{
+ ImGui::SetNextWindowPos(ImVec2(180, 382), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(482, 308), ImGuiCond_FirstUseEver);
+
+ ImGui::Begin("Memory Editor", &config_debug.show_memory);
+
+ GearboyCore* core = emu_get_core();
+ Memory* memory = core->GetMemory();
+
+ ImGui::PushFont(gui_default_font);
+
+ ImGui::TextColored(cyan, " BANKS: ");ImGui::SameLine();
+
+ ImGui::TextColored(magenta, "ROM1");ImGui::SameLine();
+ ImGui::Text("$%02X", memory->GetCurrentRule()->GetCurrentRomBank1Index()); ImGui::SameLine();
+ ImGui::TextColored(magenta, " RAM");ImGui::SameLine();
+ ImGui::Text("$%02X", memory->GetCurrentRule()->GetCurrentRamBankIndex()); ImGui::SameLine();
+ ImGui::TextColored(magenta, " WRAM1");ImGui::SameLine();
+ ImGui::Text("$%02X", memory->GetCurrentCGBRAMBank()); ImGui::SameLine();
+ ImGui::TextColored(magenta, " VRAM");ImGui::SameLine();
+ ImGui::Text("$%02X", memory->GetCurrentLCDRAMBank());
+
+ ImGui::PopFont();
+
+ if (ImGui::BeginTabBar("##memory_tabs", ImGuiTabBarFlags_None))
+ {
+ if (ImGui::BeginTabItem("ROM0"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetROM0(), 0x4000, 0);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+
+ if (ImGui::BeginTabItem("ROM1"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetROM1(), 0x4000, 0x4000);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+
+ if (ImGui::BeginTabItem("VRAM"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetVRAM(), 0x2000, 0x8000);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+
+ if (ImGui::BeginTabItem("RAM"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetRAM(), 0x2000, 0xA000);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+
+ if (emu_is_cgb())
+ {
+ if (ImGui::BeginTabItem("WRAM0"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetWRAM0(), 0x1000, 0xC000);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+ if (ImGui::BeginTabItem("WRAM1"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetWRAM1(), 0x1000, 0xD000);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+ }
+ else
+ {
+ if (ImGui::BeginTabItem("WRAM"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetWRAM0(), 0x2000, 0xC000);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+ }
+
+ if (ImGui::BeginTabItem("OAM"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetMemoryMap() + 0xFE00, 0x00A0, 0xFE00);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+
+ if (ImGui::BeginTabItem("IO"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetMemoryMap() + 0xFF00, 0x0080, 0xFF00);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+
+ if (ImGui::BeginTabItem("HIRAM"))
+ {
+ ImGui::PushFont(gui_default_font);
+ mem_edit.DrawContents(memory->GetMemoryMap() + 0xFF80, 0x007F, 0xFF80);
+ ImGui::PopFont();
+ ImGui::EndTabItem();
+ }
+ ImGui::EndTabBar();
+ }
+
+ ImGui::End();
+}
+
+static void debug_window_disassembler(void)
+{
+ ImGui::SetNextWindowPos(ImVec2(180, 30), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(482, 344), ImGuiCond_FirstUseEver);
+
+ ImGui::Begin("Disassembler", &config_debug.show_disassembler);
+
+ GearboyCore* core = emu_get_core();
+ Processor* processor = core->GetProcessor();
+ Processor::ProcessorState* proc_state = processor->GetState();
+ Memory* memory = core->GetMemory();
+ std::vector<Memory::stDisassembleRecord*>* breakpoints_cpu = memory->GetBreakpointsCPU();
+ std::vector<Memory::stMemoryBreakpoint>* breakpoints_mem = memory->GetBreakpointsMem();
+ Memory::stDisassembleRecord** memory_map = memory->GetDisassembledMemoryMap();
+ Memory::stDisassembleRecord** rom_map = memory->GetDisassembledROMMemoryMap();
+ Memory::stDisassembleRecord** map = NULL;
+
+ int pc = proc_state->PC->GetValue();
+
+ if (ImGui::Button("Step Over"))
+ emu_debug_step();
+ ImGui::SameLine();
+ if (ImGui::Button("Step Frame"))
+ emu_debug_next_frame();
+ ImGui::SameLine();
+ if (ImGui::Button("Continue"))
+ emu_debug_continue();
+ ImGui::SameLine();
+ if (ImGui::Button("Run To Cursor"))
+ gui_debug_runtocursor();
+
+ static bool follow_pc = true;
+ static bool show_mem = true;
+ static bool show_symbols = true;
+
+ ImGui::Checkbox("Follow PC", &follow_pc); ImGui::SameLine();
+ ImGui::Checkbox("Show Memory", &show_mem); ImGui::SameLine();
+ ImGui::Checkbox("Show Symbols", &show_symbols);
+
+ ImGui::Separator();
+
+ ImGui::Text("Go To Address: ");
+ ImGui::SameLine();
+ ImGui::PushItemWidth(45);
+ if (ImGui::InputTextWithHint("##goto_address", "XXXX", goto_address, IM_ARRAYSIZE(goto_address), ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue))
+ {
+ try
+ {
+ request_goto_address((u16)std::stoul(goto_address, 0, 16));
+ follow_pc = false;
+ }
+ catch(const std::invalid_argument&)
+ {
+ }
+ goto_address[0] = 0;
+ }
+ ImGui::PopItemWidth();
+ ImGui::SameLine();
+ if (ImGui::Button("Go", ImVec2(30, 0)))
+ {
+ try
+ {
+ request_goto_address((u16)std::stoul(goto_address, 0, 16));
+ follow_pc = false;
+ }
+ catch(const std::invalid_argument&)
+ {
+ }
+ goto_address[0] = 0;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Back", ImVec2(50, 0)))
+ {
+ goto_back_requested = true;
+ follow_pc = false;
+ }
+
+ ImGui::Separator();
+
+ if (ImGui::CollapsingHeader("Processor Breakpoints"))
+ {
+ ImGui::Checkbox("Disable All##disable_all_cpu", &emu_debug_disable_breakpoints_cpu);
+
+ ImGui::Columns(2, "breakpoints_cpu");
+ ImGui::SetColumnOffset(1, 85);
+
+ ImGui::Separator();
+
+ if (IsValidPointer(selected_record))
+ sprintf(brk_address_cpu, "%02X:%04X", selected_record->bank, selected_record->address);
+
+ ImGui::PushItemWidth(70);
+ if (ImGui::InputTextWithHint("##add_breakpoint_cpu", "XX:XXXX", brk_address_cpu, IM_ARRAYSIZE(brk_address_cpu), ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue))
+ {
+ add_breakpoint_cpu();
+ }
+ ImGui::PopItemWidth();
+
+ if (ImGui::IsItemHovered())
+ ImGui::SetTooltip("Use XXXX format for addresses in bank 0 or XX:XXXX for selecting bank and address");
+
+ if (ImGui::Button("Add##add_cpu", ImVec2(70, 0)))
+ {
+ add_breakpoint_cpu();
+ }
+
+ if (ImGui::Button("Clear All##clear_all_cpu", ImVec2(70, 0)))
+ {
+ gui_debug_reset_breakpoints_cpu();
+ }
+
+ ImGui::NextColumn();
+
+ ImGui::BeginChild("breakpoints_cpu", ImVec2(0, 80), false);
+
+ int remove = -1;
+
+ for (long unsigned int b = 0; b < breakpoints_cpu->size(); b++)
+ {
+ if (!IsValidPointer((*breakpoints_cpu)[b]))
+ continue;
+
+ ImGui::PushID(b);
+ if (ImGui::SmallButton("X"))
+ {
+ remove = b;
+ ImGui::PopID();
+ continue;
+ }
+
+ ImGui::PopID();
+
+ ImGui::PushFont(gui_default_font);
+ ImGui::SameLine();
+ ImGui::TextColored(red, "%02X:%04X", (*breakpoints_cpu)[b]->bank, (*breakpoints_cpu)[b]->address);
+ ImGui::SameLine();
+ ImGui::TextColored(gray, "%s", (*breakpoints_cpu)[b]->name);
+ ImGui::PopFont();
+ }
+
+ if (remove >= 0)
+ {
+ breakpoints_cpu->erase(breakpoints_cpu->begin() + remove);
+ }
+
+ ImGui::EndChild();
+ ImGui::Columns(1);
+ ImGui::Separator();
+ }
+
+ if (ImGui::CollapsingHeader("Memory Breakpoints"))
+ {
+ ImGui::Checkbox("Disable All##diable_all_mem", &emu_debug_disable_breakpoints_mem);
+
+ ImGui::Columns(2, "breakpoints_mem");
+ ImGui::SetColumnOffset(1, 100);
+
+ ImGui::Separator();
+
+ ImGui::PushItemWidth(85);
+ if (ImGui::InputTextWithHint("##add_breakpoint_mem", "XXXX-XXXX", brk_address_mem, IM_ARRAYSIZE(brk_address_mem), ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue))
+ {
+ add_breakpoint_mem();
+ }
+ ImGui::PopItemWidth();
+
+ if (ImGui::IsItemHovered())
+ ImGui::SetTooltip("Use XXXX format for single addresses or XXXX-XXXX for address ranges");
+
+ ImGui::Checkbox("Read", &brk_new_mem_read);
+ ImGui::Checkbox("Write", &brk_new_mem_write);
+
+ if (ImGui::Button("Add##add_mem", ImVec2(85, 0)))
+ {
+ add_breakpoint_mem();
+ }
+
+ if (ImGui::Button("Clear All##clear_all_mem", ImVec2(85, 0)))
+ {
+ gui_debug_reset_breakpoints_mem();
+ }
+
+ ImGui::NextColumn();
+
+ ImGui::BeginChild("breakpoints_mem", ImVec2(0, 130), false);
+
+ int remove = -1;
+
+ for (long unsigned int b = 0; b < breakpoints_mem->size(); b++)
+ {
+ ImGui::PushID(10000 + b);
+ if (ImGui::SmallButton("X"))
+ {
+ remove = b;
+ ImGui::PopID();
+ continue;
+ }
+
+ ImGui::PopID();
+
+ ImGui::PushFont(gui_default_font);
+ ImGui::SameLine();
+ if ((*breakpoints_mem)[b].range)
+ ImGui::TextColored(red, "%04X-%04X", (*breakpoints_mem)[b].address1, (*breakpoints_mem)[b].address2);
+ else
+ ImGui::TextColored(red, "%04X", (*breakpoints_mem)[b].address1);
+ if ((*breakpoints_mem)[b].read)
+ {
+ ImGui::SameLine(); ImGui::TextColored(gray, "R");
+ }
+ if ((*breakpoints_mem)[b].write)
+ {
+ ImGui::SameLine(); ImGui::TextColored(gray, "W");
+ }
+ ImGui::PopFont();
+ }
+
+ if (remove >= 0)
+ {
+ breakpoints_mem->erase(breakpoints_mem->begin() + remove);
+ }
+
+ ImGui::EndChild();
+ ImGui::Columns(1);
+ ImGui::Separator();
+ }
+
+ ImGui::PushFont(gui_default_font);
+
+ bool window_visible = ImGui::BeginChild("##dis", ImVec2(ImGui::GetWindowContentRegionWidth(), 0), true, 0);
+
+ if (window_visible)
+ {
+ int dis_size = 0;
+ int pc_pos = 0;
+ int goto_address_pos = 0;
+
+ std::vector<DisassmeblerLine> vec(0x10000);
+
+ for (int i = 0; i < 0x10000; i++)
+ {
+ int offset = i;
+ int bank = 0;
+
+ if ((i & 0xC000) == 0x0000)
+ {
+ bank = memory->GetCurrentRule()->GetCurrentRomBank0Index();
+ offset = (0x4000 * bank) + i;
+ map = rom_map;
+ }
+ else if ((i & 0xC000) == 0x4000)
+ {
+ bank = memory->GetCurrentRule()->GetCurrentRomBank1Index();
+ offset = (0x4000 * bank) + (i & 0x3FFF);
+ map = rom_map;
+ }
+ else
+ {
+ map = memory_map;
+ }
+
+ if (IsValidPointer(map[offset]) && map[offset]->name[0] != 0)
+ {
+ for (long unsigned int s = 0; s < symbols.size(); s++)
+ {
+ if ((symbols[s].bank == bank) && (symbols[s].address == offset) && show_symbols)
+ {
+ vec[dis_size].is_symbol = true;
+ vec[dis_size].symbol = symbols[s].text;
+ dis_size ++;
+ }
+ }
+
+ vec[dis_size].is_symbol = false;
+ vec[dis_size].record = map[offset];
+
+ if (vec[dis_size].record->address == pc)
+ pc_pos = dis_size;
+
+ if (goto_address_requested && (vec[dis_size].record->address <= goto_address_target))
+ goto_address_pos = dis_size;
+
+ vec[dis_size].is_breakpoint = false;
+
+ for (long unsigned int b = 0; b < breakpoints_cpu->size(); b++)
+ {
+ if ((*breakpoints_cpu)[b] == vec[dis_size].record)
+ {
+ vec[dis_size].is_breakpoint = true;
+ break;
+ }
+ }
+
+ dis_size++;
+ }
+ }
+
+ if (follow_pc)
+ {
+ float window_offset = ImGui::GetWindowHeight() / 2.0f;
+ float offset = window_offset - (ImGui::GetTextLineHeightWithSpacing() - 2.0f);
+ ImGui::SetScrollY((pc_pos * ImGui::GetTextLineHeightWithSpacing()) - offset);
+ }
+
+ if (goto_address_requested)
+ {
+ goto_address_requested = false;
+ goto_back = (int)ImGui::GetScrollY();
+ ImGui::SetScrollY((goto_address_pos * ImGui::GetTextLineHeightWithSpacing()) + 2);
+ }
+
+ if (goto_back_requested)
+ {
+ goto_back_requested = false;
+ ImGui::SetScrollY((float)goto_back);
+ }
+
+ ImGuiListClipper clipper(dis_size, ImGui::GetTextLineHeightWithSpacing());
+
+ while (clipper.Step())
+ {
+ for (int item = clipper.DisplayStart; item < clipper.DisplayEnd; item++)
+ {
+ if (vec[item].is_symbol)
+ {
+ ImGui::TextColored(green, "%s:", vec[item].symbol.c_str());
+ continue;
+ }
+
+ ImGui::PushID(item);
+
+ bool is_selected = (selected_record == vec[item].record);
+
+ if (ImGui::Selectable("", is_selected, ImGuiSelectableFlags_AllowDoubleClick))
+ {
+ if (ImGui::IsMouseDoubleClicked(0) && vec[item].record->jump)
+ {
+ follow_pc = false;
+ request_goto_address(vec[item].record->jump_address);
+ }
+ else if (is_selected)
+ {
+ InitPointer(selected_record);
+ brk_address_cpu[0] = 0;
+ }
+ else
+ selected_record = vec[item].record;
+ }
+
+ if (is_selected)
+ ImGui::SetItemDefaultFocus();
+
+ if (vec[item].is_breakpoint)
+ {
+ ImGui::SameLine();
+ if (vec[item].record->address == pc)
+ {
+ if (show_mem)
+ ImGui::TextColored(red, " %02X:%04X %s", vec[item].record->bank, vec[item].record->address, vec[item].record->bytes);
+ else
+ ImGui::TextColored(red, " %02X:%04X ", vec[item].record->bank, vec[item].record->address);
+ ImGui::SameLine();
+ ImGui::TextColored(yellow, "->");
+ ImGui::SameLine();
+ ImGui::TextColored(red, "%s", vec[item].record->name);
+ }
+ else
+ {
+ if (show_mem)
+ ImGui::TextColored(red, " %02X:%04X %s %s", vec[item].record->bank, vec[item].record->address, vec[item].record->bytes, vec[item].record->name);
+ else
+ ImGui::TextColored(red, " %02X:%04X %s", vec[item].record->bank, vec[item].record->address, vec[item].record->name);
+ }
+ }
+ else if (vec[item].record->address == pc)
+ {
+ ImGui::SameLine();
+ if (show_mem)
+ ImGui::TextColored(yellow, " %02X:%04X %s -> %s", vec[item].record->bank, vec[item].record->address, vec[item].record->bytes, vec[item].record->name);
+ else
+ ImGui::TextColored(yellow, " %02X:%04X -> %s", vec[item].record->bank, vec[item].record->address, vec[item].record->name);
+ }
+ else
+ {
+ ImGui::SameLine();
+ ImGui::TextColored(cyan, " %02X:%04X ", vec[item].record->bank, vec[item].record->address);
+ ImGui::SameLine();
+ if (show_mem)
+ ImGui::TextColored(gray, "%s ", vec[item].record->bytes);
+ else
+ ImGui::TextColored(gray, " ");
+
+ ImGui::SameLine();
+ ImGui::TextColored(white, "%s", vec[item].record->name);
+ }
+
+ ImGui::PopID();
+ }
+ }
+ }
+
+ ImGui::EndChild();
+
+ ImGui::PopFont();
+
+ ImGui::End();
+}
+
+static void debug_window_processor(void)
+{
+ ImGui::SetNextWindowPos(ImVec2(14, 210), ImGuiCond_FirstUseEver);
+
+ ImGui::Begin("Processor", &config_debug.show_processor, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
+
+ ImGui::PushFont(gui_default_font);
+
+ GearboyCore* core = emu_get_core();
+ Processor* processor = core->GetProcessor();
+ Processor::ProcessorState* proc_state = processor->GetState();
+ Memory* memory = core->GetMemory();
+
+ ImGui::Separator();
+
+ u8 flags = proc_state->AF->GetLow();
+
+ ImGui::TextColored(magenta, " Z"); ImGui::SameLine();
+ ImGui::Text("= %d", (bool)(flags & FLAG_ZERO)); ImGui::SameLine();
+
+ ImGui::TextColored(magenta, " N"); ImGui::SameLine();
+ ImGui::Text("= %d", (bool)(flags & FLAG_SUB));
+
+ ImGui::TextColored(magenta, " H"); ImGui::SameLine();
+ ImGui::Text("= %d", (bool)(flags & FLAG_HALF)); ImGui::SameLine();
+
+ ImGui::TextColored(magenta, " C"); ImGui::SameLine();
+ ImGui::Text("= %d", (bool)(flags & FLAG_CARRY));
+
+ ImGui::Columns(2, "registers");
+ ImGui::Separator();
+ ImGui::TextColored(cyan, " A"); ImGui::SameLine();
+ ImGui::Text("= $%02X", proc_state->AF->GetHigh());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->AF->GetHigh()));
+
+ ImGui::NextColumn();
+ ImGui::TextColored(cyan, " F"); ImGui::SameLine();
+ ImGui::Text("= $%02X", proc_state->AF->GetLow());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->AF->GetLow()));
+
+ ImGui::NextColumn();
+ ImGui::Separator();
+ ImGui::TextColored(cyan, " B"); ImGui::SameLine();
+ ImGui::Text("= $%02X", proc_state->BC->GetHigh());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->BC->GetHigh()));
+
+ ImGui::NextColumn();
+ ImGui::TextColored(cyan, " C"); ImGui::SameLine();
+ ImGui::Text("= $%02X", proc_state->BC->GetLow());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->BC->GetLow()));
+
+ ImGui::NextColumn();
+ ImGui::Separator();
+ ImGui::TextColored(cyan, " D"); ImGui::SameLine();
+ ImGui::Text("= $%02X", proc_state->DE->GetHigh());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->DE->GetHigh()));
+
+ ImGui::NextColumn();
+ ImGui::TextColored(cyan, " E"); ImGui::SameLine();
+ ImGui::Text("= $%02X", proc_state->DE->GetLow());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->DE->GetLow()));
+
+ ImGui::NextColumn();
+ ImGui::Separator();
+ ImGui::TextColored(cyan, " H"); ImGui::SameLine();
+ ImGui::Text("= $%02X", proc_state->HL->GetHigh());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->HL->GetHigh()));
+
+ ImGui::NextColumn();
+ ImGui::TextColored(cyan, " L"); ImGui::SameLine();
+ ImGui::Text("= $%02X", proc_state->HL->GetLow());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->HL->GetLow()));
+
+ ImGui::NextColumn();
+ ImGui::Columns(1);
+ ImGui::Separator();
+ ImGui::TextColored(yellow, " SP"); ImGui::SameLine();
+ ImGui::Text("= $%04X", proc_state->SP->GetValue());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED " " BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->SP->GetHigh()), BYTE_TO_BINARY(proc_state->SP->GetLow()));
+
+ ImGui::Separator();
+ ImGui::TextColored(yellow, " PC"); ImGui::SameLine();
+ ImGui::Text("= $%04X", proc_state->PC->GetValue());
+ ImGui::Text(BYTE_TO_BINARY_PATTERN_SPACED " " BYTE_TO_BINARY_PATTERN_SPACED, BYTE_TO_BINARY(proc_state->PC->GetHigh()), BYTE_TO_BINARY(proc_state->PC->GetLow()));
+
+
+ ImGui::Columns(2);
+ ImGui::Separator();
+
+ ImGui::TextColored(magenta, " IME"); ImGui::SameLine();
+ ImGui::Text("= %d", *proc_state->IME);
+
+ ImGui::NextColumn();
+
+ ImGui::TextColored(magenta, "HALT"); ImGui::SameLine();
+ ImGui::Text("= %d", *proc_state->Halt);
+
+ ImGui::NextColumn();
+
+ ImGui::Columns(1);
+
+ ImGui::Separator();
+
+ ImGui::TextColored(cyan, " DOUBLE SPEED "); ImGui::SameLine();
+ processor->CGBSpeed() ? ImGui::TextColored(green, "ON") : ImGui::TextColored(gray, "OFF");
+
+ ImGui::Separator();
+
+ ImGui::TextColored(cyan, " BOOTROM "); ImGui::SameLine();
+ memory->IsBootromRegistryEnabled() ? ImGui::TextColored(green, "ON") : ImGui::TextColored(gray, "OFF");
+
+ ImGui::PopFont();
+
+ ImGui::End();
+}
+
+static void debug_window_audio(void)
+{
+ ImGui::SetNextWindowPos(ImVec2(130, 264), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(417, 0), ImGuiCond_FirstUseEver);
+
+ ImGui::Begin("Sound Registers", &config_debug.show_audio);
+
+ ImGui::PushFont(gui_default_font);
+
+ GearboyCore* core = emu_get_core();
+ Audio* audio = core->GetAudio();
+
+ gb_apu_state_t apu_state;
+ audio->GetApu()->save_state(&apu_state);
+
+ ImGui::Columns(2, "audio");
+
+ ImGui::TextColored(yellow, "CHANNEL 1 - TONE & SWEEP:");
+
+ u8 value = apu_state.regs[0xFF10 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF10"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR10"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF11 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF11"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR11"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF12 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF12"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR12"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF13 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF13"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR13"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF14 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF14"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR14"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ ImGui::NextColumn();
+
+ ImGui::TextColored(yellow, "CHANNEL 3 - WAVE:");
+
+ value = apu_state.regs[0xFF1A - 0xFF10];
+ ImGui::TextColored(cyan, " $FF1A"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR30"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF1B - 0xFF10];
+ ImGui::TextColored(cyan, " $FF1B"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR31"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF1C - 0xFF10];
+ ImGui::TextColored(cyan, " $FF1C"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR32"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF1D - 0xFF10];
+ ImGui::TextColored(cyan, " $FF1D"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR33"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF1E - 0xFF10];
+ ImGui::TextColored(cyan, " $FF1E"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR34"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ ImGui::NextColumn();
+ ImGui::Separator();
+
+ ImGui::TextColored(yellow, "CHANNEL 2 - TONE:");
+
+ value = apu_state.regs[0xFF16 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF16"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR21"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF17 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF17"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR22"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF18 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF18"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR23"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF19 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF19"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR24"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ ImGui::NextColumn();
+
+ ImGui::TextColored(yellow, "CHANNEL 4 - NOISE:");
+
+ value = apu_state.regs[0xFF20 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF20"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR41"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF21 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF21"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR42"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF22 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF22"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR43"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF23 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF23"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR44"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ ImGui::NextColumn();
+ ImGui::Separator();
+
+ ImGui::TextColored(yellow, "CONTROL:");
+
+ value = apu_state.regs[0xFF24 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF24"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR50"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF25 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF25"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR51"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ value = apu_state.regs[0xFF26 - 0xFF10];
+ ImGui::TextColored(cyan, " $FF26"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "NR52"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", value, BYTE_TO_BINARY(value));
+
+ ImGui::NextColumn();
+
+ ImGui::TextColored(yellow, "WAVE ($FF30 - $FF37):" );
+
+ ImGui::Text(" %02X%02X %02X%02X %02X%02X %02X%02X", apu_state.regs[0x20], apu_state.regs[0x21], apu_state.regs[0x22], apu_state.regs[0x23], apu_state.regs[0x24], apu_state.regs[0x25], apu_state.regs[0x26], apu_state.regs[0x27]);
+
+ ImGui::TextColored(yellow, "WAVE ($FF38 - $FF3F):" );
+
+ ImGui::Text(" %02X%02X %02X%02X %02X%02X %02X%02X", apu_state.regs[0x28], apu_state.regs[0x29], apu_state.regs[0x2A], apu_state.regs[0x2B], apu_state.regs[0x2C], apu_state.regs[0x2D], apu_state.regs[0x2E], apu_state.regs[0x2F]);
+
+ ImGui::NextColumn();
+
+ ImGui::Columns(1);
+
+ ImGui::PopFont();
+
+ ImGui::End();
+}
+
+static void debug_window_io(void)
+{
+ ImGui::SetNextWindowPos(ImVec2(121, 164), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(420, 0), ImGuiCond_FirstUseEver);
+
+ ImGui::Begin("IO Map", &config_debug.show_iomap);
+
+ ImGui::PushFont(gui_default_font);
+
+ GearboyCore* core = emu_get_core();
+ Memory* memory = core->GetMemory();
+
+ ImGui::Columns(2, "iomap");
+
+ ImGui::TextColored(yellow, "INTERRUPTS:");
+
+ ImGui::TextColored(cyan, " $FFFF"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "IE "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFFFF), BYTE_TO_BINARY(memory->Retrieve(0xFFFF)));
+
+ ImGui::TextColored(cyan, " $FF0F"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "IF "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF0F), BYTE_TO_BINARY(memory->Retrieve(0xFF0F)));
+
+ ImGui::TextColored(cyan, " VBLNK "); ImGui::SameLine();
+ IsSetBit(memory->Retrieve(0xFF0F), 0) && IsSetBit(memory->Retrieve(0xFFFF), 0) ? ImGui::TextColored(green, "ON ") : ImGui::TextColored(gray, "OFF "); ImGui::SameLine();
+ ImGui::TextColored(magenta, "IF:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFF0F), 0)); ImGui::SameLine();
+ ImGui::TextColored(magenta, " IE:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFFFF), 0));
+
+ ImGui::TextColored(cyan, " STAT "); ImGui::SameLine();
+ IsSetBit(memory->Retrieve(0xFF0F), 1) && IsSetBit(memory->Retrieve(0xFFFF), 1) ? ImGui::TextColored(green, "ON ") : ImGui::TextColored(gray, "OFF "); ImGui::SameLine();
+ ImGui::TextColored(magenta, "IF:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFF0F), 1)); ImGui::SameLine();
+ ImGui::TextColored(magenta, " IE:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFFFF), 1));
+
+ ImGui::TextColored(cyan, " TIMER "); ImGui::SameLine();
+ IsSetBit(memory->Retrieve(0xFF0F), 2) && IsSetBit(memory->Retrieve(0xFFFF), 2) ? ImGui::TextColored(green, "ON ") : ImGui::TextColored(gray, "OFF "); ImGui::SameLine();
+ ImGui::TextColored(magenta, "IF:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFF0F), 2)); ImGui::SameLine();
+ ImGui::TextColored(magenta, " IE:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFFFF), 2));
+
+ ImGui::TextColored(cyan, " SERIAL "); ImGui::SameLine();
+ IsSetBit(memory->Retrieve(0xFF0F), 3) && IsSetBit(memory->Retrieve(0xFFFF), 3) ? ImGui::TextColored(green, "ON ") : ImGui::TextColored(gray, "OFF "); ImGui::SameLine();
+ ImGui::TextColored(magenta, "IF:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFF0F), 3)); ImGui::SameLine();
+ ImGui::TextColored(magenta, " IE:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFFFF), 3));
+
+ ImGui::TextColored(cyan, " JOYPAD "); ImGui::SameLine();
+ IsSetBit(memory->Retrieve(0xFF0F), 4) && IsSetBit(memory->Retrieve(0xFFFF), 4) ? ImGui::TextColored(green, "ON ") : ImGui::TextColored(gray, "OFF "); ImGui::SameLine();
+ ImGui::TextColored(magenta, "IF:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFF0F), 4)); ImGui::SameLine();
+ ImGui::TextColored(magenta, " IE:"); ImGui::SameLine();
+ ImGui::Text("%d", IsSetBit(memory->Retrieve(0xFFFF), 4));
+
+ ImGui::TextColored(yellow, "GBC:");
+
+ ImGui::TextColored(cyan, " $FF4D"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "KEY1"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF4D), BYTE_TO_BINARY(memory->Retrieve(0xFF4D)));
+
+ ImGui::TextColored(cyan, " $FF70"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "SVBK"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF70), BYTE_TO_BINARY(memory->Retrieve(0xFF70)));
+
+ ImGui::TextColored(yellow, "GBC LCD:");
+
+ ImGui::TextColored(cyan, " $FF68"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "BCPS"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF68), BYTE_TO_BINARY(memory->Retrieve(0xFF68)));
+
+ ImGui::TextColored(cyan, " $FF69"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "BCPD"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF69), BYTE_TO_BINARY(memory->Retrieve(0xFF69)));
+
+ ImGui::TextColored(cyan, " $FF6A"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "OCPS"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF6A), BYTE_TO_BINARY(memory->Retrieve(0xFF6A)));
+
+ ImGui::TextColored(cyan, " $FF6B"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "OCPD"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF6B), BYTE_TO_BINARY(memory->Retrieve(0xFF6B)));
+
+ ImGui::TextColored(cyan, " $FF4F"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "VBK "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF4F), BYTE_TO_BINARY(memory->Retrieve(0xFF4F)));
+
+ ImGui::TextColored(yellow, "GBC HDMA:");
+
+ ImGui::TextColored(cyan, " $FF51:$FF52"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "SOURCE "); ImGui::SameLine();
+ ImGui::Text("$%04X", (memory->Retrieve(0xFF51) << 8) | memory->Retrieve(0xFF52));
+
+ ImGui::TextColored(cyan, " $FF53:$FF54"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "DEST "); ImGui::SameLine();
+ ImGui::Text("$%04X", (memory->Retrieve(0xFF53) << 8) | memory->Retrieve(0xFF54));
+
+ ImGui::TextColored(cyan, " $FF55"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "LEN "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF55), BYTE_TO_BINARY(memory->Retrieve(0xFF55)));
+
+ ImGui::TextColored(yellow, "GBC INFRARED:");
+
+ ImGui::TextColored(cyan, " $FF56"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "RP "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF56), BYTE_TO_BINARY(memory->Retrieve(0xFF56)));
+
+ ImGui::NextColumn();
+
+ ImGui::TextColored(yellow, "LCD:");
+
+ ImGui::TextColored(cyan, " $FF40"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "LCDC"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF40), BYTE_TO_BINARY(memory->Retrieve(0xFF40)));
+
+ ImGui::TextColored(cyan, " $FF41"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "STAT"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF41), BYTE_TO_BINARY(memory->Retrieve(0xFF41)));
+
+ ImGui::TextColored(cyan, " $FF42"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "SCY "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF42), BYTE_TO_BINARY(memory->Retrieve(0xFF42)));
+
+ ImGui::TextColored(cyan, " $FF43"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "SCX "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF43), BYTE_TO_BINARY(memory->Retrieve(0xFF43)));
+
+ ImGui::TextColored(cyan, " $FF44"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "LY "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF44), BYTE_TO_BINARY(memory->Retrieve(0xFF44)));
+
+ ImGui::TextColored(cyan, " $FF45"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "LYC "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF45), BYTE_TO_BINARY(memory->Retrieve(0xFF45)));
+
+ ImGui::TextColored(cyan, " $FF46"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "DMA "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF46), BYTE_TO_BINARY(memory->Retrieve(0xFF46)));
+
+ ImGui::TextColored(cyan, " $FF47"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "BGP "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF47), BYTE_TO_BINARY(memory->Retrieve(0xFF47)));
+
+ ImGui::TextColored(cyan, " $FF48"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "OBP0"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF48), BYTE_TO_BINARY(memory->Retrieve(0xFF48)));
+
+ ImGui::TextColored(cyan, " $FF49"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "OBP1"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF49), BYTE_TO_BINARY(memory->Retrieve(0xFF49)));
+
+ ImGui::TextColored(cyan, " $FF4A"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "WY "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF4A), BYTE_TO_BINARY(memory->Retrieve(0xFF4A)));
+
+ ImGui::TextColored(cyan, " $FF4B"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "WX "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF4B), BYTE_TO_BINARY(memory->Retrieve(0xFF4B)));
+
+ ImGui::TextColored(yellow, "TIMER:");
+
+ ImGui::TextColored(cyan, " $FF04"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "DIV "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF04), BYTE_TO_BINARY(memory->Retrieve(0xFF04)));
+
+ ImGui::TextColored(cyan, " $FF05"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "TIMA"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF05), BYTE_TO_BINARY(memory->Retrieve(0xFF05)));
+
+ ImGui::TextColored(cyan, " $FF06"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "TMA "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF06), BYTE_TO_BINARY(memory->Retrieve(0xFF06)));
+
+ ImGui::TextColored(cyan, " $FF07"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "TAC "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF07), BYTE_TO_BINARY(memory->Retrieve(0xFF07)));
+
+ ImGui::TextColored(yellow, "INPUT:");
+
+ ImGui::TextColored(cyan, " $FF00"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "JOYP"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF00), BYTE_TO_BINARY(memory->Retrieve(0xFF00)));
+
+ ImGui::TextColored(yellow, "SERIAL:");
+
+ ImGui::TextColored(cyan, " $FF01"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "SB "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF01), BYTE_TO_BINARY(memory->Retrieve(0xFF01)));
+
+ ImGui::TextColored(cyan, " $FF02"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "SC "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ")", memory->Retrieve(0xFF02), BYTE_TO_BINARY(memory->Retrieve(0xFF02)));
+
+ ImGui::Columns(1);
+
+ ImGui::PopFont();
+
+ ImGui::End();
+}
+
+static void debug_window_vram(void)
+{
+ ImGui::SetNextWindowPos(ImVec2(60, 60), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(544, 534), ImGuiCond_FirstUseEver);
+
+ ImGui::Begin("VRAM Viewer", &config_debug.show_video);
+
+ if (ImGui::BeginTabBar("##vram_tabs", ImGuiTabBarFlags_None))
+ {
+ if (ImGui::BeginTabItem("Background"))
+ {
+ debug_window_vram_background();
+ ImGui::EndTabItem();
+ }
+
+ if (ImGui::BeginTabItem("Tiles"))
+ {
+ debug_window_vram_tiles();
+ ImGui::EndTabItem();
+ }
+
+ if (ImGui::BeginTabItem("OAM"))
+ {
+ debug_window_vram_oam();
+ ImGui::EndTabItem();
+ }
+
+ if (ImGui::BeginTabItem("Palettes"))
+ {
+ debug_window_vram_palettes();
+ ImGui::EndTabItem();
+ }
+
+ ImGui::EndTabBar();
+ }
+
+ ImGui::End();
+}
+
+static void debug_window_vram_background(void)
+{
+ Memory* memory = emu_get_core()->GetMemory();
+
+ static bool show_grid = true;
+ static bool show_screen = true;
+ static int tile_address_radio = 0;
+ static int map_address_radio = 0;
+ float scale = 1.5f;
+ float size = 256.0f * scale;
+ float spacing = 8.0f * scale;
+
+ ImGui::Checkbox("Show Grid##grid_bg", &show_grid); ImGui::SameLine();
+ ImGui::Checkbox("Show Screen Rect", &show_screen);
+
+ ImGui::PushFont(gui_default_font);
+
+ ImGui::Columns(2, "bg", false);
+ ImGui::SetColumnOffset(1, size + 10.0f);
+
+ ImVec2 p = ImGui::GetCursorScreenPos();
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ ImGuiIO& io = ImGui::GetIO();
+
+ ImGui::Image((void*)(intptr_t)renderer_emu_debug_vram_background, ImVec2(size, size));
+
+ if (show_grid)
+ {
+ float x = p.x;
+ for (int n = 0; n <= 32; n++)
+ {
+ draw_list->AddLine(ImVec2(x, p.y), ImVec2(x, p.y + size), ImColor(dark_gray), 1.0f);
+ x += spacing;
+ }
+
+ float y = p.y;
+ for (int n = 0; n <= 32; n++)
+ {
+ draw_list->AddLine(ImVec2(p.x, y), ImVec2(p.x + size, y), ImColor(dark_gray), 1.0f);
+ y += spacing;
+ }
+ }
+
+ if (show_screen)
+ {
+ u8 scroll_x = memory->Retrieve(0xFF43);
+ u8 scroll_y = memory->Retrieve(0xFF42);
+
+ float grid_x_max = p.x + size;
+ float grid_y_max = p.y + size;
+
+ float rect_x_min = p.x + (scroll_x * scale);
+ float rect_y_min = p.y + (scroll_y * scale);
+ float rect_x_max = p.x + ((scroll_x + GAMEBOY_WIDTH) * scale);
+ float rect_y_max = p.y + ((scroll_y + GAMEBOY_HEIGHT) * scale);
+
+ float x_overflow = 0.0f;
+ float y_overflow = 0.0f;
+
+ if (rect_x_max > grid_x_max)
+ x_overflow = rect_x_max - grid_x_max;
+ if (rect_y_max > grid_y_max)
+ y_overflow = rect_y_max - grid_y_max;
+
+ draw_list->AddLine(ImVec2(rect_x_min, rect_y_min), ImVec2(fminf(rect_x_max, grid_x_max), rect_y_min), ImColor(green), 2.0f);
+ if (x_overflow > 0.0f)
+ draw_list->AddLine(ImVec2(p.x, rect_y_min), ImVec2(p.x + x_overflow, rect_y_min), ImColor(green), 2.0f);
+
+ draw_list->AddLine(ImVec2(rect_x_min, rect_y_min), ImVec2(rect_x_min, fminf(rect_y_max, grid_y_max)), ImColor(green), 2.0f);
+ if (y_overflow > 0.0f)
+ draw_list->AddLine(ImVec2(rect_x_min, p.y), ImVec2(rect_x_min, p.y + y_overflow), ImColor(green), 2.0f);
+
+ draw_list->AddLine(ImVec2(rect_x_min, (y_overflow > 0.0f) ? p.y + y_overflow : rect_y_max), ImVec2(fminf(rect_x_max, grid_x_max), (y_overflow > 0.0f) ? p.y + y_overflow : rect_y_max), ImColor(green), 2.0f);
+ if (x_overflow > 0.0f)
+ draw_list->AddLine(ImVec2(p.x, (y_overflow > 0.0f) ? p.y + y_overflow : rect_y_max), ImVec2(p.x + x_overflow, (y_overflow > 0.0f) ? p.y + y_overflow : rect_y_max), ImColor(green), 2.0f);
+
+ draw_list->AddLine(ImVec2((x_overflow > 0.0f) ? p.x + x_overflow : rect_x_max, rect_y_min), ImVec2((x_overflow > 0.0f) ? p.x + x_overflow : rect_x_max, fminf(rect_y_max, grid_y_max)), ImColor(green), 2.0f);
+ if (y_overflow > 0.0f)
+ draw_list->AddLine(ImVec2((x_overflow > 0.0f) ? p.x + x_overflow : rect_x_max, p.y), ImVec2((x_overflow > 0.0f) ? p.x + x_overflow : rect_x_max, p.y + y_overflow), ImColor(green), 2.0f);
+ }
+
+ float mouse_x = io.MousePos.x - p.x;
+ float mouse_y = io.MousePos.y - p.y;
+
+ int tile_x = -1;
+ int tile_y = -1;
+ if ((mouse_x >= 0.0f) && (mouse_x < size) && (mouse_y >= 0.0f) && (mouse_y < size))
+ {
+ tile_x = (int)(mouse_x / spacing);
+ tile_y = (int)(mouse_y / spacing);
+
+ draw_list->AddRect(ImVec2(p.x + (tile_x * spacing), p.y + (tile_y * spacing)), ImVec2(p.x + ((tile_x + 1) * spacing), p.y + ((tile_y + 1) * spacing)), ImColor(cyan), 2.0f, 15, 2.0f);
+
+ ImGui::NextColumn();
+
+ ImGui::Image((void*)(intptr_t)renderer_emu_debug_vram_background, ImVec2(128.0f, 128.0f), ImVec2((1.0f / 32.0f) * tile_x, (1.0f / 32.0f) * tile_y), ImVec2((1.0f / 32.0f) * (tile_x + 1), (1.0f / 32.0f) * (tile_y + 1)));
+
+ ImGui::TextColored(yellow, "DMG:");
+
+ ImGui::TextColored(cyan, " X:"); ImGui::SameLine();
+ ImGui::Text("$%02X", tile_x); ImGui::SameLine();
+ ImGui::TextColored(cyan, " Y:"); ImGui::SameLine();
+ ImGui::Text("$%02X", tile_y);
+
+ u8 lcdc = memory->Retrieve(0xFF40);
+
+ int tile_start_addr = emu_debug_background_tile_address >= 0 ? emu_debug_background_tile_address : IsSetBit(lcdc, 4) ? 0x8000 : 0x8800;
+ int map_start_addr = emu_debug_background_map_address >= 0 ? emu_debug_background_map_address : IsSetBit(lcdc, 3) ? 0x9C00 : 0x9800;
+
+ u16 map_addr = map_start_addr + (32 * tile_y) + tile_x;
+
+ ImGui::TextColored(cyan, " Map Addr: "); ImGui::SameLine();
+ ImGui::Text("$%04X", map_addr);
+
+ int map_tile = 0;
+
+ if (tile_start_addr == 0x8800)
+ {
+ map_tile = static_cast<s8> (memory->Retrieve(map_addr));
+ map_tile += 128;
+ }
+ else
+ {
+ map_tile = memory->Retrieve(map_addr);
+ }
+
+ ImGui::TextColored(cyan, " Tile Addr:"); ImGui::SameLine();
+ ImGui::Text("$%04X", tile_start_addr + (map_tile << 4));
+ ImGui::TextColored(cyan, " Tile Number:"); ImGui::SameLine();
+ ImGui::Text("$%02X", memory->Retrieve(map_addr));
+
+ if (emu_is_cgb())
+ {
+ ImGui::TextColored(yellow, "GBC:");
+
+ u8 cgb_tile_attr = memory->ReadCGBLCDRAM(map_addr, true);
+ int cgb_tile_pal = cgb_tile_attr & 0x07;
+ int cgb_tile_bank = IsSetBit(cgb_tile_attr, 3) ? 1 : 0;
+ bool cgb_tile_xflip = IsSetBit(cgb_tile_attr, 5);
+ bool cgb_tile_yflip = IsSetBit(cgb_tile_attr, 6);
+
+ ImGui::TextColored(cyan, " Attributes:"); ImGui::SameLine();
+ ImGui::Text("$%02X", cgb_tile_attr);
+ ImGui::TextColored(cyan, " Palette:"); ImGui::SameLine();
+ ImGui::Text("%d", cgb_tile_pal);
+ ImGui::TextColored(cyan, " Bank:"); ImGui::SameLine();
+ ImGui::Text("%d", cgb_tile_bank);
+
+ ImGui::TextColored(cyan, " X-Flip:"); ImGui::SameLine();
+ cgb_tile_xflip ? ImGui::TextColored(green, "ON") : ImGui::TextColored(gray, "OFF");
+
+ ImGui::TextColored(cyan, " Y-Flip:"); ImGui::SameLine();
+ cgb_tile_yflip ? ImGui::TextColored(green, "ON") : ImGui::TextColored(gray, "OFF");
+ }
+ }
+
+ ImGui::Columns(1);
+
+ ImGui::PopFont();
+
+ ImGui::Text("Tile address:"); ImGui::SameLine();
+ ImGui::RadioButton("Auto##tile", &tile_address_radio, 0); ImGui::SameLine();
+ ImGui::RadioButton("0x8000", &tile_address_radio, 1); ImGui::SameLine();
+ ImGui::RadioButton("0x8800", &tile_address_radio, 2);
+
+ switch (tile_address_radio)
+ {
+ case 0:
+ emu_debug_background_tile_address = -1;
+ break;
+ case 1:
+ emu_debug_background_tile_address = 0x8000;
+ break;
+ case 2:
+ emu_debug_background_tile_address = 0x8800;
+ break;
+ default:
+ emu_debug_background_tile_address = -1;
+ break;
+ }
+
+ ImGui::Text("Map address:"); ImGui::SameLine();
+ ImGui::RadioButton("Auto##map", &map_address_radio, 0); ImGui::SameLine();
+ ImGui::RadioButton("0x9C00", &map_address_radio, 1); ImGui::SameLine();
+ ImGui::RadioButton("0x9800", &map_address_radio, 2);
+
+ switch (map_address_radio)
+ {
+ case 0:
+ emu_debug_background_map_address = -1;
+ break;
+ case 1:
+ emu_debug_background_map_address = 0x9C00;
+ break;
+ case 2:
+ emu_debug_background_map_address = 0x9800;
+ break;
+ default:
+ emu_debug_background_map_address = -1;
+ break;
+ }
+}
+
+static void debug_window_vram_tiles(void)
+{
+ static bool show_grid = true;
+ float scale = 1.5f;
+ float width = 8.0f * 16.0f * scale;
+ float height = 8.0f * 24.0f * scale;
+ float spacing = 8.0f * scale;
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ ImGuiIO& io = ImGui::GetIO();
+ ImVec2 p[2];
+
+ ImGui::Checkbox("Show Grid##grid_tiles", &show_grid);
+ ImGui::SameLine(150.0f);
+
+ ImGui::PushItemWidth(80.0f);
+
+ if (!emu_is_cgb())
+ {
+ ImGui::Combo("Palette##dmg_tile_palette", &emu_debug_tile_dmg_palette, "BGP\0OBP0\0OBP1\0\0");
+ }
+ else
+ {
+ ImGui::Combo("Palette##cgb_tile_palette", &emu_debug_tile_color_palette, "BCP0\0BCP1\0BCP2\0BCP3\0BCP4\0BCP5\0BCP6\0BCP7\0OCP0\0OCP1\0OCP2\0OCP3\0OCP4\0OCP5\0OCP6\0OCP7\0\0");
+ }
+
+ ImGui::PopItemWidth();
+
+ ImGui::Columns(2, "bg", false);
+ ImGui::SetColumnOffset(1, (width * 2.0f) + 16.0f);
+
+ p[0] = ImGui::GetCursorScreenPos();
+
+ ImGui::Image((void*)(intptr_t)renderer_emu_debug_vram_tiles[0], ImVec2(width, height));
+
+ ImGui::SameLine();
+
+ p[1] = ImGui::GetCursorScreenPos();
+
+ ImGui::Image((void*)(intptr_t)renderer_emu_debug_vram_tiles[1], ImVec2(width, height));
+
+ for (int i = 0; i < 2; i++)
+ {
+ if (show_grid)
+ {
+ float x = p[i].x;
+ for (int n = 0; n <= 16; n++)
+ {
+ draw_list->AddLine(ImVec2(x, p[i].y), ImVec2(x, p[i].y + height), ImColor(dark_gray), 1.0f);
+ x += spacing;
+ }
+
+ float y = p[i].y;
+ for (int n = 0; n <= 24; n++)
+ {
+ draw_list->AddLine(ImVec2(p[i].x, y), ImVec2(p[i].x + width, y), ImColor(dark_gray), ((n == 8) || (n == 16)) ? 3.0f : 1.0f);
+ y += spacing;
+ }
+ }
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ float mouse_x = io.MousePos.x - p[i].x;
+ float mouse_y = io.MousePos.y - p[i].y;
+
+ int tile_x = -1;
+ int tile_y = -1;
+
+ if ((mouse_x >= 0.0f) && (mouse_x < width) && (mouse_y >= 0.0f) && (mouse_y < height))
+ {
+ tile_x = (int)(mouse_x / spacing);
+ tile_y = (int)(mouse_y / spacing);
+
+ draw_list->AddRect(ImVec2(p[i].x + (tile_x * spacing), p[i].y + (tile_y * spacing)), ImVec2(p[i].x + ((tile_x + 1) * spacing), p[i].y + ((tile_y + 1) * spacing)), ImColor(cyan), 2.0f, 15, 2.0f);
+
+ ImGui::NextColumn();
+
+ ImGui::Image((void*)(intptr_t)renderer_emu_debug_vram_tiles[i], ImVec2(128.0f, 128.0f), ImVec2((1.0f / 16.0f) * tile_x, (1.0f / 24.0f) * tile_y), ImVec2((1.0f / 16.0f) * (tile_x + 1), (1.0f / 24.0f) * (tile_y + 1)));
+
+ ImGui::PushFont(gui_default_font);
+
+ ImGui::TextColored(yellow, "DETAILS:");
+
+ int tile_full = (tile_y << 4) + tile_x;
+ int tile = tile_full & 0xFF;
+
+ ImGui::TextColored(cyan, " Tile Number:"); ImGui::SameLine();
+ ImGui::Text("$%02X", tile);
+ ImGui::TextColored(cyan, " Tile Addr:"); ImGui::SameLine();
+ ImGui::Text("$%04X", 0x8000 + (tile_full << 4));
+
+ ImGui::PopFont();
+ }
+ }
+
+ ImGui::Columns(1);
+}
+
+static void debug_window_vram_oam(void)
+{
+ float scale = 5.0f;
+ float width = 8.0f * scale;
+ float height_8 = 8.0f * scale;
+ float height_16 = 16.0f * scale;
+
+ GearboyCore* core = emu_get_core();
+ Memory* memory = core->GetMemory();
+
+ ImVec2 p[40];
+
+ ImGuiIO& io = ImGui::GetIO();
+
+ u8 lcdc = memory->Retrieve(0xFF40);
+ bool sprites_16 = IsSetBit(lcdc, 2);
+
+ ImGui::PushFont(gui_default_font);
+
+ ImGui::Columns(2, "oam", false);
+ ImGui::SetColumnOffset(1, 280.0f);
+
+ ImGui::BeginChild("sprites", ImVec2(0, 0), true);
+
+ for (int s = 0; s < 40; s++)
+ {
+ p[s] = ImGui::GetCursorScreenPos();
+
+ ImGui::Image((void*)(intptr_t)renderer_emu_debug_vram_oam[s], ImVec2(width, sprites_16 ? height_16 : height_8), ImVec2(0.0f, 0.0f), ImVec2(1.0f, sprites_16 ? 1.0f : 0.5f));
+
+ float mouse_x = io.MousePos.x - p[s].x;
+ float mouse_y = io.MousePos.y - p[s].y;
+
+ if ((mouse_x >= 0.0f) && (mouse_x < width) && (mouse_y >= 0.0f) && (mouse_y < (sprites_16 ? height_16 : height_8)))
+ {
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ draw_list->AddRect(ImVec2(p[s].x, p[s].y), ImVec2(p[s].x + width, p[s].y + (sprites_16 ? height_16 : height_8)), ImColor(cyan), 2.0f, 15, 3.0f);
+ }
+
+ if (s % 5 < 4)
+ ImGui::SameLine();
+ }
+
+ ImGui::EndChild();
+
+ ImGui::NextColumn();
+
+ ImVec2 p_screen = ImGui::GetCursorScreenPos();
+
+ float screen_scale = 1.5f;
+
+ ImGui::Image((void*)(intptr_t)renderer_emu_texture, ImVec2(GAMEBOY_WIDTH * screen_scale, GAMEBOY_HEIGHT * screen_scale));
+
+ for (int s = 0; s < 40; s++)
+ {
+ if ((p[s].x == 0) && (p[s].y == 0))
+ continue;
+
+ float mouse_x = io.MousePos.x - p[s].x;
+ float mouse_y = io.MousePos.y - p[s].y;
+
+ if ((mouse_x >= 0.0f) && (mouse_x < width) && (mouse_y >= 0.0f) && (mouse_y < (sprites_16 ? height_16 : height_8)))
+ {
+ u16 address = 0xFE00 + (4 * s);
+
+ u8 y = memory->Retrieve(address);
+ u8 x = memory->Retrieve(address + 1);
+ u8 tile = memory->Retrieve(address + 2);
+ u8 flags = memory->Retrieve(address + 3);
+ int palette = IsSetBit(flags, 4) ? 1 : 0;
+ bool xflip = IsSetBit(flags, 5);
+ bool yflip = IsSetBit(flags, 6);
+ bool priority = !IsSetBit(flags, 7);
+ bool cgb_bank = IsSetBit(flags, 3);
+ int cgb_pal = flags & 0x07;
+
+ float real_x = x - 8.0f;
+ float real_y = y - 16.0f;
+ float rectx_min = p_screen.x + (real_x * screen_scale);
+ float rectx_max = p_screen.x + ((real_x + 8.0f) * screen_scale);
+ float recty_min = p_screen.y + (real_y * screen_scale);
+ float recty_max = p_screen.y + ((real_y + (sprites_16 ? 16.0f : 8.0f)) * screen_scale);
+
+ rectx_min = fminf(fmaxf(rectx_min, p_screen.x), p_screen.x + (GAMEBOY_WIDTH * screen_scale));
+ rectx_max = fminf(fmaxf(rectx_max, p_screen.x), p_screen.x + (GAMEBOY_WIDTH * screen_scale));
+ recty_min = fminf(fmaxf(recty_min, p_screen.y), p_screen.y + (GAMEBOY_HEIGHT * screen_scale));
+ recty_max = fminf(fmaxf(recty_max, p_screen.y), p_screen.y + (GAMEBOY_HEIGHT * screen_scale));
+
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ draw_list->AddRect(ImVec2(rectx_min, recty_min), ImVec2(rectx_max, recty_max), ImColor(cyan), 2.0f, 15, 2.0f);
+
+ ImGui::TextColored(yellow, "DETAILS:");
+ ImGui::TextColored(cyan, " X:"); ImGui::SameLine();
+ ImGui::Text("$%02X", x); ImGui::SameLine();
+ ImGui::TextColored(cyan, " Y:"); ImGui::SameLine();
+ ImGui::Text("$%02X", y); ImGui::SameLine();
+
+ ImGui::TextColored(cyan, " Tile:"); ImGui::SameLine();
+ ImGui::Text("$%02X", tile);
+
+ ImGui::TextColored(cyan, " Tile Addr:"); ImGui::SameLine();
+ ImGui::Text("$%04X", 0x8000 + (tile * 16)); ImGui::SameLine();
+
+ ImGui::TextColored(cyan, " Bank:"); ImGui::SameLine();
+ ImGui::Text("%d", cgb_bank);
+
+ ImGui::TextColored(cyan, " OAM Addr:"); ImGui::SameLine();
+ ImGui::Text("$%04X", address); ImGui::SameLine();
+
+
+ ImGui::TextColored(cyan, " Flags:"); ImGui::SameLine();
+ ImGui::Text("$%02X", flags);
+
+ ImGui::TextColored(cyan, " Priority:"); ImGui::SameLine();
+ priority ? ImGui::TextColored(green, "ON ") : ImGui::TextColored(gray, "OFF"); ImGui::SameLine();
+
+ ImGui::TextColored(cyan, " Palette:"); ImGui::SameLine();
+ ImGui::Text("%d", emu_is_cgb() ? cgb_pal : palette);
+
+ ImGui::TextColored(cyan, " X-Flip:"); ImGui::SameLine();
+ xflip ? ImGui::TextColored(green, "ON ") : ImGui::TextColored(gray, "OFF"); ImGui::SameLine();
+
+ ImGui::TextColored(cyan, " Y-Flip:"); ImGui::SameLine();
+ yflip ? ImGui::TextColored(green, "ON") : ImGui::TextColored(gray, "OFF");
+ }
+ }
+
+ ImGui::Columns(1);
+
+ ImGui::PopFont();
+}
+
+static void debug_window_vram_palettes(void)
+{
+ GearboyCore* core = emu_get_core();
+ Video* video = core->GetVideo();
+ Memory* memory = core->GetMemory();
+ u16* palette = core->GetDMGInternalPalette();
+
+ ImGui::PushFont(gui_default_font);
+
+ ImGui::TextColored(yellow, "DMG:"); ImGui::SameLine();
+ ImGui::TextColored(cyan, " 0 1 2 3");
+
+ u8 bgp = memory->Retrieve(0xFF47);
+ u8 obp0 = memory->Retrieve(0xFF48);
+ u8 obp1 = memory->Retrieve(0xFF49);
+
+ ImGui::TextColored(cyan, " $FF47"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "BGP "); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ") ", bgp, BYTE_TO_BINARY(bgp)); ImGui::SameLine();
+
+ for (int i = 0; i < 4; i++)
+ {
+ int index = (bgp >> (i * 2)) & 0x03;
+ int color = palette[index];
+ ImVec4 float_color = color_565_to_float(color);
+ char id[16];
+ sprintf(id, "##dmg_bg_%d", i);
+ ImGui::ColorEdit3(id, (float*)&float_color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
+ ImGui::Text("%d ", index);
+ if (i < 3)
+ ImGui::SameLine();
+ }
+
+ ImGui::TextColored(cyan, " $FF48"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "OBP0"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ") ", obp0, BYTE_TO_BINARY(obp0)); ImGui::SameLine();
+
+ for (int i = 0; i < 4; i++)
+ {
+ int index = (obp0 >> (i * 2)) & 0x03;
+ int color = palette[index];
+ ImVec4 float_color = color_565_to_float(color);
+ char id[16];
+ sprintf(id, "##dmg_bg_%d", i);
+ ImGui::ColorEdit3(id, (float*)&float_color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
+ ImGui::Text("%d ", index);
+ if (i < 3)
+ ImGui::SameLine();
+ }
+
+ ImGui::TextColored(cyan, " $FF49"); ImGui::SameLine();
+ ImGui::TextColored(magenta, "OBP1"); ImGui::SameLine();
+ ImGui::Text("$%02X (" BYTE_TO_BINARY_PATTERN_SPACED ") ", obp1, BYTE_TO_BINARY(obp1)); ImGui::SameLine();
+
+ for (int i = 0; i < 4; i++)
+ {
+ int index = (obp1 >> (i * 2)) & 0x03;
+ int color = palette[index];
+ ImVec4 float_color = color_565_to_float(color);
+ char id[16];
+ sprintf(id, "##dmg_bg_%d", i);
+ ImGui::ColorEdit3(id, (float*)&float_color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
+ ImGui::Text("%d ", index);
+ if (i < 3)
+ ImGui::SameLine();
+ }
+
+ ImGui::Text(" ");
+
+ PaletteMatrix bg_palettes = video->GetCGBBackgroundPalettes();
+ PaletteMatrix sprite_palettes = video->GetCGBSpritePalettes();
+
+ ImGui::Columns(2, "palettes");
+
+ ImGui::TextColored(yellow, "GBC BACKGROUND:");
+
+ ImGui::NextColumn();
+
+ ImGui::TextColored(yellow, "GBC SPRITES:");
+
+ ImGui::NextColumn();
+
+ ImGui::Separator();
+
+ for (int p = 0; p < 8; p++)
+ {
+ ImGui::TextColored(cyan, " %d ", p); ImGui::SameLine();
+
+ for (int c = 0; c < 4; c++)
+ {
+ u16 color = (*bg_palettes)[p][c][1];
+ ImVec4 float_color = color_565_to_float(color);
+ char id[16];
+ sprintf(id, "##cgb_bg_%d_%d", p, c);
+ ImGui::ColorEdit3(id, (float*)&float_color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoPicker);
+ if (c < 3)
+ {
+ ImGui::SameLine(); ImGui::Dummy(ImVec2(8.0f, 0.0f));
+ ImGui::SameLine();
+ }
+ }
+
+ ImGui::Text(" "); ImGui::SameLine();
+
+ for (int c = 0; c < 4; c++)
+ {
+ u16 color = (*bg_palettes)[p][c][1];
+ ImGui::Text("%04X ", color);
+ if (c < 3)
+ ImGui::SameLine();
+ }
+ }
+
+ ImGui::NextColumn();
+
+ for (int p = 0; p < 8; p++)
+ {
+ ImGui::TextColored(cyan, " %d ", p); ImGui::SameLine();
+
+ for (int c = 0; c < 4; c++)
+ {
+ u16 color = (*sprite_palettes)[p][c][1];
+ ImVec4 float_color = color_565_to_float(color);
+ char id[16];
+ sprintf(id, "##cgb_bg_%d_%d", p, c);
+ ImGui::ColorEdit3(id, (float*)&float_color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoPicker);
+ if (c < 3)
+ {
+ ImGui::SameLine(); ImGui::Dummy(ImVec2(8.0f, 0.0f));
+ ImGui::SameLine();
+ }
+ }
+
+ ImGui::Text(" "); ImGui::SameLine();
+
+ for (int c = 0; c < 4; c++)
+ {
+ u16 color = (*sprite_palettes)[p][c][1];
+ ImGui::Text("%04X ", color);
+ if (c < 3)
+ ImGui::SameLine();
+ }
+ }
+
+ ImGui::Columns(1);
+
+ ImGui::PopFont();
+}
+
+static void add_symbol(const char* line)
+{
+ Log("Loading symbol %s", line);
+
+ DebugSymbol s;
+
+ std::string str(line);
+
+ str.erase(std::remove(str.begin(), str.end(), '\r'), str.end());
+ str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
+
+ size_t first = str.find_first_not_of(' ');
+ if (std::string::npos == first)
+ {
+ str = "";
+ }
+ else
+ {
+ size_t last = str.find_last_not_of(' ');
+ str = str.substr(first, (last - first + 1));
+ }
+
+ std::size_t comment = str.find(";");
+
+ if (comment != std::string::npos)
+ str = str.substr(0 , comment);
+
+ std::size_t space = str.find(" ");
+
+ if (space != std::string::npos)
+ {
+ s.text = str.substr(space + 1 , std::string::npos);
+ str = str.substr(0, space);
+
+ std::size_t separator = str.find(":");
+
+ try
+ {
+ if (separator != std::string::npos)
+ {
+ s.address = (u16)std::stoul(str.substr(separator + 1 , std::string::npos), 0, 16);
+ s.bank = std::stoul(str.substr(0, separator), 0 , 16);
+ }
+ else
+ {
+ s.address = (u16)std::stoul(str, 0, 16);
+ s.bank = 0;
+ }
+
+ symbols.push_back(s);
+ }
+ catch(const std::invalid_argument&)
+ {
+ }
+ }
+}
+
+static void add_breakpoint_cpu(void)
+{
+ int input_len = (int)strlen(brk_address_cpu);
+ u16 target_address = 0;
+ int target_bank = 0;
+ int target_offset = 0;
+
+ try
+ {
+ if ((input_len == 7) && (brk_address_cpu[2] == ':'))
+ {
+ std::string str(brk_address_cpu);
+ std::size_t separator = str.find(":");
+
+ if (separator != std::string::npos)
+ {
+ target_address = (u16)std::stoul(str.substr(separator + 1 , std::string::npos), 0, 16);
+
+ target_bank = std::stoul(str.substr(0, separator), 0 , 16);
+ target_bank &= 0xFF;
+ }
+ }
+ else if (input_len == 4)
+ {
+ target_bank = 0;
+ target_address = (u16)std::stoul(brk_address_cpu, 0, 16);
+ }
+ else
+ {
+ return;
+ }
+ }
+ catch(const std::invalid_argument&)
+ {
+ return;
+ }
+
+ Memory::stDisassembleRecord** memoryMap = emu_get_core()->GetMemory()->GetDisassembledMemoryMap();
+ Memory::stDisassembleRecord** romMap = emu_get_core()->GetMemory()->GetDisassembledROMMemoryMap();
+ Memory::stDisassembleRecord** map = NULL;
+
+ bool rom = true;
+
+ if ((target_address & 0xC000) == 0x0000)
+ {
+ target_offset = (0x4000 * target_bank) + target_address;
+ map = romMap;
+ }
+ else if ((target_address & 0xC000) == 0x4000)
+ {
+ target_offset = (0x4000 * target_bank) + (target_address & 0x3FFF);
+ map = romMap;
+ }
+ else
+ {
+ target_offset = target_address;
+ map = memoryMap;
+ rom = false;
+ }
+
+ brk_address_cpu[0] = 0;
+
+ bool found = false;
+ std::vector<Memory::stDisassembleRecord*>* breakpoints = emu_get_core()->GetMemory()->GetBreakpointsCPU();
+
+ if (IsValidPointer(map[target_offset]))
+ {
+ for (long unsigned int b = 0; b < breakpoints->size(); b++)
+ {
+ if ((*breakpoints)[b] == map[target_offset])
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ if (!IsValidPointer(map[target_offset]))
+ {
+ map[target_offset] = new Memory::stDisassembleRecord;
+
+ if (rom)
+ {
+ map[target_offset]->address = target_offset & 0x3FFF;
+ map[target_offset]->bank = target_offset >> 14;
+ }
+ else
+ {
+ map[target_offset]->address = 0;
+ map[target_offset]->bank = 0;
+ }
+
+ map[target_offset]->name[0] = 0;
+ map[target_offset]->bytes[0] = 0;
+ map[target_offset]->size = 0;
+ map[target_offset]->jump = false;
+ map[target_offset]->jump_address = 0;
+ for (int i = 0; i < 4; i++)
+ map[target_offset]->opcodes[i] = 0;
+ }
+
+ breakpoints->push_back(map[target_offset]);
+ }
+}
+
+static void add_breakpoint_mem(void)
+{
+ int input_len = (int)strlen(brk_address_mem);
+ u16 address1 = 0;
+ u16 address2 = 0;
+ bool range = false;
+
+ try
+ {
+ if ((input_len == 9) && (brk_address_mem[4] == '-'))
+ {
+ std::string str(brk_address_mem);
+ std::size_t separator = str.find("-");
+
+ if (separator != std::string::npos)
+ {
+ address1 = (u16)std::stoul(str.substr(0, separator), 0 , 16);
+ address2 = (u16)std::stoul(str.substr(separator + 1 , std::string::npos), 0, 16);
+ range = true;
+ }
+ }
+ else if (input_len == 4)
+ {
+ address1 = (u16)std::stoul(brk_address_mem, 0, 16);
+ }
+ else
+ {
+ return;
+ }
+ }
+ catch(const std::invalid_argument&)
+ {
+ return;
+ }
+
+ bool found = false;
+ std::vector<Memory::stMemoryBreakpoint>* breakpoints = emu_get_core()->GetMemory()->GetBreakpointsMem();
+
+ for (long unsigned int b = 0; b < breakpoints->size(); b++)
+ {
+ Memory::stMemoryBreakpoint temp = (*breakpoints)[b];
+ if ((temp.address1 == address1) && (temp.address2 == address2) && (temp.range == range))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ Memory::stMemoryBreakpoint new_breakpoint;
+ new_breakpoint.address1 = address1;
+ new_breakpoint.address2 = address2;
+ new_breakpoint.range = range;
+ new_breakpoint.read = brk_new_mem_read;
+ new_breakpoint.write = brk_new_mem_write;
+
+ breakpoints->push_back(new_breakpoint);
+ }
+
+ brk_address_mem[0] = 0;
+}
+
+static void request_goto_address(u16 address)
+{
+ goto_address_requested = true;
+ goto_address_target = address;
+}
+
+static ImVec4 color_565_to_float(u16 color)
+{
+ ImVec4 ret;
+ ret.w = 0;
+ ret.x = (1.0f / 31.0f) * ((color >> 11) & 0x1F);
+ ret.y = (1.0f / 63.0f) * ((color >> 5) & 0x3F);
+ ret.z = (1.0f / 31.0f) * (color & 0x1F);
+ return ret;
+}