#include "pixelGameEngine.h" #include "Polygon.h" #define OLC_PGEX_SPLASHSCREEN #include "splash.h" #include #include #include #include using namespace olc; #define WIDTH 640 #define HEIGHT 480 class Chip8Emulator : public olc::PixelGameEngine { public: //SplashScreen s; Chip8Emulator() { sAppName = "CHIP-8!"; } public: std::arraymemory={ 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::arraydisplay; std::arrayscreen; std::stackstack; 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::arrayreg; 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::arraykeymap{X,K1,K2,K3,Q,W,E,A,S,D,Z,C,K4,R,F,V}; bool USE_ORIGINAL_CHIP8_SET=true; //True means use the original CHIP-8 spec (COSMAC VIP emulation). Set to false to use CHIP-48 spec. 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(); } bool OnUserCreate() override { gen=std::mt19937(rd()); distrib=std::uniform_int_distribution<>(0,255); for (int i=0;i=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"<>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"<>8); stack.push(pc&0xFF); //std::cout<<"Pushed 0x"<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 if (USE_ORIGINAL_CHIP8_SET){ /* In the CHIP-8 interpreter for the original COSMAC VIP, this instruction did the following: It put the value of VY into VX, and then shifted the value in VX 1 bit to the right (8XY6) or left (8XYE). VY was not affected, but the flag register VF would be set to the bit that was shifted out. However, starting with CHIP-48 and SUPER-CHIP in the early 1990s, these instructions were changed so that they shifted VX in place, and ignored the Y completely. */ 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<>7; reg[X]<<=1; }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>(8-x-1)&0x1; if (display[(start_Y+y)*ScreenWidth()+(start_X+x)]&&pixel){ //std::cout<<"Double on."<=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