#include "Interpreter.h" #include #include Interpreter::Interpreter(std::shared_ptr machine_state) : machine_state{std::move(machine_state)}, random_generator{std::random_device{}()} { srand(time(nullptr)); for (bool& display : this->machine_state->display) { display = rand() % 100 > 50; } } uint16_t Interpreter::get_word(uint16_t address) const { const uint8_t high_word = this->machine_state->memory[address]; const uint8_t low_word = this->machine_state->memory[address + 1]; const uint16_t word = high_word << 8 | low_word; return word; } void Interpreter::tick() { auto word = this->get_word(this->machine_state->pc); const auto instruction = this->decode(word, this->machine_state->pc); this->machine_state->pc += 2; this->execute_instruction(instruction); if (this->machine_state->pc >= 0xFFF) { std::cout << "PC Outside of memory, going back 0x200" << std::endl; this->machine_state->pc = 0x200; } } Instruction Interpreter::decode(const uint16_t word, const uint16_t address) const { const uint8_t operation = (word & 0xF000) >> 12; const uint8_t x = (word & 0x0F00) >> 8; const uint8_t y = (word & 0x00F0) >> 4; const uint8_t n = word & 0x000F; const uint8_t kk = word & 0x00FF; const uint16_t nnn = word & 0x0FFF; auto op_code = OpCode::NOP; if (operation == 0) { if (kk == 0xE0) { op_code = OpCode::CLS; } else if (kk == 0xEE) { op_code = OpCode::RET; } else { op_code = OpCode::SYS_ADDR; } } else if (operation == 1) { op_code = OpCode::JP_ADDR; } else if (operation == 2) { op_code = OpCode::CALL_ADDR; } else if (operation == 3) { op_code = OpCode::SE_VX_BYTE; } else if (operation == 4) { op_code = OpCode::SNE_VX_BYTE; } else if (operation == 5) { op_code = OpCode::SE_VX_VY; } else if (operation == 6) { op_code = OpCode::LD_VX_BYTE; } else if (operation == 7) { op_code = OpCode::ADD_VX_BYTE; } else if (operation == 8) { if (n == 0) { op_code = OpCode::LD_VX_VY; } else if (n == 1) { op_code = OpCode::OR_VX_VY; } else if (n == 2) { op_code = OpCode::AND_VX_VY; } else if (n == 3) { op_code = OpCode::XOR_VX_VY; } else if (n == 4) { op_code = OpCode::ADD_VX_VY; } else if (n == 5) { op_code = OpCode::SUB_VX_VY; } else if (n == 6) { op_code = OpCode::SHR_VX_VY; } else if (n == 7) { op_code = OpCode::SUBN_VX_VY; } else if (n == 0xE) { op_code = OpCode::SHL_VX_VY; } } else if (operation == 9) { op_code = OpCode::SNE_VX_VY; } else if (operation == 0xA) { op_code = OpCode::LD_I_ADDR; } else if (operation == 0xB) { op_code = OpCode::JP_V0_ADDR; } else if (operation == 0xC) { op_code = OpCode::RND_VX_BYTE; } else if (operation == 0xD) { op_code = OpCode::DRW_VX_VY_NIBBLE; } else if (operation == 0xE) { if (kk == 0x9E) { op_code = OpCode::SKP_VX; } else if (kk == 0xA1) { op_code = OpCode::SKNP_VX; } } else if (operation == 0xF) { if (kk == 0x07) { op_code = OpCode::LD_VX_DT; } else if (kk == 0x0A) { op_code = OpCode::LD_VX_K; } else if (kk == 0x15) { op_code = OpCode::LD_DT_VX; } else if (kk == 0x18) { op_code = OpCode::LD_ST_VX; } else if (kk == 0x1E) { op_code = OpCode::ADD_I_VX; } else if (kk == 0x29) { op_code = OpCode::LD_F_VX; } else if (kk == 0x33) { op_code = OpCode::LD_B_VX; } else if (kk == 0x55) { op_code = OpCode::LD_I_VX; } else if (kk == 0x65) { op_code = OpCode::LD_VX_I; } } return Instruction{ .op_code = op_code, .address = address, .operation = operation, .instruction = word, .x = x, .y = y, .n = n, .kk = kk, .nnn = nnn }; } std::vector Interpreter::disassembly() const { std::vector instructions(std::size(machine_state->memory) / 2); for (std::size_t address = 0; address < std::size(machine_state->memory); address += 2) { const auto word = this->get_word(address); instructions[address / 2] = (this->decode(word, address)); } return instructions; } void Interpreter::execute_instruction(const Instruction& instruction) { if (instruction.op_code == OpCode::CLS) this->cls(); else if (instruction.op_code == OpCode::RET) this->ret(); else if (instruction.op_code == OpCode::SYS_ADDR) this->sys_addr(); else if (instruction.op_code == OpCode::JP_ADDR) this->jp_addr(instruction); else if (instruction.op_code == OpCode::CALL_ADDR) this->call_addr(instruction); else if (instruction.op_code == OpCode::SE_VX_BYTE) this->se_vx_byte(instruction); else if (instruction.op_code == OpCode::SNE_VX_BYTE) this->sne_vx_byte(instruction); else if (instruction.op_code == OpCode::SE_VX_VY) this->se_vx_vy(instruction); else if (instruction.op_code == OpCode::LD_VX_BYTE) this->ld_vx_byte(instruction); else if (instruction.op_code == OpCode::ADD_VX_BYTE) this->add_vx_byte(instruction); else if (instruction.op_code == OpCode::LD_VX_VY) this->ld_vx_vy(instruction); else if (instruction.op_code == OpCode::OR_VX_VY) this->or_vx_vy(instruction); else if (instruction.op_code == OpCode::AND_VX_VY) this->and_vx_vy(instruction); else if (instruction.op_code == OpCode::XOR_VX_VY) this->xor_vx_vy(instruction); else if (instruction.op_code == OpCode::ADD_VX_VY) this->add_vx_vy(instruction); else if (instruction.op_code == OpCode::SUB_VX_VY) this->sub_vx_vy(instruction); else if (instruction.op_code == OpCode::SHR_VX_VY) this->shr_vx_vy(instruction); else if (instruction.op_code == OpCode::SUBN_VX_VY) this->subn_vx_vy(instruction); else if (instruction.op_code == OpCode::SHL_VX_VY) this->shl_vx_vy(instruction); else if (instruction.op_code == OpCode::SNE_VX_VY) this->sne_vx_vy(instruction); else if (instruction.op_code == OpCode::LD_I_ADDR) this->ld_i_addr(instruction); else if (instruction.op_code == OpCode::JP_V0_ADDR) this->jp_v0_addr(instruction); else if (instruction.op_code == OpCode::RND_VX_BYTE) this->rnd_vx_byte(instruction); else if (instruction.op_code == OpCode::DRW_VX_VY_NIBBLE) this->drw_vx_vy_nibble(instruction); else if (instruction.op_code == OpCode::SKP_VX) this->skp_vx(instruction); else if (instruction.op_code == OpCode::SKNP_VX) this->sknp_vx(instruction); else if (instruction.op_code == OpCode::LD_VX_DT) this->ld_vx_dt(instruction); else if (instruction.op_code == OpCode::LD_VX_K) this->ld_vx_k(instruction); else if (instruction.op_code == OpCode::LD_DT_VX) this->ld_dt_vx(instruction); else if (instruction.op_code == OpCode::LD_ST_VX) this->ld_st_vx(instruction); else if (instruction.op_code == OpCode::ADD_I_VX) this->add_i_vx(instruction); else if (instruction.op_code == OpCode::LD_F_VX) this->ld_f_vx(instruction); else if (instruction.op_code == OpCode::LD_B_VX) this->ld_b_vx(instruction); else if (instruction.op_code == OpCode::LD_I_VX) this->ld_i_vx(instruction); else if (instruction.op_code == OpCode::LD_VX_I) this->ld_vx_i(instruction); else if (instruction.op_code == OpCode::NOP) this->nop(); } void Interpreter::sys_addr() const { // NOP } void Interpreter::nop() const { // NOP } void Interpreter::cls() const { this->machine_state->display.fill(false); } void Interpreter::ret() const { this->machine_state->sp -= 1; this->machine_state->pc = this->machine_state->stack[this->machine_state->sp]; this->machine_state->stack[this->machine_state->sp] = 0; } void Interpreter::jp_addr(const Instruction& instruction) const { this->machine_state->pc = instruction.nnn; } void Interpreter::call_addr(const Instruction& instruction) const { this->machine_state->stack[this->machine_state->sp] = this->machine_state->pc; this->machine_state->sp += 1; this->machine_state->pc = instruction.nnn; } void Interpreter::se_vx_byte(const Instruction& instruction) const { if (this->machine_state->v[instruction.x] == instruction.kk) { this->machine_state->pc += 2; } } void Interpreter::sne_vx_byte(const Instruction& instruction) const { if (this->machine_state->v[instruction.x] != instruction.kk) { this->machine_state->pc += 2; } } void Interpreter::se_vx_vy(const Instruction& instruction) const { if (this->machine_state->v[instruction.x] == this->machine_state->v[instruction.y]) { this->machine_state->pc += 2; } } void Interpreter::ld_vx_byte(const Instruction& instruction) const { this->machine_state->v[instruction.x] = instruction.kk; } void Interpreter::add_vx_byte(const Instruction& instruction) const { auto& vx = this->machine_state->v[instruction.x]; vx = (vx + instruction.kk) & 0xFF; } void Interpreter::ld_vx_vy(const Instruction& instruction) const { this->machine_state->v[instruction.x] = this->machine_state->v[instruction.y]; } void Interpreter::or_vx_vy(const Instruction& instruction) const { this->machine_state->v[instruction.x] |= this->machine_state->v[instruction.y]; } void Interpreter::and_vx_vy(const Instruction& instruction) const { this->machine_state->v[instruction.x] &= this->machine_state->v[instruction.y]; } void Interpreter::xor_vx_vy(const Instruction& instruction) const { this->machine_state->v[instruction.x] ^= this->machine_state->v[instruction.y]; } void Interpreter::add_vx_vy(const Instruction& instruction) const { auto& vx = this->machine_state->v[instruction.x]; const auto& vy = this->machine_state->v[instruction.y]; auto& vf = this->machine_state->v[0xF]; if (vx + vy > 0x100) { vf = 1; } else { vf = 0; } vx = (vx + vy) & 0xFF; } void Interpreter::sub_vx_vy(const Instruction& instruction) const { auto& vx = this->machine_state->v[instruction.x]; const auto& vy = this->machine_state->v[instruction.y]; auto& vf = this->machine_state->v[0xF]; if (vx >= vy) { vf = 1; } else { vf = 0; } vx = (vx - vy) & 0xFF; } void Interpreter::shr_vx_vy(const Instruction& instruction) const { auto& vx = this->machine_state->v[instruction.x]; const auto& vy = this->machine_state->v[instruction.y]; auto& vf = this->machine_state->v[0xF]; if (quirks & static_cast(InterpreterQuirks::COSMAC_SHIFT)) { vx = vy; } if (vx & 0x01) { vf = 1; } else { vf = 0; } vx = vx >> 1; } void Interpreter::subn_vx_vy(const Instruction& instruction) const { auto& vx = this->machine_state->v[instruction.x]; const auto& vy = this->machine_state->v[instruction.y]; auto& vf = this->machine_state->v[0xF]; if (vy >= vx) { vf = 1; } else { vf = 0; } vx = (vy - vx) & 0xFF; } void Interpreter::shl_vx_vy(const Instruction& instruction) const { auto& vx = this->machine_state->v[instruction.x]; const auto& vy = this->machine_state->v[instruction.y]; auto& vf = this->machine_state->v[0xF]; if (this->quirks & static_cast(InterpreterQuirks::COSMAC_SHIFT)) { vx = vy; } if (vx & 0x80) { vf = 1; } else { vf = 0; } vx = vx << 1 & 0xFF; } void Interpreter::sne_vx_vy(const Instruction& instruction) const { if (this->machine_state->v[instruction.x] != this->machine_state->v[instruction.y]) { this->machine_state->pc += 2; } } void Interpreter::ld_i_addr(const Instruction& instruction) const { this->machine_state->i = instruction.nnn; } void Interpreter::jp_v0_addr(const Instruction& instruction) const { if (this->quirks & static_cast(InterpreterQuirks::SUPER_CHIP_JUMP)) { this->machine_state->pc = instruction.nnn + this->machine_state->v[instruction.x]; } else { this->machine_state->pc = instruction.nnn + this->machine_state->v[0]; } } void Interpreter::rnd_vx_byte(const Instruction& instruction) { auto distribution = std::uniform_int_distribution(0, 0xFF); const auto value = distribution(this->random_generator); this->machine_state->v[instruction.x] = value & instruction.kk; } void Interpreter::drw_vx_vy_nibble(const Instruction& instruction) const { const auto& memory = this->machine_state->memory; auto& display = this->machine_state->display; const auto& vx = this->machine_state->v[instruction.x]; const auto& vy = this->machine_state->v[instruction.y]; auto& vf = this->machine_state->v[0xF]; const auto& i = this->machine_state->i; 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]; for (auto bit = 0; bit < 8; bit++) { const auto current_x = start_x + bit; if (current_x > 63) { break; } const auto pixel = sprite_byte >> (7 - bit) & 0x01; const auto index = current_y * 64 + current_x; if (pixel) { if (display[index]) { display[index] = false; vf = 1; } else { display[index] = true; } } } } } void Interpreter::skp_vx(const Instruction& instruction) const { if (this->machine_state->keyboard & 1 << instruction.x) { this->machine_state->pc += 2; } } void Interpreter::sknp_vx(const Instruction& instruction) const { if (!(this->machine_state->keyboard & 1 << instruction.x)) { this->machine_state->pc += 2; } } void Interpreter::ld_vx_dt(const Instruction& instruction) const { this->machine_state->v[instruction.x] = this->machine_state->dt; } void Interpreter::ld_vx_k(const Instruction& instruction) const { if (this->machine_state->keyboard == 0) { this->machine_state->pc -= 2; return; } for (auto key = 0; key < 16; key++) { if (this->machine_state->keyboard & 1 << key) { this->machine_state->v[instruction.x] = key; break; } } } void Interpreter::ld_dt_vx(const Instruction& instruction) const { this->machine_state->dt = this->machine_state->v[instruction.x]; } void Interpreter::ld_st_vx(const Instruction& instruction) const { this->machine_state->st = this->machine_state->v[instruction.x]; } void Interpreter::add_i_vx(const Instruction& instruction) const { this->machine_state->i += this->machine_state->v[instruction.x]; } void Interpreter::ld_f_vx(const Instruction& instruction) const { this->machine_state->i = (this->machine_state->v[instruction.x] & 0xF) * 5 + 0x50; } void Interpreter::ld_b_vx(const Instruction& instruction) const { const auto number = this->machine_state->v[instruction.x]; this->machine_state->memory[this->machine_state->i] = number / 100; this->machine_state->memory[this->machine_state->i + 1] = (number - this->machine_state->memory[this->machine_state ->i] * 100) / 10; this->machine_state->memory[this->machine_state->i + 2] = number - this->machine_state->memory[this->machine_state-> i] * 100 - this->machine_state->memory[this->machine_state->i + 1] * 10; } void Interpreter::ld_i_vx(const Instruction& instruction) const { const bool use_quirk = this->quirks & static_cast(InterpreterQuirks::COSMAC_STORE_AND_LOAD); for (auto reg = 0; reg <= instruction.x; reg++) { if (use_quirk) { this->machine_state->memory[this->machine_state->i] = this->machine_state->v[reg]; this->machine_state->i++; } else { this->machine_state->memory[this->machine_state->i + reg] = this->machine_state->v[reg]; } } } void Interpreter::ld_vx_i(const Instruction& instruction) const { const bool use_quirk = this->quirks & static_cast(InterpreterQuirks::COSMAC_STORE_AND_LOAD); for (auto reg = 0; reg <= instruction.x; reg++) { if (use_quirk) { this->machine_state->v[reg] = this->machine_state->memory[this->machine_state->i]; this->machine_state->i++; } else { this->machine_state->v[reg] = this->machine_state->memory[this->machine_state->i + reg]; } } }