chip-8

feat: rom loading and init

Arjun Choudhary contact@arjunchoudhary.com

commit: c78ea68 parent: a2e4208
1 files changed, 272 insertions(+), 53 deletions(-)
Mmain.c+272-53
M · main.c +272, -53
  1@@ -1,8 +1,12 @@
  2 /*** includes ***/
  3 #include <SDL2/SDL.h>
  4+#include <SDL2/SDL_config-x86_64.h>
  5+#include <bits/types/struct_FILE.h>
  6 #include <errno.h>
  7 #include <stddef.h>
  8+#include <stdint.h>
  9 #include <stdio.h>
 10+#include <stdlib.h>
 11 #include <sys/stat.h>
 12 #include <time.h>
 13 #include <unistd.h>
 14@@ -18,20 +22,24 @@ unsigned char memory[4096];
 15 unsigned char V[16];
 16 // Index or Address Register
 17 unsigned short I;
 18-// Program counter | A psudo register that points to the next instruction on the
 19-// stack Instruction pointer being my preffered nomenclature
 20+// Program counter | A pseudo register that points to the next instruction on
 21+// the stack Instruction pointer being my preferred nomenclature
 22 unsigned short pc;
 23 // Display containing 2048 pixels in an array
 24 unsigned char gfx[64 * 32];
 25 // Timer registers that countdown to 0 at 60Hz
 26 unsigned char delay_timer;
 27 unsigned char sound_timer;
 28-//
 29+// Sound flag
 30+unsigned char sound_flag = 0;
 31+// Draw flag
 32+unsigned char draw_flag = 0;
 33+// Stack and stack pointer
 34 unsigned short stack[16];
 35 unsigned short stack_pointer;
 36 
 37-// Fontset used by CHIP 8
 38-// Also used to draw sprits using XOR
 39+// Font set used by CHIP 8
 40+// Also used to draw sprites using XOR
 41 // DEC   HEX    BIN         RESULT    DEC   HEX    BIN         RESULT
 42 // 240   0xF0   1111 0000    ****     240   0xF0   1111 0000    ****
 43 // 144   0x90   1001 0000    *  *      16   0x10   0001 0000       *
 44@@ -62,20 +70,18 @@ unsigned char chip8_fontset[80] = {
 45 unsigned char keypad[16];
 46 
 47 // Screen dimension constants
 48-const int SCREEN_WIDTH = 640;
 49-const int SCREEN_HEIGHT = 320;
 50+const int SCREEN_WIDTH = 64 * 10;
 51+const int SCREEN_HEIGHT = 32 * 10;
 52 
 53-/*** initialize cpu ***/
 54+/*** initialize CPU ***/
 55 void chip8_initialize(void) {
 56+  // Seed for rand from time.h
 57+  srand((unsigned int)time(NULL));
 58   pc = 0x200;        // program counter starts at 0x200 or 512
 59   opcode = 0;        // reset current opcode
 60   I = 0;             // reset current index register
 61   stack_pointer = 0; // reset stack pointer
 62-  printf("Chip 8 Initialized successfully \n");
 63-  // Load fontset into memory 
 64-  //for (int i = 0; i < 80; i++) {
 65-  //      memory[i] = fontset[i];
 66-  //  }
 67+  printf("\n Chip 8 Initialized successfully \n");
 68   memcpy(memory, chip8_fontset, sizeof(chip8_fontset));
 69 }
 70 
 71@@ -95,7 +101,7 @@ void chip8_load_program(char *filename) {
 72   printf("Rom size: %zu bytes \n", buf_len);
 73   fclose(program);
 74 
 75-  // load rom into memory
 76+  // load ROM into memory
 77   for (int i = 0; i < buf_len; i++) {
 78     memory[0x200 + i] = buf[i];
 79     // Big endian so check last two values from the right
 80@@ -106,66 +112,279 @@ void chip8_load_program(char *filename) {
 81 }
 82 
 83 /*** initialize display ***/
 84-int chip8_init_display (){
 85+
 86+SDL_Window *window;
 87+SDL_Renderer *renderer;
 88+
 89+int chip8_init_display() {
 90   SDL_Init(SDL_INIT_VIDEO);
 91   // Init Window struct
 92-  SDL_Window *window;
 93+
 94   // Init window
 95-   window =
 96-      SDL_CreateWindow(
 97-        "CHIP8", 
 98-        SDL_WINDOWPOS_CENTERED, 
 99-        SDL_WINDOWPOS_CENTERED,
100-        SCREEN_WIDTH,
101-        SCREEN_HEIGHT, 
102-        0);
103+  window =
104+      SDL_CreateWindow("CHIP8", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
105+                       SCREEN_WIDTH, SCREEN_HEIGHT, 0);
106   // Init renderer struct
107-  SDL_Renderer* renderer;
108-  // Init renderer
109-    renderer =  
110-      SDL_CreateRenderer(
111-        window, 
112-        -1, 
113-        SDL_RENDERER_ACCELERATED);
114-  //SDL Render color (background)
115-  SDL_SetRenderDrawColor( renderer, 0, 0, 0, 0 );
116-  // Clear the current color and use the one specified above 
117-  SDL_RenderClear( renderer );
118-  // Shape 
119-  SDL_Rect rectangle;
120-    rectangle.x = 0;
121-    rectangle.y = 0;
122-    rectangle.w = 90;
123-    rectangle.h = 100;
124-
125-  SDL_SetRenderDrawColor( renderer, 0, 0, 255, 255 );
126-  SDL_RenderFillRect( renderer, &rectangle );
127-  SDL_RenderPresent(renderer);
128-
129+  renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
130 
131   if (SDL_Init(SDL_INIT_VIDEO) < 0) {
132     printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
133     return -1;
134   }
135-
136   // Check that the window was successfully created
137+
138   if (window == NULL) {
139     // In the case that the window could not be made...
140     printf("Could not create window: %s\n", SDL_GetError());
141     return -1;
142   }
143-  SDL_Delay(3000);
144-  // Pause execution for 3000 milliseconds, for example
145-  // Close and destroy the window
146+  return 0;
147+}
148+
149+void stop_display(void) {
150   SDL_DestroyWindow(window);
151-  // Clean up
152   SDL_Quit();
153-  return 0;
154+}
155+
156+void chip8_draw(unsigned char *gfx) {
157+  SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
158+  // Init renderer
159+  SDL_RenderClear(renderer);
160+  // SDL Render color (background)
161+
162+  // Clear the current color and use the one specified above
163+  SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
164+
165+  for (int y = 0; y < 32; y++) {
166+    for (int x = 0; x < 64; x++) {
167+      if (gfx[x + (y * 64)]) {
168+        SDL_Rect rect;
169+        rect.x = x * 10;
170+        rect.y = y * 10;
171+        rect.w = 10;
172+        rect.h = 10;
173+
174+        SDL_RenderFillRect(renderer, &rect);
175+      }
176+    }
177+  }
178+  SDL_RenderPresent(renderer);
179+}
180+
181+void chip8_emulate_cycle() {
182+  draw_flag = 0;
183+  sound_flag = 0;
184+  printf(" \n");
185+
186+  // Fetch opcode present at program counter and program counter + 1
187+  // Since each opcode is supposed to be 2 bytes we fetch both pc and pc + 1
188+  // Shift the current 8 bytes in memory[pc] left by 8 points
189+  // We merge them using binary OR to get the entire opcode
190+  opcode = memory[pc] << 8 | memory[pc + 1];
191+
192+  // Vx register, we are basically "grabbing" the x present in some
193+  // instructions like 3XNN
194+  unsigned short x = (opcode & 0x0F00) >> 8;
195+
196+  // Vy register, we are basically "grabbing" the y present in some
197+  // instructions like 5XY0
198+  unsigned short y = (opcode & 0x00F0) >> 4;
199+
200+  printf("0x%X \n", opcode);
201+  switch (opcode & 0xF000) {
202+  case 0x0000:
203+    for (int i = 0; i < 64 * 32; i++) {
204+      gfx[i] = 0;
205+    }
206+    printf("00E0: Display Cleared\n");
207+    pc += 2;
208+    break;
209+    
210+  case 0xC000:
211+    V[x] = (rand() % 256) & (opcode & 0x00FF);
212+    pc += 2;
213+    printf("Cxkk: The value at V[x] is %d \n", V[x]);
214+    break;
215+
216+  case 0xA000:
217+    I = opcode & 0x0FFF;
218+    pc += 2;
219+    printf("Annn: The value at register I is set to 0x%X \n", opcode & 0x0FFF);
220+    break;
221+
222+  // set of of 7 instructions starting with FX
223+  case 0xF000:
224+  switch (opcode & 0x00FF) {
225+    case 0x0007:
226+    printf("Fx07: Place value of delay_timer into V[x]\n");
227+    V[x] = delay_timer;
228+    pc += 2;
229+    break;
230+    
231+    case 0x000A:
232+    printf("Fx0A: Stop all execution till key press, keypress is stored in V[x]\n");
233+    for (int i = 0; i < 16; i++) {
234+      if (keypad[i]) {
235+        V[x] = i;
236+        pc += 2;
237+        break;
238+          }
239+        }
240+      break;
241+    
242+    case 0x0015:
243+    printf("Fx15: Sets the delay_timer value to V[x]\n");
244+    delay_timer = V[x];
245+    pc += 2;
246+    break;
247+    
248+    // FX18: Sets the sound timer to Vx
249+    case 0x0018:
250+    printf("Fx18: Sets the sound_timer value to V[x]\n");
251+    sound_timer = V[x];
252+    pc += 2;
253+    break;
254+
255+    // FX1E: Adds Vx to I
256+    case 0x001E:
257+    printf("Fx1E:The values of I and Vx are added, and the results are stored in I\n");
258+    I += V[x];
259+    pc += 2;
260+    break;
261+
262+    // FX29: Sets I to the location of the sprite for the character
263+    // in Vx
264+    case 0x0029:
265+    printf("Fx29: Set location of I to mathching 0x sprite in V[x] \n");
266+    // Each digit contains 5 bytes 
267+    I = V[x] * 5;
268+    pc += 2;
269+    break;
270+    
271+    /*
272+    * FX33:
273+    *
274+    * Stores the binary-coded decimal representation
275+    * of VX, with the most significant of three digits
276+    * at the address in I, the middle digit at I plus
277+    * 1, and the least significant digit at I plus 2.
278+    * (In other words, take the decimal representation
279+    * of VX, place the hundreds digit in memory
280+    * at location in I, the tens digit at
281+    * location I+1, and the ones digit at
282+    * location I+2.)
283+    * */
284+
285+    case 0x0033:
286+      printf("Fx33: Stores the binary-coded decimal representation of VX \n");
287+      memory[I] = (V[x] % 1000) / 100;
288+      memory[I + 1] = (V[x] % 100) / 10;
289+      memory[I + 2] = (V[x] % 10);
290+
291+      pc += 2;
292+      break;
293+
294+    case 0x0055:
295+      printf("Fx55: Stores V[0] to V[x] in memory starting at I\n");
296+
297+      for (int i = 0; i <= x; i++) {
298+          V[i] = memory[I + i];
299+      }
300+
301+      pc += 2;
302+      break;
303+
304+    // Fills V0 through Vx (Vx included) with values from memory
305+    // starting at addr I.
306+    case 0x0065:
307+      printf("Read registers V0 through Vx from memory starting at location I \n");
308+      for (int i = 0; i <= x; i++) {
309+          V[i] = memory[I + i];
310+      }
311+      pc += 2;
312+      break;
313+
314+    default:
315+      printf("Fx65: Read registers V[0] through V[x] from memory starting at I.\n");
316+      break;  
317+  }
318+
319+  break;
320+
321+
322+  // 6XNN: Sets Vx to NN
323+  case 0x6000:
324+    V[x] = (opcode & 0x00FF);
325+    pc += 2;
326+    printf("6xkk: The interpreter puts the value kk into register V[x] \n");
327+    break;
328+
329+  // 1NNN: Jumps to address NNN
330+  case 0x1000:
331+    printf("1nnn: Jump to location nnn \n");
332+    pc = opcode & 0x0FFF;
333+    break;
334+
335+  // Display n-byte sprite starting at memory location I at (Vx, Vy)
336+  // Set VF = collision.
337+  case 0xD000:
338+    draw_flag = 1;
339+    unsigned short height = opcode & 0x000F;
340+    unsigned short pixel;
341+    // set collision flag to 0
342+    V[0xF] = 0;
343+    // loop over each row
344+    for (int row = 0; row < height; row++) {
345+      // fetch the pixel value from the memory starting at location I
346+      pixel = memory[I + row];
347+      // loop over 8 bits of one row
348+      for (int col = 0; col < 10; col++) {
349+        // check if current evaluated pixel is set to 1 (0x80 >>
350+        // col scnas throught the byte, one bit at the time)
351+        if ((pixel & (0x80 >> col)) != 0) {
352+          // if drawing causes any pixel to be erased set the
353+          // collision flag to 1
354+          if (gfx[(V[x] + col + ((V[y] + row) * 64))] == 1) {
355+            V[0xF] = 1;
356+          }
357+          // set pixel value by using XOR
358+          gfx[V[x] + col + ((V[y] + row) * 64)] ^= 1;
359+        }
360+      }
361+    }
362+    printf("Dxyn: Display n-byte sprite starting at memory location I at (Vx, Vy), set VF to collision \n");
363+    pc += 2;
364+    break;
365+
366+  default:
367+    printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
368+    printf("OpCode after opcode & 0xF000: 0x%X \n", opcode & 0xF000);
369+  }
370+
371+  // Update timers
372+  if (delay_timer > 0)
373+    --delay_timer;
374+  if (sound_timer > 0) {
375+    if (sound_timer == 1)
376+      printf("BEEP!\n");
377+    --sound_timer;
378+  }
379 }
380 
381 int main(int argc, char *argv[]) {
382   chip8_initialize();
383   chip8_load_program(argv[1]);
384   chip8_init_display();
385+  /*   while (1) {} */
386+
387+  for (int i = 0; i < 50; i++) {
388+    chip8_emulate_cycle();
389+    if (draw_flag == 1) {
390+      chip8_draw(gfx);
391+    }
392+    usleep(150000);
393+  }
394+  SDL_DestroyWindow(window);
395+  SDL_Quit();
396   return 0;
397-}
398+}