yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
emulator.cc
Go to the documentation of this file.
1#include "app/emu/emulator.h"
2
3#include <cstdlib>
4#include <cstdint>
5#include <fstream>
6#include <vector>
7
10#include "util/log.h"
11
12namespace yaze::core {
13 extern bool g_window_is_resizing;
14}
15
20#include "app/gui/core/color.h"
22#include "app/gui/core/icons.h"
24#include "imgui/imgui.h"
25
26namespace yaze {
27namespace emu {
28
29namespace {
30constexpr int kNativeSampleRate = 32000;
31}
32
34 // Don't call Cleanup() in destructor - renderer is already destroyed
35 // Just stop emulation
36 running_ = false;
37}
38
40 // Stop emulation
41 running_ = false;
42
43 // Don't try to destroy PPU texture during shutdown
44 // The renderer is destroyed before the emulator, so attempting to
45 // call renderer_->DestroyTexture() will crash
46 // The texture will be cleaned up automatically when SDL quits
47 ppu_texture_ = nullptr;
48
49 // Reset state
50 snes_initialized_ = false;
52}
53
55 if (use_sdl_audio_stream_ != enabled) {
56 use_sdl_audio_stream_ = enabled;
58 }
59}
60
61void Emulator::Initialize(gfx::IRenderer* renderer, const std::vector<uint8_t>& rom_data) {
62 // This method is now optional - emulator can be initialized lazily in Run()
64 rom_data_ = rom_data;
65
67 const char* env_value = std::getenv("YAZE_USE_SDL_AUDIO_STREAM");
68 if (env_value && std::atoi(env_value) != 0) {
70 }
72 }
73
74 // Cards are registered in EditorManager::Initialize() to avoid duplication
75
76 // Reset state for new ROM
77 running_ = false;
78 snes_initialized_ = false;
79
80 // Initialize audio backend if not already done
81 if (!audio_backend_) {
84
85 audio::AudioConfig config;
86 config.sample_rate = 48000;
87 config.channels = 2;
88 // Use moderate buffer size - 1024 samples = ~21ms latency
89 // This is a good balance between latency and stability
90 config.buffer_frames = 1024;
92
93 if (!audio_backend_->Initialize(config)) {
94 LOG_ERROR("Emulator", "Failed to initialize audio backend");
95 } else {
96 LOG_INFO("Emulator", "Audio backend initialized: %s",
97 audio_backend_->GetBackendName().c_str());
99 }
100 }
101
102 // Set up CPU breakpoint callback
103 snes_.cpu().on_breakpoint_hit_ = [this](uint32_t pc) -> bool {
105 };
106
107 // Set up instruction recording callback for DisassemblyViewer
108 snes_.cpu().on_instruction_executed_ = [this](uint32_t address, uint8_t opcode,
109 const std::vector<uint8_t>& operands,
110 const std::string& mnemonic,
111 const std::string& operand_str) {
112 disassembly_viewer_.RecordInstruction(address, opcode, operands, mnemonic, operand_str);
113 };
114
115 initialized_ = true;
116}
117
118void Emulator::Run(Rom* rom) {
120 const char* env_value = std::getenv("YAZE_USE_SDL_AUDIO_STREAM");
121 if (env_value && std::atoi(env_value) != 0) {
123 }
125 }
126
127 // Lazy initialization: set renderer from Controller if not set yet
128 if (!renderer_) {
129 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
130 "Emulator renderer not initialized");
131 return;
132 }
133
134 // Initialize audio backend if not already done (lazy initialization)
135 if (!audio_backend_) {
138
139 audio::AudioConfig config;
140 config.sample_rate = 48000;
141 config.channels = 2;
142 // Use moderate buffer size - 1024 samples = ~21ms latency
143 // This is a good balance between latency and stability
144 config.buffer_frames = 1024;
146
147 if (!audio_backend_->Initialize(config)) {
148 LOG_ERROR("Emulator", "Failed to initialize audio backend");
149 } else {
150 LOG_INFO("Emulator", "Audio backend initialized (lazy): %s",
151 audio_backend_->GetBackendName().c_str());
153 }
154 }
155
156 // Initialize input manager if not already done
159 LOG_ERROR("Emulator", "Failed to initialize input manager");
160 } else {
161 LOG_INFO("Emulator", "Input manager initialized: %s",
163 }
164 }
165
166 // Initialize SNES and create PPU texture on first run
167 // This happens lazily when user opens the emulator window
168 if (!snes_initialized_ && rom->is_loaded()) {
169 // Create PPU texture with correct format for SNES emulator
170 // ARGB8888 matches the XBGR format used by the SNES PPU (pixel format 1)
171 if (!ppu_texture_) {
173 512, 480, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING);
174 if (ppu_texture_ == NULL) {
175 printf("Failed to create PPU texture: %s\n", SDL_GetError());
176 return;
177 }
178 }
179
180 // Initialize SNES with ROM data (either from Initialize() or from rom parameter)
181 if (rom_data_.empty()) {
182 rom_data_ = rom->vector();
183 }
185
186 // Note: DisassemblyViewer recording is always enabled via callback
187 // No explicit setup needed - callback is set in Initialize()
188
189 // Note: PPU pixel format set to 1 (XBGR) in Init() which matches ARGB8888 texture
190
191 wanted_frames_ = 1.0 / (snes_.memory().pal_timing() ? 50.0 : 60.0);
192 wanted_samples_ = 48000 / (snes_.memory().pal_timing() ? 50 : 60);
193 snes_initialized_ = true;
194
195 count_frequency = SDL_GetPerformanceFrequency();
196 last_count = SDL_GetPerformanceCounter();
197 time_adder = 0.0;
198 frame_count_ = 0;
199 fps_timer_ = 0.0;
200 current_fps_ = 0.0;
201
202 // Start emulator in running state by default
203 // User can press Space to pause if needed
204 running_ = true;
205 }
206
207
208 // Auto-pause emulator during window resize to prevent crashes
209 // MODERN APPROACH: Only pause on actual window resize, not focus loss
210 static bool was_running_before_resize = false;
211
212 // Check if window is being resized (set in HandleEvents)
214 was_running_before_resize = true;
215 running_ = false;
216 } else if (!yaze::core::g_window_is_resizing && !running_ && was_running_before_resize) {
217 // Auto-resume after resize completes
218 running_ = true;
219 was_running_before_resize = false;
220 }
221
222 // REMOVED: Aggressive focus-based pausing
223 // Modern emulators (RetroArch, bsnes, etc.) continue running in background
224 // Users can manually pause with Space if they want to save CPU/battery
225
226 if (running_) {
227 // Poll input and update SNES controller state
228 input_manager_.Poll(&snes_, 1); // Player 1
229
230 uint64_t current_count = SDL_GetPerformanceCounter();
231 uint64_t delta = current_count - last_count;
232 last_count = current_count;
233 double seconds = delta / (double)count_frequency;
234 time_adder += seconds;
235
236 // Cap time accumulation to prevent spiral of death and improve stability
237 if (time_adder > wanted_frames_ * 3.0) {
239 }
240
241 // Track frames to skip for performance
242 int frames_to_process = 0;
243 while (time_adder >= wanted_frames_ - 0.002) {
245 frames_to_process++;
246 }
247
248 // Limit maximum frames to process (prevent spiral of death)
249 if (frames_to_process > 4) {
250 frames_to_process = 4;
251 }
252
253 if (snes_initialized_ && frames_to_process > 0) {
254 // Process frames (skip rendering for all but last frame if falling behind)
255 for (int i = 0; i < frames_to_process; i++) {
256 bool should_render = (i == frames_to_process - 1);
257
258 // Run frame
259 if (turbo_mode_) {
260 snes_.RunFrame();
261 }
262 snes_.RunFrame();
263
264 // Track FPS
265 frame_count_++;
267 if (fps_timer_ >= 1.0) {
269 frame_count_ = 0;
270 fps_timer_ = 0.0;
271 }
272
273 // Only render and handle audio on the last frame
274 if (should_render) {
275 // SMOOTH AUDIO BUFFERING
276 // Strategy: Always queue samples, never drop. Use dynamic rate control
277 // to keep buffer at target level. This prevents pops and glitches.
278
279 if (audio_backend_) {
281 if (use_sdl_audio_stream_ && audio_backend_->SupportsAudioStream()) {
282 audio_backend_->SetAudioStreamResampling(true, kNativeSampleRate, 2);
284 } else {
285 audio_backend_->SetAudioStreamResampling(false, kNativeSampleRate, 2);
286 audio_stream_active_ = false;
287 }
289 }
290
291 const bool use_native_stream =
293 audio_backend_->SupportsAudioStream();
294
295 auto audio_status = audio_backend_->GetStatus();
296 uint32_t queued_frames = audio_status.queued_frames;
297
298 // Synchronize DSP frame boundary for resampling
299 snes_.apu().dsp().NewFrame();
300
301 // Target buffer: 2.0 frames for low latency with safety margin
302 const uint32_t max_buffer = wanted_samples_ * 4;
303
304 if (queued_frames < max_buffer) {
305 bool queue_ok = true;
306
307 if (use_native_stream) {
308 const int frames_native = snes_.apu().dsp().CopyNativeFrame(
309 audio_buffer_, snes_.memory().pal_timing());
310 queue_ok = audio_backend_->QueueSamplesNative(
311 audio_buffer_, frames_native, 2, kNativeSampleRate);
312 } else {
314 const int num_samples = wanted_samples_ * 2; // Stereo
315 queue_ok = audio_backend_->QueueSamples(audio_buffer_, num_samples);
316 }
317
318 if (!queue_ok && use_native_stream) {
320 const int num_samples = wanted_samples_ * 2;
321 queue_ok = audio_backend_->QueueSamples(audio_buffer_, num_samples);
322 }
323
324 if (!queue_ok) {
325 static int error_count = 0;
326 if (++error_count % 300 == 0) {
327 LOG_WARN("Emulator",
328 "Failed to queue audio (count: %d, stream=%s)",
329 error_count, use_native_stream ? "SDL" : "manual");
330 }
331 }
332 } else {
333 // Buffer overflow - skip this frame's audio
334 static int overflow_count = 0;
335 if (++overflow_count % 60 == 0) {
336 LOG_WARN("Emulator",
337 "Audio buffer overflow (count: %d, queued: %u)",
338 overflow_count, queued_frames);
339 }
340 }
341 }
342
343 // Update PPU texture only on rendered frames
344 void* ppu_pixels_;
345 int ppu_pitch_;
346 if (renderer_->LockTexture(ppu_texture_, NULL, &ppu_pixels_, &ppu_pitch_)) {
347 snes_.SetPixels(static_cast<uint8_t*>(ppu_pixels_));
349
350 // WORKAROUND: Tiny delay after texture unlock to prevent macOS Metal crash
351 // macOS CoreAnimation/Metal driver bug in layer_presented() callback
352 // Without this, rapid texture updates corrupt Metal's frame tracking
353 SDL_Delay(1);
354 }
355 }
356 }
357 }
358 }
359
361}
362
364 try {
365 if (!card_registry_) return; // Card registry must be injected
366
367 static gui::EditorCard cpu_card("CPU Debugger", ICON_MD_MEMORY);
368 static gui::EditorCard ppu_card("PPU Viewer", ICON_MD_VIDEOGAME_ASSET);
369 static gui::EditorCard memory_card("Memory Viewer", ICON_MD_MEMORY);
370 static gui::EditorCard breakpoints_card("Breakpoints", ICON_MD_STOP);
371 static gui::EditorCard performance_card("Performance", ICON_MD_SPEED);
372 static gui::EditorCard ai_card("AI Agent", ICON_MD_SMART_TOY);
373 static gui::EditorCard save_states_card("Save States", ICON_MD_SAVE);
374 static gui::EditorCard keyboard_card("Keyboard Config", ICON_MD_KEYBOARD);
375 static gui::EditorCard apu_card("APU Debugger", ICON_MD_MUSIC_NOTE);
376 static gui::EditorCard audio_card("Audio Mixer", ICON_MD_AUDIO_FILE);
377
378 cpu_card.SetDefaultSize(400, 500);
379 ppu_card.SetDefaultSize(550, 520);
380 memory_card.SetDefaultSize(800, 600);
381 breakpoints_card.SetDefaultSize(400, 350);
382 performance_card.SetDefaultSize(350, 300);
383
384 // Get visibility flags from registry and pass them to Begin() for proper X button functionality
385 // This ensures each card window can be closed by the user via the window close button
386 bool* cpu_visible = card_registry_->GetVisibilityFlag("emulator.cpu_debugger");
387 if (cpu_visible && *cpu_visible) {
388 if (cpu_card.Begin(cpu_visible)) {
390 }
391 cpu_card.End();
392 }
393
394 bool* ppu_visible = card_registry_->GetVisibilityFlag("emulator.ppu_viewer");
395 if (ppu_visible && *ppu_visible) {
396 if (ppu_card.Begin(ppu_visible)) {
397 RenderNavBar();
399 }
400 ppu_card.End();
401 }
402
403 bool* memory_visible = card_registry_->GetVisibilityFlag("emulator.memory_viewer");
404 if (memory_visible && *memory_visible) {
405 if (memory_card.Begin(memory_visible)) {
407 }
408 memory_card.End();
409 }
410
411 bool* breakpoints_visible = card_registry_->GetVisibilityFlag("emulator.breakpoints");
412 if (breakpoints_visible && *breakpoints_visible) {
413 if (breakpoints_card.Begin(breakpoints_visible)) {
415 }
416 breakpoints_card.End();
417 }
418
419 bool* performance_visible = card_registry_->GetVisibilityFlag("emulator.performance");
420 if (performance_visible && *performance_visible) {
421 if (performance_card.Begin(performance_visible)) {
423 }
424 performance_card.End();
425 }
426
427 bool* ai_agent_visible = card_registry_->GetVisibilityFlag("emulator.ai_agent");
428 if (ai_agent_visible && *ai_agent_visible) {
429 if (ai_card.Begin(ai_agent_visible)) {
431 }
432 ai_card.End();
433 }
434
435 bool* save_states_visible = card_registry_->GetVisibilityFlag("emulator.save_states");
436 if (save_states_visible && *save_states_visible) {
437 if (save_states_card.Begin(save_states_visible)) {
439 }
440 save_states_card.End();
441 }
442
443 bool* keyboard_config_visible = card_registry_->GetVisibilityFlag("emulator.keyboard_config");
444 if (keyboard_config_visible && *keyboard_config_visible) {
445 if (keyboard_card.Begin(keyboard_config_visible)) {
447 }
448 keyboard_card.End();
449 }
450
451 bool* apu_debugger_visible = card_registry_->GetVisibilityFlag("emulator.apu_debugger");
452 if (apu_debugger_visible && *apu_debugger_visible) {
453 if (apu_card.Begin(apu_debugger_visible)) {
455 }
456 apu_card.End();
457 }
458
459 bool* audio_mixer_visible = card_registry_->GetVisibilityFlag("emulator.audio_mixer");
460 if (audio_mixer_visible && *audio_mixer_visible) {
461 if (audio_card.Begin(audio_mixer_visible)) {
462 // RenderAudioMixer();
463 }
464 audio_card.End();
465 }
466
467 } catch (const std::exception& e) {
468 // Fallback to basic UI if theming fails
469 ImGui::Text("Error loading emulator UI: %s", e.what());
470 if (ImGui::Button("Retry")) {
471 // Force theme manager reinitialization
472 auto& theme_manager = gui::ThemeManager::Get();
473 theme_manager.InitializeBuiltInThemes();
474 }
475 }
476}
477
479 // Delegate to UI layer
480 ui::RenderSnesPpu(this);
481}
482
484 // Delegate to UI layer
485 ui::RenderNavBar(this);
486}
487
488// REMOVED: HandleEvents() - replaced by ui::InputHandler::Poll()
489// The old ImGui::IsKeyPressed/Released approach was event-based and didn't work properly
490// for continuous game input. Now using SDL_GetKeyboardState() for proper polling.
491
493 // Delegate to UI layer
495}
496
498 // Delegate to UI layer
500}
501
503 try {
504 auto& theme_manager = gui::ThemeManager::Get();
505 const auto& theme = theme_manager.GetCurrentTheme();
506
507 // Debugger controls toolbar
508 if (ImGui::Button(ICON_MD_PLAY_ARROW)) { running_ = true; }
509 ImGui::SameLine();
510 if (ImGui::Button(ICON_MD_PAUSE)) { running_ = false; }
511 ImGui::SameLine();
512 if (ImGui::Button(ICON_MD_SKIP_NEXT " Step")) {
513 if (!running_) snes_.cpu().RunOpcode();
514 }
515 ImGui::SameLine();
516 if (ImGui::Button(ICON_MD_REFRESH)) { snes_.Reset(true); }
517
518 ImGui::Separator();
519
520 // Breakpoint controls
521 static char bp_addr[16] = "00FFD9";
522 ImGui::Text(ICON_MD_BUG_REPORT " Breakpoints:");
523 ImGui::PushItemWidth(100);
524 ImGui::InputText("##BPAddr", bp_addr, IM_ARRAYSIZE(bp_addr),
525 ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
526 ImGui::PopItemWidth();
527 ImGui::SameLine();
528 if (ImGui::Button(ICON_MD_ADD " Add")) {
529 uint32_t addr = std::strtoul(bp_addr, nullptr, 16);
532 "", absl::StrFormat("BP at $%06X", addr));
533 }
534
535 // List breakpoints
536 ImGui::BeginChild("##BPList", ImVec2(0, 100), true);
537 for (const auto& bp : breakpoint_manager_.GetAllBreakpoints()) {
539 bool enabled = bp.enabled;
540 if (ImGui::Checkbox(absl::StrFormat("##en%d", bp.id).c_str(), &enabled)) {
541 breakpoint_manager_.SetEnabled(bp.id, enabled);
542 }
543 ImGui::SameLine();
544 ImGui::Text("$%06X", bp.address);
545 ImGui::SameLine();
546 ImGui::TextDisabled("(hits: %d)", bp.hit_count);
547 ImGui::SameLine();
548 if (ImGui::SmallButton(absl::StrFormat(ICON_MD_DELETE "##%d", bp.id).c_str())) {
550 }
551 }
552 }
553 ImGui::EndChild();
554
555 ImGui::Separator();
556
557 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "CPU Status");
558 ImGui::PushStyleColor(ImGuiCol_ChildBg,
559 ConvertColorToImVec4(theme.child_bg));
560 ImGui::BeginChild("##CpuStatus", ImVec2(0, 180), true);
561
562 // Compact register display in a table
563 if (ImGui::BeginTable(
564 "Registers", 4,
565 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
566 ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed, 60);
567 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 80);
568 ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed, 60);
569 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 80);
570 ImGui::TableHeadersRow();
571
572 ImGui::TableNextRow();
573 ImGui::TableNextColumn();
574 ImGui::Text("A");
575 ImGui::TableNextColumn();
576 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%04X",
577 snes_.cpu().A);
578 ImGui::TableNextColumn();
579 ImGui::Text("D");
580 ImGui::TableNextColumn();
581 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%04X",
582 snes_.cpu().D);
583
584 ImGui::TableNextRow();
585 ImGui::TableNextColumn();
586 ImGui::Text("X");
587 ImGui::TableNextColumn();
588 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%04X",
589 snes_.cpu().X);
590 ImGui::TableNextColumn();
591 ImGui::Text("DB");
592 ImGui::TableNextColumn();
593 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%02X",
594 snes_.cpu().DB);
595
596 ImGui::TableNextRow();
597 ImGui::TableNextColumn();
598 ImGui::Text("Y");
599 ImGui::TableNextColumn();
600 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%04X",
601 snes_.cpu().Y);
602 ImGui::TableNextColumn();
603 ImGui::Text("PB");
604 ImGui::TableNextColumn();
605 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%02X",
606 snes_.cpu().PB);
607
608 ImGui::TableNextRow();
609 ImGui::TableNextColumn();
610 ImGui::Text("PC");
611 ImGui::TableNextColumn();
612 ImGui::TextColored(ConvertColorToImVec4(theme.success), "0x%04X",
613 snes_.cpu().PC);
614 ImGui::TableNextColumn();
615 ImGui::Text("SP");
616 ImGui::TableNextColumn();
617 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%02X",
618 snes_.memory().mutable_sp());
619
620 ImGui::TableNextRow();
621 ImGui::TableNextColumn();
622 ImGui::Text("PS");
623 ImGui::TableNextColumn();
624 ImGui::TextColored(ConvertColorToImVec4(theme.warning), "0x%02X",
625 snes_.cpu().status);
626 ImGui::TableNextColumn();
627 ImGui::Text("Cycle");
628 ImGui::TableNextColumn();
629 ImGui::TextColored(ConvertColorToImVec4(theme.info), "%llu",
631
632 ImGui::EndTable();
633 }
634
635 ImGui::EndChild();
636 ImGui::PopStyleColor();
637
638 // SPC700 Status Panel
639 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "SPC700 Status");
640 ImGui::PushStyleColor(ImGuiCol_ChildBg,
641 ConvertColorToImVec4(theme.child_bg));
642 ImGui::BeginChild("##SpcStatus", ImVec2(0, 150), true);
643
644 if (ImGui::BeginTable(
645 "SPCRegisters", 4,
646 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
647 ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed, 50);
648 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 60);
649 ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed, 50);
650 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 60);
651 ImGui::TableHeadersRow();
652
653 ImGui::TableNextRow();
654 ImGui::TableNextColumn();
655 ImGui::Text("A");
656 ImGui::TableNextColumn();
657 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%02X",
658 snes_.apu().spc700().A);
659 ImGui::TableNextColumn();
660 ImGui::Text("PC");
661 ImGui::TableNextColumn();
662 ImGui::TextColored(ConvertColorToImVec4(theme.success), "0x%04X",
663 snes_.apu().spc700().PC);
664
665 ImGui::TableNextRow();
666 ImGui::TableNextColumn();
667 ImGui::Text("X");
668 ImGui::TableNextColumn();
669 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%02X",
670 snes_.apu().spc700().X);
671 ImGui::TableNextColumn();
672 ImGui::Text("SP");
673 ImGui::TableNextColumn();
674 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%02X",
675 snes_.apu().spc700().SP);
676
677 ImGui::TableNextRow();
678 ImGui::TableNextColumn();
679 ImGui::Text("Y");
680 ImGui::TableNextColumn();
681 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "0x%02X",
682 snes_.apu().spc700().Y);
683 ImGui::TableNextColumn();
684 ImGui::Text("PSW");
685 ImGui::TableNextColumn();
686 ImGui::TextColored(
687 ConvertColorToImVec4(theme.warning), "0x%02X",
688 snes_.apu().spc700().FlagsToByte(snes_.apu().spc700().PSW));
689
690 ImGui::EndTable();
691 }
692
693 ImGui::EndChild();
694 ImGui::PopStyleColor();
695
696 // New Disassembly Viewer
697 if (ImGui::CollapsingHeader("Disassembly Viewer",
698 ImGuiTreeNodeFlags_DefaultOpen)) {
699 uint32_t current_pc = (static_cast<uint32_t>(snes_.cpu().PB) << 16) | snes_.cpu().PC;
700 auto& disasm = snes_.cpu().disassembly_viewer();
701 if (disasm.IsAvailable()) {
702 disasm.Render(current_pc, snes_.cpu().breakpoints_);
703 } else {
704 ImGui::TextColored(ConvertColorToImVec4(theme.error), "Disassembly viewer unavailable.");
705 }
706 }
707 } catch (const std::exception& e) {
708 // Ensure any pushed styles are popped on error
709 try {
710 ImGui::PopStyleColor();
711 } catch (...) {
712 // Ignore PopStyleColor errors
713 }
714 ImGui::Text("CPU Debugger Error: %s", e.what());
715 }
716}
717
719 // Delegate to UI layer
721}
722
724 // Delegate to UI layer
726}
727
729 const std::vector<InstructionEntry>& instruction_log) {
730 // Delegate to UI layer (legacy log deprecated)
731 ui::RenderCpuInstructionLog(this, instruction_log.size());
732}
733
735 // TODO: Create ui::RenderSaveStates() when save state system is implemented
736 auto& theme_manager = gui::ThemeManager::Get();
737 const auto& theme = theme_manager.GetCurrentTheme();
738
739 ImGui::TextColored(ConvertColorToImVec4(theme.warning),
740 ICON_MD_SAVE " Save States - Coming Soon");
741 ImGui::TextWrapped("Save state functionality will be implemented here.");
742}
743
745 // Delegate to the input manager UI
747}
748
750 // Delegate to UI layer
752}
753
754} // namespace emu
755} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:74
auto vector() const
Definition rom.h:210
bool is_loaded() const
Definition rom.h:200
bool * GetVisibilityFlag(size_t session_id, const std::string &base_card_id)
Get visibility flag pointer for a card.
bool ShouldBreakOnExecute(uint32_t pc, CpuType cpu)
Check if execution should break at this address.
void RemoveBreakpoint(uint32_t id)
Remove a breakpoint by ID.
void SetEnabled(uint32_t id, bool enabled)
Enable or disable a breakpoint.
std::vector< Breakpoint > GetAllBreakpoints() const
Get all breakpoints.
uint32_t AddBreakpoint(uint32_t address, Type type, CpuType cpu, const std::string &condition="", const std::string &description="")
Add a new breakpoint.
gfx::IRenderer * renderer()
Definition emulator.h:63
void Initialize(gfx::IRenderer *renderer, const std::vector< uint8_t > &rom_data)
Definition emulator.cc:61
void RenderModernCpuDebugger()
Definition emulator.cc:502
std::unique_ptr< audio::IAudioBackend > audio_backend_
Definition emulator.h:159
bool audio_stream_env_checked_
Definition emulator.h:170
void set_use_sdl_audio_stream(bool enabled)
Definition emulator.cc:54
void RenderMemoryViewer()
Definition emulator.cc:497
bool audio_stream_config_dirty_
Definition emulator.h:168
uint64_t count_frequency
Definition emulator.h:146
void RenderKeyboardConfig()
Definition emulator.cc:744
debug::DisassemblyViewer disassembly_viewer_
Definition emulator.h:176
std::vector< uint8_t > rom_data_
Definition emulator.h:178
void RenderPerformanceMonitor()
Definition emulator.cc:718
void RenderAIAgentPanel()
Definition emulator.cc:723
void RenderBreakpointList()
Definition emulator.cc:492
BreakpointManager breakpoint_manager_
Definition emulator.h:175
uint64_t last_count
Definition emulator.h:147
input::InputManager input_manager_
Definition emulator.h:181
editor::EditorCardRegistry * card_registry_
Definition emulator.h:184
void RenderCpuInstructionLog(const std::vector< InstructionEntry > &instructionLog)
Definition emulator.cc:728
void Run(Rom *rom)
Definition emulator.cc:118
void RenderEmulatorInterface()
Definition emulator.cc:363
int16_t * audio_buffer_
Definition emulator.h:155
gfx::IRenderer * renderer_
Definition emulator.h:165
auto mutable_cycles() -> uint64_t &
Definition snes.h:76
void SetSamples(int16_t *sample_data, int wanted_samples)
Definition snes.cc:738
void Reset(bool hard=false)
Definition snes.cc:58
void RunFrame()
Definition snes.cc:104
auto apu() -> Apu &
Definition snes.h:73
auto cpu() -> Cpu &
Definition snes.h:71
void Init(std::vector< uint8_t > &rom_data)
Definition snes.cc:39
auto memory() -> MemoryImpl &
Definition snes.h:74
void SetPixels(uint8_t *pixel_data)
Definition snes.cc:742
static std::unique_ptr< IAudioBackend > Create(BackendType type)
void RecordInstruction(uint32_t address, uint8_t opcode, const std::vector< uint8_t > &operands, const std::string &mnemonic, const std::string &operand_str)
Record an instruction execution.
virtual std::string GetBackendName() const =0
Get backend name for debugging.
void Poll(Snes *snes, int player=1)
bool Initialize(InputBackendFactory::BackendType type=InputBackendFactory::BackendType::SDL2)
Defines an abstract interface for all rendering operations.
Definition irenderer.h:35
virtual void UnlockTexture(TextureHandle texture)=0
virtual bool LockTexture(TextureHandle texture, SDL_Rect *rect, void **pixels, int *pitch)=0
virtual TextureHandle CreateTextureWithFormat(int width, int height, uint32_t format, int access)=0
Creates a new texture with a specific pixel format.
Draggable, dockable card for editor sub-windows.
bool Begin(bool *p_open=nullptr)
void SetDefaultSize(float width, float height)
static ThemeManager & Get()
#define ICON_MD_PAUSE
Definition icons.h:1387
#define ICON_MD_MEMORY
Definition icons.h:1193
#define ICON_MD_PLAY_ARROW
Definition icons.h:1477
#define ICON_MD_REFRESH
Definition icons.h:1570
#define ICON_MD_STOP
Definition icons.h:1860
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2074
#define ICON_MD_BUG_REPORT
Definition icons.h:325
#define ICON_MD_SPEED
Definition icons.h:1815
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1262
#define ICON_MD_ADD
Definition icons.h:84
#define ICON_MD_KEYBOARD
Definition icons.h:1026
#define ICON_MD_SKIP_NEXT
Definition icons.h:1771
#define ICON_MD_SAVE
Definition icons.h:1642
#define ICON_MD_DELETE
Definition icons.h:528
#define ICON_MD_AUDIO_FILE
Definition icons.h:210
#define ICON_MD_SMART_TOY
Definition icons.h:1779
#define LOG_ERROR(category, format,...)
Definition log.h:110
#define LOG_WARN(category, format,...)
Definition log.h:108
#define LOG_INFO(category, format,...)
Definition log.h:106
bool g_window_is_resizing
Definition window.cc:55
void RenderKeyboardConfig(input::InputManager *manager)
Render keyboard configuration UI.
void RenderPerformanceMonitor(Emulator *emu)
Performance metrics (FPS, frame time, audio status)
void RenderAIAgentPanel(Emulator *emu)
AI Agent panel for automated testing/gameplay.
void RenderSnesPpu(Emulator *emu)
SNES PPU output display.
void RenderNavBar(Emulator *emu)
Navigation bar with play/pause, step, reset controls.
void RenderBreakpointList(Emulator *emu)
Breakpoint list and management.
void RenderApuDebugger(Emulator *emu)
APU/Audio debugger with handshake tracker.
void RenderMemoryViewer(Emulator *emu)
Memory viewer/editor.
void RenderCpuInstructionLog(Emulator *emu, uint32_t log_size)
CPU instruction log (legacy, prefer DisassemblyViewer)
Main namespace for the application.
Definition controller.cc:20