Emulator housekeeping
This commit is contained in:
@@ -155,9 +155,9 @@ void Emulator::Run() {
|
|||||||
if (running_) {
|
if (running_) {
|
||||||
HandleEvents();
|
HandleEvents();
|
||||||
UpdateEmulator();
|
UpdateEmulator();
|
||||||
RenderEmulator();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderEmulator();
|
||||||
if (debugger_) {
|
if (debugger_) {
|
||||||
RenderDebugger();
|
RenderDebugger();
|
||||||
}
|
}
|
||||||
@@ -166,11 +166,25 @@ void Emulator::Run() {
|
|||||||
void Emulator::RenderEmulator() {
|
void Emulator::RenderEmulator() {
|
||||||
ImVec2 size = ImVec2(320, 240);
|
ImVec2 size = ImVec2(320, 240);
|
||||||
if (snes_.running()) {
|
if (snes_.running()) {
|
||||||
ImGui::Image((void*)snes_.Ppu().GetScreen()->texture(), size, ImVec2(0, 0),
|
ImGui::BeginChild(
|
||||||
|
"EmulatorOutput", ImVec2(0, 0), true,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove |
|
||||||
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
|
||||||
|
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
|
||||||
|
ImGui::Image((void*)snes_.ppu().GetScreen()->texture(), size, ImVec2(0, 0),
|
||||||
ImVec2(1, 1));
|
ImVec2(1, 1));
|
||||||
|
ImGui::EndChild();
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
} else {
|
} else {
|
||||||
|
ImGui::BeginChild(
|
||||||
|
"EmulatorOutput", ImVec2(0, 0), true,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove |
|
||||||
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
|
||||||
|
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
|
||||||
ImGui::Dummy(size);
|
ImGui::Dummy(size);
|
||||||
|
ImGui::EndChild();
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Emulator output not available.");
|
ImGui::Text("Emulator output not available.");
|
||||||
}
|
}
|
||||||
@@ -242,10 +256,10 @@ void Emulator::RenderDebugger() {
|
|||||||
"DebugTable", 3,
|
"DebugTable", 3,
|
||||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
|
||||||
TableNextColumn();
|
TableNextColumn();
|
||||||
RenderCpuState(snes_.Cpu());
|
RenderCpuState(snes_.cpu());
|
||||||
|
|
||||||
TableNextColumn();
|
TableNextColumn();
|
||||||
RenderCPUInstructionLog(snes_.Cpu().instruction_log_);
|
RenderCPUInstructionLog(snes_.cpu().instruction_log_);
|
||||||
|
|
||||||
TableNextColumn();
|
TableNextColumn();
|
||||||
RenderBreakpointList();
|
RenderBreakpointList();
|
||||||
@@ -286,21 +300,21 @@ void Emulator::RenderBreakpointList() {
|
|||||||
if (ImGui::InputText("##BreakpointInput", breakpoint_input, 10,
|
if (ImGui::InputText("##BreakpointInput", breakpoint_input, 10,
|
||||||
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||||
int breakpoint = std::stoi(breakpoint_input, nullptr, 16);
|
int breakpoint = std::stoi(breakpoint_input, nullptr, 16);
|
||||||
snes_.Cpu().SetBreakpoint(breakpoint);
|
snes_.cpu().SetBreakpoint(breakpoint);
|
||||||
memset(breakpoint_input, 0, sizeof(breakpoint_input));
|
memset(breakpoint_input, 0, sizeof(breakpoint_input));
|
||||||
}
|
}
|
||||||
SameLine();
|
SameLine();
|
||||||
if (ImGui::Button("Add")) {
|
if (ImGui::Button("Add")) {
|
||||||
int breakpoint = std::stoi(breakpoint_input, nullptr, 16);
|
int breakpoint = std::stoi(breakpoint_input, nullptr, 16);
|
||||||
snes_.Cpu().SetBreakpoint(breakpoint);
|
snes_.cpu().SetBreakpoint(breakpoint);
|
||||||
memset(breakpoint_input, 0, sizeof(breakpoint_input));
|
memset(breakpoint_input, 0, sizeof(breakpoint_input));
|
||||||
}
|
}
|
||||||
SameLine();
|
SameLine();
|
||||||
if (ImGui::Button("Clear")) {
|
if (ImGui::Button("Clear")) {
|
||||||
snes_.Cpu().ClearBreakpoints();
|
snes_.cpu().ClearBreakpoints();
|
||||||
}
|
}
|
||||||
Separator();
|
Separator();
|
||||||
auto breakpoints = snes_.Cpu().GetBreakpoints();
|
auto breakpoints = snes_.cpu().GetBreakpoints();
|
||||||
if (!breakpoints.empty()) {
|
if (!breakpoints.empty()) {
|
||||||
Text("Breakpoints:");
|
Text("Breakpoints:");
|
||||||
ImGui::BeginChild("BreakpointsList", ImVec2(0, 100), true);
|
ImGui::BeginChild("BreakpointsList", ImVec2(0, 100), true);
|
||||||
|
|||||||
@@ -64,45 +64,45 @@ ROMInfo SNES::ReadRomHeader(uint32_t offset) {
|
|||||||
// Read cartridge title
|
// Read cartridge title
|
||||||
char title[22];
|
char title[22];
|
||||||
for (int i = 0; i < 21; ++i) {
|
for (int i = 0; i < 21; ++i) {
|
||||||
title[i] = cpu.ReadByte(offset + i);
|
title[i] = cpu_.ReadByte(offset + i);
|
||||||
}
|
}
|
||||||
title[21] = '\0'; // Null-terminate the string
|
title[21] = '\0'; // Null-terminate the string
|
||||||
romInfo.title = std::string(title);
|
romInfo.title = std::string(title);
|
||||||
|
|
||||||
// Read ROM speed and memory map mode
|
// Read ROM speed and memory map mode
|
||||||
uint8_t romSpeedAndMapMode = cpu.ReadByte(offset + 0x15);
|
uint8_t romSpeedAndMapMode = cpu_.ReadByte(offset + 0x15);
|
||||||
romInfo.romSpeed = (ROMSpeed)(romSpeedAndMapMode & 0x07);
|
romInfo.romSpeed = (ROMSpeed)(romSpeedAndMapMode & 0x07);
|
||||||
romInfo.bankSize = (BankSize)((romSpeedAndMapMode >> 5) & 0x01);
|
romInfo.bankSize = (BankSize)((romSpeedAndMapMode >> 5) & 0x01);
|
||||||
|
|
||||||
// Read ROM type
|
// Read ROM type
|
||||||
romInfo.romType = (ROMType)cpu.ReadByte(offset + 0x16);
|
romInfo.romType = (ROMType)cpu_.ReadByte(offset + 0x16);
|
||||||
|
|
||||||
// Read ROM size
|
// Read ROM size
|
||||||
romInfo.romSize = (ROMSize)cpu.ReadByte(offset + 0x17);
|
romInfo.romSize = (ROMSize)cpu_.ReadByte(offset + 0x17);
|
||||||
|
|
||||||
// Read RAM size
|
// Read RAM size
|
||||||
romInfo.sramSize = (SRAMSize)cpu.ReadByte(offset + 0x18);
|
romInfo.sramSize = (SRAMSize)cpu_.ReadByte(offset + 0x18);
|
||||||
|
|
||||||
// Read country code
|
// Read country code
|
||||||
romInfo.countryCode = (CountryCode)cpu.ReadByte(offset + 0x19);
|
romInfo.countryCode = (CountryCode)cpu_.ReadByte(offset + 0x19);
|
||||||
|
|
||||||
// Read license
|
// Read license
|
||||||
romInfo.license = (License)cpu.ReadByte(offset + 0x1A);
|
romInfo.license = (License)cpu_.ReadByte(offset + 0x1A);
|
||||||
|
|
||||||
// Read ROM version
|
// Read ROM version
|
||||||
romInfo.version = cpu.ReadByte(offset + 0x1B);
|
romInfo.version = cpu_.ReadByte(offset + 0x1B);
|
||||||
|
|
||||||
// Read checksum complement
|
// Read checksum complement
|
||||||
romInfo.checksumComplement = cpu.ReadWord(offset + 0x1E);
|
romInfo.checksumComplement = cpu_.ReadWord(offset + 0x1E);
|
||||||
|
|
||||||
// Read checksum
|
// Read checksum
|
||||||
romInfo.checksum = cpu.ReadWord(offset + 0x1C);
|
romInfo.checksum = cpu_.ReadWord(offset + 0x1C);
|
||||||
|
|
||||||
// Read NMI VBL vector
|
// Read NMI VBL vector
|
||||||
romInfo.nmiVblVector = cpu.ReadWord(offset + 0x3E);
|
romInfo.nmiVblVector = cpu_.ReadWord(offset + 0x3E);
|
||||||
|
|
||||||
// Read reset vector
|
// Read reset vector
|
||||||
romInfo.resetVector = cpu.ReadWord(offset + 0x3C);
|
romInfo.resetVector = cpu_.ReadWord(offset + 0x3C);
|
||||||
|
|
||||||
return romInfo;
|
return romInfo;
|
||||||
}
|
}
|
||||||
@@ -112,18 +112,18 @@ void SNES::Init(ROM& rom) {
|
|||||||
// Disable the emulation flag (switch to 65816 native mode)
|
// Disable the emulation flag (switch to 65816 native mode)
|
||||||
|
|
||||||
// Initialize CPU
|
// Initialize CPU
|
||||||
cpu.Init();
|
cpu_.Init();
|
||||||
|
|
||||||
// Read the ROM header
|
// Read the ROM header
|
||||||
auto header_offset = GetHeaderOffset(memory_);
|
auto header_offset = GetHeaderOffset(memory_);
|
||||||
rom_info_ = ReadRomHeader(header_offset);
|
rom_info_ = ReadRomHeader(header_offset);
|
||||||
cpu.PC = rom_info_.resetVector;
|
cpu_.PC = rom_info_.resetVector;
|
||||||
|
|
||||||
// Initialize PPU
|
// Initialize PPU
|
||||||
ppu.Init();
|
ppu_.Init();
|
||||||
|
|
||||||
// Initialize APU
|
// Initialize APU
|
||||||
apu.Init();
|
apu_.Init();
|
||||||
|
|
||||||
// Initialize SDL_Mixer to play the audio samples
|
// Initialize SDL_Mixer to play the audio samples
|
||||||
// Mix_HookMusic(audio_callback, &apu);
|
// Mix_HookMusic(audio_callback, &apu);
|
||||||
@@ -232,16 +232,16 @@ void SNES::Run() {
|
|||||||
frame_accumulated_time += delta_time;
|
frame_accumulated_time += delta_time;
|
||||||
|
|
||||||
// Update the CPU
|
// Update the CPU
|
||||||
cpu.UpdateClock(delta_time);
|
cpu_.UpdateClock(delta_time);
|
||||||
cpu.Update(GetCpuMode());
|
cpu_.Update(GetCpuMode());
|
||||||
|
|
||||||
// Update the PPU
|
// Update the PPU
|
||||||
ppu.UpdateClock(delta_time);
|
ppu_.UpdateClock(delta_time);
|
||||||
ppu.Update();
|
ppu_.Update();
|
||||||
|
|
||||||
// Update the APU
|
// Update the APU
|
||||||
apu.UpdateClock(delta_time);
|
apu_.UpdateClock(delta_time);
|
||||||
apu.Update();
|
apu_.Update();
|
||||||
|
|
||||||
if (frame_accumulated_time >= frame_time) {
|
if (frame_accumulated_time >= frame_time) {
|
||||||
// renderer.Render();
|
// renderer.Render();
|
||||||
@@ -279,11 +279,11 @@ void SNES::NmiIsr() {
|
|||||||
// ...
|
// ...
|
||||||
|
|
||||||
// Push CPU registers to stack
|
// Push CPU registers to stack
|
||||||
cpu.PHP();
|
cpu_.PHP();
|
||||||
|
|
||||||
// Reset DB and DP registers
|
// Reset DB and DP registers
|
||||||
cpu.DB = 0x80; // Assuming bank 0x80, can be changed to 0x00
|
cpu_.DB = 0x80; // Assuming bank 0x80, can be changed to 0x00
|
||||||
cpu.D = 0;
|
cpu_.D = 0;
|
||||||
|
|
||||||
if (v_blank_flag_) {
|
if (v_blank_flag_) {
|
||||||
VBlankRoutine();
|
VBlankRoutine();
|
||||||
@@ -296,7 +296,7 @@ void SNES::NmiIsr() {
|
|||||||
frame_counter_++;
|
frame_counter_++;
|
||||||
|
|
||||||
// Restore CPU registers
|
// Restore CPU registers
|
||||||
cpu.PHB();
|
cpu_.PHB();
|
||||||
}
|
}
|
||||||
|
|
||||||
// VBlank routine
|
// VBlank routine
|
||||||
@@ -307,7 +307,7 @@ void SNES::VBlankRoutine() {
|
|||||||
|
|
||||||
void SNES::BootAPUWithIPL() {
|
void SNES::BootAPUWithIPL() {
|
||||||
// 1. Waiting for the SPC700 to be ready
|
// 1. Waiting for the SPC700 to be ready
|
||||||
while (!apu.IsReadySignalReceived()) {
|
while (!apu_.IsReadySignalReceived()) {
|
||||||
// Active waiting (this can be optimized)
|
// Active waiting (this can be optimized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ class SNES : public DMA {
|
|||||||
|
|
||||||
bool running() const { return running_; }
|
bool running() const { return running_; }
|
||||||
|
|
||||||
auto Cpu() -> CPU& { return cpu; }
|
auto cpu() -> CPU& { return cpu_; }
|
||||||
auto Ppu() -> PPU& { return ppu; }
|
auto ppu() -> Ppu& { return ppu_; }
|
||||||
auto Memory() -> MemoryImpl* { return &memory_; }
|
auto Memory() -> MemoryImpl* { return &memory_; }
|
||||||
|
|
||||||
void SetCpuMode(int mode) { cpu_mode_ = mode; }
|
void SetCpuMode(int mode) { cpu_mode_ = mode; }
|
||||||
@@ -74,8 +74,8 @@ class SNES : public DMA {
|
|||||||
|
|
||||||
void SetupMemory(ROM& rom) {
|
void SetupMemory(ROM& rom) {
|
||||||
// Setup observers for the memory space
|
// Setup observers for the memory space
|
||||||
memory_.AddObserver(&apu);
|
memory_.AddObserver(&apu_);
|
||||||
memory_.AddObserver(&ppu);
|
memory_.AddObserver(&ppu_);
|
||||||
|
|
||||||
// Load the ROM into memory and set up the memory mapping
|
// Load the ROM into memory and set up the memory mapping
|
||||||
memory_.Initialize(rom.vector());
|
memory_.Initialize(rom.vector());
|
||||||
@@ -91,9 +91,9 @@ class SNES : public DMA {
|
|||||||
ClockImpl clock_;
|
ClockImpl clock_;
|
||||||
AudioRamImpl audio_ram_;
|
AudioRamImpl audio_ram_;
|
||||||
|
|
||||||
CPU cpu{memory_, clock_};
|
CPU cpu_{memory_, clock_};
|
||||||
PPU ppu{memory_, clock_};
|
Ppu ppu_{memory_, clock_};
|
||||||
APU apu{memory_, audio_ram_, clock_};
|
APU apu_{memory_, audio_ram_, clock_};
|
||||||
|
|
||||||
// Helper classes
|
// Helper classes
|
||||||
ROMInfo rom_info_;
|
ROMInfo rom_info_;
|
||||||
|
|||||||
Reference in New Issue
Block a user