From a09d7d10c89115fb3a14a9c28769c34e945fdd8d Mon Sep 17 00:00:00 2001 From: scawful Date: Mon, 6 Oct 2025 15:21:48 -0400 Subject: [PATCH] feat: Implement Deadlock Detection and Improve Emulator Shutdown Logging - Added deadlock detection in the emulator's main loop to identify when the CPU is stuck, enhancing debugging capabilities. - Updated logging during emulator shutdown to provide clearer status messages, including final CPU state and resource cleanup. - Refactored audio and texture cleanup processes to ensure proper resource management during shutdown. --- src/app/emu/audio/apu.cc | 11 +++++----- src/app/emu/audio/spc700.cc | 9 +++++--- src/app/emu/emu.cc | 42 +++++++++++++++++++++++++++++++------ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/app/emu/audio/apu.cc b/src/app/emu/audio/apu.cc index 2d9d2ee8..e28bd7c3 100644 --- a/src/app/emu/audio/apu.cc +++ b/src/app/emu/audio/apu.cc @@ -16,16 +16,15 @@ namespace emu { static const double apuCyclesPerMaster = (32040 * 32) / (1364 * 262 * 60.0); static const double apuCyclesPerMasterPal = (32040 * 32) / (1364 * 312 * 50.0); -// Standard SNES IPL ROM (64 bytes at $FFC0-$FFFF) - Hardware verified -// Counter acknowledgments working - tested with ALTTP -// Source: Verified against bsnes, Mesen, anomie docs, SnesLab protocol +// SNES IPL ROM with counter acknowledgments - WORKING VERSION +// Counter echo at $FFE6 proven functional with ALTTP static const uint8_t bootRom[0x40] = { 0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa, 0xf4, 0x8f, 0xbb, 0xf5, 0xe4, 0xf4, 0x68, 0xcc, 0xd0, 0xfa, 0x2f, 0x19, 0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5, - 0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xcb, 0xf4, 0xd0, 0xf1, 0xab, 0x01, - 0x10, 0xed, 0x7e, 0xf4, 0xba, 0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, - 0xf4, 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0xc0, 0xff}; + 0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xcb, 0xf4, 0xd0, 0xf3, 0xab, 0x01, + 0x10, 0xef, 0x7e, 0xf4, 0x10, 0xeb, 0xba, 0xf6, 0xda, 0x00, 0xba, + 0xf4, 0xc4, 0xf4, 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0xc0, 0xff}; // Helper to reset the cycle tracking on emulator reset static uint64_t g_last_master_cycles = 0; diff --git a/src/app/emu/audio/spc700.cc b/src/app/emu/audio/spc700.cc index e0db6bd2..6a8c2440 100644 --- a/src/app/emu/audio/spc700.cc +++ b/src/app/emu/audio/spc700.cc @@ -1106,11 +1106,14 @@ void Spc700::ExecuteInstructions(uint8_t opcode) { break; } case 0xcb: { // movsy dp - uint16_t addr = dp(); - if (addr == 0x00F4) { + // CRITICAL: Only call dp() once in bstep=0, reuse saved address in bstep=1 + if (bstep == 0) { + adr = dp(); // Save address for bstep=1 + } + if (adr == 0x00F4 && bstep == 1) { LOG_INFO("SPC", "MOVSY writing Y=$%02X to F4 at PC=$%04X", Y, PC); } - MOVSY(addr); + MOVSY(adr); // Use saved address break; } case 0xcc: { // movsy abs diff --git a/src/app/emu/emu.cc b/src/app/emu/emu.cc index b8a76d85..1060937f 100644 --- a/src/app/emu/emu.cc +++ b/src/app/emu/emu.cc @@ -207,6 +207,22 @@ int main(int argc, char **argv) { snes_.RunFrame(); frame_count++; + // Detect deadlock - CPU stuck in same location + static uint16_t last_cpu_pc = 0; + static int stuck_count = 0; + uint16_t current_cpu_pc = snes_.cpu().PC; + + if (current_cpu_pc == last_cpu_pc && current_cpu_pc >= 0x88B0 && current_cpu_pc <= 0x88C0) { + stuck_count++; + if (stuck_count > 180 && frame_count % 60 == 0) { + printf("[WARNING] CPU stuck at $%02X:%04X for %d frames (APU deadlock?)\n", + snes_.cpu().PB, current_cpu_pc, stuck_count); + } + } else { + stuck_count = 0; + } + last_cpu_pc = current_cpu_pc; + // Print status every 60 frames (1 second) if (frame_count % 60 == 0) { printf("[Frame %d] CPU=$%02X:%04X SPC=$%04X APU_cycles=%llu\n", @@ -216,10 +232,11 @@ int main(int argc, char **argv) { // Auto-exit after max_frames (if set) if (max_frames > 0 && frame_count >= max_frames) { - printf("\nReached max frames (%d), exiting...\n", max_frames); - printf("Final state: CPU=$%02X:%04X SPC=$%04X\n", + printf("\n[EMULATOR] Reached max frames (%d), shutting down...\n", max_frames); + printf("[EMULATOR] Final state: CPU=$%02X:%04X SPC=$%04X\n", snes_.cpu().PB, snes_.cpu().PC, snes_.apu().spc700().PC); running = false; + break; // Exit inner loop immediately } snes_.SetSamples(audio_buffer_, wanted_samples_); @@ -244,13 +261,26 @@ int main(int argc, char **argv) { SDL_RenderPresent(renderer_.get()); // should vsync } + printf("[EMULATOR] Cleaning up SDL resources...\n"); + + // Clean up audio SDL_PauseAudioDevice(audio_device_, 1); + SDL_ClearQueuedAudio(audio_device_); SDL_CloseAudioDevice(audio_device_); delete[] audio_buffer_; - // ImGui_ImplSDLRenderer2_Shutdown(); - // ImGui_ImplSDL2_Shutdown(); - // ImGui::DestroyContext(); + + // Clean up texture + if (ppu_texture_) { + SDL_DestroyTexture(ppu_texture_); + } + + // Clean up renderer and window (done by unique_ptr destructors) + renderer_.reset(); + window_.reset(); + + // Quit SDL SDL_Quit(); - + + printf("[EMULATOR] Shutdown complete.\n"); return EXIT_SUCCESS; }