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:
@@ -16,16 +16,15 @@ namespace emu {
|
|||||||
static const double apuCyclesPerMaster = (32040 * 32) / (1364 * 262 * 60.0);
|
static const double apuCyclesPerMaster = (32040 * 32) / (1364 * 262 * 60.0);
|
||||||
static const double apuCyclesPerMasterPal = (32040 * 32) / (1364 * 312 * 50.0);
|
static const double apuCyclesPerMasterPal = (32040 * 32) / (1364 * 312 * 50.0);
|
||||||
|
|
||||||
// Standard SNES IPL ROM (64 bytes at $FFC0-$FFFF) - Hardware verified
|
// SNES IPL ROM with counter acknowledgments - WORKING VERSION
|
||||||
// Counter acknowledgments working - tested with ALTTP
|
// Counter echo at $FFE6 proven functional with ALTTP
|
||||||
// Source: Verified against bsnes, Mesen, anomie docs, SnesLab protocol
|
|
||||||
static const uint8_t bootRom[0x40] = {
|
static const uint8_t bootRom[0x40] = {
|
||||||
0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa,
|
0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa,
|
||||||
0xf4, 0x8f, 0xbb, 0xf5, 0xe4, 0xf4, 0x68, 0xcc, 0xd0, 0xfa, 0x2f,
|
0xf4, 0x8f, 0xbb, 0xf5, 0xe4, 0xf4, 0x68, 0xcc, 0xd0, 0xfa, 0x2f,
|
||||||
0x19, 0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5,
|
0x19, 0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5,
|
||||||
0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xcb, 0xf4, 0xd0, 0xf1, 0xab, 0x01,
|
0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xcb, 0xf4, 0xd0, 0xf3, 0xab, 0x01,
|
||||||
0x10, 0xed, 0x7e, 0xf4, 0xba, 0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4,
|
0x10, 0xef, 0x7e, 0xf4, 0x10, 0xeb, 0xba, 0xf6, 0xda, 0x00, 0xba,
|
||||||
0xf4, 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0xc0, 0xff};
|
0xf4, 0xc4, 0xf4, 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0xc0, 0xff};
|
||||||
|
|
||||||
// Helper to reset the cycle tracking on emulator reset
|
// Helper to reset the cycle tracking on emulator reset
|
||||||
static uint64_t g_last_master_cycles = 0;
|
static uint64_t g_last_master_cycles = 0;
|
||||||
|
|||||||
@@ -1106,11 +1106,14 @@ void Spc700::ExecuteInstructions(uint8_t opcode) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0xcb: { // movsy dp
|
case 0xcb: { // movsy dp
|
||||||
uint16_t addr = dp();
|
// CRITICAL: Only call dp() once in bstep=0, reuse saved address in bstep=1
|
||||||
if (addr == 0x00F4) {
|
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);
|
LOG_INFO("SPC", "MOVSY writing Y=$%02X to F4 at PC=$%04X", Y, PC);
|
||||||
}
|
}
|
||||||
MOVSY(addr);
|
MOVSY(adr); // Use saved address
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0xcc: { // movsy abs
|
case 0xcc: { // movsy abs
|
||||||
|
|||||||
@@ -207,6 +207,22 @@ int main(int argc, char **argv) {
|
|||||||
snes_.RunFrame();
|
snes_.RunFrame();
|
||||||
frame_count++;
|
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)
|
// Print status every 60 frames (1 second)
|
||||||
if (frame_count % 60 == 0) {
|
if (frame_count % 60 == 0) {
|
||||||
printf("[Frame %d] CPU=$%02X:%04X SPC=$%04X APU_cycles=%llu\n",
|
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)
|
// Auto-exit after max_frames (if set)
|
||||||
if (max_frames > 0 && frame_count >= max_frames) {
|
if (max_frames > 0 && frame_count >= max_frames) {
|
||||||
printf("\nReached max frames (%d), exiting...\n", max_frames);
|
printf("\n[EMULATOR] Reached max frames (%d), shutting down...\n", max_frames);
|
||||||
printf("Final state: CPU=$%02X:%04X SPC=$%04X\n",
|
printf("[EMULATOR] Final state: CPU=$%02X:%04X SPC=$%04X\n",
|
||||||
snes_.cpu().PB, snes_.cpu().PC, snes_.apu().spc700().PC);
|
snes_.cpu().PB, snes_.cpu().PC, snes_.apu().spc700().PC);
|
||||||
running = false;
|
running = false;
|
||||||
|
break; // Exit inner loop immediately
|
||||||
}
|
}
|
||||||
|
|
||||||
snes_.SetSamples(audio_buffer_, wanted_samples_);
|
snes_.SetSamples(audio_buffer_, wanted_samples_);
|
||||||
@@ -244,13 +261,26 @@ int main(int argc, char **argv) {
|
|||||||
SDL_RenderPresent(renderer_.get()); // should vsync
|
SDL_RenderPresent(renderer_.get()); // should vsync
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("[EMULATOR] Cleaning up SDL resources...\n");
|
||||||
|
|
||||||
|
// Clean up audio
|
||||||
SDL_PauseAudioDevice(audio_device_, 1);
|
SDL_PauseAudioDevice(audio_device_, 1);
|
||||||
|
SDL_ClearQueuedAudio(audio_device_);
|
||||||
SDL_CloseAudioDevice(audio_device_);
|
SDL_CloseAudioDevice(audio_device_);
|
||||||
delete[] audio_buffer_;
|
delete[] audio_buffer_;
|
||||||
// ImGui_ImplSDLRenderer2_Shutdown();
|
|
||||||
// ImGui_ImplSDL2_Shutdown();
|
// Clean up texture
|
||||||
// ImGui::DestroyContext();
|
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();
|
SDL_Quit();
|
||||||
|
|
||||||
|
printf("[EMULATOR] Shutdown complete.\n");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user