Browse Source

Add 8x16 sprite support; create debug macros

master
Nathaniel Walizer 1 year ago
parent
commit
36ad6f4e19
4 changed files with 110 additions and 96 deletions
  1. +1
    -1
      src/nes.c
  2. +12
    -11
      src/ppu.c
  3. +24
    -0
      src/ppu.h
  4. +73
    -84
      src/sdl_render.c

+ 1
- 1
src/nes.c View File

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


+ 12
- 11
src/ppu.c View File

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



+ 24
- 0
src/ppu.h View File

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


+ 73
- 84
src/sdl_render.c View File

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


Loading…
Cancel
Save