Add working basic component based GUI system. Allows to open and close windows without restarting.
This commit is contained in:
parent
f18ad715fb
commit
eee13026b0
|
@ -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
|
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:
|
only tested on Linux. Here is how to run the project:
|
||||||
- Change the ```rom_path``` at line 26 of ```main.c```
|
- 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 .```
|
- Generate the Makefile with CMake: ```cmake .```
|
||||||
- Build the project with Make: ```make```
|
- Build the project with Make: ```make```
|
||||||
- Run the emulator: ```./nes_emulator```
|
- Run the emulator: ```./nes_emulator```
|
||||||
|
@ -21,6 +20,7 @@ only tested on Linux. Here is how to run the project:
|
||||||
- SDL
|
- SDL
|
||||||
|
|
||||||
## Development Roadmap
|
## Development Roadmap
|
||||||
|
- Basic component based GUI: Done
|
||||||
- CPU
|
- CPU
|
||||||
- RAM: Done
|
- RAM: Done
|
||||||
- ROM: Done (iNes 1.0 format only)
|
- 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
|
- MMC5: To Do
|
||||||
- ...
|
- ...
|
||||||
- Debug
|
- Debug
|
||||||
- Frame Delay: Done
|
- Frame Delay: Broken, removed
|
||||||
- Pattern Table Viewer: Done
|
- Pattern Table Viewer: Done
|
||||||
- Nametable Viewer: Done
|
- Nametable Viewer: Done
|
||||||
- CPU Debugger: To Do
|
- CPU Debugger: To Do
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
set(HEADERS gui.h main_window.h)
|
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 gui.c main_window.c)
|
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)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
add_library(nes_gui ${SOURCE} ${HEADERS})
|
add_library(nes_gui ${SOURCE} ${HEADERS})
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -7,6 +7,9 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "component.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.
|
* 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.
|
* 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 window;
|
||||||
|
window.id = next_window_id++;
|
||||||
window.width = width;
|
window.width = width;
|
||||||
window.height = height;
|
window.height = height;
|
||||||
window.scale = scale;
|
window.scale = scale;
|
||||||
|
|
|
@ -15,6 +15,7 @@ typedef struct window_sdl_context {
|
||||||
} WindowSdlContext;
|
} WindowSdlContext;
|
||||||
|
|
||||||
typedef struct window {
|
typedef struct window {
|
||||||
|
int id;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int scale;
|
int scale;
|
||||||
|
|
|
@ -49,12 +49,13 @@ static inline MenuItemComponent *menu_get_next_item(LinkedListCursor *cursor) {
|
||||||
return next_node->data;
|
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));
|
MenuComponent *menu = malloc(sizeof(MenuComponent));
|
||||||
|
|
||||||
menu->window_width = window->width * window->scale;
|
menu->window_width = window->width * window->scale;
|
||||||
menu->visible = false;
|
menu->visible = false;
|
||||||
|
|
||||||
|
menu->action_processor = action_processor;
|
||||||
menu->items = linked_list_create(false);
|
menu->items = linked_list_create(false);
|
||||||
menu->highlight_item = NULL;
|
menu->highlight_item = NULL;
|
||||||
|
|
||||||
|
@ -66,11 +67,11 @@ MenuComponent *menu_create(Window *window, TTF_Font *font) {
|
||||||
return menu;
|
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));
|
MenuItemComponent *menu_item = malloc(sizeof(MenuItemComponent));
|
||||||
|
|
||||||
menu_item->label = label;
|
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->sub_items = linked_list_create(false);
|
||||||
menu_item->is_highlighted = false;
|
menu_item->is_highlighted = false;
|
||||||
|
|
||||||
|
@ -234,7 +235,9 @@ bool menu_mouse_click(MenuComponent *menu) {
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,9 @@
|
||||||
#define MENU_ITEM_MARGIN_X 20
|
#define MENU_ITEM_MARGIN_X 20
|
||||||
#define MENU_ITEM_MARGIN_Y 4
|
#define MENU_ITEM_MARGIN_Y 4
|
||||||
|
|
||||||
typedef void (*on_click_callback)();
|
|
||||||
|
|
||||||
typedef struct menu_item_component {
|
typedef struct menu_item_component {
|
||||||
char *label;
|
char *label;
|
||||||
on_click_callback on_click;
|
int click_action_type;
|
||||||
|
|
||||||
bool is_highlighted;
|
bool is_highlighted;
|
||||||
|
|
||||||
|
@ -31,10 +29,13 @@ typedef struct menu_item_component {
|
||||||
SDL_Rect collision_rect;
|
SDL_Rect collision_rect;
|
||||||
} MenuItemComponent;
|
} MenuItemComponent;
|
||||||
|
|
||||||
|
typedef void (*menu_action_processor)(int);
|
||||||
|
|
||||||
typedef struct menu_component {
|
typedef struct menu_component {
|
||||||
int window_width;
|
int window_width;
|
||||||
bool visible;
|
bool visible;
|
||||||
|
|
||||||
|
menu_action_processor action_processor;
|
||||||
LinkedList items;
|
LinkedList items;
|
||||||
MenuItemComponent *highlight_item;
|
MenuItemComponent *highlight_item;
|
||||||
|
|
||||||
|
@ -48,9 +49,10 @@ typedef struct menu_component {
|
||||||
* Creates a menu fow a window.
|
* Creates a menu fow a window.
|
||||||
* @param window A reference to the window
|
* @param window A reference to the window
|
||||||
* @param font A reference to the TTF font to use to render text
|
* @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
|
* @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.
|
* 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
|
* @param on_click The callback function to call when clicked
|
||||||
* @return A reference to the menu item
|
* @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.
|
* Adds an item to a menu.
|
||||||
|
|
|
@ -12,7 +12,15 @@
|
||||||
DebugNameTable dbg_nametable;
|
DebugNameTable dbg_nametable;
|
||||||
|
|
||||||
void dbg_nametable_init() {
|
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.vertical_mirroring = rom_get()->nametable_mirrored;
|
||||||
|
dbg_nametable.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg_nametable_build_bank(byte *nametable, DebugTile *bank) {
|
void dbg_nametable_build_bank(byte *nametable, DebugTile *bank) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ typedef struct dbg_nametable {
|
||||||
DebugTile bank_0[NAMETABLE_BANK_SIZE];
|
DebugTile bank_0[NAMETABLE_BANK_SIZE];
|
||||||
DebugTile bank_1[NAMETABLE_BANK_SIZE];
|
DebugTile bank_1[NAMETABLE_BANK_SIZE];
|
||||||
bool vertical_mirroring;
|
bool vertical_mirroring;
|
||||||
|
bool initialized;
|
||||||
} DebugNameTable;
|
} DebugNameTable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,7 +18,7 @@ pixel dbg_color_list[0x40] = COLOR_LIST;
|
||||||
COPY_PALETTE((memory)[(base_addr) + 0xd], (dest)[3]) \
|
COPY_PALETTE((memory)[(base_addr) + 0xd], (dest)[3]) \
|
||||||
|
|
||||||
|
|
||||||
void dbg_palette_init() {
|
void dbg_palette_update() {
|
||||||
byte *memory = ppu_get_state()->memory.palette;
|
byte *memory = ppu_get_state()->memory.palette;
|
||||||
|
|
||||||
palette_memory.universal_background_color = memory[0];
|
palette_memory.universal_background_color = memory[0];
|
||||||
|
|
|
@ -19,7 +19,7 @@ typedef struct dbg_palette_memory {
|
||||||
DebugPalette sprite_palettes[PALETTE_COUNT];
|
DebugPalette sprite_palettes[PALETTE_COUNT];
|
||||||
} DebugPaletteMemory;
|
} DebugPaletteMemory;
|
||||||
|
|
||||||
void dbg_palette_init();
|
void dbg_palette_update();
|
||||||
|
|
||||||
pixel dbg_get_background_color(byte palette, byte data);
|
pixel dbg_get_background_color(byte palette, byte data);
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,16 @@ void dbg_pattern_table_build_bank(DebugPattern *bank, byte *pattern_memory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg_pattern_table_init() {
|
void dbg_pattern_table_init() {
|
||||||
|
if (pattern_table.initialized) {
|
||||||
|
// Already initialized
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
byte *pattern_memory = system_get_mapper()->ppu_read(0);
|
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_0, pattern_memory);
|
||||||
dbg_pattern_table_build_bank(pattern_table.bank_1, &pattern_memory[PATTERN_BANK_SIZE]);
|
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) {
|
DebugPattern dbg_pattern_get(int pattern_id, int bank) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#ifndef NES_EMULATOR_DBG_PATTERN_TABLE_H
|
#ifndef NES_EMULATOR_DBG_PATTERN_TABLE_H
|
||||||
#define NES_EMULATOR_DBG_PATTERN_TABLE_H
|
#define NES_EMULATOR_DBG_PATTERN_TABLE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include "../include/types.h"
|
#include "../include/types.h"
|
||||||
|
|
||||||
#define PATTERN_BANK_SIZE 0x1000
|
#define PATTERN_BANK_SIZE 0x1000
|
||||||
|
@ -29,6 +30,7 @@ typedef struct dbg_pattern {
|
||||||
typedef struct dbg_pattern_table {
|
typedef struct dbg_pattern_table {
|
||||||
DebugPattern bank_0[PATTERN_TABLE_SIZE];
|
DebugPattern bank_0[PATTERN_TABLE_SIZE];
|
||||||
DebugPattern bank_1[PATTERN_TABLE_SIZE];
|
DebugPattern bank_1[PATTERN_TABLE_SIZE];
|
||||||
|
bool initialized;
|
||||||
} DebugPatternTable;
|
} DebugPatternTable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
183
gui/gui.c
183
gui/gui.c
|
@ -3,6 +3,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <SDL_ttf.h>
|
#include <SDL_ttf.h>
|
||||||
|
#include <assert.h>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "main_window.h"
|
#include "main_window.h"
|
||||||
|
@ -14,29 +15,95 @@
|
||||||
#include "char_map.h"
|
#include "char_map.h"
|
||||||
#include "dbg_palette.h"
|
#include "dbg_palette.h"
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
#define WINDOW_ID_MAIN 3
|
|
||||||
#else
|
|
||||||
#define WINDOW_ID_MAIN 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct nes_gui {
|
typedef struct nes_gui {
|
||||||
NesMainWindow main_window;
|
bool window_types_open[WINDOW_TYPE_MAX + 1];
|
||||||
NesPatternWindow pattern_window;
|
void *window_types_ref[WINDOW_TYPE_MAX + 1];
|
||||||
NesNametableWindow nametable_window;
|
int window_types_ids[WINDOW_TYPE_MAX + 1];
|
||||||
|
|
||||||
TTF_Font *font;
|
TTF_Font *font;
|
||||||
|
|
||||||
Uint32 last_frame_tick;
|
Uint32 last_frame_tick;
|
||||||
Uint32 frame_delay;
|
Uint32 frame_delay;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
unsigned long tick;
|
unsigned long tick;
|
||||||
#endif
|
|
||||||
} NesGui;
|
} NesGui;
|
||||||
NesGui gui;
|
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() {
|
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();
|
TTF_Init();
|
||||||
gui.font = TTF_OpenFont("./nintendo-nes-font.ttf", 16);
|
gui.font = TTF_OpenFont("./nintendo-nes-font.ttf", 16);
|
||||||
if (gui.font == NULL) {
|
if (gui.font == NULL) {
|
||||||
|
@ -44,57 +111,41 @@ bool gui_init() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
memset(gui.window_types_ids, -1, (WINDOW_TYPE_MAX + 1) * sizeof(*gui.window_types_ids));
|
||||||
gui.tick = 0;
|
gui_window_create(WINDOW_TYPE_MAIN);
|
||||||
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);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_uninit() {
|
void gui_free() {
|
||||||
main_window_uninit(&gui.main_window);
|
for (int i = 0; i < WINDOW_TYPE_MAX; i++) {
|
||||||
|
gui_window_destroy(i);
|
||||||
#if DEBUG
|
}
|
||||||
char_map_uninit();
|
|
||||||
|
|
||||||
pattern_window_uninit(&gui.pattern_window);
|
|
||||||
nametable_window_uninit(&gui.nametable_window);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TTF_CloseFont(gui.font);
|
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;
|
bool lctrl = false;
|
||||||
|
|
||||||
int gui_input() {
|
int gui_input() {
|
||||||
|
assert(gui.window_types_open[WINDOW_TYPE_MAIN]);
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
PPUDebugFlags *ppu_debug = &ppu_get_state()->debug;
|
PPUDebugFlags *ppu_debug = &ppu_get_state()->debug;
|
||||||
#endif
|
|
||||||
|
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE) {
|
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE) {
|
||||||
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_LCTRL) {
|
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_LCTRL) {
|
||||||
lctrl = true;
|
lctrl = true;
|
||||||
|
@ -108,7 +159,6 @@ int gui_input() {
|
||||||
case SDLK_p:
|
case SDLK_p:
|
||||||
system_toggle_pause(lctrl);
|
system_toggle_pause(lctrl);
|
||||||
break;
|
break;
|
||||||
#if DEBUG
|
|
||||||
case SDLK_t:
|
case SDLK_t:
|
||||||
ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger;
|
ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger;
|
||||||
break;
|
break;
|
||||||
|
@ -116,51 +166,46 @@ int gui_input() {
|
||||||
ppu_debug->flags.tile_debugger_pattern_half = (ppu_debug->flags.tile_debugger_pattern_half + 1) % 3;
|
ppu_debug->flags.tile_debugger_pattern_half = (ppu_debug->flags.tile_debugger_pattern_half + 1) % 3;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym);
|
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;
|
break;
|
||||||
#else
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *main_window_ref = gui.window_types_ref[WINDOW_TYPE_MAIN];
|
||||||
if (event.type == SDL_MOUSEMOTION) {
|
if (event.type == SDL_MOUSEMOTION) {
|
||||||
int x = event.motion.x;
|
int x = event.motion.x;
|
||||||
int y = event.motion.y;
|
int y = event.motion.y;
|
||||||
|
|
||||||
if (event.window.windowID == WINDOW_ID_MAIN) {
|
if (gui.window_types_ids[WINDOW_TYPE_MAIN] == event.window.windowID) {
|
||||||
main_window_mouse_motion(&gui.main_window, x, y);
|
main_window_mouse_motion(main_window_ref, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type == SDL_MOUSEBUTTONUP && event.window.windowID == WINDOW_ID_MAIN) {
|
if (event.type == SDL_MOUSEBUTTONUP && gui.window_types_ids[WINDOW_TYPE_MAIN] == event.window.windowID) {
|
||||||
main_window_mouse_click(&gui.main_window);
|
main_window_mouse_click(main_window_ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define GUI_WINDOW_RENDER(window_type, prefix) GUI_WINDOW_ACTION_BASE(window_type, prefix ## _window_render)
|
||||||
|
|
||||||
void gui_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
|
// Update the nametable
|
||||||
dbg_palette_init();
|
GUI_WINDOW_ACTION_BASE(WINDOW_TYPE_NAMETABLE, nametable_window_update);
|
||||||
pattern_window_render(&gui.pattern_window);
|
GUI_WINDOW_RENDER(WINDOW_TYPE_NAMETABLE, nametable);
|
||||||
|
|
||||||
nametable_window_update(&gui.nametable_window);
|
// Update the pattern table
|
||||||
nametable_window_render(&gui.nametable_window);
|
GUI_WINDOW_ACTION_BASE(WINDOW_TYPE_PATTERN_TABLE, pattern_window_update);
|
||||||
|
GUI_WINDOW_RENDER(WINDOW_TYPE_PATTERN_TABLE, pattern)
|
||||||
|
|
||||||
gui.tick++;
|
gui.tick++;
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void gui_present() {
|
|
||||||
#if DEBUG
|
|
||||||
pattern_window_present(&gui.pattern_window);
|
|
||||||
nametable_window_present(&gui.nametable_window);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_delay() {
|
void gui_delay() {
|
||||||
|
@ -179,7 +224,3 @@ void gui_delay() {
|
||||||
TTF_Font *gui_get_font() {
|
TTF_Font *gui_get_font() {
|
||||||
return gui.font;
|
return gui.font;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int gui_get_frame_delay() {
|
|
||||||
return gui.frame_delay;
|
|
||||||
}
|
|
41
gui/gui.h
41
gui/gui.h
|
@ -8,17 +8,50 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <SDL_ttf.h>
|
#include <SDL_ttf.h>
|
||||||
|
|
||||||
|
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();
|
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();
|
int gui_input();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the graphical user interface to the screen.
|
||||||
|
*/
|
||||||
void gui_render();
|
void gui_render();
|
||||||
void gui_present();
|
|
||||||
|
/**
|
||||||
|
* Blocks until the next frame should be drawn, making the frame rate 60 hertz.
|
||||||
|
*/
|
||||||
void gui_delay();
|
void gui_delay();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the font used for the graphical user interface.
|
||||||
|
* @return A reference to the TTF font.
|
||||||
|
*/
|
||||||
TTF_Font* gui_get_font();
|
TTF_Font* gui_get_font();
|
||||||
unsigned int gui_get_frame_delay();
|
|
||||||
|
|
||||||
#endif //NES_EMULATOR_GUI_H
|
#endif //NES_EMULATOR_GUI_H
|
||||||
|
|
|
@ -8,52 +8,46 @@
|
||||||
#include "char_map.h"
|
#include "char_map.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "components/window_menu.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->window = window_create("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE);
|
||||||
window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING);
|
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);
|
// MenuItemComponent *mi_file = menu_item_create("FILE", ACTION_TYPE_OPEN_);
|
||||||
menu_append(menu, mi_file);
|
// menu_append(menu, mi_file);
|
||||||
MenuItemComponent *mi_file_open = menu_item_create("OPEN ROM...", NULL);
|
// MenuItemComponent *mi_file_open = menu_item_create("OPEN ROM...", NULL);
|
||||||
menu_item_append(mi_file, mi_file_open);
|
// 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);
|
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);
|
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_item_append(mi_debug, mi_debug_pattern);
|
||||||
|
|
||||||
menu_build(menu);
|
menu_build(menu);
|
||||||
window_add_component(&window->window, menu_as_component(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);
|
SDL_DestroyTexture(window->texture);
|
||||||
window_destroy(&window->window);
|
window_destroy(&window->window);
|
||||||
|
|
||||||
|
free(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
void main_window_render(MainWindow *window, pixel *pixels) {
|
||||||
|
|
||||||
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) {
|
|
||||||
SDL_UpdateTexture(window->texture, NULL, pixels, 256 * sizeof(pixel));
|
SDL_UpdateTexture(window->texture, NULL, pixels, 256 * sizeof(pixel));
|
||||||
|
|
||||||
window_render_texture(&window->window, window->texture);
|
window_render_texture(&window->window, window->texture);
|
||||||
|
@ -62,10 +56,10 @@ void main_window_render(NesMainWindow *window, pixel *pixels) {
|
||||||
window_present(&window->window);
|
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);
|
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);
|
window_mouse_click(&window->window);
|
||||||
}
|
}
|
|
@ -17,13 +17,24 @@ typedef struct nes_main_window {
|
||||||
Window window;
|
Window window;
|
||||||
|
|
||||||
SDL_Texture *texture;
|
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);
|
* Destroys an instance of the main window.
|
||||||
void main_window_mouse_click(NesMainWindow *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
|
#endif //NES_EMULATOR_MAIN_WINDOW_H
|
||||||
|
|
|
@ -4,22 +4,32 @@
|
||||||
|
|
||||||
#include "nametable_window.h"
|
#include "nametable_window.h"
|
||||||
#include "dbg_nametable.h"
|
#include "dbg_nametable.h"
|
||||||
|
#include "dbg_palette.h"
|
||||||
|
|
||||||
#define NW_WIDTH (NW_ROW_TILE_COUNT * PATTERN_DRAW_SIZE)
|
#define NW_WIDTH (NW_ROW_TILE_COUNT * PATTERN_DRAW_SIZE)
|
||||||
#define NW_HEIGHT (NW_ROW_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)
|
#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->window = window_create("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE);
|
||||||
window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING);
|
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);
|
SDL_DestroyTexture(window->texture);
|
||||||
window_destroy(&window->window);
|
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);
|
dbg_nametable_render_bank(bank, buffer);
|
||||||
|
|
||||||
SDL_Rect rect;
|
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));
|
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();
|
dbg_nametable_update();
|
||||||
|
|
||||||
pixel buffer[NW_BUFFER_SIZE * 4] = {0};
|
pixel buffer[NW_BUFFER_SIZE * 4] = {0};
|
||||||
|
@ -41,10 +52,7 @@ void nametable_window_update(NesNametableWindow *window) {
|
||||||
nametable_window_update_bank(window, 3, buffer);
|
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);
|
window_render_texture(&window->window, window->texture);
|
||||||
}
|
|
||||||
|
|
||||||
void nametable_window_present(NesNametableWindow *window) {
|
|
||||||
window_present(&window->window);
|
window_present(&window->window);
|
||||||
}
|
}
|
|
@ -15,16 +15,30 @@
|
||||||
typedef struct nes_nametable_window {
|
typedef struct nes_nametable_window {
|
||||||
Window window;
|
Window window;
|
||||||
SDL_Texture *texture;
|
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);
|
/**
|
||||||
|
* Renders a nemtable window to the screen.
|
||||||
void nametable_window_present(NesNametableWindow *window);
|
* @param window A reference to the window to render
|
||||||
|
*/
|
||||||
|
void nametable_window_render(NametableWindow *window);
|
||||||
|
|
||||||
#endif //NES_EMULATOR_NAMETABLE_WINDOW_H
|
#endif //NES_EMULATOR_NAMETABLE_WINDOW_H
|
||||||
|
|
|
@ -4,23 +4,35 @@
|
||||||
|
|
||||||
#include "pattern_window.h"
|
#include "pattern_window.h"
|
||||||
#include "dbg_pattern_table.h"
|
#include "dbg_pattern_table.h"
|
||||||
|
#include "dbg_palette.h"
|
||||||
|
|
||||||
#define PW_WIDTH (PW_ROW_TILE_COUNT * PATTERN_DRAW_SIZE)
|
#define PW_WIDTH (PW_ROW_TILE_COUNT * PATTERN_DRAW_SIZE)
|
||||||
#define PW_HEIGHT (PW_WIDTH * 2)
|
#define PW_HEIGHT (PW_WIDTH * 2)
|
||||||
#define PW_BUFFER_SIZE (PW_WIDTH * PW_HEIGHT)
|
#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->window = window_create("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE);
|
||||||
window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STATIC);
|
window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STATIC);
|
||||||
window->palette = 0;
|
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);
|
SDL_DestroyTexture(window->texture);
|
||||||
window_destroy(&window->window);
|
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};
|
pixel buffer[PW_BUFFER_SIZE] = {0};
|
||||||
dbg_pattern_draw_bank(PATTERN_BANK_0, buffer, window->palette);
|
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);
|
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));
|
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) {
|
if (keycode != SDLK_o) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -38,13 +50,10 @@ void pattern_window_key_up(NesPatternWindow *window, SDL_KeyCode keycode) {
|
||||||
window->palette = 0;
|
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);
|
window_render_texture(&window->window, window->texture);
|
||||||
}
|
|
||||||
|
|
||||||
void pattern_window_present(NesPatternWindow *window) {
|
|
||||||
window_present(&window->window);
|
window_present(&window->window);
|
||||||
}
|
}
|
|
@ -16,15 +16,37 @@ typedef struct nes_pattern_window {
|
||||||
Window window;
|
Window window;
|
||||||
SDL_Texture *texture;
|
SDL_Texture *texture;
|
||||||
byte palette;
|
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);
|
* Updates the content of a pattern window with the current data from the ROM mapper.
|
||||||
void pattern_window_present(NesPatternWindow *window);
|
* @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
|
#endif //NES_EMULATOR_PATTERN_WINDOW_H
|
||||||
|
|
|
@ -69,7 +69,6 @@ typedef struct ppu_tile_fetch {
|
||||||
byte pattern_table_tile_high;
|
byte pattern_table_tile_high;
|
||||||
} PPUTileFetch;
|
} PPUTileFetch;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
byte tile_debugger: 1;
|
byte tile_debugger: 1;
|
||||||
|
@ -77,7 +76,6 @@ typedef union {
|
||||||
} flags;
|
} flags;
|
||||||
byte flags_byte;
|
byte flags_byte;
|
||||||
} PPUDebugFlags;
|
} PPUDebugFlags;
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct ppu {
|
typedef struct ppu {
|
||||||
PPUMemory memory;
|
PPUMemory memory;
|
||||||
|
@ -103,9 +101,7 @@ typedef struct ppu {
|
||||||
unsigned int scanline;
|
unsigned int scanline;
|
||||||
unsigned int cycle;
|
unsigned int cycle;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
PPUDebugFlags debug;
|
PPUDebugFlags debug;
|
||||||
#endif
|
|
||||||
} PPU;
|
} PPU;
|
||||||
|
|
||||||
PPU *ppu_get_state();
|
PPU *ppu_get_state();
|
||||||
|
|
14
main.c
14
main.c
|
@ -101,30 +101,28 @@ int main() {
|
||||||
|
|
||||||
if (!rom_load(rom_path)) {
|
if (!rom_load(rom_path)) {
|
||||||
system_uninit();
|
system_uninit();
|
||||||
gui_uninit();
|
gui_free();
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gui_post_sysinit();
|
|
||||||
system_start();
|
system_start();
|
||||||
|
|
||||||
bool stop = false;
|
while (true) {
|
||||||
while (!stop) {
|
|
||||||
if (gui_input() < 0) {
|
if (gui_input() < 0) {
|
||||||
stop = true;
|
// The main window has been closed, stop the emulation
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
system_next_frame();
|
system_next_frame();
|
||||||
|
|
||||||
gui_render();
|
gui_render();
|
||||||
|
|
||||||
gui_present();
|
// Delay the next frame to lock the emulation to 60hz
|
||||||
gui_delay();
|
gui_delay();
|
||||||
}
|
}
|
||||||
|
|
||||||
system_uninit();
|
system_uninit();
|
||||||
rom_unload();
|
rom_unload();
|
||||||
gui_uninit();
|
gui_free();
|
||||||
|
|
||||||
close_logging();
|
close_logging();
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
#include "../cpu/cpu.h"
|
#include "../cpu/cpu.h"
|
||||||
#include "../include/rom.h"
|
#include "../include/rom.h"
|
||||||
#include "colors.h"
|
#include "colors.h"
|
||||||
#include "tile_debugger.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "tile_debugger.h"
|
||||||
|
|
||||||
#define PPU_SCANLINE_VISIBLE_MAX 240
|
#define PPU_SCANLINE_VISIBLE_MAX 240
|
||||||
#define PPU_SCANLINE_POST_RENDER_MIN PPU_SCANLINE_VISIBLE_MAX
|
#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) {
|
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) {
|
||||||
if ((ppu_state.debug.flags.tile_debugger_pattern_half == 1 && high) ||
|
if ((ppu_state.debug.flags.tile_debugger_pattern_half == 1 && high) ||
|
||||||
(ppu_state.debug.flags.tile_debugger_pattern_half == 2 && !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);
|
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;
|
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;
|
address pattern_addr = ppu_state.bg_pattern_table_addr | tile_index << 4 | high << 3 | tile_row_index;
|
||||||
|
|
Loading…
Reference in New Issue