43int main(
int argc,
char** argv) {
44 absl::InitializeSymbolizer(argv[0]);
46 absl::FailureSignalHandlerOptions options;
47 options.symbolize_stacktrace =
true;
48 options.use_alternate_stack =
50 options.alarm_on_failure_secs =
52 options.call_previous_handler =
true;
53 absl::InstallFailureSignalHandler(options);
55 absl::ParseCommandLine(argc, argv);
57 if (absl::GetFlag(FLAGS_emu_no_gui)) {
59 if (!rom.
LoadFromFile(absl::GetFlag(FLAGS_emu_rom)).ok()) {
64 std::vector<uint8_t> rom_data = rom.
vector();
67 if (!absl::GetFlag(FLAGS_emu_load_state).empty()) {
68 auto status = snes.
loadState(absl::GetFlag(FLAGS_emu_load_state));
70 printf(
"Failed to load state: %s\n",
71 std::string(status.message()).c_str());
76 for (
int i = 0; i < absl::GetFlag(FLAGS_emu_frames); ++i) {
80 if (!absl::GetFlag(FLAGS_emu_dump_state).empty()) {
81 auto status = snes.
saveState(absl::GetFlag(FLAGS_emu_dump_state));
83 printf(
"Failed to save state: %s\n",
84 std::string(status.message()).c_str());
97 config.
title =
"Yaze Emulator";
104 if (!window_backend->Initialize(config).ok()) {
105 printf(
"Failed to initialize window backend\n");
111 if (!window_backend->InitializeRenderer(renderer.get())) {
112 printf(
"Failed to initialize renderer\n");
113 window_backend->Shutdown();
118 if (!window_backend->InitializeImGui(renderer.get()).ok()) {
119 printf(
"Failed to initialize ImGui\n");
120 window_backend->Shutdown();
135 constexpr int kNativeSampleRate = 32040;
137 if (!audio_backend->Initialize(audio_config)) {
138 printf(
"Failed to initialize audio backend\n");
141 printf(
"Audio initialized: %s\n", audio_backend->GetBackendName().c_str());
144 if (audio_backend->SupportsAudioStream()) {
145 audio_backend->SetAudioStreamResampling(
true, kNativeSampleRate, 2);
146 printf(
"Audio resampling enabled: %dHz -> %dHz\n", kNativeSampleRate,
152 std::unique_ptr<int16_t[]> audio_buffer(
156 void* ppu_texture = renderer->CreateTexture(512, 480);
158 printf(
"SDL_CreateTexture failed: %s\n", SDL_GetError());
159 window_backend->Shutdown();
165 std::vector<uint8_t> rom_data_;
177 const int max_frames = absl::GetFlag(FLAGS_emu_max_frames);
178 bool fix_red_tint = absl::GetFlag(FLAGS_emu_fix_red_tint);
181 const uint64_t count_frequency = SDL_GetPerformanceFrequency();
182 uint64_t last_count = SDL_GetPerformanceCounter();
183 double time_adder = 0.0;
184 double wanted_frame_time = 0.0;
185 int wanted_samples = 0;
189 std::string rom_path = absl::GetFlag(FLAGS_emu_rom);
190 if (rom_path.empty()) {
191 rom_path =
"assets/zelda3.sfc";
195 printf(
"Failed to load ROM: %s\n", rom_path.c_str());
200 printf(
"Loaded ROM: %s (%zu bytes)\n", rom_path.c_str(), rom_.
size());
201 rom_data_ = rom_.
vector();
202 snes_.
Init(rom_data_);
205 const bool is_pal = snes_.
memory().pal_timing();
206 const double refresh_rate = is_pal ? 50.0 : 60.0;
207 wanted_frame_time = 1.0 / refresh_rate;
210 wanted_samples = kNativeSampleRate /
static_cast<int>(refresh_rate);
212 printf(
"Emulator initialized: %s mode (%.1f Hz)\n", is_pal ?
"PAL" :
"NTSC",
218 while (window_backend->PollEvent(event)) {
219 switch (event.
type) {
222 rom_data_ = rom_.
vector();
223 snes_.
Init(rom_data_);
225 const bool is_pal = snes_.
memory().pal_timing();
226 const double refresh_rate = is_pal ? 50.0 : 60.0;
227 wanted_frame_time = 1.0 / refresh_rate;
230 wanted_samples = kNativeSampleRate /
static_cast<int>(refresh_rate);
232 printf(
"Loaded new ROM via drag-and-drop: %s\n",
247 const uint64_t current_count = SDL_GetPerformanceCounter();
248 const uint64_t delta = current_count - last_count;
249 last_count = current_count;
250 const double seconds =
251 static_cast<double>(delta) /
static_cast<double>(count_frequency);
252 time_adder += seconds;
255 while (time_adder >= wanted_frame_time - 0.002) {
256 time_adder -= wanted_frame_time;
260 input_manager_.
Poll(&snes_, 1);
265 static uint16_t last_cpu_pc = 0;
266 static int stuck_count = 0;
267 uint16_t current_cpu_pc = snes_.
cpu().PC;
269 if (current_cpu_pc == last_cpu_pc && current_cpu_pc >= 0x88B0 &&
270 current_cpu_pc <= 0x88C0) {
272 if (stuck_count > 180 && frame_count % 60 == 0) {
274 "[WARNING] CPU stuck at $%02X:%04X for %d frames (APU "
276 snes_.
cpu().PB, current_cpu_pc, stuck_count);
281 last_cpu_pc = current_cpu_pc;
284 if (frame_count % 60 == 0) {
285 printf(
"[Frame %d] CPU=$%02X:%04X SPC=$%04X APU_cycles=%llu\n",
286 frame_count, snes_.
cpu().PB, snes_.
cpu().PC,
287 snes_.
apu().spc700().PC, snes_.
apu().GetCycles());
291 if (max_frames > 0 && frame_count >= max_frames) {
292 printf(
"\n[EMULATOR] Reached max frames (%d), shutting down...\n",
294 printf(
"[EMULATOR] Final state: CPU=$%02X:%04X SPC=$%04X\n",
295 snes_.
cpu().PB, snes_.
cpu().PC, snes_.
apu().spc700().PC);
301 snes_.
SetSamples(audio_buffer.get(), wanted_samples);
303 if (audio_backend && audio_backend->IsInitialized()) {
304 auto status = audio_backend->GetStatus();
306 if (status.queued_frames <=
307 static_cast<uint32_t
>(wanted_samples * 6)) {
310 if (!audio_backend->QueueSamplesNative(
311 audio_buffer.get(), wanted_samples, 2, kNativeSampleRate)) {
313 if (audio_backend->SupportsAudioStream()) {
314 audio_backend->SetAudioStreamResampling(
true, kNativeSampleRate,
316 audio_backend->QueueSamplesNative(
317 audio_buffer.get(), wanted_samples, 2, kNativeSampleRate);
324 void* ppu_pixels =
nullptr;
326 if (renderer->LockTexture(ppu_texture,
nullptr, &ppu_pixels,
328 uint8_t* pixels =
static_cast<uint8_t*
>(ppu_pixels);
336 for (
int i = 0; i < 512 * 480; ++i) {
337 uint8_t b = pixels[i * 4 + 0];
338 uint8_t r = pixels[i * 4 + 2];
339 pixels[i * 4 + 0] = r;
340 pixels[i * 4 + 2] = b;
344 renderer->UnlockTexture(ppu_texture);
350 window_backend->NewImGuiFrame();
353 ImGui::Begin(
"Emulator Stats");
354 ImGui::Text(
"Frame: %d", frame_count);
355 ImGui::Text(
"FPS: %.1f", ImGui::GetIO().Framerate);
356 ImGui::Checkbox(
"Fix Red Tint", &fix_red_tint);
359 ImGui::Text(
"CPU PC: $%02X:%04X", snes_.
cpu().PB, snes_.
cpu().PC);
360 ImGui::Text(
"SPC PC: $%04X", snes_.
apu().spc700().PC);
368 renderer->RenderCopy(ppu_texture,
nullptr,
nullptr);
371 window_backend->RenderImGui(renderer.get());
375#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
381 printf(
"\n[EMULATOR] Shutting down...\n");
385 renderer->DestroyTexture(ppu_texture);
386 ppu_texture =
nullptr;
391 audio_backend->Shutdown();
395 window_backend->ShutdownImGui();
396 renderer->Shutdown();
397 window_backend->Shutdown();
399 printf(
"[EMULATOR] Shutdown complete.\n");