diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
index 0d48d42..2d5e37e 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -246,9 +246,12 @@
+
+
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 130056a..8fe3209 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -3,5 +3,6 @@
+
\ No newline at end of file
diff --git a/src/Chip8.cpp b/src/Chip8.cpp
index 52078df..294e10c 100644
--- a/src/Chip8.cpp
+++ b/src/Chip8.cpp
@@ -7,7 +7,9 @@
#include "bitops.h"
-Chip8::Chip8(): target_cycle_time{1.0 / 700.0},
+Chip8::Chip8(): machine_state{std::make_shared()},
+ interpreter{machine_state, 0},
+ target_cycle_time{1.0 / 700.0},
last_update_time{0},
accumulator{0},
run{false},
@@ -21,7 +23,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 +54,22 @@ std::vector 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 +79,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 +104,7 @@ void Chip8::update() {
}
- graphics.draw(interpreter.display, buffer.str());
+ graphics.draw(machine_state->display, buffer.str());
}
void Chip8::execute_instructions() {
diff --git a/src/Chip8.h b/src/Chip8.h
index d36e78c..dc713cd 100644
--- a/src/Chip8.h
+++ b/src/Chip8.h
@@ -7,6 +7,7 @@
class Chip8 {
+ std::shared_ptr machine_state;
Graphics graphics;
Interpreter interpreter;
diff --git a/src/Graphics.cpp b/src/Graphics.cpp
index 6e2a055..c0559bd 100644
--- a/src/Graphics.cpp
+++ b/src/Graphics.cpp
@@ -4,6 +4,7 @@
#include "Graphics.h"
+#include
#include
void SDLWindowDestroyer::operator()(SDL_Window* window) const {
@@ -50,7 +51,7 @@ bool Graphics::init() {
return true;
}
-void Graphics::draw(std::bitset<2048> display, std::string info) {
+void Graphics::draw(std::array display, std::string info) {
SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer.get());
@@ -60,7 +61,7 @@ void Graphics::draw(std::bitset<2048> display, std::string info) {
SDL_RenderPresent(renderer.get());
}
-void Graphics::draw_display(std::bitset<2048> display) {
+void Graphics::draw_display(std::array display) {
SDL_Surface* surface = nullptr;
if (SDL_LockTextureToSurface(texture.get(), nullptr, &surface)) {
diff --git a/src/Graphics.h b/src/Graphics.h
index 729c931..40b0889 100644
--- a/src/Graphics.h
+++ b/src/Graphics.h
@@ -28,12 +28,12 @@ class Graphics {
int height;
int scale;
- void draw_display(std::bitset<2048> display);
+ void draw_display(std::array display);
void draw_info(std::string info);
public:
Graphics();
bool init();
- void draw(std::bitset<2048> display, std::string info);
+ void draw(std::array display, std::string info);
};
diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp
index b363847..3784b17 100644
--- a/src/Interpreter.cpp
+++ b/src/Interpreter.cpp
@@ -4,21 +4,14 @@
#include
#include
#include
+#include
#include
#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 machine_state, uint8_t quirks):
+ machine_state{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& 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(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(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(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(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(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(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(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(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(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];
}
}
}
diff --git a/src/Interpreter.h b/src/Interpreter.h
index e056821..02b8114 100644
--- a/src/Interpreter.h
+++ b/src/Interpreter.h
@@ -2,77 +2,120 @@
#define INTERPRETER_H
#include
#include
+#include
#include
#include
+#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 machine_state;
+
std::mt19937 random_generator;
-
-public:
- std::vector memory;
- std::vector v;
- std::vector 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 machine_state, uint8_t quirks);
- void set_quirks(uint8_t quirks);
void load_rom(const std::vector& rom);
void run();
diff --git a/src/MachineState.h b/src/MachineState.h
new file mode 100644
index 0000000..1aeebce
--- /dev/null
+++ b/src/MachineState.h
@@ -0,0 +1,19 @@
+#ifndef MACHINESTATE_H
+#define MACHINESTATE_H
+#include
+#include
+
+struct MachineState {
+ std::array memory{};
+ std::array v{};
+ std::array stack{};
+ std::array 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