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

Dependencies:   mbed

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);
    }  
}