From 0078a19a9eb9a50d66c450c8f094f20fab18b2ae Mon Sep 17 00:00:00 2001 From: Nathaniel Walizer Date: Sat, 30 Nov 2024 10:43:20 -0800 Subject: [PATCH] Baby's first background --- Makefile | 2 +- src/nese.c | 4 +- src/ppu.c | 2 +- src/ppu.h | 9 ++-- src/sdl_render.c | 120 +++++++++++++++++++++++++++++++++++++---------- 5 files changed, 106 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 6a0e335..3156af0 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ BINDIR = bin # nese TARGET_1 = nese -LDLIBS_1 = -lSDL +LDLIBS_1 = -lSDL2 SRC_SRCS_1 = nese.c ines.c nes.c ppu.c sdl_render.c diff --git a/src/nese.c b/src/nese.c index d063221..d9825f8 100644 --- a/src/nese.c +++ b/src/nese.c @@ -54,7 +54,7 @@ int main(int argc, char* argv[]) { nes_init(&sys); nes_reset(&sys); - int total_cycles = 0; + uint64_t total_cycles = 0; int last_frame_rendered = -1; for (int i = 0; i < n_loops && status == 0; ++i) { int run = 0; @@ -78,7 +78,7 @@ int main(int argc, char* argv[]) { float ms_run = ( total_cycles * 1000. * nes_clock_master_den) / nes_clock_master_num; - fprintf(stdout, "Ran %f ms, %d master cycles (%s)\n", + fprintf(stdout, "Ran %f ms, %lu master cycles (%s)\n", ms_run, total_cycles, status == 0 ? "OK" : "Halted"); diff --git a/src/ppu.c b/src/ppu.c index 04eaebc..4a19bde 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -156,7 +156,7 @@ int nes_ppu_run(nes_ppu* ppu, int cycles) { ppu->status &= ~ppu_Status_VBlank; ppu->scanline = 0; ppu->frame++; - // TODO: Render callback? + // TODO: Render callback if vblank was previously set } else if (ppu->scanline >= nes_ppu_prerender + nes_ppu_height + nes_ppu_postrender) { diff --git a/src/ppu.h b/src/ppu.h index 91ccca2..2cfbe82 100644 --- a/src/ppu.h +++ b/src/ppu.h @@ -10,9 +10,12 @@ #define nes_ppu_postrender (1U) #define nes_ppu_vblank (20U) -#define nes_ppu_render_w (320U) +#define nes_ppu_render_w (320U) // Includes full overscan #define nes_ppu_render_h nes_ppu_height +#define nes_ppu_blocks_w (32U) +#define nes_ppu_blocks_h (30U) + #define nes_ppu_mem_size (0x4000U) #define nes_ppu_mem_pal_start (0x3F00U) #define nes_ppu_mem_pal_size (0x0020U) @@ -24,8 +27,8 @@ typedef enum { ppu_Control_Nametable_Mask = 0b00000011, ppu_Control_VRAM_Inc = 0b00000100, - ppu_Control_Sprite_Addr = 0b00001000, - ppu_Control_Back_Addr = 0b00010000, + ppu_Control_Sprite_Bank = 0b00001000, + ppu_Control_Back_Bank = 0b00010000, ppu_Control_Sprite_Size = 0b00100000, ppu_Control_Master = 0b01000000, ppu_Control_VBlank = 0b10000000, diff --git a/src/sdl_render.c b/src/sdl_render.c index 56b0b57..3c41cf0 100644 --- a/src/sdl_render.c +++ b/src/sdl_render.c @@ -1,4 +1,4 @@ -#include +#include #include "render.h" #include "ppu.h" @@ -10,7 +10,7 @@ typedef struct { uint8_t b; } __attribute__ (( packed )) sPal; -static struct sPal nes_palette[64] = { +static sPal 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}, @@ -35,42 +35,70 @@ static struct sPal nes_palette[64] = { typedef struct { SDL_Surface* surface; - uint8_t buffer[3 * nes_ppu_render_w * nes_ppu_render_h]; + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Texture* texture; } sdl_render_data; static sdl_render_data the_render_data = {0}; -static int filter(const SDL_Event* event) { - return event->type == SDL_QUIT; +static int filter(void*, SDL_Event* event) { + return (event->type == SDL_QUIT); } 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 { - SDL_WM_SetCaption("NESe", "NESe"); + data->window = SDL_CreateWindow( + "NESe", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + nes_ppu_render_w * 4, + nes_ppu_render_h * 4, + 0 + ); + if (NULL == data->window) { + fprintf(stderr, "SDL: Failed to create window\n"); + SDL_Quit(); + status = -1; + } + } - if (NULL == SDL_SetVideoMode(nes_ppu_render_w, - nes_ppu_render_h, - 24, SDL_HWSURFACE)) { - fprintf(stderr, "SDL: Failed to set video mode\n"); + 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) { - the_render_data.surface = SDL_CreateRGBSurfaceFrom( - the_render_data.buffer, nes_ppu_render_w, - nes_ppu_render_h, 24, nes_ppu_render_w * 3, - 0xFFU, 0xFF00U, 0xFF0000U, 0 + data->texture = SDL_CreateTexture( + the_render_data.renderer, + SDL_PIXELFORMAT_RGB24, + SDL_TEXTUREACCESS_STREAMING, + nes_ppu_render_w, + nes_ppu_render_h ); + if (NULL == data->texture) { + fprintf(stderr, "SDL: Failed to create texture\n"); + SDL_DestroyRenderer(data->renderer); + SDL_DestroyWindow(data->window); + SDL_Quit(); + status = -1; + } + } - SDL_SetEventFilter(filter); - + if (0 == status) { + SDL_SetEventFilter(filter, NULL); rend->data = &the_render_data; } @@ -79,22 +107,66 @@ 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; - if (NULL != data->surface) { - SDL_FreeSurface(data->surface); - data->surface = NULL; - } + SDL_DestroyTexture(data->texture); + SDL_DestroyRenderer(data->renderer); + SDL_DestroyWindow(data->window); SDL_Quit(); } +typedef struct { + +} oam_sprite; + +static void render_sprite(const nes_ppu* ppu, int index, + void* loc, int pitch) { + uint8_t* sprite = &ppu->chr_mem[index * 16U]; + uint8_t* dst_line = (uint8_t*)loc; + for (int y = 8; y > 0; --y) { + uint8_t hi = sprite[0U]; + uint8_t lo = sprite[8U]; + sPal* dst = (sPal*)dst_line; + for (int x = 8; x > 0; --x) { + int pal_idx = (!!(hi & 0x80) << 1) | !!(lo & 0x80); + *dst = nes_palette[16 + (pal_idx * 4)]; + ++dst; + hi <<= 1; + lo <<= 1; + } + dst_line += pitch; + ++sprite; + } +} + +// 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 - - SDL_Surface* screen = SDL_GetVideoSurface(); - if (0 == SDL_BlitSurface(data->surface, NULL, screen, NULL)) { - SDL_UpdateRect(screen, 0, 0, 0, 0); + void* buffer = NULL; + int pitch = 0; + SDL_LockTexture(data->texture, NULL, &buffer, &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); + 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 + ); + ++index; + dst += 3 * 8; + } + dst_line += pitch * 8; } + SDL_UnlockTexture(data->texture); + + + SDL_RenderCopy(data->renderer, data->texture, NULL, NULL); + SDL_RenderPresent(data->renderer); SDL_Event event = {0}; return (1 == SDL_PollEvent(&event) && event.type == SDL_QUIT) ?