/* Copyright (C) 2021 Gentoo-libre Install
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include //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 \n", argv[0]); return 1;}
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;
}