yaze 0.2.0
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 <cstdint>
4#include <vector>
5
9#include "app/emu/snes.h"
10#include "app/gui/icons.h"
11#include "app/gui/input.h"
12#include "app/gui/zeml.h"
13#include "app/rom.h"
14#include "imgui/imgui.h"
15#include "imgui_memory_editor.h"
16
17namespace yaze {
18namespace app {
19namespace emu {
20
21namespace {
22bool ShouldDisplay(const InstructionEntry& entry, const char* filter) {
23 if (filter[0] == '\0') {
24 return true;
25 }
26
27 // Supported fields: address, opcode, operands
28 if (entry.operands.find(filter) != std::string::npos) {
29 return true;
30 }
31
32 if (absl::StrFormat("%06X", entry.address).find(filter) !=
33 std::string::npos) {
34 return true;
35 }
36
37 if (opcode_to_mnemonic.at(entry.opcode).find(filter) != std::string::npos) {
38 return true;
39 }
40
41 return false;
42}
43
44} // namespace
45
46using ImGui::NextColumn;
47using ImGui::SameLine;
48using ImGui::Separator;
49using ImGui::TableNextColumn;
50using ImGui::Text;
51
53 static bool loaded = false;
54 if (!snes_.running() && rom()->is_loaded()) {
55 ppu_texture_ = SDL_CreateTexture(core::Renderer::GetInstance().renderer(),
56 SDL_PIXELFORMAT_ARGB8888,
57 SDL_TEXTUREACCESS_STREAMING, 512, 480);
58 if (ppu_texture_ == NULL) {
59 printf("Failed to create texture: %s\n", SDL_GetError());
60 return;
61 }
62 rom_data_ = rom()->vector();
64 wanted_frames_ = 1.0 / (snes_.Memory().pal_timing() ? 50.0 : 60.0);
65 wanted_samples_ = 48000 / (snes_.Memory().pal_timing() ? 50 : 60);
66 loaded = true;
67
68 count_frequency = SDL_GetPerformanceFrequency();
69 last_count = SDL_GetPerformanceCounter();
70 time_adder = 0.0;
71 }
72
74
75 if (running_) {
77
78 uint64_t current_count = SDL_GetPerformanceCounter();
79 uint64_t delta = current_count - last_count;
80 last_count = current_count;
81 float seconds = delta / (float)count_frequency;
82 time_adder += seconds;
83 // allow 2 ms earlier, to prevent skipping due to being just below wanted
84 while (time_adder >= wanted_frames_ - 0.002) {
86
87 if (loaded) {
88 if (turbo_mode_) {
90 }
92
94 if (SDL_GetQueuedAudioSize(audio_device_) <= wanted_samples_ * 4 * 6) {
95 SDL_QueueAudio(audio_device_, audio_buffer_, wanted_samples_ * 4);
96 }
97
98 void* ppu_pixels_;
99 int ppu_pitch_;
100 if (SDL_LockTexture(ppu_texture_, NULL, &ppu_pixels_, &ppu_pitch_) !=
101 0) {
102 printf("Failed to lock texture: %s\n", SDL_GetError());
103 return;
104 }
105 snes_.SetPixels(static_cast<uint8_t*>(ppu_pixels_));
106 SDL_UnlockTexture(ppu_texture_);
107 }
108 }
109 }
110
112}
113
115 ImVec2 size = ImVec2(512, 480);
116 if (snes_.running()) {
117 ImGui::BeginChild("EmulatorOutput", ImVec2(0, 480), true,
118 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
119 ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
120 ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
121 ImGui::Image((ImTextureID)(intptr_t)ppu_texture_, size, ImVec2(0, 0),
122 ImVec2(1, 1));
123 ImGui::EndChild();
124
125 } else {
126 ImGui::Text("Emulator output not available.");
127 ImGui::BeginChild("EmulatorOutput", ImVec2(0, 480), true,
128 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
129 ImGui::SetCursorPosX(((ImGui::GetWindowSize().x * 0.5f) - size.x) * 0.5f);
130 ImGui::SetCursorPosY(((ImGui::GetWindowSize().y * 0.5f) - size.y) * 0.5f);
131 ImGui::Dummy(size);
132 ImGui::EndChild();
133 }
134 ImGui::Separator();
135}
136
138 std::string navbar_layout = R"(
139 BeginMenuBar {
140 BeginMenu title="Options" {
141 MenuItem title="Input" {}
142 MenuItem title="Audio" {}
143 MenuItem title="Video" {}
144 }
145 }
146 )";
147
148 static auto navbar_node = gui::zeml::Parse(navbar_layout);
149 gui::zeml::Render(navbar_node);
150
151 if (ImGui::Button(ICON_MD_PLAY_ARROW)) {
152 running_ = true;
153 }
154 if (ImGui::IsItemHovered()) {
155 ImGui::SetTooltip("Start Emulation");
156 }
157 SameLine();
158
159 if (ImGui::Button(ICON_MD_PAUSE)) {
160 running_ = false;
161 }
162 if (ImGui::IsItemHovered()) {
163 ImGui::SetTooltip("Pause Emulation");
164 }
165 SameLine();
166
167 if (ImGui::Button(ICON_MD_SKIP_NEXT)) {
168 // Step through Code logic
169 snes_.cpu().RunOpcode();
170 }
171 if (ImGui::IsItemHovered()) {
172 ImGui::SetTooltip("Step Through Code");
173 }
174 SameLine();
175
176 if (ImGui::Button(ICON_MD_REFRESH)) {
177 // Reset Emulator logic
178 snes_.Reset(true);
179 }
180 if (ImGui::IsItemHovered()) {
181 ImGui::SetTooltip("Reset Emulator");
182 }
183 SameLine();
184
185 if (ImGui::Button(ICON_MD_STOP)) {
186 // Stop Emulation logic
187 running_ = false;
188 }
189 if (ImGui::IsItemHovered()) {
190 ImGui::SetTooltip("Stop Emulation");
191 }
192 SameLine();
193
194 if (ImGui::Button(ICON_MD_SAVE)) {
195 // Save State logic
196 }
197 if (ImGui::IsItemHovered()) {
198 ImGui::SetTooltip("Save State");
199 }
200 SameLine();
201
202 if (ImGui::Button(ICON_MD_SYSTEM_UPDATE_ALT)) {
203 }
204 if (ImGui::IsItemHovered()) {
205 ImGui::SetTooltip("Load State");
206 }
207
208 // Additional elements
209 SameLine();
210 if (ImGui::Button(ICON_MD_SETTINGS)) {
211 // Settings logic
212 }
213 if (ImGui::IsItemHovered()) {
214 ImGui::SetTooltip("Settings");
215 }
216
217 static bool open_file = false;
218 SameLine();
219 if (ImGui::Button(ICON_MD_INFO)) {
220 open_file = true;
221
222 // About Debugger logic
223 }
224 if (ImGui::IsItemHovered()) {
225 ImGui::SetTooltip("About Debugger");
226 }
227 SameLine();
228 ImGui::Checkbox("Logging", snes_.cpu().mutable_log_instructions());
229
230 SameLine();
231 ImGui::Checkbox("Turbo", &turbo_mode_);
232
233 static bool show_memory_viewer = false;
234
235 SameLine();
236 if (ImGui::Button(ICON_MD_MEMORY)) {
237 show_memory_viewer = !show_memory_viewer;
238 }
239 if (ImGui::IsItemHovered()) {
240 ImGui::SetTooltip("Memory Viewer");
241 }
242
243 if (show_memory_viewer) {
244 ImGui::Begin("Memory Viewer", &show_memory_viewer);
246 ImGui::End();
247 }
248
249 if (open_file) {
251 if (!file_name.empty()) {
252 std::ifstream file(file_name, std::ios::binary);
253 // Load the data directly into rom_data
254 rom_data_.assign(std::istreambuf_iterator<char>(file),
255 std::istreambuf_iterator<char>());
257 open_file = false;
258 }
259 }
260}
261
263 // Handle user input events
264 if (ImGui::IsKeyPressed(keybindings_.a_button)) {
265 snes_.SetButtonState(1, 0, true);
266 }
267
268 if (ImGui::IsKeyPressed(keybindings_.b_button)) {
269 snes_.SetButtonState(1, 1, true);
270 }
271
272 if (ImGui::IsKeyPressed(keybindings_.select_button)) {
273 snes_.SetButtonState(1, 2, true);
274 }
275
276 if (ImGui::IsKeyPressed(keybindings_.start_button)) {
277 snes_.SetButtonState(1, 3, true);
278 }
279
280 if (ImGui::IsKeyPressed(keybindings_.up_button)) {
281 snes_.SetButtonState(1, 4, true);
282 }
283
284 if (ImGui::IsKeyPressed(keybindings_.down_button)) {
285 snes_.SetButtonState(1, 5, true);
286 }
287
288 if (ImGui::IsKeyPressed(keybindings_.left_button)) {
289 snes_.SetButtonState(1, 6, true);
290 }
291
292 if (ImGui::IsKeyPressed(keybindings_.right_button)) {
293 snes_.SetButtonState(1, 7, true);
294 }
295
296 if (ImGui::IsKeyPressed(keybindings_.x_button)) {
297 snes_.SetButtonState(1, 8, true);
298 }
299
300 if (ImGui::IsKeyPressed(keybindings_.y_button)) {
301 snes_.SetButtonState(1, 9, true);
302 }
303
304 if (ImGui::IsKeyPressed(keybindings_.l_button)) {
305 snes_.SetButtonState(1, 10, true);
306 }
307
308 if (ImGui::IsKeyPressed(keybindings_.r_button)) {
309 snes_.SetButtonState(1, 11, true);
310 }
311
312 if (ImGui::IsKeyReleased(keybindings_.a_button)) {
313 snes_.SetButtonState(1, 0, false);
314 }
315
316 if (ImGui::IsKeyReleased(keybindings_.b_button)) {
317 snes_.SetButtonState(1, 1, false);
318 }
319
320 if (ImGui::IsKeyReleased(keybindings_.select_button)) {
321 snes_.SetButtonState(1, 2, false);
322 }
323
324 if (ImGui::IsKeyReleased(keybindings_.start_button)) {
325 snes_.SetButtonState(1, 3, false);
326 }
327
328 if (ImGui::IsKeyReleased(keybindings_.up_button)) {
329 snes_.SetButtonState(1, 4, false);
330 }
331
332 if (ImGui::IsKeyReleased(keybindings_.down_button)) {
333 snes_.SetButtonState(1, 5, false);
334 }
335
336 if (ImGui::IsKeyReleased(keybindings_.left_button)) {
337 snes_.SetButtonState(1, 6, false);
338 }
339
340 if (ImGui::IsKeyReleased(keybindings_.right_button)) {
341 snes_.SetButtonState(1, 7, false);
342 }
343
344 if (ImGui::IsKeyReleased(keybindings_.x_button)) {
345 snes_.SetButtonState(1, 8, false);
346 }
347
348 if (ImGui::IsKeyReleased(keybindings_.y_button)) {
349 snes_.SetButtonState(1, 9, false);
350 }
351
352 if (ImGui::IsKeyReleased(keybindings_.l_button)) {
353 snes_.SetButtonState(1, 10, false);
354 }
355
356 if (ImGui::IsKeyReleased(keybindings_.r_button)) {
357 snes_.SetButtonState(1, 11, false);
358 }
359}
360
362 if (ImGui::Button("Set SPC PC")) {
363 snes_.apu().spc700().PC = 0xFFEF;
364 }
365 Separator();
366 Text("Breakpoints");
367 Separator();
368 static char breakpoint_input[10] = "";
369 static int current_memory_mode = 0;
370
371 static bool read_mode = false;
372 static bool write_mode = false;
373 static bool execute_mode = false;
374
375 if (ImGui::Combo("##TypeOfMemory", &current_memory_mode, "PRG\0RAM\0")) {
376 }
377
378 ImGui::Checkbox("Read", &read_mode);
379 SameLine();
380 ImGui::Checkbox("Write", &write_mode);
381 SameLine();
382 ImGui::Checkbox("Execute", &execute_mode);
383
384 // Breakpoint input fields and buttons
385 if (ImGui::InputText("##BreakpointInput", breakpoint_input, 10,
386 ImGuiInputTextFlags_EnterReturnsTrue)) {
387 int breakpoint = std::stoi(breakpoint_input, nullptr, 16);
388 snes_.cpu().SetBreakpoint(breakpoint);
389 memset(breakpoint_input, 0, sizeof(breakpoint_input));
390 }
391 SameLine();
392 if (ImGui::Button("Add")) {
393 int breakpoint = std::stoi(breakpoint_input, nullptr, 16);
394 snes_.cpu().SetBreakpoint(breakpoint);
395 memset(breakpoint_input, 0, sizeof(breakpoint_input));
396 }
397 SameLine();
398 if (ImGui::Button("Clear")) {
399 snes_.cpu().ClearBreakpoints();
400 }
401 Separator();
402 auto breakpoints = snes_.cpu().GetBreakpoints();
403 if (!breakpoints.empty()) {
404 Text("Breakpoints:");
405 ImGui::BeginChild("BreakpointsList", ImVec2(0, 100), true);
406 for (auto breakpoint : breakpoints) {
407 if (ImGui::Selectable(absl::StrFormat("0x%04X", breakpoint).c_str())) {
408 // Jump to breakpoint
409 // snes_.cpu().JumpToBreakpoint(breakpoint);
410 }
411 }
412 ImGui::EndChild();
413 }
414 Separator();
415 gui::InputHexByte("PB", &manual_pb_, 50.f);
416 gui::InputHexWord("PC", &manual_pc_, 75.f);
417 if (ImGui::Button("Set Current Address")) {
418 snes_.cpu().PC = manual_pc_;
419 snes_.cpu().PB = manual_pb_;
420 }
421}
422
424 static MemoryEditor ram_edit;
425 static MemoryEditor aram_edit;
426 static MemoryEditor mem_edit;
427
428 if (ImGui::BeginTable("MemoryViewerTable", 4,
429 ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
430 ImGui::TableSetupColumn("Bookmarks");
431 ImGui::TableSetupColumn("RAM");
432 ImGui::TableSetupColumn("ARAM");
433 ImGui::TableSetupColumn("ROM");
434 ImGui::TableHeadersRow();
435
436 TableNextColumn();
437 if (ImGui::CollapsingHeader("Bookmarks", ImGuiTreeNodeFlags_DefaultOpen)) {
438 // Input for adding a new bookmark
439 static char nameBuf[256];
440 static uint64_t uint64StringBuf;
441 ImGui::InputText("Name", nameBuf, IM_ARRAYSIZE(nameBuf));
442 gui::InputHex("Address", &uint64StringBuf);
443 if (ImGui::Button("Add Bookmark")) {
444 bookmarks.push_back({nameBuf, uint64StringBuf});
445 memset(nameBuf, 0, sizeof(nameBuf));
446 uint64StringBuf = 0;
447 }
448
449 // Tree view of bookmarks
450 for (const auto& bookmark : bookmarks) {
451 if (ImGui::TreeNode(bookmark.name.c_str(), ICON_MD_STAR)) {
452 auto bookmark_string = absl::StrFormat(
453 "%s: 0x%08X", bookmark.name.c_str(), bookmark.value);
454 if (ImGui::Selectable(bookmark_string.c_str())) {
455 mem_edit.GotoAddrAndHighlight(static_cast<ImU64>(bookmark.value),
456 1);
457 }
458 SameLine();
459 if (ImGui::Button("Delete")) {
460 // Logic to delete the bookmark
461 bookmarks.erase(std::remove_if(bookmarks.begin(), bookmarks.end(),
462 [&](const Bookmark& b) {
463 return b.name == bookmark.name &&
464 b.value == bookmark.value;
465 }),
466 bookmarks.end());
467 }
468 ImGui::TreePop();
469 }
470 }
471 }
472
473 TableNextColumn();
474 if (ImGui::BeginChild("RAM", ImVec2(0, 0), true,
475 ImGuiWindowFlags_NoMove |
476 ImGuiWindowFlags_NoScrollbar |
477 ImGuiWindowFlags_NoScrollWithMouse)) {
478 ram_edit.DrawContents((void*)snes_.get_ram(), 0x20000);
479 ImGui::EndChild();
480 }
481
482 TableNextColumn();
483 if (ImGui::BeginChild("ARAM", ImVec2(0, 0), true,
484 ImGuiWindowFlags_NoMove |
485 ImGuiWindowFlags_NoScrollbar |
486 ImGuiWindowFlags_NoScrollWithMouse)) {
487 aram_edit.DrawContents((void*)snes_.apu().ram.data(),
488 snes_.apu().ram.size());
489 ImGui::EndChild();
490 }
491
492 TableNextColumn();
493 if (ImGui::BeginChild("ROM", ImVec2(0, 0), true,
494 ImGuiWindowFlags_NoMove |
495 ImGuiWindowFlags_NoScrollbar |
496 ImGuiWindowFlags_NoScrollWithMouse)) {
497 mem_edit.DrawContents((void*)snes_.Memory().rom_.data(),
498 snes_.Memory().rom_.size());
499 ImGui::EndChild();
500 }
501
502 ImGui::EndTable();
503 }
504}
505
507 const std::vector<InstructionEntry>& instruction_log) {
508 if (ImGui::CollapsingHeader("Instruction Log",
509 ImGuiTreeNodeFlags_DefaultOpen)) {
510 // Filtering options
511 static char filter[256];
512 ImGui::InputText("Filter", filter, IM_ARRAYSIZE(filter));
513
514 // Instruction list
515 ImGui::BeginChild("InstructionList", ImVec2(0, 0), ImGuiChildFlags_None);
516 for (const auto& entry : instruction_log) {
517 if (ShouldDisplay(entry, filter)) {
518 if (ImGui::Selectable(
519 absl::StrFormat("%06X:", entry.address).c_str())) {
520 // Logic to handle click (e.g., jump to address, set breakpoint)
521 }
522
523 ImGui::SameLine();
524
525 ImVec4 color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
526 ImGui::TextColored(color, "%s",
527 opcode_to_mnemonic.at(entry.opcode).c_str());
528 ImVec4 operand_color = ImVec4(0.7f, 0.5f, 0.3f, 1.0f);
529 ImGui::SameLine();
530 ImGui::TextColored(operand_color, "%s", entry.operands.c_str());
531 }
532 }
533 // Jump to the bottom of the child scrollbar
534 if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
535 ImGui::SetScrollHereY(1.0f);
536 }
537
538 ImGui::EndChild();
539 }
540}
541
542} // namespace emu
543} // namespace app
544} // namespace yaze
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath.
static Renderer & GetInstance()
Definition renderer.h:27
SDL_AudioDeviceID audio_device_
Definition emulator.h:180
std::vector< uint8_t > rom_data_
Definition emulator.h:185
std::vector< Bookmark > bookmarks
Definition emulator.h:157
void RenderCpuInstructionLog(const std::vector< InstructionEntry > &instructionLog)
Definition emulator.cc:506
EmulatorKeybindings keybindings_
Definition emulator.h:187
gui::zeml::Node emulator_node_
Definition emulator.h:189
SDL_Texture * ppu_texture_
Definition emulator.h:183
auto apu() -> audio::Apu &
Definition snes.h:71
void Init(std::vector< uint8_t > &rom_data)
Definition snes.cc:36
auto Memory() -> memory::MemoryImpl &
Definition snes.h:72
void SetSamples(int16_t *sample_data, int wanted_samples)
Definition snes.cc:545
auto cpu() -> Cpu &
Definition snes.h:69
void SetButtonState(int player, int button, bool pressed)
Definition snes.cc:551
auto get_ram() -> uint8_t *
Definition snes.h:73
void SetPixels(uint8_t *pixel_data)
Definition snes.cc:549
bool running() const
Definition snes.h:68
void Reset(bool hard=false)
Definition snes.cc:48
const std::unordered_map< uint8_t, std::string > opcode_to_mnemonic
Definition opcodes.h:7
#define ICON_MD_PAUSE
Definition icons.h:1387
#define ICON_MD_SETTINGS
Definition icons.h:1697
#define ICON_MD_INFO
Definition icons.h:991
#define ICON_MD_MEMORY
Definition icons.h:1193
#define ICON_MD_STAR
Definition icons.h:1843
#define ICON_MD_PLAY_ARROW
Definition icons.h:1477
#define ICON_MD_REFRESH
Definition icons.h:1570
#define ICON_MD_STOP
Definition icons.h:1857
#define ICON_MD_SYSTEM_UPDATE_ALT
Definition icons.h:1923
#define ICON_MD_SKIP_NEXT
Definition icons.h:1768
#define ICON_MD_SAVE
Definition icons.h:1642
bool ShouldDisplay(const InstructionEntry &entry, const char *filter)
Definition emulator.cc:22
Node Parse(const std::string &yazon_input, const std::map< std::string, void * > &data_bindings)
Parse a zeml string.
Definition zeml.cc:360
void Render(Node &node)
Render a zeml tree.
Definition zeml.cc:381
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:175
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:142
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:161
Definition common.cc:22