From 10e21b988e934f122d83c7aa4a56349e2025bbfb Mon Sep 17 00:00:00 2001 From: Paul Duncan Date: Mon, 18 Jun 2018 15:02:51 -0400 Subject: add mmu_rp/mmu_wp, add gpu ports --- ops.yaml | 371 ++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 295 insertions(+), 76 deletions(-) diff --git a/ops.yaml b/ops.yaml index 1b17ac5..da79eef 100644 --- a/ops.yaml +++ b/ops.yaml @@ -7577,13 +7577,10 @@ templates: GB_BTN_START = (1 << 7), } gb_btn_t; - typedef enum { - GPU_MODE_OAM, - GPU_MODE_VRAM, - GPU_MODE_HBLANK, - GPU_MODE_VBLANK, - GPU_MODE_LAST, - } gpu_mode_t; + #define GPU_MODE_OAM 2 + #define GPU_MODE_VRAM 3 + #define GPU_MODE_HBLANK 0 + #define GPU_MODE_VBLANK 1 typedef struct { struct { @@ -7617,9 +7614,19 @@ templates: } mmu; struct { - gpu_mode_t mode; uint16_t clock; - uint16_t line; + uint8_t mode, + lcdc, + stat, + scy, + scx, + wy, + wx, + bgp, + obp0, + obp1, + line, + lyc; uint8_t frame[3 * 160 * 144]; } gpu; @@ -7633,21 +7640,209 @@ templates: } cpu; } gb_t; + #define PORT_P1 0xFF00 + #define PORT_IE 0xFFFF + #define PORT_IF 0xFF0F + + // lcd ports + #define PORT_LCDC 0xFF40 + #define PORT_STAT 0xFF41 + #define PORT_SCY 0xFF42 + #define PORT_SCX 0xFF43 + #define PORT_LY 0xFF44 + #define PORT_LYC 0xFF45 + #define PORT_DMA 0xFF46 + #define PORT_BGP 0xFF47 + #define PORT_OBP0 0xFF48 + #define PORT_OBP1 0xFF49 + #define PORT_WY 0xFF4A + #define PORT_WX 0xFF4B + + // forward references for mmu_oam_dma() + static uint8_t mmu_rb(gb_t * const, const uint16_t); + static void mmu_wb(gb_t * const, const uint16_t, const uint8_t); + + static void + mmu_oam_dma( + gb_t * const ctx, + const uint8_t val + ) { + const uint16_t src_addr = (val << 8); + // copy 160 bytes of data to OAM + // (src: http://gbdev.gg8.se/wiki/articles/Video_Display#LCD_OAM_DMA_Transfers) + for (uint8_t i = 0; i < 160; i++) { + mmu_wb(ctx, 0xFE00 + i, mmu_rb(ctx, src_addr + i)); + } + } + static uint8_t - mmu_ri( + mmu_rp( const gb_t * const ctx, - const uint8_t mask + const uint16_t addr ) { - return (ctx->mmu.iv & mask) & 0x1F; + switch (addr) { + case PORT_IE: + // interrupt enabled mask + return ctx->mmu.ie & 0x1F; + break; + case PORT_IF: + // interrupt flags + return ctx->mmu.iv & 0x1F; + break; + case PORT_P1: + // buttons (p1) + switch (ctx->mmu.p1_mode) { + case P1_MODE_P14: + return ctx->mmu.btns & 0xF; + case P1_MODE_P15: + return (ctx->mmu.btns & 0xF0) >> 4; + default: + return 0; + } + + break; + case PORT_LCDC: + return ctx->gpu.lcdc; + + break; + case PORT_STAT: + return (ctx->gpu.stat & 0xF8) | + ((ctx->gpu.line == ctx->gpu.lyc) ? (1 << 3) : 0) | + (ctx->gpu.mode & 0x3); + + break; + case PORT_SCY: + return ctx->gpu.scy; + + break; + case PORT_SCX: + return ctx->gpu.scx; + + break; + case PORT_LY: + return ctx->gpu.line; + + break; + case PORT_LYC: + return ctx->gpu.lyc; + + break; + case PORT_DMA: + // write-only (gb-manual, p62) + return 0; + + break; + case PORT_BGP: + return ctx->gpu.bgp; + + break; + case PORT_OBP0: + return ctx->gpu.obp0; + + break; + case PORT_OBP1: + return ctx->gpu.obp1; + + break; + case PORT_WY: + return ctx->gpu.wy; + + break; + case PORT_WX: + return ctx->gpu.wx; + + break; + default: + // TODO: io ports + return 0; + } } static void - mmu_wi( + mmu_wp( gb_t * const ctx, - const uint8_t flag, - const bool set + const uint16_t addr, + const uint8_t val ) { - ctx->mmu.iv &= 0x1F & (set ? flag : ~flag); + switch (addr) { + case PORT_IE: + // interrupt enabled mask + ctx->mmu.ie = (val & 0x1F); + + break; + case PORT_IF: + // interrupt flags + ctx->mmu.iv = (val & 0x1F); + + break; + case PORT_P1: + // buttons (p1) + switch (val) { + case 0x10: + ctx->mmu.p1_mode = P1_MODE_P14; + break; + case 0x20: + ctx->mmu.p1_mode = P1_MODE_P15; + break; + default: + // TODO: io ports + break; + } + + break; + case PORT_LCDC: + ctx->gpu.lcdc = val; + + break; + case PORT_STAT: + ctx->gpu.stat = (val & 0xF8); + + break; + case PORT_SCY: + ctx->gpu.scy = val; + + break; + case PORT_SCX: + ctx->gpu.scx = val; + + break; + case PORT_LY: + // FIXME: according to URL below writing to this register zeroes it: + // (src: http://gbdev.gg8.se/wiki/articles/Video_Display) + + break; + case PORT_LYC: + ctx->gpu.lyc = val; + + break; + case PORT_DMA: + mmu_oam_dma(ctx, val); + + break; + case PORT_BGP: + ctx->gpu.bgp = val; + + break; + case PORT_OBP0: + ctx->gpu.obp0 = val; + + break; + case PORT_OBP1: + ctx->gpu.obp1 = val; + + break; + case PORT_WY: + ctx->gpu.wy = val; + + break; + case PORT_WX: + ctx->gpu.wx = val; + + break; + default: + // TODO: io ports + break; + } } static uint8_t @@ -7722,25 +7917,9 @@ templates: break; case 0x0F00: - if (addr == 0xFFFF) { - // interrupt enabled mask - return ctx->mmu.ie & 0x1F; - } else if (addr == 0xFF0F) { - // interrupt flags - return mmu_ri(ctx, 0xFF); - } else if (addr == 0xFF00) { - // buttons (p1) - switch (ctx->mmu.p1_mode) { - case P1_MODE_P14: - return ctx->mmu.btns & 0xF; - case P1_MODE_P15: - return (ctx->mmu.btns & 0xF0) >> 4; - default: - return 0; - } - } else if (addr < 0xFF80) { - // TODO: io ports - return 0; + if (addr == PORT_IE || addr < 0xFF80) { + // io port + return mmu_rp(ctx, addr); } else { // zero page return ctx->mmu.zram[addr & 0x7F]; @@ -7828,24 +8007,9 @@ templates: break; case 0x0F00: - if (addr == 0xFFFF) { - ctx->mmu.ie = (val & 0x1F); - } else if (addr == 0xFF0F) { - // write interrupt flags - mmu_wi(ctx, val); - } else if (addr == 0XFF00) { - switch (val) { - case 0x10: - ctx->mmu.p1_mode = P1_MODE_P14; - break; - case 0x20: - ctx->mmu.p1_mode = P1_MODE_P15; - break; - default: - ctx->mmu.p1_mode = P1_MODE_NONE; - } - } else if (addr < 0xFF80) { - // TODO: io ports + if (addr == PORT_IE || addr < 0xFF80) { + // io ports + mmu_wp(ctx, addr, val); } else { // zero page ctx->mmu.zram[addr & 0x7F] = val; @@ -8786,6 +8950,54 @@ templates: cpu_ww(ctx, RW_PC, mmu_rw(ctx, addr)); } + static void + gpu_set_line( + gb_t * const ctx, + const uint8_t line + ) { + ctx->gpu.line = line; + + if (ctx->gpu.stat & (1 << 6) && (line == ctx->gpu.lyc)) { + // lyc interrupt is enabled, trigger LCDC interrupt + mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & (1 << 1)); + } + } + + static void + gpu_set_mode( + gb_t * const ctx, + const uint8_t mode + ) { + ctx->gpu.mode = mode; + + switch (mode) { + case GPU_MODE_OAM: + if (ctx->gpu.stat & (1 << 5)) { + // vblank interrupt is enabled, trigger LCDC interrupt + mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & (1 << 1)); + } + + break; + case GPU_MODE_HBLANK: + if (ctx->gpu.stat & (1 << 3)) { + // hblank interrupt is enabled, trigger LCDC interrupt + mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & (1 << 1)); + } + + break; + case GPU_MODE_VBLANK: + if (ctx->gpu.stat & (1 << 4)) { + // vblank interrupt is enabled, trigger vblank interrupt + mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & 1); + } + + break; + default: + // do nothing + break; + } + } + static void gpu_step( gb_t * const ctx, @@ -8809,9 +9021,7 @@ templates: // clear clock, set mode ctx->gpu.clock = 0; - ctx->gpu.mode = GPU_MODE_HBLANK; - - // TODO: trigger hblank interrupt + gpu_set_mode(ctx, GPU_MODE_HBLANK); } break; @@ -8819,27 +9029,33 @@ templates: if (ctx->gpu.clock > 204) { // clear clock, increment line ctx->gpu.clock = 0; - ctx->gpu.line++; + gpu_set_line(ctx, ctx->gpu.line + 1); if (ctx->gpu.line < 143) { - // process next scanline - ctx->gpu.mode = GPU_MODE_OAM; - } else { - // trigger vblank interrupt - mmu_wi(ctx, 1, true); + // TODO: process scanline - // set gpu mode - ctx->gpu.mode = GPU_MODE_VBLANK; + // set mode + gpu_set_mode(ctx, GPU_MODE_OAM); + } else { + // set mode + gpu_set_mode(ctx, GPU_MODE_VBLANK); } } break; case GPU_MODE_VBLANK: - if (ctx->gpu.clock > 4560) { - // reset line, clear clock, set mode - ctx->gpu.line = 0; + if (ctx->gpu.clock > 204) { + // clear clock, increment line ctx->gpu.clock = 0; - ctx->gpu.mode = GPU_MODE_OAM; + gpu_set_line(ctx, ctx->gpu.line + 1); + + + if (ctx->gpu.line > 153) { + // reset clock, reset line, set mode + ctx->gpu.clock = 0; + gpu_set_line(ctx, 0); + gpu_set_mode(ctx, GPU_MODE_OAM); + } } break; @@ -8862,7 +9078,7 @@ templates: } // trigger p1 interrupt - mmu_wi(ctx, (1 << 4), true); + mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & (1 << 4)); // TODO: handle HALT and STOP } @@ -8872,21 +9088,24 @@ templates: ) { if (ctx->cpu.ime) { // get interrupt vector (masked against enabled interrupts) - const uint8_t iv = mmu_rb(ctx, 0xFFFF) & mmu_ri(ctx, 0xFF); + const uint8_t iv = mmu_rb(ctx, PORT_IE) & mmu_rb(ctx, PORT_IF); for (uint8_t i = 0; i < 5; i++) { - const uint8_t m = (4 - i) ? (1 << (4 - i)) : 1; + const uint8_t mask = i ? (1 << i) : 1; // has this interrupt been triggered? - if (iv & m) { + if (iv & mask) { // disable ime ctx->cpu.ime = false; - // clear interrupt flag - mmu_wi(ctx, m, false); + // clear flag + mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) ^ mask); + + // jump to handler + rst(ctx, 0x0040 + (8 * i)); - // trigger interrupt - rst(ctx, 0x0040 + (8 * (4 - i))); + // FIXME: according to url below, this should take 5 cycles + // (src: http://gbdev.gg8.se/wiki/articles/Interrupts) // break out of for loop break; -- cgit v1.2.3