Emulation of the 1970's Chip-8 machine. The emulator has 7 games that are unmodified from the original Chip-8 format.
Chip8Emulator.cpp
- Committer:
- taylorza
- Date:
- 2015-02-08
- Revision:
- 0:bc3f11b1b41f
File content as of revision 0:bc3f11b1b41f:
#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); } }