From 9a6dc67c3e69b34ad01631c492fa14924dcea41c Mon Sep 17 00:00:00 2001 From: Samuel Walker Date: Fri, 31 Jan 2025 14:39:38 -0700 Subject: [PATCH] Finished CPU instructions and created ui window. --- include/cpu.h | 9 ++++- include/emu.h | 1 + include/interrupts.h | 16 +++++++++ include/ui.h | 9 +++++ lib/bus.c | 11 +++--- lib/cpu.c | 17 ++++++++++ lib/cpu_proc.c | 72 +++++++++++++++++++++++++++++++++++++-- lib/cpu_util.c | 8 +++++ lib/emu.c | 81 +++++++++++++++++++++++++++++++------------- lib/interrupts.c | 34 +++++++++++++++++++ lib/ui.c | 32 +++++++++++++++++ tests/CMakeLists.txt | 8 ++++- 12 files changed, 265 insertions(+), 33 deletions(-) create mode 100644 include/interrupts.h create mode 100644 include/ui.h create mode 100644 lib/interrupts.c create mode 100644 lib/ui.c diff --git a/include/cpu.h b/include/cpu.h index 984ebce..99d5997 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -30,7 +30,9 @@ typedef struct { bool stepping; bool int_master_enabled; + bool enabling_ime; u8 ie_register; + u8 int_flags; } cpu_context; void cpu_init(); @@ -41,6 +43,8 @@ typedef void (*IN_PROC)(cpu_context *); IN_PROC inst_get_processor(in_type type); #define CPU_FLAG_Z BIT(ctx->regs.f, 7) +#define CPU_FLAG_N BIT(ctx->regs.f, 6) +#define CPU_FLAG_H BIT(ctx->regs.f, 5) #define CPU_FLAG_C BIT(ctx->regs.f, 4) u16 cpu_read_reg(reg_type rt); @@ -56,4 +60,7 @@ void fetch_data(); u8 cpu_get_ie_register(); void cpu_set_ie_register(u8 ie); -cpu_registers *cpu_get_regs(); \ No newline at end of file +cpu_registers *cpu_get_regs(); + +u8 cpu_get_int_flags(); +void cpu_set_int_flags(u8 value); \ No newline at end of file diff --git a/include/emu.h b/include/emu.h index af0d9b9..10eddb7 100644 --- a/include/emu.h +++ b/include/emu.h @@ -5,6 +5,7 @@ typedef struct { bool paused; bool running; + bool die; u64 ticks; } emu_context; diff --git a/include/interrupts.h b/include/interrupts.h new file mode 100644 index 0000000..59b6bd9 --- /dev/null +++ b/include/interrupts.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +typedef enum { + IT_VBLANK = 1, + IT_LCD_STAT = 2, + IT_TIMER = 4, + IT_SERIAL = 8, + IT_JOYPAD = 16 +} interrupt_type; + +void cpu_request_interrupt(interrupt_type t); + +void cpu_handle_interrupts(cpu_context *ctx); \ No newline at end of file diff --git a/include/ui.h b/include/ui.h new file mode 100644 index 0000000..1272c08 --- /dev/null +++ b/include/ui.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +static const int SCREEN_WIDTH = 1024; +static const int SCREEN_HEIGHT = 768; + +void ui_init(); +void ui_handle_events(); \ No newline at end of file diff --git a/lib/bus.c b/lib/bus.c index a290bc7..e569e71 100644 --- a/lib/bus.c +++ b/lib/bus.c @@ -25,7 +25,8 @@ u8 bus_read(u16 address) { //Char/Map Data //TODO printf("UNSUPPORTED bus_read(%04X)\n", address); - NO_IMPL + //NO_IMPL + return 0; } else if (address < 0xC000) { //Cartridge RAM return cart_read(address); @@ -39,7 +40,8 @@ u8 bus_read(u16 address) { //OAM //TODO printf("UNSUPPORTED bus_read(%04X)\n", address); - NO_IMPL + //NO_IMPL + return 0; } else if (address < 0xFF00) { //reserved unusable return 0; @@ -48,6 +50,7 @@ u8 bus_read(u16 address) { //TODO printf("UNSUPPORTED bus_read(%04X)\n", address); //NO_IMPL + return 0; } else if (address == 0xFFFF) { //CPU ENABLE REGISTER //TODO @@ -65,7 +68,7 @@ void bus_write(u16 address, u8 value) { //Char/Map Data //TODO printf("UNSUPPORTED bus_write(%04X)\n", address); - NO_IMPL + //NO_IMPL } else if (address < 0xC000) { //Cartridge RAM cart_write(address, value); @@ -81,7 +84,7 @@ void bus_write(u16 address, u8 value) { //OAM //TODO printf("UNSUPPORTED bus_write(%04X)\n", address); - NO_IMPL + //NO_IMPL } else if (address < 0xFF00) { //reserved unusable return; diff --git a/lib/cpu.c b/lib/cpu.c index f5768cd..09472f2 100644 --- a/lib/cpu.c +++ b/lib/cpu.c @@ -2,6 +2,7 @@ #include #include #include +#include cpu_context ctx = {0}; @@ -44,6 +45,22 @@ bool cpu_step() { exit(-7); } execute(); + } else { + //is halted + emu_cycles(1); + + if(ctx.int_flags) { + ctx.halted = false; + } + } + + if(ctx.int_master_enabled) { + cpu_handle_interrupts(&ctx); + ctx.enabling_ime = false; + } + + if(ctx.enabling_ime) { + ctx.int_master_enabled = true; } return true; diff --git a/lib/cpu_proc.c b/lib/cpu_proc.c index 92e2dae..558044b 100644 --- a/lib/cpu_proc.c +++ b/lib/cpu_proc.c @@ -33,6 +33,46 @@ static void proc_nop(cpu_context *ctx) { } +static void proc_daa(cpu_context *ctx) { + u8 u = 0; + int fc = 0; + + if(CPU_FLAG_H || (!CPU_FLAG_N && (ctx->regs.a & 0xF) >9)) { + u = 6; + } + + if(CPU_FLAG_C || (!CPU_FLAG_N && ctx->regs.a > 0x99)) { + u |= 0x60; + fc = 1; + } + + ctx->regs.a += CPU_FLAG_N ? -u : u; + + cpu_set_flags(ctx, ctx->regs.a == 0, -1, 0, fc); +} + +static void proc_cpl(cpu_context *ctx) { + ctx->regs.a = -ctx->regs.a; + cpu_set_flags(ctx, -1, 1, 1, -1); +} + +static void proc_scf(cpu_context *ctx) { + cpu_set_flags(ctx, -1, 0, 0, 1); +} + +static void proc_ccf(cpu_context *ctx) { + cpu_set_flags(ctx, -1, 0, 0, CPU_FLAG_C ^ 1); +} + +static void proc_halt(cpu_context *ctx) { + ctx->halted = true; +} + +static void proc_stop(cpu_context *ctx) { + printf("CPU STOP!\n"); + NO_IMPL +} + static void proc_rlca(cpu_context *ctx) { u8 u = ctx->regs.a; bool c = (u >> 7) & 1; @@ -51,11 +91,22 @@ static void proc_rrca(cpu_context *ctx) { } static void proc_rla(cpu_context *ctx) { - + u8 u = ctx->regs.a; + u8 cf = CPU_FLAG_C; + u8 c = (u >> 7) & 1; + + ctx->regs.a = (u << 1) | cf; + cpu_set_flags(ctx, 0, 0, 0, c); } static void proc_rra(cpu_context *ctx) { - + u8 carry = CPU_FLAG_C; + u8 new_c = ctx->regs.a & 1; + + ctx->regs.a >>= 1; + ctx->regs.a |= (carry << 7); + + cpu_set_flags(ctx, 0, 0, 0, new_c); } static void proc_cb(cpu_context *ctx) { @@ -360,6 +411,10 @@ static void proc_di(cpu_context *ctx) { ctx->int_master_enabled = false; } +static void proc_ei(cpu_context *ctx) { + ctx->enabling_ime = true; +} + static void proc_ld(cpu_context *ctx) { if(ctx->dest_is_mem) { if(is_16_bit(ctx->cur_inst->reg_2)) { @@ -471,7 +526,18 @@ IN_PROC processors[] = { [IN_XOR] = proc_xor, [IN_OR] = proc_or, [IN_CP] = proc_cp, - [IN_CB] = proc_cb + [IN_RLCA] = proc_rlca, + [IN_RRCA] = proc_rrca, + [IN_RRA] = proc_rra, + [IN_RLA] = proc_rla, + [IN_STOP] = proc_stop, + [IN_HALT] = proc_halt, + [IN_DAA] = proc_daa, + [IN_CPL] = proc_cpl, + [IN_SCF] = proc_scf, + [IN_CCF] = proc_ccf, + [IN_CB] = proc_cb, + [IN_EI] = proc_ei }; IN_PROC inst_get_processor(in_type type) { diff --git a/lib/cpu_util.c b/lib/cpu_util.c index f3ae0fc..adb3ecd 100644 --- a/lib/cpu_util.c +++ b/lib/cpu_util.c @@ -92,4 +92,12 @@ void cpu_set_reg8(reg_type rt, u8 val) { cpu_registers *cpu_get_regs() { return &ctx.regs; +} + +u8 cpu_get_int_flags(){ + return ctx.int_flags; +} + +void cpu_set_int_flags(u8 value){ + ctx.int_flags = value; } \ No newline at end of file diff --git a/lib/emu.c b/lib/emu.c index a9cb5e8..7de2808 100644 --- a/lib/emu.c +++ b/lib/emu.c @@ -2,8 +2,15 @@ #include #include #include -#include -#include +#include + +#ifdef _WIN32 +#include +#endif +#ifdef _UNIX +#include +#include +#endif static emu_context ctx; @@ -11,28 +18,7 @@ emu_context *emu_get_context() { return &ctx; } -void delay(u32 ms) { - SDL_Delay(ms); -} - -int emu_run(int argc, char **argv) { - if (argc < 2) { - printf("Usage: gbemu \n"); - return -1; - } - - if(!cart_load(argv[1])) { - printf("Failed to load ROM file: %s\n", argv[1]); - return -2; - } - - printf("Cart loaded..\n"); - - SDL_Init(SDL_INIT_VIDEO); - printf("SDL INIT\n"); - TTF_Init(); - printf("TTF INIT\n"); - +DWORD WINAPI cpu_run(void *p) { cpu_init(); ctx.running = true; @@ -56,6 +42,53 @@ int emu_run(int argc, char **argv) { return 0; } +void sleep_ms(int milis) { +#ifdef _WIN32 + Sleep(milis); +#endif +#ifdef _UNIX + usleep(milis * 1000); +#endif +} + +int emu_run(int argc, char **argv) { + if (argc < 2) { + printf("Usage: gbemu \n"); + return -1; + } + + if(!cart_load(argv[1])) { + printf("Failed to load ROM file: %s\n", argv[1]); + return -2; + } + + printf("Cart loaded..\n"); + + ui_init(); +#ifdef _WIN32 + HANDLE thread = CreateThread(NULL, 0, cpu_run, NULL, 0, NULL); + if(!thread) { + fprintf(stderr, "Unable to create main CPU thread!\n"); + return -1; + } +#endif +#ifdef _UNIX + pthread_t t1; + if(pthread_create(&t1, NULL, cpu_run, NULL)) { + fprintf(stderr, "Unable to create main CPU thread!\n"); + return -1; + } +#endif + + while(!ctx.die) { + sleep_ms(1); + ui_handle_events(); + } + + + return 0; +} + void emu_cycles(int cpu_cycles) { //TODO } \ No newline at end of file diff --git a/lib/interrupts.c b/lib/interrupts.c new file mode 100644 index 0000000..a2e3068 --- /dev/null +++ b/lib/interrupts.c @@ -0,0 +1,34 @@ +#include +#include + +void int_handle(cpu_context *ctx, u16 address) { + stack_push16(ctx->regs.pc); + ctx->regs.pc = address; +} + +bool int_check(cpu_context *ctx, u16 address, interrupt_type t){ + if(ctx->int_flags & t && ctx->ie_register & t) { + int_handle(ctx, address); + ctx->int_flags &= -t; + ctx->halted = false; + ctx->int_master_enabled = false; + return true; + } + return false; +} + +void cpu_request_interrupt(interrupt_type t); + +void cpu_handle_interrupts(cpu_context *ctx) { + if (int_check(ctx, 0x40, IT_VBLANK)) { + + } else if(int_check(ctx, 0x48, IT_LCD_STAT)){ + + } else if(int_check(ctx, 0x50, IT_TIMER)){ + + } else if(int_check(ctx, 0x58, IT_SERIAL)){ + + } else if(int_check(ctx, 0x60, IT_JOYPAD)){ + + } +} \ No newline at end of file diff --git a/lib/ui.c b/lib/ui.c new file mode 100644 index 0000000..6b052af --- /dev/null +++ b/lib/ui.c @@ -0,0 +1,32 @@ +#include +#include + +#include +#include + +SDL_Window *sdlWindow; +SDL_Renderer *sdlRenderer; +SDL_Texture *sdlTexture; +SDL_Surface *screen; + +void ui_init(){ + SDL_Init(SDL_INIT_VIDEO); + printf("SDL INIT\n"); + TTF_Init(); + printf("TTF INIT\n"); + + SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, 0, &sdlWindow, &sdlRenderer); +} + +void ui_handle_events(){ + SDL_Event e; + while(SDL_PollEvent(&e) > 0) { + if(e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_CLOSE) { + emu_get_context()->die = true; + } + } +} + +void delay(u32 ms) { + SDL_Delay(ms); +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 882f8df..fcf4c57 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,4 +4,10 @@ FILE(GLOB SRCS *.c) add_executable(tests ${SRCS}) target_include_directories(tests PUBLIC ../include ../build/deps/check/src ../build/deps/check) target_link_libraries(tests PUBLIC Lib check) -add_test(NAME mytests COMMAND tests) \ No newline at end of file +add_test(NAME mytests COMMAND tests) + +if(WIN32) + add_definitions(-D_WIN32) +elseif(UNIX) + add_definitions(-d_UNIX) +endif() \ No newline at end of file