123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- #include <stdint.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <time.h>
- #include <SDL2/SDL.h>
-
- #define DEBUG 1
- #define TDISP 0
-
- typedef uint8_t byte;
- typedef uint16_t word;
-
- typedef struct {
- byte memory[0x1000];
- byte screen[64][32];
- byte registers[0x10];
- word stack[24];
- byte input[0x10];
-
- word address_I;
- word program_counter;
- byte stack_pointer;
- } emulator_state_s;
-
- byte keymap[0x10] = { SDL_SCANCODE_X, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_Q,
- SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D,
- SDL_SCANCODE_Z, SDL_SCANCODE_C, SDL_SCANCODE_4, SDL_SCANCODE_R, SDL_SCANCODE_F, SDL_SCANCODE_V };
-
- int main(int argc, char **argv) {
- SDL_Window *window = NULL;
- SDL_Renderer *renderer = NULL;
- SDL_Init(SDL_INIT_VIDEO);
-
- window = SDL_CreateWindow("Chip8 Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 64 * 8, 32 * 8, SDL_WINDOW_SHOWN);
- renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED);
- if (renderer == NULL) printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
- SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
-
- srand(time(NULL));
-
- emulator_state_s state = { 0 }; // initialize everything to 0
- state.program_counter = 0x200; // chip8 programs start at 0x200 address space
-
- FILE *f = fopen(argv[1], "rb");
- fread(&state.memory[0x200], 0x1000 - 0x200, 1, f); // see above comment
- fclose(f);
-
- SDL_Event e;
-
- int delay = 0;
- int sound = 0;
- int frame = 0;
- int press = 0;
- int wait = 0;
-
- while (1) {
- if (DEBUG) printf("0x%x ", state.program_counter);
-
- if (frame == 10)
- {
- frame = 0;
- if (delay > 0) --delay;
- if (sound > 0) --sound;
- memset(state.input, 0, 0x10);
- press = 0;
- }
-
- clock_t start = clock() / (CLOCKS_PER_SEC/1000);
-
- // opcodes are 16 bits
- word opcode = (state.memory[state.program_counter++] << 8) | (state.memory[state.program_counter++]);
-
- SDL_PollEvent(&e);
- if (e.type == SDL_QUIT) break;
-
- // SDL_PumpEvents();
- const byte *keystate = SDL_GetKeyboardState(NULL);
-
- for (int i = 0; i < 0x10 && !press; ++i) {
- if (keystate[keymap[i]]){
- state.input[i] = 1;
- press = 1;
- }
-
- }
-
- if (wait && !press) continue;
- if (wait && press)
- {
- for (int i = 0; i < 0x10; ++i) {
- if (state.input[i] != 0)
- {
- state.registers[0xF] = i;
- break;
- }
- }
- wait = 0;
- }
-
- switch (opcode & 0xF000)
- {
- case 0x0000: // screen clear or function return
- if (opcode == 0x00E0){
- memset(state.screen, 0, 64 * 32);
- SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
- SDL_RenderClear(renderer);
- if (DEBUG) printf("Clearing screen ");
- }
-
- else if (opcode = 0x00EE) {
- state.program_counter = state.stack[--state.stack_pointer];
- if (DEBUG) printf("Returning to %x ", state.stack[state.stack_pointer]);
- }
-
- break;
-
- case 0x1000: // goto statement
- state.program_counter = opcode & 0x0FFF;
- if (DEBUG) printf("Jumping to %x ", state.program_counter);
-
- break;
-
- case 0x2000: // CALL
- state.stack[state.stack_pointer++] = state.program_counter;
- state.program_counter = opcode & 0x0FFF;
- if (DEBUG) printf("Calling subroutine at %x (returning to %x)", state.program_counter, state.stack[state.stack_pointer - 1]);
-
- break;
-
- case 0x3000:; // skip if reg 0x0F00 is equal to constant 0x00FF
- int reg = (opcode & 0x0F00) >> 8;
- if (DEBUG) printf("Comparing (==) register %i (%x) to %x ", reg, state.registers[reg], (byte) opcode);
- if (state.registers[reg] == (byte) opcode){
- state.program_counter += 2;
- if (DEBUG) printf("and incrementing program_counter ");
- }
-
- break;
-
- case 0x4000: // skip if not equal
- reg = (opcode & 0x0F00) >> 8;
- if (DEBUG) printf("Comparing (!=) register %i (%x) to %x ", reg, state.registers[reg], (byte) opcode);
- if (state.registers[reg] != (byte) opcode){
- state.program_counter += 2;
- if (DEBUG) printf("and incrementing program_counter ");
- }
-
- break;
-
- case 0x5000:; // skip if reg 0x0F00 is equal to 0x00F0
- int first = (opcode & 0x0F00) >> 8;
- int second = (opcode & 0x00F0) >> 4;
- if (DEBUG) printf("Comparing (==) register %i (%x) to %i (%x) ", first, state.registers[first], second, state.registers[second]);
- if (state.registers[first] == state.registers[second]){
- state.program_counter += 2;
- if (DEBUG) printf("and incrementing program_counter ");
- }
-
- break;
-
- case 0x6000: // set reg to constant
- reg = (opcode & 0x0F00) >> 8;
- state.registers[reg] = opcode & 0x00FF;
- if (DEBUG) printf("Assigning %hx to %i", state.registers[reg], reg);
-
- break;
-
- case 0x7000: // add constant to reg
- reg = (opcode & 0x0F00) >> 8;
- state.registers[reg] += opcode & 0x00FF;
- if (DEBUG) printf("Add %hi to reg %i (%hi)", opcode & 0x00FF, reg, state.registers[reg]);
-
- break;
-
- case 0x8000:; // math/bitop operators
- first = (opcode & 0x0F00) >> 8; // all use XY regs
- second = (opcode & 0x00F0) >> 4;
-
- switch (opcode & 0x000F)
- {
- case 0: // copy 1 register to another
- state.registers[first] = state.registers[second];
- if (DEBUG) printf("Copied register %i to %i (%hx)", first, second, state.registers[first]);
- break;
-
- case 1: // X = X | Y
- state.registers[first] |= state.registers[second];
- if (DEBUG) printf("OR register %i and %i (%hx)", first, second, state.registers[first]);
- break;
-
- case 2: // X = X & Y
- state.registers[first] &= state.registers[second];
- if (DEBUG) printf("AND register %i and %i (%hx)", first, second, state.registers[first]);
- break;
-
- case 3: // X = X ^ Y
- state.registers[first] ^= state.registers[second];
- if (DEBUG) printf("XOR register %i to %i (%hx)", first, second, state.registers[first]);
- break;
-
- case 4:; // X = X + Y, VF = X + 5 > 255
- unsigned int add = state.registers[first] + state.registers[second];
- if (add > 255) state.registers[0xF] = 1;
- else state.registers[0xF] = 0;
- state.registers[first] = add & 0x00FF;
- if (DEBUG) printf("ADD register %i to %i (%hx) ", second, first, state.registers[first]);
- if (DEBUG) printf("VF is %hx", state.registers[0xF]);
- break;
-
- case 5: // X = X - Y, VF = X - Y < 0
- state.registers[first] -= state.registers[second];
- if (state.registers[first] > state.registers[second]) state.registers[0xF] = 1;
- else state.registers[0xF] = 0;
- if (DEBUG) printf("SUB register %i from %i (%hx) ", second, first, state.registers[first]);
- if (DEBUG) printf("VF is %hx", state.registers[0xF]);
- break;
-
- case 6: // set least significant bit of X in VF, bitshift right
- state.registers[0xF] = state.registers[first] & 0x000F;
- state.registers[first] /= 2;
- if (DEBUG) printf("RS register %i (%hx) ", first, second, state.registers[first]);
- if (DEBUG) printf("VF is %hx", state.registers[0xF]);
- break;
-
- case 7: // X = Y - X, VF = Y - X < 0
- state.registers[first] = state.registers[second] - state.registers[first];
- if (state.registers[second] > state.registers[first]) state.registers[0xF] = 1;
- else state.registers[0xF] = 0;
- if (DEBUG) printf("SUB %i from %i (%hx) ", first, second, state.registers[first]);
- if (DEBUG) printf("VF is %hx", state.registers[0xF]);
- break;
-
- case 0xE:; // set msot significant bit of X in VF, bitshift left
- state.registers[0xF] = (state.registers[first] & 0xF000) >> 12;
- state.registers[first] *= 2;
- if (DEBUG) printf("LS register %i (%hx) ", first, second, state.registers[first]);
- if (DEBUG) printf("VF is %hx", state.registers[0xF]);
- break;
-
- default: break;
-
- } break;
-
- case 0x9000: // skip if two regs are not equal
- first = (opcode & 0x0F00) >> 8;
- second = (opcode & 0x00F0) >> 4;
- if (DEBUG) printf("Comparing (!=) register %i (%hx) to %i (%hx) ", first, state.registers[first], second, state.registers[second]);
- if (state.registers[first] != state.registers[second]) {
- state.program_counter += 2;
- if (DEBUG) printf("and incrementing program_counter");
- }
- break;
-
- case 0xA000: // assign I address
- state.address_I = opcode & 0x0FFF;
- if (DEBUG) printf("Assigning %x to I address ", opcode & 0x0FFF);
-
- break;
-
- case 0xB000: // jump to 0x0FFF + V0
- state.program_counter = (opcode & 0x0FFF) + state.registers[0x0];
- if (DEBUG) printf("Jumping to %x + %hx (%x)", opcode & 0x0FFF, state.registers[0], (opcode & 0x0FFF) + state.registers[0x0]);
-
- break;
-
- case 0xC000: // random generator
- reg = (opcode & 0x0F00) >> 8;
- state.registers[reg] = (rand() % 255) & ((byte) opcode);
- if (DEBUG) printf("Storing random in %i (%x)\n", reg, state.registers[reg]);
-
- break;
-
- case 0xD000: // draw sprite
- first = (opcode & 0x0F00) >> 8;
- second = (opcode & 0x00F0) >> 4;
-
- int x = state.registers[first];
- int y = state.registers[second];
- int h = (opcode & 0x000F);
-
- for (int line = 0; line < h; ++line)
- {
- byte data = state.memory[state.address_I + line];
- for (int xpos = 0; xpos < 8; ++xpos)
- {
- if (!(data & (1 << xpos))) continue;
- if (state.screen[x + (7 - xpos)][y + line]) state.registers[0xF] = 1;
- state.screen[x + (7 - xpos)][y + line] ^= 1;
- }
- }
-
- if (DEBUG) printf("Drawing");
- if (!TDISP) break;
- printf("\n");
- for (int y = 0; y < 32; ++y) {
- for (int x = 0; x < 64; ++x) {
- printf(state.screen[x][y] ? "O" : " ");
- }
- printf("\n");
- }
-
- break;
-
- case 0xE000: // input handling
- reg = (opcode & 0x0F00) >> 8;
- switch ((byte) opcode)
- {
- case 0x9E: // skip if button pressed
- if (DEBUG) printf("Testing if %i is pressed", state.registers[reg]);
- if (state.input[state.registers[reg]]) state.program_counter += 2;
- break;
-
- case 0xA1: // skip if button *not* pressed
- if (DEBUG) printf("Testing if %i is not pressed", state.registers[reg]);
- if (!state.input[state.registers[reg]]) state.program_counter += 2;
- break;
- }
- break;
-
- case 0xF000: // misc (timer, input, sound, memory)
- reg = (opcode & 0x0F00) >> 8;
- switch ((byte) opcode)
- {
- case 0x07:
- if (DEBUG) printf("Copying delay timer to reg %i (%hx)", reg, state.registers[reg]);
- state.registers[reg] = delay;
- break;
-
- case 0x0A:
- wait = 1;
- break;
-
- case 0x15:
- delay = state.registers[reg];
- break;
-
- case 0x18:
- sound = state.registers[reg];
- break;
-
- case 0x1E:
- state.address_I += state.registers[reg];
- if (DEBUG) printf("Adding reg %i (%hx) to I (%x)", reg, state.registers[reg], state.address_I);
- break;
-
- case 0x29:
- break; // fontset not implemented yet
-
- case 0x33:;
- int value = state.registers[reg];
- state.memory[state.address_I] = value / 100;
- state.memory[state.address_I + 1] = (value / 10) % 10;
- state.memory[state.address_I + 2] = value % 10;
- break;
-
- case 0x55:
- for (int i = 0; i < (reg + 1); ++i)
- state.memory[state.address_I + i] = state.registers[i];
- break;
-
- case 0x65:
- for (int i = 0; i < (reg + 1); ++i)
- state.registers[i] = state.memory[state.address_I + i];
- break;
-
- default:
- break;
- }
- break;
- default:
- if (DEBUG) printf("%x", opcode);
- break;
- }
- if (DEBUG) printf("\n");
-
- for (int x = 0; x < 64; ++x) {
- for (int y = 0; y < 32; ++y)
- {
- SDL_Rect pixel = { .x = (x * 8), .y = (y * 8), .w = 8, .h = 8};
- if (state.screen[x][y])
- {
- SDL_SetRenderDrawColor(renderer, 0xff, 0x69, 0xb4, 0xFF);
- SDL_RenderFillRect(renderer, &pixel);
- }
- else
- {
- SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
- SDL_RenderFillRect(renderer, &pixel);
- }
- }
- }
-
- SDL_RenderPresent(renderer);
-
- clock_t stop = clock() / (CLOCKS_PER_SEC/1000);
- SDL_Delay(1);
- ++frame;
- // SDL_DestroyWindow(window);
- // SDL_Quit();
- }
- }
|