浏览代码

Add vertical scrolling

- Still some lingering issues with vertical page mapping
master
父节点
当前提交
5160cfa44f
共有 2 个文件被更改,包括 146 次插入86 次删除
  1. +1
    -1
      Makefile
  2. +145
    -85
      src/sdl_render.c

+ 1
- 1
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


+ 145
- 85
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,


正在加载...
取消
保存