#include "chip8.hpp" #include "dbc.hpp" #include Chip8::Chip8() : randGen(std::chrono::system_clock::now().time_since_epoch().count()) { pc = START_ADDRESS; for(size_t i = 0; i < FONTSET_SIZE; ++i) { memory[FONTSET_START_ADDRESS + i] = fontset[i]; } randByte = std::uniform_int_distribution(0, 255U); // Set up function pointer table table[0x0] = &Chip8::Table0; table[0x1] = &Chip8::OP_1nnn; table[0x2] = &Chip8::OP_2nnn; table[0x3] = &Chip8::OP_3xkk; table[0x4] = &Chip8::OP_4xkk; table[0x5] = &Chip8::OP_5xy0; table[0x6] = &Chip8::OP_6xkk; table[0x7] = &Chip8::OP_7xkk; table[0x8] = &Chip8::Table8; table[0x9] = &Chip8::OP_9xy0; table[0xA] = &Chip8::OP_Annn; table[0xB] = &Chip8::OP_Bnnn; table[0xC] = &Chip8::OP_Cxkk; table[0xD] = &Chip8::OP_Dxyn; table[0xE] = &Chip8::TableE; table[0xF] = &Chip8::TableF; for (size_t i = 0; i <= 0xE; i++) { table0[i] = &Chip8::OP_NULL; table8[i] = &Chip8::OP_NULL; tableE[i] = &Chip8::OP_NULL; } table0[0x0] = &Chip8::OP_00E0; table0[0xE] = &Chip8::OP_00EE; table8[0x0] = &Chip8::OP_8xy0; table8[0x1] = &Chip8::OP_8xy1; table8[0x2] = &Chip8::OP_8xy2; table8[0x3] = &Chip8::OP_8xy3; table8[0x4] = &Chip8::OP_8xy4; table8[0x5] = &Chip8::OP_8xy5; table8[0x6] = &Chip8::OP_8xy6; table8[0x7] = &Chip8::OP_8xy7; table8[0xE] = &Chip8::OP_8xyE; tableE[0x1] = &Chip8::OP_ExA1; tableE[0xE] = &Chip8::OP_Ex9E; for (size_t i = 0; i <= 0x65; i++) { tableF[i] = &Chip8::OP_NULL; } tableF[0x07] = &Chip8::OP_Fx07; tableF[0x0A] = &Chip8::OP_Fx0A; tableF[0x15] = &Chip8::OP_Fx15; tableF[0x18] = &Chip8::OP_Fx18; tableF[0x1E] = &Chip8::OP_Fx1E; tableF[0x29] = &Chip8::OP_Fx29; tableF[0x33] = &Chip8::OP_Fx33; tableF[0x55] = &Chip8::OP_Fx55; tableF[0x65] = &Chip8::OP_Fx65; } void Chip8::LoadROM(const std::string& filename) { std::ifstream file(filename, std::ios::binary | std::ios::ate); dbc::check(file.is_open(), $F("Can't open {}", filename)); size_t size = file.tellg(); char* buffer = new char[size]; file.seekg(0, std::ios::beg); file.read(buffer, size); file.close(); for(size_t i = 0; i < size; ++i) { memory[START_ADDRESS + i] = buffer[i]; } delete[] buffer; } void Chip8::Cycle() { // Fetch opcode = (memory[pc] << 8u) | memory[pc + 1]; // Increment the PC before we execute anything pc += 2; // decode and Execute ((*this).*(table[(opcode & 0xF000u) >> 12u]))(); // Decrement the delay timer if it's been set if(delayTimer > 0) { --delayTimer; } // decrement the sound timer if it's been set if(soundTimer > 0) { --soundTimer; } } void Chip8::OP_00E0() { memset(video, 0, sizeof(video)); } void Chip8::OP_00EE() { --sp; pc = stack[sp]; } void Chip8::OP_1nnn() { uint16_t address = opcode & 0x0FFFu; pc = address; } void Chip8::OP_2nnn() { uint16_t address = opcode & 0x0FFFu; stack[sp] = pc; ++sp; pc = address; } void Chip8::OP_3xkk() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t byte = opcode & 0x00FFu; if(registers[Vx] == byte) { pc += 2; } } void Chip8::OP_4xkk() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t byte = opcode & 0x00FFu; if(registers[Vx] != byte) { pc += 2; } } void Chip8::OP_5xy0() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; if(registers[Vx] == registers[Vy]) { pc += 2; } } void Chip8::OP_6xkk() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t byte = opcode & 0x00FFu; registers[Vx] = byte; } void Chip8::OP_7xkk() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t byte = opcode & 0x00FFu; registers[Vx] += byte; } void Chip8::OP_8xy0() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; registers[Vx] = registers[Vy]; } void Chip8::OP_8xy1() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; registers[Vx] |= registers[Vy]; } void Chip8::OP_8xy2() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; registers[Vx] &= registers[Vy]; } void Chip8::OP_8xy3() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; registers[Vx] ^= registers[Vy]; } void Chip8::OP_8xy4() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; uint16_t sum = registers[Vx] + registers[Vy]; registers[0xF] = sum > 255U ? 1 : 0; registers[Vx] = sum & 0xFFu; } void Chip8::OP_8xy5() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; registers[0xF] = registers[Vx] > registers[Vy] ? 1 : 0; registers[Vx] -= registers[Vy]; } void Chip8::OP_8xy6() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; // Save LSB in VF registers[0xF] = (registers[Vx] & 0x1u); registers[Vx] >>= 1; } void Chip8::OP_8xy7() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; registers[0xF] = registers[Vy] > registers[Vx] ? 1 : 0; registers[Vx] = registers[Vy] - registers[Vx]; } void Chip8::OP_8xyE() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; // Save MSB in VF registers[0xF] = (registers[Vx] & 0x80u) >> 7u; registers[Vx] <<= 1; } void Chip8::OP_9xy0() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; if(registers[Vx] != registers[Vy]) { pc += 2; } } void Chip8::OP_Annn() { uint16_t address = opcode & 0x0FFFu; index = address; } void Chip8::OP_Bnnn() { uint16_t address = opcode & 0x0FFFu; pc = registers[0] + address; } void Chip8::OP_Cxkk() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint16_t byte = opcode & 0x0FFFu; registers[Vx] = randByte(randGen) & byte; } void Chip8::OP_Dxyn() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t Vy = (opcode & 0x00F0u) >> 4u; uint8_t height = opcode & 0x00Fu; // wrap if going beyond the screen boundaries uint8_t xPos = registers[Vx] % VIDEO_WIDTH; uint8_t yPos = registers[Vy] % VIDEO_HEIGHT; registers[0xF] = 0; for(size_t row = 0; row < height; ++row) { uint8_t spriteByte = memory[index + row]; for(size_t col = 0; col < 8; ++col) { uint8_t spritePixel = spriteByte & (0x80u >> col); uint32_t* screenPixel = &video[(yPos + row) * VIDEO_WIDTH + (xPos + col)]; // Sprite pixel is on if(spritePixel) { // Screen pixel also on - collision if(*screenPixel == 0xFFFFFFFF) { registers[0xF] = 1; } *screenPixel ^= 0xFFFFFFFF; } } } } void Chip8::OP_Ex9E() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t key = registers[Vx]; if(keypad[key]) { pc += 2; } } void Chip8::OP_ExA1() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t key = registers[Vx]; if(!keypad[key]) { pc += 2; } } void Chip8::OP_Fx07() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; registers[Vx] = delayTimer; } void Chip8::OP_Fx0A() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; if (keypad[0]) { registers[Vx] = 0; } else if (keypad[1]) { registers[Vx] = 1; } else if (keypad[2]) { registers[Vx] = 2; } else if (keypad[3]) { registers[Vx] = 3; } else if (keypad[4]) { registers[Vx] = 4; } else if (keypad[5]) { registers[Vx] = 5; } else if (keypad[6]) { registers[Vx] = 6; } else if (keypad[7]) { registers[Vx] = 7; } else if (keypad[8]) { registers[Vx] = 8; } else if (keypad[9]) { registers[Vx] = 9; } else if (keypad[10]) { registers[Vx] = 10; } else if (keypad[11]) { registers[Vx] = 11; } else if (keypad[12]) { registers[Vx] = 12; } else if (keypad[13]) { registers[Vx] = 13; } else if (keypad[14]) { registers[Vx] = 14; } else if (keypad[15]) { registers[Vx] = 15; } else { pc -= 2; } } void Chip8::OP_Fx15() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; delayTimer = registers[Vx]; } void Chip8::OP_Fx18() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; soundTimer = registers[Vx]; } void Chip8::OP_Fx1E() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; index += registers[Vx]; } void Chip8::OP_Fx29() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t digit = registers[Vx]; index = FONTSET_START_ADDRESS + (5 * digit); } // WTF? binary coded decimal void Chip8::OP_Fx33() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; uint8_t value = registers[Vx]; memory[index + 2] = value % 10; value /= 10; memory[index + 1] = value % 10; value /= 10; memory[index] = value % 10; } void Chip8::OP_Fx55() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; for(uint8_t i = 0; i <= Vx; ++i) { memory[index + i] = registers[i]; } } void Chip8::OP_Fx65() { uint8_t Vx = (opcode & 0x0F00u) >> 8u; for(uint8_t i = 0; i <= Vx; ++i) { registers[i] = memory[index + i]; } }