| @@ -22,12 +22,13 @@ endif | |||||
| CC = $(CROSS_COMPILE)gcc | CC = $(CROSS_COMPILE)gcc | ||||
| LD = $(CC) | LD = $(CC) | ||||
| PFLAGS += -g | PFLAGS += -g | ||||
| #PFLAGS += -O3 | |||||
| #PFLAGS += -s | |||||
| #PFLAGS += -O3 -s | |||||
| #PFLAGS += -DSTANDALONE | |||||
| #PFLAGS += -DDEBUG_MAPPER | #PFLAGS += -DDEBUG_MAPPER | ||||
| #PFLAGS += -DDEBUG_RENDER | #PFLAGS += -DDEBUG_RENDER | ||||
| #PFLAGS += -DDEBUG_PPU -DDEBUG_VRAM -DDEBUG_OAM | #PFLAGS += -DDEBUG_PPU -DDEBUG_VRAM -DDEBUG_OAM | ||||
| #PFLAGS += -DDEBUG_APU | #PFLAGS += -DDEBUG_APU | ||||
| #PFLAGS += -DDEBUG_INPUT | |||||
| #PFLAGS += -DE6502_DEBUG | #PFLAGS += -DE6502_DEBUG | ||||
| PFLAGS += -DE6502_ILLEGAL | PFLAGS += -DE6502_ILLEGAL | ||||
| CFLAGS += $(PFLAGS) -Wall -Werror -Wshadow -Wunused -I../ -Isrc/ | CFLAGS += $(PFLAGS) -Wall -Werror -Wshadow -Wunused -I../ -Isrc/ | ||||
| @@ -1,10 +1,6 @@ | |||||
| #ifndef NESE_INI_H_ | #ifndef NESE_INI_H_ | ||||
| #define NESE_INI_H_ | #define NESE_INI_H_ | ||||
| #define DBG_LOG(...) fprintf(stderr, __VA_ARGS__) | |||||
| #define ERR_LOG(...) DBG_LOG(__VA_ARGS__) | |||||
| #define INI_ERR(...) ERR_LOG(__VA_ARGS__) | |||||
| typedef enum { | typedef enum { | ||||
| ini_invalid = -1, | ini_invalid = -1, | ||||
| @@ -59,11 +59,15 @@ void nes_input_write(nes_input* input, uint16_t addr, uint8_t val); | |||||
| // System Glue | // System Glue | ||||
| struct nese_Components; | |||||
| typedef struct nes_Input_Reader_t { | typedef struct nes_Input_Reader_t { | ||||
| int (*init)(struct nes_Input_Reader_t*); | int (*init)(struct nes_Input_Reader_t*); | ||||
| void (*done)(struct nes_Input_Reader_t*); | void (*done)(struct nes_Input_Reader_t*); | ||||
| int (*update)(struct nes_Input_Reader_t*, nes_input*); | |||||
| int (*update)(struct nes_Input_Reader_t*, | |||||
| struct nese_Components*); | |||||
| void* data; | void* data; | ||||
| int menu_timer; | |||||
| } nes_Input_Reader; | } nes_Input_Reader; | ||||
| static inline int nes_input_init(nes_Input_Reader* reader) { | static inline int nes_input_init(nes_Input_Reader* reader) { | ||||
| @@ -74,9 +78,11 @@ static inline void nes_input_done(nes_Input_Reader* reader) { | |||||
| reader->done(reader); | reader->done(reader); | ||||
| } | } | ||||
| static inline int nes_input_update(nes_Input_Reader* reader, | |||||
| nes_input* input) { | |||||
| return reader->update(reader, input); | |||||
| static inline int nes_input_update( | |||||
| nes_Input_Reader* reader, | |||||
| struct nese_Components* comp | |||||
| ) { | |||||
| return reader->update(reader, comp); | |||||
| } | } | ||||
| @@ -8,10 +8,9 @@ | |||||
| #include "timer.h" | #include "timer.h" | ||||
| static int get_input(nes_Input_Reader* reader, | |||||
| nes_input* input, int *last) { | |||||
| int status = nes_input_update(reader, input); | |||||
| int new_buttons = input->controllers[0].buttons; | |||||
| static int get_input(nese_Components* comp, int *last) { | |||||
| int status = nes_input_update(comp->reader, comp); | |||||
| int new_buttons = comp->sys->input.controllers[0].buttons; | |||||
| if (0 == status) { | if (0 == status) { | ||||
| status = (~*last & new_buttons); | status = (~*last & new_buttons); | ||||
| } else { | } else { | ||||
| @@ -21,25 +20,23 @@ static int get_input(nes_Input_Reader* reader, | |||||
| return status; | return status; | ||||
| } | } | ||||
| static int wait_for_input(nes_Input_Reader* reader, | |||||
| nes_input* input) { | |||||
| int buttons = input->controllers[0].buttons; | |||||
| static int wait_for_input(nese_Components* comp) { | |||||
| int buttons = comp->sys->input.controllers[0].buttons; | |||||
| int status = 0; | int status = 0; | ||||
| for ( ; | for ( ; | ||||
| 0 == status; | 0 == status; | ||||
| status = get_input(reader, input, &buttons) ) { | |||||
| status = get_input(comp, &buttons) ) { | |||||
| time_sleep(US_PER_S / 60); | time_sleep(US_PER_S / 60); | ||||
| } | } | ||||
| return status; | return status; | ||||
| } | } | ||||
| static int wait_for_input_quiet(nes_Input_Reader* reader, | |||||
| nes_input* input) { | |||||
| static int wait_for_input_quiet(nese_Components* comp) { | |||||
| int status = 0; | int status = 0; | ||||
| while ( input_Result_Quit != status && | while ( input_Result_Quit != status && | ||||
| input->controllers[0].buttons) { | |||||
| comp->sys->input.controllers[0].buttons) { | |||||
| time_sleep(US_PER_S / 60); | time_sleep(US_PER_S / 60); | ||||
| status = nes_input_update(reader, input); | |||||
| status = nes_input_update(comp->reader, comp); | |||||
| } | } | ||||
| return ( input_Result_Quit == status ? | return ( input_Result_Quit == status ? | ||||
| input_Result_Quit : 0); | input_Result_Quit : 0); | ||||
| @@ -157,8 +154,8 @@ static void show_menu(const menu_state* menu, int dim, int x, | |||||
| } | } | ||||
| static int run_menu(menu_state* state, const file_list* files, | static int run_menu(menu_state* state, const file_list* files, | ||||
| int x, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys) { | |||||
| int x, nese_Components* comp, | |||||
| const cart_info* cart) { | |||||
| menu_state menu = {0}; | menu_state menu = {0}; | ||||
| if (NULL != state) { | if (NULL != state) { | ||||
| menu = *state; | menu = *state; | ||||
| @@ -181,14 +178,13 @@ static int run_menu(menu_state* state, const file_list* files, | |||||
| menu.top = (files->count - 1) - visible; | menu.top = (files->count - 1) - visible; | ||||
| } | } | ||||
| show_menu(&menu, NULL != sys->cart.mapper, | |||||
| x, rend, files); | |||||
| show_menu(&menu, NULL != cart->file, x, | |||||
| comp->rend, files); | |||||
| int buttons = wait_for_input(input, &sys->input); | |||||
| int buttons = wait_for_input(comp); | |||||
| int special = (buttons >> 8); | int special = (buttons >> 8); | ||||
| if ( input_Result_Quit == special || | |||||
| input_Result_Refresh == special) { | |||||
| if (input_Result_Quit == special) { | |||||
| status = special; | status = special; | ||||
| } else if ( input_Result_Menu == special || | } else if ( input_Result_Menu == special || | ||||
| @@ -220,22 +216,17 @@ static int run_menu(menu_state* state, const file_list* files, | |||||
| } | } | ||||
| char* run_main_menu(menu_state* state, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys, | |||||
| const char* cur_filename) { | |||||
| char* run_main_menu(menu_state* state, nese_Components* comp, | |||||
| const cart_info* cart) { | |||||
| char* cart_filename = NULL; | char* cart_filename = NULL; | ||||
| DIR* dir = opendir("rom"); | DIR* dir = opendir("rom"); | ||||
| if (NULL == dir) { | if (NULL == dir) { | ||||
| nes_draw_last_frame(rend, NULL != sys->cart.mapper); | |||||
| nes_draw_text( | |||||
| rend, | |||||
| modal_popup( | |||||
| "No ROMS found!\nPress any key to exit", | "No ROMS found!\nPress any key to exit", | ||||
| 10, 21, color_error | |||||
| comp, cart | |||||
| ); | ); | ||||
| nes_draw_done(rend); | |||||
| wait_for_input(input, &sys->input); | |||||
| } else { | } else { | ||||
| file_list files = {0}; | file_list files = {0}; | ||||
| @@ -245,18 +236,14 @@ char* run_main_menu(menu_state* state, nes_Renderer* rend, | |||||
| menu_state menu = {0}; | menu_state menu = {0}; | ||||
| if (NULL != state) menu = *state; | if (NULL != state) menu = *state; | ||||
| if (NULL != cur_filename) { | |||||
| if (NULL != cart->filename) { | |||||
| // Add 4 to skip past "rom/" | // Add 4 to skip past "rom/" | ||||
| int current = find_file(&files, cur_filename + 4); | |||||
| int current = find_file(&files, cart->filename + 4); | |||||
| if (current >= 0) menu.cursor = current; | if (current >= 0) menu.cursor = current; | ||||
| } | } | ||||
| // Don't let window refreshes interrupt us. | // Don't let window refreshes interrupt us. | ||||
| int status = input_Result_Refresh; | |||||
| while (input_Result_Refresh == status) { | |||||
| status = run_menu(&menu, &files, 20, | |||||
| rend, input, sys); | |||||
| } | |||||
| int status = run_menu(&menu, &files, 20, comp, cart); | |||||
| if (input_Result_Quit == status) { | if (input_Result_Quit == status) { | ||||
| cart_filename = (char*)-1; | cart_filename = (char*)-1; | ||||
| @@ -280,15 +267,17 @@ char* run_main_menu(menu_state* state, nes_Renderer* rend, | |||||
| return cart_filename; | return cart_filename; | ||||
| } | } | ||||
| int run_game_menu(menu_state* state, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys) { | |||||
| int run_game_menu(menu_state* state, nese_Components* comp, | |||||
| const cart_info* cart) { | |||||
| static char* items[] = { | static char* items[] = { | ||||
| "Resume", | "Resume", | ||||
| "Save", | "Save", | ||||
| "Restore", | "Restore", | ||||
| "Reset", | "Reset", | ||||
| "Select ROM", | "Select ROM", | ||||
| #ifndef STANDALONE | |||||
| "Toggle Fullscreen", | "Toggle Fullscreen", | ||||
| #endif | |||||
| "Exit", | "Exit", | ||||
| }; | }; | ||||
| static int choices[] = { | static int choices[] = { | ||||
| @@ -297,7 +286,9 @@ int run_game_menu(menu_state* state, nes_Renderer* rend, | |||||
| input_Result_Load, | input_Result_Load, | ||||
| input_Result_Reset, | input_Result_Reset, | ||||
| input_Result_Menu, | input_Result_Menu, | ||||
| #ifndef STANDALONE | |||||
| input_Result_View, | input_Result_View, | ||||
| #endif | |||||
| input_Result_Quit, | input_Result_Quit, | ||||
| }; | }; | ||||
| static const file_list options = { | static const file_list options = { | ||||
| @@ -308,8 +299,7 @@ int run_game_menu(menu_state* state, nes_Renderer* rend, | |||||
| menu_state menu = {0}; | menu_state menu = {0}; | ||||
| if (NULL != state) menu = *state; | if (NULL != state) menu = *state; | ||||
| int status = run_menu(&menu, &options, 100, | |||||
| rend, input, sys); | |||||
| int status = run_menu(&menu, &options, 100, comp, cart); | |||||
| if (input_Result_Menu == status) { | if (input_Result_Menu == status) { | ||||
| status = input_Result_Cancel; | status = input_Result_Cancel; | ||||
| @@ -317,19 +307,19 @@ int run_game_menu(menu_state* state, nes_Renderer* rend, | |||||
| if (0 == status) status = choices[menu.cursor]; | if (0 == status) status = choices[menu.cursor]; | ||||
| wait_for_input_quiet(input, &sys->input); | |||||
| wait_for_input_quiet(comp); | |||||
| if (NULL != state) *state = menu; | if (NULL != state) *state = menu; | ||||
| return status; | return status; | ||||
| } | } | ||||
| int modal_popup(const char* message, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys) { | |||||
| int modal_popup(const char* message, nese_Components* comp, | |||||
| const cart_info* cart) { | |||||
| int w = 0; | int w = 0; | ||||
| int h = 0; | int h = 0; | ||||
| nes_text_size(rend, message, &w, &h); | |||||
| nes_text_size(comp->rend, message, &w, &h); | |||||
| int x = ((int)nes_ppu_render_w - w) / 2; | int x = ((int)nes_ppu_render_w - w) / 2; | ||||
| int y = ((int)nes_ppu_render_h - h) / 2; | int y = ((int)nes_ppu_render_h - h) / 2; | ||||
| @@ -337,9 +327,9 @@ int modal_popup(const char* message, nes_Renderer* rend, | |||||
| if (x < 5) x = 5; | if (x < 5) x = 5; | ||||
| if (y < 5) y = 5; | if (y < 5) y = 5; | ||||
| nes_draw_last_frame(rend, NULL != sys->cart.mapper); | |||||
| nes_draw_text(rend, message, x, y, color_error); | |||||
| nes_draw_done(rend); | |||||
| nes_draw_last_frame(comp->rend, NULL != cart->file); | |||||
| nes_draw_text(comp->rend, message, x, y, color_error); | |||||
| nes_draw_done(comp->rend); | |||||
| return wait_for_input(input, &sys->input); | |||||
| return wait_for_input(comp); | |||||
| } | } | ||||
| @@ -4,6 +4,7 @@ | |||||
| #include "nes.h" | #include "nes.h" | ||||
| #include "render.h" | #include "render.h" | ||||
| #include "input.h" | #include "input.h" | ||||
| #include "state.h" | |||||
| #define color_white (0xFFffffffU) | #define color_white (0xFFffffffU) | ||||
| @@ -29,16 +30,15 @@ typedef struct { | |||||
| // Returns filename of selected ROM | // Returns filename of selected ROM | ||||
| char* run_main_menu(menu_state*, nes_Renderer*, | |||||
| nes_Input_Reader*, nes*, | |||||
| const char* cur_filename); | |||||
| char* run_main_menu(menu_state*, nese_Components*, | |||||
| const cart_info*); | |||||
| // Returns nes_Input_Result indicating the choice | // Returns nes_Input_Result indicating the choice | ||||
| int run_game_menu(menu_state*, nes_Renderer*, | |||||
| nes_Input_Reader*, nes*); | |||||
| int run_game_menu(menu_state*, nese_Components*, | |||||
| const cart_info*); | |||||
| int modal_popup(const char* message, | |||||
| nes_Renderer*, nes_Input_Reader*, nes*); | |||||
| int modal_popup(const char* message, nese_Components*, | |||||
| const cart_info*); | |||||
| #endif // NESE_MENU_H_ | #endif // NESE_MENU_H_ | ||||
| @@ -107,8 +107,8 @@ static int loadsave_tick(loadsave_state* loadsave) { | |||||
| return action; | return action; | ||||
| } | } | ||||
| static int select_rom(menu_state* menu, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys, | |||||
| static int select_rom(menu_state* menu, | |||||
| nese_Components* comp, | |||||
| cart_info* cur_cart) { | cart_info* cur_cart) { | ||||
| int status = 0; | int status = 0; | ||||
| cart_info cart = {0}; | cart_info cart = {0}; | ||||
| @@ -120,7 +120,7 @@ static int select_rom(menu_state* menu, nes_Renderer* rend, | |||||
| char message[1024]; | char message[1024]; | ||||
| snprintf(message, sizeof(message) - 1, | snprintf(message, sizeof(message) - 1, | ||||
| "Could not load\n%s", cart.filename + 4); | "Could not load\n%s", cart.filename + 4); | ||||
| int button = modal_popup(message, rend, input, sys); | |||||
| int button = modal_popup(message, comp, cur_cart); | |||||
| if (input_Result_Quit == (button >> 8)) { | if (input_Result_Quit == (button >> 8)) { | ||||
| // Program closed inside modal | // Program closed inside modal | ||||
| status = input_Result_Quit; | status = input_Result_Quit; | ||||
| @@ -130,9 +130,7 @@ static int select_rom(menu_state* menu, nes_Renderer* rend, | |||||
| } | } | ||||
| if (0 == status) { | if (0 == status) { | ||||
| cart.filename = run_main_menu( | |||||
| menu, rend, input, sys, cur_cart->filename | |||||
| ); | |||||
| cart.filename = run_main_menu(menu, comp, cur_cart); | |||||
| if ((char*)-1 == cart.filename) { | if ((char*)-1 == cart.filename) { | ||||
| // This means that we quit | // This means that we quit | ||||
| @@ -150,49 +148,43 @@ static int select_rom(menu_state* menu, nes_Renderer* rend, | |||||
| } | } | ||||
| if (0 == status && NULL != cart.file) { | if (0 == status && NULL != cart.file) { | ||||
| save_sram(&sys->cart, cur_cart->filename); | |||||
| save_sram(&comp->sys->cart, cur_cart->filename); | |||||
| nes_cart_done(&sys->cart); | |||||
| nes_cart_done(&comp->sys->cart); | |||||
| cart_info_done(cur_cart); | cart_info_done(cur_cart); | ||||
| sys->cart = new_cart; | |||||
| comp->sys->cart = new_cart; | |||||
| *cur_cart = cart; | *cur_cart = cart; | ||||
| nes_setup_cart(sys); | |||||
| nes_setup_cart(comp->sys); | |||||
| } | } | ||||
| return status; | return status; | ||||
| } | } | ||||
| static int do_game_menu(menu_state* menu, nes_Renderer* rend, | |||||
| nes_Input_Reader* input, nes* sys, | |||||
| static int do_game_menu(menu_state* menu, | |||||
| nese_Components* comp, | |||||
| nese_State* state) { | nese_State* state) { | ||||
| int status = 0; | int status = 0; | ||||
| menu_state rom_menu = {0}; | menu_state rom_menu = {0}; | ||||
| while (1) { | while (1) { | ||||
| status = run_game_menu(menu, rend, input, sys); | |||||
| status = run_game_menu(menu, comp, &state->cart); | |||||
| if ( input_Result_View == status || | |||||
| input_Result_Refresh == status) { | |||||
| if (input_Result_View == status) { | |||||
| state->flags ^= (1 << State_Bit_Fullscreen); | |||||
| } else { | |||||
| // We need to do this to flush both buffers | |||||
| nes_draw_last_frame(rend, 1); | |||||
| nes_draw_done(rend); | |||||
| } | |||||
| if (input_Result_View == status) { | |||||
| #ifndef STANDALONE | |||||
| state->flags ^= (1 << State_Bit_Fullscreen); | |||||
| #endif | |||||
| // We call this both times since it does both | // We call this both times since it does both | ||||
| // the toggle and the recalculation. | // the toggle and the recalculation. | ||||
| nes_render_fullscreen( | nes_render_fullscreen( | ||||
| rend, | |||||
| comp->rend, | |||||
| state->flags & (1 << State_Bit_Fullscreen) | state->flags & (1 << State_Bit_Fullscreen) | ||||
| ); | ); | ||||
| continue; | continue; | ||||
| } else if (input_Result_Menu == status) { | } else if (input_Result_Menu == status) { | ||||
| status = select_rom(&rom_menu, rend, input, | |||||
| sys, &state->cart); | |||||
| status = select_rom(&rom_menu, comp, &state->cart); | |||||
| if (input_Result_Cancel == status) { | if (input_Result_Cancel == status) { | ||||
| status = input_Result_OK; | status = input_Result_OK; | ||||
| @@ -212,31 +204,39 @@ int main(int argc, char* argv[]) { | |||||
| nese_State state = {0}; | nese_State state = {0}; | ||||
| load_prefs_filename(&state, "nese.prefs"); | load_prefs_filename(&state, "nese.prefs"); | ||||
| #ifdef STANDALONE | |||||
| state.flags |= (1 << State_Bit_Fullscreen); | |||||
| #endif | |||||
| nes sys = {0}; | |||||
| if (status == 0) { | |||||
| status = nes_init(&sys, audio_freq); | |||||
| } | |||||
| nese_Components components = { | |||||
| .rend = &sdl_renderer, | |||||
| .reader = &sdl_input, | |||||
| .audio = &sdl_audio, | |||||
| .sys = &sys, | |||||
| }; | |||||
| nes_Renderer* rend = &sdl_renderer; | |||||
| if (status == 0) { | if (status == 0) { | ||||
| status = nes_render_init(rend); | |||||
| status = nes_render_init(components.rend); | |||||
| if (0 == status) { | if (0 == status) { | ||||
| nes_render_fullscreen( | nes_render_fullscreen( | ||||
| rend, | |||||
| components.rend, | |||||
| (state.flags & (1 << State_Bit_Fullscreen)) | (state.flags & (1 << State_Bit_Fullscreen)) | ||||
| ); | ); | ||||
| nes_render_refresh(components.rend); | |||||
| } | } | ||||
| } | } | ||||
| nes_Input_Reader* input = &sdl_input; | |||||
| if (status == 0) { | |||||
| status = nes_input_init(input); | |||||
| } | |||||
| nes_Audio_Stream* audio = &sdl_audio; | |||||
| if (status == 0) { | if (status == 0) { | ||||
| status = nes_audio_init(audio, audio_freq); | |||||
| status = nes_input_init(components.reader); | |||||
| } | } | ||||
| nes sys = {0}; | |||||
| if (status == 0) { | if (status == 0) { | ||||
| status = nes_init(&sys, audio_freq); | |||||
| status = nes_audio_init(components.audio, audio_freq); | |||||
| } | } | ||||
| if (0 == status && argc > 1) { | if (0 == status && argc > 1) { | ||||
| @@ -255,17 +255,16 @@ int main(int argc, char* argv[]) { | |||||
| // If we didn't launch with a file, run the loader | // If we didn't launch with a file, run the loader | ||||
| if (0 == status && NULL == state.cart.file) { | if (0 == status && NULL == state.cart.file) { | ||||
| status = select_rom(NULL, rend, input, | |||||
| &sys, &state.cart); | |||||
| status = select_rom(NULL, &components, &state.cart); | |||||
| } | } | ||||
| if (status == 0) { | if (status == 0) { | ||||
| menu_state game_menu = {0}; | menu_state game_menu = {0}; | ||||
| loadsave_state loadsave = { | loadsave_state loadsave = { | ||||
| .overlay = &rend->overlay, | |||||
| .overlay = &components.rend->overlay, | |||||
| }; | }; | ||||
| nes_render(rend, &sys.ppu); | |||||
| nes_render(components.rend, &sys.ppu); | |||||
| time_us t_target = time_now(); | time_us t_target = time_now(); | ||||
| uint64_t cycle_last_frame = 0; | uint64_t cycle_last_frame = 0; | ||||
| @@ -278,7 +277,7 @@ int main(int argc, char* argv[]) { | |||||
| if ( result == ppu_Result_Ready || | if ( result == ppu_Result_Ready || | ||||
| result == ppu_Result_VBlank_Off) { | result == ppu_Result_VBlank_Off) { | ||||
| status = nes_render(rend, &sys.ppu); | |||||
| status = nes_render(components.rend, &sys.ppu); | |||||
| if (status > 0) { | if (status > 0) { | ||||
| // Load/Save Operations | // Load/Save Operations | ||||
| @@ -309,25 +308,28 @@ int main(int argc, char* argv[]) { | |||||
| cycle_last_frame = total_cycles; | cycle_last_frame = total_cycles; | ||||
| // Update button states every rendered frame | // Update button states every rendered frame | ||||
| status = nes_input_update(input, &sys.input); | |||||
| status = nes_input_update(components.reader, | |||||
| &components); | |||||
| if (input_Result_Menu == status) { | |||||
| if (input_Result_Refresh == status) { | |||||
| status = input_Result_OK; | |||||
| } if (input_Result_Menu == status) { | |||||
| status = do_game_menu( | status = do_game_menu( | ||||
| &game_menu, rend, | |||||
| input, &sys, &state | |||||
| &game_menu, &components, &state | |||||
| ); | ); | ||||
| if ( input_Result_Load == status || | if ( input_Result_Load == status || | ||||
| input_Result_Save == status) { | input_Result_Save == status) { | ||||
| loadsave.mode = status; | loadsave.mode = status; | ||||
| loadsave.timer = 0; | loadsave.timer = 0; | ||||
| status = 0; | |||||
| status = input_Result_OK; | |||||
| } | } | ||||
| // Allow other options to fall through | // Allow other options to fall through | ||||
| } | } | ||||
| if (input_Result_Reset == status) { | if (input_Result_Reset == status) { | ||||
| overlay_add_message( | overlay_add_message( | ||||
| &rend->overlay, | |||||
| &components.rend->overlay, | |||||
| "Game reset", | "Game reset", | ||||
| 60 * 3 | 60 * 3 | ||||
| ); | ); | ||||
| @@ -351,7 +353,9 @@ int main(int argc, char* argv[]) { | |||||
| if (status == 0) { | if (status == 0) { | ||||
| // Update audio, too | // Update audio, too | ||||
| status = nes_audio_fill(audio, &sys.apu); | |||||
| status = nes_audio_fill( | |||||
| components.audio, &sys.apu | |||||
| ); | |||||
| } | } | ||||
| } | } | ||||
| } else if (result == ppu_Result_Halt) { | } else if (result == ppu_Result_Halt) { | ||||
| @@ -362,9 +366,11 @@ int main(int argc, char* argv[]) { | |||||
| float ms_run = ( total_cycles * 1000. * | float ms_run = ( total_cycles * 1000. * | ||||
| nes_clock_master_den) / | nes_clock_master_den) / | ||||
| nes_clock_master_num; | nes_clock_master_num; | ||||
| fprintf(stdout, "Ran %f ms, %"PRIu64" master cycles (%s)\n", | |||||
| ms_run, total_cycles, | |||||
| status == 0 ? "OK" : "Halted"); | |||||
| fprintf(stdout, | |||||
| "Ran %f ms, %"PRIu64" master cycles (%s)\n", | |||||
| ms_run, total_cycles, | |||||
| status == 0 ? "OK" : "Halted" | |||||
| ); | |||||
| // Failure might mean there's nothing to save | // Failure might mean there's nothing to save | ||||
| save_sram(&sys.cart, state.cart.filename); | save_sram(&sys.cart, state.cart.filename); | ||||
| @@ -375,9 +381,9 @@ int main(int argc, char* argv[]) { | |||||
| nes_done(&sys); | nes_done(&sys); | ||||
| nes_audio_done(audio); | |||||
| nes_input_done(input); | |||||
| nes_render_done(rend); | |||||
| nes_audio_done(components.audio); | |||||
| nes_input_done(components.reader); | |||||
| nes_render_done(components.rend); | |||||
| return status; | return status; | ||||
| } | } | ||||
| @@ -9,7 +9,9 @@ typedef struct nes_Renderer_t { | |||||
| int (*init)(struct nes_Renderer_t*); | int (*init)(struct nes_Renderer_t*); | ||||
| void (*done)(struct nes_Renderer_t*); | void (*done)(struct nes_Renderer_t*); | ||||
| int (*render)(struct nes_Renderer_t*, nes_ppu*); | int (*render)(struct nes_Renderer_t*, nes_ppu*); | ||||
| void (*fullscreen)(struct nes_Renderer_t*, int enable); | void (*fullscreen)(struct nes_Renderer_t*, int enable); | ||||
| void (*refresh)(struct nes_Renderer_t*); | |||||
| void (*draw_last_frame)(struct nes_Renderer_t*, int dim); | void (*draw_last_frame)(struct nes_Renderer_t*, int dim); | ||||
| void (*draw_text)(struct nes_Renderer_t*, const char*, int x, int y, uint32_t color); | void (*draw_text)(struct nes_Renderer_t*, const char*, int x, int y, uint32_t color); | ||||
| @@ -37,6 +39,10 @@ static inline void nes_render_fullscreen(nes_Renderer* rend, | |||||
| rend->fullscreen(rend, enable); | rend->fullscreen(rend, enable); | ||||
| } | } | ||||
| static inline void nes_render_refresh(nes_Renderer* rend) { | |||||
| rend->refresh(rend); | |||||
| } | |||||
| static inline void nes_draw_last_frame(nes_Renderer* rend, | static inline void nes_draw_last_frame(nes_Renderer* rend, | ||||
| int dim) { | int dim) { | ||||
| rend->draw_last_frame(rend, dim); | rend->draw_last_frame(rend, dim); | ||||
| @@ -1,26 +1,51 @@ | |||||
| #include <SDL.h> | #include <SDL.h> | ||||
| #include "input.h" | #include "input.h" | ||||
| #include "state.h" | |||||
| #define DBG_LOG(...) printf(__VA_ARGS__) | |||||
| #define ERR_LOG(...) DBG_LOG(__VA_ARGS__) | |||||
| #ifdef DEBUG_INPUT | |||||
| #define INPUT_DBG DBG_LOG | |||||
| #define INPUT_INFO DBG_LOG | |||||
| #else | |||||
| #define INPUT_DBG(...) | |||||
| #define INPUT_INFO(...) | |||||
| #endif | |||||
| #define INPUT_ERR ERR_LOG | |||||
| #define axis_threshold (8192L) | |||||
| typedef struct { | |||||
| int16_t last_x_axis; | |||||
| int16_t last_y_axis; | |||||
| SDL_GameController* gamepad; | |||||
| } sdl_input_data; | |||||
| static sdl_input_data the_input_data = {0}; | |||||
| static SDL_GameController* sdl_find_gamepad() { | static SDL_GameController* sdl_find_gamepad() { | ||||
| int i = SDL_NumJoysticks() - 1; | int i = SDL_NumJoysticks() - 1; | ||||
| printf("Found %d joysticks\n", i + 1); | |||||
| INPUT_INFO("Found %d joysticks\n", i + 1); | |||||
| for ( ; i >= 0 && !SDL_IsGameController(i); --i); | for ( ; i >= 0 && !SDL_IsGameController(i); --i); | ||||
| if (i >= 0) printf("Joystick %d is a gamepad\n", i); | |||||
| if (i >= 0) INPUT_INFO("Joystick %d is a gamepad\n", i); | |||||
| return (i < 0 ? NULL : SDL_GameControllerOpen(i)); | return (i < 0 ? NULL : SDL_GameControllerOpen(i)); | ||||
| } | } | ||||
| static void sdl_lose_gamepad(void* data) { | |||||
| SDL_GameController* gamepad = (SDL_GameController*)data; | |||||
| if (NULL != gamepad) SDL_GameControllerClose(gamepad); | |||||
| static void sdl_lose_gamepad(SDL_GameController* gamepad) { | |||||
| if (NULL != gamepad) { | |||||
| SDL_GameControllerClose(gamepad); | |||||
| } | |||||
| } | } | ||||
| static int sdl_match_gamepad(SDL_JoystickID id, void* data) { | |||||
| SDL_GameController* gamepad = (SDL_GameController*)data; | |||||
| return ( SDL_JoystickInstanceID( | |||||
| SDL_GameControllerGetJoystick(gamepad)) | |||||
| == id); | |||||
| static int sdl_match_gamepad(SDL_JoystickID id, | |||||
| SDL_GameController* gamepad) { | |||||
| return ( id == SDL_JoystickInstanceID( | |||||
| SDL_GameControllerGetJoystick(gamepad))); | |||||
| } | } | ||||
| /* | /* | ||||
| static int sdl_event_filter(void*, SDL_Event* event) { | static int sdl_event_filter(void*, SDL_Event* event) { | ||||
| @@ -35,27 +60,32 @@ static int sdl_event_filter(void*, SDL_Event* event) { | |||||
| } | } | ||||
| */ | */ | ||||
| static int sdl_input_init(nes_Input_Reader* reader) { | static int sdl_input_init(nes_Input_Reader* reader) { | ||||
| reader->data = &the_input_data; | |||||
| sdl_input_data* data = (sdl_input_data*)reader->data; | |||||
| int status = SDL_Init(SDL_INIT_EVENTS | | int status = SDL_Init(SDL_INIT_EVENTS | | ||||
| SDL_INIT_GAMECONTROLLER); | SDL_INIT_GAMECONTROLLER); | ||||
| if (status == 0) { | if (status == 0) { | ||||
| reader->data = sdl_find_gamepad(); | |||||
| data->gamepad = sdl_find_gamepad(); | |||||
| // SDL_SetEventFilter(sdl_event_filter, NULL); | // SDL_SetEventFilter(sdl_event_filter, NULL); | ||||
| if (NULL != reader->data) { | |||||
| printf("Gamepad found\n"); | |||||
| if (NULL != data->gamepad) { | |||||
| INPUT_INFO("Gamepad found\n"); | |||||
| } | } | ||||
| } | } | ||||
| return status; | return status; | ||||
| } | } | ||||
| static void sdl_input_done(nes_Input_Reader* input) { | |||||
| sdl_lose_gamepad(input->data); | |||||
| static void sdl_input_done(nes_Input_Reader* reader) { | |||||
| sdl_input_data* data = (sdl_input_data*)reader->data; | |||||
| sdl_lose_gamepad(data->gamepad); | |||||
| } | } | ||||
| static const int sdl_menu_key = SDLK_ESCAPE; | static const int sdl_menu_key = SDLK_ESCAPE; | ||||
| static const int sdl_save_key = SDLK_F1; | static const int sdl_save_key = SDLK_F1; | ||||
| static const int sdl_load_key = SDLK_F2; | static const int sdl_load_key = SDLK_F2; | ||||
| static const int sdl_alt_start_key = SDLK_RETURN; | |||||
| static const int sdl_keycodes[nes_controller_num_buttons] = { | static const int sdl_keycodes[nes_controller_num_buttons] = { | ||||
| SDLK_a, | SDLK_a, | ||||
| @@ -89,10 +119,31 @@ static int button_index(int keycode, const int* codes) { | |||||
| return index; | return index; | ||||
| } | } | ||||
| #define input_debug(o, ...) INPUT_DBG(__VA_ARGS__) | |||||
| /* | |||||
| static void input_debug(Overlay* overlay, | |||||
| const char* fmt, ...) { | |||||
| char msg[100] = {0}; | |||||
| va_list args; | |||||
| va_start(args, fmt); | |||||
| vsnprintf(msg, sizeof(msg) - 1, fmt, args); | |||||
| va_end(args); | |||||
| overlay_add_message(overlay, msg, 60); | |||||
| } | |||||
| */ | |||||
| static int sdl_input_update(nes_Input_Reader* reader, | static int sdl_input_update(nes_Input_Reader* reader, | ||||
| nes_input* input) { | |||||
| nese_Components* comp) { | |||||
| int status = input_Result_OK; | int status = input_Result_OK; | ||||
| sdl_input_data* data = (sdl_input_data*)reader->data; | |||||
| if (reader->menu_timer > 0 && --reader->menu_timer == 0) { | |||||
| status = input_Result_Menu; | |||||
| } | |||||
| SDL_Event event = {0}; | SDL_Event event = {0}; | ||||
| while (0 == status && 0 != SDL_PollEvent(&event)) { | while (0 == status && 0 != SDL_PollEvent(&event)) { | ||||
| if (SDL_QUIT == event.type) { | if (SDL_QUIT == event.type) { | ||||
| @@ -101,6 +152,14 @@ static int sdl_input_update(nes_Input_Reader* reader, | |||||
| } else if (SDL_WINDOWEVENT == event.type) { | } else if (SDL_WINDOWEVENT == event.type) { | ||||
| if ( SDL_WINDOWEVENT_EXPOSED == | if ( SDL_WINDOWEVENT_EXPOSED == | ||||
| event.window.event) { | event.window.event) { | ||||
| // We need to do this to flush both buffers | |||||
| // nes_draw_last_frame(comp->rend, 1); | |||||
| // nes_draw_done(comp->rend); | |||||
| // We call this both times since it does both | |||||
| // the toggle and the recalculation. | |||||
| nes_render_refresh(comp->rend); | |||||
| status = input_Result_Refresh; | status = input_Result_Refresh; | ||||
| } | } | ||||
| @@ -108,15 +167,32 @@ static int sdl_input_update(nes_Input_Reader* reader, | |||||
| SDL_KEYUP == event.type) && | SDL_KEYUP == event.type) && | ||||
| 0 == event.key.repeat | 0 == event.key.repeat | ||||
| ) { | ) { | ||||
| int index = button_index(event.key.keysym.sym, | |||||
| sdl_keycodes); | |||||
| input_debug(&comp->rend->overlay, | |||||
| "Input: K %d S %d\n", | |||||
| SDL_KEYDOWN == event.type ? 1 : 0, | |||||
| event.key.keysym.sym); | |||||
| int index = ( sdl_alt_start_key == | |||||
| event.key.keysym.sym) ? | |||||
| Button_Start : button_index( | |||||
| event.key.keysym.sym, | |||||
| sdl_keycodes); | |||||
| if (index >= 0) { | if (index >= 0) { | ||||
| uint8_t mask = (1 << index); | uint8_t mask = (1 << index); | ||||
| if (SDL_KEYDOWN == event.type) { | if (SDL_KEYDOWN == event.type) { | ||||
| input->controllers[0].buttons |= mask; | |||||
| comp->sys->input.controllers[0].buttons |= mask; | |||||
| } else { | } else { | ||||
| input->controllers[0].buttons &= ~mask; | |||||
| comp->sys->input.controllers[0].buttons &= ~mask; | |||||
| } | } | ||||
| /* | |||||
| input_debug( | |||||
| &comp->rend->overlay, | |||||
| "Input: Key: %s %02x\n", | |||||
| (SDL_KEYDOWN == event.type) ? | |||||
| "Set" : "Clear", | |||||
| mask | |||||
| ); | |||||
| */ | |||||
| } else if ( sdl_menu_key == event.key.keysym.sym && | } else if ( sdl_menu_key == event.key.keysym.sym && | ||||
| SDL_KEYDOWN == event.type) { | SDL_KEYDOWN == event.type) { | ||||
| @@ -139,14 +215,37 @@ static int sdl_input_update(nes_Input_Reader* reader, | |||||
| } else if ( SDL_CONTROLLERBUTTONDOWN == event.type || | } else if ( SDL_CONTROLLERBUTTONDOWN == event.type || | ||||
| SDL_CONTROLLERBUTTONUP == event.type) { | SDL_CONTROLLERBUTTONUP == event.type) { | ||||
| input_debug(&comp->rend->overlay, | |||||
| "Input: B %d B %d\n", | |||||
| SDL_CONTROLLERBUTTONDOWN == event.type ? | |||||
| 1 : 0, | |||||
| event.cbutton.button); | |||||
| int index = button_index(event.cbutton.button, | int index = button_index(event.cbutton.button, | ||||
| sdl_buttons); | sdl_buttons); | ||||
| if (index >= 0) { | if (index >= 0) { | ||||
| uint8_t mask = (1 << index); | uint8_t mask = (1 << index); | ||||
| if (SDL_CONTROLLERBUTTONDOWN == event.type) { | if (SDL_CONTROLLERBUTTONDOWN == event.type) { | ||||
| input->controllers[0].buttons |= mask; | |||||
| comp->sys->input.controllers[0].buttons |= mask; | |||||
| } else { | } else { | ||||
| input->controllers[0].buttons &= ~mask; | |||||
| comp->sys->input.controllers[0].buttons &= ~mask; | |||||
| } | |||||
| /* | |||||
| input_debug( | |||||
| &comp->rend->overlay, | |||||
| "Input: Button: %s %02x\n", | |||||
| (SDL_CONTROLLERBUTTONDOWN == event.type) ? | |||||
| "Set" : "Clear", | |||||
| mask | |||||
| ); | |||||
| */ | |||||
| if (Button_Start == index) { | |||||
| if (SDL_CONTROLLERBUTTONDOWN == event.type) { | |||||
| reader->menu_timer = 60; | |||||
| } else { | |||||
| reader->menu_timer = 0; | |||||
| } | |||||
| } | } | ||||
| } else if (sdl_menu_button == event.cbutton.button) { | } else if (sdl_menu_button == event.cbutton.button) { | ||||
| @@ -175,50 +274,73 @@ static int sdl_input_update(nes_Input_Reader* reader, | |||||
| uint8_t mask_set = 0; | uint8_t mask_set = 0; | ||||
| uint8_t mask_clear = 0; | uint8_t mask_clear = 0; | ||||
| int dir = 0; | |||||
| if (value <= -axis_threshold) dir = -1; | |||||
| else if (value >= axis_threshold) dir = 1; | |||||
| if (SDL_CONTROLLER_AXIS_LEFTX == axis) { | if (SDL_CONTROLLER_AXIS_LEFTX == axis) { | ||||
| mask_clear = (1 << Button_Left) | | |||||
| (1 << Button_Right); | |||||
| if (value <= -8192) { | |||||
| mask_set = (1 << Button_Left); | |||||
| } else if (value >= 8192) { | |||||
| mask_set = (1 << Button_Right); | |||||
| if (dir != data->last_x_axis) { | |||||
| mask_clear = (1 << Button_Left) | | |||||
| (1 << Button_Right); | |||||
| if (dir < 0) { | |||||
| mask_set = (1 << Button_Left); | |||||
| } else if (dir > 0) { | |||||
| mask_set = (1 << Button_Right); | |||||
| } | |||||
| } | } | ||||
| data->last_x_axis = dir; | |||||
| } else if (SDL_CONTROLLER_AXIS_LEFTY == axis) { | } else if (SDL_CONTROLLER_AXIS_LEFTY == axis) { | ||||
| mask_clear = (1 << Button_Down) | | |||||
| (1 << Button_Up); | |||||
| if (value <= -8192) { | |||||
| mask_set = (1 << Button_Up); | |||||
| } else if (value >= 8192) { | |||||
| mask_set = (1 << Button_Down); | |||||
| if (dir != data->last_y_axis) { | |||||
| mask_clear = (1 << Button_Down) | | |||||
| (1 << Button_Up); | |||||
| if (dir < 0) { | |||||
| mask_set = (1 << Button_Up); | |||||
| } else if (dir > 0) { | |||||
| mask_set = (1 << Button_Down); | |||||
| } | |||||
| } | } | ||||
| data->last_y_axis = dir; | |||||
| } | } | ||||
| input->controllers[0].buttons &= ~mask_clear; | |||||
| input->controllers[0].buttons |= mask_set; | |||||
| uint32_t old_buttons = comp->sys->input.controllers[0].buttons; | |||||
| uint32_t new_buttons = ((old_buttons & ~mask_clear) | mask_set); | |||||
| comp->sys->input.controllers[0].buttons = new_buttons; | |||||
| /* | |||||
| if (old_buttons != new_buttons) { | |||||
| input_debug(&comp->rend->overlay, | |||||
| "Input: Axis: S %02x C %02x V %d\n", | |||||
| mask_set, mask_clear, value); | |||||
| } | |||||
| */ | |||||
| } else if (SDL_CONTROLLERDEVICEADDED == event.type) { | } else if (SDL_CONTROLLERDEVICEADDED == event.type) { | ||||
| if (NULL == reader->data) { | |||||
| printf("New gamepad connected\n"); | |||||
| reader->data = sdl_find_gamepad(); | |||||
| if (reader->data) printf("Using new gamepad\n"); | |||||
| if (NULL == data->gamepad) { | |||||
| INPUT_INFO("New gamepad connected\n"); | |||||
| data->gamepad = sdl_find_gamepad(); | |||||
| if (data->gamepad) INPUT_INFO("Using new gamepad\n"); | |||||
| } else { | } else { | ||||
| printf("Redundant gamepad connected\n"); | |||||
| INPUT_INFO("Redundant gamepad connected\n"); | |||||
| } | } | ||||
| } else if (SDL_CONTROLLERDEVICEREMOVED == event.type) { | } else if (SDL_CONTROLLERDEVICEREMOVED == event.type) { | ||||
| if (sdl_match_gamepad(event.cdevice.which, | if (sdl_match_gamepad(event.cdevice.which, | ||||
| reader->data)) { | |||||
| printf("Gamepad disconnected\n"); | |||||
| data->gamepad)) { | |||||
| INPUT_INFO("Gamepad disconnected\n"); | |||||
| sdl_lose_gamepad(reader->data); | sdl_lose_gamepad(reader->data); | ||||
| reader->data = sdl_find_gamepad(); | |||||
| if (reader->data) printf("Using another gamepad\n"); | |||||
| data->gamepad = sdl_find_gamepad(); | |||||
| if (data->gamepad) INPUT_INFO("Using another gamepad\n"); | |||||
| } else { | } else { | ||||
| printf("Redundant gamepad disconnected\n"); | |||||
| INPUT_INFO("Redundant gamepad disconnected\n"); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (0 != status) { | |||||
| input_debug(&comp->rend->overlay, | |||||
| "Input: Returning %d\n", status); | |||||
| } | |||||
| return status; | return status; | ||||
| } | } | ||||
| @@ -164,7 +164,7 @@ int sdl_overlay_frame(Overlay* overlay, sdl_overlay_font* font, | |||||
| overlay_message* last = NULL, *next = NULL; | overlay_message* last = NULL, *next = NULL; | ||||
| for ( overlay_message* message = overlay->messages; | for ( overlay_message* message = overlay->messages; | ||||
| NULL != message; | NULL != message; | ||||
| last = message, message = next) { | |||||
| message = next) { | |||||
| next = message->next; | next = message->next; | ||||
| render_string(rend, x, y, vx, vy, sx, sy, | render_string(rend, x, y, vx, vy, sx, sy, | ||||
| @@ -179,6 +179,8 @@ int sdl_overlay_frame(Overlay* overlay, sdl_overlay_font* font, | |||||
| } | } | ||||
| free(message->string); | free(message->string); | ||||
| free(message); | free(message); | ||||
| } else { | |||||
| last = message; | |||||
| } | } | ||||
| } | } | ||||
| @@ -192,9 +192,12 @@ static void sdl_render_done(nes_Renderer* rend) { | |||||
| static void sdl_render_fullscreen(nes_Renderer* rend, int on) { | static void sdl_render_fullscreen(nes_Renderer* rend, int on) { | ||||
| sdl_render_data* data = (sdl_render_data*)rend->data; | sdl_render_data* data = (sdl_render_data*)rend->data; | ||||
| SDL_SetWindowFullscreen(data->window, | SDL_SetWindowFullscreen(data->window, | ||||
| on ? SDL_WINDOW_FULLSCREEN : 0); | on ? SDL_WINDOW_FULLSCREEN : 0); | ||||
| } | |||||
| static void sdl_render_refresh(nes_Renderer* rend) { | |||||
| sdl_render_data* data = (sdl_render_data*)rend->data; | |||||
| SDL_GetWindowSize(data->window, | SDL_GetWindowSize(data->window, | ||||
| &data->win_w, &data->win_h); | &data->win_w, &data->win_h); | ||||
| @@ -215,6 +218,10 @@ static void sdl_render_fullscreen(nes_Renderer* rend, int on) { | |||||
| data->view.h = h; | data->view.h = h; | ||||
| SDL_RenderSetClipRect(data->renderer, &data->view); | SDL_RenderSetClipRect(data->renderer, &data->view); | ||||
| // We need to do this to flush both buffers | |||||
| // nes_draw_last_frame(rend, 1); | |||||
| // nes_draw_done(rend); | |||||
| } | } | ||||
| static inline void render_sprite_line( | static inline void render_sprite_line( | ||||
| @@ -581,6 +588,8 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { | |||||
| } else { | } else { | ||||
| REND_LOG("Scanline %3d -> Postrender\n", ppu->scanline); | REND_LOG("Scanline %3d -> Postrender\n", ppu->scanline); | ||||
| SDL_RenderClear(data->renderer); | |||||
| SDL_UnlockTexture(data->texture); | SDL_UnlockTexture(data->texture); | ||||
| SDL_RenderCopy(data->renderer, data->texture, | SDL_RenderCopy(data->renderer, data->texture, | ||||
| NULL, &data->view); | NULL, &data->view); | ||||
| @@ -653,6 +662,7 @@ nes_Renderer sdl_renderer = { | |||||
| .done = sdl_render_done, | .done = sdl_render_done, | ||||
| .render = sdl_render, | .render = sdl_render, | ||||
| .fullscreen = sdl_render_fullscreen, | .fullscreen = sdl_render_fullscreen, | ||||
| .refresh = sdl_render_refresh, | |||||
| .draw_last_frame = sdl_redraw_frame, | .draw_last_frame = sdl_redraw_frame, | ||||
| .draw_text = sdl_draw_text, | .draw_text = sdl_draw_text, | ||||
| .text_size = sdl_text_size, | .text_size = sdl_text_size, | ||||
| @@ -4,6 +4,13 @@ | |||||
| #include "ini.h" | #include "ini.h" | ||||
| // Running State | |||||
| // TODO | |||||
| // Persistent State | |||||
| void cart_info_done(cart_info* cart) { | void cart_info_done(cart_info* cart) { | ||||
| if (cart->file) fclose(cart->file); | if (cart->file) fclose(cart->file); | ||||
| free(cart->filename); | free(cart->filename); | ||||
| @@ -5,6 +5,24 @@ | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include "input.h" | |||||
| #include "overlay.h" | |||||
| #include "render.h" | |||||
| #include "audio.h" | |||||
| #include "nes.h" | |||||
| // Running State | |||||
| typedef struct nese_Components { | |||||
| nes_Renderer* rend; | |||||
| nes_Input_Reader* reader; | |||||
| nes_Audio_Stream* audio; | |||||
| nes* sys; | |||||
| } nese_Components; | |||||
| // Persistent State | |||||
| typedef struct { | typedef struct { | ||||
| char* filename; | char* filename; | ||||