43 const auto& theme = theme_manager.GetCurrentTheme();
46 ImGui::BeginChild(
"##CPUDebugger", ImVec2(0, 0),
true);
53 auto& cpu = emu->
snes().cpu();
57 ImGuiTreeNodeFlags_DefaultOpen)) {
58 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 6));
63 if (ImGui::IsItemHovered()) {
64 ImGui::SetTooltip(
"Execute single instruction (F10)");
69 ImVec2(120, kButtonHeight))) {
73 if (ImGui::IsItemHovered()) {
74 ImGui::SetTooltip(
"Run until next breakpoint (F5)");
84 ImGuiTreeNodeFlags_DefaultOpen)) {
85 if (ImGui::BeginTable(
"CPU_Registers", 4,
86 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
87 ImGui::TableSetupColumn(
"Reg", ImGuiTableColumnFlags_WidthFixed, 40.0f);
88 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthFixed, 70.0f);
89 ImGui::TableSetupColumn(
"Reg", ImGuiTableColumnFlags_WidthFixed, 40.0f);
90 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthFixed, 70.0f);
91 ImGui::TableHeadersRow();
94 ImGui::TableNextRow();
95 ImGui::TableNextColumn();
97 ImGui::TableNextColumn();
99 ImGui::TableNextColumn();
101 ImGui::TableNextColumn();
105 ImGui::TableNextRow();
106 ImGui::TableNextColumn();
108 ImGui::TableNextColumn();
110 ImGui::TableNextColumn();
112 ImGui::TableNextColumn();
116 ImGui::TableNextRow();
117 ImGui::TableNextColumn();
119 ImGui::TableNextColumn();
121 ImGui::TableNextColumn();
123 ImGui::TableNextColumn();
127 ImGui::TableNextRow();
128 ImGui::TableNextColumn();
130 ImGui::TableNextColumn();
132 ImGui::TableNextColumn();
134 ImGui::TableNextColumn();
147 auto RenderFlag = [&](
const char* name,
bool value) {
153 if (ImGui::IsItemHovered()) {
154 ImGui::SetTooltip(
"%s: %s", name, value ?
"Set" :
"Clear");
159 RenderFlag(
"N", cpu.GetNegativeFlag());
160 RenderFlag(
"V", cpu.GetOverflowFlag());
161 RenderFlag(
"D", cpu.GetDecimalFlag());
162 RenderFlag(
"I", cpu.GetInterruptFlag());
163 RenderFlag(
"Z", cpu.GetZeroFlag());
164 RenderFlag(
"C", cpu.GetCarryFlag());
174 static char bp_input[10] =
"";
176 ImGui::SetNextItemWidth(150);
177 if (ImGui::InputTextWithHint(
"##BP",
"Address (hex)", bp_input,
179 ImGuiInputTextFlags_CharsHexadecimal |
180 ImGuiInputTextFlags_EnterReturnsTrue)) {
181 if (strlen(bp_input) > 0) {
182 uint32_t addr = std::stoi(bp_input,
nullptr, 16);
184 memset(bp_input, 0,
sizeof(bp_input));
189 if (ImGui::Button(
ICON_MD_ADD " Add", ImVec2(80, 0))) {
190 if (strlen(bp_input) > 0) {
191 uint32_t addr = std::stoi(bp_input,
nullptr, 16);
193 memset(bp_input, 0,
sizeof(bp_input));
206 if (!breakpoints.empty()) {
207 ImGui::BeginChild(
"##BPList", ImVec2(0, 150),
true);
208 for (
size_t i = 0; i < breakpoints.size(); ++i) {
209 uint32_t bp = breakpoints[i];
215 ImGui::SameLine(200);
217 cpu.ClearBreakpoint(bp);
225 "No breakpoints set");
230 ImGui::PopStyleColor();
238 const auto& theme = theme_manager.GetCurrentTheme();
241 ImGui::BeginChild(
"##BreakpointList", ImVec2(0, 0),
true);
250 ImGui::Text(
"Active Breakpoints: %zu", breakpoints.size());
253 if (!breakpoints.empty()) {
254 if (ImGui::BeginTable(
"BreakpointTable", 3,
255 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
256 ImGui::TableSetupColumn(
ICON_MD_TAG, ImGuiTableColumnFlags_WidthFixed,
258 ImGui::TableSetupColumn(
"Address", ImGuiTableColumnFlags_WidthFixed, 100);
259 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthStretch);
260 ImGui::TableHeadersRow();
262 for (
size_t i = 0; i < breakpoints.size(); ++i) {
263 ImGui::TableNextRow();
264 ImGui::TableNextColumn();
267 ImGui::TableNextColumn();
271 ImGui::TableNextColumn();
274 emu->
snes().cpu().ClearBreakpoint(breakpoints[i]);
287 ImGui::PopStyleColor();
369 const auto& theme = theme_manager.GetCurrentTheme();
372 ImGui::BeginChild(
"##ApuDebugger", ImVec2(0, 0),
true);
379 auto& tracker = emu->
snes().apu_handshake_tracker();
383 ImGuiTreeNodeFlags_DefaultOpen)) {
385 auto phase_str = tracker.GetPhaseString();
387 const char* phase_icon;
389 if (phase_str ==
"RUNNING") {
392 }
else if (phase_str ==
"TRANSFER_ACTIVE") {
395 }
else if (phase_str ==
"WAITING_BBAA" || phase_str ==
"IPL_BOOT") {
405 ImGui::TextColored(phase_color,
"%s %s", phase_icon, phase_str.c_str());
410 if (tracker.IsHandshakeComplete()) {
419 if (tracker.IsTransferActive() || tracker.GetBytesTransferred() > 0) {
423 ImGui::BulletText(
"Bytes: %d", tracker.GetBytesTransferred());
424 ImGui::BulletText(
"Blocks: %d", tracker.GetBlockCount());
426 auto progress = tracker.GetTransferProgress();
427 if (!progress.empty()) {
436 ImGui::TextWrapped(
"%s", tracker.GetStatusSummary().c_str());
440 if (ImGui::CollapsingHeader(
ICON_MD_LIST " Port Activity Log",
441 ImGuiTreeNodeFlags_DefaultOpen)) {
442 ImGui::BeginChild(
"##PortLog", ImVec2(0, 200),
true);
444 const auto& history = tracker.GetPortHistory();
446 if (history.empty()) {
451 int start_idx = std::max(0,
static_cast<int>(history.size()) - 50);
452 for (
size_t i = start_idx; i < history.size(); ++i) {
453 const auto& entry = history[i];
460 ImGui::TextColored(color,
"[%04llu] %s %s F%d = $%02X @ PC=$%04X %s",
461 entry.timestamp, entry.is_cpu ?
"CPU" :
"SPC", icon,
462 entry.port + 4, entry.value, entry.pc,
463 entry.description.c_str());
467 if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
468 ImGui::SetScrollHereY(1.0f);
477 " Current Port Values",
478 ImGuiTreeNodeFlags_DefaultOpen)) {
479 if (ImGui::BeginTable(
"APU_Ports", 4,
480 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
481 ImGui::TableSetupColumn(
"Port", ImGuiTableColumnFlags_WidthFixed, 50);
482 ImGui::TableSetupColumn(
"CPU → SPC", ImGuiTableColumnFlags_WidthFixed,
484 ImGui::TableSetupColumn(
"SPC → CPU", ImGuiTableColumnFlags_WidthFixed,
486 ImGui::TableSetupColumn(
"Address", ImGuiTableColumnFlags_WidthStretch);
487 ImGui::TableHeadersRow();
489 for (
int i = 0; i < 4; ++i) {
490 ImGui::TableNextRow();
491 ImGui::TableNextColumn();
494 ImGui::TableNextColumn();
496 emu->
snes().apu().in_ports_[i]);
498 ImGui::TableNextColumn();
500 emu->
snes().apu().out_ports_[i]);
502 ImGui::TableNextColumn();
503 ImGui::TextDisabled(
"$214%d / $F%d", i, i + 4);
512 ImGuiTreeNodeFlags_DefaultOpen)) {
519 ImVec2(-1, kLargeButtonHeight))) {
520 LOG_INFO(
"APU_DEBUG",
"=== MANUAL HANDSHAKE TEST ===");
521 emu->
snes().Write(0x002140, 0xCC);
522 emu->
snes().Write(0x002141, 0x01);
523 emu->
snes().Write(0x002142, 0x00);
524 emu->
snes().Write(0x002143, 0x02);
525 LOG_INFO(
"APU_DEBUG",
"Handshake sequence executed");
527 if (ImGui::IsItemHovered()) {
529 "Execute full handshake sequence:\n"
530 "$CC → F4, $01 → F5, $00 → F6, $02 → F7");
536 if (ImGui::TreeNode(
ICON_MD_EDIT " Manual Port Writes")) {
537 static uint8_t port_values[4] = {0xCC, 0x01, 0x00, 0x02};
539 for (
int i = 0; i < 4; ++i) {
541 ImGui::Text(
"F%d ($214%d):", i + 4, i);
543 ImGui::SetNextItemWidth(80);
544 ImGui::InputScalar(
"##val", ImGuiDataType_U8, &port_values[i], NULL,
545 NULL,
"%02X", ImGuiInputTextFlags_CharsHexadecimal);
547 if (ImGui::Button(
ICON_MD_SEND " Write", ImVec2(100, 0))) {
548 emu->
snes().Write(0x002140 + i, port_values[i]);
549 LOG_INFO(
"APU_DEBUG",
"Wrote $%02X to F%d", port_values[i], i + 4);
561 ImVec2(-1, kButtonHeight))) {
562 emu->
snes().apu().Reset();
567 ImVec2(-1, kButtonHeight))) {
569 LOG_INFO(
"APU_DEBUG",
"Port history cleared");
574 ImGui::Text(
"Audio Resampling");
577 const char* items[] = {
"Linear",
"Hermite",
"Cosine",
"Cubic"};
579 static_cast<int>(emu->
snes().apu().dsp().interpolation_type);
580 if (ImGui::Combo(
"Interpolation", ¤t_item, items,
581 IM_ARRAYSIZE(items))) {
582 emu->
snes().apu().dsp().interpolation_type =
587 ImGui::PopStyleColor();
595 const auto& theme = theme_manager.GetCurrentTheme();
598 ImGui::BeginChild(
"##AIAgent", ImVec2(0, 0),
true);
609 ImGui::Text(
"Status:");
611 ImGui::TextColored(status_color,
"%s %s",
613 agent_ready ?
"Ready" :
"Not Ready");
619 ImGuiTreeNodeFlags_DefaultOpen)) {
622 ImGui::BulletText(
"FPS: %.2f", metrics.fps);
623 ImGui::BulletText(
"Cycles: %llu", metrics.cycles);
624 ImGui::BulletText(
"CPU PC: $%02X:%04X", metrics.cpu_pb, metrics.cpu_pc);
625 ImGui::BulletText(
"Audio Queued: %u frames", metrics.audio_frames_queued);
626 ImGui::BulletText(
"Running: %s", metrics.is_running ?
"YES" :
"NO");
632 ImVec2(-1, kLargeButtonHeight))) {
636 if (ImGui::Button(
ICON_MD_STOP " Stop Agent", ImVec2(-1, kButtonHeight))) {
642 ImGui::PopStyleColor();