Archived
1
Fork 0
This repository has been archived on 2025-04-12. You can view files and clone it, but cannot push or open issues or pull requests.
chip8/src/emu.cpp

447 lines
9.1 KiB
C++
Raw Normal View History

2020-04-29 14:47:33 -04:00
#include "emu.hpp"
#include <cstdio>
#include <iostream>
#include <array>
typedef void (*cpu_func)(const uint16_t opcode);
void null_func(const uint16_t opcode) {
printf("unimplemented: %.4X\n", opcode);
}
template<typename T, size_t Size>
void safe_call(const std::array<T, Size>& array, const int index, const uint16_t opcode) {
if(index < Size) {
array[index](opcode);
} else {
null_func(opcode);
}
}
// 0x00E0
void op_e0(const uint16_t opcode) {
for(int y = 0; y < screen_height; y++) {
for(int x = 0; x < screen_width; x++) {
state.pixels[to_coord(x, y)] = 0;
}
}
state.draw_dirty = true;
state.PC += 2;
}
// 0x00EE
void op_ee(const uint16_t opcode) {
state.stack_pointer--;
state.PC = state.stack[state.stack_pointer];
state.PC += 2;
}
constexpr std::array op0_func = {
op_e0, // e0
null_func,
null_func,
null_func,
null_func,
null_func,
null_func,
null_func,
null_func,
null_func,
null_func,
null_func,
null_func,
null_func,
op_ee // ee
};
void operation0(const uint16_t opcode) {
safe_call(op0_func, opcode & 0x000f, opcode);
}
// 1NNN
void operation1(const uint16_t opcode) {
const uint16_t nnn = opcode & 0x0fff;
state.PC = nnn;
}
// 2NNN
void operation2(const uint16_t opcode) {
const uint16_t nnn = opcode & 0x0fff;
state.stack[state.stack_pointer] = state.PC;
state.stack_pointer++;
state.PC = nnn;
}
// 3XNN
void operation3(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t nn = opcode & 0x00ff;
if(state.v[x] == nn)
state.PC += 4;
else
state.PC += 2;
}
// 4XNN
void operation4(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t nn = opcode & 0x00ff;
if(state.v[x] != nn)
state.PC += 4;
else
state.PC += 2;
}
// 6XNN
void operation6(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t nn = opcode & 0x00ff;
state.v[x] = nn;
state.PC += 2;
}
// &XNN
void operation7(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t nn = opcode & 0x00ff;
state.v[x] += nn;
state.PC += 2;
}
// 8XY0
void op8_func0(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t y = (opcode & 0x00f0) >> 4;
state.v[x] = state.v[y];
state.PC += 2;
}
// 8XY1
void op8_func2(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t y = (opcode & 0x00f0) >> 4;
state.v[x] = state.v[x] & state.v[y];
state.PC += 2;
}
// 8XY3
void op8_func3(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t y = (opcode & 0x00f0) >> 4;
state.v[x] = state.v[x] ^ state.v[y];
state.PC += 2;
}
// 8XY4
void op8_func4(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t y = (opcode & 0x00f0) >> 4;
// TODO: implement tests
if(state.v[y] < (0xFF - state.v[x]))
state.v[0xF] = 1;
else
state.v[0xF] = 0;
state.v[x] += state.v[y];
state.PC += 2;
}
// 8XY5
void op8_func5(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t y = (opcode & 0x00f0) >> 4;
// TODO: implement tests
if(state.v[y] > state.v[x])
state.v[0xF] = 0;
else
state.v[0xF] = 1;
state.v[x] -= state.v[y];
state.PC += 2;
}
// 8XY6
void op8_func6(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
state.v[0xF] = (state.v[x] & 1);
state.v[x] >>= 1;
state.PC += 2;
}
const std::array op8_func = {
op8_func0,
null_func,
op8_func2,
op8_func3,
op8_func4,
op8_func5,
op8_func6
};
void operation8(const uint16_t opcode) {
safe_call(op8_func, opcode & 0x000f, opcode);
}
// 9XY0
void operation9(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t y = (opcode & 0x00f0) >> 4;
if(state.v[x] != state.v[y])
state.PC += 4;
else
state.PC += 2;
}
// ANNN
void operationA(const uint16_t opcode) {
const uint16_t nnn = opcode & 0x0fff;
state.I = nnn;
state.PC += 2;
}
// CXNN
void operationC(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t nn = opcode & 0x00ff;
srand(time(nullptr));
state.v[x] = (rand() % 0xFF) & nn;
state.PC += 2;
}
// DXYN
void operationD(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
const uint8_t y = (opcode & 0x00f0) >> 4;
const uint8_t n = opcode & 0x000f;
const uint8_t x_pos = state.v[x];
const uint8_t y_pos = state.v[y];
const uint8_t height = n;
state.v[0xF] = 0;
state.draw_dirty = true;
for(int y = 0; y < height; y++) {
const uint8_t pixel = state.memory[state.I + y];
for(int x = 0; x < 8; x++) {
const int final_x = (x_pos + x) % screen_width;
const int final_y = (y_pos + y) % screen_height;
if((pixel & (0x80 >> x)) != 0) {
if(state.pixels[to_coord(final_x, final_y)] == 1) {
state.v[0xF] = 1;
if(options.enable_anti_flicker)
state.draw_dirty = false; // anti-flicker mechanism
}
state.pixels[to_coord(final_x, final_y)] ^= 1;
}
}
}
state.PC += 2;
}
// EX9E & EXA1
void operationE(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
switch(opcode & 0x00ff) {
case 0x9E:
{
if(state.keys[state.v[x]] == 1)
state.PC += 4;
else
state.PC += 2;
}
break;
case 0xA1:
{
if(state.keys[state.v[x]] == 0)
state.PC += 4;
else
state.PC += 2;
}
break;
}
}
// FX07
void opF_func7(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
state.v[x] = state.delay_timer;
state.PC += 2;
}
// FX0A
void opF_funcA(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
for(int i = 0; i < 16; i++) {
if(state.keys[i] != 0) {
state.v[x] = i;
state.PC += 2;
}
}
}
// FX55/FX65 & FX15
void opF_func5(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
switch((opcode & 0x00F0) >> 4) {
case 0x6:
{
for(int i = 0; i <= x; i++)
state.v[i] = state.memory[state.I + i];
if(options.emulate_original)
state.I += x + 1;
state.PC += 2;
}
break;
case 0x5:
{
for(int i = 0; i <= x; i++)
state.memory[state.I + i] = state.v[i];
if(options.emulate_original)
state.I += x + 1;
state.PC += 2;
}
break;
case 0x1:
{
state.delay_timer = state.v[x];
state.PC += 2;
}
break;
}
}
// FX18
void opF_func8(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
state.sound_timer = state.v[x];
state.PC += 2;
}
// FX29
void opF_func9(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
state.I = state.v[x] * 0x5;
state.PC += 2;
}
// FX1E
void opF_funcE(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
if((state.I + state.v[x]) > 0xFFF)
state.v[15] = 1;
else
state.v[15] = 0;
state.I += state.v[x];
state.PC += 2;
}
// FX33
void opF_func3(const uint16_t opcode) {
const uint8_t x = (opcode & 0x0f00) >> 8;
int decimal_rep = state.v[x];
state.memory[state.I] = (decimal_rep % 1000) / 100;
state.memory[state.I + 1] = (decimal_rep % 100) / 10;
state.memory[state.I + 2] = (decimal_rep % 10);
state.PC += 2;
}
const std::array opF_func = {
null_func,
null_func,
null_func,
opF_func3,
null_func,
opF_func5,
null_func,
opF_func7,
opF_func8,
opF_func9,
opF_funcA,
null_func,
null_func,
null_func,
opF_funcE
};
void operationF(const uint16_t opcode) {
safe_call(opF_func, opcode & 0x000F, opcode);
}
constexpr std::array cpu_opcode = {
operation0, // 0x0,
operation1, // 0x1
operation2, // 0x2
operation3, // 0x3
operation4, // 0x4
null_func, // 0x5
operation6, // 0x6
operation7, // 0x7
operation8, // 0x8
operation9, // 0x9
operationA, // 0xA
null_func, // 0xB
operationC, // 0xC
operationD, // 0xD
operationE, // 0xE
operationF, // 0xF
};
void process_opcode(const uint16_t opcode) {
safe_call(cpu_opcode, opcode >> 12, opcode);
}
void save_state() {
stored_state = state;
}
void load_state() {
state = stored_state;
}