|
|
|
@@ -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, |
|
|
|
|