diff --git a/Makefile b/Makefile index 1bbfce2de..b4feeb4ec 100644 --- a/Makefile +++ b/Makefile @@ -562,10 +562,19 @@ endif EMU_FLAGS = +# Adding a txt file to this location will then reference a UNFLoader path specified in the file, instead of locally. +# This is expecially important for WSL users because UNFLoader.exe is incredibly slow when run within WSL's filesystem, so this can be used to point to the C drive. +# The file should only contain the directory path that contains UNFLoader[.exe] (do not specify the filename). +LOADER_DIR_FILE_SPECIFICATION_PATH = ~/.local/share/HackerSM64/UNFLoader-dir.txt +LOADER_DIR = ./$(TOOLS_DIR) + +ifneq (,$(wildcard $(LOADER_DIR_FILE_SPECIFICATION_PATH))) + LOADER_DIR = $(shell cat $(LOADER_DIR_FILE_SPECIFICATION_PATH)) +endif ifneq (,$(call find-command,wslview)) - LOADER = ./$(TOOLS_DIR)/UNFLoader.exe + LOADER_EXEC = $(LOADER_DIR)/UNFLoader.exe else - LOADER = ./$(TOOLS_DIR)/UNFLoader + LOADER_EXEC = $(LOADER_DIR)/UNFLoader endif SHA1SUM = sha1sum @@ -621,17 +630,17 @@ test-pj64: $(ROM) # someone2639 # download and extract most recent unfloader build if needed -$(LOADER): -ifeq (,$(wildcard $(LOADER))) +$(LOADER_EXEC): +ifeq (,$(wildcard $(LOADER_EXEC))) @$(PRINT) "Downloading latest UNFLoader...$(NO_COL)\n" - $(PYTHON) $(TOOLS_DIR)/get_latest_unfloader.py $(TOOLS_DIR) + $(PYTHON) $(TOOLS_DIR)/get_latest_unfloader.py $(LOADER_DIR) endif -load: $(ROM) $(LOADER) - $(LOADER) -r $< +load: $(ROM) $(LOADER_EXEC) + $(LOADER_EXEC) -r $< -unf: $(ROM) $(LOADER) - $(LOADER) -d -r $< +unf: $(ROM) $(LOADER_EXEC) + $(LOADER_EXEC) -d -r $< libultra: $(BUILD_DIR)/libultra.a diff --git a/src/usb/debug.c b/src/usb/debug.c index 0685789ce..059e2e442 100644 --- a/src/usb/debug.c +++ b/src/usb/debug.c @@ -5,38 +5,55 @@ A basic debug library that makes use of the USB library for N64 flashcarts. https://github.com/buu342/N64-UNFLoader ***************************************************************/ + #include "debug.h" #ifndef LIBDRAGON - #include + #include #include // Needed for Crash's Linux toolchain #else #include #include - #include #endif +#include #include +#include #include - #if DEBUG_MODE /********************************* Definitions *********************************/ - #define MSG_FAULT 0x10 - #define MSG_READ 0x11 - #define MSG_WRITE 0x12 + // USB thread messages + #define MSG_FAULT 0x10 + #define MSG_READ 0x11 + #define MSG_WRITE 0x12 - #define USBERROR_NONE 0 - #define USBERROR_NOTTEXT 1 - #define USBERROR_UNKNOWN 2 - #define USBERROR_TOOMUCH 3 - #define USBERROR_CUSTOM 4 + #define USBERROR_NONE 0 + #define USBERROR_NOTTEXT 1 + #define USBERROR_UNKNOWN 2 + #define USBERROR_TOOMUCH 3 + #define USBERROR_CUSTOM 4 - #define HASHTABLE_SIZE 7 - #define COMMAND_TOKENS 10 - #define BUFFER_SIZE 256 + // RDB thread messages (Libultra) + #ifndef LIBDRAGON + #define MSG_RDB_PACKET 0x10 + #define MSG_RDB_BPHIT 0x11 + #define MSG_RDB_PAUSE 0x12 + #endif + + // Breakpoints + #define BPOINT_COUNT 10 + #define MAKE_BREAKPOINT_INDEX(indx) (0x0000000D | ((indx) << 6)) + #define GET_BREAKPOINT_INDEX(addr) ((((addr) >> 6) & 0x0000FFFF)) + + // Helpful stuff + #define HASHTABLE_SIZE 7 + #define COMMAND_TOKENS 10 + #define BUFFER_SIZE 256 + #define REGISTER_COUNT 72 // 32 GPRs + 6 SPRs + 16 FPRs + fsr + fir (fcr0) + #define REGISTER_SIZE 16 // GDB expects the registers to be 64-bits /********************************* @@ -44,12 +61,21 @@ https://github.com/buu342/N64-UNFLoader *********************************/ #ifdef LIBDRAGON - typedef unsigned char u8; + #ifndef TRUE + #define TRUE 1 + #endif + #ifndef FALSE + #define FALSE 0 + #endif + #define OS_PHYSICAL_TO_K0(x) (void *)(((u32)(x)+0x80000000)) + #define OS_PHYSICAL_TO_K1(x) (void *)(((u32)(x)+0xa0000000)) + + typedef unsigned char u8; typedef unsigned short u16; typedef unsigned long u32; typedef unsigned long long u64; - typedef signed char s8; + typedef signed char s8; typedef short s16; typedef long s32; typedef long long s64; @@ -66,6 +92,9 @@ https://github.com/buu342/N64-UNFLoader typedef float f32; typedef double f64; + + typedef void* OSMesg; + typedef exception_t OSThread; #endif @@ -81,6 +110,12 @@ https://github.com/buu342/N64-UNFLoader char *string; } regDesc; + // Because of the thread context's messy struct, this'll come in handy + typedef struct { + int size; + void* ptr; + } regType; + // Thread message struct typedef struct { @@ -99,24 +134,61 @@ https://github.com/buu342/N64-UNFLoader void* next; } debugCommand; + // Remote debugger packet lookup table + typedef struct + { + char* command; + void (*func)(); + } RDBPacketLUT; + + // Breakpoint struct + typedef struct + { + u32* addr; + u32 instruction; + } bPoint; + /********************************* Function Prototypes *********************************/ + // Threads + static void debug_thread_usb(void* arg); #ifndef LIBDRAGON - // Threads #if USE_FAULTTHREAD - static void debug_thread_fault(void *arg); + static void debug_thread_fault(void* arg); #endif - static void debug_thread_usb(void *arg); - - // Other + #else + #if AUTOPOLL_ENABLED + static void debug_timer_usb(int overflow); + #endif + #endif + #if USE_RDBTHREAD + #ifndef LIBDRAGON + static void debug_thread_rdb(void* arg); + #else + static void debug_thread_rdb(exception_t* arg); + static void debug_thread_rdb_pause(); + #endif + static void debug_thread_rdb_loop(OSThread* t); + static void debug_rdb_qsupported(OSThread* t); + static void debug_rdb_haltreason(OSThread* t); + static void debug_rdb_dumpregisters(OSThread* t); + static void debug_rdb_writeregisters(OSThread* t); + static void debug_rdb_readmemory(OSThread* t); + static void debug_rdb_writememory(OSThread* t); + static void debug_rdb_addbreakpoint(OSThread* t); + static void debug_rdb_removebreakpoint(OSThread* t); + static void debug_rdb_continue(OSThread* t); + static void debug_rdb_pause(OSThread* t); + #endif + + // Other + #ifndef LIBDRAGON #if OVERWRITE_OSPRINT static void* debug_osSyncPrintf_implementation(void *unused, const char *str, size_t len); #endif - #else - static void debug_thread_usb(void *arg); #endif static inline void debug_handle_64drivebutton(); @@ -160,112 +232,151 @@ https://github.com/buu342/N64-UNFLoader static u64 debug_64dbut_hold = 0; #ifndef LIBDRAGON - // Fault thread globals - #if USE_FAULTTHREAD - static OSMesgQueue faultMessageQ; - static OSMesg faultMessageBuf; - static OSThread faultThread; - static u64 faultThreadStack[FAULT_THREAD_STACK/sizeof(u64)]; - #endif // USB thread globals static OSMesgQueue usbMessageQ; static OSMesg usbMessageBuf; static OSThread usbThread; static u64 usbThreadStack[USB_THREAD_STACK/sizeof(u64)]; + #if AUTOPOLL_ENABLED + static OSTimer usbThreadTimer; + #endif - // List of error causes - static regDesc causeDesc[] = { - {CAUSE_BD, CAUSE_BD, "BD"}, - {CAUSE_IP8, CAUSE_IP8, "IP8"}, - {CAUSE_IP7, CAUSE_IP7, "IP7"}, - {CAUSE_IP6, CAUSE_IP6, "IP6"}, - {CAUSE_IP5, CAUSE_IP5, "IP5"}, - {CAUSE_IP4, CAUSE_IP4, "IP4"}, - {CAUSE_IP3, CAUSE_IP3, "IP3"}, - {CAUSE_SW2, CAUSE_SW2, "IP2"}, - {CAUSE_SW1, CAUSE_SW1, "IP1"}, - {CAUSE_EXCMASK, EXC_INT, "Interrupt"}, - {CAUSE_EXCMASK, EXC_MOD, "TLB modification exception"}, - {CAUSE_EXCMASK, EXC_RMISS, "TLB exception on load or instruction fetch"}, - {CAUSE_EXCMASK, EXC_WMISS, "TLB exception on store"}, - {CAUSE_EXCMASK, EXC_RADE, "Address error on load or instruction fetch"}, - {CAUSE_EXCMASK, EXC_WADE, "Address error on store"}, - {CAUSE_EXCMASK, EXC_IBE, "Bus error exception on instruction fetch"}, - {CAUSE_EXCMASK, EXC_DBE, "Bus error exception on data reference"}, - {CAUSE_EXCMASK, EXC_SYSCALL, "System call exception"}, - {CAUSE_EXCMASK, EXC_BREAK, "Breakpoint exception"}, - {CAUSE_EXCMASK, EXC_II, "Reserved instruction exception"}, - {CAUSE_EXCMASK, EXC_CPU, "Coprocessor unusable exception"}, - {CAUSE_EXCMASK, EXC_OV, "Arithmetic overflow exception"}, - {CAUSE_EXCMASK, EXC_TRAP, "Trap exception"}, - {CAUSE_EXCMASK, EXC_VCEI, "Virtual coherency exception on intruction fetch"}, - {CAUSE_EXCMASK, EXC_FPE, "Floating point exception (see fpcsr)"}, - {CAUSE_EXCMASK, EXC_WATCH, "Watchpoint exception"}, - {CAUSE_EXCMASK, EXC_VCED, "Virtual coherency exception on data reference"}, - {0, 0, ""} - }; + // Fault thread globals + #if USE_FAULTTHREAD + static OSMesgQueue faultMessageQ; + static OSMesg faultMessageBuf; + static OSThread faultThread; + static u64 faultThreadStack[FAULT_THREAD_STACK/sizeof(u64)]; - // List of register descriptions - static regDesc srDesc[] = { - {SR_CU3, SR_CU3, "CU3"}, - {SR_CU2, SR_CU2, "CU2"}, - {SR_CU1, SR_CU1, "CU1"}, - {SR_CU0, SR_CU0, "CU0"}, - {SR_RP, SR_RP, "RP"}, - {SR_FR, SR_FR, "FR"}, - {SR_RE, SR_RE, "RE"}, - {SR_BEV, SR_BEV, "BEV"}, - {SR_TS, SR_TS, "TS"}, - {SR_SR, SR_SR, "SR"}, - {SR_CH, SR_CH, "CH"}, - {SR_CE, SR_CE, "CE"}, - {SR_DE, SR_DE, "DE"}, - {SR_IBIT8, SR_IBIT8, "IM8"}, - {SR_IBIT7, SR_IBIT7, "IM7"}, - {SR_IBIT6, SR_IBIT6, "IM6"}, - {SR_IBIT5, SR_IBIT5, "IM5"}, - {SR_IBIT4, SR_IBIT4, "IM4"}, - {SR_IBIT3, SR_IBIT3, "IM3"}, - {SR_IBIT2, SR_IBIT2, "IM2"}, - {SR_IBIT1, SR_IBIT1, "IM1"}, - {SR_KX, SR_KX, "KX"}, - {SR_SX, SR_SX, "SX"}, - {SR_UX, SR_UX, "UX"}, - {SR_KSU_MASK, SR_KSU_USR, "USR"}, - {SR_KSU_MASK, SR_KSU_SUP, "SUP"}, - {SR_KSU_MASK, SR_KSU_KER, "KER"}, - {SR_ERL, SR_ERL, "ERL"}, - {SR_EXL, SR_EXL, "EXL"}, - {SR_IE, SR_IE, "IE"}, - {0, 0, ""} - }; + // List of error causes + static regDesc causeDesc[] = { + {CAUSE_BD, CAUSE_BD, "BD"}, + {CAUSE_IP8, CAUSE_IP8, "IP8"}, + {CAUSE_IP7, CAUSE_IP7, "IP7"}, + {CAUSE_IP6, CAUSE_IP6, "IP6"}, + {CAUSE_IP5, CAUSE_IP5, "IP5"}, + {CAUSE_IP4, CAUSE_IP4, "IP4"}, + {CAUSE_IP3, CAUSE_IP3, "IP3"}, + {CAUSE_SW2, CAUSE_SW2, "IP2"}, + {CAUSE_SW1, CAUSE_SW1, "IP1"}, + {CAUSE_EXCMASK, EXC_INT, "Interrupt"}, + {CAUSE_EXCMASK, EXC_MOD, "TLB modification exception"}, + {CAUSE_EXCMASK, EXC_RMISS, "TLB exception on load or instruction fetch"}, + {CAUSE_EXCMASK, EXC_WMISS, "TLB exception on store"}, + {CAUSE_EXCMASK, EXC_RADE, "Address error on load or instruction fetch"}, + {CAUSE_EXCMASK, EXC_WADE, "Address error on store"}, + {CAUSE_EXCMASK, EXC_IBE, "Bus error exception on instruction fetch"}, + {CAUSE_EXCMASK, EXC_DBE, "Bus error exception on data reference"}, + {CAUSE_EXCMASK, EXC_SYSCALL, "System call exception"}, + {CAUSE_EXCMASK, EXC_BREAK, "Breakpoint exception"}, + {CAUSE_EXCMASK, EXC_II, "Reserved instruction exception"}, + {CAUSE_EXCMASK, EXC_CPU, "Coprocessor unusable exception"}, + {CAUSE_EXCMASK, EXC_OV, "Arithmetic overflow exception"}, + {CAUSE_EXCMASK, EXC_TRAP, "Trap exception"}, + {CAUSE_EXCMASK, EXC_VCEI, "Virtual coherency exception on intruction fetch"}, + {CAUSE_EXCMASK, EXC_FPE, "Floating point exception (see fpcsr)"}, + {CAUSE_EXCMASK, EXC_WATCH, "Watchpoint exception"}, + {CAUSE_EXCMASK, EXC_VCED, "Virtual coherency exception on data reference"}, + {0, 0, ""} + }; + + // List of register descriptions + static regDesc srDesc[] = { + {SR_CU3, SR_CU3, "CU3"}, + {SR_CU2, SR_CU2, "CU2"}, + {SR_CU1, SR_CU1, "CU1"}, + {SR_CU0, SR_CU0, "CU0"}, + {SR_RP, SR_RP, "RP"}, + {SR_FR, SR_FR, "FR"}, + {SR_RE, SR_RE, "RE"}, + {SR_BEV, SR_BEV, "BEV"}, + {SR_TS, SR_TS, "TS"}, + {SR_SR, SR_SR, "SR"}, + {SR_CH, SR_CH, "CH"}, + {SR_CE, SR_CE, "CE"}, + {SR_DE, SR_DE, "DE"}, + {SR_IBIT8, SR_IBIT8, "IM8"}, + {SR_IBIT7, SR_IBIT7, "IM7"}, + {SR_IBIT6, SR_IBIT6, "IM6"}, + {SR_IBIT5, SR_IBIT5, "IM5"}, + {SR_IBIT4, SR_IBIT4, "IM4"}, + {SR_IBIT3, SR_IBIT3, "IM3"}, + {SR_IBIT2, SR_IBIT2, "IM2"}, + {SR_IBIT1, SR_IBIT1, "IM1"}, + {SR_KX, SR_KX, "KX"}, + {SR_SX, SR_SX, "SX"}, + {SR_UX, SR_UX, "UX"}, + {SR_KSU_MASK, SR_KSU_USR, "USR"}, + {SR_KSU_MASK, SR_KSU_SUP, "SUP"}, + {SR_KSU_MASK, SR_KSU_KER, "KER"}, + {SR_ERL, SR_ERL, "ERL"}, + {SR_EXL, SR_EXL, "EXL"}, + {SR_IE, SR_IE, "IE"}, + {0, 0, ""} + }; + + // List of floating point registers descriptions + static regDesc fpcsrDesc[] = { + {FPCSR_FS, FPCSR_FS, "FS"}, + {FPCSR_C, FPCSR_C, "C"}, + {FPCSR_CE, FPCSR_CE, "Unimplemented operation"}, + {FPCSR_CV, FPCSR_CV, "Invalid operation"}, + {FPCSR_CZ, FPCSR_CZ, "Division by zero"}, + {FPCSR_CO, FPCSR_CO, "Overflow"}, + {FPCSR_CU, FPCSR_CU, "Underflow"}, + {FPCSR_CI, FPCSR_CI, "Inexact operation"}, + {FPCSR_EV, FPCSR_EV, "EV"}, + {FPCSR_EZ, FPCSR_EZ, "EZ"}, + {FPCSR_EO, FPCSR_EO, "EO"}, + {FPCSR_EU, FPCSR_EU, "EU"}, + {FPCSR_EI, FPCSR_EI, "EI"}, + {FPCSR_FV, FPCSR_FV, "FV"}, + {FPCSR_FZ, FPCSR_FZ, "FZ"}, + {FPCSR_FO, FPCSR_FO, "FO"}, + {FPCSR_FU, FPCSR_FU, "FU"}, + {FPCSR_FI, FPCSR_FI, "FI"}, + {FPCSR_RM_MASK, FPCSR_RM_RN, "RN"}, + {FPCSR_RM_MASK, FPCSR_RM_RZ, "RZ"}, + {FPCSR_RM_MASK, FPCSR_RM_RP, "RP"}, + {FPCSR_RM_MASK, FPCSR_RM_RM, "RM"}, + {0, 0, ""} + }; + #endif + #endif - // List of floating point registers descriptions - static regDesc fpcsrDesc[] = { - {FPCSR_FS, FPCSR_FS, "FS"}, - {FPCSR_C, FPCSR_C, "C"}, - {FPCSR_CE, FPCSR_CE, "Unimplemented operation"}, - {FPCSR_CV, FPCSR_CV, "Invalid operation"}, - {FPCSR_CZ, FPCSR_CZ, "Division by zero"}, - {FPCSR_CO, FPCSR_CO, "Overflow"}, - {FPCSR_CU, FPCSR_CU, "Underflow"}, - {FPCSR_CI, FPCSR_CI, "Inexact operation"}, - {FPCSR_EV, FPCSR_EV, "EV"}, - {FPCSR_EZ, FPCSR_EZ, "EZ"}, - {FPCSR_EO, FPCSR_EO, "EO"}, - {FPCSR_EU, FPCSR_EU, "EU"}, - {FPCSR_EI, FPCSR_EI, "EI"}, - {FPCSR_FV, FPCSR_FV, "FV"}, - {FPCSR_FZ, FPCSR_FZ, "FZ"}, - {FPCSR_FO, FPCSR_FO, "FO"}, - {FPCSR_FU, FPCSR_FU, "FU"}, - {FPCSR_FI, FPCSR_FI, "FI"}, - {FPCSR_RM_MASK, FPCSR_RM_RN, "RN"}, - {FPCSR_RM_MASK, FPCSR_RM_RZ, "RZ"}, - {FPCSR_RM_MASK, FPCSR_RM_RP, "RP"}, - {FPCSR_RM_MASK, FPCSR_RM_RM, "RM"}, - {0, 0, ""} + #if USE_RDBTHREAD + // Remote debugger thread globals + #ifndef LIBDRAGON + static OSMesgQueue rdbMessageQ; + static OSMesg rdbMessageBuf; + static OSThread rdbThread; + static u64 rdbThreadStack[RDB_THREAD_STACK/sizeof(u64)]; + #endif + + // RDB status globals + static vu8 debug_rdbpaused = FALSE; + #ifndef LIBDRAGON + static OSTime debug_pausetime = 0; + #else + static u32 debug_pausetime = 0; + static u8 debug_ismanualpause = FALSE; + #endif + static bPoint debug_bpoints[BPOINT_COUNT]; + + // Remote debugger packet lookup table + RDBPacketLUT lut_rdbpackets[] = { + // Due to the use of strncmp, the order of strings matters! + {"qSupported", debug_rdb_qsupported}, + {"?", debug_rdb_haltreason}, + {"g", debug_rdb_dumpregisters}, + {"G", debug_rdb_writeregisters}, + {"m", debug_rdb_readmemory}, + {"M", debug_rdb_writememory}, + {"Z0", debug_rdb_addbreakpoint}, + {"z0", debug_rdb_removebreakpoint}, + {"c", debug_rdb_continue}, + {"\x03", debug_rdb_pause}, }; #endif @@ -280,17 +391,31 @@ https://github.com/buu342/N64-UNFLoader ==============================*/ void debug_initialize() - { + { // Initialize the USB functions if (!usb_initialize()) return; - // Overwrite osSyncPrintf + // Initialize globals + memset(debug_commands_hashtable, 0, sizeof(debugCommand*)*HASHTABLE_SIZE); + memset(debug_commands_elements, 0, sizeof(debugCommand)*MAX_COMMANDS); + + // Libultra functions #ifndef LIBDRAGON + // Overwrite osSyncPrintf #if OVERWRITE_OSPRINT __printfunc = (void*)debug_osSyncPrintf_implementation; #endif + // Initialize the USB thread + osCreateThread(&usbThread, USB_THREAD_ID, debug_thread_usb, 0, + (usbThreadStack+USB_THREAD_STACK/sizeof(u64)), + USB_THREAD_PRI); + osStartThread(&usbThread); + #if AUTOPOLL_ENABLED + osSetTimer(&usbThreadTimer, 0, OS_USEC_TO_CYCLES(AUTOPOLL_TIME*1000), &usbMessageQ, (OSMesg)NULL); + #endif + // Initialize the fault thread #if USE_FAULTTHREAD osCreateThread(&faultThread, FAULT_THREAD_ID, debug_thread_fault, 0, @@ -299,11 +424,34 @@ https://github.com/buu342/N64-UNFLoader osStartThread(&faultThread); #endif - // Initialize the USB thread - osCreateThread(&usbThread, USB_THREAD_ID, debug_thread_usb, 0, - (usbThreadStack+USB_THREAD_STACK/sizeof(u64)), - USB_THREAD_PRI); - osStartThread(&usbThread); + // Initialize the remote debugger thread + #if USE_RDBTHREAD + osCreateThread(&rdbThread, RDB_THREAD_ID, debug_thread_rdb, (void*)osGetThreadId(NULL), + (rdbThreadStack+RDB_THREAD_STACK/sizeof(u64)), + RDB_THREAD_PRI); + osStartThread(&rdbThread); + + // Initialize breakpoints + osSetEventMesg(OS_EVENT_CPU_BREAK, &rdbMessageQ, (OSMesg)MSG_RDB_BPHIT); + memset(debug_bpoints, 0, BPOINT_COUNT*sizeof(bPoint)); + + // Pause the main thread + usb_purge(); + usb_write(DATATYPE_TEXT, "Pausing main thread until GDB connects and resumes\n", 51+1); + osSendMesg(&rdbMessageQ, (OSMesg)MSG_RDB_PAUSE, OS_MESG_BLOCK); + #endif + #else + timer_init(); // If the timer subsystem has been initialized already, it's not a problem to call it again. + #if AUTOPOLL_ENABLED + new_timer(TIMER_TICKS(AUTOPOLL_TIME*1000), TF_CONTINUOUS, debug_timer_usb); + #endif + #if USE_RDBTHREAD + memset(debug_bpoints, 0, BPOINT_COUNT*sizeof(bPoint)); + register_exception_handler(debug_thread_rdb); + usb_purge(); + usb_write(DATATYPE_TEXT, "Pausing main thread until GDB connects and resumes\n", 51+1); + debug_thread_rdb_pause(); + #endif #endif // Mark the debug mode as initialized @@ -345,7 +493,7 @@ https://github.com/buu342/N64-UNFLoader usbMesg msg; va_list args; - // use the internal libultra printf function to format the string + // Use the internal libultra printf function to format the string va_start(args, message); #ifndef LIBDRAGON len = _Printf(&printf_handler, debug_buffer, message, args); @@ -471,7 +619,14 @@ https://github.com/buu342/N64-UNFLoader #endif // Intentionally cause a TLB exception on load/instruction fetch + #ifdef LIBDRAGON + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Warray-bounds" + #endif crash = *(volatile char *)1; + #ifdef LIBDRAGON + #pragma GCC diagnostic pop + #endif (void)crash; } @@ -570,9 +725,6 @@ https://github.com/buu342/N64-UNFLoader // Ensure debug mode is initialized if (!debug_initialized) return; - - // Handle 64Drive button polling - debug_handle_64drivebutton(); // Send a read message to the USB thread msg.msgtype = MSG_READ; @@ -734,12 +886,12 @@ https://github.com/buu342/N64-UNFLoader dataleft = 0; break; } - // fallthrough + FALL_THROUGH; case '@': filestep++; if (filestep < 3) break; - // fallthrough + FALL_THROUGH; default: // Decide what to do based on the file handle if (filestep == 0 && debug_command_incoming_start[tok] == -1) @@ -776,6 +928,24 @@ https://github.com/buu342/N64-UNFLoader // Rewind the USB fully usb_rewind(datasize); } + + #ifdef LIBDRAGON + #if AUTOPOLL_ENABLED + /*============================== + debug_timer_usb + A function that's called by the auto-poll timer + @param How many ticks the timer overflew by (unused) + ==============================*/ + + static void debug_timer_usb(int overflow) + { + usbMesg msg; + (void)overflow; // To prevent unused variable errors + msg.msgtype = MSG_READ; + debug_thread_usb(&msg); + } + #endif + #endif /*============================== @@ -811,6 +981,29 @@ https://github.com/buu342/N64-UNFLoader int header = usb_poll(); debugCommand* entry; + // RDB packets should be rerouted to the RDB thread + #if USE_RDBTHREAD + if (USBHEADER_GETTYPE(header) == DATATYPE_RDBPACKET) + { + #ifndef LIBDRAGON + osSendMesg(&rdbMessageQ, (OSMesg)MSG_RDB_PACKET, OS_MESG_BLOCK); + #else + + // Exceptional case, handle pausing through CTRL+C + char packetstart; + usb_read(&packetstart, 1); + if (packetstart == '\x03') + { + usb_rewind(1); + debug_thread_rdb_pause(); + } + else + debug_thread_rdb_loop(NULL); + #endif + continue; + } + #endif + // Ensure we're receiving a text command if (USBHEADER_GETTYPE(header) != DATATYPE_TEXT) { @@ -863,6 +1056,9 @@ https://github.com/buu342/N64-UNFLoader } } + // Handle 64Drive button polling + debug_handle_64drivebutton(); + // Spit out an error if there was one during the command parsing if (errortype != USBERROR_NONE) { @@ -885,14 +1081,18 @@ https://github.com/buu342/N64-UNFLoader errortype = USBERROR_NONE; } + // Handle the other USB messages - switch (threadMsg->msgtype) + if (threadMsg != NULL) { - case MSG_WRITE: - if (usb_timedout()) - usb_sendheartbeat(); - usb_write(threadMsg->datatype, threadMsg->buff, threadMsg->size); - break; + switch (threadMsg->msgtype) + { + case MSG_WRITE: + if (usb_timedout()) + usb_sendheartbeat(); + usb_write(threadMsg->datatype, threadMsg->buff, threadMsg->size); + break; + } } // If we're in libdragon, break out of the loop as we don't need it @@ -934,8 +1134,10 @@ https://github.com/buu342/N64-UNFLoader return ret; } - #endif - + #endif + #endif + + #ifndef LIBDRAGON #if USE_FAULTTHREAD /*============================== @@ -949,7 +1151,7 @@ https://github.com/buu342/N64-UNFLoader static void debug_printreg(u32 value, char *name, regDesc *desc) { char first = 1; - debug_printf("%s\t\t0x%08x <", name, value); + debug_printf("%s\t\t0x%16x <", name, value); while (desc->mask != 0) { if ((value & desc->mask) == desc->value) @@ -990,15 +1192,33 @@ https://github.com/buu342/N64-UNFLoader { __OSThreadContext* context = &curr->context; + // If the debug or rdb thread crashed, restart it + if (curr->id == USB_THREAD_ID) + { + osCreateThread(&usbThread, USB_THREAD_ID, debug_thread_usb, 0, + (usbThreadStack+USB_THREAD_STACK/sizeof(u64)), + USB_THREAD_PRI); + osStartThread(&usbThread); + } + #if USE_RDBTHREAD + else if (curr->id == RDB_THREAD_ID) + { + osCreateThread(&rdbThread, RDB_THREAD_ID, debug_thread_rdb, (void*)osGetThreadId(NULL), + (rdbThreadStack+RDB_THREAD_STACK/sizeof(u64)), + RDB_THREAD_PRI); + osStartThread(&rdbThread); + } + #endif + // Print the basic info debug_printf("Fault in thread: %d\n\n", curr->id); - debug_printf("pc\t\t0x%08x\n", context->pc); + debug_printf("pc\t\t0x%16x\n", context->pc); if (assert_file == NULL) debug_printreg(context->cause, "cause", causeDesc); else debug_printf("cause\t\tAssertion failed in file '%s', line %d.\n", assert_file, assert_line); debug_printreg(context->sr, "sr", srDesc); - debug_printf("badvaddr\t0x%08x\n\n", context->badvaddr); + debug_printf("badvaddr\t0x%16x\n\n", context->badvaddr); // Print the registers debug_printf("at 0x%016llx v0 0x%016llx v1 0x%016llx\n", context->at, context->v0, context->v1); @@ -1026,8 +1246,857 @@ https://github.com/buu342/N64-UNFLoader } } } - #endif #endif + #if USE_RDBTHREAD + #ifndef LIBDRAGON + /*============================== + debug_thread_rdb + Handles the remote debugger thread (Libultra) + @param Arbitrary data that the thread can receive. + Used for passing the main thread ID. + ==============================*/ + + static void debug_thread_rdb(void *arg) + { + OSId mainid = (OSId)arg; + OSThread* mainthread = &rdbThread; + + // Find the main thread pointer given its ID + while (mainthread->id != mainid) + mainthread = mainthread->tlnext; + + // Create the message queue for the rdb messages + osCreateMesgQueue(&rdbMessageQ, &rdbMessageBuf, 1); + + // Thread loop + while (1) + { + OSMesg msg; + OSThread* affected = NULL; + + // Wait for an rdb message to arrive + osRecvMesg(&rdbMessageQ, &msg, OS_MESG_BLOCK); + + // Exceptional case, handle pausing through CTRL+C + if (USBHEADER_GETTYPE(usb_poll()) == DATATYPE_RDBPACKET) + { + char packetstart; + usb_read(&packetstart, 1); + if (packetstart == '\x03') + msg = (OSMesg)MSG_RDB_PAUSE; + usb_rewind(1); + } + + + // Check what message we received + switch ((s32)msg) + { + case MSG_RDB_PACKET: + break; // Do nothing + case MSG_RDB_BPHIT: + affected = mainthread; + debug_rdbpaused = TRUE; + debug_pausetime = osGetTime(); + + // Find out which thread hit the bp exception + while (1) + { + if (affected->flags & OS_FLAG_CPU_BREAK) + break; + affected = affected->tlnext; + } + usb_purge(); + usb_write(DATATYPE_RDBPACKET, "T05swbreak:;", 12+1); + break; + case MSG_RDB_PAUSE: + affected = mainthread; + debug_rdbpaused = TRUE; + debug_pausetime = osGetTime(); + break; + } + + // Do the RDB thread main loop + debug_thread_rdb_loop(affected); + } + } + #else + + /*============================== + debug_thread_rdb_pause + "Pauses" the program execution in Libdragon. + @param The received exception + ==============================*/ + + static void debug_thread_rdb_pause() + { + debug_ismanualpause = TRUE; + debug_pausetime = C0_COUNT(); + asm volatile("break"); // Jank workaround because I can't "message" the exception handler "thread" + } + + + /*============================== + debug_thread_rdb + Handles the remote debugger logic (Libdragon) + @param The received exception + ==============================*/ + + static void debug_thread_rdb(exception_t* exc) + { + switch (exc->code) + { + case EXCEPTION_CODE_BREAKPOINT: + debug_rdbpaused = TRUE; + if (!debug_ismanualpause) + { + usb_purge(); + usb_write(DATATYPE_RDBPACKET, "T05swbreak:;", 12+1); + } + debug_pausetime = C0_COUNT(); + debug_thread_rdb_loop(exc); + if (debug_ismanualpause) + { + debug_ismanualpause = FALSE; + exc->regs->epc += 4; // Gotta increment PC otherwise the program will likely hit the breakpoint again in debug_initialize + } + break; + default: + exception_default_handler(exc); + break; + } + } + #endif + + + /*============================== + debug_thread_rdb_loop + Handles the loop of the remote debugger thread + @param (Libultra) A pointer to the affected thread + (Libdragon) A pointer to the exception struct + ==============================*/ + + static void debug_thread_rdb_loop(OSThread* affected) + { + // Handle the RDB packet + do + { + int usbheader = usb_poll(); + if (USBHEADER_GETTYPE(usbheader) == DATATYPE_RDBPACKET) + { + int i; + u8 found = FALSE; + u32 size = USBHEADER_GETSIZE(usbheader); + + // Read the GDB packet from USB + memset(debug_buffer, 0, BUFFER_SIZE); + usb_read(&debug_buffer, (size <= BUFFER_SIZE) ? size : BUFFER_SIZE); + + // Run a function based on what we received + for (i=0; i<(sizeof(lut_rdbpackets)/sizeof(lut_rdbpackets[0])); i++) + { + if (!strncmp(lut_rdbpackets[i].command, debug_buffer, strlen(lut_rdbpackets[i].command))) + { + found = TRUE; + lut_rdbpackets[i].func(affected); + break; + } + } + + // If we didn't find a supported command, then reply back with nothing + if (!found) + { + usb_purge(); + usb_write(DATATYPE_RDBPACKET, "\0", 1); + } + } + usb_purge(); + } + while (debug_rdbpaused); // Loop forever while we are paused + } + + /*============================== + debug_rdb_qsupported + Responds to GDB with the supported features + @param The affected thread, if any + ==============================*/ + + static void debug_rdb_qsupported(OSThread* t) + { + sprintf(debug_buffer, "swbreak+"); + usb_purge(); + usb_write(DATATYPE_RDBPACKET, debug_buffer, strlen(debug_buffer)); + } + + + /*============================== + debug_rdb_haltreason + Responds to GDB with the halt reason + @param The affected thread, if any + ==============================*/ + + static void debug_rdb_haltreason(OSThread* t) + { + usb_purge(); + usb_write(DATATYPE_RDBPACKET, "S02", 3+1); + } + + + #ifndef LIBDRAGON + /*============================== + register_fromindex + Gets a register type from a given index + @param The affected thread context + @param The register index + @returns The regType that matches the given index + ==============================*/ + + static regType register_fromindex(__OSThreadContext* context, int index) + { + regType reg = {0, NULL}; + switch (index) + { + case 0: // Zero register + case 26: // K0 + case 27: // K1 + case 71: // FCR0 + return reg; + case 32: reg.size = 4; reg.ptr = ((u32*)&context->sr); return reg; + case 33: reg.size = 8; reg.ptr = ((u64*)&context->lo); return reg; + case 34: reg.size = 8; reg.ptr = ((u64*)&context->hi); return reg; + case 35: reg.size = 4; reg.ptr = ((u32*)&context->badvaddr); return reg; + case 36: reg.size = 4; reg.ptr = ((u32*)&context->cause); return reg; + case 37: reg.size = 4; reg.ptr = ((u32*)&context->pc); return reg; + case 70: reg.size = 4; reg.ptr = ((u32*)&context->fpcsr); return reg; + default: + if (index < 32) + { + reg.size = 8; + if (index > 27) + reg.ptr = ((u64*)&context->gp)+(index-28); + else + reg.ptr = ((u64*)&context->at)+(index-1); + return reg; + } + else + { + reg.size = 8; + reg.ptr = ((u64*)&context->fp0)+(((u32)(index-38))/2); + return reg; + } + } + } + #else + /*============================== + register_fromindex + Gets a register type from a given index + @param The affected thread context + @param The register index + @returns The regType that matches the given index + ==============================*/ + + static regType register_fromindex(reg_block_t* context, int index) + { + regType reg = {0, NULL}; + switch (index) + { + case 0: // Zero register + case 26: // K0 + case 27: // K1 + case 71: // FCR0 + case 35: // BadVAddr + case 37: // pc + return reg; + case 32: reg.size = 4; reg.ptr = ((u32*)&context->sr); return reg; + case 33: reg.size = 8; reg.ptr = ((u64*)&context->lo); return reg; + case 34: reg.size = 8; reg.ptr = ((u64*)&context->hi); return reg; + case 36: reg.size = 4; reg.ptr = ((u32*)&context->cr); return reg; + case 70: reg.size = 4; reg.ptr = ((u32*)&context->fc31); return reg; + default: + if (index < 32) + { + reg.size = 8; + reg.ptr = (u64*)&context->gpr[index-1]; + return reg; + } + else + { + reg.size = 8; + reg.ptr = (u64*)&context->fpr[index-38]; + return reg; + } + } + return reg; + } + #endif + + + /*============================== + debug_rdb_printreg_rle + Sprintf's a register value into a buffer, + compressed with Run-Length Encoding + @param The buffer to write to + @param The register type + @returns The number of bytes written + ==============================*/ + + static u32 debug_rdb_printreg_rle(char* buf, regType reg) + { + if (reg.ptr != NULL) + { + int i; + u8 count; + u32 totalwrote = 0; + char last; + char temp[REGISTER_SIZE+1]; + + // Read the register value into a string + if (reg.size == 8) + sprintf(temp, "%016llx", (u64)(*(u64*)reg.ptr)); + else + sprintf(temp, "%016llx", (u64)(*(u32*)reg.ptr)); + last = temp[0]; + count = 1; + + // Find repeated characters + for (i=1; i<(REGISTER_SIZE+1); i++) + { + if (temp[i] != last) + { + // If the repeat was more than 3, then it's worth RLE'ing + if (count > 3) + { + // Because 6 (#) and 7 ($) are special characters in GDB, we have to do them differently + if (count == 7) + totalwrote += sprintf(buf+totalwrote, "%c*\"%c", last, last); + else if (count == 8) + totalwrote += sprintf(buf+totalwrote, "%c*\"%c%c", last, last, last); + else + totalwrote += sprintf(buf+totalwrote, "%c*%c", last, ' '+(count-4)); + } + else + { + int j; + for (j=0; jcontext; + #else + reg_block_t* context = t->regs; + #endif + + // Start by sending a HEADER packet with the chunk count + header[0] = DATATYPE_RDBPACKET; + header[1] = chunkcount; + usb_purge(); + usb_write(DATATYPE_HEADER, &header, sizeof(u32)*2); + + // Perform the humpty dumpty + offset += sprintf(debug_buffer+offset, "0*,"); // Zero register + for (i=1; iepc)); + #endif + else + { + regType reg = register_fromindex(context, i); + offset += debug_rdb_printreg_rle(debug_buffer+offset, reg); + } + + // Send a chunk if we're about to overrun the debug buffer + if ((strlen(debug_buffer)+REGISTER_SIZE+1) > BUFFER_SIZE || i == (REGISTER_COUNT-1)) + { + usb_write(DATATYPE_RDBPACKET, debug_buffer, strlen(debug_buffer)+1); + memset(debug_buffer, 0, BUFFER_SIZE); + offset = 0; + chunkcount--; + } + } + + // Finish sending the other chunks + for (i=chunkcount; i>=0; i--) + usb_write(DATATYPE_RDBPACKET, "\0", 1); + } + else + { + usb_purge(); + usb_write(DATATYPE_RDBPACKET, "E00", 3+1); + } + } + + + /*============================== + hex2u64 + Converts a string containing a hexadecimal value + into a number. This exists because strtol is broken + on ModernSDK. + @param The string with the hexadecimal number + @returns The converted value + ==============================*/ + + static u64 hex2u64(char* addr) + { + int i = 0; + u64 ret = 0; + while (addr[i] != '\0') + { + u32 val; + if (addr[i] <= '9') + val = addr[i]-'0'; + else if (addr[i] <= 'F') + val = 10 + addr[i] - 'A'; + else + val = 10 + addr[i] - 'a'; + ret = (ret << 4) | (val & 0xF); + i++; + } + return ret; + } + + + /*============================== + debug_rdb_writeregisters + Writes a set of registers from a GDB packet + @param The affected thread, if any + ==============================*/ + + static void debug_rdb_writeregisters(OSThread* t) + { + if (t != NULL) + { + int i; + #ifndef LIBDRAGON + __OSThreadContext* context = &t->context; + #else + reg_block_t* context = t->regs; + #endif + + // The incoming data probably won't fit in the buffer, so we'll go bit by bit + usb_rewind(BUFFER_SIZE); + + // Skip the 'G' at the start of the command + usb_skip(1); + + // Do the writing + for (i=0; i= 0x80000000 && addr < 0x80000000 + osMemSize) + { + #ifndef LIBDRAGON + osWritebackDCache((u32*)addr, size); + #else + data_cache_hit_writeback((u32*)addr, size); + #endif + validaddress = TRUE; + } + + // Read the memory address, one byte at a time + while (read < size) + { + u8 val = 0; + if (validaddress) + val = *((vu8*)(addr+read)); + written += sprintf(debug_buffer+written, "%02x", val); + + // Send the partial address dump if we're almost overrunning the buffer, or if we've finished + read++; + if (written+3 >= BUFFER_SIZE || read == size) + { + usb_write(DATATYPE_RDBPACKET, &debug_buffer, strlen(debug_buffer)+1); + written = 0; + chunkcount--; + } + } + + // Finish sending the other chunks + for (i=chunkcount; i>=0; i--) + usb_write(DATATYPE_RDBPACKET, "\0", 1); + } + + + /*============================== + debug_rdb_writememory + Writes the memory from a GDB packet + @param The affected thread, if any + ==============================*/ + + static void debug_rdb_writememory(OSThread* t) + { + int i; + u32 addr; + u32 size; + #ifdef LIBDRAGON + u32 osMemSize = get_memory_size(); + #endif + char* commandp = &debug_buffer[0]; + + // Skip the 'M' at the start of the command + commandp++; + + // Extract the address value + strtok(commandp, ","); + addr = (u32)hex2u64(commandp); + + // Extract the size value + commandp = strtok(NULL, ":"); + size = (u32)hex2u64(commandp); + + // Finally, point to the data we're actually gonna write + commandp = strtok(NULL, "\0"); + + // We need to translate the address before trying to read it + addr = debug_rdb_translateaddr(addr); + + // Ensure we are writing to a valid memory address + if (addr >= 0x80000000 && addr < 0x80000000 + osMemSize) + { + // Read the memory address, one byte at a time + for (i=0; i= 0x80000000 && addr < 0x80000000 + osMemSize) + { + for (i=0; i= 0x80000000 && addr < 0x80000000 + osMemSize) + { + index = GET_BREAKPOINT_INDEX(*((u32*)addr))-1; + if (debug_bpoints[index].addr == (u32*)addr) + { + int i; + + // Remove the breakpoint + *((vu32*)addr) = debug_bpoints[index].instruction; + #ifndef LIBDRAGON + osWritebackDCache((u32*)addr, 4); + osInvalICache((u32*)addr, 4); + #else + data_cache_hit_writeback((u32*)addr, 4); + inst_cache_hit_invalidate((u32*)addr, 4); + #endif + + // Move all the breakpoints in front of it back + for (i=index; i 0) { @@ -642,17 +642,17 @@ void usb_read(void* buffer, int nbytes) left = usb_dataleft; if (block > left) block = left; - + // Call the read function if we're reading a new block if (usb_readblock != blockoffset) { usb_readblock = blockoffset; funcPointer_read(); } - + // Copy from the USB buffer to the supplied buffer - memcpy(buffer+read, usb_buffer+copystart, block); - + memcpy((void*)(((u32)buffer)+read), usb_buffer+copystart, block); + // Increment/decrement all our counters read += block; left -= block; @@ -729,7 +729,7 @@ char usb_timedout() version. ==============================*/ -void usb_sendheartbeat() +void usb_sendheartbeat(void) { u8 buffer[4]; @@ -900,7 +900,7 @@ static u32 usb_64drive_cui_read(u32 offset) // Wait until USB FIFO is disarmed while ((usb_io_read(D64_REG_USBCOMSTAT) & D64_CUI_ARM_MASK) != D64_CUI_ARM_IDLE) ; - + // Due to a 64drive bug, we need to ignore the last 512 bytes of the transfer if it's larger than 512 bytes if (size > 512) size -= 512; @@ -921,7 +921,7 @@ static u32 usb_64drive_cui_read(u32 offset) static void usb_64drive_write(int datatype, const void* data, int size) { - u32 left = size; + s32 left = size; u32 pi_address = D64_BASE + DEBUG_ADDRESS; // Return if previous transfer timed out @@ -942,12 +942,16 @@ static void usb_64drive_write(int datatype, const void* data, int size) // Copy data to PI DMA aligned buffer memcpy(usb_buffer, data, block); + + // Pad the buffer with zeroes if it wasn't 4 byte aligned + while (block%4) + usb_buffer[block++] = 0; // Copy block of data from RDRAM to SDRAM usb_dma_write(usb_buffer, pi_address, ALIGN(block, 2)); // Update pointers and variables - data += block; + data = (void*)(((u32)data) + block); left -= block; pi_address += block; } @@ -1015,7 +1019,7 @@ static void usb_64drive_read(void) @return FALSE on success, TRUE on failure ==============================*/ -static char usb_everdrive_usbbusy(void) +static char usb_everdrive_usbbusy(void) { u32 val; u32 timeout = usb_timeout_start(); @@ -1025,6 +1029,7 @@ static char usb_everdrive_usbbusy(void) if (usb_timeout_check(timeout, ED_TIMEOUT)) { usb_io_write(ED_REG_USBCFG, ED_USBMODE_RDNOP); + usb_didtimeout = TRUE; return TRUE; } } @@ -1039,11 +1044,11 @@ static char usb_everdrive_usbbusy(void) @return TRUE if it can read, FALSE if not ==============================*/ -static char usb_everdrive_canread(void) +static char usb_everdrive_canread(void) { u32 val; u32 status = ED_USBSTAT_POWER; - + // Read the USB register and check its status val = usb_io_read(ED_REG_USBCFG); status = val & (ED_USBSTAT_POWER | ED_USBSTAT_RXF); @@ -1063,15 +1068,15 @@ static char usb_everdrive_canread(void) static void usb_everdrive_readusb(void* buffer, int size) { u16 block, addr; - - while (size) + + while (size) { // Get the block size block = BUFFER_SIZE; if (block > size) block = size; addr = BUFFER_SIZE - block; - + // Request to read from the USB usb_io_write(ED_REG_USBCFG, ED_USBMODE_RD | addr); @@ -1104,7 +1109,7 @@ static void usb_everdrive_write(int datatype, const void* data, int size) int left = size; int offset = 8; u32 header = (size & 0x00FFFFFF) | (datatype << 24); - + // Put in the DMA header along with length and type information in the global buffer usb_buffer[0] = 'D'; usb_buffer[1] = 'M'; @@ -1114,7 +1119,7 @@ static void usb_everdrive_write(int datatype, const void* data, int size) usb_buffer[5] = (header >> 16) & 0xFF; usb_buffer[6] = (header >> 8) & 0xFF; usb_buffer[7] = header & 0xFF; - + // Write data to USB until we've finished while (left > 0) { @@ -1122,10 +1127,10 @@ static void usb_everdrive_write(int datatype, const void* data, int size) int blocksend, baddr; if (block+offset > BUFFER_SIZE) block = BUFFER_SIZE-offset; - + // Copy the data to the next available spots in the global buffer memcpy(usb_buffer+offset, (void*)((char*)data+read), block); - + // Restart the loop to write the CMP signal if we've finished if (!wrotecmp && read+block >= size) { @@ -1136,7 +1141,7 @@ static void usb_everdrive_write(int datatype, const void* data, int size) read = 0; continue; } - + // Ensure the data is 2 byte aligned and the block address is correct blocksend = ALIGN((block+offset), 2); baddr = BUFFER_SIZE - blocksend; @@ -1144,7 +1149,7 @@ static void usb_everdrive_write(int datatype, const void* data, int size) // Set USB to write mode and send data through USB usb_io_write(ED_REG_USBCFG, ED_USBMODE_WRNOP); usb_dma_write(usb_buffer, ED_REG_USBDAT + baddr, blocksend); - + // Set USB to write mode with the new address and wait for USB to end (or stop if it times out) usb_io_write(ED_REG_USBCFG, ED_USBMODE_WR | baddr); if (usb_everdrive_usbbusy()) @@ -1152,7 +1157,7 @@ static void usb_everdrive_write(int datatype, const void* data, int size) usb_didtimeout = TRUE; return; } - + // Keep track of what we've read so far left -= block; read += block; @@ -1171,49 +1176,49 @@ static void usb_everdrive_write(int datatype, const void* data, int size) static u32 usb_everdrive_poll(void) { - int len; - int offset = 0; - char buffaligned[32]; - char* buff = (char*)OS_DCACHE_ROUNDUP_ADDR(buffaligned); - + int len; + int offset = 0; + unsigned char buffaligned[32]; + unsigned char* buff = (unsigned char*)OS_DCACHE_ROUNDUP_ADDR(buffaligned); + // Wait for the USB to be ready if (usb_everdrive_usbbusy()) return 0; - + // Check if the USB is ready to be read if (!usb_everdrive_canread()) return 0; - + // Read the first 8 bytes that are being received and check if they're valid usb_everdrive_readusb(buff, 8); if (buff[0] != 'D' || buff[1] != 'M' || buff[2] != 'A' || buff[3] != '@') return 0; - + // Store information about the incoming data - usb_datatype = (int)buff[4]; - usb_datasize = (int)buff[5]<<16 | (int)buff[6]<<8 | (int)buff[7]<<0; + usb_datatype = buff[4]; + usb_datasize = (buff[5] << 16) | (buff[6] << 8) | (buff[7] << 0); usb_dataleft = usb_datasize; usb_readblock = -1; - + // Get the aligned data size. Must be 2 byte aligned len = ALIGN(usb_datasize, 2); - + // While there's data to service - while (len > 0) + while (len > 0) { u32 bytes_do = BUFFER_SIZE; if (len < BUFFER_SIZE) bytes_do = len; - + // Read a chunk from USB and store it into our temp buffer usb_everdrive_readusb(usb_buffer, bytes_do); - + // Copy received block to ROM usb_dma_write(usb_buffer, ED_BASE + DEBUG_ADDRESS + offset, bytes_do); offset += bytes_do; len -= bytes_do; } - + // Read the CMP Signal if (usb_everdrive_usbbusy()) return 0; @@ -1227,7 +1232,7 @@ static u32 usb_everdrive_poll(void) usb_readblock = -1; return 0; } - + // Return the data header return USBHEADER_CREATE(usb_datatype, usb_datasize); } @@ -1358,7 +1363,7 @@ static void usb_sc64_write(int datatype, const void* data, int size) usb_dma_write(usb_buffer, pi_address, ALIGN(block, 2)); // Update pointers and variables - data += block; + data = (void*)(((u32)data) + block); left -= block; pi_address += block; } @@ -1414,7 +1419,7 @@ static u32 usb_sc64_poll(void) // Return 0 if there's no data if (size == 0) return 0; - + // Fill USB read data variables usb_datatype = datatype; usb_dataleft = size; diff --git a/src/usb/usb.h b/src/usb/usb.h index 215577468..27262fc32 100644 --- a/src/usb/usb.h +++ b/src/usb/usb.h @@ -6,60 +6,61 @@ /********************************* DataType macros *********************************/ - + // UNCOMMENT THE #DEFINE IF USING LIBDRAGON //#define LIBDRAGON - + // Settings #define USE_OSRAW 0 // Use if you're doing USB operations without the PI Manager (libultra only) #define DEBUG_ADDRESS_SIZE 8*1024*1024 // Max size of USB I/O. The bigger this value, the more ROM you lose! #define CHECK_EMULATOR 0 // Stops the USB library from working if it detects an emulator to prevent problems - + // Cart definitions #define CART_NONE 0 #define CART_64DRIVE 1 #define CART_EVERDRIVE 2 #define CART_SC64 3 - + // Data types defintions - #define DATATYPE_TEXT 0x01 - #define DATATYPE_RAWBINARY 0x02 - #define DATATYPE_HEADER 0x03 - #define DATATYPE_SCREENSHOT 0x04 - #define DATATYPE_HEARTBEAT 0x05 - - + #define DATATYPE_TEXT 0x01 + #define DATATYPE_RAWBINARY 0x02 + #define DATATYPE_HEADER 0x03 + #define DATATYPE_SCREENSHOT 0x04 + #define DATATYPE_HEARTBEAT 0x05 + #define DATATYPE_RDBPACKET 0x06 + + /********************************* Convenience macros *********************************/ - + // Use these to conveniently read the header from usb_poll() - #define USBHEADER_GETTYPE(header) ((header & 0xFF000000) >> 24) - #define USBHEADER_GETSIZE(header) ((header & 0x00FFFFFF)) - - + #define USBHEADER_GETTYPE(header) (((header) & 0xFF000000) >> 24) + #define USBHEADER_GETSIZE(header) (((header) & 0x00FFFFFF)) + + /********************************* USB Functions *********************************/ - + /*============================== usb_initialize Initializes the USB buffers and pointers @return 1 if the USB initialization was successful, 0 if not ==============================*/ - - extern char usb_initialize(); - - + + extern char usb_initialize(void); + + /*============================== usb_getcart Returns which flashcart is currently connected @return The CART macro that corresponds to the identified flashcart ==============================*/ - - extern char usb_getcart(); - - + + extern char usb_getcart(void); + + /*============================== usb_write Writes data to the USB. @@ -68,54 +69,54 @@ @param A buffer with the data to send @param The size of the data being sent ==============================*/ - + extern void usb_write(int datatype, const void* data, int size); - - + + /*============================== usb_poll Returns the header of data being received via USB The first byte contains the data type, the next 3 the number of bytes left to read @return The data header, or 0 ==============================*/ - + extern u32 usb_poll(); - - + + /*============================== usb_read Reads bytes from USB into the provided buffer @param The buffer to put the read data in @param The number of bytes to read ==============================*/ - + extern void usb_read(void* buffer, int size); - - + + /*============================== usb_skip Skips a USB read by the specified amount of bytes @param The number of bytes to skip ==============================*/ - + extern void usb_skip(int nbytes); - - + + /*============================== usb_rewind Rewinds a USB read by the specified amount of bytes @param The number of bytes to rewind ==============================*/ - + extern void usb_rewind(int nbytes); - - + + /*============================== usb_purge Purges the incoming USB data ==============================*/ - - extern void usb_purge(); + + extern void usb_purge(void); /*============================== @@ -124,7 +125,7 @@ @return 1 if the USB timed out, 0 if not ==============================*/ - extern char usb_timedout(); + extern char usb_timedout(void); /*============================== @@ -136,6 +137,6 @@ version. ==============================*/ - extern void usb_sendheartbeat(); + extern void usb_sendheartbeat(void); #endif diff --git a/tools/get_latest_unfloader.py b/tools/get_latest_unfloader.py index 3da3e3d9c..868729618 100644 --- a/tools/get_latest_unfloader.py +++ b/tools/get_latest_unfloader.py @@ -13,7 +13,9 @@ def get_latest_build_artifacts_url(): return os.path.join(body['value'][0]['url'], 'artifacts') def main(): - destpath = sys.argv[1] if len(sys.argv) > 1 else './' + destpath = sys.argv[1] if len(sys.argv) > 1 else '.' + unfloader_zip_path = os.path.join(destpath, "UNFLoader.zip") + os.makedirs(destpath, exist_ok=True) is_wsl = 'microsoft-standard' in str(platform.uname()).lower() unf_fn = 'UNFLoader.exe' if is_wsl else 'UNFLoader' artifact_url = get_latest_build_artifacts_url() @@ -32,24 +34,24 @@ def main(): # download unf zipfile artifact_res = request('GET', platform_artifact_url) - with open('UNFLoader.zip', 'wb') as unf_fp: + with open(unfloader_zip_path, 'wb') as unf_fp: unf_fp.write(artifact_res.content) # only extract the specific file that we need unfpath = None - with zipfile.ZipFile('UNFLoader.zip', 'r') as zip_ref: + with zipfile.ZipFile(unfloader_zip_path, 'r') as zip_ref: for zipinfo in zip_ref.infolist(): if not zipinfo.is_dir(): - unfpath = zip_ref.extract(zipinfo) + unfpath = zip_ref.extract(zipinfo, destpath) unf_bin_path = os.path.join(destpath, unf_fn) - # file gets extracted to ./unfloader-{platform}/UNFLoader[.exe], - # so move binary to ./UNFLoader[.exe] + # file gets extracted to [destpath]/unfloader-{platform}/UNFLoader[.exe], + # so move binary to [destpath]/UNFLoader[.exe] os.rename(unfpath, unf_bin_path) - # remove ./unfloader-{platform}/ directory + # remove [destpath]/unfloader-{platform}/ directory os.rmdir(unfpath.rstrip(unf_fn)) # remove UNFLoader.zip - os.remove('UNFLoader.zip') + os.remove(unfloader_zip_path) # now need to add executable file permissions to unfloader st = os.stat(unf_bin_path)