Files
CHIP-8/src/Interpreter.cpp

489 lines
11 KiB
C++

#include "Interpreter.h"
#include <format>
#include <iomanip>
#include <ios>
#include <iostream>
#include <bits/ostream.tcc>
#include "SDL3/SDL_log.h"
Interpreter::Interpreter():
memory(4096, 0),
v(16, 0),
stack(16, 0),
pc(0x200),
sp(0),
i(0),
dt(0),
st(0),
keyboard(0),
quirks(0) {
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), memory.begin() + 0x050);
}
void Interpreter::set_quirks(uint8_t quirks) {
this->quirks = quirks;
}
void Interpreter::load_rom(const std::vector<uint8_t>& rom) {
std::copy(std::begin(rom), std::end(rom), memory.begin() + 0x200);
}
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 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;
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;
switch (opcode) {
case 0:
if (kk == 0xE0) {
clear_screen();
} else if (kk == 0xEE) {
return_from_subrutine();
}
break;
case 1:
jump(nnn);
break;
case 2: call_subrutine(nnn);
break;
case 3:
skip_if_vx_eq_val(x, kk);
break;
case 4:
skip_if_vx_neq_val(x, kk);
break;
case 5:
skip_if_vx_eq_vy(x, y);
break;
case 6:
load_vx_val(x, kk);
break;
case 7:
add_vx_val(x, kk);
break;
case 8:
switch (n) {
case 0:
load_vx_vy(x, y);
break;
case 1:
vx_or_vy(x, y);
break;
case 2:
vx_and_vy(x, y);
break;
case 3:
vx_xor_vy(x, y);
break;
case 4:
vx_add_vy(x, y);
break;
case 5:
vx_sub_vy(x, y);
break;
case 6:
if (quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_SHIFT)) {
vx_shift_right_cosmac(x, y);
} else {
vx_shift_right(x);
}
break;
case 7:
vx_subn_vy(x, y);
break;
case 0xE:
if (quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_SHIFT)) {
vx_shift_left_cosmac(x, y);
} else {
vx_shift_left(x);
}
break;
}
break;
case 9:
skip_if_vx_neq_vy(x, y);
break;
case 0xA:
set_index_val(nnn);
break;
case 0xB:
if (quirks & static_cast<int>(InterpreterQuirks::SUPER_CHIP_JUMP)) {
jump_with_offset_super_chip(x, nnn);
} else {
jump_with_offset(nnn);
}
break;
case 0XC:
set_random_value(x, kk);
break;
case 0xD:
display_sprite(x, y, n);
break;
case 0xE:
if (kk == 0x9E) {
skip_if_key_pressed(x);
} else if (kk == 0xA1) {
skip_if_key_not_pressed(x);
}
case 0xF:
switch (kk) {
case 0x07:
load_vx_dt(x);
break;
case 0x0A:
get_key(x);
break;
case 0x15:
load_dt_vx(x);
break;
case 0x18:
load_st_vx(x);
break;
case 0x1E:
add_i_vx(x);
break;
case 0x29:
font_sprite_location(x);
break;
case 0x33:
calculate_bcd(x);
break;
case 0x55:
store_registers_to_memory(x);
break;
case 0x65:
load_registers_from_memory(x);
break;
}
break;
}
if (pc >= 0xFFF) {
SDL_Log("PC Outside of memory, going back 0x200");
pc = 0x200;
}
}
void Interpreter::clear_screen() {
display.reset();
}
void Interpreter::return_from_subrutine() {
sp -= 1;
pc = stack[sp];
}
void Interpreter::jump(const uint16_t nnn) {
pc = nnn;
}
void Interpreter::call_subrutine(const uint16_t nnn) {
stack[sp] = pc;
sp += 1;
pc = nnn;
}
void Interpreter::skip_if_vx_eq_val(const uint8_t x, const uint8_t kk) {
if (v[x] == kk) {
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::skip_if_vx_eq_vy(const uint8_t x, const uint8_t y) {
if (v[x] == v[y]) {
pc += 2;
}
}
void Interpreter::load_vx_val(const uint8_t x, const uint8_t kk) {
v[x] = kk;
}
void Interpreter::add_vx_val(const uint8_t x, const uint8_t kk) {
v[x] += kk;
}
void Interpreter::load_vx_vy(const uint8_t x, const uint8_t y) {
v[x] = v[y];
}
void Interpreter::vx_or_vy(const uint8_t x, const uint8_t y) {
v[x] = v[x] | v[y];
}
void Interpreter::vx_and_vy(const uint8_t x, const uint8_t y) {
v[x] = v[x] & v[y];
}
void Interpreter::vx_xor_vy(const uint8_t x, const uint8_t y) {
v[x] = v[x] ^ v[y];
}
void Interpreter::vx_add_vy(const uint8_t x, const uint8_t y) {
if (v[x] + v[y] > 0xFF) {
v[0xF] = 1;
} else {
v[0xF] = 0;
}
v[x] = (v[x] + v[y]);
}
void Interpreter::vx_sub_vy(const uint8_t x, const uint8_t y) {
if (v[x] > v[y]) {
v[0xF] = 1;
} else {
v[0xF] = 0;
}
v[x] = v[x] - v[y];
}
void Interpreter::vx_shift_right(const uint8_t x) {
if (v[x] & 0x01) {
v[0xF] = 1;
} else {
v[0xF] = 0;
}
v[x] = v[x] >> 1;
}
void Interpreter::vx_shift_right_cosmac(const uint8_t x, const uint8_t y) {
v[x] = v[y];
if (v[x] & 0x01) {
v[0xF] = 1;
} else {
v[0xF] = 0;
}
v[x] = v[x] >> 1;
}
void Interpreter::vx_subn_vy(const uint8_t x, const uint8_t y) {
if (v[y] > v[x]) {
v[0xF] = 1;
} else {
v[0xF] = 0;
}
v[x] = v[y] - v[x];
}
void Interpreter::vx_shift_left(const uint8_t x) {
if (v[x] & 0x80) {
v[0xF] = 1;
} 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;
}
}
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) {
auto random = std::uniform_int_distribution<uint8_t>(0, 0xFF);
const auto value = random(random_generator);
v[x] = value & 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;
for (auto row = 0; row < n; row++) {
auto current_y = start_y + row;
if (current_y > 31) {
break;
}
const auto sprite_byte = memory[i + row];
for (auto bit = 0; bit < 8; bit++) {
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.set(index, false);
v[0xF] = 1;
} else {
display.set(index, true);
}
}
}
}
}
void Interpreter::skip_if_key_pressed(const uint8_t x) {
if (keyboard & (1 << x)) {
pc += 2;
}
}
void Interpreter::skip_if_key_not_pressed(const uint8_t x) {
if (!(keyboard & (1 << x))) {
pc += 2;
}
}
void Interpreter::load_vx_dt(const uint8_t x) {
v[x] = dt;
}
void Interpreter::get_key(const uint8_t x) {
if (keyboard == 0) {
pc -= 2;
return;
}
for (auto key = 0; key < 16; key++) {
if (keyboard & (1 << key)) {
v[x] = key;
break;
}
}
}
void Interpreter::load_dt_vx(const uint8_t x) {
dt = v[x];
}
void Interpreter::load_st_vx(const uint8_t x) {
st = v[x];
}
void Interpreter::add_i_vx(const uint8_t x) {
i = i + v[x];
}
void Interpreter::font_sprite_location(const uint8_t x) {
i = ((v[x] & 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::store_registers_to_memory(const uint8_t x) {
bool use_quirk = quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
for (auto reg = 0; reg <= x; reg++) {
if (use_quirk) {
memory[i] = v[reg];
i++;
} else {
memory[i + reg] = v[reg];
}
}
}
void Interpreter::load_registers_from_memory(const uint8_t x) {
bool use_quirk = quirks & static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
for (auto reg = 0; reg <= x; reg++) {
if (use_quirk) {
v[reg] = memory[i];
i++;
} else {
v[reg] = memory[i + reg];
}
}
}