123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- #include "Chip8.hpp"
- #include <chrono>
- #include <cstdint>
- #include <cstring>
- #include <fstream>
- #include <random>
-
-
- const unsigned int FONTSET_SIZE = 80;
- const unsigned int FONTSET_START_ADDRESS = 0x50;
- const unsigned int START_ADDRESS = 0x200;
-
-
- uint8_t fontset[FONTSET_SIZE] =
- {
- 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
- };
-
-
- Chip8::Chip8()
- : randGen(std::chrono::system_clock::now().time_since_epoch().count())
- {
- // Initialize PC
- pc = START_ADDRESS;
-
- // Load fonts into memory
- for (unsigned int i = 0; i < FONTSET_SIZE; ++i)
- {
- memory[FONTSET_START_ADDRESS + i] = fontset[i];
- }
-
- // Initialize RNG
- randByte = std::uniform_int_distribution<uint8_t>(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;
-
- 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;
-
- 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(char const* filename)
- {
- std::ifstream file(filename, std::ios::binary | std::ios::ate);
-
- if (file.is_open())
- {
- std::streampos size = file.tellg();
- char* buffer = new char[size];
- file.seekg(0, std::ios::beg);
- file.read(buffer, size);
- file.close();
-
- for (long 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::Table0()
- {
- ((*this).*(table0[opcode & 0x000Fu]))();
- }
-
- void Chip8::Table8()
- {
- ((*this).*(table8[opcode & 0x000Fu]))();
- }
-
- void Chip8::TableE()
- {
- ((*this).*(tableE[opcode & 0x000Fu]))();
- }
-
- void Chip8::TableF()
- {
- ((*this).*(tableF[opcode & 0x00FFu]))();
- }
-
- void Chip8::OP_NULL()
- {}
-
- 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];
-
- if (sum > 255U)
- {
- registers[0xF] = 1;
- }
- else
- {
- registers[0xF] = 0;
- }
-
- registers[Vx] = sum & 0xFFu;
- }
-
- void Chip8::OP_8xy5()
- {
- uint8_t Vx = (opcode & 0x0F00u) >> 8u;
- uint8_t Vy = (opcode & 0x00F0u) >> 4u;
-
- if (registers[Vx] > registers[Vy])
- {
- registers[0xF] = 1;
- }
- else
- {
- registers[0xF] = 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;
-
- if (registers[Vy] > registers[Vx])
- {
- registers[0xF] = 1;
- }
- else
- {
- registers[0xF] = 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;
- uint8_t byte = opcode & 0x00FFu;
-
- 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 & 0x000Fu;
-
- // Wrap if going beyond screen boundaries
- uint8_t xPos = registers[Vx] % VIDEO_WIDTH;
- uint8_t yPos = registers[Vy] % VIDEO_HEIGHT;
-
- registers[0xF] = 0;
-
- for (unsigned int row = 0; row < height; ++row)
- {
- uint8_t spriteByte = memory[index + row];
-
- for (unsigned int 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;
- }
-
- // Effectively XOR with the sprite pixel
- *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);
- }
-
- void Chip8::OP_Fx33()
- {
- uint8_t Vx = (opcode & 0x0F00u) >> 8u;
- uint8_t value = registers[Vx];
-
- // Ones-place
- memory[index + 2] = value % 10;
- value /= 10;
-
- // Tens-place
- memory[index + 1] = value % 10;
- value /= 10;
-
- // Hundreds-place
- 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];
- }
- }
|