feat: rom loading and init
1 files changed, 272 insertions(+), 53 deletions(-) | |||
---|---|---|---|
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+}