#include #include "render.h" #include "ppu.h" #include "mapper.h" 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}, {0x00,0x2F,0x66}, {0x00,0x00,0x00}, {0x05,0x05,0x05}, {0x05,0x05,0x05}, {0xC8,0xC8,0xC8}, {0x00,0x59,0xFF}, {0x44,0x3C,0xFF}, {0xB7,0x33,0xCC}, {0xFF,0x33,0xAA}, {0xFF,0x37,0x5E}, {0xFF,0x37,0x1A}, {0xD5,0x4B,0x00}, {0xC4,0x62,0x00}, {0x3C,0x7B,0x00}, {0x1E,0x84,0x15}, {0x00,0x95,0x66}, {0x00,0x84,0xC4}, {0x11,0x11,0x11}, {0x09,0x09,0x09}, {0x09,0x09,0x09}, {0xFF,0xFF,0xFF}, {0x00,0x95,0xFF}, {0x6F,0x84,0xFF}, {0xD5,0x6F,0xFF}, {0xFF,0x77,0xCC}, {0xFF,0x6F,0x99}, {0xFF,0x7B,0x59}, {0xFF,0x91,0x5F}, {0xFF,0xA2,0x33}, {0xA6,0xBF,0x00}, {0x51,0xD9,0x6A}, {0x4D,0xD5,0xAE}, {0x00,0xD9,0xFF}, {0x66,0x66,0x66}, {0x0D,0x0D,0x0D}, {0x0D,0x0D,0x0D}, {0xFF,0xFF,0xFF}, {0x84,0xBF,0xFF}, {0xBB,0xBB,0xFF}, {0xD0,0xBB,0xFF}, {0xFF,0xBF,0xEA}, {0xFF,0xBF,0xCC}, {0xFF,0xC4,0xB7}, {0xFF,0xCC,0xAE}, {0xFF,0xD9,0xA2}, {0xCC,0xE1,0x99}, {0xAE,0xEE,0xB7}, {0xAA,0xF7,0xEE}, {0xB3,0xEE,0xFF}, {0xDD,0xDD,0xDD}, {0x11,0x11,0x11}, {0x11,0x11,0x11} }; static inline uint8_t* chr_mem(const nes_ppu* ppu, uint16_t addr) { return ppu->mapper->chr_addr(ppu->mapper, addr); } typedef struct { SDL_Window* window; SDL_Renderer* renderer; SDL_Surface* background; SDL_Surface* background_line; SDL_Surface* foreground; SDL_Surface* sprite; SDL_Surface* target; } sdl_render_data; static sdl_render_data the_render_data = {0}; static int sdl_render_init(nes_Renderer* rend) { sdl_render_data* data = &the_render_data; int status = SDL_Init(SDL_INIT_VIDEO); if (0 != status) { fprintf(stderr, "SDL: Failed to initialize\n"); } else { data->window = SDL_CreateWindow( "NESe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, nes_ppu_scan_w * 4, nes_ppu_scan_h * 4, 0 ); if (NULL == data->window) { fprintf(stderr, "SDL: Failed to create window\n"); SDL_Quit(); status = -1; } } if (0 == status) { data->renderer = SDL_CreateRenderer(data->window, -1, 0); if (NULL == data->renderer) { fprintf(stderr, "SDL: Failed to create renderer\n"); SDL_DestroyWindow(data->window); SDL_Quit(); status = -1; } } if (0 == status) { data->background = SDL_CreateRGBSurfaceWithFormat( 0, nes_ppu_render_w, nes_ppu_render_h, 8, SDL_PIXELFORMAT_INDEX8 ); 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->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(); status = -1; } } if (0 == status) { data->foreground = SDL_CreateRGBSurfaceWithFormat( 0, nes_ppu_render_w, nes_ppu_render_h, 8, SDL_PIXELFORMAT_INDEX8 ); if (NULL == data->foreground) { fprintf(stderr, "SDL: Failed to create foreground\n"); SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); SDL_DestroyWindow(data->window); SDL_Quit(); status = -1; } } if (0 == status) { data->sprite = SDL_CreateRGBSurfaceWithFormat( 0, 8, 16, 8, SDL_PIXELFORMAT_INDEX8 ); if (NULL == data->sprite) { fprintf(stderr, "SDL: Failed to create sprite\n"); SDL_FreeSurface(data->foreground); SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); SDL_DestroyWindow(data->window); SDL_Quit(); status = -1; } } if (0 == status) { data->target = SDL_CreateRGBSurfaceWithFormat( 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->sprite); SDL_FreeSurface(data->foreground); SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); SDL_DestroyWindow(data->window); SDL_Quit(); status = -1; } } if (0 == status) { SDL_SetPaletteColors(data->background->format->palette, nes_palette, 0U, 64U); SDL_SetColorKey(data->background, SDL_TRUE, 0xFFU); SDL_SetPaletteColors(data->foreground->format->palette, nes_palette, 0U, 64U); SDL_SetColorKey(data->foreground, SDL_TRUE, 0xFFU); SDL_SetPaletteColors(data->sprite->format->palette, nes_palette, 0U, 64U); SDL_SetColorKey(data->sprite, SDL_TRUE, 0xFFU); rend->data = &the_render_data; } return status; } static void sdl_render_done(nes_Renderer* rend) { sdl_render_data* data = (sdl_render_data*)rend->data; SDL_FreeSurface(data->target); SDL_FreeSurface(data->sprite); SDL_FreeSurface(data->foreground); SDL_FreeSurface(data->background_line); SDL_FreeSurface(data->background); SDL_DestroyRenderer(data->renderer); SDL_DestroyWindow(data->window); SDL_Quit(); } static inline void render_sprite_line( const nes_ppu* ppu, int index, int y, const uint8_t* pal, uint8_t* dst, int start, int end) { uint8_t* sprite = chr_mem(ppu, index * 16U); uint8_t lo = sprite[0U + y] << start; uint8_t hi = sprite[8U + y] << start; for (int x = start; x < end; ++x) { int pal_idx = (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7)); if (pal_idx) *dst = pal[pal_idx]; dst++; hi <<= 1; lo <<= 1; } } static inline void render_sprite_line_flip( const nes_ppu* ppu, int index, int y, const uint8_t* pal, uint8_t* dst, int start, int end) { uint8_t* sprite = chr_mem(ppu, index * 16U); uint8_t lo = sprite[0U + y] >> start; uint8_t hi = sprite[8U + y] >> start; for (int x = start; x < end; ++x) { int pal_idx = (((hi & 1) << 1) | (lo & 1)); if (pal_idx) *dst = pal[pal_idx]; dst++; hi >>= 1; lo >>= 1; } } static inline void render_bg_sprite_line( const nes_ppu* ppu, int index, int y, const uint8_t* pal, uint8_t* dst, int start, int end) { uint8_t* sprite = chr_mem(ppu, index * 16U); uint8_t lo = sprite[0U + y] << (start % 8); uint8_t hi = sprite[8U + y] << (start % 8); for (int x = start; x < end; ++x) { int pal_idx = (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7)); *dst = (pal_idx ? pal[pal_idx] : 0xFFU); dst++; hi <<= 1; lo <<= 1; } } /* static void render_bg_sprite(const nes_ppu* ppu, int index, const uint8_t* pal, void* loc, int pitch) { uint8_t* sprite = chr_mem(ppu, index * 16U); uint8_t* dst_line = (uint8_t*)loc; for (int y = 8; y > 0; --y) { uint8_t lo = sprite[0U]; uint8_t hi = sprite[8U]; uint8_t* dst = dst_line; for (int x = 8; x > 0; --x) { int pal_idx = ( ((hi & 0x80) >> 6) | ((lo & 0x80) >> 7)); *dst++ = (pal_idx ? pal[pal_idx] : 0xFFU); hi <<= 1; lo <<= 1; } dst_line += pitch; ++sprite; } } static void render_background_area(const nes_ppu* ppu, int page, void* buffer, int pitch, int xs, int ys, int w, int h) { int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0; const uint8_t* index_line = nes_map_vram_addr(ppu->mapper, page << 10); const uint8_t* attrs = index_line + 960U; index_line += xs + (ys * nes_ppu_blocks_w); uint8_t* dst_line = (uint8_t*)buffer; for (int y = ys; y < h + ys; ++y) { uint8_t* dst = dst_line; const uint8_t* index = index_line; for (int x = xs; x < w + xs; ++x) { 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_bg_sprite(ppu, bank + *index, pal, dst, pitch); ++index; dst += 8; } dst_line += pitch * 8; index_line += nes_ppu_blocks_w; } } */ static inline void render_bg_scanline_area( const nes_ppu* ppu, int page, uint8_t* dst, int x, int y, int w) { int block_x = x / 8; int line_end = x + w; int block_y = y / 8; y = y % 8; int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0; const uint8_t* indexes = nes_map_vram_addr(ppu->mapper, page << 10); const uint8_t* attrs = indexes + 960U; const uint8_t* index = indexes + (block_y * nes_ppu_blocks_w) + block_x; while (x < line_end) { int attr_idx = ((block_y / 4) * 8) + (block_x / 4); int shift = 2 * ((block_y & 0b10) | ((block_x & 0b10) >> 1)); int pal_idx = (attrs[attr_idx] >> shift) & 3; const uint8_t* pal = &ppu->palette[pal_idx * 4]; int end = (x + 8) & ~7; if (end > line_end) end = line_end; render_bg_sprite_line(ppu, bank + *index, y, pal, dst, x, end); ++index; ++block_x; dst += (end - x); x = end; } } static void render_bg_scanline(const nes_ppu* ppu,/* int scanline,*/ uint8_t* dst) { int page = (ppu->control & ppu_Control_Nametable_Mask); int x = ppu->scroll_x; int y = ppu->scroll_y /*+ scanline*/; /* if (y >= nes_ppu_render_h) { y -= nes_ppu_render_h; page ^= 0b10; } */ int w = (nes_ppu_render_w - x); if (!(ppu->mask & ppu_Mask_Left_Back)) { // Handle column 0 flag - need to fill with transparency memset(dst, 0xFFU, 8); w -= 8; x += 8; dst += 8; } render_bg_scanline_area(ppu, page, dst, x, y, w); render_bg_scanline_area(ppu, page ^ 1, dst + w, 0, y, nes_ppu_render_w - w); } /* static void render_background_line(const nes_ppu* ppu, int line, void* buffer, int pitch) { // TODO: Handle column 0 flag 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( ppu, page, buffer, pitch, x, line, nes_ppu_blocks_w - x, 1 ); // Right buffer += (nes_ppu_blocks_w - x) * 8U; render_background_area( ppu, page ^ 1, buffer, pitch, 0, line, 1U + x, 1 ); } static void render_sprite(nes_ppu* ppu, int index, const uint8_t* pal, uint8_t attr, void* loc, int pitch) { uint8_t* sprite = chr_mem(ppu, index * 16U); uint8_t* dst_line = (uint8_t*)loc; int dx = 1; if (attr & oam_Attr_Flip_X) { dst_line += 7; dx = -dx; } if (attr & oam_Attr_Flip_Y) { dst_line += (7 * pitch); pitch = -pitch; } for (int y = 8; y > 0; --y) { uint8_t lo = sprite[0U]; uint8_t hi = sprite[8U]; uint8_t* dst = dst_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); *dst = nes_pal_idx; dst += dx; hi <<= 1; lo <<= 1; } dst_line += pitch; ++sprite; } } */ static void render_line_sprites(nes_ppu* ppu, uint8_t* dst_line, int scanline, int background, const oam_sprite* sprites, int n_sprites) { for (int i_sprite = n_sprites - 1; i_sprite >= 0; --i_sprite) { const oam_sprite* sprite = &sprites[i_sprite]; if ((sprite->attr & oam_Attr_Background) ^ background) { continue; } int index = sprite->index; int bank = (ppu->control & ppu_Control_Sprite_Bank) ? 0x100 : 0; if (ppu->control & ppu_Control_Sprite_Size) { bank = (index & 1) ? 0x100 : 0; index &= 0xFEU; } index += bank; int y = scanline - (sprite->y + 1); if (ppu->control & ppu_Control_Sprite_Size) { if (y >= 8) { index ^= 1; y -= 8; } if (sprite->attr & oam_Attr_Flip_Y) { index ^= 1; } } int pal_idx = (sprite->attr & oam_Attr_Pal_Mask); const uint8_t* pal = &ppu->palette[16 + (pal_idx * 4)]; if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y; int end = nes_ppu_render_w - sprite->x; if (end > 8) end = 8; int start = 0; if ( !(ppu->mask & ppu_Mask_Left_Sprite) && sprite->x < 8) { start = 8 - sprite->x; } if (sprite->attr & oam_Attr_Flip_X) { render_sprite_line_flip(ppu, index, y, pal, dst_line + sprite->x + start, start, end); } else { render_sprite_line(ppu, index, y, pal, dst_line + sprite->x + start, start, end); } } } /* static void render_sprites(nes_ppu* ppu, SDL_Surface* buffer, SDL_Surface* target, 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; 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; } int y = (sprite->y + 1); if (y >= nes_ppu_render_h) continue; uint8_t* dst = dst_origin; 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)]; 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, 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 = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8, }; SDL_BlitSurface(buffer, &sprite_rect, target, &target_rect); } } */ // 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 y, const oam_sprite* sprite, const uint8_t* chr, const uint8_t* back) { int hit_pos = -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; int end = nes_ppu_render_w; if (end > sprite->x + 8) end = sprite->x + 8; for (int x = sprite->x; x < end; ++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; } #if 0 static void update_sprite_hit(nes_ppu* ppu, int block_line, const void* back_pixels, int back_pitch) { const oam_sprite* sprite = (oam_sprite*)ppu->oam; 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 = chr_mem(ppu, index * 16U); int render_line = block_line * 8U; 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; if (end_y > 8) end_y = 8; int hit = -1; const uint8_t* back = (uint8_t*)back_pixels + x_fine; back += (render_line + start_y) * back_pitch; for (int y = start_y; y < end_y; ++y) { hit = eval_sprite_line( ppu, render_line + y - y_fine, sprite, chr, back ); if (hit >= 0) { ppu->hit_line = y - y_fine + render_line; ppu->hit_dot = hit; break; } back += back_pitch; } } } 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); */ } #endif static void update_scanline_hit(nes_ppu* ppu, uint8_t* back_line, int scanline) { const oam_sprite* sprite = &ppu->oam[0]; int bank = (ppu->control & ppu_Control_Sprite_Bank) ? 0x100 : 0; if ( !(ppu->mask & ppu_Mask_Left_Sprite) && sprite->x < 8) { return; } int y_pos = (sprite->y + 1); int y = scanline - y_pos; if (0 > y) return; int h = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8; if (y >= h) return; int index = sprite->index; if (ppu->control & ppu_Control_Sprite_Size) { bank = (index & 1) ? 0x100 : 0; index &= 0xFEU; } index += bank; if (ppu->control & ppu_Control_Sprite_Size) { if (y >= 8) { index ^= 1; y -= 8; } if (sprite->attr & oam_Attr_Flip_Y) { index ^= 1; } } const uint8_t* chr = chr_mem(ppu, index * 16U); int hit = eval_sprite_line(ppu, y, sprite, chr, back_line); if (hit >= 0) { REND_LOG("Upcoming hit @ %d, %d\n", scanline + 1, hit); REND_LOG("(Currently @ %d, %d)\n", ppu->scanline, ppu->cycle); ppu->hit_line = scanline; ppu->hit_dot = hit; } } static int select_line_sprites(const nes_ppu* ppu, int scanline, oam_sprite* sprites, int max) { int n_sprites = 0; for ( int i_sprite = 0; i_sprite < nes_ppu_oam_sprite_count && n_sprites < max; ++i_sprite) { const oam_sprite* sprite = &ppu->oam[i_sprite]; int y_pos = (sprite->y + 1); int y = scanline - y_pos; if (0 > y) continue; int h = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8; if (y >= h) continue; *sprites = *sprite; ++sprites; ++n_sprites; } return n_sprites; } static void render_scanline(nes_ppu* ppu, int line, sdl_render_data* data) { SDL_Rect dst_rect = { .x = 0, .y = line, .w = nes_ppu_render_w, .h = 1, }; if (line >= 0) { // Emulate the happy part of the backdrop override quirk int pal_idx = (ppu->addr >= nes_ppu_mem_pal_start) ? (ppu->addr & (nes_ppu_mem_pal_size - 1)) : 0; SDL_Color ext = nes_palette[ppu->palette[pal_idx]]; SDL_FillRect(data->target, &dst_rect, ((int)ext.r << 16) | ((int)ext.g << 8) | ext.b); } if (!(ppu->mask & (ppu_Mask_Sprite | ppu_Mask_Back))) { // Do nothing if BOTH are disabled. return; } SDL_Rect src_rect = { .x = 0, .y = 0, .w = nes_ppu_render_w, .h = 1, }; uint8_t* foreground = data->foreground->pixels; uint8_t* background = data->background->pixels; // We check for hits if EITHER layer is enabled. render_bg_scanline(ppu, background); if (ppu->hit_line <= 0) { update_scanline_hit(ppu, background, line); } if (line >= 0) { oam_sprite line_sprites[8] = {0}; int n_sprites = select_line_sprites(ppu, line, line_sprites, 8); if (ppu->mask & ppu_Mask_Sprite) { memset(foreground, 0xFFU, nes_ppu_render_w); render_line_sprites(ppu, foreground, line, oam_Attr_Background, line_sprites, n_sprites); SDL_BlitSurface(data->foreground, &src_rect, data->target, &dst_rect); } if (ppu->mask & ppu_Mask_Back) { SDL_BlitSurface(data->background, &src_rect, data->target, &dst_rect); } if (ppu->mask & ppu_Mask_Sprite) { memset(foreground, 0xFFU, nes_ppu_render_w); render_line_sprites(ppu, foreground, line, 0, line_sprites, n_sprites); SDL_BlitSurface(data->foreground, &src_rect, data->target, &dst_rect); } } /*if (line + 1 < nes_ppu_height)*/ { ppu->scroll_y++; if (ppu->scroll_y >= nes_ppu_render_h) { ppu->scroll_y -= nes_ppu_render_h; ppu->control ^= 0b10; } /* // We check for hits if EITHER layer is enabled. render_bg_scanline(ppu, background); if (ppu->hit_line <= 0) { update_scanline_hit(ppu, background, line + 1); } */ } } static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { int status = 0; sdl_render_data* data = (sdl_render_data*)rend->data; if (ppu->scanline < nes_ppu_prerender) { REND_LOG("Scanline %3d -> Prerender\n", ppu->scanline); /* memset(data->foreground->pixels, 0xFFU, data->foreground->pitch * data->foreground->h); if (ppu->mask & ppu_Mask_Sprite) { render_sprites(ppu, data->sprite, data->target, oam_Attr_Background); render_sprites(ppu, data->sprite, data->foreground, 0); } */ /* int line = ppu->scanline - (int)nes_ppu_prerender; render_scanline(ppu, line, data); */ } else if (ppu->scanline < nes_ppu_prerender + nes_ppu_height) { /* int line = (ppu->scanline - (int)nes_ppu_prerender) / 8; 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) { render_block_line(ppu, line, data); } */ REND_LOG("Scanline %3d : N %d X %d Y %d\n", ppu->scanline, ppu->control & ppu_Control_Nametable_Mask, ppu->scroll_x, ppu->scroll_y); int line = ppu->scanline - (int)nes_ppu_prerender; render_scanline(ppu, line, data); } else { 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)) { if (ppu->mask & ppu_Mask_Sprite) { // TODO: Render background sprites that start this block } if (ppu->mask & ppu_Mask_Back) { render_block_line(ppu, nes_ppu_blocks_h, data); } if (ppu->mask & ppu_Mask_Sprite) { // TODO: Render foreground sprites that end this block } } SDL_BlitSurface(data->background, NULL, data->target, NULL); } if (ppu->mask & ppu_Mask_Sprite) { render_sprites(ppu, data->sprite, data->target, 0); SDL_BlitSurface(data->foreground, NULL, data->target, NULL); } */ SDL_Texture* texture = SDL_CreateTextureFromSurface( data->renderer, data->target ); SDL_RenderCopy(data->renderer, texture, NULL, NULL); SDL_RenderPresent(data->renderer); SDL_DestroyTexture(texture); status = 1; } return status; } nes_Renderer sdl_renderer = { .init = sdl_render_init, .done = sdl_render_done, .render = sdl_render, };