|
|
|
@@ -3,14 +3,15 @@ |
|
|
|
#include "render.h" |
|
|
|
#include "ppu.h" |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
typedef struct { |
|
|
|
uint8_t r; |
|
|
|
uint8_t g; |
|
|
|
uint8_t b; |
|
|
|
} __attribute__ (( packed )) sPal; |
|
|
|
*/ |
|
|
|
|
|
|
|
static sPal nes_palette[64] = { |
|
|
|
static SDL_Color nes_palette[64] = { |
|
|
|
{0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6}, |
|
|
|
{0xBB,0x00,0x6A}, {0xB7,0x00,0x1E}, {0xB3,0x00,0x00}, {0x91,0x26,0x00}, |
|
|
|
{0x7B,0x2B,0x00}, {0x00,0x3E,0x00}, {0x00,0x48,0x0D}, {0x00,0x3C,0x22}, |
|
|
|
@@ -34,10 +35,11 @@ static sPal nes_palette[64] = { |
|
|
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
SDL_Surface* surface; |
|
|
|
SDL_Window* window; |
|
|
|
SDL_Renderer* renderer; |
|
|
|
SDL_Texture* texture; |
|
|
|
SDL_Surface* background; |
|
|
|
SDL_Surface* sprite8; |
|
|
|
SDL_Surface* target; |
|
|
|
} sdl_render_data; |
|
|
|
|
|
|
|
static sdl_render_data the_render_data = {0}; |
|
|
|
@@ -59,8 +61,8 @@ static int sdl_render_init(nes_Renderer* rend) { |
|
|
|
"NESe", |
|
|
|
SDL_WINDOWPOS_UNDEFINED, |
|
|
|
SDL_WINDOWPOS_UNDEFINED, |
|
|
|
nes_ppu_render_w * 4, |
|
|
|
nes_ppu_render_h * 4, |
|
|
|
nes_ppu_scan_w * 4, |
|
|
|
nes_ppu_scan_h * 4, |
|
|
|
0 |
|
|
|
); |
|
|
|
if (NULL == data->window) { |
|
|
|
@@ -81,6 +83,11 @@ static int sdl_render_init(nes_Renderer* rend) { |
|
|
|
} |
|
|
|
|
|
|
|
if (0 == status) { |
|
|
|
data->background = SDL_CreateRGBSurfaceWithFormat( |
|
|
|
0, nes_ppu_render_w, nes_ppu_render_h, |
|
|
|
8, SDL_PIXELFORMAT_INDEX8 |
|
|
|
); |
|
|
|
/* |
|
|
|
data->texture = SDL_CreateTexture( |
|
|
|
the_render_data.renderer, |
|
|
|
SDL_PIXELFORMAT_RGB24, |
|
|
|
@@ -88,8 +95,33 @@ static int sdl_render_init(nes_Renderer* rend) { |
|
|
|
nes_ppu_render_w, |
|
|
|
nes_ppu_render_h |
|
|
|
); |
|
|
|
if (NULL == data->texture) { |
|
|
|
fprintf(stderr, "SDL: Failed to create texture\n"); |
|
|
|
*/ |
|
|
|
if (NULL == data->background) { |
|
|
|
fprintf(stderr, "SDL: Failed to create background\n"); |
|
|
|
SDL_DestroyRenderer(data->renderer); |
|
|
|
SDL_DestroyWindow(data->window); |
|
|
|
SDL_Quit(); |
|
|
|
status = -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (0 == status) { |
|
|
|
data->sprite8 = SDL_CreateRGBSurfaceWithFormat( |
|
|
|
0, 8, 8, 8, SDL_PIXELFORMAT_INDEX8 |
|
|
|
); |
|
|
|
SDL_SetPaletteColors(data->sprite8->format->palette, |
|
|
|
nes_palette, 0U, 64U); |
|
|
|
SDL_SetColorKey(data->sprite8, SDL_TRUE, 0xFFU); |
|
|
|
} |
|
|
|
|
|
|
|
if (0 == status) { |
|
|
|
data->target = SDL_CreateRGBSurfaceWithFormat( |
|
|
|
0U, nes_ppu_scan_w, nes_ppu_scan_h, 24U, |
|
|
|
SDL_PIXELFORMAT_RGB888 |
|
|
|
); |
|
|
|
if (NULL == data->target) { |
|
|
|
fprintf(stderr, "SDL: Failed to create target\n"); |
|
|
|
SDL_FreeSurface(data->background); |
|
|
|
SDL_DestroyRenderer(data->renderer); |
|
|
|
SDL_DestroyWindow(data->window); |
|
|
|
SDL_Quit(); |
|
|
|
@@ -98,6 +130,9 @@ static int sdl_render_init(nes_Renderer* rend) { |
|
|
|
} |
|
|
|
|
|
|
|
if (0 == status) { |
|
|
|
SDL_SetPaletteColors(data->background->format->palette, |
|
|
|
nes_palette, 0U, 64U); |
|
|
|
SDL_SetColorKey(data->background, SDL_TRUE, 0xFFU); |
|
|
|
SDL_SetEventFilter(filter, NULL); |
|
|
|
rend->data = &the_render_data; |
|
|
|
} |
|
|
|
@@ -107,66 +142,213 @@ 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_DestroyTexture(data->texture); |
|
|
|
SDL_FreeSurface(data->target); |
|
|
|
SDL_FreeSurface(data->background); |
|
|
|
SDL_DestroyRenderer(data->renderer); |
|
|
|
SDL_DestroyWindow(data->window); |
|
|
|
SDL_Quit(); |
|
|
|
} |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
typedef enum { |
|
|
|
Render_Mode_Sprite = 0b000, |
|
|
|
Render_Mode_Background = 0b001, |
|
|
|
Render_Mode_Behind = 0b010, |
|
|
|
Render_Mode_Collide = 0b100, |
|
|
|
} Render_Mode; |
|
|
|
|
|
|
|
} oam_sprite; |
|
|
|
|
|
|
|
static void render_sprite(const nes_ppu* ppu, int index, |
|
|
|
void* loc, int pitch) { |
|
|
|
static int 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) { |
|
|
|
int hit_pos = -1; |
|
|
|
uint8_t* sprite = &ppu->chr_mem[index * 16U]; |
|
|
|
uint8_t* dst_line = (uint8_t*)loc; |
|
|
|
const uint8_t* back_line = (uint8_t*)back_loc; |
|
|
|
for (int y = 8; y > 0; --y) { |
|
|
|
uint8_t hi = sprite[0U]; |
|
|
|
uint8_t lo = sprite[8U]; |
|
|
|
sPal* dst = (sPal*)dst_line; |
|
|
|
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) << 1) | !!(lo & 0x80); |
|
|
|
*dst = nes_palette[16 + (pal_idx * 4)]; |
|
|
|
++dst; |
|
|
|
int pal_idx = ((hi & 0x80) >> 6) | ((lo & 0x80) >> 7); |
|
|
|
int nes_pal_idx = (pal_idx ? pal[pal_idx] : 0xFFU); |
|
|
|
if ( hit_pos < 0 && |
|
|
|
(ppu->mask & ppu_Mask_Back) && |
|
|
|
(mode & Render_Mode_Collide) && |
|
|
|
nes_pal_idx != 0xFFU && |
|
|
|
*back != 0xFFU ) { |
|
|
|
hit_pos = (8 - x) + (8 * (8 - y)); |
|
|
|
} |
|
|
|
if ((mode & Render_Mode_Behind) && *back != 0xFFU) { |
|
|
|
nes_pal_idx = 0xFFU; |
|
|
|
} |
|
|
|
*dst++ = nes_pal_idx; |
|
|
|
++back; |
|
|
|
hi <<= 1; |
|
|
|
lo <<= 1; |
|
|
|
} |
|
|
|
dst_line += pitch; |
|
|
|
back_line += back_pitch; |
|
|
|
++sprite; |
|
|
|
} |
|
|
|
return hit_pos; |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Don't re-render background unless VRAM has changed |
|
|
|
|
|
|
|
static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { |
|
|
|
sdl_render_data* data = (sdl_render_data*)rend->data; |
|
|
|
|
|
|
|
// TODO |
|
|
|
void* buffer = NULL; |
|
|
|
int pitch = 0; |
|
|
|
SDL_LockTexture(data->texture, NULL, &buffer, &pitch); |
|
|
|
static void render_background(nes_ppu* ppu, |
|
|
|
void* buffer, int pitch) { |
|
|
|
int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0; |
|
|
|
uint8_t* index = &ppu->vram[0]; |
|
|
|
uint8_t* dst_line = |
|
|
|
(uint8_t*)buffer + |
|
|
|
((nes_ppu_render_w - (8U * nes_ppu_blocks_w)) / 2); |
|
|
|
// TODO: Support beyond nametable 0 |
|
|
|
const uint8_t* index = &ppu->vram[0U]; |
|
|
|
const uint8_t* attrs = &ppu->vram[960U]; |
|
|
|
uint8_t* dst_line = (uint8_t*)buffer; |
|
|
|
for (int y = 0; y < nes_ppu_blocks_h; ++y) { |
|
|
|
uint8_t* dst = dst_line; |
|
|
|
for (int x = 0; x < nes_ppu_blocks_w; ++x) { |
|
|
|
render_sprite( |
|
|
|
ppu, bank + *index, dst, pitch |
|
|
|
); |
|
|
|
if (x > 0 || (ppu->mask & ppu_Mask_Left_Back)) { |
|
|
|
int attr_idx = ((y / 4) * 8) + (x / 4); |
|
|
|
int shift = 2 * ((y & 0b10) | ((x & 0b10) >> 1)); |
|
|
|
int pal_idx = (attrs[attr_idx] >> shift) & 3; |
|
|
|
const uint8_t* pal = &ppu->palette[pal_idx * 4]; |
|
|
|
render_sprite(ppu, bank + *index, pal, |
|
|
|
Render_Mode_Background, |
|
|
|
dst, pitch, NULL, 0); |
|
|
|
} |
|
|
|
++index; |
|
|
|
dst += 3 * 8; |
|
|
|
dst += 8; |
|
|
|
} |
|
|
|
dst_line += pitch * 8; |
|
|
|
} |
|
|
|
SDL_UnlockTexture(data->texture); |
|
|
|
} |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
uint8_t y; |
|
|
|
uint8_t index; |
|
|
|
uint8_t attr; |
|
|
|
uint8_t x; |
|
|
|
} oam_sprite; |
|
|
|
|
|
|
|
SDL_RenderCopy(data->renderer, data->texture, NULL, NULL); |
|
|
|
SDL_RenderPresent(data->renderer); |
|
|
|
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 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 ( !(ppu->mask & ppu_Mask_Left_Sprite) && |
|
|
|
sprite->x < 8) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
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; |
|
|
|
// TODO: Support mirroring |
|
|
|
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; |
|
|
|
int hit_pos = render_sprite(ppu, index, pal, mode, |
|
|
|
dst, pitch, |
|
|
|
dst_back, back_pitch); |
|
|
|
if (hit_pos >= 0) { |
|
|
|
ppu->hit_line = y + (hit_pos / 8); |
|
|
|
ppu->hit_dot = sprite->x + (hit_pos % 8); |
|
|
|
} |
|
|
|
|
|
|
|
SDL_Rect target_rect = { |
|
|
|
.x = sprite->x + |
|
|
|
((nes_ppu_scan_w - nes_ppu_render_w) / 2), |
|
|
|
.y = y, |
|
|
|
.w = 8, |
|
|
|
.h = 8, |
|
|
|
}; |
|
|
|
SDL_BlitSurface(buffer, &sprite_rect, |
|
|
|
target, &target_rect); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static SDL_Rect render_rect = { |
|
|
|
.x = (nes_ppu_scan_w - nes_ppu_render_w) / 2, |
|
|
|
.y = 0, |
|
|
|
.w = nes_ppu_render_w, |
|
|
|
.h = nes_ppu_render_h, |
|
|
|
}; |
|
|
|
|
|
|
|
static SDL_Rect back_rect = { |
|
|
|
.x = 0, |
|
|
|
.y = 0, |
|
|
|
.w = nes_ppu_render_w, |
|
|
|
.h = nes_ppu_render_h, |
|
|
|
}; |
|
|
|
|
|
|
|
static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { |
|
|
|
sdl_render_data* data = (sdl_render_data*)rend->data; |
|
|
|
|
|
|
|
if (ppu->mask & (ppu_Mask_Back | ppu_Mask_Sprite)) { |
|
|
|
SDL_FillRect(data->target, NULL, 0x808080/*nes_palette[0]*/); |
|
|
|
if (ppu->mask & ppu_Mask_Back) { |
|
|
|
render_background(ppu, data->background->pixels, |
|
|
|
data->background->pitch); |
|
|
|
SDL_BlitSurface(data->background, &back_rect, |
|
|
|
data->target, &render_rect); |
|
|
|
} |
|
|
|
if (ppu->mask & ppu_Mask_Sprite) { |
|
|
|
render_sprites(ppu, data->sprite8, data->target, |
|
|
|
data->background->pixels, |
|
|
|
data->background->pitch); |
|
|
|
} |
|
|
|
SDL_Texture* texture = SDL_CreateTextureFromSurface( |
|
|
|
data->renderer, data->target |
|
|
|
); |
|
|
|
SDL_RenderCopy(data->renderer, texture, NULL, NULL); |
|
|
|
SDL_RenderPresent(data->renderer); |
|
|
|
SDL_DestroyTexture(texture); |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
// HACK: THIS IS TERRIBLE |
|
|
|
static uint32_t last_ms = 0; |
|
|
|
uint32_t now_ms = SDL_GetTicks(); |
|
|
|
if (last_ms != 0) { |
|
|
|
int delay = (last_ms + (1000 / 35)) - now_ms; |
|
|
|
if (delay > 0) SDL_Delay(delay); |
|
|
|
// else fprintf(stderr, "Delta %d\n", now_ms - last_ms); |
|
|
|
} |
|
|
|
last_ms = now_ms; |
|
|
|
*/ |
|
|
|
|
|
|
|
SDL_Event event = {0}; |
|
|
|
return (1 == SDL_PollEvent(&event) && event.type == SDL_QUIT) ? |
|
|
|
|