35 const auto& theme = theme_manager.GetCurrentTheme();
38 ImGui::BeginChild(
"##CPUDebugger", ImVec2(0, 0),
true);
45 auto& cpu = emu->
snes().cpu();
48 if (ImGui::CollapsingHeader(
ICON_MD_SETTINGS " Controls", ImGuiTreeNodeFlags_DefaultOpen)) {
49 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 6));
54 if (ImGui::IsItemHovered()) {
55 ImGui::SetTooltip(
"Execute single instruction (F10)");
63 if (ImGui::IsItemHovered()) {
64 ImGui::SetTooltip(
"Run until next breakpoint (F5)");
73 if (ImGui::CollapsingHeader(
ICON_MD_MEMORY " Registers", ImGuiTreeNodeFlags_DefaultOpen)) {
74 if (ImGui::BeginTable(
"CPU_Registers", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
75 ImGui::TableSetupColumn(
"Reg", ImGuiTableColumnFlags_WidthFixed, 40.0f);
76 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthFixed, 70.0f);
77 ImGui::TableSetupColumn(
"Reg", ImGuiTableColumnFlags_WidthFixed, 40.0f);
78 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthFixed, 70.0f);
79 ImGui::TableHeadersRow();
82 ImGui::TableNextRow();
83 ImGui::TableNextColumn();
85 ImGui::TableNextColumn();
87 ImGui::TableNextColumn();
89 ImGui::TableNextColumn();
93 ImGui::TableNextRow();
94 ImGui::TableNextColumn();
96 ImGui::TableNextColumn();
98 ImGui::TableNextColumn();
100 ImGui::TableNextColumn();
104 ImGui::TableNextRow();
105 ImGui::TableNextColumn();
107 ImGui::TableNextColumn();
109 ImGui::TableNextColumn();
111 ImGui::TableNextColumn();
115 ImGui::TableNextRow();
116 ImGui::TableNextColumn();
118 ImGui::TableNextColumn();
120 ImGui::TableNextColumn();
122 ImGui::TableNextColumn();
134 auto RenderFlag = [&](
const char* name,
bool value) {
138 if (ImGui::IsItemHovered()) {
139 ImGui::SetTooltip(
"%s: %s", name, value ?
"Set" :
"Clear");
144 RenderFlag(
"N", cpu.GetNegativeFlag());
145 RenderFlag(
"V", cpu.GetOverflowFlag());
146 RenderFlag(
"D", cpu.GetDecimalFlag());
147 RenderFlag(
"I", cpu.GetInterruptFlag());
148 RenderFlag(
"Z", cpu.GetZeroFlag());
149 RenderFlag(
"C", cpu.GetCarryFlag());
159 static char bp_input[10] =
"";
161 ImGui::SetNextItemWidth(150);
162 if (ImGui::InputTextWithHint(
"##BP",
"Address (hex)", bp_input,
sizeof(bp_input),
163 ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
164 if (strlen(bp_input) > 0) {
165 uint32_t addr = std::stoi(bp_input,
nullptr, 16);
167 memset(bp_input, 0,
sizeof(bp_input));
172 if (ImGui::Button(
ICON_MD_ADD " Add", ImVec2(80, 0))) {
173 if (strlen(bp_input) > 0) {
174 uint32_t addr = std::stoi(bp_input,
nullptr, 16);
176 memset(bp_input, 0,
sizeof(bp_input));
189 if (!breakpoints.empty()) {
190 ImGui::BeginChild(
"##BPList", ImVec2(0, 150),
true);
191 for (
size_t i = 0; i < breakpoints.size(); ++i) {
192 uint32_t bp = breakpoints[i];
198 ImGui::SameLine(200);
200 cpu.ClearBreakpoint(bp);
208 "No breakpoints set");
213 ImGui::PopStyleColor();
220 const auto& theme = theme_manager.GetCurrentTheme();
223 ImGui::BeginChild(
"##BreakpointList", ImVec2(0, 0),
true);
232 ImGui::Text(
"Active Breakpoints: %zu", breakpoints.size());
235 if (!breakpoints.empty()) {
236 if (ImGui::BeginTable(
"BreakpointTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
237 ImGui::TableSetupColumn(
ICON_MD_TAG, ImGuiTableColumnFlags_WidthFixed, 40);
238 ImGui::TableSetupColumn(
"Address", ImGuiTableColumnFlags_WidthFixed, 100);
239 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthStretch);
240 ImGui::TableHeadersRow();
242 for (
size_t i = 0; i < breakpoints.size(); ++i) {
243 ImGui::TableNextRow();
244 ImGui::TableNextColumn();
247 ImGui::TableNextColumn();
250 ImGui::TableNextColumn();
253 emu->
snes().cpu().ClearBreakpoint(breakpoints[i]);
266 ImGui::PopStyleColor();
340 const auto& theme = theme_manager.GetCurrentTheme();
343 ImGui::BeginChild(
"##ApuDebugger", ImVec2(0, 0),
true);
350 auto& tracker = emu->
snes().apu_handshake_tracker();
353 if (ImGui::CollapsingHeader(
ICON_MD_HANDSHAKE " Handshake Status", ImGuiTreeNodeFlags_DefaultOpen)) {
355 auto phase_str = tracker.GetPhaseString();
357 const char* phase_icon;
359 if (phase_str ==
"RUNNING") {
362 }
else if (phase_str ==
"TRANSFER_ACTIVE") {
365 }
else if (phase_str ==
"WAITING_BBAA" || phase_str ==
"IPL_BOOT") {
375 ImGui::TextColored(phase_color,
"%s %s", phase_icon, phase_str.c_str());
380 if (tracker.IsHandshakeComplete()) {
389 if (tracker.IsTransferActive() || tracker.GetBytesTransferred() > 0) {
393 ImGui::BulletText(
"Bytes: %d", tracker.GetBytesTransferred());
394 ImGui::BulletText(
"Blocks: %d", tracker.GetBlockCount());
396 auto progress = tracker.GetTransferProgress();
397 if (!progress.empty()) {
405 ImGui::TextWrapped(
"%s", tracker.GetStatusSummary().c_str());
409 if (ImGui::CollapsingHeader(
ICON_MD_LIST " Port Activity Log", ImGuiTreeNodeFlags_DefaultOpen)) {
410 ImGui::BeginChild(
"##PortLog", ImVec2(0, 200),
true);
412 const auto& history = tracker.GetPortHistory();
414 if (history.empty()) {
419 int start_idx = std::max(0,
static_cast<int>(history.size()) - 50);
420 for (
size_t i = start_idx; i < history.size(); ++i) {
421 const auto& entry = history[i];
427 ImGui::TextColored(color,
"[%04llu] %s %s F%d = $%02X @ PC=$%04X %s",
429 entry.is_cpu ?
"CPU" :
"SPC",
434 entry.description.c_str());
438 if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
439 ImGui::SetScrollHereY(1.0f);
448 ImGuiTreeNodeFlags_DefaultOpen)) {
449 if (ImGui::BeginTable(
"APU_Ports", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
450 ImGui::TableSetupColumn(
"Port", ImGuiTableColumnFlags_WidthFixed, 50);
451 ImGui::TableSetupColumn(
"CPU → SPC", ImGuiTableColumnFlags_WidthFixed, 80);
452 ImGui::TableSetupColumn(
"SPC → CPU", ImGuiTableColumnFlags_WidthFixed, 80);
453 ImGui::TableSetupColumn(
"Address", ImGuiTableColumnFlags_WidthStretch);
454 ImGui::TableHeadersRow();
456 for (
int i = 0; i < 4; ++i) {
457 ImGui::TableNextRow();
458 ImGui::TableNextColumn();
461 ImGui::TableNextColumn();
463 "$%02X", emu->
snes().apu().in_ports_[i]);
465 ImGui::TableNextColumn();
467 "$%02X", emu->
snes().apu().out_ports_[i]);
469 ImGui::TableNextColumn();
470 ImGui::TextDisabled(
"$214%d / $F%d", i, i + 4);
478 if (ImGui::CollapsingHeader(
ICON_MD_BUILD " Quick Actions", ImGuiTreeNodeFlags_DefaultOpen)) {
484 if (ImGui::Button(
ICON_MD_PLAY_CIRCLE " Full Handshake Test", ImVec2(-1, kLargeButtonHeight))) {
485 LOG_INFO(
"APU_DEBUG",
"=== MANUAL HANDSHAKE TEST ===");
486 emu->
snes().Write(0x002140, 0xCC);
487 emu->
snes().Write(0x002141, 0x01);
488 emu->
snes().Write(0x002142, 0x00);
489 emu->
snes().Write(0x002143, 0x02);
490 LOG_INFO(
"APU_DEBUG",
"Handshake sequence executed");
492 if (ImGui::IsItemHovered()) {
493 ImGui::SetTooltip(
"Execute full handshake sequence:\n"
494 "$CC → F4, $01 → F5, $00 → F6, $02 → F7");
500 if (ImGui::TreeNode(
ICON_MD_EDIT " Manual Port Writes")) {
501 static uint8_t port_values[4] = {0xCC, 0x01, 0x00, 0x02};
503 for (
int i = 0; i < 4; ++i) {
505 ImGui::Text(
"F%d ($214%d):", i + 4, i);
507 ImGui::SetNextItemWidth(80);
508 ImGui::InputScalar(
"##val", ImGuiDataType_U8, &port_values[i], NULL, NULL,
"%02X",
509 ImGuiInputTextFlags_CharsHexadecimal);
511 if (ImGui::Button(
ICON_MD_SEND " Write", ImVec2(100, 0))) {
512 emu->
snes().Write(0x002140 + i, port_values[i]);
513 LOG_INFO(
"APU_DEBUG",
"Wrote $%02X to F%d", port_values[i], i + 4);
525 emu->
snes().apu().Reset();
529 if (ImGui::Button(
ICON_MD_CLEAR_ALL " Clear Port History", ImVec2(-1, kButtonHeight))) {
531 LOG_INFO(
"APU_DEBUG",
"Port history cleared");
536 ImGui::Text(
"Audio Resampling");
539 const char* items[] = {
"Linear",
"Hermite",
"Cosine",
"Cubic"};
540 int current_item =
static_cast<int>(emu->
snes().apu().dsp().interpolation_type);
541 if (ImGui::Combo(
"Interpolation", ¤t_item, items, IM_ARRAYSIZE(items))) {
542 emu->
snes().apu().dsp().interpolation_type =
547 ImGui::PopStyleColor();
554 const auto& theme = theme_manager.GetCurrentTheme();
557 ImGui::BeginChild(
"##AIAgent", ImVec2(0, 0),
true);
568 ImGui::Text(
"Status:");
570 ImGui::TextColored(status_color,
"%s %s",
572 agent_ready ?
"Ready" :
"Not Ready");
577 if (ImGui::CollapsingHeader(
ICON_MD_DATA_OBJECT " Metrics", ImGuiTreeNodeFlags_DefaultOpen)) {
580 ImGui::BulletText(
"FPS: %.2f", metrics.fps);
581 ImGui::BulletText(
"Cycles: %llu", metrics.cycles);
582 ImGui::BulletText(
"CPU PC: $%02X:%04X", metrics.cpu_pb, metrics.cpu_pc);
583 ImGui::BulletText(
"Audio Queued: %u frames", metrics.audio_frames_queued);
584 ImGui::BulletText(
"Running: %s", metrics.is_running ?
"YES" :
"NO");
589 if (ImGui::Button(
ICON_MD_PLAY_ARROW " Start Agent Session", ImVec2(-1, kLargeButtonHeight))) {
593 if (ImGui::Button(
ICON_MD_STOP " Stop Agent", ImVec2(-1, kButtonHeight))) {
599 ImGui::PopStyleColor();