Emulation of the 1970's Chip-8 machine. The emulator has 7 games that are unmodified from the original Chip-8 format.

Dependencies:   mbed

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