From 8bdb482cf4cd1aceecd488ba3d0a3d7fa49870f9 Mon Sep 17 00:00:00 2001 From: Nathaniel Walizer Date: Thu, 24 Apr 2025 00:55:29 -0700 Subject: [PATCH] Refactor to separate SDL rendering from platform code --- Makefile | 7 +- src/cartinfo.c | 47 +++++++++ src/cartinfo.h | 20 ++++ src/linux/port.c | 211 +++++----------------------------------- src/port.h | 2 + src/{ => sdl}/overlay.c | 0 src/{ => sdl}/overlay.h | 0 src/sdl/render.c | 138 ++++++++++++++++++++++++++ src/sdl/render.h | 26 +++++ 9 files changed, 264 insertions(+), 187 deletions(-) create mode 100644 src/cartinfo.c create mode 100644 src/cartinfo.h rename src/{ => sdl}/overlay.c (100%) rename src/{ => sdl}/overlay.h (100%) create mode 100644 src/sdl/render.c create mode 100644 src/sdl/render.h diff --git a/Makefile b/Makefile index 7e81eb2..c1f5ceb 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ #CROSS_COMPILE = arm-none-eabi- OS = linux +GFX = sdl CC = $(CROSS_COMPILE)gcc LD = $(CC) @@ -26,19 +27,22 @@ TEST_OBJS = $(TEST_SRCS:%.c=$(OBJDIR)/%.o) MAPDIR = $(SRCDIR)/map +GFXDIR = $(SRCDIR)/$(GFX) NESE_SRC_SRCS = f6502.c f6502_opcodes.c NESE_SRC_SRCS += nese.c nes.c cart.c mapper.c NESE_SRC_SRCS += ppu.c apu.c NESE_SRC_SRCS += memory.c serdes.c save.c rle.c -NESE_SRC_SRCS += overlay.c +NESE_SRC_SRCS += cartinfo.c NESE_SRC_SRCS += $(OS)/port.c +NESE_GFX_SRCS = $(notdir $(wildcard $(GFXDIR)/*.c)) NESE_MAP_SRCS = $(notdir $(wildcard $(MAPDIR)/*.c)) NESE_DEBUG = CART NESE_SRCS += $(NESE_SRC_SRCS:%=$(SRCDIR)/%) NESE_SRCS += $(NESE_MAP_SRCS:%=$(MAPDIR)/%) +NESE_SRCS += $(NESE_GFX_SRCS:%=$(GFXDIR)/%) NESE_OBJS = $(NESE_SRCS:%.c=$(OBJDIR)/%.o) @@ -49,6 +53,7 @@ all: $(BINDIR)/nese $(BINDIR)/test: CFLAGS += -DF6502_FLAT -DF6502_TEST -DF6502_TRACE $(BINDIR)/nese: CFLAGS += $(foreach debug,$(NESE_DEBUG), -DDEBUG_$(debug)) +$(BINDIR)/nese: CFLAGS += -I$(GFXDIR) $(BINDIR)/nese: CFLAGS += $(shell sdl2-config --cflags) $(BINDIR)/nese: LDFLAGS += $(shell sdl2-config --libs) $(BINDIR)/nese: $(NESE_OBJS) diff --git a/src/cartinfo.c b/src/cartinfo.c new file mode 100644 index 0000000..01f3b92 --- /dev/null +++ b/src/cartinfo.c @@ -0,0 +1,47 @@ +#include "cart.h" +#include "cartinfo.h" +#include "port.h" + + +void unload_cart(Cart_Info* cart) { + if (cart->cart_data) { + nese_unmap_file(cart->cart_data, cart->filesize); + cart->cart_data = NULL; + cart->filesize = 0; + } + + if (cart->file) { + fclose(cart->file); + cart->file = NULL; + } +} + +int load_cart(Cart_Info* cart, const char* filename, nes* sys) { + int status = 0; + int filesize = 0; + void* cart_data = NULL; + + FILE* file = fopen(filename, "rb"); + if (NULL == file) { + status = -1; + } else { + filesize = nese_file_size(file); + cart_data = nese_map_file(file, filesize, + Filemap_Mode_Read); + if (NULL == cart_data) { + fprintf(stderr, "Failed to map %s\n", filename); + status = -1; + } else { + status = nes_cart_load_mem(cart_data, filesize, sys); + } + } + + if (0 == status) { + cart->filename = filename; + cart->filesize = filesize; + cart->file = file; + cart->cart_data = cart_data; + } + + return status; +} diff --git a/src/cartinfo.h b/src/cartinfo.h new file mode 100644 index 0000000..18a6068 --- /dev/null +++ b/src/cartinfo.h @@ -0,0 +1,20 @@ +#ifndef NESE_INFO_H_ +#define NESE_INFO_H_ + +#include + +#include "nes.h" + + +typedef struct { + const char* filename; + FILE* file; + int filesize; + void* cart_data; +} Cart_Info; + +int load_cart(Cart_Info*, const char* filename, nes*); +void unload_cart(Cart_Info* cart); + + +#endif // NESE_INFO_H_ \ No newline at end of file diff --git a/src/linux/port.c b/src/linux/port.c index 665839c..022cdfd 100644 --- a/src/linux/port.c +++ b/src/linux/port.c @@ -7,14 +7,13 @@ #include #include - -#include - #include "cart.h" #include "nese.h" #include "overlay.h" #include "port.h" #include "save.h" +#include "render.h" +#include "cartinfo.h" #define NESE_DEBUG "Port" #include "log.h" @@ -48,8 +47,7 @@ int nese_mkdir(const char* dir) { return mkdir(dir, 0777); } - -static int nese_file_size(FILE* file) { +int nese_file_size(FILE* file) { int size = -1; if (0 == fseek(file, 0, SEEK_END)) { size = ftell(file); @@ -79,31 +77,18 @@ void* nese_alloc(int size) { /* - * SDL-specific init and entry point - * This should be maximally reusable across platforms + * Platform-specific features and controls */ -typedef struct { - const char* filename; - FILE* file; - int filesize; - void* cart_data; -} cart_info; - typedef enum { Flag_Turbo = 0b1, } Platform_Flags; typedef struct { nes* sys; - cart_info cart; + Cart_Info cart; int64_t t_target; - SDL_Window* window; - SDL_Renderer* renderer; - SDL_Texture* texture; - SDL_Surface* target; - SDL_Surface* screen; - SDL_Rect view; + Render_Info renderer; Overlay overlay; int fps_msg_id; Platform_Flags flags; @@ -209,40 +194,13 @@ static nese_Action process_events(nes* sys) { } -/* Time / Video */ - -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} -}; +/* + * Time / Video - Should be maximally reusable across platforms + */ int nese_frame_start(void* plat_data, uint8_t background) { - platform_data* plat = (platform_data*)plat_data; - - SDL_Color ext = nes_palette[background]; - // TODO: Compose color according to GPU format? - // Why are R/B reversed on STM32? - SDL_FillRect(plat->target, NULL, ((int)ext.r << 16) | - ((int)ext.g << 8) | ext.b); - - return 0; + return render_frame_start( &((platform_data*)plat_data)->renderer, + background); } int nese_line_ready(void* plat_data, uint8_t* buffer, int line) { @@ -258,7 +216,6 @@ int nese_line_ready(void* plat_data, uint8_t* buffer, int line) { SDL_BlitSurface(plat->scanline, NULL, plat->target, &rect); */ - return 0; } @@ -283,7 +240,6 @@ int64_t time_sleep_until(int64_t t_target) { int nese_frame_ready(void* plat_data) { - int status = 0; platform_data* plat = (platform_data*)plat_data; /* static int frame = 0; @@ -320,22 +276,20 @@ int nese_frame_ready(void* plat_data) { } putc('\n', stdout); */ - SDL_RenderFlush(plat->renderer); + int status = render_frame(&plat->renderer); - SDL_BlitSurface(plat->screen, NULL, plat->target, NULL); - - SDL_UnlockTexture(plat->texture); - SDL_RenderCopy(plat->renderer, plat->texture, - NULL, NULL); - SDL_LockTextureToSurface(plat->texture, NULL, - &plat->target); + if (0 == status) { + overlay_render(&plat->overlay, + nes_ppu_render_w, nes_ppu_render_h, + &plat->renderer.view, plat->renderer.renderer); + } - overlay_render(&plat->overlay, - nes_ppu_render_w, nes_ppu_render_h, - &plat->view, plat->renderer); + if (0 == status) { + status = render_frame_end(&plat->renderer); + } - SDL_RenderPresent(plat->renderer); + // TODO: Check status first? nese_Action action = process_events(plat->sys); switch (action) { @@ -423,78 +377,10 @@ int nese_get_audio_frequency(void*) { /* Platform Data */ static int plat_init(platform_data* plat) { - int status = SDL_Init( - SDL_INIT_EVENTS | - SDL_INIT_VIDEO - ); + int status = render_info_init(&plat->renderer, plat->sys); if (0 == status) { - plat->view = (SDL_Rect){ - .w = nes_ppu_render_w * 8, - .h = nes_ppu_render_h * 7, - }; - plat->window = SDL_CreateWindow( - "NESe", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - plat->view.w, plat->view.h, - 0 - ); - if (NULL == plat->window) { - LOGE("SDL: Failed to create window"); - status = -1; - } - } - - if (0 == status) { - plat->renderer = SDL_CreateRenderer( - plat->window, -1, - SDL_RENDERER_ACCELERATED/* | - SDL_RENDERER_PRESENTVSYNC*/ - ); - if (NULL == plat->renderer) { - LOGE("SDL: Failed to create renderer"); - status = -1; - } - } - - if (0 == status) { - plat->texture = SDL_CreateTexture( - plat->renderer, SDL_PIXELFORMAT_RGB888, - SDL_TEXTUREACCESS_STREAMING, - nes_ppu_render_w, nes_ppu_render_h - ); - if (NULL == plat->texture) { - LOGE("SDL: Failed to create target"); - status = -1; - } else { - SDL_LockTextureToSurface(plat->texture, NULL, - &plat->target); -// SDL_FillRect(data->target, NULL, color_background); - } - } - - if (0 == status) { - plat->screen = SDL_CreateRGBSurfaceWithFormatFrom( - plat->sys->ppu.screen_data, - nes_ppu_render_w, nes_ppu_render_h, - 8, nes_ppu_render_w, - SDL_PIXELFORMAT_INDEX8 - ); - if (NULL == plat->screen) { - LOGE("SDL: Failed to create screen"); - status = -1; - } else { - SDL_SetPaletteColors( - plat->screen->format->palette, - nes_palette, 0U, 64U - ); - SDL_SetColorKey(plat->screen, SDL_TRUE, 0xFFU); - } - } - - if (0 == status) { - status = overlay_init(&plat->overlay, plat->renderer); + status = overlay_init(&plat->overlay, plat->renderer.renderer); } if (0 == status) { @@ -505,59 +391,12 @@ static int plat_init(platform_data* plat) { } static void plat_done(platform_data* plat) { - if (NULL != plat->texture) SDL_DestroyTexture(plat->texture); - if (NULL != plat->renderer) SDL_DestroyRenderer(plat->renderer); - if (NULL != plat->window) SDL_DestroyWindow(plat->window); + render_info_done(&plat->renderer); overlay_done(&plat->overlay); SDL_Quit(); } -static int load_cart(const char* filename, platform_data* plat) { - int status = 0; - int filesize = 0; - void* cart_data = NULL; - - FILE* file = fopen(filename, "rb"); - if (NULL == file) { - status = -1; - } else { - filesize = nese_file_size(file); - cart_data = nese_map_file(file, filesize, - Filemap_Mode_Read); - if (NULL == cart_data) { - fprintf(stderr, "Failed to map %s\n", filename); - status = -1; - } else { - status = nes_cart_load_mem(cart_data, filesize, - plat->sys); - } - } - - if (0 == status) { - plat->cart.filename = filename; - plat->cart.filesize = filesize; - plat->cart.file = file; - plat->cart.cart_data = cart_data; - } - - return status; -} - -static void unload_cart(cart_info* cart) { - if (cart->cart_data) { - nese_unmap_file(cart->cart_data, cart->filesize); - cart->cart_data = NULL; - cart->filesize = 0; - } - - if (cart->file) { - fclose(cart->file); - cart->file = NULL; - } -} - - // This is too big for the stack. static nes sys = {0}; @@ -576,7 +415,7 @@ int main(int argc, char* argv[]) { } if (0 == status) { - status = load_cart(argv[1], &plat); + status = load_cart(&plat.cart, argv[1], &sys); } if (0 == status) { diff --git a/src/port.h b/src/port.h index 7977d11..15a7c6d 100644 --- a/src/port.h +++ b/src/port.h @@ -6,6 +6,8 @@ #include "input.h" +int nese_file_size(FILE* file); + typedef enum { Filemap_Mode_Read = 0, Filemap_Mode_Write, diff --git a/src/overlay.c b/src/sdl/overlay.c similarity index 100% rename from src/overlay.c rename to src/sdl/overlay.c diff --git a/src/overlay.h b/src/sdl/overlay.h similarity index 100% rename from src/overlay.h rename to src/sdl/overlay.h diff --git a/src/sdl/render.c b/src/sdl/render.c new file mode 100644 index 0000000..39d1ac3 --- /dev/null +++ b/src/sdl/render.c @@ -0,0 +1,138 @@ +#include "render.h" + +#define NESE_DEBUG "SDL" +#include "log.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} +}; + + +int render_frame_start(Render_Info* renderer, uint8_t background) { + SDL_Color ext = nes_palette[background]; + // TODO: Compose color according to GPU format? + // Why are R/B reversed on STM32? + SDL_FillRect(renderer->target, NULL, ((int)ext.r << 16) | + ((int)ext.g << 8) | ext.b); + return 0; +} + +int render_frame(Render_Info* renderer) { + SDL_RenderFlush(renderer->renderer); + + SDL_BlitSurface(renderer->screen, NULL, renderer->target, NULL); + + SDL_UnlockTexture(renderer->texture); + SDL_RenderCopy(renderer->renderer, renderer->texture, + NULL, NULL); + SDL_LockTextureToSurface(renderer->texture, NULL, + &renderer->target); + + return 0; +} + +int render_frame_end(Render_Info* renderer) { + SDL_RenderPresent(renderer->renderer); + + return 0; +} + + +int render_info_init(Render_Info* renderer, nes* sys) { + int status = SDL_Init( + SDL_INIT_EVENTS | + SDL_INIT_VIDEO + ); + + if (0 == status) { + renderer->view = (SDL_Rect){ + .w = nes_ppu_render_w * 4, + .h = nes_ppu_render_h * 4, + }; + renderer->window = SDL_CreateWindow( + "NESe", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + renderer->view.w, renderer->view.h, + 0 + ); + if (NULL == renderer->window) { + LOGE("Failed to create window"); + status = -1; + } + } + + if (0 == status) { + renderer->renderer = SDL_CreateRenderer( + renderer->window, -1, + SDL_RENDERER_ACCELERATED/* | + SDL_RENDERER_PRESENTVSYNC*/ + ); + if (NULL == renderer->renderer) { + LOGE("Failed to create renderer"); + status = -1; + } + } + + if (0 == status) { + renderer->texture = SDL_CreateTexture( + renderer->renderer, SDL_PIXELFORMAT_RGB888, + SDL_TEXTUREACCESS_STREAMING, + nes_ppu_render_w, nes_ppu_render_h + ); + if (NULL == renderer->texture) { + LOGE("Failed to create target"); + status = -1; + } else { + SDL_LockTextureToSurface(renderer->texture, NULL, + &renderer->target); +// SDL_FillRect(data->target, NULL, color_background); + } + } + + if (0 == status) { + renderer->screen = SDL_CreateRGBSurfaceWithFormatFrom( + sys->ppu.screen_data, + nes_ppu_render_w, nes_ppu_render_h, + 8, nes_ppu_render_w, + SDL_PIXELFORMAT_INDEX8 + ); + if (NULL == renderer->screen) { + LOGE("Failed to create screen"); + status = -1; + } else { + SDL_SetPaletteColors( + renderer->screen->format->palette, + nes_palette, 0U, 64U + ); + SDL_SetColorKey(renderer->screen, SDL_TRUE, 0xFFU); + } + } + + return status; +} + +void render_info_done(Render_Info* renderer) { + if (NULL != renderer->texture) SDL_DestroyTexture(renderer->texture); + if (NULL != renderer->renderer) SDL_DestroyRenderer(renderer->renderer); + if (NULL != renderer->window) SDL_DestroyWindow(renderer->window); +} diff --git a/src/sdl/render.h b/src/sdl/render.h new file mode 100644 index 0000000..6a341d0 --- /dev/null +++ b/src/sdl/render.h @@ -0,0 +1,26 @@ +#ifndef NESE_RENDER_H_ +#define NESE_RENDER_H_ + +#include + +#include "nes.h" + + +typedef struct { + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Texture* texture; + SDL_Surface* target; + SDL_Surface* screen; + SDL_Rect view; +} Render_Info; + +int render_info_init(Render_Info*, nes*); +void render_info_done(Render_Info*); + +int render_frame_start(Render_Info*, uint8_t background); +int render_frame(Render_Info*); +int render_frame_end(Render_Info*); + + +#endif // NESE_RENDER_H_ \ No newline at end of file