chip-8

feat: more opcode coverage

Arjun Choudhary contact@arjunchoudhary.com

commit: 914dc35 parent: c78ea68
1 files changed, 414 insertions(+), 291 deletions(-)
Mmain.c+414-291
M · main.c +414, -291
  1@@ -63,7 +63,7 @@ unsigned char chip8_fontset[80] = {
  2     0xF0, 0x80, 0x80, 0x80, 0xF0, // C
  3     0xE0, 0x90, 0x90, 0x90, 0xE0, // D
  4     0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
  5-    0xF0, 0x80, 0xF0, 0x80, 0x80  // F
  6+    0xF0, 0x80, 0xF0, 0x80, 0x80 // F
  7 };
  8 
  9 // Keypad
 10@@ -74,317 +74,440 @@ const int SCREEN_WIDTH = 64 * 10;
 11 const int SCREEN_HEIGHT = 32 * 10;
 12 
 13 /*** initialize CPU ***/
 14-void chip8_initialize(void) {
 15-  // Seed for rand from time.h
 16-  srand((unsigned int)time(NULL));
 17-  pc = 0x200;        // program counter starts at 0x200 or 512
 18-  opcode = 0;        // reset current opcode
 19-  I = 0;             // reset current index register
 20-  stack_pointer = 0; // reset stack pointer
 21-  printf("\n Chip 8 Initialized successfully \n");
 22-  memcpy(memory, chip8_fontset, sizeof(chip8_fontset));
 23+void chip8_initialize(void)
 24+{
 25+    // Seed for rand from time.h
 26+    srand((unsigned int)time(NULL));
 27+    pc = 0x200; // program counter starts at 0x200 or 512
 28+    opcode = 0; // reset current opcode
 29+    I = 0; // reset current index register
 30+    stack_pointer = 0; // reset stack pointer
 31+    printf("\n Chip 8 Initialized successfully \n");
 32+    memcpy(memory, chip8_fontset, sizeof(chip8_fontset));
 33 }
 34 
 35 /*** load rom ***/
 36-void chip8_load_program(char *filename) {
 37-  FILE *program = fopen(filename, "rb");
 38-
 39-  // get file size
 40-  fseek(program, 0, SEEK_END);
 41-  long buf_len = ftell(program);
 42-  rewind(program);
 43-
 44-  // load data into buffer
 45-  char *buf = malloc((buf_len + 1) * sizeof(char));
 46-  fread(buf, buf_len, 1, program);
 47-  printf("Rom loaded succesfully \n");
 48-  printf("Rom size: %zu bytes \n", buf_len);
 49-  fclose(program);
 50-
 51-  // load ROM into memory
 52-  for (int i = 0; i < buf_len; i++) {
 53-    memory[0x200 + i] = buf[i];
 54-    // Big endian so check last two values from the right
 55-    printf("Byte successfully read: %08X \n", buf[i]);
 56-  }
 57-  // clear buffer
 58-  free(buf);
 59+void chip8_load_program(char* filename)
 60+{
 61+    FILE* program = fopen(filename, "rb");
 62+
 63+    // get file size
 64+    fseek(program, 0, SEEK_END);
 65+    long buf_len = ftell(program);
 66+    rewind(program);
 67+
 68+    // load data into buffer
 69+    char* buf = malloc((buf_len + 1) * sizeof(char));
 70+    fread(buf, buf_len, 1, program);
 71+    printf("Rom loaded succesfully \n");
 72+    printf("Rom size: %zu bytes \n", buf_len);
 73+    fclose(program);
 74+
 75+    // load ROM into memory
 76+    for (int i = 0; i < buf_len; i++) {
 77+        memory[0x200 + i] = buf[i];
 78+        // Big endian so check last two values from the right
 79+        printf("Byte successfully read: %08X \n", buf[i]);
 80+    }
 81+    // clear buffer
 82+    free(buf);
 83 }
 84 
 85 /*** initialize display ***/
 86 
 87-SDL_Window *window;
 88-SDL_Renderer *renderer;
 89-
 90-int chip8_init_display() {
 91-  SDL_Init(SDL_INIT_VIDEO);
 92-  // Init Window struct
 93-
 94-  // Init window
 95-  window =
 96-      SDL_CreateWindow("CHIP8", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
 97-                       SCREEN_WIDTH, SCREEN_HEIGHT, 0);
 98-  // Init renderer struct
 99-  renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
100-
101-  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
102-    printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
103-    return -1;
104-  }
105-  // Check that the window was successfully created
106-
107-  if (window == NULL) {
108-    // In the case that the window could not be made...
109-    printf("Could not create window: %s\n", SDL_GetError());
110-    return -1;
111-  }
112-  return 0;
113+SDL_Window* window;
114+SDL_Renderer* renderer;
115+
116+int chip8_init_display()
117+{
118+    SDL_Init(SDL_INIT_VIDEO);
119+    // Init Window struct
120+
121+    // Init window
122+    window = SDL_CreateWindow("CHIP8",
123+        SDL_WINDOWPOS_CENTERED,
124+        SDL_WINDOWPOS_CENTERED,
125+        SCREEN_WIDTH,
126+        SCREEN_HEIGHT,
127+        0);
128+    // Init renderer struct
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+    // Check that the window was successfully created
136+
137+    if (window == NULL) {
138+        // In the case that the window could not be made...
139+        printf("Could not create window: %s\n", SDL_GetError());
140+        return -1;
141+    }
142+    return 0;
143 }
144 
145-void stop_display(void) {
146-  SDL_DestroyWindow(window);
147-  SDL_Quit();
148+void stop_display(void)
149+{
150+    SDL_DestroyWindow(window);
151+    SDL_Quit();
152 }
153 
154-void chip8_draw(unsigned char *gfx) {
155-  SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
156-  // Init renderer
157-  SDL_RenderClear(renderer);
158-  // SDL Render color (background)
159-
160-  // Clear the current color and use the one specified above
161-  SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
162-
163-  for (int y = 0; y < 32; y++) {
164-    for (int x = 0; x < 64; x++) {
165-      if (gfx[x + (y * 64)]) {
166-        SDL_Rect rect;
167-        rect.x = x * 10;
168-        rect.y = y * 10;
169-        rect.w = 10;
170-        rect.h = 10;
171-
172-        SDL_RenderFillRect(renderer, &rect);
173-      }
174+void chip8_draw(unsigned char* gfx)
175+{
176+    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
177+    // Init renderer
178+    SDL_RenderClear(renderer);
179+    // SDL Render color (background)
180+
181+    // Clear the current color and use the one specified above
182+    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
183+
184+    for (int y = 0; y < 32; y++) {
185+        for (int x = 0; x < 64; x++) {
186+            if (gfx[x + (y * 64)]) {
187+                SDL_Rect rect;
188+                rect.x = x * 10;
189+                rect.y = y * 10;
190+                rect.w = 10;
191+                rect.h = 10;
192+
193+                SDL_RenderFillRect(renderer, &rect);
194+            }
195+        }
196     }
197-  }
198-  SDL_RenderPresent(renderer);
199+    SDL_RenderPresent(renderer);
200 }
201 
202-void chip8_emulate_cycle() {
203-  draw_flag = 0;
204-  sound_flag = 0;
205-  printf(" \n");
206-
207-  // Fetch opcode present at program counter and program counter + 1
208-  // Since each opcode is supposed to be 2 bytes we fetch both pc and pc + 1
209-  // Shift the current 8 bytes in memory[pc] left by 8 points
210-  // We merge them using binary OR to get the entire opcode
211-  opcode = memory[pc] << 8 | memory[pc + 1];
212-
213-  // Vx register, we are basically "grabbing" the x present in some
214-  // instructions like 3XNN
215-  unsigned short x = (opcode & 0x0F00) >> 8;
216-
217-  // Vy register, we are basically "grabbing" the y present in some
218-  // instructions like 5XY0
219-  unsigned short y = (opcode & 0x00F0) >> 4;
220-
221-  printf("0x%X \n", opcode);
222-  switch (opcode & 0xF000) {
223-  case 0x0000:
224-    for (int i = 0; i < 64 * 32; i++) {
225-      gfx[i] = 0;
226-    }
227-    printf("00E0: Display Cleared\n");
228-    pc += 2;
229-    break;
230-    
231-  case 0xC000:
232-    V[x] = (rand() % 256) & (opcode & 0x00FF);
233-    pc += 2;
234-    printf("Cxkk: The value at V[x] is %d \n", V[x]);
235-    break;
236-
237-  case 0xA000:
238-    I = opcode & 0x0FFF;
239-    pc += 2;
240-    printf("Annn: The value at register I is set to 0x%X \n", opcode & 0x0FFF);
241-    break;
242-
243-  // set of of 7 instructions starting with FX
244-  case 0xF000:
245-  switch (opcode & 0x00FF) {
246-    case 0x0007:
247-    printf("Fx07: Place value of delay_timer into V[x]\n");
248-    V[x] = delay_timer;
249-    pc += 2;
250-    break;
251-    
252-    case 0x000A:
253-    printf("Fx0A: Stop all execution till key press, keypress is stored in V[x]\n");
254-    for (int i = 0; i < 16; i++) {
255-      if (keypad[i]) {
256-        V[x] = i;
257+void chip8_emulate_cycle()
258+{
259+    draw_flag = 0;
260+    sound_flag = 0;
261+    printf(" \n");
262+
263+    // Fetch opcode present at program counter and program counter + 1
264+    // Since each opcode is supposed to be 2 bytes we fetch both pc and pc + 1
265+    // Shift the current 8 bytes in memory[pc] left by 8 points
266+    // We merge them using binary OR to get the entire opcode
267+    opcode = memory[pc] << 8 | memory[pc + 1];
268+
269+    // Vx register, we are basically "grabbing" the x present in some
270+    // instructions like 3XNN
271+    unsigned short x = (opcode & 0x0F00) >> 8;
272+
273+    // Vy register, we are basically "grabbing" the y present in some
274+    // instructions like 5XY0
275+    unsigned short y = (opcode & 0x00F0) >> 4;
276+
277+    printf("0x%X \n", opcode);
278+    switch (opcode & 0xF000) {
279+    case 0x0000:
280+        for (int i = 0; i < 64 * 32; i++) {
281+            gfx[i] = 0;
282+        }
283+        printf("00E0: Display Cleared\n");
284+        pc += 2;
285+        break;
286+
287+    case 0xC000:
288+        V[x] = (rand() % 256) & (opcode & 0x00FF);
289         pc += 2;
290+        printf("Cxkk: The value at V[x] is %d \n", V[x]);
291         break;
292-          }
293+
294+    case 0xA000:
295+        I = opcode & 0x0FFF;
296+        pc += 2;
297+        printf("Annn: The value at register I is set to 0x%X \n",
298+            opcode & 0x0FFF);
299+        break;
300+
301+    // 2nnn CALL subroutine at nnn
302+    case 0x2000:
303+        stack_pointer += 1;
304+        stack[stack_pointer] = pc;
305+        pc = opcode & 0x0FFF; // parse the NNN
306+        printf("2nnn: Calls subroutine at nnn \n");
307+        break;
308+
309+    // 3XNN: Skip next instruction if Vx = kk
310+    case 0x3000:
311+        if (V[x] == (opcode & 0x00FF)) {
312+            pc += 2;
313         }
314-      break;
315-    
316-    case 0x0015:
317-    printf("Fx15: Sets the delay_timer value to V[x]\n");
318-    delay_timer = V[x];
319-    pc += 2;
320-    break;
321-    
322-    // FX18: Sets the sound timer to Vx
323-    case 0x0018:
324-    printf("Fx18: Sets the sound_timer value to V[x]\n");
325-    sound_timer = V[x];
326-    pc += 2;
327-    break;
328-
329-    // FX1E: Adds Vx to I
330-    case 0x001E:
331-    printf("Fx1E:The values of I and Vx are added, and the results are stored in I\n");
332-    I += V[x];
333-    pc += 2;
334-    break;
335-
336-    // FX29: Sets I to the location of the sprite for the character
337-    // in Vx
338-    case 0x0029:
339-    printf("Fx29: Set location of I to mathching 0x sprite in V[x] \n");
340-    // Each digit contains 5 bytes 
341-    I = V[x] * 5;
342-    pc += 2;
343-    break;
344-    
345-    /*
346-    * FX33:
347-    *
348-    * Stores the binary-coded decimal representation
349-    * of VX, with the most significant of three digits
350-    * at the address in I, the middle digit at I plus
351-    * 1, and the least significant digit at I plus 2.
352-    * (In other words, take the decimal representation
353-    * of VX, place the hundreds digit in memory
354-    * at location in I, the tens digit at
355-    * location I+1, and the ones digit at
356-    * location I+2.)
357-    * */
358-
359-    case 0x0033:
360-      printf("Fx33: Stores the binary-coded decimal representation of VX \n");
361-      memory[I] = (V[x] % 1000) / 100;
362-      memory[I + 1] = (V[x] % 100) / 10;
363-      memory[I + 2] = (V[x] % 10);
364-
365-      pc += 2;
366-      break;
367-
368-    case 0x0055:
369-      printf("Fx55: Stores V[0] to V[x] in memory starting at I\n");
370-
371-      for (int i = 0; i <= x; i++) {
372-          V[i] = memory[I + i];
373-      }
374-
375-      pc += 2;
376-      break;
377-
378-    // Fills V0 through Vx (Vx included) with values from memory
379-    // starting at addr I.
380-    case 0x0065:
381-      printf("Read registers V0 through Vx from memory starting at location I \n");
382-      for (int i = 0; i <= x; i++) {
383-          V[i] = memory[I + i];
384-      }
385-      pc += 2;
386-      break;
387+        pc += 2;
388+        printf("3XNN: Skip next instruction if V[x] == kk \n");
389+        break;
390 
391-    default:
392-      printf("Fx65: Read registers V[0] through V[x] from memory starting at I.\n");
393-      break;  
394-  }
395-
396-  break;
397-
398-
399-  // 6XNN: Sets Vx to NN
400-  case 0x6000:
401-    V[x] = (opcode & 0x00FF);
402-    pc += 2;
403-    printf("6xkk: The interpreter puts the value kk into register V[x] \n");
404-    break;
405-
406-  // 1NNN: Jumps to address NNN
407-  case 0x1000:
408-    printf("1nnn: Jump to location nnn \n");
409-    pc = opcode & 0x0FFF;
410-    break;
411-
412-  // Display n-byte sprite starting at memory location I at (Vx, Vy)
413-  // Set VF = collision.
414-  case 0xD000:
415-    draw_flag = 1;
416-    unsigned short height = opcode & 0x000F;
417-    unsigned short pixel;
418-    // set collision flag to 0
419-    V[0xF] = 0;
420-    // loop over each row
421-    for (int row = 0; row < height; row++) {
422-      // fetch the pixel value from the memory starting at location I
423-      pixel = memory[I + row];
424-      // loop over 8 bits of one row
425-      for (int col = 0; col < 10; col++) {
426-        // check if current evaluated pixel is set to 1 (0x80 >>
427-        // col scnas throught the byte, one bit at the time)
428-        if ((pixel & (0x80 >> col)) != 0) {
429-          // if drawing causes any pixel to be erased set the
430-          // collision flag to 1
431-          if (gfx[(V[x] + col + ((V[y] + row) * 64))] == 1) {
432-            V[0xF] = 1;
433-          }
434-          // set pixel value by using XOR
435-          gfx[V[x] + col + ((V[y] + row) * 64)] ^= 1;
436+    // 4xkk: Skip next instruction if V[x] != kk
437+    case 0x4000:
438+        if (V[x] != (opcode & 0x00FF)) {
439+            pc += 2;
440         }
441-      }
442+        pc += 2;
443+        printf("4xkk: Skip next instruction if V[x] != kk \n");
444+        break;
445+
446+    // 6XNN: Sets Vx to NN
447+    case 0x6000:
448+        V[x] = (opcode & 0x00FF);
449+        pc += 2;
450+        printf("6xkk: The interpreter puts the value kk into register V[x] \n");
451+        break;
452+
453+    // 7xkk: ADD Vx, byte
454+    case 0x7000:
455+        V[x] += opcode & 0x00FF;
456+        pc += 2;
457+        printf("7xkk: Adds the value kk ot the value of V[x] then stores it in "
458+               "V[x] \n");
459+        break;
460+
461+    // 1NNN: Jumps to address NNN
462+    case 0x1000:
463+        printf("1nnn: Jump to location nnn \n");
464+        pc = opcode & 0x0FFF;
465+        break;
466+
467+    // Display n-byte sprite starting at memory location I at (Vx, Vy)
468+    // Set VF = collision.
469+    case 0xD000:
470+        draw_flag = 1;
471+        unsigned short height = opcode & 0x000F;
472+        unsigned short pixel;
473+        // set collision flag to 0
474+        V[0xF] = 0;
475+        // loop over each row
476+        for (int row = 0; row < height; row++) {
477+            // fetch the pixel value from the memory starting at location I
478+            pixel = memory[I + row];
479+            // loop over 8 bits of one row
480+            for (int col = 0; col < 10; col++) {
481+                // check if current evaluated pixel is set to 1 (0x80 >>
482+                // col scnas throught the byte, one bit at the time)
483+                if ((pixel & (0x80 >> col)) != 0) {
484+                    // if drawing causes any pixel to be erased set the
485+                    // collision flag to 1
486+                    if (gfx[(V[x] + col + ((V[y] + row) * 64))] == 1) {
487+                        V[0xF] = 1;
488+                    }
489+                    // set pixel value by using XOR
490+                    gfx[V[x] + col + ((V[y] + row) * 64)] ^= 1;
491+                }
492+            }
493+        }
494+        printf(
495+            "Dxyn: Display n-byte sprite starting at memory location I at (Vx, "
496+            "Vy), set VF to collision \n");
497+        pc += 2;
498+        break;
499+
500+    // Instructions starting with FX
501+    case 0xF000:
502+        switch (opcode & 0x00FF) {
503+        case 0x0007:
504+            printf("Fx07: Place value of delay_timer into V[x]\n");
505+            V[x] = delay_timer;
506+            pc += 2;
507+            break;
508+
509+        case 0x000A:
510+            printf(
511+                "Fx0A: Stop all execution till key press, keypress is stored in "
512+                "V[x]\n");
513+            for (int i = 0; i < 16; i++) {
514+                if (keypad[i]) {
515+                    V[x] = i;
516+                    pc += 2;
517+                    break;
518+                }
519+            }
520+            break;
521+
522+        case 0x0015:
523+            printf("Fx15: Sets the delay_timer value to V[x]\n");
524+            delay_timer = V[x];
525+            pc += 2;
526+            break;
527+
528+        // FX18: Sets the sound timer to Vx
529+        case 0x0018:
530+            printf("Fx18: Sets the sound_timer value to V[x]\n");
531+            sound_timer = V[x];
532+            pc += 2;
533+            break;
534+
535+        // FX1E: Adds Vx to I
536+        case 0x001E:
537+            printf("Fx1E:The values of I and Vx are added, and the results are "
538+                   "stored in I\n");
539+            I += V[x];
540+            pc += 2;
541+            break;
542+
543+        // FX29: Sets I to the location of the sprite for the character
544+        // in Vx
545+        case 0x0029:
546+            printf("Fx29: Set location of I to mathching 0x sprite in V[x] \n");
547+            // Each digit contains 5 bytes
548+            I = V[x] * 5;
549+            pc += 2;
550+            break;
551+
552+            /*
553+             * FX33:
554+             *
555+             * Stores the binary-coded decimal representation
556+             * of VX, with the most significant of three digits
557+             * at the address in I, the middle digit at I plus
558+             * 1, and the least significant digit at I plus 2.
559+             * (In other words, take the decimal representation
560+             * of VX, place the hundreds digit in memory
561+             * at location in I, the tens digit at
562+             * location I+1, and the ones digit at
563+             * location I+2.)
564+             * */
565+
566+        case 0x0033:
567+            printf(
568+                "Fx33: Stores the binary-coded decimal representation of VX \n");
569+            memory[I] = (V[x] % 1000) / 100;
570+            memory[I + 1] = (V[x] % 100) / 10;
571+            memory[I + 2] = (V[x] % 10);
572+
573+            pc += 2;
574+            break;
575+
576+        case 0x0055:
577+            printf("Fx55: Stores V[0] to V[x] in memory starting at I\n");
578+
579+            for (int i = 0; i <= x; i++) {
580+                V[i] = memory[I + i];
581+            }
582+
583+            pc += 2;
584+            break;
585+
586+        // Fills V0 through Vx (Vx included) with values from memory
587+        // starting at addr I.
588+        case 0x0065:
589+            printf("Read registers V0 through Vx from memory starting at "
590+                   "location I \n");
591+            for (int i = 0; i <= x; i++) {
592+                V[i] = memory[I + i];
593+            }
594+            pc += 2;
595+            break;
596+
597+        default:
598+            printf("Unknown opcode 0x%x.\n", opcode);
599+            break;
600+        }
601+        break;
602+
603+    // Instructions starting with 8
604+    case (0x8000):
605+        switch (opcode & 0x0FFF) {
606+        // 8xy0: Sets Vx to Vy
607+        case (0x0000):
608+            V[x] = V[y];
609+            pc += 2;
610+            break;
611+
612+        // 8xy1: V[x] = V[x] | V[y]
613+        case (0x0001):
614+            V[x] = (V[x] | V[y]);
615+            pc += 2;
616+            break;
617+
618+        // 8xy2: V[x] = V[x] | V[y]
619+        case (0x0002):
620+            V[x] = (V[x] & V[y]);
621+            pc += 2;
622+            break;
623+
624+        // 8xy3: V[x] = [Vx] ^ V[y]
625+        case (0x0003):
626+            V[x] = (V[x] ^ V[y]);
627+            pc += 2;
628+            break;
629+
630+        // 8xy4: V[x] = V[x] + V[y], set VF = carry.
631+        case (0x0004):
632+            if (V[x] + V[y] > 255) {
633+                printf("is this valid?");
634+                V[0xF] = 1;
635+            } else {
636+                V[0xF] = 0;
637+            }
638+            V[x] += V[y];
639+            pc += 2;
640+            break;
641+
642+        // 8xy5: V[x] = V[x] - V[y], set VF = NOT BORROW
643+        case (0x0005):
644+            if (V[x] > V[y]) {
645+                V[0xF] = 1;
646+            } else {
647+                V[0xF] = 0;
648+            }
649+            V[x] -= V[y];
650+            pc += 2;
651+            break;
652+
653+        // 8xy6: Sets Vx to Vy minus Vx. Vf is set to 0 when there's a borrow
654+        case (0x0006):
655+            V[0xF] = V[x] & 0x1;
656+            V[x] = (V[x] >> 1);
657+            pc += 2;
658+            break;
659+
660+        // Set Vx = Vy - Vx, set VF = NOT borrow
661+        case (0x0007):
662+            if (V[y] > V[x]) {
663+                V[0xF] = 1;
664+            } else {
665+                V[0xF] = 0;
666+            }
667+            V[x] = V[y] - V[x];
668+            pc += 2;
669+            break;
670+
671+        default:
672+            printf("Unknown opcode 0x%X.\n", opcode);
673+            break;
674+        }
675+        break;
676+
677+    default:
678+        printf("Unknown opcode 0x%X.\n", opcode);
679     }
680-    printf("Dxyn: Display n-byte sprite starting at memory location I at (Vx, Vy), set VF to collision \n");
681-    pc += 2;
682-    break;
683-
684-  default:
685-    printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
686-    printf("OpCode after opcode & 0xF000: 0x%X \n", opcode & 0xF000);
687-  }
688-
689-  // Update timers
690-  if (delay_timer > 0)
691-    --delay_timer;
692-  if (sound_timer > 0) {
693-    if (sound_timer == 1)
694-      printf("BEEP!\n");
695-    --sound_timer;
696-  }
697-}
698 
699-int main(int argc, char *argv[]) {
700-  chip8_initialize();
701-  chip8_load_program(argv[1]);
702-  chip8_init_display();
703-  /*   while (1) {} */
704+    // Update timers
705+    if (delay_timer > 0)
706+        --delay_timer;
707+    if (sound_timer > 0) {
708+        if (sound_timer == 1)
709+            printf("BEEP!\n");
710+        --sound_timer;
711+    }
712+}
713 
714-  for (int i = 0; i < 50; i++) {
715-    chip8_emulate_cycle();
716-    if (draw_flag == 1) {
717-      chip8_draw(gfx);
718+int main(int argc, char* argv[])
719+{
720+    chip8_initialize();
721+    chip8_load_program(argv[1]);
722+    chip8_init_display();
723+    /*   while (1) {} */
724+
725+    for (int i = 0; i < 500; i++) {
726+        chip8_emulate_cycle();
727+        if (draw_flag == 1) {
728+            chip8_draw(gfx);
729+        }
730+        usleep(10000);
731     }
732-    usleep(150000);
733-  }
734-  SDL_DestroyWindow(window);
735-  SDL_Quit();
736-  return 0;
737+    SDL_DestroyWindow(window);
738+    SDL_Quit();
739+    return 0;
740 }