From 74cb8ce06217305f9aee7bf313f61589aaf7d6ee Mon Sep 17 00:00:00 2001 From: Gregory Heskett Date: Tue, 30 Apr 2024 02:31:34 -0400 Subject: [PATCH 1/2] Update to newest UNF (d5f2ad100b76e372036453f8d25f9b6fbbbd2d1a) (November 22 2023) --- src/usb/debug.c | 1359 ++++++++++++++++++++++++++++++++++++++++++----- src/usb/debug.h | 40 +- src/usb/usb.c | 153 +++--- src/usb/usb.h | 89 ++-- 4 files changed, 1365 insertions(+), 276 deletions(-) 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 From e3472e05cf994ece4f3abb1147318ebe5eaf78b7 Mon Sep 17 00:00:00 2001 From: Gregory Heskett Date: Sat, 3 Aug 2024 19:09:22 -0400 Subject: [PATCH 2/2] Add option for .local/share/HackerSM64/UNFLoader-dir.txt This allows referencing a custom UNFLoader path to be used, ideally for placing on the C drive for WSL instances. UNFLoader tends to hang for an unbearably long time when saved somewhere within the WSL directory structure, and simply using the Linux build isn't an option because it can't access Windows USB devices trivially. --- Makefile | 27 ++++++++++++++++++--------- tools/get_latest_unfloader.py | 18 ++++++++++-------- 2 files changed, 28 insertions(+), 17 deletions(-) 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/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)