chip-8

feat: input handling

fix: 0xc0 parsing

Arjun Choudhary contact@arjunchoudhary.com

commit: 0810b8b parent: 914dc35
2 files changed, 192 insertions(+), 108 deletions(-)
AREADME.md+0-0
Mmain.c+192-108
A · README.md +0, -0
M · main.c +192, -108
  1@@ -70,8 +70,8 @@ unsigned char chip8_fontset[80] = {
  2 unsigned char keypad[16];
  3 
  4 // Screen dimension constants
  5-const int SCREEN_WIDTH = 64 * 10;
  6-const int SCREEN_HEIGHT = 32 * 10;
  7+const int SCREEN_WIDTH = 64 * 20;
  8+const int SCREEN_HEIGHT = 32 * 20;
  9 
 10 /*** initialize CPU ***/
 11 void chip8_initialize(void)
 12@@ -167,10 +167,11 @@ void chip8_draw(unsigned char* gfx)
 13         for (int x = 0; x < 64; x++) {
 14             if (gfx[x + (y * 64)]) {
 15                 SDL_Rect rect;
 16-                rect.x = x * 10;
 17-                rect.y = y * 10;
 18-                rect.w = 10;
 19-                rect.h = 10;
 20+                rect.x = x * 20;
 21+                rect.y = y * 20;
 22+                // size of each "pixel" keep it consistent with the scaling of the display in SCREEN_WIDTH/HEIGHT
 23+                rect.w = 20;
 24+                rect.h = 20;
 25 
 26                 SDL_RenderFillRect(renderer, &rect);
 27             }
 28@@ -202,24 +203,29 @@ void chip8_emulate_cycle()
 29     printf("0x%X \n", opcode);
 30     switch (opcode & 0xF000) {
 31     case 0x0000:
 32-        for (int i = 0; i < 64 * 32; i++) {
 33-            gfx[i] = 0;
 34+        switch (opcode & 0x00FF) {
 35+        case 0x00E0:
 36+            for (int i = 0; i < 64 * 32; i++) {
 37+                gfx[i] = 0;
 38+            }
 39+            printf("00E0: Display Cleared\n");
 40+            pc += 2;
 41+            break;
 42+        case 0x00EE:
 43+            pc = stack[stack_pointer];
 44+            stack_pointer--;
 45+            printf("00EE: Retrurn from a subroutine\n");
 46+            pc += 2;
 47+            break;
 48+        default:
 49+            break;
 50         }
 51-        printf("00E0: Display Cleared\n");
 52-        pc += 2;
 53-        break;
 54-
 55-    case 0xC000:
 56-        V[x] = (rand() % 256) & (opcode & 0x00FF);
 57-        pc += 2;
 58-        printf("Cxkk: The value at V[x] is %d \n", V[x]);
 59         break;
 60 
 61-    case 0xA000:
 62-        I = opcode & 0x0FFF;
 63-        pc += 2;
 64-        printf("Annn: The value at register I is set to 0x%X \n",
 65-            opcode & 0x0FFF);
 66+    // 1NNN: Jumps to address NNN
 67+    case 0x1000:
 68+        printf("1nnn: Jump to location nnn \n");
 69+        pc = opcode & 0x0FFF;
 70         break;
 71 
 72     // 2nnn CALL subroutine at nnn
 73@@ -248,6 +254,15 @@ void chip8_emulate_cycle()
 74         printf("4xkk: Skip next instruction if V[x] != kk \n");
 75         break;
 76 
 77+    // 5xy0 - SE Vx, Vy Skip next instruction if Vx = Vy.
 78+    case 0x5000:
 79+        if (V[x] == V[y]) {
 80+            pc += 2;
 81+        }
 82+        pc += 2;
 83+        printf("5xy0: Skip next instruction if V[x] != kk \n");
 84+        break;
 85+
 86     // 6XNN: Sets Vx to NN
 87     case 0x6000:
 88         V[x] = (opcode & 0x00FF);
 89@@ -263,10 +278,92 @@ void chip8_emulate_cycle()
 90                "V[x] \n");
 91         break;
 92 
 93-    // 1NNN: Jumps to address NNN
 94-    case 0x1000:
 95-        printf("1nnn: Jump to location nnn \n");
 96-        pc = opcode & 0x0FFF;
 97+    case 0x9000:
 98+        if (V[x] != V[y]) {
 99+            pc += 2;
100+        }
101+        pc += 2;
102+        break;
103+
104+    // Instructions starting with 8
105+    case 0x8000:
106+        switch (opcode & 0x000F) {
107+        // 8xy0: Sets Vx to Vy
108+        case 0x0000:
109+            V[x] = V[y];
110+            pc += 2;
111+            break;
112+
113+        // 8xy1: V[x] = V[x] | V[y]
114+        case 0x0001:
115+            V[x] = (V[x] | V[y]);
116+            pc += 2;
117+            break;
118+
119+        // 8xy2: V[x] = V[x] | V[y]
120+        case 0x0002:
121+            V[x] = (V[x] & V[y]);
122+            pc += 2;
123+            break;
124+
125+        // 8xy3: V[x] = [Vx] ^ V[y]
126+        case 0x0003:
127+            V[x] = (V[x] ^ V[y]);
128+            pc += 2;
129+            break;
130+
131+        // 8xy4: V[x] = V[x] + V[y], set VF = carry.
132+        case 0x0004:
133+            if (V[x] + V[y] > 255) {
134+                printf("is this valid?");
135+                V[0xF] = 1;
136+            } else {
137+                V[0xF] = 0;
138+            }
139+            V[x] += V[y];
140+            pc += 2;
141+            break;
142+
143+        // 8xy5: V[x] = V[x] - V[y], set VF = NOT BORROW
144+        case 0x0005:
145+            if (V[x] > V[y]) {
146+                V[0xF] = 1;
147+            } else {
148+                V[0xF] = 0;
149+            }
150+            V[x] -= V[y];
151+            pc += 2;
152+            break;
153+
154+        // 8xy6: Sets Vx to Vy minus Vx. Vf is set to 0 when there's a borrow
155+        case 0x0006:
156+            V[0xF] = V[x] & 0x1;
157+            V[x] = (V[x] >> 1);
158+            pc += 2;
159+            break;
160+
161+        // Set Vx = Vy - Vx, set VF = NOT borrow
162+        case 0x0007:
163+            if (V[y] > V[x]) {
164+                V[0xF] = 1;
165+            } else {
166+                V[0xF] = 0;
167+            }
168+            V[x] = V[y] - V[x];
169+            pc += 2;
170+            break;
171+
172+        // 8xyE: Stores the most significant bit of V[x] and shifts V[x]
173+        case 0x000E:
174+            V[0xF] = (V[x] >> 7) & 0x1;
175+            V[x] = (V[x] << 1);
176+            pc += 2;
177+            break;
178+
179+        default:
180+            printf("Unknown opcode 0x%X.\n", opcode);
181+            break;
182+        }
183         break;
184 
185     // Display n-byte sprite starting at memory location I at (Vx, Vy)
186@@ -282,7 +379,7 @@ void chip8_emulate_cycle()
187             // fetch the pixel value from the memory starting at location I
188             pixel = memory[I + row];
189             // loop over 8 bits of one row
190-            for (int col = 0; col < 10; col++) {
191+            for (int col = 0; col < 8; col++) {
192                 // check if current evaluated pixel is set to 1 (0x80 >>
193                 // col scnas throught the byte, one bit at the time)
194                 if ((pixel & (0x80 >> col)) != 0) {
195@@ -302,6 +399,43 @@ void chip8_emulate_cycle()
196         pc += 2;
197         break;
198 
199+    case 0xC000:
200+        V[x] = (rand() % 256) & (opcode & 0x00FF);
201+        pc += 2;
202+        printf("Cxkk: The value at V[x] is %d \n", V[x]);
203+        break;
204+
205+    case 0xA000:
206+        I = opcode & 0x0FFF;
207+        pc += 2;
208+        printf("Annn: The value at register I is set to 0x%X \n",
209+            opcode & 0x0FFF);
210+        break;
211+
212+    case 0xE000:
213+        switch (opcode & 0x00FF) {
214+
215+        // Ex9e: Skips next instructuion if key stored in V[x]is pressed
216+        case 0x009E:
217+            if (keypad[V[x]]) {
218+                pc += 2;
219+            }
220+            pc += 2;
221+            break;
222+
223+        // ExA1: Skips next instruction if key stroed in V[x] is not pressed
224+        case 0x00A1:
225+            if (!keypad[V[x]]) {
226+                pc += 2;
227+            }
228+            pc += 2;
229+            break;
230+
231+        default:
232+            printf("Uknown opcode: 0x%X\n", opcode);
233+        }
234+        break;
235+
236     // Instructions starting with FX
237     case 0xF000:
238         switch (opcode & 0x00FF) {
239@@ -399,88 +533,14 @@ void chip8_emulate_cycle()
240             pc += 2;
241             break;
242 
243-        default:
244-            printf("Unknown opcode 0x%x.\n", opcode);
245-            break;
246-        }
247-        break;
248-
249-    // Instructions starting with 8
250-    case (0x8000):
251-        switch (opcode & 0x0FFF) {
252-        // 8xy0: Sets Vx to Vy
253-        case (0x0000):
254-            V[x] = V[y];
255-            pc += 2;
256-            break;
257-
258-        // 8xy1: V[x] = V[x] | V[y]
259-        case (0x0001):
260-            V[x] = (V[x] | V[y]);
261-            pc += 2;
262-            break;
263-
264-        // 8xy2: V[x] = V[x] | V[y]
265-        case (0x0002):
266-            V[x] = (V[x] & V[y]);
267-            pc += 2;
268-            break;
269-
270-        // 8xy3: V[x] = [Vx] ^ V[y]
271-        case (0x0003):
272-            V[x] = (V[x] ^ V[y]);
273-            pc += 2;
274-            break;
275-
276-        // 8xy4: V[x] = V[x] + V[y], set VF = carry.
277-        case (0x0004):
278-            if (V[x] + V[y] > 255) {
279-                printf("is this valid?");
280-                V[0xF] = 1;
281-            } else {
282-                V[0xF] = 0;
283-            }
284-            V[x] += V[y];
285-            pc += 2;
286-            break;
287-
288-        // 8xy5: V[x] = V[x] - V[y], set VF = NOT BORROW
289-        case (0x0005):
290-            if (V[x] > V[y]) {
291-                V[0xF] = 1;
292-            } else {
293-                V[0xF] = 0;
294-            }
295-            V[x] -= V[y];
296-            pc += 2;
297-            break;
298-
299-        // 8xy6: Sets Vx to Vy minus Vx. Vf is set to 0 when there's a borrow
300-        case (0x0006):
301-            V[0xF] = V[x] & 0x1;
302-            V[x] = (V[x] >> 1);
303-            pc += 2;
304-            break;
305-
306-        // Set Vx = Vy - Vx, set VF = NOT borrow
307-        case (0x0007):
308-            if (V[y] > V[x]) {
309-                V[0xF] = 1;
310-            } else {
311-                V[0xF] = 0;
312-            }
313-            V[x] = V[y] - V[x];
314-            pc += 2;
315-            break;
316-
317         default:
318             printf("Unknown opcode 0x%X.\n", opcode);
319             break;
320         }
321         break;
322-
323     default:
324         printf("Unknown opcode 0x%X.\n", opcode);
325+        break;
326     }
327 
328     // Update timers
329@@ -493,21 +553,45 @@ void chip8_emulate_cycle()
330     }
331 }
332 
333+SDL_Event event;
334+SDL_Scancode keymappings[16] = {
335+    SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4,
336+    SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R,
337+    SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D, SDL_SCANCODE_F,
338+    SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V
339+};
340+
341 int main(int argc, char* argv[])
342 {
343+    const Uint8* state = SDL_GetKeyboardState(NULL);
344     chip8_initialize();
345     chip8_load_program(argv[1]);
346     chip8_init_display();
347-    /*   while (1) {} */
348-
349-    for (int i = 0; i < 500; i++) {
350+    while (1) {
351         chip8_emulate_cycle();
352+
353         if (draw_flag == 1) {
354             chip8_draw(gfx);
355+            // Delay to slow down the clock
356+            /*             usleep(45000); */
357+            sleep(1);
358+        }
359+        while (SDL_PollEvent(&event) != 0) {
360+            switch (event.type) {
361+            case SDL_QUIT:
362+                SDL_DestroyWindow(window);
363+                SDL_Quit();
364+                return 0;
365+                break;
366+            default:
367+                if (state[SDL_SCANCODE_ESCAPE]) {
368+                    return 0;
369+                }
370+                // update keyboard state
371+                for (int keycode = 0; keycode < 16; keycode++) {
372+                    keypad[keycode] = state[keymappings[keycode]];
373+                }
374+            }
375         }
376-        usleep(10000);
377     }
378-    SDL_DestroyWindow(window);
379-    SDL_Quit();
380-    return 0;
381 }