diff --git a/src/linux/port.c b/src/linux/port.c index 8aee815..1cfd7af 100644 --- a/src/linux/port.c +++ b/src/linux/port.c @@ -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; } diff --git a/src/nes.c b/src/nes.c index 19d8a22..03e6415 100644 --- a/src/nes.c +++ b/src/nes.c @@ -3,7 +3,7 @@ #include "nes.h" #include "port.h" -#define NESE_DEBUG "NES" +//#define NESE_DEBUG "NES" #include "log.h" diff --git a/src/overlay.c b/src/overlay.c index d584af1..8914bce 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -178,7 +178,7 @@ typedef struct Overlay_message_t { int overlay_init(Overlay* overlay, SDL_Renderer* rend) { overlay->messages = NULL; - overlay->next_id = 0; + overlay->next_id = 1; overlay_font_init(&overlay->font, rend); return 0; } @@ -203,6 +203,32 @@ void overlay_done(Overlay* overlay) { overlay_font_done(&overlay->font); } +static Overlay_message* overlay_find_message(Overlay* overlay, int id) { + Overlay_message* message = overlay->messages; + for ( ; + NULL != message && message->id != id; + message = message->next + ); + return message; +} + +int overlay_update_message(Overlay* overlay, int id, const char* string) { + Overlay_message* msg = overlay_find_message(overlay, id); + if (NULL != msg) { + free(msg->string); + msg->string = strdup(string); + } + return (NULL == msg) ? -1 : 0; +} + +int overlay_update_expiry(Overlay* overlay, int id, int expiry) { + Overlay_message* msg = overlay_find_message(overlay, id); + if (NULL != msg) { + msg->expiry = expiry; + } + return (NULL == msg) ? -1 : 0; +} + int overlay_clear_message(Overlay* overlay, int id) { int result = -1; Overlay_message* last = NULL; diff --git a/src/overlay.h b/src/overlay.h index c5ce5ee..1c9ec05 100644 --- a/src/overlay.h +++ b/src/overlay.h @@ -35,6 +35,8 @@ int overlay_init(Overlay*, SDL_Renderer*); void overlay_done(Overlay*); int overlay_add_message(Overlay*, const char*, int expiry); +int overlay_update_message(Overlay*, int id, const char*); +int overlay_update_expiry(Overlay*, int id, int expiry); int overlay_clear_message(Overlay*, int id); int overlay_clear(Overlay*);