diff --git a/src/nes.c b/src/nes.c index 081028a..6c8ffd9 100644 --- a/src/nes.c +++ b/src/nes.c @@ -44,7 +44,7 @@ void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) { nes_ppu_write(&sys->ppu, nes_mem_ppu_start + addr, val); } else if (nes_ppu_dma_reg == addr) { -// printf("PPU: OAM DMA $%02x00 > $%02x\n", val, sys->ppu.oam_addr); + OAM_LOG("PPU: OAM DMA $%02x00 > $%02x\n", val, sys->ppu.oam_addr); for (int i = 0; i < nes_ppu_oam_size; ++i) { sys->ppu.oam[(uint8_t)(i + sys->ppu.oam_addr)] = nes_mem_read(sys, ((uint16_t)val << 8) + i); diff --git a/src/ppu.c b/src/ppu.c index e4e294c..27dae16 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -25,7 +25,7 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) { ppu->latch = 0; } else if (oam_reg_data == addr) { -// printf("PPU: OAM READ %02x > %02x\n", ppu->oam_addr, val); + OAM_LOG("PPU: OAM READ %02x > %02x\n", ppu->oam_addr, val); val = ppu->oam[ppu->oam_addr]; } else if (ppu_reg_data == addr) { @@ -44,7 +44,7 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) { ppu->data = ppu->chr_mem[ppu->addr]; } else if (ppu->addr < nes_ppu_mem_vram_start + nes_ppu_mem_vram_size) { -// printf("PPU: VRAM READ %04x > %02x\n", ppu->addr, val); + VRAM_LOG("PPU: VRAM READ %04x > %02x\n", ppu->addr, val); ppu->data = nes_vram_read( ppu->vram_map, ppu->addr - nes_ppu_mem_vram_start @@ -68,15 +68,15 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { // fprintf(stdout, "PPU: W-> $%04x %02x\n", addr, val); if (ppu_reg_ctrl == addr) { -// printf("PPU: CTRL %02x\n", val); + PPU_LOG("PPU: CTRL %02x\n", val); ppu->control = val; } else if (oam_reg_addr == addr) { -// printf("PPU: OAM ADDR %02x\n", val); + OAM_LOG("PPU: OAM ADDR %02x\n", val); ppu->oam_addr = val; } else if (oam_reg_data == addr) { -// printf("PPU: OAM %02x < %02x\n", ppu->oam_addr, val); + OAM_LOG("PPU: OAM %02x < %02x\n", ppu->oam_addr, val); ppu->oam[ppu->oam_addr++] = val; } else if (ppu_reg_mask == addr) { @@ -84,10 +84,10 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { } else if (ppu_reg_scroll == addr) { if (ppu->latch) { -// printf("PPU: Scroll Y %02x\n", val); + PPU_LOG("PPU: Scroll Y %02x\n", val); ppu->scroll_y = val; } else { -// printf("PPU: Scroll X %02x\n", val); + PPU_LOG("PPU: Scroll X %02x\n", val); ppu->scroll_x = val; } ppu->latch = !ppu->latch; @@ -96,7 +96,7 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { if (ppu->latch) { ppu->addr &= 0x3F00U; ppu->addr |= val; -// printf("PPU: VRAM ADDR %04x\n", ppu->addr); + VRAM_LOG("PPU: VRAM ADDR %04x\n", ppu->addr); // Take advantage of the quick split quirk ppu->scroll_x &= 0b00000111; @@ -138,7 +138,7 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { printf("!!! PPU: VRAM OOB: %04x\n", vram_addr); vram_addr &= (nes_ppu_mem_vram_size - 1); } -/* +#ifdef DEBUG_VRAM { int page = vram_addr >> 10; int loc = vram_addr & 0x3FFU; @@ -152,7 +152,7 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { loc - 960, val); } } -*/ +#endif // DEBUG_VRAM // printf("PPU: VRAM %04x < %02x\n", vram_addr, val); nes_vram_write(ppu->vram_map, vram_addr, val); } @@ -225,7 +225,8 @@ int nes_ppu_run(nes_ppu* ppu, int cycles) { if ( 0 != ppu->hit_line && ppu->scanline > ppu->hit_line && - ppu->cycle > ppu->hit_dot) { + ppu->cycle >= ppu->hit_dot) { +// if (!(ppu->status & ppu_Status_Hit)) printf("PPU: Hit @ %d, %d\n", ppu->hit_line + 1, ppu->hit_dot); ppu->status |= ppu_Status_Hit; } diff --git a/src/ppu.h b/src/ppu.h index 002d30b..8f38cfd 100644 --- a/src/ppu.h +++ b/src/ppu.h @@ -6,6 +6,30 @@ #include "vram.h" +#define DBG_LOG(...) printf(__VA_ARGS__) + +#ifdef DEBUG_OAM +#define OAM_LOG DBG_LOG +#else +#define OAM_LOG(...) +#endif +#ifdef DEBUG_VRAM +#define VRAM_LOG DBG_LOG +#else +#define VRAM_LOG(...) +#endif +#ifdef DEBUG_PPU +#define PPU_LOG DBG_LOG +#else +#define PPU_LOG(...) +#endif +#ifdef DEBUG_RENDER +#define REND_LOG DBG_LOG +#else +#define REND_LOG(...) +#endif + + #define nes_ppu_dots (341U) #define nes_ppu_prerender (1U) #define nes_ppu_height (240U) diff --git a/src/sdl_render.c b/src/sdl_render.c index 1a39eaf..5882afe 100644 --- a/src/sdl_render.c +++ b/src/sdl_render.c @@ -32,7 +32,7 @@ typedef struct { SDL_Renderer* renderer; SDL_Surface* background; SDL_Surface* background_line; - SDL_Surface* sprite8; + SDL_Surface* sprite; SDL_Surface* target; } sdl_render_data; @@ -104,10 +104,10 @@ static int sdl_render_init(nes_Renderer* rend) { } if (0 == status) { - data->sprite8 = SDL_CreateRGBSurfaceWithFormat( - 0, 8, 8, 8, SDL_PIXELFORMAT_INDEX8 + data->sprite = SDL_CreateRGBSurfaceWithFormat( + 0, 8, 16, 8, SDL_PIXELFORMAT_INDEX8 ); - if (NULL == data->sprite8) { + if (NULL == data->sprite) { fprintf(stderr, "SDL: Failed to create sprite\n"); SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); @@ -125,7 +125,7 @@ static int sdl_render_init(nes_Renderer* rend) { ); if (NULL == data->target) { fprintf(stderr, "SDL: Failed to create target\n"); - SDL_FreeSurface(data->sprite8); + SDL_FreeSurface(data->sprite); SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); @@ -140,9 +140,9 @@ static int sdl_render_init(nes_Renderer* rend) { nes_palette, 0U, 64U); SDL_SetColorKey(data->background, SDL_TRUE, 0xFFU); - SDL_SetPaletteColors(data->sprite8->format->palette, + SDL_SetPaletteColors(data->sprite->format->palette, nes_palette, 0U, 64U); - SDL_SetColorKey(data->sprite8, SDL_TRUE, 0xFFU); + SDL_SetColorKey(data->sprite, SDL_TRUE, 0xFFU); rend->data = &the_render_data; } @@ -153,7 +153,7 @@ static int sdl_render_init(nes_Renderer* rend) { static void sdl_render_done(nes_Renderer* rend) { sdl_render_data* data = (sdl_render_data*)rend->data; SDL_FreeSurface(data->target); - SDL_FreeSurface(data->sprite8); + SDL_FreeSurface(data->sprite); SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); @@ -161,16 +161,6 @@ static void sdl_render_done(nes_Renderer* rend) { SDL_Quit(); } -typedef enum { - Render_Mode_Sprite = 0b00000, - Render_Mode_Background = 0b00001, - Render_Mode_Behind = 0b00010, -// Render_Mode_Collide = 0b00100, - Render_Mode_Flip_X = 0b01000, - Render_Mode_Flip_Y = 0b10000, -} Render_Mode; - - static void render_bg_sprite(const nes_ppu* ppu, int index, const uint8_t* pal, void* loc, int pitch) { @@ -249,90 +239,75 @@ static void render_background_line(const nes_ppu* ppu, int line, ); } + +typedef struct { + uint8_t y; + uint8_t index; + uint8_t attr; + uint8_t x; +} oam_sprite; + +typedef enum { + oam_Attr_Pal_Mask = 0b00000011, + oam_Attr_Background = 0b00100000, + oam_Attr_Flip_X = 0b01000000, + oam_Attr_Flip_Y = 0b10000000, +} oam_Attribute; + +typedef enum { + oam_Index_Bank = 0b00000001, + oam_Index_Tile_Mask = 0b11111110, +} oam_Index; + + static void render_sprite(nes_ppu* ppu, int index, - const uint8_t* pal, Render_Mode mode, - void* loc, int pitch, - const void* back_loc, int back_pitch) { + const uint8_t* pal, uint8_t attr, + void* loc, int pitch) { uint8_t* sprite = &ppu->chr_mem[index * 16U]; uint8_t* dst_line = (uint8_t*)loc; - const uint8_t* back_line = (uint8_t*)back_loc; int dx = 1; - if (mode & Render_Mode_Flip_X) { + if (attr & oam_Attr_Flip_X) { dst_line += 7; - back_line += 7; dx = -dx; } - if (mode & Render_Mode_Flip_Y) { + if (attr & oam_Attr_Flip_Y) { dst_line += (7 * pitch); - back_line += (7 * back_pitch); pitch = -pitch; - back_pitch = -back_pitch; } for (int y = 8; y > 0; --y) { uint8_t lo = sprite[0U]; uint8_t hi = sprite[8U]; uint8_t* dst = dst_line; - const uint8_t* back = back_line; for (int x = 8; x > 0; --x) { int pal_idx = ( ((hi & 0x80) >> 6) | ((lo & 0x80) >> 7)); int nes_pal_idx = (pal_idx ? pal[pal_idx] : 0xFFU); - if ((mode & Render_Mode_Behind) && *back != 0xFFU) { - nes_pal_idx = 0xFFU; - } *dst = nes_pal_idx; dst += dx; - back += dx; hi <<= 1; lo <<= 1; } dst_line += pitch; - back_line += back_pitch; ++sprite; } } -typedef struct { - uint8_t y; - uint8_t index; - uint8_t attr; - uint8_t x; -} oam_sprite; - -typedef enum { - oam_Attr_Pal_Mask = 0b00000011, - oam_Attr_Background = 0b00100000, - oam_Attr_Flip_X = 0b01000000, - oam_Attr_Flip_Y = 0b10000000, -} oam_Attribute; - -typedef enum { - oam_Index_Bank = 0b00000001, - oam_Index_Tile_Mask = 0b11111110, -} oam_Index; - - -static const SDL_Rect sprite_rect = { - .x = 0, - .y = 0, - .w = 8, - .h = 8, -}; - static void render_sprites(nes_ppu* ppu, SDL_Surface* buffer, SDL_Surface* target, - const void* back, int back_pitch) { + int background) { int bank = (ppu->control & ppu_Control_Sprite_Bank) ? 0x100 : 0; const oam_sprite* sprites = (const oam_sprite*)ppu->oam; uint8_t* dst_origin = (uint8_t*)buffer->pixels; int pitch = buffer->pitch; - const uint8_t* back_origin = (uint8_t*)back; for ( int i_sprite = nes_ppu_oam_sprite_count - 1; i_sprite >= 0; --i_sprite) { const oam_sprite* sprite = &sprites[i_sprite]; + if ((sprite->attr & oam_Attr_Background) ^ background) { + continue; + } if ( !(ppu->mask & ppu_Mask_Left_Sprite) && sprite->x < 8) { continue; @@ -340,30 +315,42 @@ static void render_sprites(nes_ppu* ppu, int y = (sprite->y + 1); if (y >= nes_ppu_render_h) continue; uint8_t* dst = dst_origin; - int back_offset = sprite->x + (y * back_pitch); - const uint8_t* dst_back = back_offset + back_origin; - // TODO: Support 8x16 sprites - int index = bank + sprite->index; + int index = sprite->index; + if (ppu->control & ppu_Control_Sprite_Size) { + bank = (index & 1) ? 0x100 : 0; + index &= 0xFEU; + } + index += bank; int pal_idx = (sprite->attr & oam_Attr_Pal_Mask); const uint8_t* pal = &ppu->palette[16 + (pal_idx * 4)]; - Render_Mode mode = (sprite->attr & oam_Attr_Background) ? - Render_Mode_Behind : - Render_Mode_Sprite; -// if (i_sprite == 0) mode |= Render_Mode_Collide; - if (sprite->attr & oam_Attr_Flip_X) { - mode |= Render_Mode_Flip_X; - } - if (sprite->attr & oam_Attr_Flip_Y) { - mode |= Render_Mode_Flip_Y; + if (ppu->control & ppu_Control_Sprite_Size) { + if (sprite->attr & oam_Attr_Flip_Y) index++; + render_sprite(ppu, index, pal, sprite->attr, + dst, pitch); + dst += pitch * 8; + if (sprite->attr & oam_Attr_Flip_Y) { + index--; + } else { + index++; + } } - render_sprite(ppu, index, pal, mode, dst, pitch, - dst_back, back_pitch); + render_sprite(ppu, index, pal, sprite->attr, + dst, pitch); + + SDL_Rect sprite_rect = { + .x = 0, + .y = 0, + .w = 8, + .h = (ppu->control & ppu_Control_Sprite_Size) ? + 16 : 8, + }; SDL_Rect target_rect = { .x = sprite->x, .y = y, .w = 8, - .h = 8, + .h = (ppu->control & ppu_Control_Sprite_Size) ? + 16 : 8, }; SDL_BlitSurface(buffer, &sprite_rect, target, &target_rect); @@ -510,7 +497,7 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { sdl_render_data* data = (sdl_render_data*)rend->data; if (ppu->scanline < nes_ppu_prerender) { -// printf("Scanline %3d -> Prerender\n", ppu->scanline); + REND_LOG("Scanline %3d -> Prerender\n", ppu->scanline); // Emulate the happy part of the backdrop override quirk int pal_idx = (ppu->addr >= nes_ppu_mem_pal_start) ? @@ -524,7 +511,7 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { nes_ppu_height) { int line = (ppu->scanline - (int)nes_ppu_prerender) / 8; -// printf("Scanline %3d -> Line %2d @ X %d\n", ppu->scanline, line, ppu->scroll_x); + REND_LOG("Scanline %3d -> Line %2d @ X %d\n", ppu->scanline, line, ppu->scroll_x); // TODO: Only re-render if VRAM/scroll changes? if (ppu->mask & ppu_Mask_Back) { @@ -532,8 +519,12 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { } } else { -// printf("Scanline %3d -> Postrender\n", ppu->scanline); + REND_LOG("Scanline %3d -> Postrender\n", ppu->scanline); + if (ppu->mask & ppu_Mask_Sprite) { + render_sprites(ppu, data->sprite, data->target, + oam_Attr_Background); + } if (ppu->mask & ppu_Mask_Back) { // Render final partial line if (0 != (ppu->scroll_y % 8)) { @@ -544,9 +535,7 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { data->target, NULL); } if (ppu->mask & ppu_Mask_Sprite) { - render_sprites(ppu, data->sprite8, data->target, - data->background->pixels, - data->background->pitch); + render_sprites(ppu, data->sprite, data->target, 0); } SDL_Texture* texture = SDL_CreateTextureFromSurface( data->renderer, data->target