From 84aa309a2952fa91f9096df9906ff6785a28336c Mon Sep 17 00:00:00 2001 From: Nicholas Orlowsky Date: Wed, 6 Sep 2023 02:10:31 -0400 Subject: [PATCH] add flag support and update README --- Makefile | 11 ++- README.md | 51 ++++++++++++- main.c | 146 +++++++++++++++++++++++++------------- screenshots/flag-test.png | Bin 0 -> 7142 bytes 4 files changed, 155 insertions(+), 53 deletions(-) create mode 100644 screenshots/flag-test.png diff --git a/Makefile b/Makefile index dc0d752..5c27225 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,16 @@ build: gcc ./main.c -g -lSDL2 -lGL run: build - ./a.out + ./a.out ${ROM_FILE} + +run-turbo: build + ./a.out ${ROM_FILE} turbo debug: build - gdb ./a.out + gdb ./a.out ${ROM_FILE} + +debug-turbo: build + gdb ./a.out ${ROM_FILE} turbo + format: clang-format ./*.c -i diff --git a/README.md b/README.md index a850cd1..758c5a8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,56 @@ # yacemu (Yet Another Chip Eight Emulator) -A Chip-8 emulator written in C. Depends on SDL2. +A Chip-8 emulator (interpereter) written in C. Depends on SDL2. + +### Building & Testing + +In order to run yacemu, you must have sdl2 and make installed. + +Once you have the dependencies installed, you can build the emulator with: +```shell +make build +``` + +You can build start the emulator with: +```shell +make run ROM_FILE=[PATH_TO_YOUR_ROM] +``` + +If you would like to run it in turbo mode (faster emulation, useful for testing) then run: +```shell +make run-turbo ROM_FILE=[PATH_TO_YOUR_ROM] +``` + +If you would like to debug with gdb then run: +```shell +make debug ROM_FILE=[PATH_TO_YOUR_ROM] +``` + +If you would like to debug with gdb in turbo mode then run: +```shell +make debug-turbo ROM_FILE=[PATH_TO_YOUR_ROM] +``` + +### Running + +If you have a binary, you can run it with the following: + +```shell +yacemu [PATH_TO_YOUR_ROM] +``` + +If you would like to run it in turbo mode (faster emulation, useful for testing) then run: + +```shell +yacemu [PATH_TO_YOUR_ROM] turbo +``` ### Todo - [x] Graphics - [x] Corax+ Required Instructions -- [ ] Rest of Instructions +- [x] Proper Flag Handling - [ ] Working Input -- [ ] Tetris Running +- [ ] Rest of Instructions +- [ ] Tetris Working Running - [ ] Extended instruction set (MEGACHIP, SUPER CHIP-48) - [ ] Add my own custom instructions - [ ] Write a ROM that uses the above instructions @@ -16,3 +60,4 @@ A Chip-8 emulator written in C. Depends on SDL2. ![Chip 8 Logo Demo](https://github.com/nickorlow/yacemu/blob/main/screenshots/chip8-logo.png?raw=true) ![IBM Logo Demo](https://github.com/nickorlow/yacemu/blob/main/screenshots/ibm-logo.png?raw=true) ![Corax Plus Test Demo](https://github.com/nickorlow/yacemu/blob/main/screenshots/corax+-test.png?raw=true) +![Flag Test Demo](https://github.com/nickorlow/yacemu/blob/main/screenshots/flag-test.png?raw=true) diff --git a/main.c b/main.c index b47284d..cf52554 100644 --- a/main.c +++ b/main.c @@ -1,14 +1,15 @@ +#include +#include +#include +#include #include #include #include -#include -#include -#include #define BASE_ADDR 0x200 #define FONTSET_SIZE 80 -#define SCREEN_WIDTH 64 -#define SCREEN_HEIGHT 32 +#define SCREEN_WIDTH 64 +#define SCREEN_HEIGHT 32 struct emu_state { uint8_t registers[16]; @@ -43,7 +44,6 @@ uint8_t fontset[FONTSET_SIZE] = { 0xF0, 0x80, 0xF0, 0x80, 0x80 // F }; - void load_rom(char *file_name, struct emu_state *emulator_state) { printf("Loading ROM %s\n", file_name); @@ -110,12 +110,12 @@ void instr_CALL(struct emu_state *emulator_state) { // the value in register X is equal to byte KK void instr_SE(struct emu_state *emulator_state) { uint8_t reg = (emulator_state->opcode & 0x0F00U) >> 8; - uint8_t register_val = - emulator_state->registers[reg]; + uint8_t register_val = emulator_state->registers[reg]; uint8_t byte_val = emulator_state->opcode & 0x00FFU; #ifndef NDEBUG - printf("SE R[%#x] %d (reg_val: %d, skip?: %d)\n", reg, byte_val, register_val, register_val == byte_val); + printf("SE R[%#x] %d (reg_val: %d, skip?: %d)\n", reg, byte_val, register_val, + register_val == byte_val); #endif if (byte_val == register_val) { @@ -177,7 +177,8 @@ void instr_ADD(struct emu_state *emulator_state) { uint8_t num = emulator_state->opcode & 0x00FFU; #ifndef NDEBUG - printf("ADD R[%#x] %d (was: %d, now: %d)\n", reg, num, emulator_state->registers[reg], emulator_state->registers[reg] + num); + printf("ADD R[%#x] %d (was: %d, now: %d)\n", reg, num, + emulator_state->registers[reg], emulator_state->registers[reg] + num); #endif emulator_state->registers[reg] += num; @@ -207,50 +208,79 @@ void instr_XOR_reg(struct emu_state *emulator_state) { // ADD instruction adds the value of register X with register Y void instr_ADD_reg(struct emu_state *emulator_state) { + uint8_t prev_value = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] += emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + + emulator_state->registers[0xF] = + (prev_value > + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]) + ? 1 + : 0; + emulator_state->program_counter += 2; } // SUB instruction subtracts the value of register X with register Y void instr_SUB_reg(struct emu_state *emulator_state) { + uint8_t prev_value = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] -= emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + + // This doesn't exactly make sense, yet the tests pass.... + emulator_state->registers[0xF] = + (prev_value < + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]) + ? 0 + : 1; emulator_state->program_counter += 2; } -// SHR instruction bit shifts Vx one to the right +// SHR instruction bit shifts Vx one to the right void instr_SHR_reg(struct emu_state *emulator_state) { - uint8_t reg_x_val = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + uint8_t reg_x_val = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] >>= 1; + if ((reg_x_val & 0x01) == 1) { emulator_state->registers[0xF] = 1; } else { emulator_state->registers[0xF] = 0; } - emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] >>= 1; - emulator_state->program_counter += 2; } -// SHL instruction bit shifts Vx one to the left +// SHL instruction bit shifts Vx one to the left void instr_SHL_reg(struct emu_state *emulator_state) { - uint8_t reg_x_val = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; - if (((reg_x_val & 0xA0) >> 4) == 1) { + uint8_t reg_x_val = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] <<= 1; + + if (((reg_x_val & 0xA0) >> 7) == 1) { emulator_state->registers[0xF] = 1; } else { emulator_state->registers[0xF] = 0; } - emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] <<= 1; - emulator_state->program_counter += 2; } -// SUBN sets Vx to Vy - Vx +// SUBN sets Vx to Vy - Vx void instr_SUBN_reg(struct emu_state *emulator_state) { - uint8_t reg_x_val = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; - uint8_t reg_y_val = emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + uint8_t reg_x_val = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + uint8_t reg_y_val = + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = + reg_y_val - reg_x_val; if (reg_y_val > reg_x_val) { emulator_state->registers[0xF] = 1; @@ -258,8 +288,6 @@ void instr_SUBN_reg(struct emu_state *emulator_state) { emulator_state->registers[0xF] = 0; } - emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = reg_y_val - reg_x_val; - emulator_state->program_counter += 2; } @@ -271,7 +299,8 @@ void instr_LD_I(struct emu_state *emulator_state) { void instr_LD_reg_I(struct emu_state *emulator_state) { unsigned int reg = (emulator_state->opcode & 0x0F00U) >> 8; for (unsigned int i = 0; i <= reg; i++) { - emulator_state->registers[i] = emulator_state->memory[emulator_state->index + i]; + emulator_state->registers[i] = + emulator_state->memory[emulator_state->index + i]; } emulator_state->program_counter += 2; } @@ -279,18 +308,20 @@ void instr_LD_reg_I(struct emu_state *emulator_state) { void instr_LD_I_reg(struct emu_state *emulator_state) { unsigned int reg = (emulator_state->opcode & 0x0F00U) >> 8; for (unsigned int i = 0; i <= reg; i++) { - emulator_state->memory[emulator_state->index + i] = emulator_state->registers[i]; + emulator_state->memory[emulator_state->index + i] = + emulator_state->registers[i]; } emulator_state->program_counter += 2; } void instr_LD_B_reg(struct emu_state *emulator_state) { - unsigned int val = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; - emulator_state->memory[emulator_state->index + 2] = val % 10; + unsigned int val = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + emulator_state->memory[emulator_state->index + 2] = val % 10; val /= 10; emulator_state->memory[emulator_state->index + 1] = val % 10; val /= 10; - emulator_state->memory[emulator_state->index + 0] = val % 10; + emulator_state->memory[emulator_state->index + 0] = val % 10; emulator_state->program_counter += 2; } @@ -307,8 +338,10 @@ void instr_ADD_I_reg(struct emu_state *emulator_state) { } void instr_DRW(struct emu_state *emulator_state) { - uint8_t x_cord = emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; - uint8_t y_cord = emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + uint8_t x_cord = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + uint8_t y_cord = + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; uint8_t size = emulator_state->opcode & 0x000FU; y_cord %= SCREEN_HEIGHT; x_cord %= SCREEN_WIDTH; @@ -316,8 +349,10 @@ void instr_DRW(struct emu_state *emulator_state) { emulator_state->registers[0xF] = 0; for (unsigned int r = 0; r < size; r++) { for (unsigned int c = 0; c < 8; c++) { - uint32_t* screen_pixel = &emulator_state->screen[(r+y_cord) * SCREEN_WIDTH + (x_cord + c)]; - uint8_t sprite_pixel = emulator_state->memory[emulator_state->index + r] & (0x80U >> c); + uint32_t *screen_pixel = + &emulator_state->screen[(r + y_cord) * SCREEN_WIDTH + (x_cord + c)]; + uint8_t sprite_pixel = + emulator_state->memory[emulator_state->index + r] & (0x80U >> c); if (sprite_pixel) { if (*screen_pixel == 0xFFFFFFFF) { emulator_state->registers[0xF] = 1; @@ -398,21 +433,31 @@ int main(int argc, char *argv[]) { memset(state.screen, 0, sizeof(state.screen)); SDL_Init(SDL_INIT_EVERYTHING); - SDL_Window* window = SDL_CreateWindow("Chip-8 Emulator", // creates a window - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - SCREEN_WIDTH * 10, SCREEN_HEIGHT * 10, 0); - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + SDL_Window *window = + SDL_CreateWindow("Chip-8 Emulator", // creates a window + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + SCREEN_WIDTH * 10, SCREEN_HEIGHT * 10, 0); + SDL_Renderer *renderer = + SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - SDL_Texture* texture = SDL_CreateTexture( - renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); + SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_STREAMING, + SCREEN_WIDTH, SCREEN_HEIGHT); - for(int i = 0; i != -1; i++) { + int turbo_mode = argc == 3 && strcmp(argv[2], "turbo") == 0; + + if (turbo_mode) { + printf("WARNING: Turbo mode has been enabled. Interpereter will run " + "extremely fast!\n"); + } + + for (long i = 0; i != -1; i++) { state.opcode = (((uint16_t)state.memory[state.program_counter]) << 8) | (uint16_t)state.memory[state.program_counter + 1]; void (*op_func)(struct emu_state *) = get_op_func(state.opcode); - printf("\n[%d] | PC: %#x / OPCODE: %#x (%s) \n", i, state.program_counter, state.opcode, get_instr_name(op_func)); + printf("\n[%d] | PC: %#x / OPCODE: %#x \n", i, state.program_counter, + state.opcode); if (op_func == NULL) { printf("Illegal Instruction.\n"); @@ -421,14 +466,19 @@ int main(int argc, char *argv[]) { op_func(&state); - SDL_UpdateTexture(texture, NULL, state.screen, sizeof(state.screen[0]) * SCREEN_WIDTH); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); + SDL_UpdateTexture(texture, NULL, state.screen, + sizeof(state.screen[0]) * SCREEN_WIDTH); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); - usleep(16000); + usleep(turbo_mode ? 100 : 16000); + + if (SDL_QuitRequested()) { + printf("Received Quit from SDL. Goodbye!"); + break; + } } - while (1 == 1) {} return 0; } diff --git a/screenshots/flag-test.png b/screenshots/flag-test.png new file mode 100644 index 0000000000000000000000000000000000000000..12cb10b803aa2c2990e7e480f4b699389627b7e5 GIT binary patch literal 7142 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 literal 0 HcmV?d00001