diff --git a/Makefile b/Makefile index 138723a..f73a3e9 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,8 @@ 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 += cartinfo.c menu.c +NESE_SRC_SRCS += memory.c serdes.c save.c rle.c cartinfo.c +NESE_SRC_SRCS += menu.c list.c state.c menus.c NESE_SRC_SRCS += $(OS)/port.c NESE_GFX_SRCS = $(notdir $(wildcard $(GFXDIR)/*.c)) NESE_MAP_SRCS = $(notdir $(wildcard $(MAPDIR)/*.c)) diff --git a/src/linux/port.c b/src/linux/port.c index ed46a9c..9bbe5b5 100644 --- a/src/linux/port.c +++ b/src/linux/port.c @@ -16,7 +16,9 @@ #include "save.h" #include "draw.h" #include "cartinfo.h" -#include "menu.h" +#include "list.h" +#include "state.h" +#include "menus.h" #define NESE_DEBUG "Port" #include "log.h" @@ -79,71 +81,30 @@ void* nese_alloc(int size) { } -/* - * Platform-specific features and controls - */ - -#define ACTION_DELAY (60U * 1U) -#define ACTION_NOTIFY (60U * 3U) +/* Time */ -typedef struct { - int timer; - int msg_id; - nese_Action action; -} Action_State; - -static void action_immediate(Action_State* state, nese_Action action) { - state->action = action; - state->timer = 0; -} - -static void action_start(Action_State* state, nese_Action action, - Overlay* overlay) { - if (state->action != Action_OK) { - // We can't do two things at once - cancel the current action - overlay_clear_message(overlay, state->msg_id); - state->action = Action_OK; +#define NS_PER_S (1000L * 1000L * 1000L) +#define FRAME_TIME_NS (16639267L) - } else { - state->action = action; - - const char* op = NULL; - if (Action_Load == action) op = "Restoring ..."; - else if (Action_Save == action) op = "Saving ..."; - else if (Action_Reset == action) op = "Resetting ..."; - if (NULL != op) { - state->timer = ACTION_DELAY; - state->msg_id = overlay_add_message(overlay, op, state->timer); - } else { - state->timer = 0; - } - } +uint64_t time_now(void) { + struct timespec ts_now = {0}; + clock_gettime(CLOCK_REALTIME, &ts_now); + return (ts_now.tv_sec * NS_PER_S) + ts_now.tv_nsec; } -static nese_Action action_tick(Action_State* state, Overlay* overlay) { - nese_Action action = Action_OK; - - if (state->action != Action_OK && --state->timer <= 0) { - action = state->action; - state->action = Action_OK; - overlay_clear_message(overlay, state->msg_id); - } - - return action; +int64_t time_sleep_until(int64_t t_target) { + int64_t t_now = time_now(); + int64_t t_diff = (t_target - t_now) / 1000; + if (t_diff > 0) { + usleep(t_diff); + } + return t_diff * 1000; } -static int action_notify(nese_Action action, int status, Overlay* overlay) { - int msg_id = 0; - const char* op = NULL; - if (Action_Load == action) op = (status < 0 ? "Restore failed" : "Restored"); - else if (Action_Save == action) op = (status < 0 ? "Save failed" : "Saved"); - else if (Action_Reset == action) op = "Reset"; - if (NULL != op) { - msg_id = overlay_add_message(overlay, op, ACTION_NOTIFY); - } - return msg_id; -} +/* + * Platform-specific features and controls + */ #define PLAT_FILENAME_SIZE (1024U) @@ -164,62 +125,6 @@ typedef struct { } platform_data; -/* Directories */ - -static int cmp_files(const void* _a, const void* _b) { - const char* a = *(const char**)_a; - const char* b = *(const char**)_b; - - int diff = 0; - for (char ca = 1, cb = 1; ca && cb && 0 == diff; ++a, ++b) { - // Cut extensions; replace underscore with space - ca = (*a == '_' ? ' ' : (*a == '.' ? '\0' : *a)); - cb = (*b == '_' ? ' ' : (*b == '.' ? '\0' : *b)); - diff = (ca - cb); - } - - return diff; -} - -static int count_files(DIR* dir) { - int count = 0; - struct dirent* de = NULL; - while (NULL != (de = readdir(dir))) { - if ('.' != de->d_name[0]) ++count; - } - rewinddir(dir); - return count; -} - -static void make_file_list(DIR* dir, File_List* files) { - files->count = count_files(dir); - files->files = calloc(files->count, sizeof(char*)); - struct dirent* de = NULL; - int i_file = 0; - while (NULL != (de = readdir(dir))) { - if ('.' != de->d_name[0]) { - files->files[i_file] = strdup(de->d_name); - ++i_file; - } - } - qsort(files->files, files->count, sizeof(char*), cmp_files); -} - -static void free_file_list(File_List* files) { - for (int i = 0; i < files->count; ++i) { - free(files->files[i]); - } - free(files->files); -} - -static int find_file(File_List* files, const char* filename) { - int i = (files->count - 1); - for ( ; i >= 0 && - (0 != strcmp(files->files[i], filename)); --i); - return i; -} - - /* Input */ static const int sdl_action_keycodes[Action_Max] = { @@ -307,6 +212,14 @@ static nese_Action process_events(nes* sys) { return action; } +nese_Action nese_update_input(void* plat_data, nes_Input* input) { + time_sleep_until(time_now() + (NS_PER_S / 60)); + platform_data* plat = (platform_data*)plat_data; + nese_Action action = process_events(plat->sys); + if (NULL != input) *input = plat->sys->core.memory.input; + return action; +} + /* * Time / Video - Should be maximally reusable across platforms @@ -334,24 +247,6 @@ int nese_line_ready(void* plat_data, uint8_t* buffer, int line) { } -#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 (ts_now.tv_sec * NS_PER_S) + ts_now.tv_nsec; -} - -int64_t time_sleep_until(int64_t t_target) { - int64_t t_now = time_now(); - int64_t t_diff = (t_target - t_now) / 1000; - if (t_diff > 0) { - usleep(t_diff); - } - return t_diff * 1000; -} - static nese_Action game_menu(platform_data* plat); int nese_frame_ready(void* plat_data) { @@ -480,7 +375,8 @@ int nese_frame_ready(void* plat_data) { char message[32]; snprintf(message, sizeof(message), "%.1f fps", fps); - overlay_update_message(&plat->overlay, plat->fps_msg_id, message); + overlay_update_message(&plat->overlay, plat->fps_msg_id, + message); } slept_total = 0; @@ -526,58 +422,6 @@ int nese_get_audio_frequency(void*) { /* Menus */ -nese_Action nese_update_input(void* plat_data, nes_Input* input) { - time_sleep_until(time_now() + (NS_PER_S / 60)); - platform_data* plat = (platform_data*)plat_data; - nese_Action action = process_events(plat->sys); - if (NULL != input) *input = plat->sys->core.memory.input; - return action; -} - -static nese_Action run_rom_menu(platform_data* plat, Menu_State* state, - char* filename, int sz_filename) { - nese_Action action = Action_Cancel; - - DIR* dir = opendir("rom"); - - if (NULL == dir) { - modal_popup(plat, "No ROMS found!\nPress any key to exit", - color_error); - - } else { - File_List files = {0}; - make_file_list(dir, &files); - closedir(dir); - - Menu_State menu = {0}; - if (NULL != state) menu = *state; - - // Add 4 to skip past "rom/" - if (strlen(filename) > 4) { - int current = find_file(&files, filename + 4); - if (current >= 0) menu.cursor = current; - } - - action = run_menu(plat, &menu, &files, 20); - - if (Action_OK == action) { - if ( menu.cursor >= 0 && - menu.cursor < files.count) { - snprintf(filename, sz_filename - 1, - "%s/%s", "rom", files.files[menu.cursor]); - } else { - action = Action_Cancel; - } - } - - free_file_list(&files); - - if (NULL != state) *state = menu; - } - - return action; -} - static nese_Action nese_load_cart(platform_data* plat, const char* filename) { nese_Action action = Action_OK; unload_cart(&plat->cart); @@ -621,46 +465,6 @@ static nese_Action select_rom(platform_data* plat, char* file, int sz_file) { return action; } -static nese_Action run_game_menu(platform_data* plat, Menu_State* state) { - static char* items[] = { - "Resume", - "Save", - "Restore", - "Reset", - "Select ROM", - "Exit", - }; - static const nese_Action choices[] = { - Action_Cancel, - Action_Save, - Action_Load, - Action_Reset, - Action_Menu, - Action_Quit, - }; - static const File_List options = { - .files = items, - .count = (sizeof(items) / sizeof(*items)), - }; - - Menu_State menu = {0}; - if (NULL != state) menu = *state; - - nese_Action action = run_menu(plat, &menu, &options, 100); - if (Action_Menu == action) { - action = Action_Cancel; - } else if (Action_OK == action) { - if ( menu.cursor >= 0 && - menu.cursor < (sizeof(choices) / sizeof(*choices))) { - action = choices[menu.cursor]; - } - } - - if (NULL != state) *state = menu; - - return action; -} - static nese_Action game_menu(platform_data* plat) { nese_Action action = Action_OK; Menu_State menu = {0}; diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..52387b8 --- /dev/null +++ b/src/list.c @@ -0,0 +1,59 @@ +#include +#include + +#include "list.h" + + +static int cmp_files(const void* _a, const void* _b) { + const char* a = *(const char**)_a; + const char* b = *(const char**)_b; + + int diff = 0; + for (char ca = 1, cb = 1; ca && cb && 0 == diff; ++a, ++b) { + // Cut extensions; replace underscore with space + ca = (*a == '_' ? ' ' : (*a == '.' ? '\0' : *a)); + cb = (*b == '_' ? ' ' : (*b == '.' ? '\0' : *b)); + diff = (ca - cb); + } + + return diff; +} + +static int count_files(DIR* dir) { + int count = 0; + struct dirent* de = NULL; + while (NULL != (de = readdir(dir))) { + if ('.' != de->d_name[0]) ++count; + } + rewinddir(dir); + return count; +} + + +void make_file_list(DIR* dir, File_List* files) { + files->count = count_files(dir); + files->files = calloc(files->count, sizeof(char*)); + struct dirent* de = NULL; + int i_file = 0; + while (NULL != (de = readdir(dir))) { + if ('.' != de->d_name[0]) { + files->files[i_file] = strdup(de->d_name); + ++i_file; + } + } + qsort(files->files, files->count, sizeof(char*), cmp_files); +} + +void free_file_list(File_List* files) { + for (int i = 0; i < files->count; ++i) { + free(files->files[i]); + } + free(files->files); +} + +int find_file(File_List* files, const char* filename) { + int i = (files->count - 1); + for ( ; i >= 0 && + (0 != strcmp(files->files[i], filename)); --i); + return i; +} diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..18541f4 --- /dev/null +++ b/src/list.h @@ -0,0 +1,14 @@ +#ifndef NESE_LIST_H_ +#define NESE_LIST_H_ + +#include + +#include "menu.h" + + +void make_file_list(DIR* dir, File_List* files); +void free_file_list(File_List* files); +int find_file(File_List* files, const char* filename); + + +#endif // NESE_LIST_H_ diff --git a/src/menus.c b/src/menus.c new file mode 100644 index 0000000..b648c2d --- /dev/null +++ b/src/menus.c @@ -0,0 +1,90 @@ +#include + +#include "draw.h" +#include "list.h" +#include "menus.h" + + +nese_Action run_rom_menu(void* plat, Menu_State* state, + char* filename, int sz_filename) { + nese_Action action = Action_Cancel; + + DIR* dir = opendir("rom"); + + if (NULL == dir) { + modal_popup(plat, "No ROMS found!\nPress any key to exit", + color_error); + + } else { + File_List files = {0}; + make_file_list(dir, &files); + closedir(dir); + + Menu_State menu = {0}; + if (NULL != state) menu = *state; + + // Add 4 to skip past "rom/" + if (strlen(filename) > 4) { + int current = find_file(&files, filename + 4); + if (current >= 0) menu.cursor = current; + } + + action = run_menu(plat, &menu, &files, 20); + + if (Action_OK == action) { + if ( menu.cursor >= 0 && + menu.cursor < files.count) { + snprintf(filename, sz_filename - 1, + "%s/%s", "rom", files.files[menu.cursor]); + } else { + action = Action_Cancel; + } + } + + free_file_list(&files); + + if (NULL != state) *state = menu; + } + + return action; +} + +nese_Action run_game_menu(void* plat, Menu_State* state) { + static char* items[] = { + "Resume", + "Save", + "Restore", + "Reset", + "Select ROM", + "Exit", + }; + static const nese_Action choices[] = { + Action_Cancel, + Action_Save, + Action_Load, + Action_Reset, + Action_Menu, + Action_Quit, + }; + static const File_List options = { + .files = items, + .count = (sizeof(items) / sizeof(*items)), + }; + + Menu_State menu = {0}; + if (NULL != state) menu = *state; + + nese_Action action = run_menu(plat, &menu, &options, 100); + if (Action_Menu == action) { + action = Action_Cancel; + } else if (Action_OK == action) { + if ( menu.cursor >= 0 && + menu.cursor < (sizeof(choices) / sizeof(*choices))) { + action = choices[menu.cursor]; + } + } + + if (NULL != state) *state = menu; + + return action; +} diff --git a/src/menus.h b/src/menus.h new file mode 100644 index 0000000..80c7c6e --- /dev/null +++ b/src/menus.h @@ -0,0 +1,13 @@ +#ifndef NESE_MENUS_H_ +#define NESE_MENUS_H_ + +#include "menu.h" + + +nese_Action run_rom_menu(void* plat, Menu_State* state, + char* filename, int sz_filename); + +nese_Action run_game_menu(void* plat, Menu_State* state); + + +#endif // NESE_MENUS_H_ diff --git a/src/state.c b/src/state.c new file mode 100644 index 0000000..bd9b339 --- /dev/null +++ b/src/state.c @@ -0,0 +1,57 @@ +#include "state.h" + + +#define ACTION_DELAY (60U * 1U) +#define ACTION_NOTIFY (60U * 3U) + + +void action_immediate(Action_State* state, nese_Action action) { + state->action = action; + state->timer = 0; +} + +void action_start(Action_State* state, nese_Action action, Overlay* overlay) { + if (state->action != Action_OK) { + // We can't do two things at once - cancel the current action + overlay_clear_message(overlay, state->msg_id); + state->action = Action_OK; + + } else { + state->action = action; + + const char* op = NULL; + if (Action_Load == action) op = "Restoring ..."; + else if (Action_Save == action) op = "Saving ..."; + else if (Action_Reset == action) op = "Resetting ..."; + if (NULL != op) { + state->timer = ACTION_DELAY; + state->msg_id = overlay_add_message(overlay, op, state->timer); + } else { + state->timer = 0; + } + } +} + +nese_Action action_tick(Action_State* state, Overlay* overlay) { + nese_Action action = Action_OK; + + if (state->action != Action_OK && --state->timer <= 0) { + action = state->action; + state->action = Action_OK; + overlay_clear_message(overlay, state->msg_id); + } + + return action; +} + +int action_notify(nese_Action action, int status, Overlay* overlay) { + int msg_id = 0; + const char* op = NULL; + if (Action_Load == action) op = (status < 0 ? "Restore failed" : "Restored"); + else if (Action_Save == action) op = (status < 0 ? "Save failed" : "Saved"); + else if (Action_Reset == action) op = "Reset"; + if (NULL != op) { + msg_id = overlay_add_message(overlay, op, ACTION_NOTIFY); + } + return msg_id; +} diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..65ac9a5 --- /dev/null +++ b/src/state.h @@ -0,0 +1,21 @@ +#ifndef NESE_STATE_H_ +#define NESE_STATE_H_ + +#include "action.h" +#include "overlay.h" + + +typedef struct { + int timer; + int msg_id; + nese_Action action; +} Action_State; + + +void action_immediate(Action_State* state, nese_Action action); +void action_start(Action_State* state, nese_Action action, Overlay* overlay); +nese_Action action_tick(Action_State* state, Overlay* overlay); +int action_notify(nese_Action action, int status, Overlay* overlay); + + +#endif // NESE_STATE_H_