44 const auto& theme = theme_manager.GetCurrentTheme();
55 auto& cpu = emu->
snes().cpu();
59 ImGuiTreeNodeFlags_DefaultOpen)) {
65 if (ImGui::IsItemHovered()) {
66 ImGui::SetTooltip(
"Execute single instruction (F10)");
71 ImVec2(120, kButtonHeight))) {
75 if (ImGui::IsItemHovered()) {
76 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");
236 const auto& theme = theme_manager.GetCurrentTheme();
248 ImGui::Text(
"Active Breakpoints: %zu", breakpoints.size());
251 if (!breakpoints.empty()) {
252 if (ImGui::BeginTable(
"BreakpointTable", 3,
253 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
254 ImGui::TableSetupColumn(
ICON_MD_TAG, ImGuiTableColumnFlags_WidthFixed,
256 ImGui::TableSetupColumn(
"Address", ImGuiTableColumnFlags_WidthFixed, 100);
257 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthStretch);
258 ImGui::TableHeadersRow();
260 for (
size_t i = 0; i < breakpoints.size(); ++i) {
261 ImGui::TableNextRow();
262 ImGui::TableNextColumn();
265 ImGui::TableNextColumn();
269 ImGui::TableNextColumn();
272 emu->
snes().cpu().ClearBreakpoint(breakpoints[i]);
363 const auto& theme = theme_manager.GetCurrentTheme();
374 auto& tracker = emu->
snes().apu_handshake_tracker();
378 ImGuiTreeNodeFlags_DefaultOpen)) {
380 auto phase_str = tracker.GetPhaseString();
382 const char* phase_icon;
384 if (phase_str ==
"RUNNING") {
387 }
else if (phase_str ==
"TRANSFER_ACTIVE") {
390 }
else if (phase_str ==
"WAITING_BBAA" || phase_str ==
"IPL_BOOT") {
400 ImGui::TextColored(phase_color,
"%s %s", phase_icon, phase_str.c_str());
405 if (tracker.IsHandshakeComplete()) {
414 if (tracker.IsTransferActive() || tracker.GetBytesTransferred() > 0) {
418 ImGui::BulletText(
"Bytes: %d", tracker.GetBytesTransferred());
419 ImGui::BulletText(
"Blocks: %d", tracker.GetBlockCount());
421 auto progress = tracker.GetTransferProgress();
422 if (!progress.empty()) {
431 ImGui::TextWrapped(
"%s", tracker.GetStatusSummary().c_str());
435 if (ImGui::CollapsingHeader(
ICON_MD_LIST " Port Activity Log",
436 ImGuiTreeNodeFlags_DefaultOpen)) {
437 ImGui::BeginChild(
"##PortLog", ImVec2(0, 200),
true);
439 const auto& history = tracker.GetPortHistory();
441 if (history.empty()) {
446 int start_idx = std::max(0,
static_cast<int>(history.size()) - 50);
447 for (
size_t i = start_idx; i < history.size(); ++i) {
448 const auto& entry = history[i];
455 ImGui::TextColored(color,
"[%04llu] %s %s F%d = $%02X @ PC=$%04X %s",
456 entry.timestamp, entry.is_cpu ?
"CPU" :
"SPC", icon,
457 entry.port + 4, entry.value, entry.pc,
458 entry.description.c_str());
462 if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
463 ImGui::SetScrollHereY(1.0f);
472 " Current Port Values",
473 ImGuiTreeNodeFlags_DefaultOpen)) {
474 if (ImGui::BeginTable(
"APU_Ports", 4,
475 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
476 ImGui::TableSetupColumn(
"Port", ImGuiTableColumnFlags_WidthFixed, 50);
477 ImGui::TableSetupColumn(
"CPU → SPC", ImGuiTableColumnFlags_WidthFixed,
479 ImGui::TableSetupColumn(
"SPC → CPU", ImGuiTableColumnFlags_WidthFixed,
481 ImGui::TableSetupColumn(
"Address", ImGuiTableColumnFlags_WidthStretch);
482 ImGui::TableHeadersRow();
484 for (
int i = 0; i < 4; ++i) {
485 ImGui::TableNextRow();
486 ImGui::TableNextColumn();
489 ImGui::TableNextColumn();
491 emu->
snes().apu().in_ports_[i]);
493 ImGui::TableNextColumn();
495 emu->
snes().apu().out_ports_[i]);
497 ImGui::TableNextColumn();
498 ImGui::TextDisabled(
"$214%d / $F%d", i, i + 4);
507 ImGuiTreeNodeFlags_DefaultOpen)) {
514 ImVec2(-1, kLargeButtonHeight))) {
515 LOG_INFO(
"APU_DEBUG",
"=== MANUAL HANDSHAKE TEST ===");
516 emu->
snes().Write(0x002140, 0xCC);
517 emu->
snes().Write(0x002141, 0x01);
518 emu->
snes().Write(0x002142, 0x00);
519 emu->
snes().Write(0x002143, 0x02);
520 LOG_INFO(
"APU_DEBUG",
"Handshake sequence executed");
522 if (ImGui::IsItemHovered()) {
524 "Execute full handshake sequence:\n"
525 "$CC → F4, $01 → F5, $00 → F6, $02 → F7");
531 if (ImGui::TreeNode(
ICON_MD_EDIT " Manual Port Writes")) {
532 static uint8_t port_values[4] = {0xCC, 0x01, 0x00, 0x02};
534 for (
int i = 0; i < 4; ++i) {
536 ImGui::Text(
"F%d ($214%d):", i + 4, i);
538 ImGui::SetNextItemWidth(80);
539 ImGui::InputScalar(
"##val", ImGuiDataType_U8, &port_values[i], NULL,
540 NULL,
"%02X", ImGuiInputTextFlags_CharsHexadecimal);
542 if (ImGui::Button(
ICON_MD_SEND " Write", ImVec2(100, 0))) {
543 emu->
snes().Write(0x002140 + i, port_values[i]);
544 LOG_INFO(
"APU_DEBUG",
"Wrote $%02X to F%d", port_values[i], i + 4);
556 ImVec2(-1, kButtonHeight))) {
557 emu->
snes().apu().Reset();
562 ImVec2(-1, kButtonHeight))) {
564 LOG_INFO(
"APU_DEBUG",
"Port history cleared");
569 ImGui::Text(
"Audio Resampling");
572 const char* items[] = {
"Linear",
"Hermite",
"Cosine",
"Cubic"};
574 static_cast<int>(emu->
snes().apu().dsp().interpolation_type);
575 if (ImGui::Combo(
"Interpolation", ¤t_item, items,
576 IM_ARRAYSIZE(items))) {
577 emu->
snes().apu().dsp().interpolation_type =
588 const auto& theme = theme_manager.GetCurrentTheme();
602 ImGui::Text(
"Status:");
604 ImGui::TextColored(status_color,
"%s %s",
606 agent_ready ?
"Ready" :
"Not Ready");
612 ImGuiTreeNodeFlags_DefaultOpen)) {
615 ImGui::BulletText(
"FPS: %.2f", metrics.fps);
616 ImGui::BulletText(
"Cycles: %llu", metrics.cycles);
617 ImGui::BulletText(
"CPU PC: $%02X:%04X", metrics.cpu_pb, metrics.cpu_pc);
618 ImGui::BulletText(
"Audio Queued: %u frames", metrics.audio_frames_queued);
619 ImGui::BulletText(
"Running: %s", metrics.is_running ?
"YES" :
"NO");
625 ImVec2(-1, kLargeButtonHeight))) {
629 if (ImGui::Button(
ICON_MD_STOP " Stop Agent", ImVec2(-1, kButtonHeight))) {