|
|
|
@@ -7,14 +7,13 @@ |
|
|
|
#include <sys/mman.h> |
|
|
|
#include <sys/stat.h> |
|
|
|
|
|
|
|
|
|
|
|
#include <SDL.h> |
|
|
|
|
|
|
|
#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) { |
|
|
|
|