/* 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; }