diff --git a/Makefile b/Makefile index 99d05b0..d0d5735 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = gcc LD = $(CC) -CFLAGS = -Og -g -Wall -Werror -Wshadow -I.. #-DE6502_DEBUG +CFLAGS = -g -Wall -Werror -Wshadow -I.. #-DE6502_DEBUG LDFLAGS = OBJDIR = obj diff --git a/src/sdl_render.c b/src/sdl_render.c index 7f2dfa7..dd8fbb9 100644 --- a/src/sdl_render.c +++ b/src/sdl_render.c @@ -31,6 +31,7 @@ typedef struct { SDL_Window* window; SDL_Renderer* renderer; SDL_Surface* background; + SDL_Surface* background_line; SDL_Surface* sprite8; SDL_Surface* target; } sdl_render_data; @@ -73,12 +74,28 @@ static int sdl_render_init(nes_Renderer* rend) { if (0 == status) { data->background = SDL_CreateRGBSurfaceWithFormat( - 0, nes_ppu_render_w + 8, nes_ppu_render_h + 8, + 0, nes_ppu_render_w, nes_ppu_render_h, 8, SDL_PIXELFORMAT_INDEX8 ); if (NULL == data->background) { - fprintf(stderr, "SDL: Failed to create background 0\n"); + fprintf(stderr, "SDL: Failed to create background\n"); + SDL_DestroyRenderer(data->renderer); + SDL_DestroyWindow(data->window); + SDL_Quit(); + status = -1; + } + } + + if (0 == status) { + data->background_line = SDL_CreateRGBSurfaceWithFormat( + 0, nes_ppu_render_w + 8, 8, + 8, SDL_PIXELFORMAT_INDEX8 + ); + + if (NULL == data->background_line) { + fprintf(stderr, "SDL: Failed to create block buffer\n"); + SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); SDL_DestroyWindow(data->window); SDL_Quit(); @@ -92,6 +109,7 @@ static int sdl_render_init(nes_Renderer* rend) { ); if (NULL == data->sprite8) { fprintf(stderr, "SDL: Failed to create sprite\n"); + SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); SDL_DestroyWindow(data->window); @@ -102,12 +120,13 @@ static int sdl_render_init(nes_Renderer* rend) { if (0 == status) { data->target = SDL_CreateRGBSurfaceWithFormat( - 0U, nes_ppu_render_w, nes_ppu_render_h, 24U, - SDL_PIXELFORMAT_RGB888 + 0U, nes_ppu_render_w, nes_ppu_render_h, + 24U, SDL_PIXELFORMAT_RGB888 ); if (NULL == data->target) { fprintf(stderr, "SDL: Failed to create target\n"); SDL_FreeSurface(data->sprite8); + SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); SDL_DestroyWindow(data->window); @@ -135,6 +154,7 @@ 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->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); SDL_DestroyWindow(data->window); @@ -201,12 +221,16 @@ static void render_background_area(const nes_ppu* ppu, int page, static void render_background_line(const nes_ppu* ppu, int line, void* buffer, int pitch) { - // TODO: Handle vertical scrolling // TODO: Handle column 0 flag - buffer += line * pitch * 8U; int page = (ppu->control & ppu_Control_Nametable_Mask); int x = ppu->scroll_x / 8; + line += ppu->scroll_y / 8; + + if (line >= nes_ppu_blocks_h) { + line -= nes_ppu_blocks_h; + page ^= 0b10; + } // Left render_background_area( @@ -287,46 +311,6 @@ typedef enum { oam_Index_Tile_Mask = 0b11111110, } oam_Index; -// Check sprite (0 only) collision on a scanline -// This assumes that we've verified that this sprite -// intersects with this scanline. -// Scanline is 0-239 from inside the rendering window -// (though we should never see this called with 0). -static int eval_sprite_line(const nes_ppu* ppu, int line, - const oam_sprite* sprite, - const uint8_t* chr, - const uint8_t* back) { - int hit_pos = -1; - - int y = line - (sprite->y + 1); - - if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y; - uint8_t lo = chr[0U + y]; - uint8_t hi = chr[8U + y]; - - back += sprite->x; - for (int x = sprite->x; x < nes_ppu_render_w; ++x) { - int pal_idx = (sprite->attr & oam_Attr_Flip_X) ? - (((hi & 1) << 1) | (lo & 1)) : - ( ((hi & 0x80) >> 6) | - ((lo & 0x80) >> 7)); - if (pal_idx && *back != 0xFFU) { - hit_pos = x; - break; - } - ++back; - if (sprite->attr & oam_Attr_Flip_X) { - hi >>= 1; - lo >>= 1; - } else { - hi <<= 1; - lo <<= 1; - } - } - - return hit_pos; -} - static const SDL_Rect sprite_rect = { .x = 0, @@ -385,19 +369,59 @@ static void render_sprites(nes_ppu* ppu, } } -static void update_sprite_hit(nes_ppu* ppu, +// Check sprite (0 only) collision on a scanline +// This assumes that we've verified that this sprite +// intersects with this scanline. +// Scanline is 0-239 from inside the rendering window +// (though we should never see this called with 0). +static int eval_sprite_line(const nes_ppu* ppu, int line, + const oam_sprite* sprite, + const uint8_t* chr, + const uint8_t* back) { + int hit_pos = -1; + + int y = line - (sprite->y + 1); + + if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y; + uint8_t lo = chr[0U + y]; + uint8_t hi = chr[8U + y]; + + back += sprite->x; + for (int x = sprite->x; x < nes_ppu_render_w; ++x) { + int pal_idx = (sprite->attr & oam_Attr_Flip_X) ? + (((hi & 1) << 1) | (lo & 1)) : + ( ((hi & 0x80) >> 6) | + ((lo & 0x80) >> 7)); + if (pal_idx && *back != 0xFFU) { + hit_pos = x; + break; + } + ++back; + if (sprite->attr & oam_Attr_Flip_X) { + hi >>= 1; + lo >>= 1; + } else { + hi <<= 1; + lo <<= 1; + } + } + + return hit_pos; +} + +static void update_sprite_hit(nes_ppu* ppu, int block_line, const void* back_line, int back_pitch) { const oam_sprite* sprite = (oam_sprite*)ppu->oam; - int block_line = (ppu->scanline - nes_ppu_prerender) / 8U; int x_fine = ppu->scroll_x % 8; + int y_fine = ppu->scroll_x % 8; int index = sprite->index; if (ppu->control & ppu_Control_Sprite_Bank) { index += 0x100U; } const uint8_t* chr = &ppu->chr_mem[index * 16U]; int render_line = block_line * 8U; - int start_y = (sprite->y + 1) - render_line; + int start_y = (sprite->y + 1) + y_fine - render_line; int end_y = start_y + 8; if (start_y < 8 && end_y > 0) { if (start_y < 0) start_y = 0; @@ -411,7 +435,7 @@ static void update_sprite_hit(nes_ppu* ppu, sprite, chr, back ); if (hit >= 0) { - ppu->hit_line = y + render_line; + ppu->hit_line = y - y_fine + render_line; ppu->hit_dot = hit; break; } @@ -420,6 +444,66 @@ static void update_sprite_hit(nes_ppu* ppu, } } +static void render_block_line(nes_ppu* ppu, int line, + sdl_render_data* data) { + // Render single line of blocks + + render_background_line(ppu, line, + data->background_line->pixels, + data->background_line->pitch); + + int x_fine = ppu->scroll_x % 8; + int y_fine = ppu->scroll_y % 8; + + // Check for Sprite 0 Hit + if ( 0 >= ppu->hit_line && + (ppu->mask & ppu_Mask_Sprite)) { + update_sprite_hit(ppu, line, + data->background_line->pixels, + data->background_line->pitch); + } + + // Copy line onto full background + + const uint8_t* src = data->background_line->pixels + + x_fine; + int start_y = ((line * 8U) - y_fine); + int end_y = start_y + 8; + if (start_y < 0) { + src -= (start_y * data->background_line->pitch); + start_y = 0; + } + uint8_t* dst = data->background->pixels + + (start_y * data->background->pitch); + + for (int y = start_y; y < end_y; ++y) { + if ((void*)dst >= data->background->pixels) { + memcpy(dst, src, nes_ppu_render_w); + } + src += data->background_line->pitch; + dst += data->background->pitch; + } + +/* + SDL_Rect back_rect = { + .x = x_fine, + .y = 0, + .w = nes_ppu_render_w, + .h = 8U, + }; + + SDL_Rect render_rect = { + .x = 0, + .y = (line * 8U) - y_fine, + .w = nes_ppu_render_w, + .h = 8U, + }; + + SDL_BlitSurface(data->background_line, &back_rect, + data->background, &render_rect); +*/ +} + static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { int status = 0; sdl_render_data* data = (sdl_render_data*)rend->data; @@ -441,47 +525,23 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { // printf("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) { - // TODO: Only re-render if VRAM/scroll changes - render_background_line(ppu, line, - data->background->pixels, - data->background->pitch); - - // TODO: Y scroll support - - int x_fine = ppu->scroll_x % 8; - - // Check for Sprite 0 Hit - if ( 0 >= ppu->hit_line && - (ppu->mask & ppu_Mask_Sprite)) { - update_sprite_hit(ppu, - data->background->pixels, - data->background->pitch); - } - - // Gotta render it now while scroll is set - - SDL_Rect back_rect = { - .x = x_fine, - .y = line * 8U, - .w = nes_ppu_render_w, - .h = 8U, - }; - - SDL_Rect render_rect = { - .x = 0, - .y = line * 8U, - .w = nes_ppu_render_w, - .h = 8U, - }; - - SDL_BlitSurface(data->background, &back_rect, - data->target, &render_rect); + render_block_line(ppu, line, data); } } else { // printf("Scanline %3d -> Postrender\n", ppu->scanline); + if (ppu->mask & ppu_Mask_Back) { + // Render final partial line + if (0 != (ppu->scroll_y % 8)) { + render_block_line(ppu, nes_ppu_blocks_h, data); + } + + SDL_BlitSurface(data->background, NULL, + data->target, NULL); + } if (ppu->mask & ppu_Mask_Sprite) { render_sprites(ppu, data->sprite8, data->target, data->background->pixels,