summaryrefslogtreecommitdiffstats
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c674
1 files changed, 674 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..b1158ea
--- /dev/null
+++ b/main.c
@@ -0,0 +1,674 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <SDL2/SDL.h>
+#include <time.h>//for rand()
+#include "main.h"
+#include "number_sprites.h"
+
+//used throughout
+int x,y,N;
+
+unsigned int lastFrame = 0;//number of milliseconds since last frame, wraps around in ~47 days
+unsigned int lastTick = 0;//number of milliseconds since tick, wraps around in ~47 days
+unsigned int delayTimer = 0;//meant to count down at 60Hz
+
+//opcodes are two bytes and stored big endian.
+int main(int argc, char *argv[])
+{
+srand(time(NULL));//seed pseudorandom number generator
+
+SDL_Window *window;
+SDL_Renderer *renderer;
+SDL_Texture *texture;
+SDL_Event event;
+
+if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
+ exit(3);
+ }
+
+window = SDL_CreateWindow("SDL_CreateTexture",
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ WIDTH*16, HEIGHT*16,
+ SDL_WINDOW_RESIZABLE);
+
+renderer = SDL_CreateRenderer(window, -1, 0);
+//renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);//locking to the screen Hz seems to be a stopgap method for now...
+texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
+
+int pitch;
+
+uint8_t opcode[2];
+
+for(int i = 0; i < 0xF; ++i){V[i] = 0;}//zero out all the registers
+for(int i = 0; i < 12; ++i){returnPos[i] = 0;}//zero out the stack
+
+
+if(!argv[1]){fprintf(stderr,"Usage: %s <program>\n", argv[0]); return 1;}
+
+
+//FILE *input = fopen("PONG", "r");
+FILE *input = fopen(argv[1], "r");
+if (!input){fprintf(stderr, "Program not found.\n"); return 1;}
+
+fseek(input, 0L, SEEK_END);
+int size = ftell(input);
+rewind(input);
+
+char *program = malloc(sizeof(char)*size);
+if (!program){fprintf(stderr, "Malloc failed!\n"); exit(1);}
+
+int result = fread(program, 1, size, input);
+if (!result){fprintf(stderr, "Fread failed!\n"); exit(1);}
+fclose(input);//not needed anymore
+
+I = program; //point I to the start of the program
+
+int position = 0;//position in program
+while(1)
+{
+int ret = SDL_LockTexture(texture, NULL, (void **) &pixels, &pitch);
+if (ret != 0){SDL_Log("ret=%d pix=%p error=%s", ret, pixels, SDL_GetError());}
+
+memcpy(opcode, program+position, 2*sizeof(char));
+position += 2;//read next opcode next time
+
+int half;//half will equal top half of the first opcode
+half = (opcode[0] & 0xF0) >> 4;
+
+switch (half)
+ {
+ case 0x0://0NNN or 00E0 or 00EE
+ switch(opcode[1])
+ {
+ case 0xE0://00E0
+ printf("display_clear()\n"); //clear the screen
+ display_clear();
+ break;
+ case 0xEE://00EE
+ printf("return;\n");//return from subroute
+ position = returnPos[0];//set position back to the stored position
+ for (int i = 0; i < 11; ++i){returnPos[i] = returnPos[i+1];}//shift all above elements onto the first one to simulate a stack pop
+ break;
+ default://0NNN
+ fprintf(stderr, "RCA 1802 program calling not supported\n"); exit(1);
+ break;
+ }
+ break;
+ //1NNN
+ case 0x1:
+ //below is correct
+ //the lower 4 bits contain the second char
+ N = lowerFour(opcode);
+ N*=0x100;// *= 0x100 sets the correct value for the highest bit
+ //placeholder
+ if ((N+opcode[1])-512 < 0){printf("Program writes below 512 bytes\n"); exit(1);}
+ printf("Jump to address %d\n", (N+opcode[1])-512);//goto NNN
+ position = (N+opcode[1])-512;
+ break;
+ //2NNN
+ case 0x2:
+ N = lowerFour(opcode);
+ N*=0x100;
+ if ((N+opcode[1])-512 < 0){printf("Program writes below 512 bytes\n"); exit(1);}
+ printf("Call subroute at %X\n", (N+opcode[1])-512);// *(0xNNN)()
+ for (int i = 0; i < 11; ++i){returnPos[i+1] = returnPos[i];}//shift up any "stack" contents so any previous return addresses aren't overwritten
+ returnPos[0] = position;//position is where a subroute should return (the counter is automatically incremented)
+ position = (N+opcode[1])-512;//just use a jump
+ break;
+ case 0x3://3XNN
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ if (V[x] == opcode[1]){position+=2;}//if(Vx==NN), skip the next opcode
+ printf("3%X%02X\n", x, opcode[1]);
+ break;
+ case 0x4://4XNN
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ if (V[x] != opcode[1]){position+=2;}//if(Vx!=NN), skip the next opcode
+ printf("4%X%02X\n", x, opcode[1]);
+ break;
+ case 0x5://5XY0
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ if (V[x] == V[y]){position+=2;}//if(Vx==Vy), skip the next opcode
+ printf("5%X%X0\n", x, y);
+ break;
+ case 0x6://6XNN
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ V[x] = (opcode[1] & 0x00FF);//Vx = NN
+ printf("6%X%02X\n", x, opcode[1]);
+ break;
+ case 0x7://7XNN
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+// V[x] += (opcode[1] & 0x00FF); //Vx += NN
+// printf("7%X%02X\n", x, opcode[1]);
+ V[x] += opcode[1]; //Vx += NN
+ printf("7%X%02X\n", x, opcode[1]);
+ break;
+ case 0x8://8XY0 or 8XY1 or 8XY2 or 8XY3 or 8XY4 or 8XY5 or 8XY6 or 8XY7 or 8XYE
+ switch(lowerFour(opcode+1))
+ {
+ case 0x0://8XY0
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ V[x] = V[y];//Vx = Vy
+ printf("8%X%X0\n", x, y);
+ break;
+ case 0x1://8XY1
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ V[x] = V[x]|V[y];//Vx = Vx|Vy
+ printf("8%X%X1\n", x, y);
+ break;
+ case 0x2://8XY2
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ V[x] = V[x] & V[y]; //Vx=Vx&Vy
+ printf("8%X%X2\n", x, y);
+ break;
+ case 0x3://8XY3
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ V[x] = V[x] ^ V[y]; //Vx=Vx^Vy
+ printf("8%X%X3\n", x, y);
+ break;
+ case 0x4://8XY4
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ if (V[x] > (0xFF - V[y]))
+ {
+ V[0xF] = 1;//carry
+ printf("Carry\n");
+ }
+ else
+ {
+ V[0xF] = 0;//no carry
+ printf("No carry\n");
+ }
+ V[x] += V[y]; //Vx+=Vy
+ printf("8%X%X4\n", x, y);
+ break;
+ case 0x5://8XY5
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ if (V[y] > V[x])
+ {
+ V[0xF] = 0;
+ printf("Borrow\n");
+ }
+ else
+ {
+ V[0xF] = 1;
+ printf("No Borrow\n");
+ }
+ V[x] -= V[y]; //Vx-=Vy VF is set to 0 if there's a borrow, and to 1 when there isn't
+ printf("8%X%X5\n", x, y);
+ break;
+ case 0x6://8XY6
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ //Stores the least significant bit of VX in VF
+ V[0xF] = V[x] & 1;
+ //and then shifts VX to the right by 1
+// V[x] >>= 1;
+ //OR
+ //Set register VF to the least significant bit in V[x]
+ //Store the value of register VY shifted right one bit in register VX
+ V[x] = V[y] >> 1;
+ printf("8%X%X6\n", x, y);
+ break;
+ case 0x7://8XY7
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ if (V[x] > V[y])
+ {
+ V[0xF] = 0;
+ printf("Borrow\n");
+ }
+ else
+ {
+ V[0xF] = 1;
+ printf("No Borrow\n");
+ }
+ V[x] = V[y] - V[x]; //Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
+ printf("8%X%X7\n", x, y);
+ break;
+ case 0xE://8XYE
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ //Stores the most significant bit of VX in VF
+ V[0xF] = (V[x] & 0xFF) >> 7;
+ //and then shifts VX to the left by 1
+ //V[x] <<= 1;
+ //OR
+ //Store the value of register VY shifted left one bit in register VX
+ V[x] = V[y] << 1;
+ printf("8%X%XE\n", x, y);
+ break;
+ default:
+ fprintf(stderr, "Illegal Instruction\n"); exit(1);
+ break;
+ }
+ break;
+ case 0x9://9XY0
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits of opcode[1] contains (V)y
+ y = upperFour(opcode+1);
+ if (V[x]!=V[y]){printf("Skip next instruction\n"); position+=2;}//skip next instruction if VX != VY.
+ else{printf("Don't skip next instruction\n");}
+ break;
+ case 0xA://ANNN
+ //the lower 4 bits contain the second char
+ N = lowerFour(opcode);
+ N*=0x100;// *= 0x100 sets the correct value for the highest bit
+ //placeholder
+ I = program+(N+opcode[1])-512;//I = NNN <-- this does seem to set the position of I correctly
+ printf("A%03X\n", N);
+// printf("Value at I: %c\n", *(I) );
+// exit(1);
+ break;
+ case 0xB://BNNN
+ //the lower 4 bits contain the first 'N'
+ N = lowerFour(opcode);
+ N*=0x100;// *= 0x100 sets the correct value for the highest bit
+ //Jumps to the address NNN plus V0.
+ printf("Jump to address %X\n", (V[0]+N+opcode[1])-512);//PC=V0+NNN
+ position = (V[0]+N+opcode[1])-512;
+ break;
+ case 0xC://CXNN
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ V[x] = (rand() % 0xFF) & (opcode[1] & 0x00FF);//Vx=rand()&NN
+ printf("C%X%02X\n", x, opcode[1]);
+ break;
+ case 0xD://DXYN
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //the upper 4 bits contain (V)y
+ y = upperFour(opcode+1);
+ //the lower 4 bits contain N
+ N = lowerFour(opcode+1);
+ printf("D%X%X%X ", x, y, N);
+ printf("draw(X=%d, Y=%d, %d)\n", V[x], V[y], N);
+ draw(V[x], V[y], N);
+ break;
+ case 0xE://EX9E or EXA1
+ switch(opcode[1])
+ {
+ case 0x9E://EX9E
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //skips the next instruction if the key stored in Vx is held down. (non blocking)
+ SDL_PumpEvents();//pumpEvents() needs to be run unless you would like maybe the last frames keyboard state
+ const uint8_t *keyBoardState = SDL_GetKeyboardState(NULL);//get keyboard state
+ //below is more compact than a switch
+ if (keyBoardState[SDL_SCANCODE_KP_0] && (0 == V[x])){printf("Skip since 0 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_1] && (1 == V[x])){printf("Skip since 1 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_2] && (2 == V[x])){printf("Skip since 2 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_3] && (3 == V[x])){printf("Skip since 3 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_4] && (4 == V[x])){printf("Skip since 4 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_5] && (5 == V[x])){printf("Skip since 5 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_6] && (6 == V[x])){printf("Skip since 6 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_7] && (7 == V[x])){printf("Skip since 7 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_8] && (8 == V[x])){printf("Skip since 8 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_KP_9] && (9 == V[x])){printf("Skip since 9 down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_A] && (0xA == V[x])){printf("Skip since A down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_S] && (0xB == V[x])){printf("Skip since B down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_D] && (0xC == V[x])){printf("Skip since C down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_Z] && (0xD == V[x])){printf("Skip since D down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_X] && (0xE == V[x])){printf("Skip since E down\n"); position+=2; break;}
+ if (keyBoardState[SDL_SCANCODE_C] && (0xF == V[x])){printf("Skip since F down\n"); position+=2; break;}
+ printf("E%X9E\n", x);
+ break;
+ case 0xA1://EXA1
+ //Note: SDL_SCANCODE uses the original key and will know work as expected if keyboard is remapped
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //skips the next instruction if the key stored in Vx isn't pressed. (non blocking, placeholder a)
+ //below does not register 2 held down keys at once
+/* SDL_PumpEvents();//SDL_PumpEvents() is used since SDL_PumpEvent() doesn't seem to register a held down key.
+ printf("Yes\n");
+ switch(V[x])//there should be a better way than this...
+ {
+ case 0:
+ if (event.key.keysym.sym != SDLK_0){printf("Skip since 0 is not held down\n"); position+=2;}
+ break;
+ case 1:
+ if (event.key.keysym.sym != SDLK_1){printf("Skip since 1 is not held down\n"); position+=2;}
+ break;
+ case 2:
+ if (event.key.keysym.sym != SDLK_2){printf("Skip since 2 is not held down\n"); position+=2;}
+ break;
+ case 3:
+ if (event.key.keysym.sym != SDLK_3){printf("Skip since 2 is not held down\n"); position+=2;}
+ break;
+ case 4:
+ if (event.key.keysym.sym != SDLK_4){printf("Skip since 4 is not held down\n"); position+=2;}
+ break;
+ case 5:
+ if (event.key.keysym.sym != SDLK_5){printf("Skip since 5 is not held down\n"); position+=2;}
+ break;
+ case 6:
+ if (event.key.keysym.sym != SDLK_6){printf("Skip since 6 is not held down\n"); position+=2;}
+ break;
+ case 7:
+ if (event.key.keysym.sym != SDLK_7){printf("Skip since 7 is not held down\n"); position+=2;}
+ break;
+ case 8:
+ if (event.key.keysym.sym != SDLK_8){printf("Skip since 8 is not held down\n"); position+=2;}
+ break;
+ case 9:
+ if (event.key.keysym.sym != SDLK_9){printf("Skip since 9 is not held down\n"); position+=2;}
+ break;
+ case 0xA:
+ if (event.key.keysym.sym != SDLK_a){printf("Skip since A is not held down\n"); position+=2;}
+ break;
+ case 0xB:
+ if (event.key.keysym.sym != SDLK_b){printf("Skip since B is not held down\n"); position+=2;}
+ break;
+ case 0xC:
+ if (event.key.keysym.sym != SDLK_c){printf("Skip since C is not held down\n"); position+=2;}
+ break;
+ case 0xD:
+ if (event.key.keysym.sym != SDLK_d){printf("Skip since D is not held down\n"); position+=2;}
+ break;
+ case 0xE:
+ if (event.key.keysym.sym != SDLK_e){printf("Skip since E is not held down\n"); position+=2;}
+ break;
+ case 0xF:
+ if (event.key.keysym.sym != SDLK_f){printf("Skip since F is not held down\n"); position+=2;}
+ break;
+ default:
+ fprintf(stderr, "Value in VX seems to be invalid\n");
+ printf("Value is: %X\n", V[x]);
+ exit(1);
+ }
+*/
+ SDL_PumpEvents();//SDL_pumpEvents() is used instead of SDL_pollEvents for many reasons
+ const uint8_t *notKeyBoardState = SDL_GetKeyboardState(NULL);//get keyboard state
+ switch(V[x])
+ {
+ case 0:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_0]){printf("Skip since 0 is not held down\n"); position+=2;}
+ break;
+ case 1:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_1]){printf("Skip since 1 is not held down\n"); position+=2;}
+ break;
+ case 2:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_2]){printf("Skip since 2 is not held down\n"); position+=2;}
+ break;
+ case 3:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_3]){printf("Skip since 2 is not held down\n"); position+=2;}
+ break;
+ case 4:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_4]){printf("Skip since 4 is not held down\n"); position+=2;}
+ break;
+ case 5:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_5]){printf("Skip since 5 is not held down\n"); position+=2;}
+ break;
+ case 6:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_6]){printf("Skip since 6 is not held down\n"); position+=2;}
+ break;
+ case 7:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_7]){printf("Skip since 7 is not held down\n"); position+=2;}
+ break;
+ case 8:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_8]){printf("Skip since 8 is not held down\n"); position+=2;}
+ break;
+ case 9:
+ if (!notKeyBoardState[SDL_SCANCODE_KP_9]){printf("Skip since 9 is not held down\n"); position+=2;}
+ break;
+ case 0xA:
+ if (!notKeyBoardState[SDL_SCANCODE_A]){printf("Skip since A is not held down\n"); position+=2;}
+ break;
+ case 0xB:
+ if (!notKeyBoardState[SDL_SCANCODE_S]){printf("Skip since B is not held down\n"); position+=2;}
+ break;
+ case 0xC:
+ if (!notKeyBoardState[SDL_SCANCODE_D]){printf("Skip since C is not held down\n"); position+=2;}
+ break;
+ case 0xD:
+ if (!notKeyBoardState[SDL_SCANCODE_Z]){printf("Skip since D is not held down\n"); position+=2;}
+ break;
+ case 0xE:
+ if (!notKeyBoardState[SDL_SCANCODE_X]){printf("Skip since E is not held down\n"); position+=2;}
+ break;
+ case 0xF:
+ if (!notKeyBoardState[SDL_SCANCODE_C]){printf("Skip since F is not held down\n"); position+=2;}
+ break;
+ default:
+ fprintf(stderr, "Value in VX is invalid\n");
+ printf("Value is: %X\n", V[x]);
+ exit(1);
+ }
+ printf("E%XA1\n", x);
+ break;
+ default:
+ fprintf(stderr, "Illegal Instruction\n"); exit(1);
+ break;
+ }
+ break;
+ case 0xF://FX07 or FX0A or FX15 or FX18 or FX1E or FX29 or FX33 or FX55 or FX65
+ switch (opcode[1])
+ {
+ case 0x07://FX07
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ V[x] = delayTimer; //set VX to the value of the delay timer
+ printf("V%X = get_delay(%d)\n", x, delayTimer);
+ break;
+ case 0x0A://FX0A
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //A key press is awaited, checked to see if valid and then stored in VX. (Blocking Operation)
+ char c;
+ anotherChar: if ((c = getchar()) == EOF){free(program); exit(0);}
+ if (!((c>='0') && (c <='9'))){goto anotherChar;}//get another character if input is invalid
+ V[x] = c % 10;
+ printf("F%X0A\n", x);
+ break;
+ case 0x15://FX15
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ delayTimer = V[x];//set delayTimer to whatever is in the register
+ printf("delay_timer(%X)\n", V[x]);
+// printf("F%X15\n", x);
+ break;
+ case 0x18://FX18
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //The sound timer can be left unimplemeted
+ printf("sound_timer(%X)\n", V[x]);//set the sound timer to VX
+ printf("F%X18\n", x);
+ break;
+ case 0x1E://FX1E
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ I += V[x];//Adds VX to I.
+ // VF is set to 1 when there is a range overflow (I+VX>0xFFF), and to 0 when there isn't.
+ V[0xF] = (*(I)+V[x])>0xFFF;
+ break;
+ case 0x29://FX29
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //Sets I to the location of the sprite for the character in VX. Characters 0-F (in hexadecimal) are represented by a 4x5 font.
+ //while the characters would usually be stored in the interpretor, we just store them elsewhere
+// printf("x value: %d\n", x); exit(1);
+ switch (V[x])
+ {
+ case 0:
+ I=zero;
+ break;
+ case 1:
+ I=one;
+ break;
+ case 2:
+ I=two;
+ break;
+ case 3:
+ I=three;
+ break;
+ case 4:
+ I=four;
+ break;
+ case 5:
+ I=five;
+ break;
+ case 6:
+ I=six;
+ break;
+ case 7:
+ I=seven;
+ break;
+ case 8:
+ I=eight;
+ break;
+ case 9:
+ I=nine;
+ break;
+ case 0xA:
+ I=A;
+ break;
+ case 0xB:
+ I=B;
+ break;
+ case 0xC:
+ I=C;
+ break;
+ case 0xD:
+ I=D;
+ break;
+ case 0xE:
+ I=E;
+ break;
+ case 0xF:
+ I=F;
+ break;
+ default:
+ printf("sprite_address(x), x out of bounds\n");
+ exit(1);
+ break;
+ }
+ printf("F%X29\n", x);
+ break;
+ case 0x33://FX33
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //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.)
+ printf("set_BCD(%X)\n", V[x]);
+ *I = V[x] / 100;
+ *(I+1) = (V[x] / 10) % 10;
+ *(I+2) = (V[x] % 100) % 10;
+ printf("F%X33\n", x);
+ break;
+ case 0x55://FX55
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //Stores V0 to VX (including VX) in memory starting at address I. The offset from I is increased by 1 for each value written, but I itself is left unmodified.
+ for (int i=0; i<=x; ++i)
+ {
+ *(I+i) = V[i];
+ }
+ printf("reg_dump(%X)\n", V[x]);
+ break;
+ case 0x65://FX65
+ //the lower 4 bits contain (V)x
+ x = lowerFour(opcode);
+ //Fills V0 to VX (including VX) with values from memory starting at address I. The offset from I is increased by 1 for each value written, but I itself is left unmodified.
+ for (int i=0; i<=x; ++i)//testy
+ {
+ V[i] = *(I+i);
+ }
+ printf("reg_load(%X)\n", V[x]);//placeholder
+ break;
+ default:
+ fprintf(stderr, "Illegal Instruction\n"); exit(1);
+ break;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "Illegal Instruction\n"); exit(1);
+ break;
+ }
+
+printf("Position in program: %ld, opcode:%x%x\n", I-program,opcode[0],opcode[1]);
+
+if (render)//only render the frame if draw() or clear() was just called
+{
+//unlock texture and copy it
+SDL_UnlockTexture(texture);
+SDL_RenderCopy(renderer, texture, NULL, NULL);
+
+//Since 1 frame in 60Hz is 16.67ms, which we can't get with a ms timer, 17 ms is used. As a result, frametime (should be) slightly slower than 60Hz.
+if (SDL_GetTicks() < (lastFrame+17)){SDL_Delay((lastFrame+17)-SDL_GetTicks());}//SDL_Delay may delay longer than the given value, but it seems to be reasonably accurate.
+
+SDL_RenderPresent(renderer);//render the frame
+lastFrame = SDL_GetTicks();//get the approximate time that the frame was rendered at
+render = false;//don't render until draw() or clear() is called
+}
+
+
+if (SDL_GetTicks() < (lastTick+2)){SDL_Delay((lastTick+2)-SDL_GetTicks());}//limit clockrate to ~500Hz (too fast for some games, the right speed for some too)
+if (delayTimer && (SDL_GetTicks()%16 == 0)||(SDL_GetTicks()%16 == 1) ){--delayTimer;}//deincrement the delay timer if nonzero (delay timer is meant to count down at 60Hz, % 16 on the total runtime ticks seems be roughly correct).
+
+lastTick = SDL_GetTicks();
+
+/*
+getchar();//step through each instruction
+printf("Step\n");
+*/
+/*
+for (int i=0; i<0xF+1; ++i)
+ {
+ printf("V%X: %d\t", i, V[i]);
+ }
+*/
+/*
+printf("Position: %d\t", position);
+printf("I: %ld", I-program);
+
+putchar('\n');
+*/
+
+//SDL_Delay(40);//want to watch the output slowly
+SDL_PollEvent(&event);
+if (event.type == SDL_QUIT){break;}//exit if the quit signal is given
+}
+
+SDL_DestroyRenderer(renderer);
+SDL_Quit();
+free(program);
+
+return 0;
+}