Eliminando codigo antiguo y creando base de control panel
This commit is contained in:
@@ -7,6 +7,7 @@ target_sources(
|
||||
Interpreter/Interpreter.h Interpreter/Interpreter.cpp
|
||||
Graphics/Graphics.h Graphics/Graphics.cpp
|
||||
UI/UIManager.h UI/UIManager.cpp
|
||||
UI/Chip8Display.h UI/Chip8Display.cpp
|
||||
UI/Display.h UI/Display.cpp
|
||||
UI/ControlPanel.h UI/ControlPanel.cpp
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE vendor)
|
||||
@@ -1,11 +1,9 @@
|
||||
#include "Machine.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Graphics/Graphics.h"
|
||||
#include "Interpreter/Interpreter.h"
|
||||
#include "SDL3/SDL.h"
|
||||
#include "UI/Chip8Display.h"
|
||||
#include "UI/Display.h"
|
||||
#include "UI/UIManager.h"
|
||||
|
||||
#include "imgui_impl_sdl3.h"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Graphics/Graphics.h"
|
||||
#include "Interpreter/Interpreter.h"
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "UI/Chip8Display.h"
|
||||
#include "UI/Display.h"
|
||||
#include "UI/UIManager.h"
|
||||
|
||||
class Machine {
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
#include "../Chip8ControlPanel.h"
|
||||
#include "ControlPanel.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
void Chip8ControlPanel::render() {
|
||||
ControlPanel::ControlPanel(
|
||||
std::shared_ptr<Graphics> graphics,
|
||||
std::shared_ptr<MachineState> machine_state
|
||||
): graphics{std::move(graphics)},
|
||||
machine_state{std::move(machine_state)} {}
|
||||
|
||||
void ControlPanel::render() {
|
||||
constexpr auto full_width = ImVec2(-FLT_MIN, 0.0f);
|
||||
|
||||
if (ImGui::Begin("Chip-8 - Controls")) {
|
||||
ImGui::Button("Load Rom", full_width);
|
||||
if (ImGui::Button(run ? "Pause" : "Run", full_width)) {
|
||||
run = !run;
|
||||
this->run = !run;
|
||||
}
|
||||
|
||||
ImGui::Text("Status: %s", "Stopped");
|
||||
26
src/UI/ControlPanel.h
Normal file
26
src/UI/ControlPanel.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef CONTROLPANEL_H
|
||||
#define CONTROLPANEL_H
|
||||
#include <memory>
|
||||
|
||||
#include "../Graphics/Graphics.h"
|
||||
#include "../Interpreter/MachineState.h"
|
||||
|
||||
|
||||
class ControlPanel {
|
||||
std::shared_ptr<Graphics> graphics;
|
||||
std::shared_ptr<MachineState> machine_state;
|
||||
|
||||
bool run = false;
|
||||
int steps = 1;
|
||||
int speed = 100;
|
||||
public:
|
||||
ControlPanel(
|
||||
std::shared_ptr<Graphics> graphics,
|
||||
std::shared_ptr<MachineState> machine_state
|
||||
);
|
||||
|
||||
void render() ;
|
||||
};
|
||||
|
||||
|
||||
#endif //CONTROLPANEL_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Chip8Display.h"
|
||||
#include "Display.h"
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
@@ -12,7 +12,7 @@ void SDLTextureDestroyer::operator()(SDL_Texture* texture) const {
|
||||
SDL_DestroyTexture(texture);
|
||||
}
|
||||
|
||||
Chip8Display::Chip8Display(std::shared_ptr<Graphics> graphics, std::shared_ptr<MachineState> machine_state):
|
||||
Display::Display(std::shared_ptr<Graphics> graphics, std::shared_ptr<MachineState> machine_state):
|
||||
graphics{std::move(graphics)},
|
||||
machine_state{std::move(machine_state)},
|
||||
width(64),
|
||||
@@ -21,7 +21,7 @@ Chip8Display::Chip8Display(std::shared_ptr<Graphics> graphics, std::shared_ptr<M
|
||||
foreground_color(0x00, 0xb9, 0xbe),
|
||||
texture{create_texture()} {}
|
||||
|
||||
std::unique_ptr<SDL_Texture, SDLTextureDestroyer> Chip8Display::create_texture() const {
|
||||
std::unique_ptr<SDL_Texture, SDLTextureDestroyer> Display::create_texture() const {
|
||||
SDL_Texture* raw_texture = SDL_CreateTexture(this->graphics->get_renderer().get(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
|
||||
if (!raw_texture) {
|
||||
@@ -33,12 +33,12 @@ std::unique_ptr<SDL_Texture, SDLTextureDestroyer> Chip8Display::create_texture()
|
||||
return std::unique_ptr<SDL_Texture, SDLTextureDestroyer>(raw_texture);
|
||||
}
|
||||
|
||||
void Chip8Display::render() const {
|
||||
void Display::render() const {
|
||||
update_texture();
|
||||
display_widget();
|
||||
}
|
||||
|
||||
void Chip8Display::update_texture() const {
|
||||
void Display::update_texture() const {
|
||||
SDL_Surface* surface = nullptr;
|
||||
const auto& display = machine_state->display;
|
||||
|
||||
@@ -78,7 +78,7 @@ void Chip8Display::update_texture() const {
|
||||
}
|
||||
}
|
||||
|
||||
void Chip8Display::display_widget() const {
|
||||
void Display::display_widget() const {
|
||||
ImGui::Begin("CHIP-8 - Display");
|
||||
|
||||
const ImVec2 available_size = ImGui::GetContentRegionAvail();
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef CHIP8DISPLAY_H
|
||||
#define CHIP8DISPLAY_H
|
||||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
#include <memory>
|
||||
|
||||
#include "../Graphics/Color.h"
|
||||
@@ -11,7 +11,7 @@ struct SDLTextureDestroyer {
|
||||
void operator()(SDL_Texture* texture) const;
|
||||
};
|
||||
|
||||
class Chip8Display {
|
||||
class Display {
|
||||
std::shared_ptr<Graphics> graphics;
|
||||
std::shared_ptr<MachineState> machine_state;
|
||||
|
||||
@@ -26,8 +26,9 @@ class Chip8Display {
|
||||
std::unique_ptr<SDL_Texture, SDLTextureDestroyer> create_texture() const;
|
||||
void update_texture() const;
|
||||
void display_widget() const;
|
||||
|
||||
public:
|
||||
Chip8Display(
|
||||
Display(
|
||||
std::shared_ptr<Graphics> graphics,
|
||||
std::shared_ptr<MachineState> machine_state
|
||||
);
|
||||
@@ -36,4 +37,4 @@ public:
|
||||
};
|
||||
|
||||
|
||||
#endif //CHIP8DISPLAY_H
|
||||
#endif //DISPLAY_H
|
||||
@@ -3,8 +3,10 @@
|
||||
UIManager::UIManager(std::shared_ptr<Graphics> graphics, std::shared_ptr<MachineState> machine_state):
|
||||
graphics{std::move(graphics)},
|
||||
machine_state{std::move(machine_state)},
|
||||
chip8_display{std::make_unique<Chip8Display>(this->graphics, this->machine_state)} {}
|
||||
display{std::make_unique<Display>(this->graphics, this->machine_state)},
|
||||
control_panel{std::make_unique<ControlPanel>(this->graphics, this->machine_state)} {}
|
||||
|
||||
void UIManager::render() const {
|
||||
this->chip8_display->render();
|
||||
void UIManager::render() {
|
||||
this->display->render();
|
||||
this->control_panel->render();
|
||||
}
|
||||
|
||||
@@ -2,19 +2,21 @@
|
||||
#define UIMANAGER_H
|
||||
#include <memory>
|
||||
|
||||
#include "Chip8Display.h"
|
||||
#include "ControlPanel.h"
|
||||
#include "Display.h"
|
||||
#include "../Graphics/Graphics.h"
|
||||
|
||||
class UIManager {
|
||||
std::shared_ptr<Graphics> graphics;
|
||||
std::shared_ptr<MachineState> machine_state;
|
||||
|
||||
std::unique_ptr<Chip8Display> chip8_display;
|
||||
std::unique_ptr<Display> display;
|
||||
std::unique_ptr<ControlPanel> control_panel;
|
||||
|
||||
public:
|
||||
UIManager(std::shared_ptr<Graphics> graphics, std::shared_ptr<MachineState> machine_state);
|
||||
|
||||
void render() const;
|
||||
void render() ;
|
||||
};
|
||||
|
||||
#endif //UIMANAGER_H
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
#include "../Chip8.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "bitops.h"
|
||||
|
||||
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{true},
|
||||
step{false} {}
|
||||
|
||||
bool Chip8::init() {
|
||||
if (!graphics.init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
last_update_time = SDL_GetTicks();
|
||||
accumulator = 0;
|
||||
|
||||
const auto rom = read_rom("roms/1-chip8-logo.ch8");
|
||||
interpreter.load_rom(rom);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Chip8::set_keyboard_state(std::span<const bool> keyboard_state) {
|
||||
this->keyboard_state = keyboard_state;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Chip8::read_rom(const std::string& path) {
|
||||
std::ifstream rom_file(path, std::ios::binary);
|
||||
|
||||
rom_file.seekg(0, std::ios::end);
|
||||
const std::streampos file_size = rom_file.tellg();
|
||||
rom_file.seekg(0, std::ios::beg);
|
||||
|
||||
std::cout << "ROM size: " << file_size << std::endl;
|
||||
|
||||
std::vector<std::uint8_t> rom(file_size);
|
||||
|
||||
rom.insert(
|
||||
rom.begin(),
|
||||
std::istreambuf_iterator<char>(rom_file),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
|
||||
return rom;
|
||||
}
|
||||
|
||||
void Chip8::load_keyboard() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void Chip8::iterate() {
|
||||
load_keyboard();
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << std::format(
|
||||
"PC: {:03X} | SP: {:02X} | I {:02X} | DT {:02X} | ST {:02X} | INST: {:02X}{:02X} | V0-VF: ",
|
||||
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}", machine_state->v[i]);
|
||||
if (i < 15) buffer << ",";
|
||||
}
|
||||
buffer << " | ";
|
||||
|
||||
if (run) {
|
||||
execute_instructions();
|
||||
} else if (step) {
|
||||
execute_instruction();
|
||||
step = false;
|
||||
last_update_time = SDL_GetTicks();
|
||||
} else {
|
||||
last_update_time = SDL_GetTicks();
|
||||
}
|
||||
|
||||
|
||||
graphics.draw();
|
||||
}
|
||||
|
||||
void Chip8::execute_instructions() {
|
||||
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;
|
||||
|
||||
while (accumulator >= target_cycle_time) {
|
||||
execute_instruction();
|
||||
accumulator -= target_cycle_time;
|
||||
}
|
||||
}
|
||||
|
||||
void Chip8::execute_instruction() {
|
||||
interpreter.run();
|
||||
}
|
||||
|
||||
void Chip8::on_keydown(SDL_Scancode scancode) {
|
||||
if (scancode == SDL_SCANCODE_F1) {
|
||||
run = !run;
|
||||
} else if (scancode == SDL_SCANCODE_F2) {
|
||||
step = true;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef CHIP8_H
|
||||
#define CHIP8_H
|
||||
#include <span>
|
||||
|
||||
#include "Graphics.h"
|
||||
#include "Interpreter.h"
|
||||
|
||||
|
||||
class Chip8 {
|
||||
std::shared_ptr<MachineState> machine_state;
|
||||
Graphics graphics;
|
||||
Interpreter interpreter;
|
||||
|
||||
double target_cycle_time;
|
||||
|
||||
Uint64 last_update_time;
|
||||
double accumulator;
|
||||
|
||||
std::span<const bool> keyboard_state;
|
||||
bool run;
|
||||
bool step;
|
||||
|
||||
void load_keyboard();
|
||||
void execute_instructions();
|
||||
void execute_instruction();
|
||||
public:
|
||||
Chip8();
|
||||
|
||||
bool init();
|
||||
|
||||
void iterate();
|
||||
|
||||
std::vector<uint8_t> read_rom(const std::string& path);
|
||||
|
||||
void set_keyboard_state(std::span<const bool> keyboard_state);
|
||||
void on_keydown(SDL_Scancode scancode);
|
||||
};
|
||||
|
||||
|
||||
#endif //CHIP8_H
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef CHIP8CONTROLPANEL_H
|
||||
#define CHIP8CONTROLPANEL_H
|
||||
|
||||
|
||||
class Chip8ControlPanel {
|
||||
bool run = false;
|
||||
int steps = 10;
|
||||
int speed = 700;
|
||||
public:
|
||||
void render();
|
||||
};
|
||||
|
||||
|
||||
#endif //CHIP8CONTROLPANEL_H
|
||||
@@ -1,173 +0,0 @@
|
||||
//
|
||||
// Created by ryuuji on 6/20/25.
|
||||
//
|
||||
|
||||
#include "../Graphics.h"
|
||||
#include "Chip8ControlPanel.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);
|
||||
}
|
||||
|
||||
void SDLRendererDestroyer::operator()(SDL_Renderer* renderer) const {
|
||||
std::cout << "Destroying renderer" << std::endl;
|
||||
SDL_DestroyRenderer(renderer);
|
||||
}
|
||||
|
||||
void SDLTextureDestroyer::operator()(SDL_Texture* texture) const {
|
||||
std::cout << "Destroying texture" << std::endl;
|
||||
SDL_DestroyTexture(texture);
|
||||
}
|
||||
|
||||
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)) {
|
||||
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
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)) {
|
||||
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
this->window = std::unique_ptr<SDL_Window, SDLWindowDestroyer>(raw_window);
|
||||
this->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());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
ctrPanel.render();
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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 % chip8_width);
|
||||
int y = (i / chip8_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());
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::draw_chip8_widget() {
|
||||
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>(chip8_width));
|
||||
const int scale_y = static_cast<int>(static_cast<float>(available_size.y) / static_cast<float>(chip8_height));
|
||||
const int scale = std::max(1, std::min(scale_x, scale_y));
|
||||
const ImVec2 scaled_size(static_cast<float>(chip8_width * scale), static_cast<float>(chip8_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(static_cast<ImTextureID>(reinterpret_cast<intptr_t>(texture.get())), scaled_size);
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// Created by ryuuji on 6/20/25.
|
||||
//
|
||||
|
||||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
|
||||
#include "Chip8ControlPanel.h"
|
||||
#include "imgui.h"
|
||||
#include "MachineState.h"
|
||||
#include "SDL3/SDL.h"
|
||||
|
||||
struct SDLWindowDestroyer {
|
||||
void operator()(SDL_Window *window) const;
|
||||
};
|
||||
struct SDLRendererDestroyer {
|
||||
void operator()(SDL_Renderer *renderer) const;
|
||||
};
|
||||
struct SDLTextureDestroyer {
|
||||
void operator()(SDL_Texture *texture) const;
|
||||
};
|
||||
|
||||
class Graphics {
|
||||
std::shared_ptr<MachineState> machine_state;
|
||||
|
||||
std::unique_ptr<SDL_Window, SDLWindowDestroyer> window;
|
||||
std::unique_ptr<SDL_Renderer, SDLRendererDestroyer> renderer;
|
||||
std::unique_ptr<SDL_Texture, SDLTextureDestroyer> texture;
|
||||
|
||||
Chip8ControlPanel ctrPanel;
|
||||
|
||||
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();
|
||||
|
||||
public:
|
||||
Graphics(std::shared_ptr<MachineState> machine_state);
|
||||
|
||||
bool init();
|
||||
void draw();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //RENDERER_H
|
||||
@@ -1,496 +0,0 @@
|
||||
#include "../Interpreter.h"
|
||||
|
||||
#include <format>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <stack>
|
||||
#include <bits/ostream.tcc>
|
||||
|
||||
#include "SDL3/SDL_log.h"
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void Interpreter::load_fonts() {
|
||||
constexpr uint8_t font_set[] = {
|
||||
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
|
||||
0x20, 0x60, 0x20, 0x20, 0x70, // 1
|
||||
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
|
||||
0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
|
||||
0x90, 0x90, 0xF0, 0x10, 0x10, // 4
|
||||
0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
|
||||
0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
|
||||
0xF0, 0x10, 0x20, 0x40, 0x40, // 7
|
||||
0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
|
||||
0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
|
||||
0xF0, 0x90, 0xF0, 0x90, 0x90, // A
|
||||
0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
|
||||
0xF0, 0x80, 0x80, 0x80, 0xF0, // C
|
||||
0xE0, 0x90, 0x90, 0x90, 0xE0, // D
|
||||
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
|
||||
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
|
||||
};
|
||||
|
||||
std::copy(std::begin(font_set), std::end(font_set), machine_state->memory.begin() + 0x050);
|
||||
}
|
||||
|
||||
|
||||
void Interpreter::load_rom(const std::vector<uint8_t>& rom) {
|
||||
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 Instruction instruction = decode_next();
|
||||
|
||||
machine_state->pc += 2;
|
||||
|
||||
execute(instruction);
|
||||
|
||||
if (machine_state->pc >= 0xFFF) {
|
||||
SDL_Log("PC Outside of memory, going back 0x200");
|
||||
machine_state->pc = 0x200;
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::sys_addr(const Instruction& instruction) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
void Interpreter::nop(const Instruction& instruction) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
void Interpreter::cls(const Instruction& instruction) {
|
||||
machine_state->display.fill(false);
|
||||
}
|
||||
|
||||
void Interpreter::ret(const Instruction& instruction) {
|
||||
machine_state->sp -= 1;
|
||||
machine_state->pc = machine_state->stack[machine_state->sp];
|
||||
}
|
||||
|
||||
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::sne_vx_byte(const Instruction& instruction) {
|
||||
if (machine_state->v[instruction.x] != instruction.kk) {
|
||||
machine_state->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::ld_vx_byte(const Instruction& instruction) {
|
||||
machine_state->v[instruction.x] = instruction.kk;
|
||||
}
|
||||
|
||||
void Interpreter::add_vx_byte(const Instruction& instruction) {
|
||||
machine_state->v[instruction.x] += instruction.kk;
|
||||
}
|
||||
|
||||
void Interpreter::ld_vx_vy(const Instruction& instruction) {
|
||||
machine_state->v[instruction.x] = machine_state->v[instruction.y];
|
||||
}
|
||||
|
||||
void Interpreter::or_vx_vy(const Instruction& instruction) {
|
||||
machine_state->v[instruction.x] |= machine_state->v[instruction.y];
|
||||
}
|
||||
|
||||
void Interpreter::and_vx_vy(const Instruction& instruction) {
|
||||
machine_state->v[instruction.x] &= machine_state->v[instruction.y];
|
||||
}
|
||||
|
||||
void Interpreter::xor_vx_vy(const Instruction& instruction) {
|
||||
machine_state->v[instruction.x] ^= machine_state->v[instruction.y];
|
||||
}
|
||||
|
||||
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 {
|
||||
vf = 0;
|
||||
}
|
||||
|
||||
vx += vy;
|
||||
}
|
||||
|
||||
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 {
|
||||
vf = 0;
|
||||
}
|
||||
|
||||
vx -= vy;
|
||||
}
|
||||
|
||||
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 {
|
||||
vf = 0;
|
||||
}
|
||||
|
||||
vx = vx >> 1;
|
||||
}
|
||||
|
||||
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 (vy > vx) {
|
||||
vf = 1;
|
||||
} else {
|
||||
vf = 0;
|
||||
}
|
||||
|
||||
vx = vy - vx;
|
||||
}
|
||||
|
||||
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 {
|
||||
vf = 0;
|
||||
}
|
||||
|
||||
vx = vx << 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 {
|
||||
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) {
|
||||
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];
|
||||
|
||||
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) {
|
||||
if (machine_state->keyboard & (1 << instruction.x)) {
|
||||
machine_state->pc += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::sknp_vx(const Instruction& instruction) {
|
||||
if (!(machine_state->keyboard & (1 << instruction.x))) {
|
||||
machine_state->pc += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::ld_vx_dt(const Instruction& instruction) {
|
||||
machine_state->v[instruction.x] = machine_state->dt;
|
||||
}
|
||||
|
||||
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 (machine_state->keyboard & (1 << key)) {
|
||||
machine_state->v[instruction.x] = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::ld_dt_vx(const Instruction& instruction) {
|
||||
machine_state->dt = machine_state->v[instruction.x];
|
||||
}
|
||||
|
||||
void Interpreter::ld_st_vx(const Instruction& instruction) {
|
||||
machine_state->st = machine_state->v[instruction.x];
|
||||
}
|
||||
|
||||
void Interpreter::add_i_vx(const Instruction& instruction) {
|
||||
machine_state->i += machine_state->v[instruction.x];
|
||||
}
|
||||
|
||||
void Interpreter::ld_f_vx(const Instruction& instruction) {
|
||||
auto& vx = machine_state->v[instruction.x];
|
||||
machine_state->i = ((vx & 0xF) * 5) + 0x50;
|
||||
}
|
||||
|
||||
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::ld_i_vx(const Instruction& instruction) {
|
||||
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) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
#ifndef INTERPRETER_H
|
||||
#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;
|
||||
uint8_t quirks;
|
||||
|
||||
void load_fonts();
|
||||
|
||||
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(std::shared_ptr<MachineState> machine_state, uint8_t quirks);
|
||||
|
||||
void load_rom(const std::vector<uint8_t>& rom);
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
|
||||
#endif //INTERPRETER_H
|
||||
@@ -1,19 +0,0 @@
|
||||
#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
|
||||
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// Created by ryuuji on 6/21/25.
|
||||
//
|
||||
|
||||
#ifndef BITOPS_H
|
||||
#define BITOPS_H
|
||||
#include <type_traits>
|
||||
|
||||
template <typename T>
|
||||
T bit_set(const T number, const int bit) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned integral type");
|
||||
return number | (1 << bit);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T bit_clear(const T number, const int bit) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned integral type");
|
||||
return number & ~(1 << bit);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T bit_toggle(const T number, const int bit) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned integral type");
|
||||
return number ^ (1 << bit);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool bit_check(const T number, const int bit) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned integral type");
|
||||
return (number >> bit) & 1;
|
||||
}
|
||||
|
||||
#endif //BITOPS_H
|
||||
@@ -1,51 +0,0 @@
|
||||
#define SDL_MAIN_USE_CALLBACKS 1
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#include "Chip8.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
|
||||
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
||||
const auto chip8 = new Chip8();
|
||||
*appstate = chip8;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppIterate(void* appstate) {
|
||||
const auto chip8 = static_cast<Chip8*>(appstate);
|
||||
|
||||
chip8->iterate();
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (event->type == SDL_EVENT_KEY_DOWN) {
|
||||
chip8->on_keydown(event->key.scancode);
|
||||
}
|
||||
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
|
||||
const auto chip8 = static_cast<Chip8*>(appstate);
|
||||
delete chip8;
|
||||
}
|
||||
Reference in New Issue
Block a user