Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All tests passing #3

Merged
merged 5 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
# chip8
Chip8 emulator made in C89 for fun :-)
Chip8 emulator made in C89 for fun :-)

![Opcodes Tests](./screenshots/corax.png?raw=true "Corax Opcodes Test")
![Quirks Tests](./screenshots/quirks.png?raw=true "Quirks Test")
![Flags Tests](./screenshots/flags.png?raw=true "Flags Test")
![Slippery Slope](./screenshots/slipperyslope.png?raw=true "Slippery Slope")

### TODO:
* [] Audio Support
* [] Color interpolation (crt style fadeout)
* [] XO/Super Chip Support
2 changes: 2 additions & 0 deletions include/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ static void debug_print(const char *fmt, ...) {
}
#endif

#define NUM_INSTRS_PER_FRAME 8

#endif /* COMMON_H */
4 changes: 2 additions & 2 deletions include/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ extern word SP;
extern word I;
extern byte delay_timer;
extern byte sound_timer;
extern byte just_pressed_key;
extern bool keyboard_status[CHIP8_KEY_COUNT];
extern bool can_render;

void cpu_init();
void execute();
void execute(int tick);
void update_timers();

#endif
Binary file added screenshots/corax.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/flags.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/quirks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/slipperyslope.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 90 additions & 19 deletions src/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ word SP;
word I;
byte delay_timer;
byte sound_timer;
byte just_pressed_key;
bool keyboard_status[CHIP8_KEY_COUNT];
bool legacy;
bool can_render;

void cpu_init() {
int i;
Expand All @@ -40,6 +41,8 @@ void cpu_init() {
I = 0x0000;
delay_timer = 0;
sound_timer = 0;
legacy = true;
can_render = true;

for(i = 0; i < 80; i++) {
memory[i] = chip8_font[i];
Expand All @@ -56,7 +59,7 @@ void cpu_init() {
* common functionality, but this may change if I decide to really make this
* emulator work well.
**/
void execute() {
void execute(int tick) {
word opcode = read(PC) << 8 | read(PC + 1);
word opcode_first_nibble = opcode >> 12;

Expand All @@ -66,6 +69,7 @@ void execute() {
case 0x0: {
if(opcode == 0x00E0) {
memset(pixel_buffer, 0, sizeof(pixel_buffer));
can_render = true;
PC += 2;
}else if(opcode == 0x00EE) {
if(SP == 0) {
Expand Down Expand Up @@ -117,15 +121,49 @@ void execute() {
}
case 0x8: {
switch(N) {
case 0: V[X] = V[Y]; break;
case 1: V[X] |= V[Y]; break;
case 2: V[X] &= V[Y]; break;
case 0: {
V[X] = V[Y];
break;
}
case 1: {
if(legacy) {
V[X] = V[X] | V[Y];
V[0xF] = 0x00;
}

if(!legacy) {
word result = V[X] | V[Y];
V[0xF] = (result >> 8) & 0x01;
V[X] |= V[Y];
}
break;
}
case 2: {
if(legacy) {
V[X] = V[X] & V[Y];
V[0xF] = 0x00;
}

if(!legacy) {
word result = V[X] & V[Y];
V[0xF] = (result >> 8) & 0x01;
V[X] &= V[Y];
}
break;
}
case 3: {
word result = V[X] ^ V[Y];
V[0xF] = (result >> 8) & 0x01;
V[X] = result;
if(legacy) {
V[X] = V[X] ^ V[Y];
V[0xF] = 0x00;
}

if(!legacy) {
word result = V[X] ^ V[Y];
V[0xF] = (result >> 8) & 0x01;
V[X] = result;
}
break;
}
break;
case 4: {
word sum = V[X] + V[Y];
V[X] = (byte)sum;
Expand All @@ -141,6 +179,7 @@ void execute() {
case 6: {
/* Some ambiguities to this instr */
word flag = V[X] & 0x0001;
if(legacy) { V[X] = V[Y]; }
V[X] >>= 1;
V[0xF] = flag;
} break;
Expand All @@ -151,6 +190,7 @@ void execute() {
case 0xE: {
/* Some ambiguities to this instr */
bool flag = V[X] & 0x80;
if(legacy) { V[X] = V[Y]; }
V[X] = V[X] << 1;
V[0xF] = flag ? true : false;
}
Expand Down Expand Up @@ -188,22 +228,29 @@ void execute() {
break;
}
case 0xD: {
int x_pos = V[X] & (CHIP8_WIDTH - 1);
int y_pos = V[Y] & (CHIP8_HEIGHT - 1);
int idx, pixel;

V[0xF] = 0;
for (idx = 0; idx < N; idx++) {
byte data = read(I + idx);
if(y_pos + idx >= CHIP8_HEIGHT) break;

for (pixel = 0; pixel < 8; pixel++) {
byte sprite_pixel = GET_BIT(data, pixel);
bool* display_pixel = &pixel_buffer[(V[Y] + idx) % CHIP8_HEIGHT]
[(V[X] + (7 - pixel)) % CHIP8_WIDTH];
int x_coord = x_pos + (7 - pixel);
bool* display_pixel = &pixel_buffer[(y_pos + idx)][x_coord];
if(x_coord >= CHIP8_WIDTH) continue;


if(sprite_pixel != 0 && *display_pixel != 0){
V[0xF] = 1;
}
*display_pixel ^= sprite_pixel; /* XOR draw */
}
}
can_render = true;
PC += 2;
break;
}
Expand Down Expand Up @@ -245,14 +292,32 @@ void execute() {
break;

case 0x0A: {
int i;
static bool any_key_pressed = false;
static int key = -1;

debug_print("[DEBUG] Wait for key instruction\n");
if(just_pressed_key) {
PC += 2;
just_pressed_key = false;
break;
for(i = 0; i < CHIP8_KEY_COUNT; i++) {
if(keyboard_status[i]) {
PC += 2;
any_key_pressed = true;
key = i;
}
}

if(!any_key_pressed) PC -= 2;
else {
if(keyboard_status[key] == 0) {
V[X] = key;
key = -1;
any_key_pressed = false;
}else {
PC -= 2;
}
}

break;
}
break;

case 0x29: {
debug_print("I = location of font for character V[0x%x] = 0x%x\n", X, V[X]);
Expand All @@ -274,8 +339,6 @@ void execute() {
write(I, digits[0]);
write(I + 1, digits[1]);
write(I + 2, digits[2]);

PC += 2;
}
break;

Expand All @@ -285,6 +348,10 @@ void execute() {
for(idx = 0; idx <= (X); idx++) {
write(I + idx, V[idx]);
}

if (legacy) {
I += ((X) + 1);
}
}
break;

Expand All @@ -298,6 +365,10 @@ void execute() {
for(idx = 0; idx <= (X); idx++) {
V[idx] = read(I + idx);
}

if (legacy) {
I += ((X) + 1);
}
}
break;

Expand Down Expand Up @@ -327,4 +398,4 @@ void update_timers() {
sound_timer--;
printf("BEEP!!!!\n");
}
}
}
21 changes: 10 additions & 11 deletions src/display.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "../include/display.h"
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_stdinc.h>
#include <SDL2/SDL_video.h>

SDL_Window* win = NULL;
Expand Down Expand Up @@ -41,6 +42,12 @@ void init_display() {
memset(pixel_buffer, 0, sizeof(pixel_buffer));
}

/**
* To fix flickering we can interpolate when pixels are turned off.
* Just store a temporary copy of the current buffer, look for those
* pixels that have changed between the prev and new frames, make those
* a greyish color for one frame then make zero.
**/
static void render_pixel_buffer() {
SDL_Rect pixel_rect; /* Declare variables at the beginning */
int x, y;
Expand Down Expand Up @@ -91,10 +98,8 @@ int keymap(unsigned char k) {
}
}

void update_display(bool* quit) {
static Uint32 last_frame_time = 0;
Uint32 current_time = SDL_GetTicks();

/* Note: our display should be 60 fps and run ~700 cycles per second */
void update_display(bool* quit) {
while( SDL_PollEvent( &e ) ) {
switch(e.type) {
case SDL_QUIT: {
Expand All @@ -105,7 +110,6 @@ void update_display(bool* quit) {
int chip8_key = keymap(e.key.keysym.sym);
if (chip8_key != -1) {
keyboard_status[chip8_key] = true;
just_pressed_key = true;
}
}
break;
Expand All @@ -114,7 +118,6 @@ void update_display(bool* quit) {
int chip8_key = keymap(e.key.keysym.sym);
if (chip8_key != -1) {
keyboard_status[chip8_key] = false;
just_pressed_key = true;
}
}
break;
Expand All @@ -123,12 +126,8 @@ void update_display(bool* quit) {
}
}

if(current_time - last_frame_time >= FRAME_DELAY) {
if(can_render) {
SDL_ShowWindow(win);
render_pixel_buffer();

last_frame_time = current_time;
} else {
SDL_Delay(FRAME_DELAY - (current_time - last_frame_time));
}
}
25 changes: 21 additions & 4 deletions src/emulate.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
#include "../include/emulate.h"
#include "../include/cpu.h"

#include <SDL2/SDL.h>

#define INSTRUCTION_DELAY_MS 16

void emulate() {
bool quit = false;
Uint32 last_time = SDL_GetTicks();

init_display();
cpu_init();

/* Every 16ms we execute NUM_INSTRS_PER_FRAME instrs */
while(quit == false) {
execute();
update_timers();
update_display(&quit);
Uint32 current_time = SDL_GetTicks();
Uint32 time_diff = current_time - last_time;

if(time_diff >= INSTRUCTION_DELAY_MS) {
int it;
for(it = 0; it < NUM_INSTRS_PER_FRAME; it++) {
execute(it);
update_display(&quit);
}
last_time += INSTRUCTION_DELAY_MS;

/* Timers update every NUM_INSTRS_PER_FRAME instructions, not every instruction */
update_timers();
}
}
printf("Hi from emulator\n");
}
Binary file added tests/15PUZZLE
Binary file not shown.
Binary file added tests/BLINKY
Binary file not shown.
Binary file added tests/BLITZ
Binary file not shown.
Binary file added tests/BRIX
Binary file not shown.
Binary file added tests/CONNECT4
Binary file not shown.
Binary file added tests/GUESS
Binary file not shown.
Binary file added tests/HIDDEN
Binary file not shown.
Binary file added tests/INVADERS
Binary file not shown.
Binary file added tests/KALEID
Binary file not shown.
Binary file added tests/MAZE
Binary file not shown.
Binary file added tests/MERLIN
Binary file not shown.
Binary file added tests/MISSILE
Binary file not shown.
Binary file added tests/PONG
Binary file not shown.
Binary file added tests/PONG2
Binary file not shown.
Binary file added tests/PUZZLE
Binary file not shown.
Binary file added tests/SYZYGY
Binary file not shown.
Binary file added tests/TANK
Binary file not shown.
Binary file added tests/TETRIS
Binary file not shown.
Binary file added tests/TICTAC
Binary file not shown.
Binary file added tests/UFO
Binary file not shown.
Binary file added tests/VBRIX
Binary file not shown.
Binary file added tests/VERS
Binary file not shown.
Binary file added tests/WIPEOFF
Binary file not shown.