Reorganizando codigo, otra vez :p

This commit is contained in:
2025-06-23 01:52:28 -04:00
parent 8df1300bd1
commit b0d8e6135d
23 changed files with 942 additions and 46 deletions

3
.idea/misc.xml generated
View File

@@ -9,5 +9,8 @@
<file path="$PROJECT_DIR$/vendor" /> <file path="$PROJECT_DIR$/vendor" />
<file path="$PROJECT_DIR$/vendor/imgui" /> <file path="$PROJECT_DIR$/vendor/imgui" />
</libraryRoots> </libraryRoots>
<excludeRoots>
<file path="$PROJECT_DIR$/src/old" />
</excludeRoots>
</component> </component>
</project> </project>

View File

@@ -1,13 +1,13 @@
add_executable(${PROJECT_NAME}) add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PRIVATE main.cpp target_sources(
bitops.h ${PROJECT_NAME}
Graphics.cpp PRIVATE
Graphics.h main.cpp
Chip8.cpp Machine.h Machine.cpp
Chip8.h Interpreter/Interpreter.h Interpreter/Interpreter.cpp
Interpreter.cpp Graphics/Graphics.h Graphics/Graphics.cpp
Interpreter.h Graphics/Chip8Display.h Graphics/Chip8Display.cpp
Chip8ControlPanel.cpp
Chip8ControlPanel.h
) )
target_link_libraries(${PROJECT_NAME} PRIVATE vendor) target_link_libraries(${PROJECT_NAME} PRIVATE vendor)

View File

@@ -0,0 +1,80 @@
#include "Chip8Display.h"
#include <iostream>
#include <memory>
#include <vector>
#include <bits/ostream.tcc>
#include "imgui.h"
#include "SDL3/SDL.h"
void SDLTextureDestroyer::operator()(SDL_Texture* texture) const {
SDL_DestroyTexture(texture);
}
Chip8Display::Chip8Display(std::shared_ptr<MachineState> machine_state, SDL_Renderer& renderer):
machine_state{std::move(machine_state)},
renderer{renderer},
width(64),
height(32) {
SDL_Texture* raw_texture = SDL_CreateTexture(&renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height);
this->texture = std::unique_ptr<SDL_Texture, SDLTextureDestroyer>(raw_texture);
SDL_SetTextureScaleMode(texture.get(), SDL_SCALEMODE_LINEAR);
}
void Chip8Display::render() const {
update_texture();
display_widget();
}
void Chip8Display::update() const {
}
void Chip8Display::update_texture() const {
SDL_Surface* surface = nullptr;
const 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]) {
const int x = (i % width);
const int y = (i / width);
SDL_Rect rect = {x, y, 1, 1};
Uint32 color = SDL_MapRGB(
SDL_GetPixelFormatDetails(surface->format),
nullptr,
0,
255,
0
);
SDL_FillSurfaceRect(surface, &rect, color);
}
}
SDL_UnlockTexture(texture.get());
} else {
SDL_Log("Failed to lock texture: %s", SDL_GetError());
}
}
void Chip8Display::display_widget() const {
ImGui::Begin("CHIP-8 - Display");
const ImVec2 available_size = ImGui::GetContentRegionAvail();
const int scale_x = static_cast<int>(static_cast<float>(available_size.x) / static_cast<float>(width));
const int scale_y = static_cast<int>(static_cast<float>(available_size.y) / static_cast<float>(height));
const int scale = std::max(1, std::min(scale_x, scale_y));
const ImVec2 scaled_size(static_cast<float>(width * scale), static_cast<float>(height * scale));
const ImVec2 cursor_pos = ImGui::GetCursorPos();
const 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));
ImGui::Image((ImTextureID)((intptr_t)texture.get()), scaled_size);
ImGui::End();
}

View File

@@ -0,0 +1,35 @@
#ifndef CHIP8DISPLAY_H
#define CHIP8DISPLAY_H
#include <memory>
#include "../Interpreter/MachineState.h"
#include "SDL3/SDL.h"
struct SDLTextureDestroyer {
void operator()(SDL_Texture* texture) const;
};
class Chip8Display {
std::shared_ptr<MachineState> machine_state;
SDL_Renderer& renderer;
int width;
int height;
std::unique_ptr<SDL_Texture, SDLTextureDestroyer> texture;
void update_texture() const;
void display_widget() const;
public:
Chip8Display(
std::shared_ptr<MachineState> machine_state,
SDL_Renderer& renderer
);
void update() const;
void render() const;
};
#endif //CHIP8DISPLAY_H

97
src/Graphics/Graphics.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include "Graphics.h"
#include <format>
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_sdlrenderer3.h"
void SDLWindowDestroyer::operator()(SDL_Window* window) const {
SDL_DestroyWindow(window);
}
void SDLRendererDestroyer::operator()(SDL_Renderer* renderer) const {
SDL_DestroyRenderer(renderer);
}
Graphics::Graphics(): window_width{1920}, window_height{1080}, main_scale{1.0f} {
create_sdl();
create_imgui();
}
void Graphics::create_sdl() {
SDL_SetAppMetadata("CHIP-8 Emulator", "0.0.1", "fun.skrd.chip8");
if (!SDL_Init(SDL_INIT_VIDEO)) {
throw std::runtime_error(std::format("Couldn't initialize SDL: {}", SDL_GetError()));
}
SDL_Window* raw_window = nullptr;
SDL_Renderer* raw_renderer = nullptr;
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
)) {
throw std::runtime_error(std::format("Couldn't create window/renderer: {}", SDL_GetError()));
}
window = std::unique_ptr<SDL_Window, SDLWindowDestroyer>(raw_window);
renderer = std::unique_ptr<SDL_Renderer, SDLRendererDestroyer>(raw_renderer);
SDL_SetRenderVSync(renderer.get(), 1);
SDL_SetWindowPosition(window.get(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
SDL_ShowWindow(window.get());
}
void Graphics::create_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());
}
SDL_Renderer& Graphics::get_renderer() {
return *renderer;
}
void Graphics::start_render() {
if (SDL_GetWindowFlags(window.get()) & SDL_WINDOW_MINIMIZED) {
SDL_Delay(10);
}
ImGui_ImplSDLRenderer3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
}
void Graphics::end_render() {
ImGuiIO& io = ImGui::GetIO();
ImGui::Render();
SDL_SetRenderScale(renderer.get(), io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, 0xFF);
SDL_RenderClear(renderer.get());
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer.get());
SDL_RenderPresent(renderer.get());
}

35
src/Graphics/Graphics.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef GRAPHICS_H
#define GRAPHICS_H
#include <memory>
#include "Chip8Display.h"
#include "SDL3/SDL.h"
struct SDLWindowDestroyer {
void operator()(SDL_Window* window) const;
};
struct SDLRendererDestroyer {
void operator()(SDL_Renderer* renderer) const;
};
class Graphics {
int window_width;
int window_height;
float main_scale;
std::unique_ptr<SDL_Window, SDLWindowDestroyer> window;
std::unique_ptr<SDL_Renderer, SDLRendererDestroyer> renderer;
void create_sdl();
void create_imgui();
public:
Graphics();
void start_render();
void end_render();
SDL_Renderer& get_renderer();
};
#endif //GRAPHICS_H

View File

@@ -0,0 +1,19 @@
#ifndef INSTRUCTION_H
#define INSTRUCTION_H
#include <cstdint>
#include "OpCode.h"
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;
};
#endif //INSTRUCTION_H

View File

@@ -0,0 +1,458 @@
#include "Interpreter.h"
#include <iostream>
#include <random>
Interpreter::Interpreter(std::shared_ptr<MachineState> machine_state):
machine_state{std::move(machine_state)},
random_generator{std::random_device{}()} {}
void Interpreter::tick() {
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 auto instruction = decode(word);
machine_state->pc += 2;
execute_instruction(instruction);
if (machine_state->pc >= 0xFFF) {
std::cout << "PC Outside of memory, going back 0x200" << std::endl;
machine_state->pc = 0x200;
}
}
Instruction Interpreter::decode(const uint16_t 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;
auto op_code = OpCode::NOP;
if (operation == 0) {
if (kk == 0xE0) {
op_code = OpCode::CLS;
} else if (kk == 0xEE) {
op_code = OpCode::RET;
} else {
op_code = OpCode::SYS_ADDR;
}
} 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_instruction(const Instruction& instruction) {
if (instruction.op_code == OpCode::CLS) cls();
else if (instruction.op_code == OpCode::RET) ret();
else if (instruction.op_code == OpCode::SYS_ADDR) sys_addr();
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();
}
void Interpreter::sys_addr() {
// NOP
}
void Interpreter::nop() {
// NOP
}
void Interpreter::cls() const {
machine_state->display.fill(false);
}
void Interpreter::ret() const {
machine_state->sp -= 1;
machine_state->pc = machine_state->stack[machine_state->sp];
}
void Interpreter::jp_addr(const Instruction& instruction) const {
machine_state->pc = instruction.nnn;
}
void Interpreter::call_addr(const Instruction& instruction) const {
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) const {
if (machine_state->v[instruction.x] == instruction.kk) {
machine_state->pc += 2;
}
}
void Interpreter::sne_vx_byte(const Instruction& instruction) const {
if (machine_state->v[instruction.x] != instruction.kk) {
machine_state->pc += 2;
}
}
void Interpreter::se_vx_vy(const Instruction& instruction) const {
if (machine_state->v[instruction.x] == machine_state->v[instruction.y]) {
machine_state->pc += 2;
}
}
void Interpreter::ld_vx_byte(const Instruction& instruction) const {
machine_state->v[instruction.x] = instruction.kk;
}
void Interpreter::add_vx_byte(const Instruction& instruction) const {
machine_state->v[instruction.x] += instruction.kk;
}
void Interpreter::ld_vx_vy(const Instruction& instruction) const {
machine_state->v[instruction.x] = machine_state->v[instruction.y];
}
void Interpreter::or_vx_vy(const Instruction& instruction) const {
machine_state->v[instruction.x] |= machine_state->v[instruction.y];
}
void Interpreter::and_vx_vy(const Instruction& instruction) const {
machine_state->v[instruction.x] &= machine_state->v[instruction.y];
}
void Interpreter::xor_vx_vy(const Instruction& instruction) const {
machine_state->v[instruction.x] ^= machine_state->v[instruction.y];
}
void Interpreter::add_vx_vy(const Instruction& instruction) const {
auto& vx = machine_state->v[instruction.x];
const auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
if (vx + vy > 0xFF) {
vf = 1;
} else {
vf = 0;
}
vx += vy;
}
void Interpreter::sub_vx_vy(const Instruction& instruction) const {
auto& vx = machine_state->v[instruction.x];
const auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
if (vx > vy) {
vf = 1;
} else {
vf = 0;
}
vx -= vy;
}
void Interpreter::shr_vx_vy(const Instruction& instruction) const {
auto& vx = machine_state->v[instruction.x];
const 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 {
vf = 0;
}
vx = vx >> 1;
}
void Interpreter::subn_vx_vy(const Instruction& instruction) const {
auto& vx = machine_state->v[instruction.x];
const auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
if (vy > vx) {
vf = 1;
} else {
vf = 0;
}
vx = vy - vx;
}
void Interpreter::shl_vx_vy(const Instruction& instruction) const {
auto& vx = machine_state->v[instruction.x];
const 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 {
vf = 0;
}
vx = vx << 1;
}
void Interpreter::sne_vx_vy(const Instruction& instruction) const {
if (machine_state->v[instruction.x] != machine_state->v[instruction.y]) {
machine_state->pc += 2;
}
}
void Interpreter::ld_i_addr(const Instruction& instruction) const {
machine_state->i = instruction.nnn;
}
void Interpreter::jp_v0_addr(const Instruction& instruction) const {
if (quirks & static_cast<uint8_t>(InterpreterQuirks::SUPER_CHIP_JUMP)) {
machine_state->pc = instruction.nnn + machine_state->v[instruction.x];
} else {
machine_state->pc = instruction.nnn + machine_state->v[0];
}
}
void Interpreter::rnd_vx_byte(const Instruction& instruction) {
auto random = std::uniform_int_distribution<uint8_t>(0, 0xFF);
const auto value = random(random_generator);
machine_state->v[instruction.x] = value & instruction.kk;
}
void Interpreter::drw_vx_vy_nibble(const Instruction& instruction) const {
const auto& memory = machine_state->memory;
auto& display = machine_state->display;
const auto& vx = machine_state->v[instruction.x];
const auto& vy = machine_state->v[instruction.y];
auto& vf = machine_state->v[0xF];
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[machine_state->i + row];
for (auto bit = 0; bit < 8; bit++) {
const auto current_x = start_x + bit;
if (current_x > 63) {
break;
}
const auto pixel = sprite_byte >> (7 - bit) & 0x01;
const auto index = current_y * 64 + current_x;
if (pixel) {
if (display[index]) {
display[index] = false;
vf = 1;
} else {
display[index] = true;
}
}
}
}
}
void Interpreter::skp_vx(const Instruction& instruction) const {
if (machine_state->keyboard & 1 << instruction.x) {
machine_state->pc += 2;
}
}
void Interpreter::sknp_vx(const Instruction& instruction) const {
if (!(machine_state->keyboard & 1 << instruction.x)) {
machine_state->pc += 2;
}
}
void Interpreter::ld_vx_dt(const Instruction& instruction) const {
machine_state->v[instruction.x] = machine_state->dt;
}
void Interpreter::ld_vx_k(const Instruction& instruction) const {
if (machine_state->keyboard == 0) {
machine_state->pc -= 2;
return;
}
for (auto key = 0; key < 16; key++) {
if (machine_state->keyboard & 1 << key) {
machine_state->v[instruction.x] = key;
break;
}
}
}
void Interpreter::ld_dt_vx(const Instruction& instruction) const {
machine_state->dt = machine_state->v[instruction.x];
}
void Interpreter::ld_st_vx(const Instruction& instruction) const {
machine_state->st = machine_state->v[instruction.x];
}
void Interpreter::add_i_vx(const Instruction& instruction) const {
machine_state->i += machine_state->v[instruction.x];
}
void Interpreter::ld_f_vx(const Instruction& instruction) const {
machine_state->i = (machine_state->v[instruction.x] & 0xF) * 5 + 0x50;
}
void Interpreter::ld_b_vx(const Instruction& instruction) const {
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::ld_i_vx(const Instruction& instruction) const {
const bool use_quirk = quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
for (auto reg = 0; reg <= instruction.x; reg++) {
if (use_quirk) {
machine_state->memory[machine_state->i] = machine_state->v[reg];
machine_state->i++;
} else {
machine_state->memory[machine_state->i + reg] = machine_state->v[reg];
}
}
}
void Interpreter::ld_vx_i(const Instruction& instruction) const {
const bool use_quirk = quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
for (auto reg = 0; reg <= instruction.x; reg++) {
if (use_quirk) {
machine_state->v[reg] = machine_state->memory[machine_state->i];
machine_state->i++;
} else {
machine_state->v[reg] = machine_state->memory[machine_state->i + reg];
}
}
}

View File

@@ -0,0 +1,70 @@
#ifndef INTERPRETER_H
#define INTERPRETER_H
#include <memory>
#include <random>
#include "Instruction.h"
#include "MachineState.h"
enum class InterpreterQuirks {
COSMAC_SHIFT = 1 << 0,
SUPER_CHIP_JUMP = 1 << 1,
COSMAC_STORE_AND_LOAD = 1 << 2,
};
class Interpreter {
std::shared_ptr<MachineState> machine_state;
std::mt19937 random_generator;
uint8_t quirks = 0;
static Instruction decode(uint16_t word);
void execute_instruction(const Instruction& instruction);
static void sys_addr();
static void nop();
void cls() const;
void ret() const;
void jp_addr(const Instruction& instruction) const;
void call_addr(const Instruction& instruction) const;
void se_vx_byte(const Instruction& instruction) const;
void sne_vx_byte(const Instruction& instruction) const;
void se_vx_vy(const Instruction& instruction) const;
void ld_vx_byte(const Instruction& instruction) const;
void add_vx_byte(const Instruction& instruction) const;
void ld_vx_vy(const Instruction& instruction) const;
void or_vx_vy(const Instruction& instruction) const;
void and_vx_vy(const Instruction& instruction) const;
void xor_vx_vy(const Instruction& instruction) const;
void add_vx_vy(const Instruction& instruction) const;
void sub_vx_vy(const Instruction& instruction) const;
void shr_vx_vy(const Instruction& instruction) const;
void subn_vx_vy(const Instruction& instruction) const;
void shl_vx_vy(const Instruction& instruction) const;
void sne_vx_vy(const Instruction& instruction) const;
void ld_i_addr(const Instruction& instruction) const;
void jp_v0_addr(const Instruction& instruction) const;
void rnd_vx_byte(const Instruction& instruction);
void drw_vx_vy_nibble(const Instruction& instruction) const;
void skp_vx(const Instruction& instruction) const;
void sknp_vx(const Instruction& instruction) const;
void ld_vx_dt(const Instruction& instruction) const;
void ld_vx_k(const Instruction& instruction) const;
void ld_dt_vx(const Instruction& instruction) const;
void ld_st_vx(const Instruction& instruction) const;
void add_i_vx(const Instruction& instruction) const;
void ld_f_vx(const Instruction& instruction) const;
void ld_b_vx(const Instruction& instruction) const;
void ld_i_vx(const Instruction& instruction) const;
void ld_vx_i(const Instruction& instruction) const;
public:
explicit Interpreter(std::shared_ptr<MachineState> machine_state);
void tick();
};
#endif //INTERPRETER_H

43
src/Interpreter/OpCode.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef OPCODE_H
#define OPCODE_H
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
};
#endif //OPCODE_H

48
src/Machine.cpp Normal file
View File

@@ -0,0 +1,48 @@
#include "Machine.h"
#include "imgui.h"
#include "imgui_impl_sdl3.h"
Machine::Machine():
machine_state{std::make_shared<MachineState>()},
interpreter{std::make_unique<Interpreter>(machine_state)},
graphics{std::make_unique<Graphics>()},
chip8_display{std::make_unique<Chip8Display>(machine_state, graphics->get_renderer())},
ips{700},
last_update_time{0},
accumulator{0} {}
void Machine::iterate() {
execute_interpreter();
chip8_display->update();
graphics->start_render();
ImGui::ShowDemoWindow();
chip8_display->render();
graphics->end_render();
}
bool Machine::on_event(const SDL_Event* event) {
ImGui_ImplSDL3_ProcessEvent(event);
if (event->type == SDL_EVENT_QUIT) {
return false;
}
return true;
}
void Machine::execute_interpreter() {
const auto target_cycle_time = 1.0 / ips;
const auto current_time = SDL_GetTicks();
const auto delta_time = static_cast<double>(current_time - last_update_time) / 1000.0;
last_update_time = current_time;
accumulator += delta_time;
// ReSharper disable once CppDFALoopConditionNotUpdated
while (accumulator >= target_cycle_time) {
interpreter->tick();
accumulator -= target_cycle_time;
}
}

27
src/Machine.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef MACHINE_H
#define MACHINE_H
#include "Graphics/Chip8Display.h"
#include "Graphics/Graphics.h"
#include "Interpreter/Interpreter.h"
#include "SDL3/SDL_events.h"
class Machine {
std::shared_ptr<MachineState> machine_state;
std::unique_ptr<Interpreter> interpreter;
std::unique_ptr<Graphics> graphics;
std::unique_ptr<Chip8Display> chip8_display;
int ips;
uint64_t last_update_time;
double accumulator;
void execute_interpreter();
public:
Machine();
void iterate();
static bool on_event(const SDL_Event* event);
};
#endif //MACHINE_H

View File

@@ -1,53 +1,34 @@
#define SDL_MAIN_USE_CALLBACKS 1 #define SDL_MAIN_USE_CALLBACKS 1
#include <iostream>
#include <bits/ostream.tcc>
#include <SDL3/SDL_main.h> #include <SDL3/SDL_main.h>
#include "Chip8.h" #include <memory>
#include "imgui.h" #include "Machine.h"
#include "imgui_impl_sdl3.h"
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
const auto chip8 = new Chip8(); auto machine = std::make_unique<Machine>();
*appstate = chip8; *appstate = machine.release();
if (!chip8->init()) {
return SDL_APP_FAILURE;
}
int num_keys;
const bool* kb_state = SDL_GetKeyboardState(&num_keys);
const std::span kb_state_view(kb_state, num_keys);
chip8->set_keyboard_state(kb_state_view);
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
SDL_AppResult SDL_AppIterate(void* appstate) { SDL_AppResult SDL_AppIterate(void* appstate) {
const auto chip8 = static_cast<Chip8*>(appstate); const auto machine = static_cast<Machine*>(appstate);
machine->iterate();
chip8->update();
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
const auto chip8 = static_cast<Chip8*>(appstate); const auto machine = static_cast<Machine*>(appstate);
ImGui_ImplSDL3_ProcessEvent(event); if (!machine->on_event(event)) {
if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS; return SDL_APP_SUCCESS;
} }
if (event->type == SDL_EVENT_KEY_DOWN) {
chip8->on_keydown(event->key.scancode);
}
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
void SDL_AppQuit(void* appstate, SDL_AppResult result) { void SDL_AppQuit(void* appstate, SDL_AppResult result) {
const auto chip8 = static_cast<Chip8*>(appstate); std::unique_ptr<Machine> chip8(static_cast<Machine*>(appstate));
delete chip8;
} }

View File

@@ -1,4 +1,4 @@
#include "Chip8.h" #include "../Chip8.h"
#include <filesystem> #include <filesystem>
#include <format> #include <format>
@@ -74,7 +74,7 @@ void Chip8::load_keyboard() {
} }
void Chip8::update() { void Chip8::iterate() {
load_keyboard(); load_keyboard();
std::stringstream buffer; std::stringstream buffer;

View File

@@ -28,7 +28,7 @@ public:
bool init(); bool init();
void update(); void iterate();
std::vector<uint8_t> read_rom(const std::string& path); std::vector<uint8_t> read_rom(const std::string& path);

View File

@@ -1,4 +1,4 @@
#include "Chip8ControlPanel.h" #include "../Chip8ControlPanel.h"
#include <format> #include <format>

View File

@@ -2,7 +2,7 @@
// Created by ryuuji on 6/20/25. // Created by ryuuji on 6/20/25.
// //
#include "Graphics.h" #include "../Graphics.h"
#include "Chip8ControlPanel.h" #include "Chip8ControlPanel.h"
#include <array> #include <array>

View File

@@ -1,4 +1,4 @@
#include "Interpreter.h" #include "../Interpreter.h"
#include <format> #include <format>
#include <iomanip> #include <iomanip>

View File

@@ -6,7 +6,7 @@
#include <random> #include <random>
#include <vector> #include <vector>
#include "MachineState.h" #include "../MachineState.h"
enum class InterpreterQuirks { enum class InterpreterQuirks {
COSMAC_SHIFT = 1 << 0, COSMAC_SHIFT = 1 << 0,