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.
This commit is contained in:
scawful
2025-10-06 15:21:48 -04:00
parent 673201e4fd
commit a09d7d10c8
3 changed files with 47 additions and 15 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}