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