From 43a638ff84cc1264e5edbb75aee87e18f72f86f6 Mon Sep 17 00:00:00 2001 From: Nicholas Orlowsky Date: Thu, 31 Aug 2023 18:17:53 -0400 Subject: [PATCH] passes corax+ --- Makefile | 4 +- README.md | 7 ++ main.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 264 insertions(+), 21 deletions(-) create mode 100644 README.md diff --git a/Makefile b/Makefile index 180bf9f..dc0d752 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ build: - gcc ./main.c + gcc ./main.c -g -lSDL2 -lGL run: build ./a.out +debug: build + gdb ./a.out format: clang-format ./*.c -i diff --git a/README.md b/README.md new file mode 100644 index 0000000..18845eb --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# chip8-emu +A Chip-8 emulator written in C + + +### Todo +- [ ] + diff --git a/main.c b/main.c index 23954d8..f5f03e2 100644 --- a/main.c +++ b/main.c @@ -1,9 +1,14 @@ #include #include #include +#include +#include +#include #define BASE_ADDR 0x200 #define FONTSET_SIZE 80 +#define SCREEN_WIDTH 64 +#define SCREEN_HEIGHT 32 struct emu_state { uint8_t registers[16]; @@ -15,7 +20,7 @@ struct emu_state { uint8_t sound_timer; uint8_t delay_timer; uint8_t keypad[16]; - uint32_t screen[2048]; + uint32_t screen[SCREEN_WIDTH * SCREEN_HEIGHT]; uint16_t opcode; }; @@ -38,6 +43,7 @@ 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); @@ -103,9 +109,14 @@ void instr_CALL(struct emu_state *emulator_state) { // SE instruction skips the next instruction if // 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[emulator_state->opcode & 0x0F00U >> 8]; + emulator_state->registers[reg]; uint8_t byte_val = emulator_state->opcode & 0x00FFU; + + printf("SE R[%#x] %d (reg_val: %d, skip?: %d)\n", reg, byte_val, register_val, register_val == byte_val); + + if (byte_val == register_val) { emulator_state->program_counter += 2; } @@ -116,7 +127,7 @@ void instr_SE(struct emu_state *emulator_state) { // the value in register X is not equal to byte KK void instr_SNE(struct emu_state *emulator_state) { uint8_t register_val = - emulator_state->registers[emulator_state->opcode & 0x0F00U >> 8]; + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; uint8_t byte_val = emulator_state->opcode & 0x00FFU; if (byte_val != register_val) { emulator_state->program_counter += 2; @@ -129,33 +140,123 @@ void instr_SNE(struct emu_state *emulator_state) { // register Y void instr_SE_reg(struct emu_state *emulator_state) { uint8_t register_x_val = - emulator_state->registers[emulator_state->opcode & 0x0F00U >> 8]; + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; uint8_t register_y_val = - emulator_state->registers[emulator_state->opcode & 0x00F0U >> 4]; + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; if (register_x_val == register_y_val) { emulator_state->program_counter += 2; } emulator_state->program_counter += 2; } +// SNE instruction skips the next instruction if +// the value in register X is equal to value in +// register Y +void instr_SNE_reg(struct emu_state *emulator_state) { + uint8_t register_x_val = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; + uint8_t register_y_val = + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + if (register_x_val != register_y_val) { + emulator_state->program_counter += 2; + } + emulator_state->program_counter += 2; +} + // LD instruction loads the value of byte kk into register Y void instr_LD(struct emu_state *emulator_state) { - emulator_state->registers[emulator_state->opcode & 0x0F00U] = + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = emulator_state->opcode & 0x00FFU; emulator_state->program_counter += 2; } // ADD instruction adds kk to register X void instr_ADD(struct emu_state *emulator_state) { - emulator_state->registers[emulator_state->opcode & 0x0F00U >> 8] += - emulator_state->opcode & 0x00FFU; + uint8_t reg = (emulator_state->opcode & 0x0F00U) >> 8; + uint8_t num = emulator_state->opcode & 0x00FFU; + + printf("ADD R[%#x] %d (was: %d, now: %d)\n", reg, num, emulator_state->registers[reg], emulator_state->registers[reg] + num); + + emulator_state->registers[reg] += num; emulator_state->program_counter += 2; } // LD instruction loads the value of register X into register Y void instr_LD_reg(struct emu_state *emulator_state) { - emulator_state->registers[emulator_state->opcode & 0x0F00U >> 8] = - emulator_state->registers[emulator_state->opcode & 0x00F0U >> 4]; + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + emulator_state->program_counter += 2; +} + +// OR instruction ors the value of register X with register Y +void instr_OR_reg(struct emu_state *emulator_state) { + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] |= + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + emulator_state->program_counter += 2; +} + +// XOR instruction xors the value of register X with register Y +void instr_XOR_reg(struct emu_state *emulator_state) { + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] ^= + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + emulator_state->program_counter += 2; +} + +// ADD instruction adds the value of register X with register Y +void instr_ADD_reg(struct emu_state *emulator_state) { + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] += + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + 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) { + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] -= + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + emulator_state->program_counter += 2; +} + +// 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]; + 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 +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) { + 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 +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]; + + if (reg_y_val > reg_x_val) { + emulator_state->registers[0xF] = 1; + } else { + emulator_state->registers[0xF] = 0; + } + + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] = reg_y_val - reg_x_val; + emulator_state->program_counter += 2; } @@ -164,15 +265,65 @@ void instr_LD_I(struct emu_state *emulator_state) { emulator_state->program_counter += 2; } +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->program_counter += 2; +} + +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->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; + val /= 10; + emulator_state->memory[emulator_state->index + 1] = val % 10; + val /= 10; + emulator_state->memory[emulator_state->index + 0] = val % 10; + emulator_state->program_counter += 2; +} + void instr_AND_reg(struct emu_state *emulator_state) { - emulator_state->registers[emulator_state->opcode & 0x0F00U >> 8] &= - emulator_state->registers[emulator_state->opcode & 0x00F0U >> 4]; + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8] &= + emulator_state->registers[(emulator_state->opcode & 0x00F0U) >> 4]; + emulator_state->program_counter += 2; +} + +void instr_ADD_I_reg(struct emu_state *emulator_state) { + emulator_state->index += + emulator_state->registers[(emulator_state->opcode & 0x0F00U) >> 8]; emulator_state->program_counter += 2; } void instr_DRW(struct emu_state *emulator_state) { - emulator_state->registers[emulator_state->opcode & 0x0F00U >> 8] &= - 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; + + 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); + if (sprite_pixel) { + if (*screen_pixel == 0xFFFFFFFF) { + emulator_state->registers[0xF] = 1; + } + *screen_pixel ^= 0xFFFFFFFF; + } + } + } + emulator_state->program_counter += 2; } @@ -187,6 +338,40 @@ void *get_op_func(uint16_t opcode) { return &instr_JP; } else if ((opcode & 0xF000U) == 0x2000U) { return &instr_CALL; + } else if ((opcode & 0xF000U) == 0x3000U) { + return &instr_SE; + } else if ((opcode & 0xF000U) == 0x4000U) { + return &instr_SNE; + } else if ((opcode & 0xF00FU) == 0x5000U) { + return &instr_SE_reg; + } else if ((opcode & 0xF00FU) == 0x9000U) { + return &instr_SNE_reg; + } else if ((opcode & 0xF00FU) == 0x8000U) { + return &instr_LD_reg; + } else if ((opcode & 0xF00FU) == 0x8001U) { + return &instr_OR_reg; + } else if ((opcode & 0xF00FU) == 0x8002U) { + return &instr_AND_reg; + } else if ((opcode & 0xF00FU) == 0x8003U) { + return &instr_XOR_reg; + } else if ((opcode & 0xF00FU) == 0x8004U) { + return &instr_ADD_reg; + } else if ((opcode & 0xF00FU) == 0x8005U) { + return &instr_SUB_reg; + } else if ((opcode & 0xF00FU) == 0x8006U) { + return &instr_SHR_reg; + } else if ((opcode & 0xF00FU) == 0x8007U) { + return &instr_SUBN_reg; + } else if ((opcode & 0xF00FU) == 0x800EU) { + return &instr_SHL_reg; + } else if ((opcode & 0xF0FFU) == 0xf065U) { + return &instr_LD_reg_I; + } else if ((opcode & 0xF0FFU) == 0xf033U) { + return &instr_LD_B_reg; + } else if ((opcode & 0xF0FFU) == 0xf055U) { + return &instr_LD_I_reg; + } else if ((opcode & 0xF0FFU) == 0xf01EU) { + return &instr_ADD_I_reg; } else if ((opcode & 0xF000U) == 0xA000U) { return &instr_LD_I; } else if ((opcode & 0xF000U) == 0x6000U) { @@ -202,22 +387,71 @@ void *get_op_func(uint16_t opcode) { return NULL; } -int main() { - struct emu_state state; - load_rom("tetris.ch8", &state); - state.program_counter = BASE_ADDR; +char* get_instr_name(void* function) { + if (function == &instr_CLS) { + return "CLS"; + } else if (function == &instr_RET) { + return "RET"; + } else if (function == &instr_JP) { + return "JP"; + } else if (function == &instr_CALL) { + return "CALL"; + } else if (function == &instr_SE) { + return "SE"; + } else if (function == &instr_LD_I) { + return "LD_I"; + } else if (function == &instr_LD) { + return "LD"; + } else if (function == &instr_ADD) { + return "ADD"; + } else if (function == &instr_DRW) { + return "DRW"; + } else if (function == &instr_AND_reg) { + return "AND_reg"; + } else { + return "UNKNOWN"; // Handle unknown instructions + } +} - while (1 == 1) { + +int main(int argc, char *argv[]) { + struct emu_state state; + load_rom(argv[1], &state); + state.program_counter = BASE_ADDR; + 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_Texture* texture = SDL_CreateTexture( + renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); + + for(int i = 0; i != -1; i++) { state.opcode = (((uint16_t)state.memory[state.program_counter]) << 8) | (uint16_t)state.memory[state.program_counter + 1]; - printf("PC: %#x / OPCODE: %#x\n", state.program_counter, state.opcode); void (*op_func)(struct emu_state *) = get_op_func(state.opcode); + + printf("\n[%d] | PC: %#x / OPCODE: %#x (%s) - ", i, state.program_counter, state.opcode, get_instr_name(op_func)); + if (op_func == NULL) { printf("Illegal Instruction.\n"); return 1; } 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); + + usleep(16000); } + while (1 == 1) {} return 0; }