summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Duncan <pabs@pablotron.org>2018-06-18 15:02:51 -0400
committerPaul Duncan <pabs@pablotron.org>2018-06-18 15:02:51 -0400
commit10e21b988e934f122d83c7aa4a56349e2025bbfb (patch)
tree1cbe9e750cc7645c6cbbca7259d8e22a889b5227
parentb48f7bfb063de855881f5b3f66a4a2f8aab648b4 (diff)
downloadgb-c-10e21b988e934f122d83c7aa4a56349e2025bbfb.tar.bz2
gb-c-10e21b988e934f122d83c7aa4a56349e2025bbfb.zip
add mmu_rp/mmu_wp, add gpu ports
-rw-r--r--ops.yaml371
1 files 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;
@@ -8787,6 +8951,54 @@ templates:
}
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,
const uint16_t clock
@@ -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;