chip-8
chip-8 emulatorfeat: input handling
fix: 0xc0 parsingADD · README.md +0 -0
--- a/README.md
+++ b/README.mdMOD · main.c +192 -108--- a/main.c
+++ b/main.c
@@ -70,8 +70,8 @@ unsigned char chip8_fontset[80] = {
unsigned char keypad[16];
// Screen dimension constants
-const int SCREEN_WIDTH = 64 * 10;
-const int SCREEN_HEIGHT = 32 * 10;
+const int SCREEN_WIDTH = 64 * 20;
+const int SCREEN_HEIGHT = 32 * 20;
/*** initialize CPU ***/
void chip8_initialize(void)
@@ -167,10 +167,11 @@ void chip8_draw(unsigned char* gfx)
for (int x = 0; x < 64; x++) {
if (gfx[x + (y * 64)]) {
SDL_Rect rect;
- rect.x = x * 10;
- rect.y = y * 10;
- rect.w = 10;
- rect.h = 10;
+ rect.x = x * 20;
+ rect.y = y * 20;
+ // size of each "pixel" keep it consistent with the scaling of the display in SCREEN_WIDTH/HEIGHT
+ rect.w = 20;
+ rect.h = 20;
SDL_RenderFillRect(renderer, &rect);
}
@@ -202,24 +203,29 @@ void chip8_emulate_cycle()
printf("0x%X \n", opcode);
switch (opcode & 0xF000) {
case 0x0000:
- for (int i = 0; i < 64 * 32; i++) {
- gfx[i] = 0;
+ switch (opcode & 0x00FF) {
+ case 0x00E0:
+ for (int i = 0; i < 64 * 32; i++) {
+ gfx[i] = 0;
+ }
+ printf("00E0: Display Cleared\n");
+ pc += 2;
+ break;
+ case 0x00EE:
+ pc = stack[stack_pointer];
+ stack_pointer--;
+ printf("00EE: Retrurn from a subroutine\n");
+ pc += 2;
+ break;
+ default:
+ break;
}
- printf("00E0: Display Cleared\n");
- pc += 2;
- break;
-
- case 0xC000:
- V[x] = (rand() % 256) & (opcode & 0x00FF);
- pc += 2;
- printf("Cxkk: The value at V[x] is %d \n", V[x]);
break;
- case 0xA000:
- I = opcode & 0x0FFF;
- pc += 2;
- printf("Annn: The value at register I is set to 0x%X \n",
- opcode & 0x0FFF);
+ // 1NNN: Jumps to address NNN
+ case 0x1000:
+ printf("1nnn: Jump to location nnn \n");
+ pc = opcode & 0x0FFF;
break;
// 2nnn CALL subroutine at nnn
@@ -248,6 +254,15 @@ void chip8_emulate_cycle()
printf("4xkk: Skip next instruction if V[x] != kk \n");
break;
+ // 5xy0 - SE Vx, Vy Skip next instruction if Vx = Vy.
+ case 0x5000:
+ if (V[x] == V[y]) {
+ pc += 2;
+ }
+ pc += 2;
+ printf("5xy0: Skip next instruction if V[x] != kk \n");
+ break;
+
// 6XNN: Sets Vx to NN
case 0x6000:
V[x] = (opcode & 0x00FF);
@@ -263,10 +278,92 @@ void chip8_emulate_cycle()
"V[x] \n");
break;
- // 1NNN: Jumps to address NNN
- case 0x1000:
- printf("1nnn: Jump to location nnn \n");
- pc = opcode & 0x0FFF;
+ case 0x9000:
+ if (V[x] != V[y]) {
+ pc += 2;
+ }
+ pc += 2;
+ break;
+
+ // Instructions starting with 8
+ case 0x8000:
+ switch (opcode & 0x000F) {
+ // 8xy0: Sets Vx to Vy
+ case 0x0000:
+ V[x] = V[y];
+ pc += 2;
+ break;
+
+ // 8xy1: V[x] = V[x] | V[y]
+ case 0x0001:
+ V[x] = (V[x] | V[y]);
+ pc += 2;
+ break;
+
+ // 8xy2: V[x] = V[x] | V[y]
+ case 0x0002:
+ V[x] = (V[x] & V[y]);
+ pc += 2;
+ break;
+
+ // 8xy3: V[x] = [Vx] ^ V[y]
+ case 0x0003:
+ V[x] = (V[x] ^ V[y]);
+ pc += 2;
+ break;
+
+ // 8xy4: V[x] = V[x] + V[y], set VF = carry.
+ case 0x0004:
+ if (V[x] + V[y] > 255) {
+ printf("is this valid?");
+ V[0xF] = 1;
+ } else {
+ V[0xF] = 0;
+ }
+ V[x] += V[y];
+ pc += 2;
+ break;
+
+ // 8xy5: V[x] = V[x] - V[y], set VF = NOT BORROW
+ case 0x0005:
+ if (V[x] > V[y]) {
+ V[0xF] = 1;
+ } else {
+ V[0xF] = 0;
+ }
+ V[x] -= V[y];
+ pc += 2;
+ break;
+
+ // 8xy6: Sets Vx to Vy minus Vx. Vf is set to 0 when there's a borrow
+ case 0x0006:
+ V[0xF] = V[x] & 0x1;
+ V[x] = (V[x] >> 1);
+ pc += 2;
+ break;
+
+ // Set Vx = Vy - Vx, set VF = NOT borrow
+ case 0x0007:
+ if (V[y] > V[x]) {
+ V[0xF] = 1;
+ } else {
+ V[0xF] = 0;
+ }
+ V[x] = V[y] - V[x];
+ pc += 2;
+ break;
+
+ // 8xyE: Stores the most significant bit of V[x] and shifts V[x]
+ case 0x000E:
+ V[0xF] = (V[x] >> 7) & 0x1;
+ V[x] = (V[x] << 1);
+ pc += 2;
+ break;
+
+ default:
+ printf("Unknown opcode 0x%X.\n", opcode);
+ break;
+ }
break;
// Display n-byte sprite starting at memory location I at (Vx, Vy)
@@ -282,7 +379,7 @@ void chip8_emulate_cycle()
// fetch the pixel value from the memory starting at location I
pixel = memory[I + row];
// loop over 8 bits of one row
- for (int col = 0; col < 10; col++) {
+ for (int col = 0; col < 8; col++) {
// check if current evaluated pixel is set to 1 (0x80 >>
// col scnas throught the byte, one bit at the time)
if ((pixel & (0x80 >> col)) != 0) {
@@ -302,6 +399,43 @@ void chip8_emulate_cycle()
pc += 2;
break;
+ case 0xC000:
+ V[x] = (rand() % 256) & (opcode & 0x00FF);
+ pc += 2;
+ printf("Cxkk: The value at V[x] is %d \n", V[x]);
+ break;
+
+ case 0xA000:
+ I = opcode & 0x0FFF;
+ pc += 2;
+ printf("Annn: The value at register I is set to 0x%X \n",
+ opcode & 0x0FFF);
+ break;
+
+ case 0xE000:
+ switch (opcode & 0x00FF) {
+
+ // Ex9e: Skips next instructuion if key stored in V[x]is pressed
+ case 0x009E:
+ if (keypad[V[x]]) {
+ pc += 2;
+ }
+ pc += 2;
+ break;
+
+ // ExA1: Skips next instruction if key stroed in V[x] is not pressed
+ case 0x00A1:
+ if (!keypad[V[x]]) {
+ pc += 2;
+ }
+ pc += 2;
+ break;
+
+ default:
+ printf("Uknown opcode: 0x%X\n", opcode);
+ }
+ break;
+
// Instructions starting with FX
case 0xF000:
switch (opcode & 0x00FF) {
@@ -400,87 +534,13 @@ void chip8_emulate_cycle()
break;
default:
- printf("Unknown opcode 0x%x.\n", opcode);
- break;
- }
- break;
-
- // Instructions starting with 8
- case (0x8000):
- switch (opcode & 0x0FFF) {
- // 8xy0: Sets Vx to Vy
- case (0x0000):
- V[x] = V[y];
- pc += 2;
- break;
-
- // 8xy1: V[x] = V[x] | V[y]
- case (0x0001):
- V[x] = (V[x] | V[y]);
- pc += 2;
- break;
-
- // 8xy2: V[x] = V[x] | V[y]
- case (0x0002):
- V[x] = (V[x] & V[y]);
- pc += 2;
- break;
-
- // 8xy3: V[x] = [Vx] ^ V[y]
- case (0x0003):
- V[x] = (V[x] ^ V[y]);
- pc += 2;
- break;
-
- // 8xy4: V[x] = V[x] + V[y], set VF = carry.
- case (0x0004):
- if (V[x] + V[y] > 255) {
- printf("is this valid?");
- V[0xF] = 1;
- } else {
- V[0xF] = 0;
- }
- V[x] += V[y];
- pc += 2;
- break;
-
- // 8xy5: V[x] = V[x] - V[y], set VF = NOT BORROW
- case (0x0005):
- if (V[x] > V[y]) {
- V[0xF] = 1;
- } else {
- V[0xF] = 0;
- }
- V[x] -= V[y];
- pc += 2;
- break;
-
- // 8xy6: Sets Vx to Vy minus Vx. Vf is set to 0 when there's a borrow
- case (0x0006):
- V[0xF] = V[x] & 0x1;
- V[x] = (V[x] >> 1);
- pc += 2;
- break;
-
- // Set Vx = Vy - Vx, set VF = NOT borrow
- case (0x0007):
- if (V[y] > V[x]) {
- V[0xF] = 1;
- } else {
- V[0xF] = 0;
- }
- V[x] = V[y] - V[x];
- pc += 2;
- break;
-
- default:
printf("Unknown opcode 0x%X.\n", opcode);
break;
}
break;
-
default:
printf("Unknown opcode 0x%X.\n", opcode);
+ break;
}
// Update timers
@@ -493,21 +553,45 @@ void chip8_emulate_cycle()
}
}
+SDL_Event event;
+SDL_Scancode keymappings[16] = {
+ SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4,
+ SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R,
+ SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D, SDL_SCANCODE_F,
+ SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V
+};
+
int main(int argc, char* argv[])
{
+ const Uint8* state = SDL_GetKeyboardState(NULL);
chip8_initialize();
chip8_load_program(argv[1]);
chip8_init_display();
- /* while (1) {} */
-
- for (int i = 0; i < 500; i++) {
+ while (1) {
chip8_emulate_cycle();
+
if (draw_flag == 1) {
chip8_draw(gfx);
+ // Delay to slow down the clock
+ /* usleep(45000); */
+ sleep(1);
+ }
+ while (SDL_PollEvent(&event) != 0) {
+ switch (event.type) {
+ case SDL_QUIT:
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+ return 0;
+ break;
+ default:
+ if (state[SDL_SCANCODE_ESCAPE]) {
+ return 0;
+ }
+ // update keyboard state
+ for (int keycode = 0; keycode < 16; keycode++) {
+ keypad[keycode] = state[keymappings[keycode]];
+ }
+ }
}
- usleep(10000);
}
- SDL_DestroyWindow(window);
- SDL_Quit();
- return 0;
}=
\ No newline at end of file