# YAZE Emulator Enhancement Roadmap **Version**: 1.0 **Date**: October 8, 2025 **Status**: Planning Phase **Target**: Mesen2-Level Debugging + AI Integration --- ## Executive Summary This document outlines the roadmap for evolving YAZE's SNES emulator from a basic runtime into a **world-class debugging platform** with AI agent integration. The goal is to achieve feature parity with Mesen2's advanced debugging capabilities while adding unique AI-powered features through the z3ed CLI system. ### Core Objectives 1. **Advanced Debugger** - Breakpoints, watchpoints, memory inspection, trace logging 2. **Performance Optimization** - Cycle-accurate timing, dynarec, frame pacing 3. **Audio System Fix** - SDL2 audio output currently broken, needs investigation 4. **AI Integration** - z3ed agent can read/write emulator state, automate testing 5. **SPC700 Debugger** - Full audio CPU debugging with APU state inspection --- ## Current State Analysis ### What Works - **CPU Emulation**: 65816 core functional, runs games - **PPU Rendering**: Display works, texture updates to SDL2 - **ROM Loading**: Can load and execute SNES ROMs - **Basic Controls**: Start/stop/pause/reset functionality - **Memory Access**: Read/write to CPU memory space - **Renderer Integration**: Now using `IRenderer` interface (SDL3-ready!) - **Stability**: Emulator pauses during window resize (macOS protection) ### What's Broken โŒ - **Audio Output**: SDL2 audio device initialized but no sound plays - **SPC700 Debugging**: No inspection tools for audio CPU - **Performance**: Not cycle-accurate, timing issues - **Debugging Tools**: Minimal breakpoint support, no watchpoints - **Memory Viewer**: Basic hex view, no structured inspection - **Trace Logging**: No execution tracing capability ### What's Missing Pending: - **Advanced Breakpoints**: Conditional, access-based, CPU/SPC700 - **Memory Watchpoints**: Track reads/writes to specific addresses - **Disassembly View**: Real-time code annotation - **Performance Profiling**: Hotspot analysis, cycle counting - **Event Viewer**: Track NMI, IRQ, DMA events - **PPU Inspector**: VRAM, OAM, palette debugging - **APU Inspector**: DSP state, sample buffer, channel visualization - **AI Integration**: z3ed agent can't access emulator yet --- ## Tool Phase 1: Audio System Fix (Priority: CRITICAL) ### Problem Analysis **Current State**: ```cpp // controller.cc:31-33 editor_manager_.emulator().set_audio_buffer(window_.audio_buffer_.get()); editor_manager_.emulator().set_audio_device_id(window_.audio_device_); // window.cc:114-130 window.audio_device_ = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); SDL_PauseAudioDevice(window.audio_device_, 0); // Unpause ``` **The Issue**: Audio device is initialized and unpaused, but `SDL_QueueAudio()` in emulator isn't producing sound. ### Investigation Steps 1. **Verify Audio Device State** ```cpp // Add to Emulator::Run() if (frame_count_ % 60 == 0) { // Every second uint32_t queued = SDL_GetQueuedAudioSize(audio_device_); SDL_AudioStatus status = SDL_GetAudioDeviceStatus(audio_device_); printf("[AUDIO] Queued: %u bytes, Status: %d (1=playing, 2=paused)\n", queued, status); } ``` 2. **Check SPC700 Sample Generation** ```cpp // Verify snes_.SetSamples() is producing valid data snes_.SetSamples(audio_buffer_, wanted_samples_); // Debug output int16_t* samples = audio_buffer_; bool has_audio = false; for (int i = 0; i < wanted_samples_ * 2; i++) { if (samples[i] != 0) { has_audio = true; break; } } if (!has_audio && frame_count_ % 60 == 0) { printf("[AUDIO] Warning: All samples are zero!\n"); } ``` 3. **Validate Audio Format Compatibility** ```cpp // window.cc - Check if requested format matches obtained format SDL_AudioSpec want, have; // ... (existing code) window.audio_device_ = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); printf("[AUDIO] Requested: %dHz, %d channels, format=%d\n", want.freq, want.channels, want.format); printf("[AUDIO] Obtained: %dHz, %d channels, format=%d\n", have.freq, have.channels, have.format); if (have.freq != want.freq || have.channels != want.channels) { LOG_ERROR("Audio", "Audio spec mismatch - may need resampling"); } ``` ### Likely Fixes **Fix 1: Audio Device Paused State** ```cpp // The audio device might be re-pausing itself // Try forcing unpause in the emulator loop if (frame_count_ % 60 == 0) { SDL_PauseAudioDevice(audio_device_, 0); // Ensure unpaused } ``` **Fix 2: Sample Format Conversion** ```cpp // SPC700 might be outputting wrong format // Ensure AUDIO_S16 (signed 16-bit) matches emulator output void Snes::SetSamples(int16_t* buffer, int count) { // Verify apu_.GetSamples() returns int16_t, not float or uint16_t apu_.GetSamples(buffer, count); // Debug: Check for clipping or DC offset for (int i = 0; i < count * 2; i++) { if (buffer[i] < -32768 || buffer[i] > 32767) { printf("[AUDIO] Sample %d out of range: %d\n", i, buffer[i]); } } } ``` **Fix 3: Buffer Size Mismatch** ```cpp // Ensure buffer allocation matches usage // window.cc:128 window.audio_buffer_ = std::make_shared(audio_frequency / 50 * 4); // This allocates: 48000 / 50 * 4 = 3840 int16_t samples // Emulator uses: wanted_samples_ * 4 bytes = (48000/60) * 4 = 3200 bytes // = 1600 int16_t samples (800 per channel) // MISMATCH! Should be: window.audio_buffer_ = std::make_shared(audio_frequency / 50 * 2); // Stereo ``` ### Quick Win Actions **File**: `window.cc` **Line**: 128 **Change**: ```cpp // Before: window.audio_buffer_ = std::make_shared(audio_frequency / 50 * 4); // After: // Allocate for stereo at 60Hz (worst case) // 48000Hz / 60 FPS = 800 samples/frame * 2 channels = 1600 int16_t window.audio_buffer_ = std::make_shared((audio_frequency / 50) * 2); ``` **File**: `emulator.cc` **After Line**: 216 **Add**: ```cpp // Debug audio output if (frame_count_ % 300 == 0) { // Every 5 seconds uint32_t queued = SDL_GetQueuedAudioSize(audio_device_); SDL_AudioStatus status = SDL_GetAudioDeviceStatus(audio_device_); printf("[AUDIO] Status=%d, Queued=%u, WantedSamples=%d\n", status, queued, wanted_samples_); } ``` **Estimated Fix Time**: 2-4 hours --- ## ๐Ÿ› Phase 2: Advanced Debugger (Mesen2 Feature Parity) ### Feature Comparison: YAZE vs Mesen2 | Feature | Mesen2 | YAZE Current | YAZE Target | |---------|--------|--------------|-------------| | CPU Breakpoints | Execute/Read/Write | Warning: Basic Execute | Full Support | | Memory Watchpoints | Conditional | โŒ None | Conditional | | Disassembly View | Live Annotated | โŒ Static | Live + Labels | | Memory Viewer | Multi-region | Warning: Basic Hex | Structured | | Trace Logger | CPU/SPC/DMA | โŒ None | All Channels | | Event Viewer | IRQ/NMI/DMA | โŒ None | Full Timeline | | Performance | Cycle Accurate | โŒ Approximate | Cycle Accurate | | Save States | Warning: Limited | Warning: Basic | Full State | | PPU Debugger | Layer Viewer | โŒ None | VRAM Inspector | | APU Debugger | DSP Viewer | โŒ None | Channel Mixer | | Scripting | Lua | โŒ None | z3ed + AI! | ### 2.1 Breakpoint System **Architecture**: ```cpp // src/app/emu/debug/breakpoint_manager.h class BreakpointManager { public: enum class Type { EXECUTE, // Break when PC reaches address READ, // Break when address is read WRITE, // Break when address is written ACCESS, // Break on read OR write CONDITIONAL // Break when condition is true }; struct Breakpoint { uint32_t address; Type type; bool enabled; std::string condition; // Lua expression or simple comparison uint32_t hit_count; std::function callback; // Optional custom logic }; uint32_t AddBreakpoint(uint32_t address, Type type, const std::string& condition = ""); void RemoveBreakpoint(uint32_t id); bool ShouldBreak(uint32_t address, Type access_type); std::vector ListBreakpoints(); private: std::unordered_map breakpoints_; uint32_t next_id_ = 1; }; ``` **CPU Integration**: ```cpp // src/app/emu/cpu/cpu.cc void CPU::RunOpcode() { // Check execute breakpoint BEFORE running if (breakpoint_mgr_->ShouldBreak(PC, BreakpointType::EXECUTE)) { emulator_->OnBreakpointHit(PC, BreakpointType::EXECUTE); return; // Pause execution } // Run instruction... ExecuteInstruction(); // Memory access breakpoints handled in Read()/Write() } uint8_t CPU::Read(uint32_t address) { if (breakpoint_mgr_->ShouldBreak(address, BreakpointType::READ)) { emulator_->OnBreakpointHit(address, BreakpointType::READ); } return memory_->Read(address); } ``` **z3ed Integration**: ```bash # CLI commands z3ed emu breakpoint add --address 0x00FFD9 --type execute z3ed emu breakpoint add --address 0x7E0010 --type write --condition "value > 100" z3ed emu breakpoint list z3ed emu breakpoint remove --id 1 # AI agent can use these "Set a breakpoint at the Link damage handler" โ†’ Agent finds damage code address โ†’ z3ed emu breakpoint add ``` **Estimated Effort**: 8-12 hours --- ### 2.2 Memory Watchpoints **Features**: - Track specific memory regions - Log all accesses with stack traces - Detect buffer overflows - Find data corruption sources **Implementation**: ```cpp // src/app/emu/debug/watchpoint_manager.h class WatchpointManager { public: struct Watchpoint { uint32_t start_address; uint32_t end_address; bool track_reads; bool track_writes; std::vector history; }; struct AccessLog { uint32_t pc; // Where the access happened uint32_t address; // What address was accessed uint8_t old_value; // Value before write uint8_t new_value; // Value after write bool is_write; uint64_t cycle_count; }; void AddWatchpoint(uint32_t start, uint32_t end, bool reads, bool writes); void OnMemoryAccess(uint32_t pc, uint32_t address, bool is_write, uint8_t old_val, uint8_t new_val); std::vector GetHistory(uint32_t address, int max_entries = 100); }; ``` **Use Cases**: - Find where Link's HP is being modified - Track item collection bugs - Debug event flag corruption - Detect unintended memory writes **z3ed Commands**: ```bash z3ed emu watch add --start 0x7E0000 --end 0x7E1FFF --reads --writes z3ed emu watch history --address 0x7E0010 z3ed emu watch export --format csv ``` **Estimated Effort**: 6-8 hours --- ### 2.3 Live Disassembly Viewer **Mesen2 Inspiration**: - Scrollable code view with current PC highlighted - Labels from ROM labels file - Inline comments - Jump target visualization - Hot code highlighting (most-executed instructions) **Architecture**: ```cpp // src/app/emu/debug/disassembly_viewer.h (already exists!) // Enhance existing viewer class DisassemblyViewer { public: struct DisassembledInstruction { uint32_t address; std::string mnemonic; std::string operands; std::string comment; uint32_t execution_count; // NEW: Hotspot tracking bool is_breakpoint; bool is_current_pc; }; void Update(CPU& cpu); void RenderWindow(); void JumpToAddress(uint32_t address); void ToggleBreakpoint(uint32_t address); // NEW: Hotspot profiling void EnableProfiling(bool enable); std::vector GetHotspots(int top_n = 10); private: std::unordered_map execution_counts_; std::shared_ptr labels_; // From ROM }; ``` **ImGui Integration**: ```cpp void Emulator::RenderDisassemblyWindow() { if (ImGui::Begin("Disassembly", &show_disassembly_)) { // Scrollable list ImGui::BeginChild("DisasmScroll"); // Show ยฑ50 instructions around PC uint32_t pc = snes_.cpu().PC; for (int offset = -50; offset <= 50; offset++) { auto instr = disassembly_viewer_.GetInstruction(pc + offset); // Highlight current PC if (offset == 0) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); } // Show execution count for hotspots if (instr.execution_count > 1000) { ImGui::TextColored(ImVec4(1,0.5,0,1), ""); // Hot code } ImGui::Text("%06X %s %s ; %s", instr.address, instr.mnemonic.c_str(), instr.operands.c_str(), instr.comment.c_str()); if (offset == 0) ImGui::PopStyleColor(); // Click to toggle breakpoint if (ImGui::IsItemClicked()) { disassembly_viewer_.ToggleBreakpoint(instr.address); } } ImGui::EndChild(); } ImGui::End(); } ``` **Estimated Effort**: 10-12 hours --- ### 2.4 Enhanced Memory Viewer **Multi-Region Support**: ```cpp enum class MemoryRegion { WRAM, // 0x7E0000-0x7FFFFF (128KB) SRAM, // Cartridge RAM VRAM, // PPU video RAM CGRAM, // PPU palette RAM OAM, // PPU sprite RAM ARAM, // SPC700 audio RAM ROM, // Cartridge ROM REGISTERS // Hardware registers }; ``` **Structured Views**: ```cpp class MemoryViewer { public: void RenderHexView(MemoryRegion region); void RenderStructView(uint32_t address, const std::string& struct_name); void RenderDiffView(uint32_t address, const uint8_t* reference); // NEW: ROM label integration void SetLabels(std::shared_ptr labels); std::string GetLabelForAddress(uint32_t address); // NEW: Goto functionality void GotoAddress(uint32_t address); void GotoLabel(const std::string& label); }; ``` **ImGui Layout**: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Memory Viewer โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Region: [WRAM โ–ผ] | Goto: [0x7E0010] [Find] โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Addr +0 +1 +2 +3 +4 +5 +6 +7 ASCII โ”‚ โ”‚ 7E0000 00 05 3C 00 00 00 00 00 ..<..... โ”‚ โ† Link's HP โ”‚ 7E0008 1F 00 00 00 00 00 00 00 ........ โ”‚ โ”‚ ... โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Watchpoints: [Add] [Clear All] โ”‚ โ”‚ โ€ข 0x7E0000-0x7E0010 (R/W) - Link stats โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` **Estimated Effort**: 6-8 hours --- ## Phase 3: Performance Optimizations ### 3.1 Cycle-Accurate Timing **Current Issue**: Emulator runs on frame timing, not cycle timing **Mesen2 Approach**: ```cpp class CPU { void RunCycle() { if (cycles_until_next_instruction_ == 0) { ExecuteInstruction(); // Sets cycles_until_next_instruction_ } else { cycles_until_next_instruction_--; } // Other components run per-cycle ppu_.RunCycle(); apu_.RunCycle(); dma_.RunCycle(); } }; // Emulator loop void Emulator::RunFrame() { const int cycles_per_frame = snes_.memory().pal_timing() ? 1538400 : 1789773; for (int i = 0; i < cycles_per_frame; i++) { snes_.RunCycle(); // ONE cycle at a time } } ``` **Benefits**: - Accurate mid-scanline effects - Proper DMA timing - Correct PPU rendering edge cases - Deterministic emulation **Estimated Effort**: 20-30 hours (major refactor) --- ### 3.2 Dynamic Recompilation (Dynarec) **Why**: Cycle-accurate interpretation is slow (~30 FPS). Dynarec can hit 60 FPS. **Strategy**: ```cpp class Dynarec { // Compile frequently-executed code blocks to native ARM/x64 void* CompileBlock(uint32_t start_pc); void InvalidateBlock(uint32_t address); // When code changes // Cache compiled blocks std::unordered_map code_cache_; }; void CPU::RunOpcode() { if (dynarec_enabled_) { // Check if block is compiled if (auto* block = dynarec_.GetBlock(PC)) { return ((BlockFunc)block)(); // Execute native code } } // Fallback to interpreter ExecuteInstruction(); } ``` **Complexity**: Very high - requires assembly code generation **Alternative**: Use existing dynarec library like `bsnes-jit` **Estimated Effort**: 40-60 hours (or use library: 10 hours) --- ### 3.3 Frame Pacing Improvements **Current Issue**: SDL_Delay(1) is too coarse **Better Approach**: ```cpp void Emulator::RunFrame() { auto frame_start = std::chrono::high_resolution_clock::now(); // Run SNES frame snes_.RunFrame(); // Calculate how long to wait auto frame_end = std::chrono::high_resolution_clock::now(); auto frame_duration = std::chrono::duration_cast( frame_end - frame_start); auto target_duration = std::chrono::microseconds( static_cast(wanted_frames_ * 1'000'000)); if (frame_duration < target_duration) { auto sleep_time = target_duration - frame_duration; std::this_thread::sleep_for(sleep_time); // Precise sleep } } ``` **Estimated Effort**: 2-3 hours --- ## Game Phase 4: SPC700 Audio CPU Debugger ### 4.1 APU Inspector Window **Layout**: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ APU Debugger โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ SPC700 CPU State: โ”‚ โ”‚ PC: 0x1234 A: 0x00 X: 0x05 Y: 0xFF PSW: 0x02 โ”‚ โ”‚ SP: 0xEF Cycles: 12,345,678 โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ DSP Registers: [Channel 0โ–ผ] โ”‚ โ”‚ VOL_L: 127 VOL_R: 127 PITCH: 2048 โ”‚ โ”‚ ADSR: 0xBE7F GAIN: 0x7F ENVX: 45 OUTX: 78 โ”‚ โ”‚ โ”‚ โ”‚ Waveform: [โ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–“โ–“โ–“โ–‘โ–‘โ–‘โ–‘โ–’โ–’โ–’โ–’โ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–“โ–“โ–“โ–‘โ–‘โ–‘โ–‘] (live) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Audio RAM (64KB): โ”‚ โ”‚ 0000 BRR BRR BRR BRR ... (sample data) โ”‚ โ”‚ ... โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Channel Mixer: โ”‚ โ”‚ 0: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘ (75%) 1: โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ (0%) โ”‚ โ”‚ 2: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ (45%) 3: โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ (0%) โ”‚ โ”‚ ... โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` **Implementation**: ```cpp // src/app/emu/debug/apu_inspector.h class ApuInspector { public: void RenderWindow(Snes& snes); void RenderChannelMixer(); void RenderWaveform(int channel); void RenderDspRegisters(); void RenderAudioRam(); // Sample buffer visualization void UpdateWaveformData(const int16_t* samples, int count); private: std::array, 8> channel_waveforms_; int selected_channel_ = 0; }; ``` **z3ed Commands**: ```bash z3ed emu apu status z3ed emu apu channel --id 0 z3ed emu apu dump-ram --output audio_ram.bin z3ed emu apu export-samples --channel 0 --format wav ``` **Estimated Effort**: 12-15 hours --- ### 4.2 Audio Sample Export **Feature**: Export audio samples to WAV for analysis ```cpp class AudioExporter { public: void StartRecording(int channel = -1); // -1 = all channels void StopRecording(); void ExportToWav(const std::string& filename); private: std::vector recorded_samples_; bool recording_ = false; }; ``` **Use Cases**: - Debug why sound effects aren't playing - Export game music for analysis - Compare with real SNES hardware recordings **Estimated Effort**: 4-6 hours --- ## AI Phase 5: z3ed AI Agent Integration ### 5.1 Emulator State Access **Add to ConversationalAgentService**: ```cpp // src/cli/service/agent/conversational_agent_service.h class ConversationalAgentService { public: // NEW: Emulator access void SetEmulator(emu::Emulator* emulator); // Tool: Get emulator state std::string HandleEmulatorState(); std::string HandleCpuState(); std::string HandleMemoryRead(uint32_t address, int count); std::string HandleMemoryWrite(uint32_t address, const std::vector& data); private: emu::Emulator* emulator_ = nullptr; }; ``` **z3ed Tool Schema**: ```json { "name": "emulator-read-memory", "description": "Read emulator memory at a specific address", "parameters": { "address": {"type": "integer", "description": "Memory address (e.g., 0x7E0010)"}, "count": {"type": "integer", "description": "Number of bytes to read"}, "region": {"type": "string", "enum": ["wram", "sram", "rom", "aram"]} } } ``` **Example AI Queries**: ``` User: "What is Link's current HP?" Agent: [calls emulator-read-memory address=0x7E0000 count=1] โ†’ Response: "Link has 6 hearts (0x60 = 96 health points)" User: "Set Link to full health" Agent: [calls emulator-write-memory address=0x7E0000 data=[0xA0]] โ†’ Response: "Link's HP set to 160 (full health)" User: "Where is the game stuck?" Agent: [calls emulator-cpu-state] โ†’ Response: "PC=$00:8234 - Infinite loop in NMI handler" ``` --- ### 5.2 Automated Test Generation **Use Case**: AI generates emulator tests from natural language **Example Flow**: ```bash z3ed agent test-scenario --prompt "Test that Link takes damage from enemies" # AI generates: { "steps": [ {"action": "load-state", "file": "link_at_full_hp.sfc"}, {"action": "run-frames", "count": 60}, {"action": "assert-memory", "address": "0x7E0000", "value": "0xA0"}, {"action": "move-link", "direction": "right", "frames": 30}, {"action": "assert-memory-decreased", "address": "0x7E0000"}, {"action": "screenshot", "name": "link_damaged.png"} ] } ``` **Implementation**: ```cpp // src/cli/commands/agent/test_scenario_runner.h class TestScenarioRunner { public: struct TestStep { std::string action; absl::flat_hash_map params; }; absl::Status RunScenario(const std::vector& steps); private: void ExecuteLoadState(const std::string& file); void ExecuteRunFrames(int count); void ExecuteAssertMemory(uint32_t address, uint8_t expected); void ExecuteMoveLink(const std::string& direction, int frames); void ExecuteScreenshot(const std::string& filename); }; ``` **Estimated Effort**: 8-10 hours --- ### 5.3 Memory Map Learning **Feature**: AI learns ROM's memory layout from debugging sessions **Architecture**: ```cpp // Extends existing learn command z3ed agent learn --memory-map "0x7E0000" --label "link_hp" --type "uint8" z3ed agent learn --memory-map "0x7E0010" --label "link_x_pos" --type "uint16" // AI can then use this knowledge User: "What is Link's position?" Agent: [checks learned memory map] [calls emulator-read-memory address=0x7E0010 count=2 type=uint16] ``` **Storage**: ```json // ~/.yaze/agent/memory_maps/zelda3.json { "rom_hash": "abc123...", "symbols": { "0x7E0000": {"name": "link_hp", "type": "uint8", "description": "Link's health"}, "0x7E0010": {"name": "link_x_pos", "type": "uint16", "description": "Link X coordinate"}, "0x7E0012": {"name": "link_y_pos", "type": "uint16", "description": "Link Y coordinate"} } } ``` **Estimated Effort**: 6-8 hours --- ## ๐Ÿ“Š Phase 6: Performance Profiling ### 6.1 Cycle Counter & Profiler **Mesen2 Feature**: Shows which code is hot, helps optimize hacks **Implementation**: ```cpp class PerformanceProfiler { public: struct FunctionProfile { uint32_t start_address; uint32_t end_address; std::string name; uint64_t total_cycles; uint32_t call_count; float percentage; }; void StartProfiling(); void StopProfiling(); std::vector GetHotFunctions(int top_n = 20); void ExportFlameGraph(const std::string& filename); private: std::unordered_map address_cycle_counts_; }; ``` **ImGui Visualization**: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Performance Profiler โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Function Cycles Calls % โ”‚ โ”‚ NMI Handler 2,456,789 1,234 15.2% โ”‚ โ”‚ Link Update 1,987,654 3,600 12.3% โ”‚ โ”‚ PPU Transfer 1,234,567 890 7.6% โ”‚ โ”‚ ... โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ [Start Profiling] [Stop] [Export Flame Graph] โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` **z3ed Commands**: ```bash z3ed emu profile start # ... run game for a bit ... z3ed emu profile stop z3ed emu profile report --top 20 z3ed emu profile export --format flamegraph --output profile.svg ``` **Estimated Effort**: 10-12 hours --- ### 6.2 Frame Time Analysis **Track frame timing issues**: ```cpp class FrameTimeAnalyzer { struct FrameStats { float cpu_time_ms; float ppu_time_ms; float apu_time_ms; float total_time_ms; int dropped_frames; }; void RecordFrame(); FrameStats GetAverageStats(int last_n_frames = 60); std::vector GetFrameTimeGraph(int frames = 300); // 5 seconds }; ``` **Visualization**: Real-time graph showing frame time spikes **Estimated Effort**: 4-6 hours --- ## Phase 7: Event System & Timeline ### 7.1 Event Logger **Mesen2 Feature**: Timeline view of all hardware events **Events to Track**: - NMI (V-Blank) - IRQ (H-Blank, Timer) - DMA transfers - HDMA activations - PPU mode changes - Audio sample playback starts **Implementation**: ```cpp class EventLogger { public: enum class EventType { NMI, IRQ, DMA, HDMA, PPU_MODE_CHANGE, APU_SAMPLE_START }; struct Event { EventType type; uint64_t cycle; uint32_t pc; // Where CPU was when event occurred std::string details; }; void LogEvent(EventType type, const std::string& details); std::vector GetEvents(uint64_t start_cycle, uint64_t end_cycle); void Clear(); private: std::deque event_history_; // Keep last 10,000 events }; ``` **Visualization**: ``` Timeline (last 5 frames): Frame 1: [NMI]โ”€โ”€โ”€โ”€[DMA]โ”€โ”€[HDMA]โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Frame 2: [NMI]โ”€โ”€โ”€โ”€[DMA]โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€[IRQ]โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Frame 3: [NMI]โ”€โ”€โ”€โ”€[DMA]โ”€โ”€[HDMA]โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ^ ^ ^ 16.67ms 18ms 20ms (timing shown) ``` **Estimated Effort**: 8-10 hours --- ## ๐Ÿง  Phase 8: AI-Powered Debugging ### 8.1 Intelligent Crash Analysis **Feature**: AI analyzes emulator state when game crashes/freezes ```bash z3ed agent debug-crash --state latest_crash.state # AI examines: # - CPU registers and flags # - Stack contents # - Recent code execution # - Memory watchpoint history # - Event timeline # AI response: "The game crashed because: 1. Infinite loop detected at $00:8234 2. This is the NMI handler 3. It's waiting for PPU register 0x2137 to change 4. The value hasn't changed in 10,000 cycles 5. Likely cause: PPU is in wrong mode (Mode 0 instead of Mode 1) Suggested fix: - Check PPU mode initialization at game start - Verify NMI handler only runs when PPU is ready" ``` **Estimated Effort**: 15-20 hours --- ### 8.2 Automated Bug Reproduction **Feature**: AI creates minimal test case from bug description ```bash z3ed agent repro --prompt "Link takes damage when he shouldn't" # AI generates reproduction steps: { "steps": [ "load_state: link_full_hp.sfc", "move: right, 50 frames", "assert: no damage taken", "expected: HP stays at 0xA0", "actual: HP decreased to 0x90", "analysis: Enemy collision box too large" ] } ``` **Estimated Effort**: 12-15 hours --- ## Implementation Roadmap ### Sprint 1: Audio Fix (Week 1) **Priority**: CRITICAL **Time**: 4 hours **Deliverables**: - Investigate audio buffer size mismatch - Add audio debug logging - Fix SDL2 audio output - Verify audio plays correctly ### Sprint 2: Basic Debugger (Weeks 2-3) **Priority**: HIGH **Time**: 20 hours **Deliverables**: - Breakpoint manager with execute/read/write - Enhanced disassembly viewer with hotspots - Improved memory viewer with regions - z3ed CLI commands for debugging ### Sprint 3: SPC700 Debugger (Week 4) **Priority**: MEDIUM **Time**: 15 hours **Deliverables**: - APU inspector window - Channel waveform visualization - Audio RAM viewer - Sample export to WAV ### Sprint 4: AI Integration (Weeks 5-6) **Priority**: MEDIUM **Time**: 25 hours **Deliverables**: - Emulator state tools for z3ed agent - Memory map learning system - Automated test scenario generation - Crash analysis AI ### Sprint 5: Performance (Weeks 7-8) **Priority**: LOW (Future) **Time**: 40+ hours **Deliverables**: - Cycle-accurate timing - Dynamic recompilation - Performance profiler - Frame pacing improvements --- ## ๐Ÿ”ฌ Technical Deep Dives ### Audio System Architecture (SDL2) **Current Flow**: ``` SPC700 โ†’ APU::GetSamples() โ†’ Snes::SetSamples() โ†’ audio_buffer_ โ†’ SDL_QueueAudio() โ†’ SDL Audio Device โ†’ System Audio ``` **Debug Points**: ```cpp // 1. Check SPC700 output void APU::GetSamples(int16_t* buffer, int count) { // Are samples being generated? LOG_IF_ZERO_SAMPLES(buffer, count); } // 2. Check buffer handoff void Snes::SetSamples(int16_t* buffer, int count) { apu_.GetSamples(buffer, count); // Are samples copied correctly? VERIFY_BUFFER_NOT_SILENT(buffer, count); } // 3. Check SDL queue if (SDL_QueueAudio(device, buffer, size) < 0) { LOG_ERROR("SDL_QueueAudio failed: %s", SDL_GetError()); } // 4. Check device status if (SDL_GetAudioDeviceStatus(device) != SDL_AUDIO_PLAYING) { LOG_ERROR("Audio device not playing!"); } ``` **Common Issues**: 1. **Buffer size mismatch** - Fixed in Phase 1 2. **Format mismatch** - SPC700 outputs float, SDL wants int16 3. **Device paused** - SDL_PauseAudioDevice() called somewhere 4. **No APU timing** - SPC700 not running or too slow --- ### Memory Regions Reference | Region | Address Range | Size | Description | |--------|--------------|------|-------------| | WRAM | 0x7E0000-0x7FFFFF | 128KB | Work RAM (game state) | | SRAM | 0x700000-0x77FFFF | Variable | Save RAM (battery) | | ROM | 0x000000-0x3FFFFF | Up to 6MB | Cartridge ROM | | VRAM | PPU Internal | 64KB | Video RAM (tiles, maps) | | CGRAM | PPU Internal | 512B | Palette RAM (colors) | | OAM | PPU Internal | 544B | Sprite RAM (objects) | | ARAM | SPC700 Internal | 64KB | Audio RAM (samples) | **Access Patterns**: ```cpp // CPU โ†’ WRAM (direct) uint8_t value = cpu.Read(0x7E0010); // CPU โ†’ VRAM (through PPU registers) cpu.Write(0x2118, low_byte); // VRAM write cpu.Write(0x2119, high_byte); // CPU โ†’ ARAM (through APU registers) cpu.Write(0x2140, data); // APU I/O port 0 ``` --- ## Game Phase 9: Advanced Features (Mesen2 Parity) ### 9.1 Rewind Feature **User Experience**: Hold button to rewind gameplay ```cpp class RewindManager { void RecordFrame(); // Save state every frame void Rewind(int frames); // Circular buffer (last 10 seconds = 600 frames) std::deque frame_history_; static constexpr int kMaxFrames = 600; }; ``` **Memory Impact**: ~600 * 100KB = 60MB (acceptable) **Estimated Effort**: 6-8 hours --- ### 9.2 TAS (Tool-Assisted Speedrun) Input Recording **Feature**: Record and replay input sequences ```cpp class InputRecorder { struct InputFrame { uint16_t buttons; // SNES controller state uint64_t frame_number; }; void StartRecording(); void StopRecording(); void SaveMovie(const std::string& filename); void PlayMovie(const std::string& filename); std::vector recorded_inputs_; }; ``` **File Format** (JSON): ```json { "rom_hash": "abc123...", "frames": [ {"frame": 0, "buttons": 0x0000}, {"frame": 60, "buttons": 0x0080}, // A button pressed {"frame": 61, "buttons": 0x0000} ] } ``` **z3ed Integration**: ```bash z3ed emu record start # ... play game ... z3ed emu record stop --output my_gameplay.json z3ed emu replay --input my_gameplay.json --verify # AI can generate TAS inputs! z3ed agent tas --prompt "Beat the first dungeon as fast as possible" ``` **Estimated Effort**: 8-10 hours --- ### 9.3 Comparison Mode **Feature**: Run two emulator instances side-by-side **Use Case**: Compare vanilla vs hacked ROM, or before/after AI changes ```cpp class ComparisonEmulator { Emulator emu_a_; Emulator emu_b_; void RunBothFrames(); void RenderSideBySide(); void HighlightDifferences(); }; ``` **Visualization**: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Vanilla ROM โ”‚ Hacked ROM โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ [Game Screen A] โ”‚ [Game Screen B] โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ HP: 6 โคโคโค โ”‚ HP: 12 โคโคโคโคโคโค โ”‚ โ† Difference โ”‚ Rupees: 50 โ”‚ Rupees: 50 โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Memory Diff: 147 bytes different ``` **Estimated Effort**: 12-15 hours --- ## Optimization Summary ### Quick Wins (< 1 week) 1. **Fix audio output** - 4 hours 2. **Add CPU breakpoints** - 6 hours 3. **Enhanced memory viewer** - 6 hours 4. **Frame pacing** - 3 hours ### Medium Term (1-2 months) 5. **Live disassembly** - 10 hours 6. **APU debugger** - 15 hours 7. **Event logger** - 8 hours 8. **AI emulator tools** - 25 hours ### Long Term (3-6 months) 9. **Cycle accuracy** - 30 hours 10. **Dynarec** - 60 hours 11. **TAS recording** - 10 hours 12. **Comparison mode** - 15 hours --- ## AI z3ed Agent Emulator Tools ### New Tool Categories **Emulator Control**: ```json { "name": "emulator-control", "actions": ["start", "stop", "pause", "reset", "step"], "description": "Control emulator execution" } ``` **Memory Tools**: ```json { "name": "emulator-read-memory", "parameters": { "address": "hex string (e.g., '0x7E0010')", "count": "number of bytes", "region": "wram|sram|rom|vram|aram" } }, { "name": "emulator-write-memory", "parameters": { "address": "hex string", "data": "array of bytes" } } ``` **State Tools**: ```json { "name": "emulator-cpu-state", "returns": { "pc": "Program Counter", "a": "Accumulator", "x": "X Register", "y": "Y Register", "sp": "Stack Pointer", "flags": "Processor flags" } }, { "name": "emulator-save-state", "parameters": {"filename": "string"} }, { "name": "emulator-load-state", "parameters": {"filename": "string"} } ``` **Debug Tools**: ```json { "name": "emulator-add-breakpoint", "parameters": { "address": "hex string", "type": "execute|read|write|access", "condition": "optional expression" } }, { "name": "emulator-get-hotspots", "parameters": {"top_n": "integer"}, "returns": "array of most-executed code addresses" } ``` ### Example AI Conversations **Debugging**: ``` User: "Why is Link's HP decreasing randomly?" Agent: Let me investigate... [calls emulator-read-memory address=0x7E0000 count=1] โ†’ Current HP: 0x60 (6 hearts) [calls emulator-add-breakpoint address=0x7E0000 type=write] โ†’ Breakpoint set [waits for breakpoint hit] โ†’ Breakpoint hit! PC=$00:8234 [calls emulator-read-memory address=0x8234 count=10] โ†’ Disassembly shows: Enemy collision handler Agent: "Link's HP is being modified by the enemy collision code at $00:8234. The collision box is checking at the wrong Y offset (+8 instead of +16). This causes damage when Link is near but not touching enemies." ``` **Testing**: ``` User: "Test that the new sword does double damage" Agent: [calls emulator-load-state file=enemy_encounter.sfc] [calls emulator-read-memory address=0x7E0010 count=2] # Link's damage โ†’ Current damage: 0x0004 [calls emulator-write-memory address=0x7E0010 data=[0x08]] # Set 2x [simulates sword swing - could use input recording] [calls emulator-read-memory address=0x7EF280] # Enemy HP โ†’ Enemy lost 8 HP (was 4 before) Agent: "Confirmed! The new sword deals 8 damage instead of 4. The 2x multiplier is working correctly." ``` --- ## ๐Ÿ“ File Structure for New Features ``` src/app/emu/ โ”œโ”€โ”€ debug/ โ”‚ โ”œโ”€โ”€ breakpoint_manager.{h,cc} # NEW โ”‚ โ”œโ”€โ”€ watchpoint_manager.{h,cc} # NEW โ”‚ โ”œโ”€โ”€ disassembly_viewer.{h,cc} # EXISTS - enhance โ”‚ โ”œโ”€โ”€ memory_viewer.{h,cc} # NEW โ”‚ โ”œโ”€โ”€ event_logger.{h,cc} # NEW โ”‚ โ”œโ”€โ”€ performance_profiler.{h,cc} # NEW โ”‚ โ””โ”€โ”€ apu_inspector.{h,cc} # NEW โ”œโ”€โ”€ tas/ โ”‚ โ”œโ”€โ”€ input_recorder.{h,cc} # NEW โ”‚ โ”œโ”€โ”€ movie_file.{h,cc} # NEW โ”‚ โ””โ”€โ”€ rewind_manager.{h,cc} # NEW โ””โ”€โ”€ emulator.{h,cc} # EXISTS - integrate above src/cli/commands/agent/ โ”œโ”€โ”€ emulator_tools.{h,cc} # NEW - z3ed agent emulator commands โ””โ”€โ”€ test_scenario_runner.{h,cc} # NEW - automated testing src/cli/service/agent/ โ””โ”€โ”€ tools/ โ”œโ”€โ”€ emulator_control_tool.cc # NEW โ”œโ”€โ”€ emulator_memory_tool.cc # NEW โ””โ”€โ”€ emulator_debug_tool.cc # NEW ``` --- ## ๐ŸŽจ UI Mockups ### Debugger Layout (ImGui) ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ YAZE Emulator - Debugging Mode โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Disassembly โ”‚ Game Display โ”‚ Registers โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 00:8000 LDA โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ A: 0x00 X: 0x05 โ”‚ โ”‚ 00:8002 STA โ”‚ โ”‚ โ”‚ โ”‚ Y: 0xFF SP: 0xEF โ”‚ โ”‚โ–บ00:8004 JMP โ”‚ โ”‚ [Zelda 3] โ”‚ โ”‚ PC: 0x8004 โ”‚ โ”‚ 00:8007 NOP โ”‚ โ”‚ โ”‚ โ”‚ PB: 0x00 DB: 0x00 โ”‚ โ”‚ 00:8008 RTL โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ Flags: nv--dizc โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Memory โ”‚ Event Timeline โ”‚ Stack โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 7E0000 00 05 โ”‚ Frame 123: โ”‚ 0x1FF: 0x00 โ”‚ โ”‚ 7E0008 3C 00 โ”‚ [NMI]โ”€โ”€[DMA]โ”€โ”€[IRQ] โ”‚ 0x1FE: 0x80 โ”‚ โ”‚ 7E0010 1F 00 โ”‚ โ”‚ 0x1FD: 0x04 โ† SP โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### APU Debugger Layout ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ SPC700 Audio Debugger โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ SPC700 Disassembly โ”‚ DSP State โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 0100 MOV A,#$00 โ”‚ Master Volume: L=127 R=127 โ”‚ โ”‚โ–บ0102 MOV (X),A โ”‚ Echo: OFF FIR: Standard โ”‚ โ”‚ 0104 INCX โ”‚ โ”‚ โ”‚ โ”‚ Channel 0: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘ (75%) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค VOL: L=100 R=100 PITCH=2048 โ”‚ โ”‚ Audio RAM (64KB) โ”‚ ADSR: Attack=15 Decay=7 Sustain=7 โ”‚ โ”‚ โ”‚ Sample: 0x0000-0x1234 (BRR) โ”‚ โ”‚ 0000 00 00 00 00 โ”‚ โ”‚ โ”‚ 0010 BRR BRR ... โ”‚ Channel 1: โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ (0%) โ”‚ โ”‚ โ”‚ (Inactive) โ”‚ โ”‚ [Export Samples] โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` --- ## Performance Targets ### Current Performance - **Emulation Speed**: ~60 FPS (with frame skipping) - **CPU Usage**: 40-60% (one core) - **Memory**: 80MB (emulator only) - **Accuracy**: ~85% (some timing issues) ### Target Performance (Post-Optimization) - **Emulation Speed**: Solid 60 FPS (no skipping) - **CPU Usage**: 20-30% (with dynarec) - **Memory**: 100MB (with debug features) - **Accuracy**: 98%+ (cycle-accurate) ### Optimization Strategy Priority 1. **Audio fix** - Enables testing with sound 2. **Frame pacing** - Eliminates jitter 3. **Hotspot profiling** - Identifies slow code 4. **Cycle accuracy** - Fixes timing bugs 5. **Dynarec** - 3x speed boost --- ## ๐Ÿงช Testing Integration ### Automated Emulator Tests (z3ed) **Unit Tests**: ```bash # Test CPU instructions z3ed emu test cpu --test-suite 65816_opcodes.json # Test PPU rendering z3ed emu test ppu --test-rom ppu_test.sfc --frames 600 # Test APU audio z3ed emu test apu --test-rom audio_test.sfc --export samples.wav ``` **Regression Tests**: ```bash # Run all ROM compatibility tests z3ed emu test regression --rom-dir test_roms/ --report report.html # Compare with reference emulator z3ed emu test compare --rom zelda3.sfc --reference bsnes --frames 1000 ``` **AI-Generated Tests**: ```bash z3ed agent test-scenario --prompt "Test that bombs damage enemies" # AI generates emulator test script { "setup": { "load_state": "link_with_bombs.sfc" }, "steps": [ {"action": "place-bomb", "x": 100, "y": 100}, {"action": "wait-frames", "count": 60}, {"action": "spawn-enemy", "type": "octorok", "x": 110, "y": 100}, {"action": "wait-frames", "count": 60}, {"action": "assert-memory", "address": "0x7EF280", "comparison": "less_than", "reference": "initial"} ] } ``` --- ## ๐Ÿ”Œ z3ed Agent + Emulator Integration ### New Agent Tools **Tool Registration** (automatic): ```cpp // src/cli/service/agent/tool_dispatcher.cc void ToolDispatcher::RegisterEmulatorTools(Emulator* emulator) { RegisterTool("emulator-start", [emulator]() { /* ... */ }); RegisterTool("emulator-stop", [emulator]() { /* ... */ }); RegisterTool("emulator-read-memory", [emulator](args) { /* ... */ }); RegisterTool("emulator-cpu-state", [emulator]() { /* ... */ }); RegisterTool("emulator-screenshot", [emulator]() { /* ... */ }); // ... 15 more tools } ``` **Example Conversations**: **1. Live Debugging**: ``` User: "Why does Link keep getting stuck in walls?" Agent: Let me debug this... [calls emulator-load-state file=link_stuck.sfc] [calls emulator-cpu-state] โ†’ PC=$00:9ABC (Link movement handler) [calls emulator-read-memory address=0x7E0022 count=2] โ†’ Link X: 0x0478, Y: 0x0234 [calls emulator-read-memory address=0x7E0020 count=2] โ†’ Subpixel X: 0x0F, Y: 0x0F (stuck at boundary!) [calls emulator-add-breakpoint address=0x9ABC type=execute] [calls emulator-step] Agent: "Found it! The collision detection at $00:9ABC is using integer division instead of proper subpixel handling. When Link's subpixel position is 0x0F, the boundary check fails and he gets stuck." ``` **2. Performance Analysis**: ``` User: "The overworld feels laggy after my changes" Agent: [calls emulator-start] [calls emulator-profile-start] [waits 300 frames = 5 seconds] [calls emulator-profile-stop] [calls emulator-get-hotspots top_n=10] โ†’ Top hotspot: $00:8500 (45% of cycles) [calls emulator-disassemble address=0x8500 count=20] Agent: "Your tile drawing code at $00:8500 is running every frame and taking 45% of CPU time! The original game only runs this when tiles change. Consider adding a dirty flag check." ``` **3. Automated ROM Testing**: ``` User: "Test my randomizer hack thoroughly" Agent: I'll run comprehensive tests... [calls emulator-load-state file=randomizer_start.sfc] For each dungeon: [calls emulator-teleport-link dungeon=N] [calls emulator-run-frames count=3600] # 60 seconds [calls emulator-screenshot name=dungeon_N.png] [calls emulator-check-crashes] [calls emulator-verify-items-reachable] [calls emulator-generate-report] Agent: "Tested all 12 dungeons. Found issues: - Dungeon 3: Softlock at room 0x34 (missing key) - Dungeon 7: Crash at room 0x12 (invalid sprite ID) All other dungeons: PASS" ``` --- ## ๐ŸŽ“ Learning from Mesen2 ### What Makes Mesen2 Great 1. **Comprehensive Debugging** - Every hardware event is logged - Full state inspection at any time - Breakpoints on everything (CPU, PPU, APU, memory) 2. **Performance** - Cycle-accurate yet fast (dynarec) - 60 FPS even with debugging enabled - Efficient state save/restore 3. **User Experience** - Integrated debugger in same window as game - Real-time visualization of state changes - Intuitive UI for complex operations 4. **Extensibility** - Lua scripting for automation - Event system for plugins - Export capabilities (traces, memory dumps) ### Our Unique Advantages **YAZE + z3ed has features Mesen2 doesn't**: 1. **AI Integration** - Natural language debugging - Automated test generation - Intelligent crash analysis 2. **ROM Editor Integration** - Edit ROM while emulator runs - See changes immediately - Debugging informs editing 3. **Collaborative Debugging** - Share emulator state with team - Remote debugging via gRPC - AI agent can help multiple users 4. **Cross-Platform Testing** - Same emulator in CLI and GUI - Automated test scenarios - CI/CD integration --- ## ๐Ÿ“Š Resource Requirements ### Development Time Estimates | Phase | Hours | Weeks (Part-Time) | |-------|-------|-------------------| | Audio Fix | 4 | 0.5 | | Basic Debugger | 20 | 2.5 | | SPC700 Debugger | 15 | 2 | | AI Integration | 25 | 3 | | Performance Opts | 40 | 5 | | **Total** | **104** | **13** | ### Memory Requirements | Feature | RAM Usage | |---------|-----------| | Base Emulator | 80 MB | | Breakpoint Manager | +2 MB | | Event Logger | +5 MB (10K events) | | Rewind Buffer | +60 MB (10 seconds) | | Performance Profiler | +10 MB | | **Total** | **~160 MB** | ### CPU Requirements | Configuration | CPU % (Single Core) | |--------------|---------------------| | Interpreter Only | 40-60% | | + Debugging | 50-70% | | + Profiling | 60-80% | | + Dynarec (future) | 20-30% | --- ## Recommended Implementation Order ### Month 1: Foundation **Weeks 1-2**: Audio fix + Basic breakpoints **Weeks 3-4**: Memory viewer + Disassembly enhancements ### Month 2: Audio & Events **Weeks 5-6**: SPC700 debugger + APU inspector **Weeks 7-8**: Event logger + Timeline view ### Month 3: AI Integration **Weeks 9-10**: z3ed emulator tools + Agent integration **Weeks 11-12**: Automated testing + Scenario runner ### Month 4: Performance **Weeks 13-14**: Cycle accuracy refactor **Weeks 15-16**: Dynarec or JIT library integration ### Month 5: Polish **Weeks 17-18**: UI/UX improvements **Weeks 19-20**: Documentation + Examples --- ## ๐Ÿ”ฎ Future Vision: AI-Powered ROM Hacking ### The Ultimate Workflow 1. **AI Explores the Game** ``` z3ed agent explore --rom zelda3.sfc --goal "Find all heart piece locations" # Agent: # - Loads ROM in emulator # - Runs around overworld automatically # - Detects heart piece spawn events via memory watchpoints # - Screenshots each location # - Generates report with coordinates ``` 2. **AI Debugs Your Hack** ``` z3ed agent debug --rom my_hack.sfc --issue "Boss doesn't take damage" # Agent: # - Loads hack in emulator # - Adds breakpoints on damage handlers # - Simulates boss fight # - Identifies missing damage check # - Suggests code fix with hex addresses ``` 3. **AI Generates TAS** ``` z3ed agent speedrun --rom zelda3.sfc --category "any%" # Agent: # - Studies game mechanics via emulator # - Discovers optimal movement patterns # - Generates frame-perfect input sequence # - Exports TAS movie file ``` 4. **AI Validates Randomizers** ``` z3ed agent validate --rom randomizer.sfc --seed 12345 # Agent: # - Generates logic graph from ROM # - Simulates playthrough via emulator # - Verifies all items are reachable # - Checks for softlocks # - Rates difficulty ``` --- ## ๐Ÿ› Appendix A: Audio Debugging Checklist **Run these checks to diagnose audio issues**: ### Check 1: Device Status ```cpp SDL_AudioStatus status = SDL_GetAudioDeviceStatus(audio_device_); printf("Audio Status: %d (1=playing, 2=paused, 3=stopped)\n", status); // Expected: 1 (SDL_AUDIO_PLAYING) ``` ### Check 2: Queue Size ```cpp uint32_t queued = SDL_GetQueuedAudioSize(audio_device_); printf("Queued Audio: %u bytes\n", queued); // Expected: 1000-8000 bytes (1-2 frames worth) // If 0: Not queueing // If >50000: Overflowing, audio thread stalled ``` ### Check 3: Sample Validation ```cpp int16_t* samples = audio_buffer_; bool all_zero = true; for (int i = 0; i < wanted_samples_ * 2; i++) { if (samples[i] != 0) { all_zero = false; break; } } if (all_zero) { printf("ERROR: All audio samples are zero! SPC700 not outputting.\n"); } ``` ### Check 4: Buffer Allocation ```cpp // In window.cc:128, verify size calculation: // For 48000Hz, 60 FPS, stereo: // samples_per_frame = 48000 / 60 = 800 // stereo_samples = 800 * 2 = 1600 int16_t // Size should be: 1600, NOT 3840 printf("Audio buffer size: %zu int16_t\n", audio_buffer_.size()); // Expected: 1600-1920 (for 60-50Hz) ``` ### Check 5: SPC700 Execution ```cpp // Verify SPC700 is actually running uint64_t apu_cycles = snes_.apu().GetCycles(); // Should increase every frame // If stuck: SPC700 deadlock or not running ``` ### Quick Fixes to Try **Fix A: Force Unpause** ```cpp // emulator.cc, in RunFrame(): SDL_PauseAudioDevice(audio_device_, 0); // Force play state ``` **Fix B: Larger Queue** ```cpp // If buffer underruns, queue more: SDL_QueueAudio(audio_device_, audio_buffer_, wanted_samples_ * 4 * 2); // 2 frames ``` **Fix C: Clear Stale Queue** ```cpp // If queue is stuck: if (SDL_GetQueuedAudioSize(audio_device_) > 50000) { SDL_ClearQueuedAudio(audio_device_); // Reset } ``` --- ## ๐Ÿ“ Appendix B: Mesen2 Feature Reference ### Debugger Windows (Inspiration) 1. **CPU Debugger**: Disassembly, registers, breakpoints 2. **Memory Tools**: Hex viewer, search, compare 3. **PPU Viewer**: Layer toggles, VRAM, OAM, palettes 4. **Event Viewer**: Timeline of all hardware events 5. **Trace Logger**: Full execution log with filters 6. **Performance Profiler**: Hotspot analysis 7. **Script Window**: Lua scripting for automation ### Event Types Tracked - **CPU**: NMI, IRQ, BRK instruction, RESET - **PPU**: V-Blank, H-Blank, Mode change, Sprite overflow - **DMA**: General DMA, HDMA, channels used - **APU**: Sample playback, DSP writes, Timer IRQ - **Cart**: Save RAM write, special chip events ### Trace Logger Format ``` Cycle PC Opcode A X Y SP Flags Event 0 $00FFD9 SEI 00 00 00 1FF nvmxdiZc 1 $00FFDA CLI 00 00 00 1FF nvmxdizc 2 $00FFDB JMP 00 00 00 1FF nvmxdizc ... 16749 $008234 LDA 05 00 00 1EF Nvmxdizc [NMI] ``` --- ## Success Criteria ### Phase 1 Complete When: - Audio plays correctly from SDL2 - Can hear game music and sound effects - No audio crackling or dropouts - Audio buffer diagnostics implemented ### Phase 2 Complete When: - Can set breakpoints on any address - Disassembly view shows live execution - Memory viewer has multi-region support - z3ed CLI can control debugger ### Phase 3 Complete When: - SPC700 debugger shows all APU state - Can visualize audio channels - Can export audio samples to WAV ### Phase 4 Complete When: - AI agent can read emulator memory - AI agent can control emulation - AI can generate test scenarios - Automated ROM testing works ### Phase 5 Complete When: - Emulator is cycle-accurate - 60 FPS maintained with debugging - Dynarec provides 3x speedup - All Mesen2 features implemented --- ## ๐ŸŽ“ Learning Resources ### SNES Emulation - [SNES Development Manual](https://www.romhacking.net/documents/226/) - [Fullsnes by nocash](https://problemkaputt.de/fullsnes.htm) - [bsnes source code](https://github.com/bsnes-emu/bsnes) - Reference implementation - [Mesen2 source code](https://github.com/SourMesen/Mesen2) - Feature inspiration ### Audio Debugging - [SPC700 Reference](https://wiki.superfamicom.org/spc700-reference) - [DSP Register Guide](https://wiki.superfamicom.org/dsp-registers) - [BRR Audio Format](https://wiki.superfamicom.org/bit-rate-reduction-brr) ### Performance Optimization - [Fast SNES Emulation](https://github.com/arm9/snes9x-rpi) - ARM optimization - [Dynarec Tutorial](https://github.com/rasky/r64emu/wiki/Dynamic-Recompilation) - N64 but applicable --- ## Credits & Acknowledgments **Emulator Core**: Based on LakeSnes by elzo-d **Debugger Inspiration**: Mesen2 by SourMesen **AI Integration**: z3ed agent system **Documentation**: With love (and Puerto Rican soup! ๐Ÿฒ) --- *Document Version: 1.0* *Last Updated: October 8, 2025* *Next Review: After Audio Fix* *Sleep Well! ๐Ÿ˜ด*