Compare commits

...

4 Commits

12 changed files with 595 additions and 433 deletions

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

3
.idea/editor.xml generated
View File

@@ -246,9 +246,12 @@
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALLOW_COMMENT_AFTER_LBRACE/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/DISABLE_SPACE_CHANGES_BEFORE_TRAILING_COMMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/EMPTY_BLOCK_STYLE/@EntryValue" value="TOGETHER" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/EXPORT_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CASE_FROM_SWITCH/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_COMMENTS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_ENUM_INITIALIZERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />

1
.idea/vcs.xml generated
View File

@@ -3,5 +3,6 @@
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/vendor/SDL" vcs="Git" />
<mapping directory="$PROJECT_DIR$/vendor/imgui" vcs="Git" />
</component>
</project>

View File

@@ -7,10 +7,13 @@
#include "bitops.h"
Chip8::Chip8(): target_cycle_time{1.0 / 700.0},
Chip8::Chip8(): machine_state{std::make_shared<MachineState>()},
graphics{machine_state},
interpreter{machine_state, 0},
target_cycle_time{1.0 / 700.0},
last_update_time{0},
accumulator{0},
run{false},
run{true},
step{false} {}
bool Chip8::init() {
@@ -21,7 +24,7 @@ bool Chip8::init() {
last_update_time = SDL_GetTicks();
accumulator = 0;
const auto rom = read_rom("roms/6-keypad.ch8");
const auto rom = read_rom("roms/1-chip8-logo.ch8");
interpreter.load_rom(rom);
return true;
@@ -52,22 +55,22 @@ std::vector<uint8_t> Chip8::read_rom(const std::string& path) {
}
void Chip8::load_keyboard() {
interpreter.keyboard = keyboard_state[SDL_SCANCODE_X] ? bit_set(interpreter.keyboard, 0x0) : bit_clear(interpreter.keyboard, 0x0);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_1] ? bit_set(interpreter.keyboard, 0x1) : bit_clear(interpreter.keyboard, 0x1);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_2] ? bit_set(interpreter.keyboard, 0x2) : bit_clear(interpreter.keyboard, 0x2);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_3] ? bit_set(interpreter.keyboard, 0x3) : bit_clear(interpreter.keyboard, 0x3);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_Q] ? bit_set(interpreter.keyboard, 0x4) : bit_clear(interpreter.keyboard, 0x4);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_W] ? bit_set(interpreter.keyboard, 0x5) : bit_clear(interpreter.keyboard, 0x5);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_E] ? bit_set(interpreter.keyboard, 0x6) : bit_clear(interpreter.keyboard, 0x6);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_A] ? bit_set(interpreter.keyboard, 0x7) : bit_clear(interpreter.keyboard, 0x7);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_S] ? bit_set(interpreter.keyboard, 0x8) : bit_clear(interpreter.keyboard, 0x8);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_D] ? bit_set(interpreter.keyboard, 0x9) : bit_clear(interpreter.keyboard, 0x9);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_Z] ? bit_set(interpreter.keyboard, 0xA) : bit_clear(interpreter.keyboard, 0xA);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_C] ? bit_set(interpreter.keyboard, 0xB) : bit_clear(interpreter.keyboard, 0xB);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_4] ? bit_set(interpreter.keyboard, 0xC) : bit_clear(interpreter.keyboard, 0xC);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_R] ? bit_set(interpreter.keyboard, 0xD) : bit_clear(interpreter.keyboard, 0xD);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_F] ? bit_set(interpreter.keyboard, 0xE) : bit_clear(interpreter.keyboard, 0xE);
interpreter.keyboard = keyboard_state[SDL_SCANCODE_V] ? bit_set(interpreter.keyboard, 0xF) : bit_clear(interpreter.keyboard, 0xF);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_X] ? bit_set(machine_state->keyboard, 0x0) : bit_clear(machine_state->keyboard, 0x0);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_1] ? bit_set(machine_state->keyboard, 0x1) : bit_clear(machine_state->keyboard, 0x1);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_2] ? bit_set(machine_state->keyboard, 0x2) : bit_clear(machine_state->keyboard, 0x2);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_3] ? bit_set(machine_state->keyboard, 0x3) : bit_clear(machine_state->keyboard, 0x3);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_Q] ? bit_set(machine_state->keyboard, 0x4) : bit_clear(machine_state->keyboard, 0x4);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_W] ? bit_set(machine_state->keyboard, 0x5) : bit_clear(machine_state->keyboard, 0x5);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_E] ? bit_set(machine_state->keyboard, 0x6) : bit_clear(machine_state->keyboard, 0x6);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_A] ? bit_set(machine_state->keyboard, 0x7) : bit_clear(machine_state->keyboard, 0x7);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_S] ? bit_set(machine_state->keyboard, 0x8) : bit_clear(machine_state->keyboard, 0x8);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_D] ? bit_set(machine_state->keyboard, 0x9) : bit_clear(machine_state->keyboard, 0x9);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_Z] ? bit_set(machine_state->keyboard, 0xA) : bit_clear(machine_state->keyboard, 0xA);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_C] ? bit_set(machine_state->keyboard, 0xB) : bit_clear(machine_state->keyboard, 0xB);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_4] ? bit_set(machine_state->keyboard, 0xC) : bit_clear(machine_state->keyboard, 0xC);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_R] ? bit_set(machine_state->keyboard, 0xD) : bit_clear(machine_state->keyboard, 0xD);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_F] ? bit_set(machine_state->keyboard, 0xE) : bit_clear(machine_state->keyboard, 0xE);
machine_state->keyboard = keyboard_state[SDL_SCANCODE_V] ? bit_set(machine_state->keyboard, 0xF) : bit_clear(machine_state->keyboard, 0xF);
}
@@ -77,16 +80,16 @@ void Chip8::update() {
std::stringstream buffer;
buffer << std::format(
"PC: {:03X} | SP: {:02X} | I {:02X} | DT {:02X} | ST {:02X} | INST: {:02X}{:02X} | V0-VF: ",
interpreter.pc,
interpreter.sp,
interpreter.i,
interpreter.dt,
interpreter.st,
interpreter.memory[interpreter.pc],
interpreter.memory[interpreter.pc + 1]
machine_state->pc,
machine_state->sp,
machine_state->i,
machine_state->dt,
machine_state->st,
machine_state->memory[machine_state->pc],
machine_state->memory[machine_state->pc + 1]
);
for (int i = 0; i < 16; ++i) {
buffer << std::format("{:02X}", interpreter.v[i]);
buffer << std::format("{:02X}", machine_state->v[i]);
if (i < 15) buffer << ",";
}
buffer << " | ";
@@ -102,7 +105,7 @@ void Chip8::update() {
}
graphics.draw(interpreter.display, buffer.str());
graphics.draw();
}
void Chip8::execute_instructions() {

View File

@@ -7,6 +7,7 @@
class Chip8 {
std::shared_ptr<MachineState> machine_state;
Graphics graphics;
Interpreter interpreter;

View File

@@ -4,8 +4,13 @@
#include "Graphics.h"
#include <array>
#include <iostream>
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_sdlrenderer3.h"
void SDLWindowDestroyer::operator()(SDL_Window* window) const {
std::cout << "Destroying window" << std::endl;
SDL_DestroyWindow(window);
@@ -21,12 +26,13 @@ void SDLTextureDestroyer::operator()(SDL_Texture* texture) const {
SDL_DestroyTexture(texture);
}
Graphics::Graphics(): width(64 * 30),
height(35 * 30),
scale(30) {}
bool Graphics::init() {
Graphics::Graphics(std::shared_ptr<MachineState> machine_state): machine_state{std::move(machine_state)},
window_width{1920},
window_height{1080},
chip8_width(64),
chip8_height(32),
main_scale(1.0f) {}
bool Graphics::init_sdl() {
SDL_SetAppMetadata("CHIP-8 Emulator", "0.0.1", "fun.skrd.chip8");
if (!SDL_Init(SDL_INIT_VIDEO)) {
@@ -36,7 +42,11 @@ bool Graphics::init() {
SDL_Window* raw_window = nullptr;
SDL_Renderer* raw_renderer = nullptr;
if (!SDL_CreateWindowAndRenderer("CHIP-8 Emulator", width, height, 0, &raw_window, &raw_renderer)) {
main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY;
if (!SDL_CreateWindowAndRenderer("CHIP-8 Emulator", window_width * main_scale, window_height * main_scale, window_flags, &raw_window, &raw_renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return false;
}
@@ -44,34 +54,88 @@ bool Graphics::init() {
this->window = std::unique_ptr<SDL_Window, SDLWindowDestroyer>(raw_window);
this->renderer = std::unique_ptr<SDL_Renderer, SDLRendererDestroyer>(raw_renderer);
SDL_Texture* raw_texture = SDL_CreateTexture(renderer.get(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, 64 * scale, 32 * scale);
SDL_SetRenderVSync(renderer.get(), 1);
SDL_SetWindowPosition(window.get(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
SDL_ShowWindow(window.get());
SDL_Texture* raw_texture = SDL_CreateTexture(renderer.get(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, chip8_width, chip8_height);
this->texture = std::unique_ptr<SDL_Texture, SDLTextureDestroyer>(raw_texture);
SDL_SetTextureScaleMode(texture.get(), SDL_SCALEMODE_NEAREST);
return true;
}
void Graphics::draw(std::bitset<2048> display, std::string info) {
SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, SDL_ALPHA_OPAQUE);
bool Graphics::init_imgui() {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
ImGui::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
style.ScaleAllSizes(main_scale);
style.FontScaleDpi = main_scale;
ImGui_ImplSDL3_InitForSDLRenderer(window.get(), renderer.get());
ImGui_ImplSDLRenderer3_Init(renderer.get());
return true;
}
bool Graphics::init() {
if (!init_sdl()) {
return false;
}
if (!init_imgui()) {
return false;
}
return true;
}
void Graphics::draw() {
if (SDL_GetWindowFlags(window.get()) & SDL_WINDOW_MINIMIZED) {
SDL_Delay(10);
}
build_chip8_texture();
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplSDLRenderer3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
draw_chip8_widget();
ImGui::Render();
SDL_SetRenderScale(renderer.get(), io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, 0xFF);
SDL_RenderClear(renderer.get());
draw_display(display);
draw_info(info);
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer.get());
SDL_RenderPresent(renderer.get());
}
void Graphics::draw_display(std::bitset<2048> display) {
void Graphics::build_chip8_texture() {
SDL_Surface* surface = nullptr;
auto& display = machine_state->display;
if (SDL_LockTextureToSurface(texture.get(), nullptr, &surface)) {
SDL_FillSurfaceRect(surface, nullptr, SDL_MapRGB(SDL_GetPixelFormatDetails(surface->format), nullptr, 0, 0, 0));
for (int i = 0; i < display.size(); i++) {
if (display[i]) {
int x = (i % 64) * scale;
int y = (i / 64) * scale;
int x = (i % chip8_width);
int y = (i / chip8_width);
SDL_Rect rect = {x, y, scale, scale};
SDL_Rect rect = {x, y, 1, 1};
Uint32 color = SDL_MapRGB(
SDL_GetPixelFormatDetails(surface->format),
nullptr,
@@ -86,26 +150,23 @@ void Graphics::draw_display(std::bitset<2048> display) {
}
SDL_UnlockTexture(texture.get());
}
SDL_FRect destination_rect{0.0, 0.0, static_cast<float>(64 * scale), static_cast<float>(32 * scale)};
SDL_RenderTexture(renderer.get(), texture.get(), nullptr, &destination_rect);
}
void Graphics::draw_info(std::string info) {
const int charsize = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
const float screen_scale = static_cast<float>(scale);
const float text_scale = 2.0f;
void Graphics::draw_chip8_widget() {
ImGui::Begin("CHIP-8");
const float info_area_start = 32.0f * screen_scale;
const float info_area_height = (35.0f - 32.0f) * screen_scale;
const float info_area_center = info_area_start + (info_area_height / 2.0f);
const ImVec2 available_size = ImGui::GetContentRegionAvail();
const float text_line = info_area_center / text_scale - (static_cast<float>(charsize) / 2.0f);
int scale_x = static_cast<int>(available_size.x / chip8_width);
int scale_y = static_cast<int>(available_size.y / chip8_height);
int scale = std::max(1, std::min(scale_x, scale_y));
ImVec2 scaled_size(chip8_width * scale, chip8_height * scale);
SDL_SetRenderScale(renderer.get(), text_scale, text_scale);
SDL_SetRenderDrawColor(renderer.get(), 255, 255, 255, SDL_ALPHA_OPAQUE);
ImVec2 cursor_pos = ImGui::GetCursorPos();
ImVec2 center_offset((available_size.x - scaled_size.x) / 2, (available_size.y - scaled_size.y) / 2);
ImGui::SetCursorPos(ImVec2(cursor_pos.x + center_offset.x, cursor_pos.y + center_offset.y));
SDL_RenderDebugText(renderer.get(), 10 / text_scale, text_line, info.data());
SDL_SetRenderScale(renderer.get(), 1.0f, 1.0f);
ImGui::Image((ImTextureID)(intptr_t)texture.get(), scaled_size);
ImGui::End();
}

View File

@@ -7,6 +7,8 @@
#include <bitset>
#include <memory>
#include "imgui.h"
#include "MachineState.h"
#include "SDL3/SDL.h"
struct SDLWindowDestroyer {
@@ -20,20 +22,31 @@ struct SDLTextureDestroyer {
};
class Graphics {
std::shared_ptr<SDL_Window> window;
std::shared_ptr<SDL_Renderer> renderer;
std::shared_ptr<SDL_Texture> texture;
std::shared_ptr<MachineState> machine_state;
int width;
int height;
int scale;
std::unique_ptr<SDL_Window, SDLWindowDestroyer> window;
std::unique_ptr<SDL_Renderer, SDLRendererDestroyer> renderer;
std::unique_ptr<SDL_Texture, SDLTextureDestroyer> texture;
int window_width;
int window_height;
int chip8_width;
int chip8_height;
float main_scale;
bool init_sdl();
bool init_imgui();
void build_chip8_texture();
void draw_chip8_widget();
void draw_display(std::bitset<2048> display);
void draw_info(std::string info);
public:
Graphics();
Graphics(std::shared_ptr<MachineState> machine_state);
bool init();
void draw(std::bitset<2048> display, std::string info);
void draw();
};

View File

@@ -4,21 +4,14 @@
#include <iomanip>
#include <ios>
#include <iostream>
#include <stack>
#include <bits/ostream.tcc>
#include "SDL3/SDL_log.h"
Interpreter::Interpreter():
memory(4096, 0),
v(16, 0),
stack(16, 0),
pc(0x200),
sp(0),
i(0),
dt(0),
st(0),
keyboard(0),
quirks(0) {
Interpreter::Interpreter(std::shared_ptr<MachineState> machine_state, uint8_t quirks):
machine_state{std::move(machine_state)},
quirks{quirks} {
this->random_generator = std::mt19937(std::random_device{}());
this->load_fonts();
}
@@ -43,351 +36,365 @@ void Interpreter::load_fonts() {
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
};
std::copy(std::begin(font_set), std::end(font_set), memory.begin() + 0x050);
std::copy(std::begin(font_set), std::end(font_set), machine_state->memory.begin() + 0x050);
}
void Interpreter::set_quirks(uint8_t quirks) {
this->quirks = quirks;
}
void Interpreter::load_rom(const std::vector<uint8_t>& rom) {
std::copy(std::begin(rom), std::end(rom), memory.begin() + 0x200);
std::copy(std::begin(rom), std::end(rom), machine_state->memory.begin() + 0x200);
}
Instruction Interpreter::decode_next() {
const uint8_t high_word = machine_state->memory[machine_state->pc];
const uint8_t low_word = machine_state->memory[machine_state->pc + 1];
const uint16_t word = (high_word << 8) | low_word;
const uint8_t operation = (word & 0xF000) >> 12;
const uint8_t x = (word & 0x0F00) >> 8;
const uint8_t y = (word & 0x00F0) >> 4;
const uint8_t n = word & 0x000F;
const uint8_t kk = word & 0x00FF;
const uint16_t nnn = word & 0x0FFF;
OpCode op_code = OpCode::NOP;
if (operation == 0) {
if (kk == 0xE0) {
op_code = OpCode::CLS;
} else if (kk == 0xEE) {
op_code = OpCode::RET;
}
} else if (operation == 1) {
op_code = OpCode::JP_ADDR;
} else if (operation == 2) {
op_code = OpCode::CALL_ADDR;
} else if (operation == 3) {
op_code = OpCode::SE_VX_BYTE;
} else if (operation == 4) {
op_code = OpCode::SNE_VX_BYTE;
} else if (operation == 5) {
op_code = OpCode::SE_VX_VY;
} else if (operation == 6) {
op_code = OpCode::LD_VX_BYTE;
} else if (operation == 7) {
op_code = OpCode::ADD_VX_BYTE;
} else if (operation == 8) {
if (n == 0) {
op_code = OpCode::LD_VX_VY;
} else if (n == 1) {
op_code = OpCode::OR_VX_VY;
} else if (n == 2) {
op_code = OpCode::AND_VX_VY;
} else if (n == 3) {
op_code = OpCode::XOR_VX_VY;
} else if (n == 4) {
op_code = OpCode::ADD_VX_VY;
} else if (n == 5) {
op_code = OpCode::SUB_VX_VY;
} else if (n == 6) {
op_code = OpCode::SHR_VX_VY;
} else if (n == 7) {
op_code = OpCode::SUBN_VX_VY;
} else if (n == 0xE) {
op_code = OpCode::SHL_VX_VY;
}
} else if (operation == 9) {
op_code = OpCode::SNE_VX_VY;
} else if (operation == 0xA) {
op_code = OpCode::LD_I_ADDR;
} else if (operation == 0xB) {
op_code = OpCode::JP_V0_ADDR;
} else if (operation == 0xC) {
op_code = OpCode::RND_VX_BYTE;
} else if (operation == 0xD) {
op_code = OpCode::DRW_VX_VY_NIBBLE;
} else if (operation == 0xE) {
if (kk == 0x9E) {
op_code = OpCode::SKP_VX;
} else if (kk == 0xA1) {
op_code = OpCode::SKNP_VX;
}
} else if (operation == 0xF) {
if (kk == 0x07) {
op_code = OpCode::LD_VX_DT;
} else if (kk == 0x0A) {
op_code = OpCode::LD_VX_K;
} else if (kk == 0x15) {
op_code = OpCode::LD_DT_VX;
} else if (kk == 0x18) {
op_code = OpCode::LD_ST_VX;
} else if (kk == 0x1E) {
op_code = OpCode::ADD_I_VX;
} else if (kk == 0x29) {
op_code = OpCode::LD_F_VX;
} else if (kk == 0x33) {
op_code = OpCode::LD_B_VX;
} else if (kk == 0x55) {
op_code = OpCode::LD_I_VX;
} else if (kk == 0x65) {
op_code = OpCode::LD_VX_I;
}
}
return Instruction{
.op_code = op_code,
.operation = operation,
.instruction = word,
.x = x,
.y = y,
.n = n,
.kk = kk,
.nnn = nnn
};
}
void Interpreter::execute(const Instruction& instruction) {
if (instruction.op_code == OpCode::CLS) cls(instruction);
else if (instruction.op_code == OpCode::RET) ret(instruction);
else if (instruction.op_code == OpCode::SYS_ADDR) sys_addr(instruction);
else if (instruction.op_code == OpCode::JP_ADDR) jp_addr(instruction);
else if (instruction.op_code == OpCode::CALL_ADDR) call_addr(instruction);
else if (instruction.op_code == OpCode::SE_VX_BYTE) se_vx_byte(instruction);
else if (instruction.op_code == OpCode::SNE_VX_BYTE) sne_vx_byte(instruction);
else if (instruction.op_code == OpCode::SE_VX_VY) se_vx_vy(instruction);
else if (instruction.op_code == OpCode::LD_VX_BYTE) ld_vx_byte(instruction);
else if (instruction.op_code == OpCode::ADD_VX_BYTE) add_vx_byte(instruction);
else if (instruction.op_code == OpCode::LD_VX_VY) ld_vx_vy(instruction);
else if (instruction.op_code == OpCode::OR_VX_VY) or_vx_vy(instruction);
else if (instruction.op_code == OpCode::AND_VX_VY) and_vx_vy(instruction);
else if (instruction.op_code == OpCode::XOR_VX_VY) xor_vx_vy(instruction);
else if (instruction.op_code == OpCode::ADD_VX_VY) add_vx_vy(instruction);
else if (instruction.op_code == OpCode::SUB_VX_VY) sub_vx_vy(instruction);
else if (instruction.op_code == OpCode::SHR_VX_VY) shr_vx_vy(instruction);
else if (instruction.op_code == OpCode::SUBN_VX_VY) subn_vx_vy(instruction);
else if (instruction.op_code == OpCode::SHL_VX_VY) shl_vx_vy(instruction);
else if (instruction.op_code == OpCode::SNE_VX_VY) sne_vx_vy(instruction);
else if (instruction.op_code == OpCode::LD_I_ADDR) ld_i_addr(instruction);
else if (instruction.op_code == OpCode::JP_V0_ADDR) jp_v0_addr(instruction);
else if (instruction.op_code == OpCode::RND_VX_BYTE) rnd_vx_byte(instruction);
else if (instruction.op_code == OpCode::DRW_VX_VY_NIBBLE) drw_vx_vy_nibble(instruction);
else if (instruction.op_code == OpCode::SKP_VX) skp_vx(instruction);
else if (instruction.op_code == OpCode::SKNP_VX) sknp_vx(instruction);
else if (instruction.op_code == OpCode::LD_VX_DT) ld_vx_dt(instruction);
else if (instruction.op_code == OpCode::LD_VX_K) ld_vx_k(instruction);
else if (instruction.op_code == OpCode::LD_DT_VX) ld_dt_vx(instruction);
else if (instruction.op_code == OpCode::LD_ST_VX) ld_st_vx(instruction);
else if (instruction.op_code == OpCode::ADD_I_VX) add_i_vx(instruction);
else if (instruction.op_code == OpCode::LD_F_VX) ld_f_vx(instruction);
else if (instruction.op_code == OpCode::LD_B_VX) ld_b_vx(instruction);
else if (instruction.op_code == OpCode::LD_I_VX) ld_i_vx(instruction);
else if (instruction.op_code == OpCode::LD_VX_I) ld_vx_i(instruction);
else if (instruction.op_code == OpCode::NOP) nop(instruction);
}
void Interpreter::run() {
const uint8_t high_instruction = memory[pc];
const uint8_t low_instruction = memory[pc + 1];
const uint16_t instruction = (high_instruction << 8) | low_instruction;
const Instruction instruction = decode_next();
const uint8_t opcode = (instruction & 0xF000) >> 12;
const uint8_t x = (instruction & 0x0F00) >> 8;
const uint8_t y = (instruction & 0x00F0) >> 4;
const uint8_t n = instruction & 0x000F;
const uint8_t kk = instruction & 0x00FF;
const uint16_t nnn = instruction & 0x0FFF;
machine_state->pc += 2;
std::cout << std::format(
"INST: {:04X} | OPCODE: {:X} | X: {:X} | Y: {:X} | N: {:X} | KK: {:X} | NNN: {:X}",
instruction,
opcode,
x,
y,
n,
kk,
nnn
) << std::endl;
pc += 2;
execute(instruction);
switch (opcode) {
case 0:
if (kk == 0xE0) {
clear_screen();
} else if (kk == 0xEE) {
return_from_subrutine();
}
break;
case 1:
jump(nnn);
break;
case 2: call_subrutine(nnn);
break;
case 3:
skip_if_vx_eq_val(x, kk);
break;
case 4:
skip_if_vx_neq_val(x, kk);
break;
case 5:
skip_if_vx_eq_vy(x, y);
break;
case 6:
load_vx_val(x, kk);
break;
case 7:
add_vx_val(x, kk);
break;
case 8:
switch (n) {
case 0:
load_vx_vy(x, y);
break;
case 1:
vx_or_vy(x, y);
break;
case 2:
vx_and_vy(x, y);
break;
case 3:
vx_xor_vy(x, y);
break;
case 4:
vx_add_vy(x, y);
break;
case 5:
vx_sub_vy(x, y);
break;
case 6:
if (quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_SHIFT)) {
vx_shift_right_cosmac(x, y);
} else {
vx_shift_right(x);
}
break;
case 7:
vx_subn_vy(x, y);
break;
case 0xE:
if (quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_SHIFT)) {
vx_shift_left_cosmac(x, y);
} else {
vx_shift_left(x);
}
break;
}
break;
case 9:
skip_if_vx_neq_vy(x, y);
break;
case 0xA:
set_index_val(nnn);
break;
case 0xB:
if (quirks & static_cast<int>(InterpreterQuirks::SUPER_CHIP_JUMP)) {
jump_with_offset_super_chip(x, nnn);
} else {
jump_with_offset(nnn);
}
break;
case 0XC:
set_random_value(x, kk);
break;
case 0xD:
display_sprite(x, y, n);
break;
case 0xE:
if (kk == 0x9E) {
skip_if_key_pressed(x);
} else if (kk == 0xA1) {
skip_if_key_not_pressed(x);
}
case 0xF:
switch (kk) {
case 0x07:
load_vx_dt(x);
break;
case 0x0A:
get_key(x);
break;
case 0x15:
load_dt_vx(x);
break;
case 0x18:
load_st_vx(x);
break;
case 0x1E:
add_i_vx(x);
break;
case 0x29:
font_sprite_location(x);
break;
case 0x33:
calculate_bcd(x);
break;
case 0x55:
store_registers_to_memory(x);
break;
case 0x65:
load_registers_from_memory(x);
break;
}
break;
}
if (pc >= 0xFFF) {
if (machine_state->pc >= 0xFFF) {
SDL_Log("PC Outside of memory, going back 0x200");
pc = 0x200;
machine_state->pc = 0x200;
}
}
void Interpreter::clear_screen() {
display.reset();
void Interpreter::sys_addr(const Instruction& instruction) {
// NOP
}
void Interpreter::return_from_subrutine() {
sp -= 1;
pc = stack[sp];
void Interpreter::nop(const Instruction& instruction) {
// NOP
}
void Interpreter::jump(const uint16_t nnn) {
pc = nnn;
void Interpreter::cls(const Instruction& instruction) {
machine_state->display.fill(false);
}
void Interpreter::call_subrutine(const uint16_t nnn) {
stack[sp] = pc;
sp += 1;
pc = nnn;
void Interpreter::ret(const Instruction& instruction) {
machine_state->sp -= 1;
machine_state->pc = machine_state->stack[machine_state->sp];
}
void Interpreter::skip_if_vx_eq_val(const uint8_t x, const uint8_t kk) {
if (v[x] == kk) {
pc += 2;
void Interpreter::jp_addr(const Instruction& instruction) {
machine_state->pc = instruction.nnn;
}
void Interpreter::call_addr(const Instruction& instruction) {
machine_state->stack[machine_state->sp] = machine_state->pc;
machine_state->sp += 1;
machine_state->pc = instruction.nnn;
}
void Interpreter::se_vx_byte(const Instruction& instruction) {
if (machine_state->v[instruction.x] == instruction.kk) {
machine_state->pc += 2;
}
}
void Interpreter::skip_if_vx_neq_val(const uint8_t x, const uint8_t kk) {
if (v[x] != kk) {
pc += 2;
void Interpreter::sne_vx_byte(const Instruction& instruction) {
if (machine_state->v[instruction.x] != instruction.kk) {
machine_state->pc += 2;
}
}
void Interpreter::skip_if_vx_eq_vy(const uint8_t x, const uint8_t y) {
if (v[x] == v[y]) {
pc += 2;
void Interpreter::se_vx_vy(const Instruction& instruction) {
if (machine_state->v[instruction.x] == machine_state->v[instruction.y]) {
machine_state->pc += 2;
}
}
void Interpreter::load_vx_val(const uint8_t x, const uint8_t kk) {
v[x] = kk;
void Interpreter::ld_vx_byte(const Instruction& instruction) {
machine_state->v[instruction.x] = instruction.kk;
}
void Interpreter::add_vx_val(const uint8_t x, const uint8_t kk) {
v[x] += kk;
void Interpreter::add_vx_byte(const Instruction& instruction) {
machine_state->v[instruction.x] += instruction.kk;
}
void Interpreter::load_vx_vy(const uint8_t x, const uint8_t y) {
v[x] = v[y];
void Interpreter::ld_vx_vy(const Instruction& instruction) {
machine_state->v[instruction.x] = machine_state->v[instruction.y];
}
void Interpreter::vx_or_vy(const uint8_t x, const uint8_t y) {
v[x] = v[x] | v[y];
void Interpreter::or_vx_vy(const Instruction& instruction) {
machine_state->v[instruction.x] |= machine_state->v[instruction.y];
}
void Interpreter::vx_and_vy(const uint8_t x, const uint8_t y) {
v[x] = v[x] & v[y];
void Interpreter::and_vx_vy(const Instruction& instruction) {
machine_state->v[instruction.x] &= machine_state->v[instruction.y];
}
void Interpreter::vx_xor_vy(const uint8_t x, const uint8_t y) {
v[x] = v[x] ^ v[y];
void Interpreter::xor_vx_vy(const Instruction& instruction) {
machine_state->v[instruction.x] ^= machine_state->v[instruction.y];
}
void Interpreter::vx_add_vy(const uint8_t x, const uint8_t y) {
if (v[x] + v[y] > 0xFF) {
v[0xF] = 1;
void Interpreter::add_vx_vy(const Instruction& instruction) {
auto& vx = machine_state->v[instruction.x];
auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
if (vx + vy > 0xFF) {
vf = 1;
} else {
v[0xF] = 0;
vf = 0;
}
v[x] = (v[x] + v[y]);
vx += vy;
}
void Interpreter::vx_sub_vy(const uint8_t x, const uint8_t y) {
if (v[x] > v[y]) {
v[0xF] = 1;
void Interpreter::sub_vx_vy(const Instruction& instruction) {
auto& vx = machine_state->v[instruction.x];
auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
if (vx > vy) {
vf = 1;
} else {
v[0xF] = 0;
vf = 0;
}
v[x] = v[x] - v[y];
vx -= vy;
}
void Interpreter::vx_shift_right(const uint8_t x) {
if (v[x] & 0x01) {
v[0xF] = 1;
void Interpreter::shr_vx_vy(const Instruction& instruction) {
auto& vx = machine_state->v[instruction.x];
auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
if (quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_SHIFT)) {
vx = vy;
}
if (vx & 0x01) {
vf = 1;
} else {
v[0xF] = 0;
vf = 0;
}
v[x] = v[x] >> 1;
vx = vx >> 1;
}
void Interpreter::vx_shift_right_cosmac(const uint8_t x, const uint8_t y) {
v[x] = v[y];
void Interpreter::subn_vx_vy(const Instruction& instruction) {
auto& vx = machine_state->v[instruction.x];
auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
if (v[x] & 0x01) {
v[0xF] = 1;
if (vy > vx) {
vf = 1;
} else {
v[0xF] = 0;
vf = 0;
}
v[x] = v[x] >> 1;
vx = vy - vx;
}
void Interpreter::vx_subn_vy(const uint8_t x, const uint8_t y) {
if (v[y] > v[x]) {
v[0xF] = 1;
void Interpreter::shl_vx_vy(const Instruction& instruction) {
auto& vx = machine_state->v[instruction.x];
auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
if (quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_SHIFT)) {
vx = vy;
}
if (vx & 0x80) {
vf = 1;
} else {
v[0xF] = 0;
vf = 0;
}
v[x] = v[y] - v[x];
vx = vx << 1;
}
void Interpreter::vx_shift_left(const uint8_t x) {
if (v[x] & 0x80) {
v[0xF] = 1;
void Interpreter::sne_vx_vy(const Instruction& instruction) {
if (machine_state->v[instruction.x] != machine_state->v[instruction.y]) {
machine_state->pc += 2;
}
}
void Interpreter::ld_i_addr(const Instruction& instruction) {
machine_state->i = instruction.nnn;
}
void Interpreter::jp_v0_addr(const Instruction& instruction) {
if (quirks & static_cast<uint8_t>(InterpreterQuirks::SUPER_CHIP_JUMP)) {
machine_state->pc = instruction.nnn + machine_state->v[instruction.x];
} else {
v[0xF] = 0;
}
v[x] = v[x] << 1;
}
void Interpreter::vx_shift_left_cosmac(const uint8_t x, const uint8_t y) {
v[x] = v[y];
if (v[x] & 0x80) {
v[0xF] = 1;
} else {
v[0xF] = 0;
}
v[x] = v[x] << 1;
}
void Interpreter::skip_if_vx_neq_vy(const uint8_t x, const uint8_t y) {
if (v[x] != v[y]) {
pc += 2;
machine_state->pc = instruction.nnn + machine_state->v[0];
}
}
void Interpreter::set_index_val(const uint16_t nnn) {
i = nnn;
}
void Interpreter::jump_with_offset(const uint16_t nnn) {
pc = nnn + v[0];
}
void Interpreter::jump_with_offset_super_chip(const uint8_t x, const uint16_t nnn) {
pc = nnn + v[x];
}
void Interpreter::set_random_value(const uint8_t x, const uint8_t kk) {
void Interpreter::rnd_vx_byte(const Instruction& instruction) {
auto random = std::uniform_int_distribution<uint8_t>(0, 0xFF);
const auto value = random(random_generator);
v[x] = value & kk;
machine_state->v[instruction.x] = value & instruction.kk;
}
void Interpreter::display_sprite(uint8_t x, uint8_t y, const uint8_t n) {
uint8_t start_x = v[x] & 63;
uint8_t start_y = v[y] & 31;
v[0xF] = 0;
void Interpreter::drw_vx_vy_nibble(const Instruction& instruction) {
auto& memory = machine_state->memory;
auto& display = machine_state->display;
auto& vx = machine_state->v[instruction.x];
auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
for (auto row = 0; row < n; row++) {
auto current_y = start_y + row;
const uint8_t start_x = vx & 63;
const uint8_t start_y = vy & 31;
vf = 0;
for (auto row = 0; row < instruction.n; row++) {
const auto current_y = start_y + row;
if (current_y > 31) {
break;
}
const auto sprite_byte = memory[i + row];
const auto sprite_byte = memory[machine_state->i + row];
for (auto bit = 0; bit < 8; bit++) {
auto current_x = start_x + bit;
const auto current_x = start_x + bit;
if (current_x > 63) {
break;
@@ -398,91 +405,92 @@ void Interpreter::display_sprite(uint8_t x, uint8_t y, const uint8_t n) {
if (pixel) {
if (display[index]) {
display.set(index, false);
v[0xF] = 1;
display[index] = false;
vf = 1;
} else {
display.set(index, true);
display[index] = true;
}
}
}
}
}
void Interpreter::skip_if_key_pressed(const uint8_t x) {
if (keyboard & (1 << x)) {
pc += 2;
void Interpreter::skp_vx(const Instruction& instruction) {
if (machine_state->keyboard & (1 << instruction.x)) {
machine_state->pc += 2;
}
}
void Interpreter::skip_if_key_not_pressed(const uint8_t x) {
if (!(keyboard & (1 << x))) {
pc += 2;
void Interpreter::sknp_vx(const Instruction& instruction) {
if (!(machine_state->keyboard & (1 << instruction.x))) {
machine_state->pc += 2;
}
}
void Interpreter::load_vx_dt(const uint8_t x) {
v[x] = dt;
void Interpreter::ld_vx_dt(const Instruction& instruction) {
machine_state->v[instruction.x] = machine_state->dt;
}
void Interpreter::get_key(const uint8_t x) {
if (keyboard == 0) {
pc -= 2;
void Interpreter::ld_vx_k(const Instruction& instruction) {
if (machine_state->keyboard == 0) {
machine_state->pc -= 2;
return;
}
for (auto key = 0; key < 16; key++) {
if (keyboard & (1 << key)) {
v[x] = key;
if (machine_state->keyboard & (1 << key)) {
machine_state->v[instruction.x] = key;
break;
}
}
}
void Interpreter::load_dt_vx(const uint8_t x) {
dt = v[x];
void Interpreter::ld_dt_vx(const Instruction& instruction) {
machine_state->dt = machine_state->v[instruction.x];
}
void Interpreter::load_st_vx(const uint8_t x) {
st = v[x];
void Interpreter::ld_st_vx(const Instruction& instruction) {
machine_state->st = machine_state->v[instruction.x];
}
void Interpreter::add_i_vx(const uint8_t x) {
i = i + v[x];
void Interpreter::add_i_vx(const Instruction& instruction) {
machine_state->i += machine_state->v[instruction.x];
}
void Interpreter::font_sprite_location(const uint8_t x) {
i = ((v[x] & 0xF) * 5) + 0x50;
void Interpreter::ld_f_vx(const Instruction& instruction) {
auto& vx = machine_state->v[instruction.x];
machine_state->i = ((vx & 0xF) * 5) + 0x50;
}
void Interpreter::calculate_bcd(const uint8_t x) {
const auto number = v[x];
memory[i] = number / 100;
memory[i + 1] = (number - (memory[i] * 100)) / 10;
memory[i + 2] = number - (memory[i] * 100) - (memory[i + 1] * 10);
void Interpreter::ld_b_vx(const Instruction& instruction) {
const auto number = machine_state->v[instruction.x];
machine_state->memory[machine_state->i] = number / 100;
machine_state->memory[machine_state->i + 1] = (number - (machine_state->memory[machine_state->i] * 100)) / 10;
machine_state->memory[machine_state->i + 2] = number - (machine_state->memory[machine_state->i] * 100) - (machine_state->memory[machine_state->i + 1] * 10);
}
void Interpreter::store_registers_to_memory(const uint8_t x) {
void Interpreter::ld_i_vx(const Instruction& instruction) {
bool use_quirk = quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
for (auto reg = 0; reg <= x; reg++) {
for (auto reg = 0; reg <= instruction.x; reg++) {
if (use_quirk) {
memory[i] = v[reg];
i++;
machine_state->memory[machine_state->i] = machine_state->v[reg];
machine_state->i++;
} else {
memory[i + reg] = v[reg];
machine_state->memory[machine_state->i + reg] = machine_state->v[reg];
}
}
}
void Interpreter::load_registers_from_memory(const uint8_t x) {
void Interpreter::ld_vx_i(const Instruction& instruction) {
bool use_quirk = quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
for (auto reg = 0; reg <= x; reg++) {
for (auto reg = 0; reg <= instruction.x; reg++) {
if (use_quirk) {
v[reg] = memory[i];
i++;
machine_state->v[reg] = machine_state->memory[machine_state->i];
machine_state->i++;
} else {
v[reg] = memory[i + reg];
machine_state->v[reg] = machine_state->memory[machine_state->i + reg];
}
}
}

View File

@@ -2,77 +2,120 @@
#define INTERPRETER_H
#include <bitset>
#include <cstdint>
#include <memory>
#include <random>
#include <vector>
#include "MachineState.h"
enum class InterpreterQuirks {
COSMAC_SHIFT = 1 << 0,
SUPER_CHIP_JUMP = 1 << 1,
COSMAC_STORE_AND_LOAD = 1 << 2,
};
enum class OpCode {
CLS, // 00E0 - CLS
RET, // 00EE - RET
SYS_ADDR, // 0nnn - SYS addr
JP_ADDR, // 1nnn - JP addr
CALL_ADDR, // 2nnn - CALL addr
SE_VX_BYTE, // 3xkk - SE Vx, byte
SNE_VX_BYTE, // 4xkk - SNE Vx, byte
SE_VX_VY, // 5xy0 - SE Vx, Vy
LD_VX_BYTE, // 6xkk - LD Vx, byte
ADD_VX_BYTE, // 7xkk - ADD Vx, byte
LD_VX_VY, // 8xy0 - LD Vx, Vy
OR_VX_VY, // 8xy1 - OR Vx, Vy
AND_VX_VY, // 8xy2 - AND Vx, Vy
XOR_VX_VY, // 8xy3 - XOR Vx, Vy
ADD_VX_VY, // 8xy4 - ADD Vx, Vy
SUB_VX_VY, // 8xy5 - SUB Vx, Vy
SHR_VX_VY, // 8xy6 - SHR Vx {, Vy}
SUBN_VX_VY, // 8xy7 - SUBN Vx, Vy
SHL_VX_VY, // 8xyE - SHL Vx {, Vy}
SNE_VX_VY, // 9xy0 - SNE Vx, Vy
LD_I_ADDR, // Annn - LD I, addr
JP_V0_ADDR, // Bnnn - JP V0, addr
RND_VX_BYTE, // Cxkk - RND Vx, byte
DRW_VX_VY_NIBBLE, // Dxyn - DRW Vx, Vy, nibble
SKP_VX, // Ex9E - SKP Vx
SKNP_VX, // ExA1 - SKNP Vx
LD_VX_DT, // Fx07 - LD Vx, DT
LD_VX_K, // Fx0A - LD Vx, K
LD_DT_VX, // Fx15 - LD DT, Vx
LD_ST_VX, // Fx18 - LD ST, Vx
ADD_I_VX, // Fx1E - ADD I, Vx
LD_F_VX, // Fx29 - LD F, Vx
LD_B_VX, // Fx33 - LD B, Vx
LD_I_VX, // Fx55 - LD [I], Vx
LD_VX_I, // Fx65 - LD Vx, [I]
NOP, // INVALID OPERATION
};
struct Instruction {
OpCode op_code;
uint8_t operation;
uint16_t instruction;
uint8_t x;
uint8_t y;
uint8_t n;
uint8_t kk;
uint16_t nnn;
};
class Interpreter {
std::shared_ptr<MachineState> machine_state;
std::mt19937 random_generator;
public:
std::vector<uint8_t> memory;
std::vector<uint16_t> v;
std::vector<uint16_t> stack;
uint16_t pc;
uint8_t sp;
uint16_t i;
uint8_t dt;
uint8_t st;
std::bitset<2048> display;
uint16_t keyboard;
private:
uint8_t quirks;
void load_fonts();
void clear_screen();
void return_from_subrutine();
void jump(uint16_t nnn);
void call_subrutine(uint16_t nnn);
void skip_if_vx_eq_val(uint8_t x, uint8_t kk);
void skip_if_vx_neq_val(uint8_t x, uint8_t kk);
void skip_if_vx_eq_vy(uint8_t x, uint8_t y);
void load_vx_val(uint8_t x, uint8_t kk);
void add_vx_val(uint8_t x, uint8_t kk);
void load_vx_vy(uint8_t x, uint8_t y);
void vx_or_vy(uint8_t x, uint8_t y);
void vx_and_vy(uint8_t x, uint8_t y);
void vx_xor_vy(uint8_t x, uint8_t y);
void vx_add_vy(uint8_t x, uint8_t y);
void vx_sub_vy(uint8_t x, uint8_t y);
void vx_shift_right(uint8_t x);
void vx_shift_right_cosmac(uint8_t x, uint8_t y);
void vx_subn_vy(uint8_t x, uint8_t y);
void vx_shift_left(uint8_t x);
void vx_shift_left_cosmac(uint8_t x, uint8_t y);
void skip_if_vx_neq_vy(uint8_t x, uint8_t y);
void set_index_val(uint16_t nnn);
void jump_with_offset(uint16_t nnn);
void jump_with_offset_super_chip(uint8_t x, uint16_t nnn);
void set_random_value(uint8_t x, uint8_t kk);
void display_sprite(uint8_t x, uint8_t y, uint8_t n);
void skip_if_key_pressed(uint8_t x);
void skip_if_key_not_pressed(uint8_t x);
void load_vx_dt(uint8_t x);
void get_key(uint8_t x);
void load_dt_vx(uint8_t x);
void load_st_vx(uint8_t x);
void add_i_vx(uint8_t x);
void font_sprite_location(uint8_t x);
void calculate_bcd(uint8_t x);
void store_registers_to_memory(uint8_t x);
void load_registers_from_memory(uint8_t x);
Instruction decode_next();
void execute(const Instruction& instruction);
void sys_addr(const Instruction& instruction);
void nop(const Instruction& instruction);
void cls(const Instruction& instruction);
void ret(const Instruction& instruction);
void jp_addr(const Instruction& instruction);
void call_addr(const Instruction& instruction);
void se_vx_byte(const Instruction& instruction);
void sne_vx_byte(const Instruction& instruction);
void se_vx_vy(const Instruction& instruction);
void ld_vx_byte(const Instruction& instruction);
void add_vx_byte(const Instruction& instruction);
void ld_vx_vy(const Instruction& instruction);
void or_vx_vy(const Instruction& instruction);
void and_vx_vy(const Instruction& instruction);
void xor_vx_vy(const Instruction& instruction);
void add_vx_vy(const Instruction& instruction);
void sub_vx_vy(const Instruction& instruction);
void shr_vx_vy(const Instruction& instruction);
void subn_vx_vy(const Instruction& instruction);
void shl_vx_vy(const Instruction& instruction);
void sne_vx_vy(const Instruction& instruction);
void ld_i_addr(const Instruction& instruction);
void jp_v0_addr(const Instruction& instruction);
void jump_with_offset_super_chip(const Instruction& instruction);
void rnd_vx_byte(const Instruction& instruction);
void drw_vx_vy_nibble(const Instruction& instruction);
void skp_vx(const Instruction& instruction);
void sknp_vx(const Instruction& instruction);
void ld_vx_dt(const Instruction& instruction);
void ld_vx_k(const Instruction& instruction);
void ld_dt_vx(const Instruction& instruction);
void ld_st_vx(const Instruction& instruction);
void add_i_vx(const Instruction& instruction);
void ld_f_vx(const Instruction& instruction);
void ld_b_vx(const Instruction& instruction);
void ld_i_vx(const Instruction& instruction);
void ld_vx_i(const Instruction& instruction);
public:
Interpreter();
Interpreter(std::shared_ptr<MachineState> machine_state, uint8_t quirks);
void set_quirks(uint8_t quirks);
void load_rom(const std::vector<uint8_t>& rom);
void run();

19
src/MachineState.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef MACHINESTATE_H
#define MACHINESTATE_H
#include <array>
#include <cstdint>
struct MachineState {
std::array<uint8_t, 4096> memory{};
std::array<uint16_t, 16> v{};
std::array<uint16_t, 16> stack{};
std::array<bool, 2048> display{};
uint16_t keyboard = 0;
uint16_t pc = 0x200;
uint8_t sp = 0;
uint16_t i = 0;
uint8_t dt = 0;
uint8_t st = 0;
};
#endif //MACHINESTATE_H

View File

@@ -4,6 +4,8 @@
#include <SDL3/SDL_main.h>
#include "Chip8.h"
#include "imgui.h"
#include "imgui_impl_sdl3.h"
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
const auto chip8 = new Chip8();
@@ -31,6 +33,8 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
const auto chip8 = static_cast<Chip8*>(appstate);
ImGui_ImplSDL3_ProcessEvent(event);
if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS;
}
@@ -39,6 +43,7 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
chip8->on_keydown(event->key.scancode);
}
return SDL_APP_CONTINUE;
}

2
vendor/imgui vendored