diff --git a/include/gb.h b/include/gb.h index 9a981cd..96a26f1 100644 --- a/include/gb.h +++ b/include/gb.h @@ -127,6 +127,7 @@ enum { #define GB_PPU_LCDC_TILE_BASE 0x10 #define GB_PPU_LCDC_WIN_ENABLE 0x20 #define GB_PPU_LCDC_WIN_TILE_MAP 0x40 +#define GB_PPU_LCDC_ENABLE 0x80 enum { GB_PPU_STAT_MODE_HBLANK = 0, @@ -169,6 +170,8 @@ typedef struct pixel_fifo_fetcher_t { //flags1 //window x condition triggered #define GB_PIXEL_FIFO_WXC_TRIGGERED 0x8 +//indicates that LCDC_ENABLE bit was fliped +#define GB_PIXEL_FIFO_LCDC_SWITCH 0x10 #define GB_OAM_PALLET 0x10 #define GB_OAM_FLIP_X 0x20 diff --git a/include/ragb_sdl.h b/include/ragb_sdl.h index f196f8e..e0a660b 100644 --- a/include/ragb_sdl.h +++ b/include/ragb_sdl.h @@ -8,14 +8,16 @@ typedef struct gb_pixel_buffer_t { SDL_Texture *texture; ut8 *buf; //buffer containing pixeldata ut32 color[4]; //rgb colors + ut32 clear_color; ut16 w; //x size in pixel ut16 h; //y size in pixel // ut16 xsf; //x scale factor for projecting onto the texture // ut16 ysf; //y scale factor for projecting onto the texture } GBPixBuf; -GBPixBuf *gb_pix_buf_new(SDL_Renderer *renderer, ut16 w, ut16 h); +GBPixBuf *gb_pix_buf_new(SDL_Renderer *renderer, ut16 w, ut16 h, ut32 clear_color); void gb_pix_buf_free(GBPixBuf *pb); void gb_pix_buf_set_pixel(GBPixBuf *pb, ut16 x, ut16 y, ut8 pixval); void gb_pix_buf_set_color(GBPixBuf *pb, ut8 color_idx, ut32 rgb); +void gb_pix_buf_clear(GBPixBuf *pb); #endif diff --git a/io/ppu.c b/io/ppu.c index 0fd8baa..b115dad 100644 --- a/io/ppu.c +++ b/io/ppu.c @@ -53,9 +53,15 @@ static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) { for (i = 0; i < len; i++) { switch (seek) { case GB_PPU_STAT: - ppu->buf[GB_PPU_STAT] = (buf[i] & 0xf8) | (ppu->buf[GB_PPU_STAT] & 0x7); + ppu->buf[GB_PPU_STAT] = (buf[i] & 0xf8) | + (ppu->buf[GB_PPU_STAT] & 0x7); case GB_PPU_LY: break; + case GB_PPU_LCDC: + if ((ppu->buf[GB_PPU_LCDC] & GB_PPU_LCDC_ENABLE) != + (buf[i] & GB_PPU_LCDC_ENABLE)) { + ppu->fifo.flags1 |= GB_PIXEL_FIFO_LCDC_SWITCH; + } default: ppu->buf[seek] = buf[i]; break; @@ -114,7 +120,8 @@ GBPPU *gb_ppu_open (RIO *io, SDL_Renderer *renderer) { return NULL; } ppu->reg_fd = desc->fd; - ppu->pixbuf = gb_pix_buf_new (renderer, 160, 144); + //TODO: use proper clear color + ppu->pixbuf = gb_pix_buf_new (renderer, 160, 144, 0xabcdef); if (!ppu->pixbuf) { r_io_desc_close (desc); r_io_fd_close (io, ppu->vram_fd); @@ -480,14 +487,70 @@ static ut32 gb_ppu_render_continue (GB *gb, ut32 cycles) { } static ut32 gb_ppu_hblank_continue (GB *gb, ut32 cycles) { + if (cycles >= gb->ppu->fifo.remaining_cycles) { + gb->ppu->buf[GB_PPU_LY]++; + gb->ppu->buf[GB_PPU_STAT] &= ~GB_PPU_STAT_MODE_MASK; + if (gb->ppu->buf[GB_PPU_LY] == 144) { + //launch vblank + gb->ppu->buf[GB_PPU_STAT] |= GB_PPU_STAT_MODE_VBLANK; + return cycles - gb->ppu->fifo.remaining_cycles; + } + //launch oam scan + gb->ppu->buf[GB_PPU_STAT] |= GB_PPU_STAT_MODE_OAM_SCAN; + return cycles - gb->ppu->fifo.remaining_cycles; + } + gb->ppu->fifo.remaining_cycles -= cycles; return 0; } static ut32 gb_ppu_vblank_continue (GB *gb, ut32 cycles) { + const ut32 m = gb->ppu->fifo.remaining_cycles % 456; + if (m && m < cycles) { + gb->ppu->buf[GB_PPU_LY]++; + if (gb->ppu->buf[GB_PPU_LY] == 156) { + gb->ppu->buf[GB_PPU_STAT] &= ~GB_PPU_STAT_MODE_MASK; + gb->ppu->buf[GB_PPU_STAT] |= GB_PPU_STAT_MODE_OAM_SCAN; + return cycles - gb->ppu->fifo.remaining_cycles; + } + } + gb->ppu->fifo.remaining_cycles -= cycles; return 0; } void gb_ppu_continue (GB *gb, ut32 cycles) { + if (gb->ppu->fifo.flags1 & GB_PIXEL_FIFO_LCDC_SWITCH) { + gb->ppu->fifo.flags1 ^= GB_PIXEL_FIFO_LCDC_SWITCH; + RIOMap *map = r_io_map_get (gb->io, gb->ppu->oam_mapid); + if (gb->ppu->buf[GB_PPU_LCDC] & GB_PPU_LCDC_ENABLE) { + //lcd was switched on + //launch oam scan + gb->ppu->buf[GB_PPU_LY] = 0; + //disable oam access + map->perm = 0; + gb->ppu->buf[GB_PPU_STAT] &= ~GB_PPU_STAT_MODE_MASK; + gb->ppu->buf[GB_PPU_STAT] |= GB_PPU_STAT_MODE_OAM_SCAN; + if (gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_INTR_OAM) { + gb_interrupts_request (gb, GB_INTERRUPT_STAT); + } + } else { + //lcd was switched off + //enable oam and vram access + map->perm = R_PERM_RWX; + map = r_io_map_get (gb->io, gb->ppu->vram_mapid); + map->perm = R_PERM_RWX; + //clear screen + gb_pix_buf_clear (gb->ppu->pixbuf); + return; + } + } + if (!(gb->ppu->buf[GB_PPU_LCDC] & GB_PPU_LCDC_ENABLE)) { + return; + } + if (gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_INTR_LYC) { + if (gb->ppu->buf[GB_PPU_LY] == gb->ppu->buf[GB_PPU_LYC]) { + gb_interrupts_request (gb, GB_INTERRUPT_STAT); + } + } while (cycles) { switch (gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_MODE_MASK) { case GB_PPU_STAT_MODE_OAM_SCAN: @@ -498,16 +561,47 @@ void gb_ppu_continue (GB *gb, ut32 cycles) { break; case GB_PPU_STAT_MODE_RENDER: cycles = gb_ppu_render_continue (gb, cycles); + if ((gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_MODE_MASK) == GB_PPU_STAT_MODE_HBLANK) { + RIOMap *map = r_io_map_get (gb->io, gb->ppu->oam_mapid); + map->perm = R_PERM_RWX; + map = r_io_map_get (gb->io, gb->ppu->vram_mapid); + map->perm = R_PERM_RWX; + } break; case GB_PPU_STAT_MODE_HBLANK: cycles = gb_ppu_hblank_continue (gb, cycles); + if ((gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_MODE_MASK) == GB_PPU_STAT_MODE_OAM_SCAN) { + RIOMap *oam = r_io_map_get (gb->io, gb->ppu->oam_mapid); + oam->perm = 0; + if (gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_INTR_OAM) { + gb_interrupts_request (gb, GB_INTERRUPT_STAT); + } + } else if ((gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_MODE_MASK) == GB_PPU_STAT_MODE_VBLANK) { + if (gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_INTR_VBLANK) { + gb_interrupts_request (gb, GB_INTERRUPT_STAT); + } + gb_interrupts_request (gb, GB_INTERRUPT_VBLANK); + gb->ppu->fifo.remaining_cycles = 4560; + } break; case GB_PPU_STAT_MODE_VBLANK: cycles = gb_ppu_vblank_continue (gb, cycles); + if ((gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_MODE_MASK) == GB_PPU_STAT_MODE_OAM_SCAN) { + gb->ppu->buf[GB_PPU_LY] = 0; + RIOMap *oam = r_io_map_get (gb->io, gb->ppu->oam_mapid); + oam->perm = 0; + if (gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_INTR_OAM) { + gb_interrupts_request (gb, GB_INTERRUPT_STAT); + } + } break; } } - //TODO + if (gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_INTR_LYC) { + if (gb->ppu->buf[GB_PPU_LY] == gb->ppu->buf[GB_PPU_LYC]) { + gb_interrupts_request (gb, GB_INTERRUPT_STAT); + } + } } void gb_ppu_close (GBPPU *ppu, RIO *io) { diff --git a/sdl/pixbuf.c b/sdl/pixbuf.c index 3c62273..c7fac5d 100644 --- a/sdl/pixbuf.c +++ b/sdl/pixbuf.c @@ -2,7 +2,7 @@ #include #include -GBPixBuf *gb_pix_buf_new (SDL_Renderer *renderer, ut16 w, ut16 h) { +GBPixBuf *gb_pix_buf_new (SDL_Renderer *renderer, ut16 w, ut16 h, ut32 clear_color) { if (!renderer || !w || !h) { return NULL; } @@ -26,6 +26,7 @@ GBPixBuf *gb_pix_buf_new (SDL_Renderer *renderer, ut16 w, ut16 h) { } pb->w = w; pb->h = h; + pb->clear_color = clear_color; return pb; } @@ -62,3 +63,15 @@ void gb_pix_buf_set_color (GBPixBuf *pb, ut8 color_idx, ut32 rgb) { } pb->color[color_idx & 0x3] = rgb; } + +void gb_pix_buf_clear (GBPixBuf *pb) { + if (!pb) { + return; + } + SDL_Texture *target = SDL_GetRenderTarget (pb->renderer); + SDL_SetRenderTarget (pb->renderer, pb->texture); + SDL_SetRenderDrawColor (pb->renderer, (pb->clear_color >> 16) & 0xff, + (pb->clear_color >> 8) & 0xff, pb->clear_color & 0xff, 0xff); + SDL_RenderClear (pb->renderer); + SDL_SetRenderTarget (pb->renderer, target); +}