diff --git a/include/gb.h b/include/gb.h index a0f7eb5..68bdce9 100644 --- a/include/gb.h +++ b/include/gb.h @@ -92,9 +92,12 @@ enum { GB_PPU_N_REGS, }; -#define GB_PPU_LCDC_BIG_OBJ 0x4 -#define GB_PPU_LCDC_BG_TILE_MAP 0x8 -#define GB_PPU_LCDC_TILE_BASE 0x10 +#define GB_PPU_LCDC_BGW_ENABLE 0x1 +#define GB_PPU_LCDC_OBJ_ENABLE 0x2 +#define GB_PPU_LCDC_BIG_OBJ 0x4 +#define GB_PPU_LCDC_BG_TILE_MAP 0x8 +#define GB_PPU_LCDC_TILE_BASE 0x10 +#define GB_PPU_LCDC_WIN_ENABLE 0x20 #define GB_PPU_LCDC_WIN_TILE_MAP 0x40 enum { @@ -123,12 +126,18 @@ typedef struct pixel_fifo_fetcher_t { ut8 state_ctr; } PixelFifoFetcher; + +// flags #define GB_PIXEL_FIFO_FETCH_OBJECT 0x10 #define GB_PIXEL_FIFO_FETCH_WINDOW 0x20 #define GB_PIXEL_FIFO_FETCH_READY 0x40 //specifies if scx & 0x7 and wy are copied into fifo #define GB_PIXEL_FIFO_REG_DATA_LOADED 0x80 +//flags1 +//window x condition triggered +#define GB_PIXEL_FIFO_WXC_TRIGGERED 0x8 + #define GB_OAM_PALLET 0x10 #define GB_OAM_FLIP_X 0x20 #define GB_OAM_FLIP_Y 0x40 @@ -144,7 +153,7 @@ typedef struct pixel_fifo_t { ut64 data; PixelFifoFetcher fetcher[2]; ut32 remaining_cycles; - ut32 obj; //object + ut32 obj; //object union { ut8 shift_out; //lower nibble is sourch info, pallet, color // 0b....spcc @@ -159,7 +168,10 @@ obj p1 - 11 ut8 n_fpixel; //number of pixel that are currently in the fifo ut8 x; ut8 wy; - ut8 dx; //discard x + union { + ut8 dx; //discard x + ut8 flags1; + }; } PixelFifo; typedef struct gb_dmg_ppu_t { diff --git a/io/ppu.c b/io/ppu.c index e33e765..aba5133 100644 --- a/io/ppu.c +++ b/io/ppu.c @@ -197,18 +197,30 @@ static void read_obj_data (GBPPU *ppu, RIO *io, ut8 *tile, int oam_fd) { } } -static void gb_ppu_pixel_fifo_merge_opixels (PixelFifo *pxf) { +static void gb_ppu_pixel_fifo_merge_opixels (PixelFifo *pxf, bool priority) { ut32 pixels = pxf->data >> 32; - ut32 newpixels = 0; + ut64 newpixels = 0; ut32 i; for (i = 0; i < 8; i++) { - if (!(pixels & (0x3 << 28))) { //check for transparency - newpixels = (newpixels << 4) | - ((pxf->fetcher[1].fetched & (0xf << (i << 2))) >> (i << 2)); - pixels = pixels << 4; + const ut8 bg_pixel = (pixels & (0xf << ((7 - i) << 2))) >> ((7 - i) << 2); + const ut8 fg_pixel = (pxf->fetcher[1].fetched & (0xf << ((7 - i) << 2))) >> ((7 - i) << 2); + newpixels <<= 4; + //background always wins if obj is transparent or bg is obj + if ((!(fg_pixel & 0x3)) || ((bg_pixel & 0xc) == 0x4) || (bg_pixel & 0x8)) { + newpixels |= bg_pixel; continue; } + if ((bg_pixel & 0xc) == 0x4) { + //bg is window + //TODO: check for window priority + } + if (!priority && (bg_pixel & 0x3)) { + newpixels |= bg_pixel; + continue; + } + newpixels |= fg_pixel; } + pxf->data = (pxf->data & 0xffffffff) | (newpixels << 32); } static void gb_pixel_fifo_fetch_continue (GB *gb) { @@ -351,16 +363,22 @@ static void shit_out_pixel (GBPPU *ppu) { ppu->fifo.shift_out = (ppu->fifo.shift_out & 0xf0) | ppu->fifo.data >> 60; ppu->fifo.data = ppu->fifo.data << 4; ppu->fifo.n_fpixel--; - if (ppu->fifo.dx) { + if (ppu->fifo.dx & 0x7) { //discard pixel - ppu->fifo.dx--; + ppu->fifo.dx = (ppu->fifo.dx & 0xf8) | ((ppu->fifo.dx & 0x7) - 1); return; } ut8 color; + //bg pallet if (!(ppu->fifo.shift_out & 0x08)) { - //bg pallet - color = (ppu->buf[GB_PPU_BGP] & (0x3 << ((ppu->fifo.shift_out & 0x3) << 1))) >> - ((ppu->fifo.shift_out & 0x3) << 1); + //check if window and bg are enabled + if (ppu->buf[GB_PPU_LCDC] & GB_PPU_LCDC_BGW_ENABLE) { + color = (ppu->buf[GB_PPU_BGP] & (0x3 << ((ppu->fifo.shift_out & 0x3) << 1))) >> + ((ppu->fifo.shift_out & 0x3) << 1); + } else { + //white + color = 0; + } } else { const ut8 pal = !!(ppu->fifo.shift_out & 0x04); color = (ppu->buf[GB_PPU_OBP0 + pal] & (0x3 << ((ppu->fifo.shift_out & 0x3) << 1))) >> @@ -390,6 +408,29 @@ static ut32 gb_ppu_render_continue (GB *gb, ut32 cycles) { fifo->flags |= GB_PIXEL_FIFO_REG_DATA_LOADED; } while (cycles) { +#if 1 + if (fifo->flags & GB_PIXEL_FIFO_FETCH_WINDOW) { + //this is probably insufficient, + //but the actual behaviour of deactivating the window mid fetch is not really documented + if (!(gb->ppu->buf[GB_PPU_LCDC] & GB_PPU_LCDC_WIN_ENABLE)) { + fifo->flags ^= GB_PIXEL_FIFO_FETCH_WINDOW; + } + } else { + if (gb->ppu->buf[GB_PPU_LCDC] & GB_PPU_LCDC_WIN_ENABLE) { + if ((fifo->wy <= gb->ppu->buf[GB_PPU_LY]) && + ((fifo->flags1 & GB_PIXEL_FIFO_WXC_TRIGGERED) || + (gb->ppu->buf[GB_PPU_WX] >= fifo->x))) { + //when window starts at 0, we don't need to discard pixels anyway + fifo->flags1 = GB_PIXEL_FIFO_WXC_TRIGGERED; + fifo->flags |= GB_PIXEL_FIFO_FETCH_WINDOW; + //clear the fetcher + fifo->fetcher[0] = (const PixelFifoFetcher) {0}; + //clear the pipe + fifo->n_fpixel = 0; + } + } + } +#endif if (fifo->n_fpixel < 9) { //initial phase on each line //also when window starts? @@ -422,7 +463,9 @@ static ut32 gb_ppu_render_continue (GB *gb, ut32 cycles) { } else { gb_pixel_fifo_fetch_continue (gb); if (fifo->flags & GB_PIXEL_FIFO_FETCH_READY) { - gb_ppu_pixel_fifo_merge_opixels (fifo); + if (gb->ppu->buf[GB_PPU_LCDC] & GB_PPU_LCDC_OBJ_ENABLE) { + gb_ppu_pixel_fifo_merge_opixels (fifo, !!(fifo->obj & GB_FIFO_OAM_PRIORITY)); + } fifo->flags ^= GB_PIXEL_FIFO_FETCH_OBJECT | GB_PIXEL_FIFO_FETCH_READY; } }