You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CHIP8Emulator/main.cpp

358 lines
9.4 KiB

2 years ago
#include "pixelGameEngine.h"
#include "Polygon.h"
#include <ios>
#include <stack>
#include <bitset>
#include <random>
2 years ago
using namespace olc;
#define WIDTH 640
#define HEIGHT 480
class Chip8Emulator : public olc::PixelGameEngine
2 years ago
{
public:
Chip8Emulator()
2 years ago
{
sAppName = "CHIP-8!";
2 years ago
}
public:
std::array<uint8_t,4096>memory={
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
//Font starts at 0x50
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
};
std::array<bool,2048>display;
std::stack<uint8_t>stack;
float pulse=0;
uint8_t delay_timer,sound_timer;
uint16_t pc=0x200;
uint16_t index; //One 16-bit index register called “I” which is used to point at locations in memory
std::array<uint8_t,16>reg;
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen; //Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> distrib;
std::array<Key,16>keymap{X,K1,K2,K3,Q,W,E,A,S,D,Z,C,K4,R,F,V};
std::string Display8(int number){
std::bitset<8>numb(number);
return numb.to_string();
}
std::string Display16(int number){
std::bitset<16>numb(number);
return numb.to_string();
}
2 years ago
bool OnUserCreate() override
{
gen=std::mt19937(rd());
distrib=std::uniform_int_distribution<>(0,255);
for (int i=0;i<display.size();i++){
display[i]=0;
}
std::ifstream file("bc_test.ch8",std::ios_base::binary);
uint16_t counter=0x200;
while (file.good()){
int val = file.get();
if (val!=-1){
memory[counter++]=val;
}
}
/*memory[0x200]=0x00;
memory[0x201]=0xE0;
memory[0x202]=0xA0;
memory[0x203]=0x50;
memory[0x204]=0x60;
memory[0x205]=0x04;
memory[0x206]=0xD0;
memory[0x207]=0x05;
memory[0x208]=0xD0;
memory[0x209]=0x05;
memory[0x210]=0x12;
memory[0x211]=0x10;*/
//0x200 program start
2 years ago
return true;
}
bool OnUserUpdate(float fElapsedTime) override
{
pulse+=fElapsedTime;
if (pulse>=1/60.f){
if (delay_timer>0){
delay_timer--;
}
if (sound_timer>0){
sound_timer--;
}
pulse-=1/60.f;
for (int i=0;i<10;i++){
//FETCH
uint16_t opcode = memory[pc]<<8|memory[pc+1];
pc+=2;
//std::cout<<"Next instruction is 0x"<<std::hex<<pc<<std::endl;
//DECODE
uint8_t nibble1 = opcode>>12;
uint8_t X = opcode>>8&0xF;
uint8_t Y = opcode>>4&0xF;
uint8_t N = opcode&0xF;
uint8_t NN = opcode&0x00FF;
uint16_t NNN = opcode&0x0FFF;
//std::cout<<"Opcode 0x"<<std::hex<<opcode<<std::dec<<": "<<Display8(nibble1)<<", "<<Display8(X)<<"/"<<Display8(Y)<<"/"<<Display8(N)<<"/"<<Display8(NN)<<"/"<<Display16(NNN)<<std::endl;
switch(nibble1){
case 0x0:{
switch (NNN){
case 0x0E0:{ //Clear screen.
for (int i=0;i<display.size();i++){
display[i]=0;
}
}break;
case 0x0EE:{ //Return from subroutine.
uint8_t byte1,byte2;
byte1=stack.top();
stack.pop();
byte2=stack.top();
stack.pop();
pc=byte2<<8|byte1;
//std::cout<<"Exiting subroutine to 0x"<<std::hex<<pc<<std::endl;
}break;
}
}break;
case 0x1:{ //Jump.
pc=NNN;
}break;
case 0x2:{ //calls the subroutine at memory location NNN
stack.push(pc>>8);
stack.push(pc&0xFF);
//std::cout<<"Pushed 0x"<<std::hex<<pc<<" onto stack"<<std::endl;
pc=NNN;
//std::cout<<"Entering subroutine @ 0x"<<std::hex<<NNN<<std::endl;
}break;
case 0x3:{//If register X is equal to NN, skip instruction.
if(reg[X]==NN){
pc+=2;
}
}break;
case 0x4:{//If register X is not equal to NN, skip instruction.
if(reg[X]!=NN){
pc+=2;
}
}break;
case 0x5:{//If register X is equal to register Y, skip instruction.
if(reg[X]==reg[Y]){
pc+=2;
}
}break;
case 0x6:{ //Set the register X to NN.
reg[X]=NN;
//std::cout<<(int)reg[X]<<std::endl;
}break;
case 0x7:{ //Add w/no carry
reg[X]+=NN;
//std::cout<<(int)reg[X]<<std::endl;
}break;
case 0x8:{
switch(N){
case 0x0:{//VX is set to the value of VY.
reg[X]=reg[Y];
}break;
case 0x1:{//Binary OR VX w/VY.
reg[X]|=reg[Y];
}break;
case 0x2:{//Binary AND VX w/VY.
reg[X]&=reg[Y];
}break;
case 0x3:{//Logical XOR VX w/VY.
reg[X]^=reg[Y];
}break;
case 0x4:{//Add VY to VX, set carry bit if overflow occurs.
reg[0xF]=0;
if ((int)reg[X]+(int)reg[Y]>255){
reg[0xF]=1;
}
reg[X]+=reg[Y];
}break;
case 0x5:{// sets VX to the result of VX - VY.
reg[0xF]=0;
if (reg[X]>reg[Y]){
reg[0xF]=1;
}
reg[X]-=reg[Y];
}break;
case 0x6:{//Shift Right
//reg[X]=reg[Y];
reg[0xF]=reg[X]&0x1;
reg[X]>>=1;
}break;
case 0x7:{//sets VX to the result of VY - VX. It's a reverse subtraction.
reg[0xF]=0;
if (reg[Y]>reg[X]){
reg[0xF]=1;
}
reg[X]=reg[Y]-reg[X];
}break;
case 0xE:{//Shift Left
//std::cout<<"Y is: "<<(int)Y<<std::endl;
//reg[X]=reg[Y];
std::cout<<"Before left shift: "<<(int)reg[X]<<std::endl;
reg[0xF]=(reg[X]&0x80)>>7;
std::cout<<"Register F: "<<(int)reg[0xF]<<std::endl;
reg[X]<<=1;
std::cout<<"After Left shift: "<<(int)reg[X]<<std::endl;
}break;
}
}break;
case 0x9:{//If register X is not equal to register Y, skip instruction.
if(reg[X]!=reg[Y]){
pc+=2;
}
}break;
case 0xA:{ //This sets the index register I to the value NNN.
index=NNN;
}break;
case 0xB:{//Jump w/Offset
pc=NNN+reg[0x0];
}break;
case 0xC:{//Random number from 0 to NN.
reg[X]=distrib(gen)&NN;
}break;
case 0xD:{ //Display
uint8_t start_X=reg[X]%ScreenWidth();
uint8_t start_Y=reg[Y]%ScreenHeight();
reg[0xF]=0;
for (uint8_t y=0;y<N;y++){
uint16_t spriteRow=memory[index+y];
//std::cout<<spriteRow<<std::endl;
for (uint8_t x=0;x<8;x++){
if (start_X+x<ScreenWidth()&&start_Y+y<ScreenHeight()){
//Memory loc: 0x50
//0xF0,0x90,0x90,0x90,0xF0
bool pixel = spriteRow>>(8-x-1)&0x1;
if (display[(start_Y+y)*ScreenWidth()+(start_X+x)]&&pixel){
//std::cout<<"Double on."<<std::endl;
reg[0xF]=0;
}
display[(start_Y+y)*ScreenWidth()+(start_X+x)]=display[(start_Y+y)*ScreenWidth()+(start_X+x)]^pixel;
}
}
}
}break;
case 0xE:{
switch (NN){
case 0x9E:{//EX9E: Skip an instruction if key in VX is pressed.
if (GetKey(keymap[reg[X]]).bHeld){
pc+=2;
}
}break;
case 0xA1:{//EXA1: Skip an instruction if key in VX is not pressed.
if (!GetKey(keymap[reg[X]]).bHeld){
pc+=2;
}
}break;
}
}break;
case 0xF:{
switch (NN){
case 0x07:{//sets VX to the current value of the delay timer
reg[X]=delay_timer;
}break;
case 0x15:{//sets the delay timer to the value in VX
delay_timer=reg[X];
}break;
case 0x18:{//sets the sound timer to the value in VX
sound_timer=reg[X];
}break;
case 0x1E:{//The index register I will get the value in VX added to it.
reg[0xF]=0;
if (index+reg[X]>=0x1000){
reg[0xF]=1;
}
index+=reg[X];
}break;
case 0x0A:{//This instruction “blocks”; it stops executing instructions and waits for key input
for (int i=0;i<keymap.size();i++){
if (GetKey(keymap[i]).bHeld){
reg[X]=i;
goto pass;
}
}
pc-=2;
pass:;
}break;
case 0x29:{//The index register I is set to the address of the hexadecimal character in VX.
index=0x50+(reg[X]&0xF)*5;
}break;
case 0x33:{//Binary-coded decimal conversion
uint8_t numb=reg[X];//156
memory[index+2]=numb%10;
numb/=10;
memory[index+1]=numb%10;
numb/=10;
memory[index]=numb%10;
}break;
case 0x55:{//Stores registers from V0 to VX in memory pointed by index.
for (int i=0;i<=X;i++){
memory[index+i]=reg[i];
}
}break;
case 0x65:{//Retrieves registers from V0 to VX in memory pointed by index.
for (int i=0;i<=X;i++){
reg[i]=memory[index+i];
}
}break;
}
}break;
}
}
Clear(VERY_DARK_BLUE);
for (int x=0;x<ScreenWidth();x++){
for (int y=0;y<ScreenHeight();y++){
Draw({x,y},display[y*ScreenWidth()+x]?WHITE:BLACK);
}
}
}
2 years ago
return true;
}
bool OnUserDestroy()override{
return true;
}
};
int main()
{
Chip8Emulator demo;
if (demo.Construct(64, 32, 10, 10))
2 years ago
demo.Start();
return 0;
}