diff --git a/debugger/CMakeLists.txt b/debugger/CMakeLists.txt index 36cecb0..3a3ae4f 100644 --- a/debugger/CMakeLists.txt +++ b/debugger/CMakeLists.txt @@ -7,7 +7,8 @@ add_library(DEBUG cursor.c cursor.h window.c - window.h) + window.h + keys.h) find_package(Curses) diff --git a/debugger/debugger.c b/debugger/debugger.c index eea7c99..015e58f 100644 --- a/debugger/debugger.c +++ b/debugger/debugger.c @@ -8,6 +8,7 @@ #include "debugger.h" #include "memory_view.h" #include "program_view.h" +#include "keys.h" void create_window() { setenv("TERMINFO", "/usr/share/terminfo", 1); @@ -36,8 +37,8 @@ void start_debugger(System *system) { doupdate(); int keycode; - while ((keycode = getch()) != CTRL_KEY_EXIT) { - if (keycode == CTRL_KEY_TOGGLE) { + while ((keycode = getch()) != KEY_EXIT_DEBUGGER) { + if (keycode == KEY_NEXT_VIEW) { window_index++; if (window_index > 1) { window_index = 0; @@ -46,13 +47,13 @@ void start_debugger(System *system) { cursor_disable(¤t_window->cursor); current_window = &windows[window_index]; cursor_enable(¤t_window->cursor); - } else if (keycode == KEY_UP) { + } else if (keycode == KEY_VIEW_UP) { current_window->handle_cursor_move(current_window, 0, CURSOR_OFFSET_UP); - } else if (keycode == KEY_DOWN) { + } else if (keycode == KEY_VIEW_DOWN) { current_window->handle_cursor_move(current_window, 0, CURSOR_OFFSET_DOWN); - } else if (keycode == KEY_LEFT) { + } else if (keycode == KEY_VIEW_LEFT) { current_window->handle_cursor_move(current_window, CURSOR_OFFSET_LEFT, 0); - } else if (keycode == KEY_RIGHT) { + } else if (keycode == KEY_VIEW_RIGHT) { current_window->handle_cursor_move(current_window, CURSOR_OFFSET_RIGHT, 0); } else { current_window->handle_key_down(current_window, keycode); diff --git a/debugger/debugger.h b/debugger/debugger.h index e847ce9..6de8a78 100644 --- a/debugger/debugger.h +++ b/debugger/debugger.h @@ -7,9 +7,6 @@ #include "../include/system.h" -#define CTRL_KEY_EXIT 3 -#define CTRL_KEY_TOGGLE 116 - void start_debugger(System *system); #endif //NESEMULATOR_DEBUGGER_H diff --git a/debugger/dialog.h b/debugger/dialog.h index 99fc583..5fe1d18 100644 --- a/debugger/dialog.h +++ b/debugger/dialog.h @@ -12,8 +12,21 @@ typedef struct dialog { PANEL *panel; } Dialog; +/** + * Creates a dialog box and present it to the user. + * + * @param message The message to show in the dialog + * @return The dialog instance + */ Dialog dialog_create(char *message); +/** + * Gets the address typed by the user. Will block until the input has been confirmed by them. + * + * @param dialog A pointer to the dialog + * @param cancelled A boolean indicating if the user has cancelled its input + * @return The address typed by the user + */ address dialog_get_address(Dialog *dialog, bool *cancelled); void dialog_remove(Dialog *dialog); diff --git a/debugger/keys.h b/debugger/keys.h new file mode 100644 index 0000000..a18084b --- /dev/null +++ b/debugger/keys.h @@ -0,0 +1,18 @@ +// +// Created by william on 2/22/24. +// + +#include + +#ifndef NESEMULATOR_KEYS_H +#define NESEMULATOR_KEYS_H + +#define KEY_EXIT_DEBUGGER 3 +#define KEY_GOTO 103 +#define KEY_NEXT_VIEW 116 +#define KEY_VIEW_DOWN KEY_DOWN +#define KEY_VIEW_LEFT KEY_LEFT +#define KEY_VIEW_RIGHT KEY_RIGHT +#define KEY_VIEW_UP KEY_UP + +#endif //NESEMULATOR_KEYS_H diff --git a/debugger/memory_view.c b/debugger/memory_view.c index 245fa22..4f8fe31 100644 --- a/debugger/memory_view.c +++ b/debugger/memory_view.c @@ -2,6 +2,7 @@ #include #include "memory_view.h" #include "dialog.h" +#include "keys.h" // // Created by william on 6/1/24. @@ -24,7 +25,7 @@ void memory_view_cursor_init(MemoryView *view) { } void memory_view_handle_key_down(InteractWindow *window, int keycode) { - if (keycode == MEMORY_VIEW_KEY_GOTO) { + if (keycode == KEY_GOTO) { Dialog dialog = dialog_create("Goto Address"); bool cancelled = false; @@ -65,7 +66,7 @@ void memory_view_print(MemoryView *view) { } void memory_view_goto(MemoryView *view, address target) { - assert(target < RAM_SIZE); + assert(target <= RAM_SIZE); address max_base_address = RAM_SIZE - MEMORY_VIEW_BYTE_COUNT; if (target > max_base_address) { diff --git a/debugger/memory_view.h b/debugger/memory_view.h index e40000d..db15633 100644 --- a/debugger/memory_view.h +++ b/debugger/memory_view.h @@ -15,7 +15,6 @@ #define MEMORY_VIEW_LINE_COUNT 0xf #define MEMORY_VIEW_LINE_BYTE_COUNT 0xf #define MEMORY_VIEW_BYTE_COUNT 0xff -#define MEMORY_VIEW_KEY_GOTO 103 typedef struct memory_view { InteractWindow *window; diff --git a/debugger/program_view.c b/debugger/program_view.c index 7badefa..d02bf06 100644 --- a/debugger/program_view.c +++ b/debugger/program_view.c @@ -7,6 +7,8 @@ #include "program_view.h" #include "../cpu/op.h" #include "memory_view.h" +#include "keys.h" +#include "dialog.h" void decode_operands(ProgramView *view) { int pc = PROGRAM_VIEW_BASE_ADDR; @@ -72,6 +74,18 @@ char *get_addr_mode_format_str(AddressingMode addr_mode) { } } +bool pv_predicate_operand_by_addr(void *data, void *userdata) { + DebugOperand *operand = (DebugOperand *) data; + address *addr = (address *) userdata; + return operand->addr == *addr; +} + +int pv_predicate_operand_distance(void *data, void *userdata) { + DebugOperand *operand = (DebugOperand *) data; + address *addr = (address *) userdata; + return operand->addr - *addr; +} + void program_view_write_line(ProgramView *view, int line, DebugOperand *operand) { char *op_name = get_op_code_name(operand->op_code); @@ -83,7 +97,7 @@ void program_view_write_line(ProgramView *view, int line, DebugOperand *operand) } void program_view_print(ProgramView *view) { - LinkedListNode *current_node = view->current_operand_node; + LinkedListNode *current_node = view->first_operand_node; int line = 0; while (line <= 0xf && current_node != NULL) { program_view_write_line(view, line, (DebugOperand *) current_node->data); @@ -97,13 +111,30 @@ void program_view_cursor_init(ProgramView *view) { view->window->cursor.width = PROGRAM_VIEW_WIDTH - 2; } +void pv_goto(ProgramView *view, address target) { + assert(target <= RAM_SIZE); + + LinkedListNode *match = linked_list_get_near(&view->operands, &pv_predicate_operand_distance, &target); + view->first_operand_node = match; + + // Move the first node back until we do not overflow the NES memory + while (RAM_SIZE - target < 0xf) { + view->first_operand_node = view->first_operand_node->previous; + target--; + } + + program_view_print(view); +} + void pv_scroll(ProgramView *view, int direction) { assert(direction == CURSOR_OFFSET_DOWN || direction == CURSOR_OFFSET_UP); - if (direction == CURSOR_OFFSET_UP) { - view->current_operand_node = view->current_operand_node->previous; - } else { - view->current_operand_node = view->current_operand_node->next; + if (direction == CURSOR_OFFSET_UP && view->first_operand_node->previous != NULL) { + view->first_operand_node = view->first_operand_node->previous; + view->last_operand_node = view->last_operand_node->previous; + } else if (view->last_operand_node->next != NULL) { + view->first_operand_node = view->first_operand_node->next; + view->last_operand_node = view->last_operand_node->next; } program_view_print(view); @@ -122,7 +153,19 @@ void pv_handle_cursor_move(InteractWindow *window, int horizontal, int vertical) } void program_view_handle_key_down(InteractWindow *window, int keycode) { + if (keycode == KEY_GOTO) { + Dialog dialog = dialog_create("Goto Address"); + bool cancelled = false; + address input = dialog_get_address(&dialog, &cancelled); + dialog_remove(&dialog); + + if (!cancelled) { + ProgramView *view = window->view; + pv_goto(view, input); + cursor_set_pos(&view->window->cursor, 0, 0); + } + } } void pv_deinit(InteractWindow *window) { @@ -130,12 +173,6 @@ void pv_deinit(InteractWindow *window) { linked_list_uninit(&view->operands); } -bool pv_predicate_operand_by_addr(void *data, void *userdata) { - DebugOperand *operand = (DebugOperand *) data; - address *addr = (address *) userdata; - return operand->addr == *addr; -} - void program_view_init(InteractWindow *interact, System *system, int x, int y) { ProgramView *view = malloc(sizeof(ProgramView)); view->window = interact; @@ -150,7 +187,12 @@ void program_view_init(InteractWindow *interact, System *system, int x, int y) { window_inter_init(interact, x, y, PROGRAM_VIEW_WIDTH, PROGRAM_VIEW_HEIGHT, "PROGRAM VIEW"); decode_operands(view); - view->current_operand_node = linked_list_get_if(&view->operands, &pv_predicate_operand_by_addr, view->pc); + view->first_operand_node = linked_list_get_if(&view->operands, &pv_predicate_operand_by_addr, view->pc); + LinkedListNode *last_node = view->first_operand_node; + for (int i = 0; i < 0xf; i++) { + last_node = last_node->next; + } + view->last_operand_node = last_node; program_view_print(view); program_view_cursor_init(view); diff --git a/debugger/program_view.h b/debugger/program_view.h index 743c548..55d1137 100644 --- a/debugger/program_view.h +++ b/debugger/program_view.h @@ -28,7 +28,8 @@ typedef struct program_view { byte *ram; address *pc; LinkedList operands; - LinkedListNode *current_operand_node; + LinkedListNode *first_operand_node; + LinkedListNode *last_operand_node; } ProgramView; void program_view_init(InteractWindow *interact, System *system, int x, int y); diff --git a/main.c b/main.c index 0107e82..70c504f 100644 --- a/main.c +++ b/main.c @@ -28,7 +28,7 @@ int main() { log_set_level(LOG_INFO); system_init(&system); - char *rom_path = "../test_roms/nestest.nes"; + char *rom_path = "../test_roms/smb.nes"; if (!rom_load(rom_path, &system)) { system_uninit(&system); @@ -37,11 +37,11 @@ int main() { system_start(&system); start_debugger(&system); +// system_loop(&system); system_uninit(&system); return EXIT_SUCCESS; -// system_loop(&system); // system_uninit(&system); diff --git a/utils/linked_list.c b/utils/linked_list.c index a2ee4da..d695f97 100644 --- a/utils/linked_list.c +++ b/utils/linked_list.c @@ -51,6 +51,21 @@ LinkedListNode *linked_list_get_if(LinkedList *list, bool(*predicate)(void *, vo return NULL; } +LinkedListNode *linked_list_get_near(LinkedList *list, int(*predicate)(void *, void *), void *userdata) { + LinkedListNode *near_node = list->head; + + int last_distance; + int current_distance = 0xffffffff >> 1; + do { + last_distance = current_distance; + near_node = near_node->next; + current_distance = predicate(near_node->data, userdata); + } while (current_distance < last_distance && near_node->next != NULL); + + // After the loop, we have found the nearest node in the list, assuming there is only one point of convergence + return near_node; +} + void linked_list_uninit(LinkedList *list) { assert(list != NULL); diff --git a/utils/linked_list.h b/utils/linked_list.h index d3e21ff..b3c9ca8 100644 --- a/utils/linked_list.h +++ b/utils/linked_list.h @@ -18,12 +18,39 @@ typedef struct linked_list { LinkedListNode *end; } LinkedList; +/** + * Initializes a new linked list. + * + * @return The linked list instance + */ LinkedList linked_list_init(); +/** + * Adds data to a linked list. + * + * @param list The linked list + * @param data The data to add + */ void linked_list_add(LinkedList *list, void *data); -LinkedListNode *linked_list_get_if(LinkedList *list, bool(*predicate)(void *, void *), void* userdata); +/** + * Searches for data corresponding to a predicate. + * The search will stop after reaching the first node matching the given predicate. + * + * @param list The list to search in + * @param predicate The predicate to match the data + * @param userdata Parameter to pass to the predicate + * @return The first node in the list matching the predicate + */ +LinkedListNode *linked_list_get_if(LinkedList *list, bool(*predicate)(void *, void *), void *userdata); +LinkedListNode *linked_list_get_near(LinkedList *list, int(*predicate)(void *, void *), void *userdata); + +/** + * Deinitializes a linked list. + * + * @param list The list to deinitialize + */ void linked_list_uninit(LinkedList *list); #endif //NESEMULATOR_LINKED_LIST_H