|
|
|
@@ -90,6 +90,10 @@ typedef struct { |
|
|
|
void* cart_data; |
|
|
|
} cart_info; |
|
|
|
|
|
|
|
typedef enum { |
|
|
|
Flag_Turbo = 0b1, |
|
|
|
} Platform_Flags; |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
nes* sys; |
|
|
|
cart_info cart; |
|
|
|
@@ -101,6 +105,8 @@ typedef struct { |
|
|
|
SDL_Surface* screen; |
|
|
|
SDL_Rect view; |
|
|
|
Overlay overlay; |
|
|
|
int fps_msg_id; |
|
|
|
Platform_Flags flags; |
|
|
|
} platform_data; |
|
|
|
|
|
|
|
|
|
|
|
@@ -115,6 +121,9 @@ typedef enum { |
|
|
|
Action_Save, |
|
|
|
Action_Menu, |
|
|
|
Action_Cancel, |
|
|
|
Action_FPS, |
|
|
|
Action_Turbo, |
|
|
|
Action_Max |
|
|
|
} nese_Action; |
|
|
|
|
|
|
|
int nese_update_input(void* plat_data, nes_Input* input) { |
|
|
|
@@ -122,12 +131,16 @@ int nese_update_input(void* plat_data, nes_Input* input) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static const int sdl_action_keycodes[Action_Max] = { |
|
|
|
[Action_Menu] = SDLK_ESCAPE, |
|
|
|
[Action_Reset] = SDLK_BACKSPACE, |
|
|
|
[Action_Load] = SDLK_F2, |
|
|
|
[Action_Save] = SDLK_F1, |
|
|
|
[Action_FPS] = SDLK_f, |
|
|
|
[Action_Turbo] = SDLK_t, |
|
|
|
}; |
|
|
|
|
|
|
|
#define sdl_save_key (SDLK_F1) |
|
|
|
#define sdl_load_key (SDLK_F2) |
|
|
|
#define sdl_alt_start_key (SDLK_RETURN) |
|
|
|
|
|
|
|
static const int sdl_keycodes[nes_controller_num_buttons] = { |
|
|
|
static const int sdl_button_keycodes[nes_controller_num_buttons] = { |
|
|
|
SDLK_a, |
|
|
|
SDLK_s, |
|
|
|
SDLK_q, |
|
|
|
@@ -138,21 +151,23 @@ static const int sdl_keycodes[nes_controller_num_buttons] = { |
|
|
|
SDLK_RIGHT, |
|
|
|
}; |
|
|
|
|
|
|
|
static int button_index(int keycode, const int* codes) { |
|
|
|
int index = nes_controller_num_buttons - 1; |
|
|
|
#define sdl_alt_start_key (SDLK_RETURN) |
|
|
|
|
|
|
|
static int keycode_index(int keycode, const int* codes, int n_codes) { |
|
|
|
int index = n_codes - 1; |
|
|
|
for ( ; index >= 0 && keycode != codes[index]; --index); |
|
|
|
return index; |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Return an action enum? |
|
|
|
static int process_events(nes* sys) { |
|
|
|
int status = 0; |
|
|
|
static nese_Action process_events(nes* sys) { |
|
|
|
nese_Action action = Action_OK; |
|
|
|
nes_Input* input = &sys->core.memory.input; |
|
|
|
|
|
|
|
SDL_Event event = {0}; |
|
|
|
while (0 == status && 0 != SDL_PollEvent(&event)) { |
|
|
|
while (Action_OK == action && 0 != SDL_PollEvent(&event)) { |
|
|
|
if (SDL_QUIT == event.type) { |
|
|
|
status = -1; |
|
|
|
action = Action_Quit; |
|
|
|
|
|
|
|
} else if ( ( SDL_KEYDOWN == event.type || |
|
|
|
SDL_KEYUP == event.type) && |
|
|
|
@@ -160,9 +175,11 @@ static int process_events(nes* sys) { |
|
|
|
) { |
|
|
|
int index = ( sdl_alt_start_key == |
|
|
|
event.key.keysym.sym) ? |
|
|
|
Button_Start : button_index( |
|
|
|
event.key.keysym.sym, |
|
|
|
sdl_keycodes); |
|
|
|
Button_Start : keycode_index( |
|
|
|
event.key.keysym.sym, |
|
|
|
sdl_button_keycodes, |
|
|
|
nes_controller_num_buttons |
|
|
|
); |
|
|
|
if (index >= 0) { |
|
|
|
uint8_t mask = (1 << index); |
|
|
|
if (SDL_KEYDOWN == event.type) { |
|
|
|
@@ -172,22 +189,17 @@ static int process_events(nes* sys) { |
|
|
|
} |
|
|
|
|
|
|
|
} else if (SDL_KEYDOWN == event.type) { |
|
|
|
switch (event.key.keysym.sym) { |
|
|
|
case sdl_save_key: |
|
|
|
status = Action_Save; |
|
|
|
break; |
|
|
|
|
|
|
|
case sdl_load_key: |
|
|
|
status = Action_Load; |
|
|
|
break; |
|
|
|
} |
|
|
|
index = keycode_index( |
|
|
|
event.key.keysym.sym, |
|
|
|
sdl_action_keycodes, Action_Max |
|
|
|
); |
|
|
|
if (index >= 0) action = index; |
|
|
|
} |
|
|
|
// TODO: Menu or other hotkeys |
|
|
|
} |
|
|
|
// TODO: Controller inputs |
|
|
|
} |
|
|
|
|
|
|
|
return status; |
|
|
|
return action; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@@ -245,13 +257,13 @@ int nese_line_ready(void* plat_data, uint8_t* buffer, int line) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#define NS_PER_S (1000U * 1000U * 1000U) |
|
|
|
#define NS_PER_S (1000L * 1000L * 1000L) |
|
|
|
#define FRAME_TIME_NS (16639267L) |
|
|
|
|
|
|
|
uint64_t time_now(void) { |
|
|
|
struct timespec ts_now = {0}; |
|
|
|
clock_gettime(CLOCK_REALTIME, &ts_now); |
|
|
|
return ((int64_t)ts_now.tv_sec * NS_PER_S) + ts_now.tv_nsec; |
|
|
|
return (ts_now.tv_sec * NS_PER_S) + ts_now.tv_nsec; |
|
|
|
} |
|
|
|
|
|
|
|
int64_t time_sleep_until(int64_t t_target) { |
|
|
|
@@ -318,27 +330,76 @@ int nese_frame_ready(void* plat_data) { |
|
|
|
|
|
|
|
SDL_RenderPresent(plat->renderer); |
|
|
|
|
|
|
|
status = process_events(plat->sys); |
|
|
|
|
|
|
|
if (Action_Save == status) { |
|
|
|
nese_Action action = process_events(plat->sys); |
|
|
|
switch (action) { |
|
|
|
case Action_Menu: |
|
|
|
case Action_Quit: |
|
|
|
status = -1; |
|
|
|
break; |
|
|
|
|
|
|
|
case Action_Save: |
|
|
|
status = save_state(plat->sys, plat->cart.filename); |
|
|
|
} else if (Action_Load == status) { |
|
|
|
break; |
|
|
|
|
|
|
|
case Action_Load: |
|
|
|
status = load_state(plat->sys, plat->cart.filename); |
|
|
|
break; |
|
|
|
|
|
|
|
case Action_FPS: |
|
|
|
if (plat->fps_msg_id <= 0) { |
|
|
|
plat->fps_msg_id = overlay_add_message(&plat->overlay, "", 0); |
|
|
|
} else { |
|
|
|
overlay_clear_message(&plat->overlay, plat->fps_msg_id); |
|
|
|
plat->fps_msg_id = 0; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case Action_Turbo: |
|
|
|
plat->flags ^= Flag_Turbo; |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Perform more actions |
|
|
|
|
|
|
|
#ifndef NESE_TURBO |
|
|
|
if (0 == status) { |
|
|
|
plat->t_target += FRAME_TIME_NS; |
|
|
|
int64_t slept_ns = time_sleep_until(plat->t_target); |
|
|
|
if (slept_ns <= -FRAME_TIME_NS) { |
|
|
|
// We're way out of sync. |
|
|
|
plat->t_target = time_now(); |
|
|
|
printf("Out of sync: %d\n", (int)slept_ns); |
|
|
|
int64_t slept_ns = 0; |
|
|
|
if (plat->flags & Flag_Turbo) { |
|
|
|
int64_t now = time_now(); |
|
|
|
slept_ns = plat->t_target - now; |
|
|
|
plat->t_target = now; |
|
|
|
} else { |
|
|
|
slept_ns = time_sleep_until(plat->t_target); |
|
|
|
if (slept_ns <= -FRAME_TIME_NS) { |
|
|
|
// We're way out of sync. |
|
|
|
plat->t_target = time_now(); |
|
|
|
printf("Out of sync: %d\n", (int)slept_ns); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int frame = 0; |
|
|
|
static int64_t slept_total = 0; |
|
|
|
slept_total += slept_ns; |
|
|
|
if (60 == ++frame) { |
|
|
|
if (plat->fps_msg_id > 0) { |
|
|
|
int64_t game_elapsed = frame * FRAME_TIME_NS; |
|
|
|
int64_t cpu_elapsed = game_elapsed - slept_total; |
|
|
|
float fps = (double)(NS_PER_S * frame) / cpu_elapsed; |
|
|
|
|
|
|
|
char message[32]; |
|
|
|
snprintf(message, sizeof(message), "%.1f fps", fps); |
|
|
|
overlay_update_message(&plat->overlay, plat->fps_msg_id, message); |
|
|
|
} |
|
|
|
|
|
|
|
slept_total = 0; |
|
|
|
frame = 0; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
#endif // NESE_TURBO |
|
|
|
|
|
|
|
|
|
|
|
return status; |
|
|
|
} |
|
|
|
|