chip-8
chip-8 emulatorfeat: rom loading and init
| 1 files changed, 272 insertions(+), 53 deletions(-) | |||
|---|---|---|---|
| MOD | main.c | +272 | -53 |
--- a/main.c
+++ b/main.c
@@ -1,8 +1,12 @@
/*** includes ***/
#include <SDL2/SDL.h>
+#include <SDL2/SDL_config-x86_64.h>
+#include <bits/types/struct_FILE.h>
#include <errno.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
@@ -18,20 +22,24 @@ unsigned char memory[4096];
unsigned char V[16];
// Index or Address Register
unsigned short I;
-// Program counter | A psudo register that points to the next instruction on the
-// stack Instruction pointer being my preffered nomenclature
+// Program counter | A pseudo register that points to the next instruction on
+// the stack Instruction pointer being my preferred nomenclature
unsigned short pc;
// Display containing 2048 pixels in an array
unsigned char gfx[64 * 32];
// Timer registers that countdown to 0 at 60Hz
unsigned char delay_timer;
unsigned char sound_timer;
-//
+// Sound flag
+unsigned char sound_flag = 0;
+// Draw flag
+unsigned char draw_flag = 0;
+// Stack and stack pointer
unsigned short stack[16];
unsigned short stack_pointer;
-// Fontset used by CHIP 8
-// Also used to draw sprits using XOR
+// Font set used by CHIP 8
+// Also used to draw sprites using XOR
// DEC HEX BIN RESULT DEC HEX BIN RESULT
// 240 0xF0 1111 0000 **** 240 0xF0 1111 0000 ****
// 144 0x90 1001 0000 * * 16 0x10 0001 0000 *
@@ -62,20 +70,18 @@ unsigned char chip8_fontset[80] = {
unsigned char keypad[16];
// Screen dimension constants
-const int SCREEN_WIDTH = 640;
-const int SCREEN_HEIGHT = 320;
+const int SCREEN_WIDTH = 64 * 10;
+const int SCREEN_HEIGHT = 32 * 10;
-/*** initialize cpu ***/
+/*** initialize CPU ***/
void chip8_initialize(void) {
+ // Seed for rand from time.h
+ srand((unsigned int)time(NULL));
pc = 0x200; // program counter starts at 0x200 or 512
opcode = 0; // reset current opcode
I = 0; // reset current index register
stack_pointer = 0; // reset stack pointer
- printf("Chip 8 Initialized successfully \n");
- // Load fontset into memory
- //for (int i = 0; i < 80; i++) {
- // memory[i] = fontset[i];
- // }
+ printf("\n Chip 8 Initialized successfully \n");
memcpy(memory, chip8_fontset, sizeof(chip8_fontset));
}
@@ -95,7 +101,7 @@ void chip8_load_program(char *filename) {
printf("Rom size: %zu bytes \n", buf_len);
fclose(program);
- // load rom into memory
+ // load ROM into memory
for (int i = 0; i < buf_len; i++) {
memory[0x200 + i] = buf[i];
// Big endian so check last two values from the right
@@ -106,66 +112,279 @@ void chip8_load_program(char *filename) {
}
/*** initialize display ***/
-int chip8_init_display (){
+
+SDL_Window *window;
+SDL_Renderer *renderer;
+
+int chip8_init_display() {
SDL_Init(SDL_INIT_VIDEO);
// Init Window struct
- SDL_Window *window;
+
// Init window
- window =
- SDL_CreateWindow(
- "CHIP8",
- SDL_WINDOWPOS_CENTERED,
- SDL_WINDOWPOS_CENTERED,
- SCREEN_WIDTH,
- SCREEN_HEIGHT,
- 0);
+ window =
+ SDL_CreateWindow("CHIP8", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+ SCREEN_WIDTH, SCREEN_HEIGHT, 0);
// Init renderer struct
- SDL_Renderer* renderer;
- // Init renderer
- renderer =
- SDL_CreateRenderer(
- window,
- -1,
- SDL_RENDERER_ACCELERATED);
- //SDL Render color (background)
- SDL_SetRenderDrawColor( renderer, 0, 0, 0, 0 );
- // Clear the current color and use the one specified above
- SDL_RenderClear( renderer );
- // Shape
- SDL_Rect rectangle;
- rectangle.x = 0;
- rectangle.y = 0;
- rectangle.w = 90;
- rectangle.h = 100;
-
- SDL_SetRenderDrawColor( renderer, 0, 0, 255, 255 );
- SDL_RenderFillRect( renderer, &rectangle );
- SDL_RenderPresent(renderer);
-
+ renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
return -1;
}
-
// Check that the window was successfully created
+
if (window == NULL) {
// In the case that the window could not be made...
printf("Could not create window: %s\n", SDL_GetError());
return -1;
}
- SDL_Delay(3000);
- // Pause execution for 3000 milliseconds, for example
- // Close and destroy the window
+ return 0;
+}
+
+void stop_display(void) {
SDL_DestroyWindow(window);
- // Clean up
SDL_Quit();
- return 0;
+}
+
+void chip8_draw(unsigned char *gfx) {
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+ // Init renderer
+ SDL_RenderClear(renderer);
+ // SDL Render color (background)
+
+ // Clear the current color and use the one specified above
+ SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+
+ for (int y = 0; y < 32; y++) {
+ 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;
+
+ SDL_RenderFillRect(renderer, &rect);
+ }
+ }
+ }
+ SDL_RenderPresent(renderer);
+}
+
+void chip8_emulate_cycle() {
+ draw_flag = 0;
+ sound_flag = 0;
+ printf(" \n");
+
+ // Fetch opcode present at program counter and program counter + 1
+ // Since each opcode is supposed to be 2 bytes we fetch both pc and pc + 1
+ // Shift the current 8 bytes in memory[pc] left by 8 points
+ // We merge them using binary OR to get the entire opcode
+ opcode = memory[pc] << 8 | memory[pc + 1];
+
+ // Vx register, we are basically "grabbing" the x present in some
+ // instructions like 3XNN
+ unsigned short x = (opcode & 0x0F00) >> 8;
+
+ // Vy register, we are basically "grabbing" the y present in some
+ // instructions like 5XY0
+ unsigned short y = (opcode & 0x00F0) >> 4;
+
+ printf("0x%X \n", opcode);
+ switch (opcode & 0xF000) {
+ case 0x0000:
+ for (int i = 0; i < 64 * 32; i++) {
+ gfx[i] = 0;
+ }
+ 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);
+ break;
+
+ // set of of 7 instructions starting with FX
+ case 0xF000:
+ switch (opcode & 0x00FF) {
+ case 0x0007:
+ printf("Fx07: Place value of delay_timer into V[x]\n");
+ V[x] = delay_timer;
+ pc += 2;
+ break;
+
+ case 0x000A:
+ printf("Fx0A: Stop all execution till key press, keypress is stored in V[x]\n");
+ for (int i = 0; i < 16; i++) {
+ if (keypad[i]) {
+ V[x] = i;
+ pc += 2;
+ break;
+ }
+ }
+ break;
+
+ case 0x0015:
+ printf("Fx15: Sets the delay_timer value to V[x]\n");
+ delay_timer = V[x];
+ pc += 2;
+ break;
+
+ // FX18: Sets the sound timer to Vx
+ case 0x0018:
+ printf("Fx18: Sets the sound_timer value to V[x]\n");
+ sound_timer = V[x];
+ pc += 2;
+ break;
+
+ // FX1E: Adds Vx to I
+ case 0x001E:
+ printf("Fx1E:The values of I and Vx are added, and the results are stored in I\n");
+ I += V[x];
+ pc += 2;
+ break;
+
+ // FX29: Sets I to the location of the sprite for the character
+ // in Vx
+ case 0x0029:
+ printf("Fx29: Set location of I to mathching 0x sprite in V[x] \n");
+ // Each digit contains 5 bytes
+ I = V[x] * 5;
+ pc += 2;
+ break;
+
+ /*
+ * FX33:
+ *
+ * Stores the binary-coded decimal representation
+ * of VX, with the most significant of three digits
+ * at the address in I, the middle digit at I plus
+ * 1, and the least significant digit at I plus 2.
+ * (In other words, take the decimal representation
+ * of VX, place the hundreds digit in memory
+ * at location in I, the tens digit at
+ * location I+1, and the ones digit at
+ * location I+2.)
+ * */
+
+ case 0x0033:
+ printf("Fx33: Stores the binary-coded decimal representation of VX \n");
+ memory[I] = (V[x] % 1000) / 100;
+ memory[I + 1] = (V[x] % 100) / 10;
+ memory[I + 2] = (V[x] % 10);
+
+ pc += 2;
+ break;
+
+ case 0x0055:
+ printf("Fx55: Stores V[0] to V[x] in memory starting at I\n");
+
+ for (int i = 0; i <= x; i++) {
+ V[i] = memory[I + i];
+ }
+
+ pc += 2;
+ break;
+
+ // Fills V0 through Vx (Vx included) with values from memory
+ // starting at addr I.
+ case 0x0065:
+ printf("Read registers V0 through Vx from memory starting at location I \n");
+ for (int i = 0; i <= x; i++) {
+ V[i] = memory[I + i];
+ }
+ pc += 2;
+ break;
+
+ default:
+ printf("Fx65: Read registers V[0] through V[x] from memory starting at I.\n");
+ break;
+ }
+
+ break;
+
+
+ // 6XNN: Sets Vx to NN
+ case 0x6000:
+ V[x] = (opcode & 0x00FF);
+ pc += 2;
+ printf("6xkk: The interpreter puts the value kk into register V[x] \n");
+ break;
+
+ // 1NNN: Jumps to address NNN
+ case 0x1000:
+ printf("1nnn: Jump to location nnn \n");
+ pc = opcode & 0x0FFF;
+ break;
+
+ // Display n-byte sprite starting at memory location I at (Vx, Vy)
+ // Set VF = collision.
+ case 0xD000:
+ draw_flag = 1;
+ unsigned short height = opcode & 0x000F;
+ unsigned short pixel;
+ // set collision flag to 0
+ V[0xF] = 0;
+ // loop over each row
+ for (int row = 0; row < height; row++) {
+ // 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++) {
+ // 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) {
+ // if drawing causes any pixel to be erased set the
+ // collision flag to 1
+ if (gfx[(V[x] + col + ((V[y] + row) * 64))] == 1) {
+ V[0xF] = 1;
+ }
+ // set pixel value by using XOR
+ gfx[V[x] + col + ((V[y] + row) * 64)] ^= 1;
+ }
+ }
+ }
+ printf("Dxyn: Display n-byte sprite starting at memory location I at (Vx, Vy), set VF to collision \n");
+ pc += 2;
+ break;
+
+ default:
+ printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+ printf("OpCode after opcode & 0xF000: 0x%X \n", opcode & 0xF000);
+ }
+
+ // Update timers
+ if (delay_timer > 0)
+ --delay_timer;
+ if (sound_timer > 0) {
+ if (sound_timer == 1)
+ printf("BEEP!\n");
+ --sound_timer;
+ }
}
int main(int argc, char *argv[]) {
chip8_initialize();
chip8_load_program(argv[1]);
chip8_init_display();
+ /* while (1) {} */
+
+ for (int i = 0; i < 50; i++) {
+ chip8_emulate_cycle();
+ if (draw_flag == 1) {
+ chip8_draw(gfx);
+ }
+ usleep(150000);
+ }
+ SDL_DestroyWindow(window);
+ SDL_Quit();
return 0;
-}
+}<
\ No newline at end of file