chip-8

chip-8 emulator

feat: input handling

fix: 0xc0 parsing

Arjun Choudhary contact@arjunchoudhary.com

commit: 0fd4a9b parent: 1ab0c3b

2 files changed, 192 insertions(+), 108 deletions(-)
ADDREADME.md+0-0
MODmain.c+192-108
ADD · README.md +0 -0
--- a/README.md
+++ b/README.md
MOD · 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