From eee13026b0c8d2beed00506f2e8ff260f5a7251b Mon Sep 17 00:00:00 2001 From: william Date: Thu, 3 Oct 2024 19:24:03 -0400 Subject: [PATCH] Add working basic component based GUI system. Allows to open and close windows without restarting. --- README.md | 4 +- gui/CMakeLists.txt | 9 +- gui/actions.c | 17 +++ gui/actions.h | 15 +++ gui/components/window.c | 4 + gui/components/window.h | 1 + gui/components/window_menu.c | 11 +- gui/components/window_menu.h | 12 ++- gui/dbg_nametable.c | 8 ++ gui/dbg_nametable.h | 1 + gui/dbg_palette.c | 2 +- gui/dbg_palette.h | 2 +- gui/dbg_pattern_table.c | 6 ++ gui/dbg_pattern_table.h | 2 + gui/gui.c | 199 +++++++++++++++++++++-------------- gui/gui.h | 41 +++++++- gui/main_window.c | 54 +++++----- gui/main_window.h | 23 ++-- gui/nametable_window.c | 26 +++-- gui/nametable_window.h | 28 +++-- gui/pattern_window.c | 27 +++-- gui/pattern_window.h | 36 +++++-- include/ppu.h | 4 - main.c | 14 ++- ppu/ppu.c | 4 +- 25 files changed, 364 insertions(+), 186 deletions(-) create mode 100644 gui/actions.c create mode 100644 gui/actions.h diff --git a/README.md b/README.md index d7d5229..370336e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ This is the repository of a Nintendo Entertainment System (NES) emulator in acti currently complete but can run and partially display a ROM. Note that the project may support Windows and MacOS, but was only tested on Linux. Here is how to run the project: - Change the ```rom_path``` at line 26 of ```main.c``` - - Optionally, change ```gui.debug_enabled``` to ```true``` at line 33 of ```gui.c``` to enable debugging - Generate the Makefile with CMake: ```cmake .``` - Build the project with Make: ```make``` - Run the emulator: ```./nes_emulator``` @@ -21,6 +20,7 @@ only tested on Linux. Here is how to run the project: - SDL ## Development Roadmap +- Basic component based GUI: Done - CPU - RAM: Done - ROM: Done (iNes 1.0 format only) @@ -42,7 +42,7 @@ only tested on Linux. Here is how to run the project: - MMC5: To Do - ... - Debug - - Frame Delay: Done + - Frame Delay: Broken, removed - Pattern Table Viewer: Done - Nametable Viewer: Done - CPU Debugger: To Do diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index d93b2e4..aed2422 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -1,10 +1,5 @@ -set(HEADERS gui.h main_window.h) -set(SOURCE gui.c main_window.c) - -if (NES_DEBUG) - list(APPEND HEADERS char_map.h pattern_window.h nametable_window.h dbg_pattern_table.h dbg_nametable.h dbg_palette.h) - list(APPEND SOURCE char_map.c pattern_window.c nametable_window.c dbg_pattern_table.c dbg_nametable.c dbg_palette.c) -endif (NES_DEBUG) +set(HEADERS actions.h gui.h main_window.h char_map.h pattern_window.h nametable_window.h dbg_pattern_table.h dbg_nametable.h dbg_palette.h) +set(SOURCE actions.c gui.c main_window.c char_map.c pattern_window.c nametable_window.c dbg_pattern_table.c dbg_nametable.c dbg_palette.c) add_library(nes_gui ${SOURCE} ${HEADERS}) diff --git a/gui/actions.c b/gui/actions.c new file mode 100644 index 0000000..4a3d4c5 --- /dev/null +++ b/gui/actions.c @@ -0,0 +1,17 @@ +// +// Created by william on 9/2/24. +// + +#include "actions.h" +#include "gui.h" + +void process_action(ActionType type) { + switch (type) { + case ACTION_TYPE_OPEN_WINDOW_NAMETABLE: + gui_window_create(WINDOW_TYPE_NAMETABLE); + break; + case ACTION_TYPE_OPEN_WINDOW_PATTERN_TABLE: + gui_window_create(WINDOW_TYPE_PATTERN_TABLE); + break; + } +} diff --git a/gui/actions.h b/gui/actions.h new file mode 100644 index 0000000..7fba24f --- /dev/null +++ b/gui/actions.h @@ -0,0 +1,15 @@ +// +// Created by william on 9/2/24. +// + +#ifndef NES_EMULATOR_ACTIONS_H +#define NES_EMULATOR_ACTIONS_H + +typedef enum { + ACTION_TYPE_OPEN_WINDOW_NAMETABLE = 1, + ACTION_TYPE_OPEN_WINDOW_PATTERN_TABLE = 2 +} ActionType; + +void process_action(ActionType type); + +#endif //NES_EMULATOR_ACTIONS_H diff --git a/gui/components/window.c b/gui/components/window.c index a2d86ad..8cb248c 100644 --- a/gui/components/window.c +++ b/gui/components/window.c @@ -7,6 +7,9 @@ #include "log.h" #include "component.h" +// A sequential window ID +static int next_window_id = 1; + /** * Easy declaration of commonly used component loop. The current component is in the 'component' variable. * Put the loop's body between FOR_EACH_COMPONENT and END_FOR_EACH_COMPONENT. @@ -68,6 +71,7 @@ Window window_create(char *title, int width, int height, int scale) { } Window window; + window.id = next_window_id++; window.width = width; window.height = height; window.scale = scale; diff --git a/gui/components/window.h b/gui/components/window.h index 78be6aa..2817b08 100644 --- a/gui/components/window.h +++ b/gui/components/window.h @@ -15,6 +15,7 @@ typedef struct window_sdl_context { } WindowSdlContext; typedef struct window { + int id; int width; int height; int scale; diff --git a/gui/components/window_menu.c b/gui/components/window_menu.c index cbe2b95..50833a9 100644 --- a/gui/components/window_menu.c +++ b/gui/components/window_menu.c @@ -49,12 +49,13 @@ static inline MenuItemComponent *menu_get_next_item(LinkedListCursor *cursor) { return next_node->data; } -MenuComponent *menu_create(Window *window, TTF_Font *font) { +MenuComponent *menu_create(Window *window, TTF_Font *font, menu_action_processor action_processor) { MenuComponent *menu = malloc(sizeof(MenuComponent)); menu->window_width = window->width * window->scale; menu->visible = false; + menu->action_processor = action_processor; menu->items = linked_list_create(false); menu->highlight_item = NULL; @@ -66,11 +67,11 @@ MenuComponent *menu_create(Window *window, TTF_Font *font) { return menu; } -MenuItemComponent *menu_item_create(char *label, on_click_callback on_click) { +MenuItemComponent *menu_item_create(char *label, int click_action_type) { MenuItemComponent *menu_item = malloc(sizeof(MenuItemComponent)); menu_item->label = label; - menu_item->on_click = on_click; + menu_item->click_action_type = click_action_type; menu_item->sub_items = linked_list_create(false); menu_item->is_highlighted = false; @@ -234,7 +235,9 @@ bool menu_mouse_click(MenuComponent *menu) { return false; } - log_info("%s", menu->highlight_item->label); + int action_type = menu->highlight_item->click_action_type; + menu->action_processor(action_type); + return true; } diff --git a/gui/components/window_menu.h b/gui/components/window_menu.h index 1b33715..5fb2fa3 100644 --- a/gui/components/window_menu.h +++ b/gui/components/window_menu.h @@ -17,11 +17,9 @@ #define MENU_ITEM_MARGIN_X 20 #define MENU_ITEM_MARGIN_Y 4 -typedef void (*on_click_callback)(); - typedef struct menu_item_component { char *label; - on_click_callback on_click; + int click_action_type; bool is_highlighted; @@ -31,10 +29,13 @@ typedef struct menu_item_component { SDL_Rect collision_rect; } MenuItemComponent; +typedef void (*menu_action_processor)(int); + typedef struct menu_component { int window_width; bool visible; + menu_action_processor action_processor; LinkedList items; MenuItemComponent *highlight_item; @@ -48,9 +49,10 @@ typedef struct menu_component { * Creates a menu fow a window. * @param window A reference to the window * @param font A reference to the TTF font to use to render text + * @param action_processor A reference to a function that will processes actions * @return A reference to the menu component */ -MenuComponent *menu_create(Window *window, TTF_Font *font); +MenuComponent *menu_create(Window *window, TTF_Font *font, menu_action_processor action_processor); /** * Creates a menu item. Can be configured with a callback function which will be called when the menu item is clicked. @@ -59,7 +61,7 @@ MenuComponent *menu_create(Window *window, TTF_Font *font); * @param on_click The callback function to call when clicked * @return A reference to the menu item */ -MenuItemComponent *menu_item_create(char *label, on_click_callback on_click); +MenuItemComponent *menu_item_create(char *label, int click_action_type); /** * Adds an item to a menu. diff --git a/gui/dbg_nametable.c b/gui/dbg_nametable.c index 6fe3c70..7c9fc03 100644 --- a/gui/dbg_nametable.c +++ b/gui/dbg_nametable.c @@ -12,7 +12,15 @@ DebugNameTable dbg_nametable; void dbg_nametable_init() { + if (dbg_nametable.initialized) { + // Already initialized + return; + } + + dbg_pattern_table_init(); + dbg_nametable.vertical_mirroring = rom_get()->nametable_mirrored; + dbg_nametable.initialized = true; } void dbg_nametable_build_bank(byte *nametable, DebugTile *bank) { diff --git a/gui/dbg_nametable.h b/gui/dbg_nametable.h index 4d14d36..4a7cb8e 100644 --- a/gui/dbg_nametable.h +++ b/gui/dbg_nametable.h @@ -22,6 +22,7 @@ typedef struct dbg_nametable { DebugTile bank_0[NAMETABLE_BANK_SIZE]; DebugTile bank_1[NAMETABLE_BANK_SIZE]; bool vertical_mirroring; + bool initialized; } DebugNameTable; /** diff --git a/gui/dbg_palette.c b/gui/dbg_palette.c index 7f40aaf..b82f480 100644 --- a/gui/dbg_palette.c +++ b/gui/dbg_palette.c @@ -18,7 +18,7 @@ pixel dbg_color_list[0x40] = COLOR_LIST; COPY_PALETTE((memory)[(base_addr) + 0xd], (dest)[3]) \ -void dbg_palette_init() { +void dbg_palette_update() { byte *memory = ppu_get_state()->memory.palette; palette_memory.universal_background_color = memory[0]; diff --git a/gui/dbg_palette.h b/gui/dbg_palette.h index 38566e2..7c95a89 100644 --- a/gui/dbg_palette.h +++ b/gui/dbg_palette.h @@ -19,7 +19,7 @@ typedef struct dbg_palette_memory { DebugPalette sprite_palettes[PALETTE_COUNT]; } DebugPaletteMemory; -void dbg_palette_init(); +void dbg_palette_update(); pixel dbg_get_background_color(byte palette, byte data); diff --git a/gui/dbg_pattern_table.c b/gui/dbg_pattern_table.c index ac49fb1..e64ab64 100644 --- a/gui/dbg_pattern_table.c +++ b/gui/dbg_pattern_table.c @@ -23,10 +23,16 @@ void dbg_pattern_table_build_bank(DebugPattern *bank, byte *pattern_memory) { } void dbg_pattern_table_init() { + if (pattern_table.initialized) { + // Already initialized + return; + } + byte *pattern_memory = system_get_mapper()->ppu_read(0); dbg_pattern_table_build_bank(pattern_table.bank_0, pattern_memory); dbg_pattern_table_build_bank(pattern_table.bank_1, &pattern_memory[PATTERN_BANK_SIZE]); + pattern_table.initialized = true; } DebugPattern dbg_pattern_get(int pattern_id, int bank) { diff --git a/gui/dbg_pattern_table.h b/gui/dbg_pattern_table.h index 337fbbb..1a0b5c2 100644 --- a/gui/dbg_pattern_table.h +++ b/gui/dbg_pattern_table.h @@ -5,6 +5,7 @@ #ifndef NES_EMULATOR_DBG_PATTERN_TABLE_H #define NES_EMULATOR_DBG_PATTERN_TABLE_H +#include #include "../include/types.h" #define PATTERN_BANK_SIZE 0x1000 @@ -29,6 +30,7 @@ typedef struct dbg_pattern { typedef struct dbg_pattern_table { DebugPattern bank_0[PATTERN_TABLE_SIZE]; DebugPattern bank_1[PATTERN_TABLE_SIZE]; + bool initialized; } DebugPatternTable; /** diff --git a/gui/gui.c b/gui/gui.c index 49b3a9c..a494eb2 100644 --- a/gui/gui.c +++ b/gui/gui.c @@ -3,6 +3,7 @@ // #include +#include #include "log.h" #include "gui.h" #include "main_window.h" @@ -14,29 +15,95 @@ #include "char_map.h" #include "dbg_palette.h" -#if DEBUG -#define WINDOW_ID_MAIN 3 -#else -#define WINDOW_ID_MAIN 1 -#endif - typedef struct nes_gui { - NesMainWindow main_window; - NesPatternWindow pattern_window; - NesNametableWindow nametable_window; + bool window_types_open[WINDOW_TYPE_MAX + 1]; + void *window_types_ref[WINDOW_TYPE_MAX + 1]; + int window_types_ids[WINDOW_TYPE_MAX + 1]; TTF_Font *font; Uint32 last_frame_tick; Uint32 frame_delay; -#if DEBUG unsigned long tick; -#endif } NesGui; NesGui gui; +#define GUI_WINDOW_OPEN_(window_type) gui.window_types_open[window_type] +#define GUI_WINDOW_REF_(window_type) gui.window_types_ref[window_type] +#define GUI_WINDOW_ACTION_BASE(window_type, call) \ + if (GUI_WINDOW_OPEN_(window_type)) { \ + call(GUI_WINDOW_REF_(window_type)); \ + } + +void gui_window_create(WindowType type) { + void **ref = &gui.window_types_ref[type]; + if (gui.window_types_open[type]) { + return; + } + gui.window_types_open[type] = true; + int window_id = -1; + + switch (type) { + case WINDOW_TYPE_MAIN: + *ref = main_window_create(&window_id); + break; + case WINDOW_TYPE_NAMETABLE: + *ref = nametable_window_create(&window_id); + break; + case WINDOW_TYPE_PATTERN_TABLE: + *ref = pattern_window_create(&window_id); + break; + } + + gui.window_types_ids[type] = window_id; +} + +void gui_window_destroy(WindowType type) { + bool *open = &gui.window_types_open[type]; + if (*open == false) { + // The window doesn't exist + return; + } + + void *ref = gui.window_types_ref[type]; + switch (type) { + case WINDOW_TYPE_MAIN: + main_window_destroy(ref); + break; + case WINDOW_TYPE_NAMETABLE: + nametable_window_destroy(ref); + break; + case WINDOW_TYPE_PATTERN_TABLE: + pattern_window_destroy(ref); + break; + } + + gui.window_types_ids[type] = -1; + *open = false; +} + +void gui_window_destroy_by_id(int window_id) { + WindowType type = -1; + for (int i = 0; i <= WINDOW_TYPE_MAX; i++) { + if (gui.window_types_ids[i] == window_id) { + type = i; + } + } + + if (type == -1) { + // Close event is sent twice? + log_error("Couldn't find window with ID %d", window_id); + return; + } + + gui_window_destroy(type); +} + bool gui_init() { + memset(gui.window_types_open, false, sizeof(bool) * WINDOW_TYPE_MAX); + memset(gui.window_types_ref, 0, sizeof(void *) * WINDOW_TYPE_MAX); + TTF_Init(); gui.font = TTF_OpenFont("./nintendo-nes-font.ttf", 16); if (gui.font == NULL) { @@ -44,56 +111,40 @@ bool gui_init() { return false; } -#if DEBUG - gui.tick = 0; - pattern_window_init(&gui.pattern_window); - nametable_window_init(&gui.nametable_window); - - char_map_init(gui.main_window.window.sdl_context.renderer, gui.font); -#endif - - main_window_init(&gui.main_window); + memset(gui.window_types_ids, -1, (WINDOW_TYPE_MAX + 1) * sizeof(*gui.window_types_ids)); + gui_window_create(WINDOW_TYPE_MAIN); return true; } -void gui_uninit() { - main_window_uninit(&gui.main_window); - -#if DEBUG - char_map_uninit(); - - pattern_window_uninit(&gui.pattern_window); - nametable_window_uninit(&gui.nametable_window); -#endif +void gui_free() { + for (int i = 0; i < WINDOW_TYPE_MAX; i++) { + gui_window_destroy(i); + } TTF_CloseFont(gui.font); } -void gui_post_sysinit() { -#if DEBUG - dbg_palette_init(); - dbg_pattern_table_init(); - dbg_nametable_init(); - - // TODO: The background_texture is rendered before the palette data is in the PPU memory, so the only color is grey - pattern_window_build_table(&gui.pattern_window); - nametable_window_update(&gui.nametable_window); -#endif -} - bool lctrl = false; int gui_input() { + assert(gui.window_types_open[WINDOW_TYPE_MAIN]); SDL_Event event; -#if DEBUG PPUDebugFlags *ppu_debug = &ppu_get_state()->debug; -#endif while (SDL_PollEvent(&event)) { if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE) { - return -1; + Uint32 window_id = event.window.windowID; + for (int type = 0; type <= WINDOW_TYPE_MAX; type++) { + bool is_main_window = gui.window_types_ids[WINDOW_TYPE_MAIN] == window_id; + + gui_window_destroy_by_id(window_id); + + if (is_main_window) { + return -1; + } + } } if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_LCTRL) { @@ -108,59 +159,53 @@ int gui_input() { case SDLK_p: system_toggle_pause(lctrl); break; -#if DEBUG - case SDLK_t: - ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger; - break; - case SDLK_n: - ppu_debug->flags.tile_debugger_pattern_half = (ppu_debug->flags.tile_debugger_pattern_half + 1) % 3; - break; - default: - pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym); - break; -#else - default: + case SDLK_t: + ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger; + break; + case SDLK_n: + ppu_debug->flags.tile_debugger_pattern_half = (ppu_debug->flags.tile_debugger_pattern_half + 1) % 3; + break; + default: + if (gui.window_types_open[WINDOW_TYPE_PATTERN_TABLE]) { + pattern_window_key_up(gui.window_types_ref[WINDOW_TYPE_PATTERN_TABLE], event.key.keysym.sym); + } break; -#endif } } + void *main_window_ref = gui.window_types_ref[WINDOW_TYPE_MAIN]; if (event.type == SDL_MOUSEMOTION) { int x = event.motion.x; int y = event.motion.y; - if (event.window.windowID == WINDOW_ID_MAIN) { - main_window_mouse_motion(&gui.main_window, x, y); + if (gui.window_types_ids[WINDOW_TYPE_MAIN] == event.window.windowID) { + main_window_mouse_motion(main_window_ref, x, y); } } - if (event.type == SDL_MOUSEBUTTONUP && event.window.windowID == WINDOW_ID_MAIN) { - main_window_mouse_click(&gui.main_window); + if (event.type == SDL_MOUSEBUTTONUP && gui.window_types_ids[WINDOW_TYPE_MAIN] == event.window.windowID) { + main_window_mouse_click(main_window_ref); } } return 1; } +#define GUI_WINDOW_RENDER(window_type, prefix) GUI_WINDOW_ACTION_BASE(window_type, prefix ## _window_render) + void gui_render() { - main_window_render(&gui.main_window, ppu_get_state()->pixels); + assert(gui.window_types_open[WINDOW_TYPE_MAIN]); + main_window_render(gui.window_types_ref[WINDOW_TYPE_MAIN], ppu_get_state()->pixels); -#if DEBUG - dbg_palette_init(); - pattern_window_render(&gui.pattern_window); + // Update the nametable + GUI_WINDOW_ACTION_BASE(WINDOW_TYPE_NAMETABLE, nametable_window_update); + GUI_WINDOW_RENDER(WINDOW_TYPE_NAMETABLE, nametable); - nametable_window_update(&gui.nametable_window); - nametable_window_render(&gui.nametable_window); + // Update the pattern table + GUI_WINDOW_ACTION_BASE(WINDOW_TYPE_PATTERN_TABLE, pattern_window_update); + GUI_WINDOW_RENDER(WINDOW_TYPE_PATTERN_TABLE, pattern) gui.tick++; -#endif -} - -void gui_present() { -#if DEBUG - pattern_window_present(&gui.pattern_window); - nametable_window_present(&gui.nametable_window); -#endif } void gui_delay() { @@ -178,8 +223,4 @@ void gui_delay() { TTF_Font *gui_get_font() { return gui.font; -} - -unsigned int gui_get_frame_delay() { - return gui.frame_delay; } \ No newline at end of file diff --git a/gui/gui.h b/gui/gui.h index 6c0a96b..eaaa351 100644 --- a/gui/gui.h +++ b/gui/gui.h @@ -8,17 +8,50 @@ #include #include +typedef enum { + WINDOW_TYPE_MAIN = 0, + WINDOW_TYPE_NAMETABLE = 1, + WINDOW_TYPE_PATTERN_TABLE = 2, +} WindowType; +#define WINDOW_TYPE_MAX WINDOW_TYPE_PATTERN_TABLE + +/** + * Initializes the graphical user interface of the emulator. + * @return A boolean indicating if the GUI was successfully initialized. + */ bool gui_init(); -void gui_uninit(); -void gui_post_sysinit(); +/** + * Free the resources used by the graphical user interface. + */ +void gui_free(); +/** + * Creates and open a window. If a window of the given type already exists, focus it. + * @param type The type of window to open. + */ +void gui_window_create(WindowType type); + +/** + * Process user input events received since the last call to gui_input. + * @return An integer indicating if the user closed the main window (-1). + */ int gui_input(); + +/** + * Renders the graphical user interface to the screen. + */ void gui_render(); -void gui_present(); + +/** + * Blocks until the next frame should be drawn, making the frame rate 60 hertz. + */ void gui_delay(); +/** + * Gets the font used for the graphical user interface. + * @return A reference to the TTF font. + */ TTF_Font* gui_get_font(); -unsigned int gui_get_frame_delay(); #endif //NES_EMULATOR_GUI_H diff --git a/gui/main_window.c b/gui/main_window.c index 2b824c7..239fe13 100644 --- a/gui/main_window.c +++ b/gui/main_window.c @@ -8,52 +8,46 @@ #include "char_map.h" #include "gui.h" #include "components/window_menu.h" +#include "actions.h" -void main_window_init(NesMainWindow *window) { +void main_window_menu_process_action(int action_type) { + process_action(action_type); +} + +MainWindow *main_window_create(int* window_id) { + MainWindow *window = malloc(sizeof(MainWindow)); window->window = window_create("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE); window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING); - MenuComponent *menu = menu_create(&window->window, gui_get_font()); + MenuComponent *menu = menu_create(&window->window, gui_get_font(), &main_window_menu_process_action); - MenuItemComponent *mi_file = menu_item_create("FILE", NULL); - menu_append(menu, mi_file); - MenuItemComponent *mi_file_open = menu_item_create("OPEN ROM...", NULL); - menu_item_append(mi_file, mi_file_open); +// MenuItemComponent *mi_file = menu_item_create("FILE", ACTION_TYPE_OPEN_); +// menu_append(menu, mi_file); +// MenuItemComponent *mi_file_open = menu_item_create("OPEN ROM...", NULL); +// menu_item_append(mi_file, mi_file_open); - MenuItemComponent *mi_debug = menu_item_create("DEBUG", NULL); + MenuItemComponent *mi_debug = menu_item_create("DEBUG", -1); menu_append(menu, mi_debug); - MenuItemComponent *mi_debug_nametable = menu_item_create("NAMETABLE", NULL); + MenuItemComponent *mi_debug_nametable = menu_item_create("NAMETABLE", ACTION_TYPE_OPEN_WINDOW_NAMETABLE); menu_item_append(mi_debug, mi_debug_nametable); - MenuItemComponent *mi_debug_pattern = menu_item_create("PATTERN TABLE", NULL); + MenuItemComponent *mi_debug_pattern = menu_item_create("PATTERN TABLE", ACTION_TYPE_OPEN_WINDOW_PATTERN_TABLE); menu_item_append(mi_debug, mi_debug_pattern); menu_build(menu); window_add_component(&window->window, menu_as_component(menu)); + + *window_id = window->window.id; + return window; } -void main_window_uninit(NesMainWindow *window) { +void main_window_destroy(MainWindow *window) { SDL_DestroyTexture(window->texture); window_destroy(&window->window); + + free(window); } -#if DEBUG - -void main_window_render_delay(SDL_Renderer *renderer) { - Uint32 delay = gui_get_frame_delay(); - - char buffer[5]; - buffer[0] = (char) ((delay / 10) + 48); - buffer[1] = (char) ((delay % 10) + 48); - buffer[2] = ' '; - buffer[3] = 'm'; - buffer[4] = 's'; - - char_map_render(renderer, buffer); -} - -#endif - -void main_window_render(NesMainWindow *window, pixel *pixels) { +void main_window_render(MainWindow *window, pixel *pixels) { SDL_UpdateTexture(window->texture, NULL, pixels, 256 * sizeof(pixel)); window_render_texture(&window->window, window->texture); @@ -62,10 +56,10 @@ void main_window_render(NesMainWindow *window, pixel *pixels) { window_present(&window->window); } -void main_window_mouse_motion(NesMainWindow *window, int x, int y) { +void main_window_mouse_motion(MainWindow *window, int x, int y) { window_mouse_motion(&window->window, x, y); } -void main_window_mouse_click(NesMainWindow *window) { +void main_window_mouse_click(MainWindow *window) { window_mouse_click(&window->window); } \ No newline at end of file diff --git a/gui/main_window.h b/gui/main_window.h index 60a08be..828fe78 100644 --- a/gui/main_window.h +++ b/gui/main_window.h @@ -17,13 +17,24 @@ typedef struct nes_main_window { Window window; SDL_Texture *texture; -} NesMainWindow; +} MainWindow; -void main_window_init(NesMainWindow *window); -void main_window_uninit(NesMainWindow *window); +/** + * Creates an instance of the main window. + * @return A reference to the created main window instance + */ +MainWindow *main_window_create(int *window_id); -void main_window_render(NesMainWindow *window, pixel* pixels); -void main_window_mouse_motion(NesMainWindow *window, int x, int y); -void main_window_mouse_click(NesMainWindow *window); +/** + * Destroys an instance of the main window. + * @param window A reference to the window to destroy + */ +void main_window_destroy(MainWindow *window); + +void main_window_render(MainWindow *window, pixel *pixels); + +void main_window_mouse_motion(MainWindow *window, int x, int y); + +void main_window_mouse_click(MainWindow *window); #endif //NES_EMULATOR_MAIN_WINDOW_H diff --git a/gui/nametable_window.c b/gui/nametable_window.c index 1d3189e..544aedf 100644 --- a/gui/nametable_window.c +++ b/gui/nametable_window.c @@ -4,22 +4,32 @@ #include "nametable_window.h" #include "dbg_nametable.h" +#include "dbg_palette.h" #define NW_WIDTH (NW_ROW_TILE_COUNT * PATTERN_DRAW_SIZE) #define NW_HEIGHT (NW_ROW_COUNT * PATTERN_DRAW_SIZE) #define NW_BUFFER_SIZE (NAMETABLE_ROW_WIDTH * NAMETABLE_COL_HEIGHT * PATTERN_DRAW_SIZE * PATTERN_DRAW_SIZE) -void nametable_window_init(NesNametableWindow *window) { +NametableWindow *nametable_window_create(int *window_id) { + NametableWindow *window = malloc(sizeof(NametableWindow)); + window->window = window_create("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE); window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING); + + dbg_nametable_init(); + + *window_id = window->window.id; + return window; } -void nametable_window_uninit(NesNametableWindow *window) { +void nametable_window_destroy(NametableWindow *window) { SDL_DestroyTexture(window->texture); window_destroy(&window->window); + + free(window); } -void nametable_window_update_bank(NesNametableWindow *window, int bank, pixel *buffer) { +void nametable_window_update_bank(NametableWindow *window, int bank, pixel *buffer) { dbg_nametable_render_bank(bank, buffer); SDL_Rect rect; @@ -31,7 +41,8 @@ void nametable_window_update_bank(NesNametableWindow *window, int bank, pixel *b SDL_UpdateTexture(window->texture, &rect, buffer, (NW_WIDTH / 2) * sizeof(pixel)); } -void nametable_window_update(NesNametableWindow *window) { +void nametable_window_update(NametableWindow *window) { + dbg_palette_update(); dbg_nametable_update(); pixel buffer[NW_BUFFER_SIZE * 4] = {0}; @@ -41,10 +52,7 @@ void nametable_window_update(NesNametableWindow *window) { nametable_window_update_bank(window, 3, buffer); } -void nametable_window_render(NesNametableWindow *window) { +void nametable_window_render(NametableWindow *window) { window_render_texture(&window->window, window->texture); -} - -void nametable_window_present(NesNametableWindow *window) { window_present(&window->window); -} \ No newline at end of file +} diff --git a/gui/nametable_window.h b/gui/nametable_window.h index 1233c22..d738ce9 100644 --- a/gui/nametable_window.h +++ b/gui/nametable_window.h @@ -15,16 +15,30 @@ typedef struct nes_nametable_window { Window window; SDL_Texture *texture; -} NesNametableWindow; +} NametableWindow; -void nametable_window_init(NesNametableWindow *window); +/** + * Creates a nametable window instance. + * @return A reference to the created window + */ +NametableWindow *nametable_window_create(int *window_id); -void nametable_window_uninit(NesNametableWindow *window); +/** + * Destroys a nametable window instance. + * @param window A reference to the window to destroy + */ +void nametable_window_destroy(NametableWindow *window); -void nametable_window_update(NesNametableWindow *window); +/** + * Updates the content of a nametable window with the current data of the PPU. + * @param window A reference to the window to update + */ +void nametable_window_update(NametableWindow *window); -void nametable_window_render(NesNametableWindow *window); - -void nametable_window_present(NesNametableWindow *window); +/** + * Renders a nemtable window to the screen. + * @param window A reference to the window to render + */ +void nametable_window_render(NametableWindow *window); #endif //NES_EMULATOR_NAMETABLE_WINDOW_H diff --git a/gui/pattern_window.c b/gui/pattern_window.c index c1043fc..387e111 100644 --- a/gui/pattern_window.c +++ b/gui/pattern_window.c @@ -4,23 +4,35 @@ #include "pattern_window.h" #include "dbg_pattern_table.h" +#include "dbg_palette.h" #define PW_WIDTH (PW_ROW_TILE_COUNT * PATTERN_DRAW_SIZE) #define PW_HEIGHT (PW_WIDTH * 2) #define PW_BUFFER_SIZE (PW_WIDTH * PW_HEIGHT) -void pattern_window_init(NesPatternWindow *window) { +PatternWindow *pattern_window_create(int *window_id) { + PatternWindow *window = malloc(sizeof(PatternWindow)); + window->window = window_create("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE); window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STATIC); window->palette = 0; + + dbg_pattern_table_init(); + + *window_id = window->window.id; + return window; } -void pattern_window_uninit(NesPatternWindow *window) { +void pattern_window_destroy(PatternWindow *window) { SDL_DestroyTexture(window->texture); window_destroy(&window->window); + + free(window); } -void pattern_window_build_table(NesPatternWindow *window) { +void pattern_window_update(PatternWindow *window) { + dbg_palette_update(); + pixel buffer[PW_BUFFER_SIZE] = {0}; dbg_pattern_draw_bank(PATTERN_BANK_0, buffer, window->palette); dbg_pattern_draw_bank(PATTERN_BANK_1, &buffer[PW_WIDTH * (PW_HEIGHT / 2)], window->palette); @@ -28,7 +40,7 @@ void pattern_window_build_table(NesPatternWindow *window) { SDL_UpdateTexture(window->texture, NULL, buffer, PW_WIDTH * sizeof(pixel)); } -void pattern_window_key_up(NesPatternWindow *window, SDL_KeyCode keycode) { +void pattern_window_key_up(PatternWindow *window, SDL_KeyCode keycode) { if (keycode != SDLK_o) { return; } @@ -38,13 +50,10 @@ void pattern_window_key_up(NesPatternWindow *window, SDL_KeyCode keycode) { window->palette = 0; } - pattern_window_build_table(window); + pattern_window_update(window); } -void pattern_window_render(NesPatternWindow *window) { +void pattern_window_render(PatternWindow *window) { window_render_texture(&window->window, window->texture); -} - -void pattern_window_present(NesPatternWindow *window) { window_present(&window->window); } \ No newline at end of file diff --git a/gui/pattern_window.h b/gui/pattern_window.h index caa8349..9a1161e 100644 --- a/gui/pattern_window.h +++ b/gui/pattern_window.h @@ -16,15 +16,37 @@ typedef struct nes_pattern_window { Window window; SDL_Texture *texture; byte palette; -} NesPatternWindow; +} PatternWindow; -void pattern_window_init(NesPatternWindow *window); -void pattern_window_uninit(NesPatternWindow *window); +/** + * Creates a pattern window instance. + * @return A reference to the created window instance + */ +PatternWindow *pattern_window_create(int *window_id); -void pattern_window_build_table(NesPatternWindow *window); +/** + * Destroys a pattern window instance. + * @param window A reference to the window to destroy + */ +void pattern_window_destroy(PatternWindow *window); -void pattern_window_key_up(NesPatternWindow *window, SDL_KeyCode keycode); -void pattern_window_render(NesPatternWindow *window); -void pattern_window_present(NesPatternWindow *window); +/** + * Updates the content of a pattern window with the current data from the ROM mapper. + * @param window A reference to the window to update + */ +void pattern_window_update(PatternWindow *window); + +/** + * Renders a pattern window to the screen. + * @param window A reference to the window to render + */ +void pattern_window_render(PatternWindow *window); + +/** + * Sends a key up event to a pattern window. + * @param window A reference to the window to send the event to + * @param keycode The code of the key of the event + */ +void pattern_window_key_up(PatternWindow *window, SDL_KeyCode keycode); #endif //NES_EMULATOR_PATTERN_WINDOW_H diff --git a/include/ppu.h b/include/ppu.h index 4b2fda8..852dcd3 100644 --- a/include/ppu.h +++ b/include/ppu.h @@ -69,7 +69,6 @@ typedef struct ppu_tile_fetch { byte pattern_table_tile_high; } PPUTileFetch; -#if DEBUG typedef union { struct { byte tile_debugger: 1; @@ -77,7 +76,6 @@ typedef union { } flags; byte flags_byte; } PPUDebugFlags; -#endif typedef struct ppu { PPUMemory memory; @@ -103,9 +101,7 @@ typedef struct ppu { unsigned int scanline; unsigned int cycle; -#if DEBUG PPUDebugFlags debug; -#endif } PPU; PPU *ppu_get_state(); diff --git a/main.c b/main.c index 125b706..2bb2647 100644 --- a/main.c +++ b/main.c @@ -101,30 +101,28 @@ int main() { if (!rom_load(rom_path)) { system_uninit(); - gui_uninit(); + gui_free(); return EXIT_FAILURE; } - gui_post_sysinit(); system_start(); - bool stop = false; - while (!stop) { + while (true) { if (gui_input() < 0) { - stop = true; + // The main window has been closed, stop the emulation + break; } system_next_frame(); - gui_render(); - gui_present(); + // Delay the next frame to lock the emulation to 60hz gui_delay(); } system_uninit(); rom_unload(); - gui_uninit(); + gui_free(); close_logging(); diff --git a/ppu/ppu.c b/ppu/ppu.c index b03f086..08143a6 100644 --- a/ppu/ppu.c +++ b/ppu/ppu.c @@ -20,8 +20,8 @@ #include "../cpu/cpu.h" #include "../include/rom.h" #include "colors.h" -#include "tile_debugger.h" #include "log.h" +#include "tile_debugger.h" #define PPU_SCANLINE_VISIBLE_MAX 240 #define PPU_SCANLINE_POST_RENDER_MIN PPU_SCANLINE_VISIBLE_MAX @@ -132,7 +132,6 @@ void ppu_draw_tile() { } byte ppu_get_pattern(byte tile_index, byte high) { -#if DEBUG if (ppu_state.debug.flags.tile_debugger) { if ((ppu_state.debug.flags.tile_debugger_pattern_half == 1 && high) || (ppu_state.debug.flags.tile_debugger_pattern_half == 2 && !high)) { @@ -141,7 +140,6 @@ byte ppu_get_pattern(byte tile_index, byte high) { return tile_debugger_encode_number_as_pattern(tile_index, ppu_state.scanline % 8); } -#endif byte tile_row_index = (ppu_state.scanline + ppu_state.y_scroll) % 8; address pattern_addr = ppu_state.bg_pattern_table_addr | tile_index << 4 | high << 3 | tile_row_index;