diff --git a/roms/games/pong_1player.ch8 b/roms/games/pong_1player.ch8 new file mode 100644 index 0000000..65d6310 Binary files /dev/null and b/roms/games/pong_1player.ch8 differ diff --git a/src/keypad.c b/src/keypad.c index d106642..f26c5e2 100644 --- a/src/keypad.c +++ b/src/keypad.c @@ -19,21 +19,19 @@ int map[]={ KEY_V // F }; -int KeypadIsPressed(unsigned char c){ - if(c<=0xF){ - if(IsKeyPressed(map[c])) - return 1; +int KeypadKeycodeValid(int keycode){ + for(int i=0;i<16;i++){ + if(map[i]==keycode) + return i; } - return 0; + return -1; } + int KeypadGetPressed(){ int keycode=GetKeyPressed(); if(keycode){ - for(int i=0;i<16;i++){ - if(map[i]==keycode) - return 1; - } + return KeypadKeycodeValid(keycode); } return -1; } diff --git a/src/keypad.h b/src/keypad.h index 9339280..922d2a8 100644 --- a/src/keypad.h +++ b/src/keypad.h @@ -2,6 +2,5 @@ #include "raylib.h" -int KeypadIsPressed(unsigned char c); int KeypadGetPressed(); diff --git a/src/main.c b/src/main.c index 7591627..ea8d606 100644 --- a/src/main.c +++ b/src/main.c @@ -1,34 +1,33 @@ #include "screen.h" #include "mem.h" #include "vcpu.h" - #include + +#define ROM "../roms/chip8-test-suite/6-keypad.ch8" +//#define ROM "../roms/games/pong_1player.ch8" + int main(int argc, char *argv[]) { - /* unsigned char byte=137; */ - /* unsigned char u,t,h; */ - /* VCPUDoubleDabble(byte,&u,&t,&h); */ - /* printf("%d: %01d%01d%01d\n",byte,h,t,u); */ - /* return 0; */ // Initialize MemInit(); - MemLoadROM("../roms/games/paddles.ch8"); - + MemLoadROM(ROM); ScreenInit(800,400); VCPUInit(); - // MemDump(); - int i=0; + // Set game to run at very high FPS (prevent raylib to interfer with emulator FPS) + SetTargetFPS(VCPU_FREQ*100); + + // Emulator main loop + int i=0; while (!WindowShouldClose()){ - for(int i=0;i<30;i++){ - VCPUFetch(); - VCPUDecode(); - VCPUExecute(); - } - ScreenUpdate(); + VCPUTick(); + if(i%600 == 0) + printf("tick\n"); + i++; } - + + // Close screen ScreenClose(); return 0; diff --git a/src/screen.c b/src/screen.c index 2e77ba9..a445bd8 100644 --- a/src/screen.c +++ b/src/screen.c @@ -15,7 +15,6 @@ void ScreenInit(int width, int height){ SetTraceLogLevel(LOG_ERROR); // Disable anoying raylib logs InitWindow(width, height, "Chip-8 Emulator"); - SetTargetFPS(60); // Set game to run at 60 frames-per-second } void ScreenClear() { diff --git a/src/vcpu.c b/src/vcpu.c index 67dea13..1f36b68 100644 --- a/src/vcpu.c +++ b/src/vcpu.c @@ -5,6 +5,7 @@ #include #include +#include #include // Current VCPU state @@ -13,6 +14,9 @@ VCPU_State State; void VCPUInit(){ State.PC=ADDR_ROM; State.S=0; + State.dtst_ticks=0; + State.screen_ticks=0; + State.keypress=-1; srand(time(NULL)); } @@ -204,11 +208,20 @@ void VCPUExecute(){ case 0xE: if(State.NN==0x9E){ // Skip if keypress in VX - if(KeypadIsPressed(State.V[State.X]&0x0F)){ - State.PC+=2; + if(State.keypress >= 0){ + if(State.V[State.X]&0x0F == State.keypress&0xF){ + State.PC+=2; + } + State.keypress=-1; } }else if(State.NN==0xA1){ // Skip if not keypress in VX - if(!KeypadIsPressed(State.V[State.X]&0x0F)) + if(State.keypress >=0){ + if(State.V[State.X]&0x0F != State.keypress&0xF){ + State.PC+=2; + } + State.keypress=-1; + } + else State.PC+=2; } break; @@ -220,13 +233,15 @@ void VCPUExecute(){ break; case 0x0A: - int key=KeypadGetPressed(); - if(key >= 0){ - State.V[State.X]=key&0x0F; + if(State.keypress >=0){ + State.V[State.X]=State.keypress&0xF; + if(State.V[State.X]&0x0F != State.keypress&0xF){ + State.PC+=2; + State.keypress=-1; + } } else - State.PC-=2; // Go back to last instruction (loop until key is pressed) - + State.PC-=2; // Go back to last instruction (loop until key is pressed) break; case 0x15: // Set timer @@ -267,6 +282,45 @@ void VCPUExecute(){ } } +void VCPUTick(){ + struct timespec start, stop; + double duration, delay; + + // Run and benchmark CPU pipeline + clock_gettime(CLOCK_REALTIME, &start); + VCPUFetch(); + VCPUDecode(); + VCPUExecute(); + State.dtst_ticks++; + State.screen_ticks++; + clock_gettime(CLOCK_REALTIME, &stop); + + // Adjust pipeline duration + duration=(stop.tv_sec - start.tv_sec) + (stop.tv_nsec - start.tv_nsec)*1e-9; + delay=1.0/VCPU_FREQ-duration; + if(delay>0){ + usleep(delay*1e6); + } + + // Update Delay Timer and Sound Timer + if(State.dtst_ticks>=(1.0*VCPU_FREQ/DTST_FREQ)){ + State.dtst_ticks=0; + if(State.DT>0) + State.DT--; + } + + // Refresh screen + if(State.screen_ticks>=(1.0*VCPU_FREQ/SCREEN_FREQ)){ + State.screen_ticks=0; + ScreenUpdate(); + } + + // Update keypressed + int keypress=KeypadGetPressed(); + if(keypress>=0) + State.keypress=keypress; +} + void VCPUDump(){ printf("opcode: 0x%04x\n",State.opcode&0xFFFF); printf("X: 0x%01x\n",State.X); diff --git a/src/vcpu.h b/src/vcpu.h index fccd397..3da6b37 100644 --- a/src/vcpu.h +++ b/src/vcpu.h @@ -1,5 +1,8 @@ #pragma once +#define VCPU_FREQ 600 +#define DTST_FREQ 60 +#define SCREEN_FREQ 60 #define REG_FLAG 0xF typedef struct VCPU_State { @@ -30,11 +33,19 @@ typedef struct VCPU_State { unsigned char N; unsigned char NN; unsigned short NNN; + + // Keypressed + int keypress; + + // Count VCPU ticks + int dtst_ticks; + int screen_ticks; } VCPU_State; void VCPUInit(); void VCPUFetch(); void VCPUDecode(); void VCPUExecute(); +void VCPUTick(); void VCPUDoubleDabble(unsigned char x, unsigned char *u, unsigned char *t, unsigned char *h); void VCPUDump();