diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d0523bb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "files.associations": { + "lcd.h": "c", + "ppu_sm.h": "c", + "string.h": "c", + "common.h": "c", + "ppu.h": "c" + } +} \ No newline at end of file diff --git a/include/common.h b/include/common.h index 61356e1..22b437a 100644 --- a/include/common.h +++ b/include/common.h @@ -15,5 +15,6 @@ typedef uint64_t u64; #define BETWEEN(a, b, c) ((a >= b) && (a <= c)) void delay(u32 ms); +u32 get_ticks(); #define NO_IMPL { fprintf(stderr, "NOT YET IMPLEMENTED\n"); exit(-5); } \ No newline at end of file diff --git a/include/dma.h b/include/dma.h new file mode 100644 index 0000000..e64aa42 --- /dev/null +++ b/include/dma.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +void dma_start(u8 start); +void dma_tick(); + +bool dma_transferring(); \ No newline at end of file diff --git a/include/lcd.h b/include/lcd.h new file mode 100644 index 0000000..b6c260c --- /dev/null +++ b/include/lcd.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +typedef struct { + //registers + u8 lcdc; + u8 lcds; + u8 scroll_y; + u8 scroll_x; + u8 ly; + u8 ly_compare; + u8 dma; + u8 bg_palette; + u8 obj_palette[2]; + u8 win_y; + u8 win_x; + + //other data... + u32 bg_colors[4]; + u32 sp1_colors[4]; + u32 sp2_colors[4]; +} lcd_context; + +typedef enum { + MODE_HBLANK, + MODE_VBLANK, + MODE_OAM, + MODE_XFER +} lcd_mode; + +lcd_context *lcd_get_context(); + +#define LCDC_BGW_ENABLE (BIT(lcd_get_context()->lcdc, 0)) +#define LCDC_OBJ_ENABLE (BIT(lcd_get_context()->lcdc, 1)) +#define LCDC_OBJ_HEIGHT (BIT(lcd_get_context()->lcdc, 2) ? 16 : 8) +#define LCDC_BG_MAP_AREA (BIT(lcd_get_context()->lcdc, 3) ? 0x9C00 : 0x9800) +#define LCDC_BGW_DATA_AREA (BIT(lcd_get_context()->lcdc, 4) ? 0x8000 : 0x8800) +#define LCDC_WIN_ENABLE (BIT(lcd_get_context()->lcdc, 5)) +#define LCDC_WIN_MAP_AREA (BIT(lcd_get_context()->lcdc, 6) ? 0x9C00 : 0x9800) +#define LCDC_LCD_ENABLE (BIT(lcd_get_context()->lcdc, 7)) + +#define LCDS_MODE ((lcd_mode)(lcd_get_context()->lcds & 0b11)) +#define LCDS_MODE_SET(mode) { lcd_get_context()->lcds &= ~0b11; lcd_get_context()->lcdc |= mode; } +#define LCDS_LYC (BIT(lcd_get_context()->lcds, 2)) +#define LCDS_LYC_SET(b) BIT_SET(lcd_get_context()->lcds, 2, b) + +typedef enum { + SS_HBLANK = (1 << 3), + SS_VBLANK = (1 << 4), + SS_OAM = (1 << 5), + SS_LYC = (1 << 6) +} stat_src; + +#define LCDS_STAT_INT(src) (lcd_get_context()->lcds & src) + +void lcd_init(); + +u8 lcd_read(u16 address); +void lcd_write(u16 address, u8 value); \ No newline at end of file diff --git a/include/ppu.h b/include/ppu.h index f7c136c..69605c5 100644 --- a/include/ppu.h +++ b/include/ppu.h @@ -2,5 +2,40 @@ #include +static const LINES_PER_FRAME = 154; +static const TICKS_PER_LINE = 456; +static const YRES = 144; +static const XRES = 160; + +typedef struct { + u8 y; + u8 x; + u8 tile; + + unsigned f_cgb_pn : 3; + unsigned f_cgb_vram_bank : 1; + unsigned f_pn : 1; + unsigned f_x_flip : 1; + unsigned f_y_flip : 1; + unsigned f_bgp : 1; +} oam_entry; + +typedef struct { + oam_entry oam_ram[40]; + u8 vram[0x2000]; + + u32 current_frame; + u32 line_ticks; + u32 *video_buffer; +} ppu_context; + void ppu_init(); -void ppu_tick(); \ No newline at end of file +void ppu_tick(); + +void ppu_oam_write(u16 address, u8 value); +u8 ppu_oam_read(u16 address); + +void ppu_vram_write(u16 address, u8 value); +u8 ppu_vram_read(u16 address); + +ppu_context *ppu_get_context(); \ No newline at end of file diff --git a/include/ppu_sm.h b/include/ppu_sm.h new file mode 100644 index 0000000..36fa05f --- /dev/null +++ b/include/ppu_sm.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +void ppu_mode_oam(); +void ppu_mode_xfer(); +void ppu_mode_vblank(); +void ppu_mode_hblank(); \ No newline at end of file diff --git a/include/timer.h b/include/timer.h index eff6444..1005447 100644 --- a/include/timer.h +++ b/include/timer.h @@ -2,5 +2,17 @@ #include +typedef struct { + u16 div; + u8 tima; + u8 tma; + u8 tac; +} timer_context; + void timer_init(); -void timer_tick(); \ No newline at end of file +void timer_tick(); + +void timer_write(u16 address, u8 value); +u8 timer_read(u16 address); + +timer_context *timer_get_context(); \ No newline at end of file diff --git a/include/ui.h b/include/ui.h index 1272c08..682c4c3 100644 --- a/include/ui.h +++ b/include/ui.h @@ -6,4 +6,5 @@ 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 +void ui_handle_events(); +void ui_update(); \ No newline at end of file diff --git a/lib/bus.c b/lib/bus.c index 1075dd1..2043b8d 100644 --- a/lib/bus.c +++ b/lib/bus.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include // 0x0000 - 0x3FFF : ROM Bank 0 // 0x4000 - 0x7FFF : ROM Bank 1 - Switchable @@ -24,10 +26,7 @@ u8 bus_read(u16 address) { return cart_read(address); } else if (address < 0xA000) { //Char/Map Data - //TODO - printf("UNSUPPORTED bus_read(%04X)\n", address); - //NO_IMPL - return 0; + return ppu_vram_read(address); } else if (address < 0xC000) { //Cartridge RAM return cart_read(address); @@ -39,10 +38,9 @@ u8 bus_read(u16 address) { return 0; } else if (address < 0xFEA0) { //OAM - //TODO - printf("UNSUPPORTED bus_read(%04X)\n", address); - //NO_IMPL - return 0; + if(dma_transferring()) + return 0xFF; + return ppu_oam_read(address); } else if (address < 0xFF00) { //reserved unusable return 0; @@ -66,10 +64,8 @@ void bus_write(u16 address, u8 value) { return; } else if (address < 0xA000) { //Char/Map Data - //TODO - printf("UNSUPPORTED bus_write(%04X)\n", address); + ppu_vram_write(address, value); return; - //NO_IMPL } else if (address < 0xC000) { //Cartridge RAM cart_write(address, value); @@ -83,10 +79,9 @@ void bus_write(u16 address, u8 value) { return; } else if (address < 0xFEA0) { //OAM - //TODO - printf("UNSUPPORTED bus_write(%04X)\n", address); + if(!dma_transferring()) + ppu_oam_write(address, value); return; - //NO_IMPL } else if (address < 0xFF00) { //reserved unusable return; diff --git a/lib/cpu.c b/lib/cpu.c index 98aed72..bf8aac7 100644 --- a/lib/cpu.c +++ b/lib/cpu.c @@ -4,11 +4,36 @@ #include #include #include +#include cpu_context ctx = {0}; +#define CPU_DEBUG 0 +#define FILE_LOG 0 + +#if FILE_LOG == 1 +FILE *log; +#endif void cpu_init() { + ctx.regs.a = 0x01; + ctx.regs.f = 0xB0; + ctx.regs.b = 0x00; + ctx.regs.c = 0x13; + ctx.regs.d = 0x00; + ctx.regs.e = 0xD8; + ctx.regs.h = 0x01; + ctx.regs.l = 0x4D; + ctx.regs.sp = 0xFFFE; ctx.regs.pc = 0x100; + ctx.ie_register = 0; + ctx.int_flags = 0; + ctx.int_master_enabled = false; + ctx.enabling_ime = false; + + timer_get_context()->div = 0xABCC; +#if FILE_LOG == 1 + log = fopen("log.txt", "w"); +#endif } static void fetch_instruction() { @@ -32,7 +57,15 @@ bool cpu_step() { if(!ctx.halted) { u16 pc = ctx.regs.pc; fetch_instruction(); + emu_cycles(1); +#if FILE_LOG == 1 + fprintf(log, "A:%02X F:%02X B:%02X C:%02X D:%02X E:%02X H:%02X L:%02X SP:%04X PC:%04X PCMEM:%02X,%02X,%02X,%02X\n", + ctx.regs.a, ctx.regs.f, ctx.regs.b, ctx.regs.c, ctx.regs.d, ctx.regs.e, ctx.regs.h, ctx.regs.l, ctx.regs.sp, pc, + bus_read(pc), bus_read(pc+1), bus_read(pc+2), bus_read(pc+3) + ); +#endif fetch_data(); +#if CPU_DEBUG == 1 char flags[16]; sprintf(flags, "%c%c%c%c", ctx.regs.f & (1 << 7) ? 'Z' : '-', @@ -52,6 +85,7 @@ bool cpu_step() { dbg_update(); dbg_print(); +#endif execute(); } else { @@ -81,4 +115,8 @@ u8 cpu_get_ie_register(){ void cpu_set_ie_register(u8 ie){ ctx.ie_register = ie; +} + +void cpu_request_interrupt(interrupt_type t) { + ctx.int_flags |= t; } \ No newline at end of file diff --git a/lib/cpu_fetch.c b/lib/cpu_fetch.c index 6f2466c..0d5e3a6 100644 --- a/lib/cpu_fetch.c +++ b/lib/cpu_fetch.c @@ -44,7 +44,7 @@ void fetch_data() { return; case AM_R_MR: { u16 addr = cpu_read_reg(ctx.cur_inst->reg_2); - if(ctx.cur_inst->reg_1 == RT_C) { + if(ctx.cur_inst->reg_2 == RT_C) { addr |= 0xFF00; } ctx.fetched_data = bus_read(addr); @@ -74,7 +74,7 @@ void fetch_data() { cpu_set_reg(RT_HL, cpu_read_reg(RT_HL)-1); return; case AM_R_A8: - ctx.fetched_data = bus_read(bus_read(ctx.regs.pc)); + ctx.fetched_data = bus_read(ctx.regs.pc); emu_cycles(2); ctx.regs.pc++; return; diff --git a/lib/cpu_proc.c b/lib/cpu_proc.c index f207dfa..0681363 100644 --- a/lib/cpu_proc.c +++ b/lib/cpu_proc.c @@ -5,7 +5,7 @@ //process CPU instructions... -void cpu_set_flags(cpu_context *ctx, u8 z, u8 n, u8 h, u8 c){ +void cpu_set_flags(cpu_context *ctx, int8_t z, int8_t n, int8_t h, int8_t c){ if (z != -1){ BIT_SET(ctx->regs.f, 7, z) } @@ -52,7 +52,7 @@ static void proc_daa(cpu_context *ctx) { } static void proc_cpl(cpu_context *ctx) { - ctx->regs.a = -ctx->regs.a; + ctx->regs.a = ~ctx->regs.a; cpu_set_flags(ctx, -1, 1, 1, -1); } @@ -112,7 +112,7 @@ static void proc_rra(cpu_context *ctx) { static void proc_cb(cpu_context *ctx) { u8 op = ctx->fetched_data; reg_type reg = decode_reg(op & 0b111); - u8 bit = (op >> 3) & 0xb111; + u8 bit = (op >> 3) & 0b111; u8 bit_op = (op >> 6) & 0b11; u8 reg_val = cpu_read_reg8(reg); emu_cycles(1); @@ -138,6 +138,7 @@ static void proc_cb(cpu_context *ctx) { reg_val |= (1 << bit); cpu_set_reg8(reg, reg_val); return; + default: break; } bool flagC = CPU_FLAG_C; @@ -148,7 +149,7 @@ static void proc_cb(cpu_context *ctx) { bool setC = false; u8 result = (reg_val << 1) & 0xFF; - if(reg_val & (1 << 7) != 0) { + if((reg_val & (1 << 7)) != 0) { result |= 1; setC = true; } @@ -322,7 +323,7 @@ static void proc_add(cpu_context *ctx) { if(ctx->cur_inst->reg_1 == RT_SP) { z = 0; h = (cpu_read_reg(ctx->cur_inst->reg_1) & 0xF) + (ctx->fetched_data & 0xF) >= 0x10; - c = (int)(cpu_read_reg(ctx->cur_inst->reg_1) & 0xFF) + (int)(ctx->fetched_data & 0xFF) > 0x100; + c = (int)(cpu_read_reg(ctx->cur_inst->reg_1) & 0xFF) + (int)(ctx->fetched_data & 0xFF) >= 0x100; } cpu_set_reg(ctx->cur_inst->reg_1, val & 0xFFFF); @@ -349,7 +350,7 @@ static void proc_inc(cpu_context *ctx) { return; } - cpu_set_flags(ctx, val == 0, 0, (val & 0x0F), -1); + cpu_set_flags(ctx, val == 0, 0, (val & 0x0F) == 0, -1); } static void proc_dec(cpu_context *ctx) { @@ -432,8 +433,8 @@ static void proc_ld(cpu_context *ctx) { u8 cflag = (cpu_read_reg(ctx->cur_inst->reg_2) & 0xFF) + (ctx->fetched_data & 0xFF) >= 0x100; cpu_set_flags(ctx, 0, 0, hflag, cflag); cpu_set_reg(ctx->cur_inst->reg_1, cpu_read_reg(ctx->cur_inst->reg_2) + (char)ctx->fetched_data); + return; } - cpu_set_reg(ctx->cur_inst->reg_1, ctx->fetched_data); } diff --git a/lib/cpu_util.c b/lib/cpu_util.c index adb3ecd..5b3dac6 100644 --- a/lib/cpu_util.c +++ b/lib/cpu_util.c @@ -73,14 +73,14 @@ u8 cpu_read_reg8(reg_type rt) { void cpu_set_reg8(reg_type rt, u8 val) { switch(rt) { - case RT_A: ctx.regs.a = val & 0XFF; break; - case RT_F: ctx.regs.f = val & 0XFF; break; - case RT_B: ctx.regs.b = val & 0XFF; break; - case RT_C: ctx.regs.c = val & 0XFF; break; - case RT_D: ctx.regs.d = val & 0XFF; break; - case RT_E: ctx.regs.e = val & 0XFF; break; - case RT_H: ctx.regs.h = val & 0XFF; break; - case RT_L: ctx.regs.l = val & 0XFF; break; + case RT_A: ctx.regs.a = val & 0xFF; break; + case RT_F: ctx.regs.f = val & 0xFF; break; + case RT_B: ctx.regs.b = val & 0xFF; break; + case RT_C: ctx.regs.c = val & 0xFF; break; + case RT_D: ctx.regs.d = val & 0xFF; break; + case RT_E: ctx.regs.e = val & 0xFF; break; + case RT_H: ctx.regs.h = val & 0xFF; break; + case RT_L: ctx.regs.l = val & 0xFF; break; case RT_HL: bus_write(cpu_read_reg(RT_HL), val); break; diff --git a/lib/dma.c b/lib/dma.c new file mode 100644 index 0000000..77a15bf --- /dev/null +++ b/lib/dma.c @@ -0,0 +1,40 @@ +#include +#include +#include + +typedef struct { + bool active; + u8 byte; + u8 value; + u8 start_delay; +} dma_context; + +static dma_context ctx; + +void dma_start(u8 start) { + ctx.active = true; + ctx.byte = 0; + ctx.start_delay = 2; + ctx.value = start; +} + +void dma_tick() { + if (!ctx.active) { + return; + } + + if (ctx.start_delay) { + ctx.start_delay--; + return; + } + + ppu_oam_write(ctx.byte, bus_read((ctx.value * 0x100) + ctx.byte)); + + ctx.byte++; + + ctx.active = ctx.byte < 0xA0; +} + +bool dma_transferring() { + return ctx.active; +} \ No newline at end of file diff --git a/lib/emu.c b/lib/emu.c index 7de2808..4f1e896 100644 --- a/lib/emu.c +++ b/lib/emu.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #ifdef _WIN32 #include @@ -19,6 +22,8 @@ emu_context *emu_get_context() { } DWORD WINAPI cpu_run(void *p) { + ppu_init(); + timer_init(); cpu_init(); ctx.running = true; @@ -36,7 +41,6 @@ DWORD WINAPI cpu_run(void *p) { return -3; } - ctx.ticks++; } return 0; @@ -80,9 +84,15 @@ int emu_run(int argc, char **argv) { } #endif + u32 prev_frame = 0; + while(!ctx.die) { sleep_ms(1); ui_handle_events(); + if (prev_frame != ppu_get_context()->current_frame) { + ui_update(); + } + prev_frame = ppu_get_context()->current_frame; } @@ -90,5 +100,12 @@ int emu_run(int argc, char **argv) { } void emu_cycles(int cpu_cycles) { - //TODO + for (int i = 0; i < cpu_cycles; i++){ + for(int n = 0; n < 4; n++){ + ctx.ticks++; + timer_tick(); + ppu_tick(); + } + dma_tick(); + } } \ No newline at end of file diff --git a/lib/instructions.c b/lib/instructions.c index 019eba0..e112042 100644 --- a/lib/instructions.c +++ b/lib/instructions.c @@ -18,7 +18,7 @@ instruction instructions[0x100] = { [0x0C] = {IN_INC, AM_R, RT_C}, [0x0D] = {IN_DEC, AM_R, RT_C}, [0x0E] = {IN_LD, AM_R_D8, RT_C}, - [0x0F] = {IN_RRA}, + [0x0F] = {IN_RRCA}, //0x1X [0x10] = {IN_STOP}, [0x11] = {IN_LD, AM_R_D16, RT_DE}, @@ -258,7 +258,7 @@ instruction instructions[0x100] = { [0xF6] = {IN_OR, AM_R_D8, RT_A}, [0xF7] = {IN_RST, AM_IMP, RT_NONE, RT_NONE, CT_NONE, 0x30}, [0xF8] = {IN_LD, AM_HL_SPR, RT_HL, RT_SP}, - [0xF9] = {IN_LD, AM_R_R, RT_HL, RT_SP}, + [0xF9] = {IN_LD, AM_R_R, RT_SP, RT_HL}, [0xFA] = {IN_LD, AM_R_A16, RT_A}, [0xFB] = {IN_EI}, [0xFE] = {IN_CP, AM_R_D8, RT_A}, diff --git a/lib/interrupts.c b/lib/interrupts.c index a2e3068..7ba39db 100644 --- a/lib/interrupts.c +++ b/lib/interrupts.c @@ -9,7 +9,7 @@ void int_handle(cpu_context *ctx, u16 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->int_flags &= ~t; ctx->halted = false; ctx->int_master_enabled = false; return true; diff --git a/lib/io.c b/lib/io.c index e8615af..45fc128 100644 --- a/lib/io.c +++ b/lib/io.c @@ -1,4 +1,8 @@ #include +#include +#include +#include +#include static char serial_data[2]; @@ -11,6 +15,18 @@ u8 io_read(u16 address){ return serial_data[1]; } + if(BETWEEN(address, 0xFF04, 0xFF07)){ + return timer_read(address); + } + + if(BETWEEN(address, 0xFF40, 0xFF4B)){ + return lcd_read(address); + } + + if(address == 0xFF0F) { + return cpu_get_int_flags(); + } + printf("UNSUPPORTED io_read(%04X)\n", address); return 0; } @@ -27,6 +43,21 @@ void io_write(u16 address, u8 value){ return; } + if(BETWEEN(address, 0xFF04, 0xFF07)){ + timer_write(address, value); + return; + } + + if(address == 0xFF0F) { + cpu_set_int_flags(value); + return; + } + + if(BETWEEN(address, 0xFF40, 0xFF4B)){ + lcd_write(address, value); + return; + } + printf("UNSUPPORTED io_write(%04X)\n", address); } diff --git a/lib/lcd.c b/lib/lcd.c new file mode 100644 index 0000000..9d90687 --- /dev/null +++ b/lib/lcd.c @@ -0,0 +1,73 @@ +#include +#include +#include + +static lcd_context ctx; + +static unsigned long colors_default[4] = {0xFFFFFFFF, 0xFFAAAAAA, 0xFF555555, 0xFF000000}; + +lcd_context *lcd_get_context() { + return &ctx; +} + +void lcd_init() { + ctx.lcdc = 0x91; + ctx.scroll_x = 0; + ctx.scroll_y = 0; + ctx.ly = 0; + ctx.ly_compare = 0; + ctx.bg_palette = 0xFC; + ctx.obj_palette[0] = 0xFF; + ctx.obj_palette[1] = 0xFF; + ctx.win_x = 0; + ctx.win_y = 0; + + for(int i = 0; i < 4; i++) { + ctx.bg_colors[i] = colors_default[i]; + ctx.sp1_colors[i] = colors_default[i]; + ctx.sp2_colors[i] = colors_default[i]; + } +} + +u8 lcd_read(u16 address) { + u8 offset = (address - 0xFF40); + u8 *p = (u8 *)&ctx; + return p[offset]; +} + +void update_palette(u8 palette_data, u8 pal) { + u32 *p_colors = ctx.bg_colors; + + switch(pal) { + case 1: + p_colors = ctx.sp1_colors; + break; + case 2: + p_colors = ctx.sp2_colors; + break; + } + + p_colors[0] = colors_default[palette_data & 0b11]; + p_colors[1] = colors_default[(palette_data >> 2) & 0b11]; + p_colors[2] = colors_default[(palette_data >> 4) & 0b11]; + p_colors[3] = colors_default[(palette_data >> 6) & 0b11]; +} + +void lcd_write(u16 address, u8 value) { + u8 offset = (address - 0xFF40); + u8 *p = (u8 *)&ctx; + p[offset] = value; + + if(offset == 6) { + //offset FF64 + dma_start(value); + } + + if(offset == 7) { + update_palette(value, 0); + } else if(offset == 8) { + update_palette(value & 0b11111100, 1); + } else if(offset == 9) { + update_palette(value & 0b11111100, 2); + } +} \ No newline at end of file diff --git a/lib/ppu.c b/lib/ppu.c index d3ab571..83f7a8c 100644 --- a/lib/ppu.c +++ b/lib/ppu.c @@ -1,9 +1,67 @@ #include +#include +#include +#include + +static ppu_context ctx; + +ppu_context *ppu_get_context() { + return &ctx; +} void ppu_init() { + ctx.current_frame = 0; + ctx.line_ticks = 0; + ctx.video_buffer = malloc(YRES * XRES * sizeof(u32)); + lcd_init(); + LCDS_MODE_SET(MODE_OAM); + + memset(ctx.oam_ram, 0, sizeof(ctx.oam_ram)); + memset(ctx.video_buffer, 0, YRES*XRES*sizeof(u32)); } void ppu_tick() { - + ctx.line_ticks++; + + switch(LCDS_MODE) { + case MODE_OAM: + ppu_mode_oam(); + break; + case MODE_XFER: + ppu_mode_xfer(); + break; + case MODE_VBLANK: + ppu_mode_vblank(); + break; + case MODE_HBLANK: + ppu_mode_hblank(); + break; + } +} + +void ppu_oam_write(u16 address, u8 value) { + if (address >= 0xFE00) { + address -= 0xFE00; + } + + u8 *p = (u8 *)ctx.oam_ram; + p[address] = value; +} + +u8 ppu_oam_read(u16 address){ + if (address > 0xFE00) { + address -= 0xFE00; + } + + u8 *p = (u8 *)ctx.oam_ram; + return p[address]; +} + +void ppu_vram_write(u16 address, u8 value){ + ctx.vram[address - 0x8000] = value; +} + +u8 ppu_vram_read(u16 address){ + return ctx.vram[address - 0x8000]; } \ No newline at end of file diff --git a/lib/ppu_sm.c b/lib/ppu_sm.c new file mode 100644 index 0000000..b64e3e8 --- /dev/null +++ b/lib/ppu_sm.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +void increment_ly() { + lcd_get_context()->ly++; + + if(lcd_get_context()->ly == lcd_get_context()->ly_compare) { + LCDS_LYC_SET(1); + + if(LCDS_STAT_INT(SS_LYC)) { + cpu_request_interrupt(IT_LCD_STAT); + } + } else { + LCDS_LYC_SET(0); + } +} + +void ppu_mode_oam() { + if(ppu_get_context()->line_ticks >= 80) { + LCDS_MODE_SET(MODE_XFER); + } +} + +void ppu_mode_xfer() { + if(ppu_get_context()->line_ticks >= 80 + 172) { + LCDS_MODE_SET(MODE_HBLANK); + } +} + +void ppu_mode_vblank() { + if(ppu_get_context()->line_ticks >= TICKS_PER_LINE) { + increment_ly(); + + if(lcd_get_context()->ly >= LINES_PER_FRAME) { + LCDS_MODE_SET(MODE_OAM); + lcd_get_context()->ly = 0; + } + + ppu_get_context()->line_ticks = 0; + } +} + +static u32 target_frame_time = 1000/60; +static long prev_frame_time = 0; +static long start_timer = 0; +static long frame_count = 0; + +void ppu_mode_hblank() { + if (ppu_get_context()->line_ticks >= TICKS_PER_LINE) { + increment_ly(); + + if(lcd_get_context()->ly >= YRES) { + LCDS_MODE_SET(MODE_VBLANK); + + cpu_request_interrupt(IT_VBLANK); + + if(LCDS_STAT_INT(SS_VBLANK)) { + cpu_request_interrupt(IT_LCD_STAT); + } + + ppu_get_context()->current_frame++; + + //calc FPS... + u32 end = get_ticks(); + u32 frame_time = end - prev_frame_time; + + if(frame_time < target_frame_time) { + delay((target_frame_time - frame_time)); + } + + if (end - start_timer >= 1000) { + u32 fps = frame_count; + start_timer = end; + frame_count = 0; + printf("FPS: %ld\n", fps); + } + + frame_count++; + prev_frame_time = get_ticks(); + + + } else { + LCDS_MODE_SET(MODE_OAM); + } + + ppu_get_context()->line_ticks = 0; + } +} \ No newline at end of file diff --git a/lib/timer.c b/lib/timer.c index e81a980..5c35e97 100644 --- a/lib/timer.c +++ b/lib/timer.c @@ -1,10 +1,88 @@ #include +#include + +static timer_context ctx = {0}; void timer_init() { - + ctx.div = 0XAC00; } void timer_tick() { - + u16 prev_div = ctx.div; + ctx.div++; + bool timer_update = false; + switch(ctx.tac & 0b11) { + case 0b00: + timer_update = (prev_div & (1 << 9)) && (!(ctx.div & (1 << 9))); + break; + + case 0b01: + timer_update = (prev_div & (1 << 3)) && (!(ctx.div & (1 << 3))); + break; + + case 0b10: + timer_update = (prev_div & (1 << 5)) && (!(ctx.div & (1 << 5))); + break; + + case 0b11: + timer_update = (prev_div & (1 << 7)) && (!(ctx.div & (1 << 7))); + break; + } + + if(timer_update && ctx.tac & (1 << 2)) { + ctx.tima++; + if(ctx.tima == 0xFF){ + ctx.tima = ctx.tma; + cpu_request_interrupt(IT_TIMER); + } + } +} + +void timer_write(u16 address, u8 value){ + switch(address) { + case 0xFF04: + //DIV + ctx.div = 0; + break; + + case 0xFF05: + //TIMA + ctx.tima = value; + break; + + case 0xFF06: + //TMA + ctx.tma = value; + break; + + case 0xFF07: + //TAC + ctx.tac = value; + break; + } +} + +u8 timer_read(u16 address) { + switch(address) { + case 0xFF04: + //DIV + return ctx.div >> 8; + + case 0xFF05: + //TIMA + return ctx.tima; + + case 0xFF06: + //TMA + return ctx.tma; + + case 0xFF07: + //TAC + return ctx.tac; + } +} + +timer_context *timer_get_context() { + return &ctx; } \ No newline at end of file diff --git a/lib/ui.c b/lib/ui.c index 6b052af..1412216 100644 --- a/lib/ui.c +++ b/lib/ui.c @@ -9,6 +9,13 @@ SDL_Renderer *sdlRenderer; SDL_Texture *sdlTexture; SDL_Surface *screen; +SDL_Window *sdlDebugWindow; +SDL_Renderer *sdlDebugRenderer; +SDL_Texture *sdlDebugTexture; +SDL_Surface *debugScreen; + +static int scale = 4; + void ui_init(){ SDL_Init(SDL_INIT_VIDEO); printf("SDL INIT\n"); @@ -16,6 +23,79 @@ void ui_init(){ printf("TTF INIT\n"); SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, 0, &sdlWindow, &sdlRenderer); + SDL_CreateWindowAndRenderer(16 * 8 * scale, 32 * 8 * scale, 0, &sdlDebugWindow, &sdlDebugRenderer); + debugScreen = SDL_CreateRGBSurface(0, (16 * 8 * scale) + (16 * scale), + (32 * 8 * scale) + (64 * scale), 32, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + sdlDebugTexture = SDL_CreateTexture(sdlDebugRenderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + (16 * 8 * scale) + (16 * scale), + (32 * 8 * scale) + (64 * scale)); + int x, y; + SDL_GetWindowPosition(sdlWindow, &x, &y); + SDL_SetWindowPosition(sdlDebugWindow, x+SCREEN_WIDTH, y); +} + +static unsigned long tile_colors[4] = {0xFFFFFFFF, 0xFFAAAAAA, 0xFF555555, 0xFF000000}; + +void display_tile(SDL_Surface *surface, u16 startLocation, u16 tileNum, int x, int y) { + SDL_Rect rc; + for(int tileY = 0; tileY < 16; tileY += 2) { + u8 b1 = bus_read(startLocation+(tileNum * 16) + tileY); + u8 b2 = bus_read(startLocation+(tileNum * 16) + tileY + 1); + + for (int bit=7; bit >= 0; bit--) { + u8 hi = !!(b1 & (1 << bit)) << 1; + u8 lo = !!(b2 & (1 << bit)); + + u8 color = hi | lo; + rc.x = x + ((7 - bit) * scale); + rc.y = y + (tileY/2 * scale); + rc.w = scale; + rc.h = scale; + SDL_FillRect(surface, &rc, tile_colors[color]); + } + } +} + +void update_debug_window() { + int xDraw = 0; + int yDraw = 0; + int tileNum = 0; + + SDL_Rect rc; + rc.x = 0; + rc.y = 0; + rc.w = debugScreen->w; + rc.h = debugScreen->h; + SDL_FillRect(debugScreen, &rc, 0xFF111111); + + u16 addr = 0x8000; + + //384 tiles, 24 x 16 + + for(int y = 0; y < 24; y++) { + for(int x = 0; x < 16; x++) { + display_tile(debugScreen, addr, tileNum, xDraw + (x * scale), yDraw + (y * scale)); + xDraw += (8 * scale); + tileNum++; + } + yDraw += (8 * scale); + xDraw = 0; + } + + SDL_UpdateTexture(sdlDebugTexture, NULL, debugScreen->pixels, debugScreen->pitch); + SDL_RenderClear(sdlDebugRenderer); + SDL_RenderCopy(sdlDebugRenderer, sdlDebugTexture, NULL, NULL); + SDL_RenderPresent(sdlDebugRenderer); +} + +void ui_update() { + update_debug_window(); } void ui_handle_events(){ @@ -29,4 +109,8 @@ void ui_handle_events(){ void delay(u32 ms) { SDL_Delay(ms); +} + +u32 get_ticks() { + return SDL_GetTicks(); } \ No newline at end of file diff --git a/roms/test.asm b/roms/test.asm index fe4cdf2..22ef6a9 100644 --- a/roms/test.asm +++ b/roms/test.asm @@ -7,5 +7,6 @@ db "My Test ROM ", 0 ; title ds $150 - @, 0 EntryPoint: -xor a +ld a,0x80 +rlc a halt \ No newline at end of file diff --git a/roms/test.gb b/roms/test.gb index 7da2205..6a1afd1 100644 Binary files a/roms/test.gb and b/roms/test.gb differ