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((void*)ppu_texture_, size, ImVec2(0, 0), ImVec2(1, 1));
122 ImGui::EndChild();
123
124 } else {
125 ImGui::Text("Emulator output not available.");
126 ImGui::BeginChild("EmulatorOutput", ImVec2(0, 480), true,
127 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
128 ImGui::SetCursorPosX(((ImGui::GetWindowSize().x * 0.5f) - size.x) * 0.5f);
129 ImGui::SetCursorPosY(((ImGui::GetWindowSize().y * 0.5f) - size.y) * 0.5f);
130 ImGui::Dummy(size);
131 ImGui::EndChild();
132 }
133 ImGui::Separator();
134}
135
137 std::string navbar_layout = R"(
138 BeginMenuBar {
139 BeginMenu title="Options" {
140 MenuItem title="Input" {}
141 MenuItem title="Audio" {}
142 MenuItem title="Video" {}
143 }
144 }
145 )";
146
147 static auto navbar_node = gui::zeml::Parse(navbar_layout);
148 gui::zeml::Render(navbar_node);
149
150 if (ImGui::Button(ICON_MD_PLAY_ARROW)) {
151 running_ = true;
152 }
153 if (ImGui::IsItemHovered()) {
154 ImGui::SetTooltip("Start Emulation");
155 }
156 SameLine();
157
158 if (ImGui::Button(ICON_MD_PAUSE)) {
159 running_ = false;
160 }
161 if (ImGui::IsItemHovered()) {
162 ImGui::SetTooltip("Pause Emulation");
163 }
164 SameLine();
165
166 if (ImGui::Button(ICON_MD_SKIP_NEXT)) {
167 // Step through Code logic
168 snes_.cpu().RunOpcode();
169 }
170 if (ImGui::IsItemHovered()) {
171 ImGui::SetTooltip("Step Through Code");
172 }
173 SameLine();
174
175 if (ImGui::Button(ICON_MD_REFRESH)) {
176 // Reset Emulator logic
177 snes_.Reset(true);
178 }
179 if (ImGui::IsItemHovered()) {
180 ImGui::SetTooltip("Reset Emulator");
181 }
182 SameLine();
183
184 if (ImGui::Button(ICON_MD_STOP)) {
185 // Stop Emulation logic
186 running_ = false;
187 }
188 if (ImGui::IsItemHovered()) {
189 ImGui::SetTooltip("Stop Emulation");
190 }
191 SameLine();
192
193 if (ImGui::Button(ICON_MD_SAVE)) {
194 // Save State logic
195 }
196 if (ImGui::IsItemHovered()) {
197 ImGui::SetTooltip("Save State");
198 }
199 SameLine();
200
201 if (ImGui::Button(ICON_MD_SYSTEM_UPDATE_ALT)) {
202 }
203 if (ImGui::IsItemHovered()) {
204 ImGui::SetTooltip("Load State");
205 }
206
207 // Additional elements
208 SameLine();
209 if (ImGui::Button(ICON_MD_SETTINGS)) {
210 // Settings logic
211 }
212 if (ImGui::IsItemHovered()) {
213 ImGui::SetTooltip("Settings");
214 }
215
216 static bool open_file = false;
217 SameLine();
218 if (ImGui::Button(ICON_MD_INFO)) {
219 open_file = true;
220
221 // About Debugger logic
222 }
223 if (ImGui::IsItemHovered()) {
224 ImGui::SetTooltip("About Debugger");
225 }
226 SameLine();
227 ImGui::Checkbox("Logging", snes_.cpu().mutable_log_instructions());
228
229 SameLine();
230 ImGui::Checkbox("Turbo", &turbo_mode_);
231
232 static bool show_memory_viewer = false;
233
234 SameLine();
235 if (ImGui::Button(ICON_MD_MEMORY)) {
236 show_memory_viewer = !show_memory_viewer;
237 }
238 if (ImGui::IsItemHovered()) {
239 ImGui::SetTooltip("Memory Viewer");
240 }
241
242 if (show_memory_viewer) {
243 ImGui::Begin("Memory Viewer", &show_memory_viewer);
245 ImGui::End();
246 }
247
248 if (open_file) {
250 if (!file_name.empty()) {
251 std::ifstream file(file_name, std::ios::binary);
252 // Load the data directly into rom_data
253 rom_data_.assign(std::istreambuf_iterator<char>(file),
254 std::istreambuf_iterator<char>());
256 open_file = false;
257 }
258 }
259}
260
262 // Handle user input events
263 // ...
264}
265
267 if (ImGui::Button("Set SPC PC")) {
268 snes_.apu().spc700().PC = 0xFFEF;
269 }
270 Separator();
271 Text("Breakpoints");
272 Separator();
273 static char breakpoint_input[10] = "";
274 static int current_memory_mode = 0;
275
276 static bool read_mode = false;
277 static bool write_mode = false;
278 static bool execute_mode = false;
279
280 if (ImGui::Combo("##TypeOfMemory", &current_memory_mode, "PRG\0RAM\0")) {
281 }
282
283 ImGui::Checkbox("Read", &read_mode);
284 SameLine();
285 ImGui::Checkbox("Write", &write_mode);
286 SameLine();
287 ImGui::Checkbox("Execute", &execute_mode);
288
289 // Breakpoint input fields and buttons
290 if (ImGui::InputText("##BreakpointInput", breakpoint_input, 10,
291 ImGuiInputTextFlags_EnterReturnsTrue)) {
292 int breakpoint = std::stoi(breakpoint_input, nullptr, 16);
293 snes_.cpu().SetBreakpoint(breakpoint);
294 memset(breakpoint_input, 0, sizeof(breakpoint_input));
295 }
296 SameLine();
297 if (ImGui::Button("Add")) {
298 int breakpoint = std::stoi(breakpoint_input, nullptr, 16);
299 snes_.cpu().SetBreakpoint(breakpoint);
300 memset(breakpoint_input, 0, sizeof(breakpoint_input));
301 }
302 SameLine();
303 if (ImGui::Button("Clear")) {
304 snes_.cpu().ClearBreakpoints();
305 }
306 Separator();
307 auto breakpoints = snes_.cpu().GetBreakpoints();
308 if (!breakpoints.empty()) {
309 Text("Breakpoints:");
310 ImGui::BeginChild("BreakpointsList", ImVec2(0, 100), true);
311 for (auto breakpoint : breakpoints) {
312 if (ImGui::Selectable(absl::StrFormat("0x%04X", breakpoint).c_str())) {
313 // Jump to breakpoint
314 // snes_.cpu().JumpToBreakpoint(breakpoint);
315 }
316 }
317 ImGui::EndChild();
318 }
319 Separator();
320 gui::InputHexByte("PB", &manual_pb_, 50.f);
321 gui::InputHexWord("PC", &manual_pc_, 75.f);
322 if (ImGui::Button("Set Current Address")) {
323 snes_.cpu().PC = manual_pc_;
324 snes_.cpu().PB = manual_pb_;
325 }
326}
327
329 static MemoryEditor ram_edit;
330 static MemoryEditor aram_edit;
331 static MemoryEditor mem_edit;
332
333 if (ImGui::BeginTable("MemoryViewerTable", 4,
334 ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
335 ImGui::TableSetupColumn("Bookmarks");
336 ImGui::TableSetupColumn("RAM");
337 ImGui::TableSetupColumn("ARAM");
338 ImGui::TableSetupColumn("ROM");
339 ImGui::TableHeadersRow();
340
341 TableNextColumn();
342 if (ImGui::CollapsingHeader("Bookmarks", ImGuiTreeNodeFlags_DefaultOpen)) {
343 // Input for adding a new bookmark
344 static char nameBuf[256];
345 static uint64_t uint64StringBuf;
346 ImGui::InputText("Name", nameBuf, IM_ARRAYSIZE(nameBuf));
347 gui::InputHex("Address", &uint64StringBuf);
348 if (ImGui::Button("Add Bookmark")) {
349 bookmarks.push_back({nameBuf, uint64StringBuf});
350 memset(nameBuf, 0, sizeof(nameBuf));
351 uint64StringBuf = 0;
352 }
353
354 // Tree view of bookmarks
355 for (const auto& bookmark : bookmarks) {
356 if (ImGui::TreeNode(bookmark.name.c_str(), ICON_MD_STAR)) {
357 auto bookmark_string = absl::StrFormat(
358 "%s: 0x%08X", bookmark.name.c_str(), bookmark.value);
359 if (ImGui::Selectable(bookmark_string.c_str())) {
360 mem_edit.GotoAddrAndHighlight(static_cast<ImU64>(bookmark.value),
361 1);
362 }
363 SameLine();
364 if (ImGui::Button("Delete")) {
365 // Logic to delete the bookmark
366 bookmarks.erase(std::remove_if(bookmarks.begin(), bookmarks.end(),
367 [&](const Bookmark& b) {
368 return b.name == bookmark.name &&
369 b.value == bookmark.value;
370 }),
371 bookmarks.end());
372 }
373 ImGui::TreePop();
374 }
375 }
376 }
377
378 TableNextColumn();
379 if (ImGui::BeginChild("RAM", ImVec2(0, 0), true,
380 ImGuiWindowFlags_NoMove |
381 ImGuiWindowFlags_NoScrollbar |
382 ImGuiWindowFlags_NoScrollWithMouse)) {
383 ram_edit.DrawContents((void*)snes_.get_ram(), 0x20000);
384 ImGui::EndChild();
385 }
386
387 TableNextColumn();
388 if (ImGui::BeginChild("ARAM", ImVec2(0, 0), true,
389 ImGuiWindowFlags_NoMove |
390 ImGuiWindowFlags_NoScrollbar |
391 ImGuiWindowFlags_NoScrollWithMouse)) {
392 aram_edit.DrawContents((void*)snes_.apu().ram.data(),
393 snes_.apu().ram.size());
394 ImGui::EndChild();
395 }
396
397 TableNextColumn();
398 if (ImGui::BeginChild("ROM", ImVec2(0, 0), true,
399 ImGuiWindowFlags_NoMove |
400 ImGuiWindowFlags_NoScrollbar |
401 ImGuiWindowFlags_NoScrollWithMouse)) {
402 mem_edit.DrawContents((void*)snes_.Memory().rom_.data(),
403 snes_.Memory().rom_.size());
404 ImGui::EndChild();
405 }
406
407 ImGui::EndTable();
408 }
409}
410
412 const std::vector<InstructionEntry>& instruction_log) {
413 if (ImGui::CollapsingHeader("Instruction Log",
414 ImGuiTreeNodeFlags_DefaultOpen)) {
415 // Filtering options
416 static char filter[256];
417 ImGui::InputText("Filter", filter, IM_ARRAYSIZE(filter));
418
419 // Instruction list
420 ImGui::BeginChild("InstructionList", ImVec2(0, 0), ImGuiChildFlags_None);
421 for (const auto& entry : instruction_log) {
422 if (ShouldDisplay(entry, filter)) {
423 if (ImGui::Selectable(
424 absl::StrFormat("%06X:", entry.address).c_str())) {
425 // Logic to handle click (e.g., jump to address, set breakpoint)
426 }
427
428 ImGui::SameLine();
429
430 ImVec4 color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
431 ImGui::TextColored(color, "%s",
432 opcode_to_mnemonic.at(entry.opcode).c_str());
433 ImVec4 operand_color = ImVec4(0.7f, 0.5f, 0.3f, 1.0f);
434 ImGui::SameLine();
435 ImGui::TextColored(operand_color, "%s", entry.operands.c_str());
436 }
437 }
438 // Jump to the bottom of the child scrollbar
439 if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
440 ImGui::SetScrollHereY(1.0f);
441 }
442
443 ImGui::EndChild();
444 }
445}
446
447} // namespace emu
448} // namespace app
449} // namespace yaze
static std::string ShowOpenFileDialog()
static Renderer & GetInstance()
Definition renderer.h:30
SDL_AudioDeviceID audio_device_
Definition emulator.h:141
std::vector< uint8_t > rom_data_
Definition emulator.h:146
std::vector< Bookmark > bookmarks
Definition emulator.h:118
void RenderCpuInstructionLog(const std::vector< InstructionEntry > &instructionLog)
Definition emulator.cc:411
gui::zeml::Node emulator_node_
Definition emulator.h:148
SDL_Texture * ppu_texture_
Definition emulator.h:144
auto apu() -> audio::Apu &
Definition snes.h:72
void Init(std::vector< uint8_t > &rom_data)
Definition snes.cc:37
auto Memory() -> memory::MemoryImpl &
Definition snes.h:73
void SetSamples(int16_t *sample_data, int wanted_samples)
Definition snes.cc:546
auto cpu() -> Cpu &
Definition snes.h:70
auto get_ram() -> uint8_t *
Definition snes.h:74
void SetPixels(uint8_t *pixel_data)
Definition snes.cc:550
bool running() const
Definition snes.h:69
void Reset(bool hard=false)
Definition snes.cc:49
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:361
void Render(Node &node)
Render a zeml tree.
Definition zeml.cc:382
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:176
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:143
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:162
Definition common.cc:21