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