Emulation of the 1970's Chip-8 machine. The emulator has 7 games that are unmodified from the original Chip-8 format.
Diff: Chip8Emulator.cpp
- Revision:
- 0:bc3f11b1b41f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Chip8Emulator.cpp Sun Feb 08 01:58:57 2015 +0000 @@ -0,0 +1,335 @@ +#include "Chip8Emulator.h" +#include <string.h> +#include "GameInput.h" +#include "Color565.h" + + +DigitalOut led1(P0_18, false); + +const uint8_t Chip8Emulator::_font[] = +{ + 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 +}; + +Chip8Emulator::Chip8Emulator(LCD_ST7735 &screen, const uint8_t *program, uint16_t programSize, uint8_t leftKey, uint8_t rightKey, uint8_t upKey, uint8_t downKey, uint8_t fireKey, uint8_t startKey) : + _bmp(64, 32), + _screen(screen), + _delay(0), + _sound(0), + _pc(0x200), + _sp(0), + _i(0), + _leftKey(leftKey), + _rightKey(rightKey), + _upKey(upKey), + _downKey(downKey), + _fireKey(fireKey), + _startKey(startKey) +{ + memset(_memory, 0, sizeof(_memory)); + memset(_stack, 0, sizeof(_stack)); + memset(_registers, 0, sizeof(_registers)); + + memcpy(_memory, _font, sizeof(_font)); + memcpy(_memory + _pc, program, programSize); +} + +void Chip8Emulator::run() +{ + _screen.clearScreen(0); + _bmp.clear(); + + Timer updateTimer; + updateTimer.start(); + int lastReading = updateTimer.read_ms(); + while(true) + { + int reading = updateTimer.read_ms(); + if (reading - lastReading > 16) + { + if (_delay > 0) --_delay; + if (_sound > 0) --_sound; + lastReading = reading; + } + wait_us(1500); + if (_sound != 0) led1 != led1; + switch(_memory[_pc] & 0xf0) + { + case 0x00: + switch(_memory[_pc + 1]) + { + case 0xe0: + _bmp.clear(); + _screen.clearScreen(); + break; + + case 0xee: + _pc = _stack[--_sp]; + break; + } + break; + + case 0x10: + _pc = ((_memory[_pc] & 0x0f) << 8) | _memory[_pc + 1]; + continue; + + case 0x20: + _stack[_sp++] = _pc; + _pc = ((_memory[_pc] & 0x0f) << 8) | _memory[_pc + 1]; + continue; + + case 0x30: + if (_registers[_memory[_pc] & 0x0f] == _memory[_pc + 1]) + { + _pc += 2; + } + break; + + case 0x40: + if (_registers[_memory[_pc] & 0x0f] != _memory[_pc + 1]) + { + _pc += 2; + } + break; + + case 0x50: + if (_registers[_memory[_pc] & 0x0f] == _registers[(_memory[_pc + 1] & 0x0f) >> 4]) + { + _pc += 2; + } + break; + + case 0x60: + _registers[_memory[_pc] & 0x0f] = _memory[_pc + 1]; + break; + + case 0x70: + _registers[_memory[_pc] & 0x0f] += _memory[_pc + 1]; + break; + + case 0x80: + switch(_memory[_pc + 1] & 0x0f) + { + case 0x00: _registers[_memory[_pc] & 0x0f] = _registers[(_memory[_pc + 1] & 0xf0) >> 4]; break; + case 0x01: _registers[_memory[_pc] & 0x0f] |= _registers[(_memory[_pc + 1] & 0xf0) >> 4]; break; + case 0x02: _registers[_memory[_pc] & 0x0f] &= _registers[(_memory[_pc + 1] & 0xf0) >> 4]; break; + case 0x03: _registers[_memory[_pc] & 0x0f] ^= _registers[(_memory[_pc + 1] & 0xf0) >> 4]; break; + + case 0x04: + { + uint8_t vx = _registers[_memory[_pc] & 0x0f]; + uint8_t vy = _registers[(_memory[_pc + 1] & 0xf0) >> 4]; + uint16_t result = vx + vy; + _registers[0x0f] = (uint8_t)(result >> 8); + _registers[_memory[_pc] & 0x0f] = (uint8_t)result; + } + break; + + case 0x05: + { + uint8_t vx = _registers[_memory[_pc] & 0x0f]; + uint8_t vy = _registers[(_memory[_pc + 1] & 0xf0) >> 4]; + uint16_t result = vx - vy; + _registers[0x0f] = (uint8_t)(((uint8_t)(result >> 8)) + 1); + _registers[_memory[_pc] & 0x0f] = (uint8_t)result; + } + break; + + case 0x06: + _registers[0x0f] = (uint8_t)(_registers[_memory[_pc] & 0x0f] & 0x01); + _registers[_memory[_pc] & 0x0f] >>= 1; + break; + + case 0x07: + _registers[0x0f] = (uint8_t)(_registers[(_memory[_pc + 1] & 0xf0) >> 4] > _registers[_memory[_pc] & 0x0f] ? 1 : 0); + _registers[_memory[_pc] & 0x0f] -= _registers[(_memory[_pc + 1] & 0xf0) >> 4]; + break; + + case 0x0E: + _registers[0x0f] = (uint8_t)(((_registers[_memory[_pc] & 0x0f] & 0x80) != 0) ? 1 : 0); + _registers[_memory[_pc] & 0x0f] <<= 1; + break; + + } + break; + + case 0x90: + if (_registers[_memory[_pc] & 0x0f] != _registers[(_memory[_pc + 1] & 0xf0) >> 4]) + { + _pc += 2; + } + break; + + case 0xA0: + _i = (uint16_t)(((_memory[_pc] & 0x0f) << 8) | _memory[_pc + 1]); + break; + + case 0xB0: + _pc = (uint16_t)((((_memory[_pc] & 0x0f) << 8) | _memory[_pc + 1]) + _registers[0]); + continue; + + case 0xC0: + _registers[_memory[_pc] & 0x0f] = (uint8_t)(rnd() & _memory[_pc + 1]); + break; + + case 0xD0: + { + _registers[0x0f] = 0; + uint8_t x = _registers[_memory[_pc] & 0x0f]; + uint8_t y = _registers[_memory[_pc + 1] >> 4]; + uint8_t n = _memory[_pc + 1] & 0x0f; + for (int i = 0; i < n; i++) + { + plot(x, y + i, _memory[_i + i]); + } + } + break; + + case 0xE0: + switch(_memory[_pc+1]) + { + case 0x9E : + if (isKeyPressed(_registers[_memory[_pc] & 0x0f])) _pc += 2; + break; + + case 0xA1 : + if (!isKeyPressed(_registers[_memory[_pc] & 0x0f])) _pc += 2; + break; + } + break; + + case 0xF0: + switch (_memory[_pc + 1]) + { + case 0x07: + _registers[_memory[_pc] & 0x0f] = _delay; + break; + + case 0x0A: + { + uint8_t key = getKeyPressed(); + if (key != 255) + { + _registers[_memory[_pc] & 0x0f] = key; + } + else + { + _pc -= 2; + } + } + break; + + case 0x15: + _delay = _registers[_memory[_pc] & 0x0f]; + break; + + case 0x18: + _sound = _registers[_memory[_pc] & 0x0f]; + break; + + case 0x1e: + _i += _registers[_memory[_pc] & 0x0f]; + break; + + case 0x29: + _i = (uint8_t)(_registers[_memory[_pc] & 0x0f] * 5); + break; + + case 0x33: + { + uint8_t r = _registers[_memory[_pc] & 0x0f]; + _memory[_i + 2] = (uint8_t)(r % 10); + r /= 10; + _memory[_i + 1] = (uint8_t)(r % 10); + r /= 10; + _memory[_i] = (uint8_t)(r % 10); + } + break; + + case 0x55: + { + uint8_t n = _memory[_pc] & 0x0f; + for (int r = 0; r <= n; r++) + { + _memory[_i + r] = _registers[r]; + } + } + break; + + case 0x65: + { + uint8_t n = _memory[_pc] & 0x0f; + for (int r = 0; r <= n; r++) + { + _registers[r] = _memory[_i + r]; + } + } + break; + } + break; + } + + _pc += 2; + if (_pc >= sizeof(_memory)) + { + _pc = 0x200 + (sizeof(_memory) - _pc - 1); + } + } +} + +uint8_t Chip8Emulator::rnd() +{ + return (uint8_t)(rand() % 256); +} + +bool Chip8Emulator::isKeyPressed(uint8_t key) +{ + return getKeyPressed() == key; +} + +uint8_t Chip8Emulator::getKeyPressed() +{ + if (GameInput::isLeftPressed()) return _leftKey; + if (GameInput::isRightPressed()) return _rightKey; + if (GameInput::isUpPressed()) return _upKey; + if (GameInput::isDownPressed()) return _downKey; + if (GameInput::isCirclePressed()) return _fireKey; + if (GameInput::isSquarePressed()) return _startKey; + + return 255; +} + +void Chip8Emulator::plot(int x, int y, uint8_t pattern) +{ + if (y >= 32) return; + for (int i = 0; i < 8; ++i) + { + if (x + i >= 64) return; + + uint16_t set = ((pattern << i) & 0x80) != 0 ? 1 : 0; + uint16_t current = _bmp.getPixel(x + i, y); + + if ((set & current) != 0) _registers[0x0f] = 1; + + _bmp.setPixel(x + i, y, current ^ set); + + int nx = OFFSET_X + (x + i) * 2; + int ny = OFFSET_Y + (y * 2); + _screen.fillRect(nx, ny, nx + 1, ny + 1, current ^ set ? Color565::White : Color565::Black); + } +} \ No newline at end of file