diff --git a/src/usb/debug.c b/src/usb/debug.c index 35baf5981..0685789ce 100644 --- a/src/usb/debug.c +++ b/src/usb/debug.c @@ -18,7 +18,6 @@ https://github.com/buu342/N64-UNFLoader #include - #if DEBUG_MODE /********************************* @@ -39,39 +38,41 @@ https://github.com/buu342/N64-UNFLoader #define COMMAND_TOKENS 10 #define BUFFER_SIZE 256 + /********************************* - Libultra types (for libdragon) + Libultra types (for libdragon) *********************************/ - + #ifdef LIBDRAGON typedef unsigned char u8; typedef unsigned short u16; typedef unsigned long u32; typedef unsigned long long u64; - + typedef signed char s8; typedef short s16; typedef long s32; typedef long long s64; - + typedef volatile unsigned char vu8; typedef volatile unsigned short vu16; typedef volatile unsigned long vu32; typedef volatile unsigned long long vu64; - + typedef volatile signed char vs8; typedef volatile short vs16; typedef volatile long vs32; typedef volatile long long vs64; - + typedef float f32; typedef double f64; #endif + /********************************* Structs *********************************/ - + // Register struct typedef struct { @@ -109,7 +110,7 @@ https://github.com/buu342/N64-UNFLoader static void debug_thread_fault(void *arg); #endif static void debug_thread_usb(void *arg); - + // Other #if OVERWRITE_OSPRINT static void* debug_osSyncPrintf_implementation(void *unused, const char *str, size_t len); @@ -117,12 +118,13 @@ https://github.com/buu342/N64-UNFLoader #else static void debug_thread_usb(void *arg); #endif + static inline void debug_handle_64drivebutton(); /********************************* Globals *********************************/ - + // Function pointers #ifndef LIBDRAGON extern int _Printf(void *(*copyfunc)(void *, const char *, size_t), void*, const char*, va_list); @@ -130,7 +132,7 @@ https://github.com/buu342/N64-UNFLoader extern void* __printfunc; #endif #endif - + // Debug globals static char debug_initialized = 0; static char debug_buffer[BUFFER_SIZE]; @@ -138,20 +140,25 @@ https://github.com/buu342/N64-UNFLoader // Commands hashtable related static debugCommand* debug_commands_hashtable[HASHTABLE_SIZE]; static debugCommand debug_commands_elements[MAX_COMMANDS]; - static int debug_commands_count = 0; + static int debug_commands_count = 0; // Command parsing related - static int debug_command_current = 0; - static int debug_command_totaltokens = 0; - static int debug_command_incoming_start[COMMAND_TOKENS]; - static int debug_command_incoming_size[COMMAND_TOKENS]; - static char* debug_command_error; - + static int debug_command_current = 0; + static int debug_command_totaltokens = 0; + static int debug_command_incoming_start[COMMAND_TOKENS]; + static int debug_command_incoming_size[COMMAND_TOKENS]; + static char* debug_command_error = NULL; + // Assertion globals - static int assert_line = 0; + static int assert_line = 0; static const char* assert_file = NULL; static const char* assert_expr = NULL; - + + // 64Drive button functions + static void (*debug_64dbut_func)() = NULL; + static u64 debug_64dbut_debounce = 0; + static u64 debug_64dbut_hold = 0; + #ifndef LIBDRAGON // Fault thread globals #if USE_FAULTTHREAD @@ -166,7 +173,7 @@ https://github.com/buu342/N64-UNFLoader static OSMesg usbMessageBuf; static OSThread usbThread; static u64 usbThreadStack[USB_THREAD_STACK/sizeof(u64)]; - + // List of error causes static regDesc causeDesc[] = { {CAUSE_BD, CAUSE_BD, "BD"}, @@ -198,7 +205,7 @@ https://github.com/buu342/N64-UNFLoader {CAUSE_EXCMASK, EXC_VCED, "Virtual coherency exception on data reference"}, {0, 0, ""} }; - + // List of register descriptions static regDesc srDesc[] = { {SR_CU3, SR_CU3, "CU3"}, @@ -233,7 +240,7 @@ https://github.com/buu342/N64-UNFLoader {SR_IE, SR_IE, "IE"}, {0, 0, ""} }; - + // List of floating point registers descriptions static regDesc fpcsrDesc[] = { {FPCSR_FS, FPCSR_FS, "FS"}, @@ -261,7 +268,8 @@ https://github.com/buu342/N64-UNFLoader {0, 0, ""} }; #endif - + + /********************************* Debug functions *********************************/ @@ -276,7 +284,7 @@ https://github.com/buu342/N64-UNFLoader // Initialize the USB functions if (!usb_initialize()) return; - + // Overwrite osSyncPrintf #ifndef LIBDRAGON #if OVERWRITE_OSPRINT @@ -409,7 +417,7 @@ https://github.com/buu342/N64-UNFLoader // Ensure debug mode is initialized if (!debug_initialized) return; - + // Create the data header to send data[0] = DATATYPE_SCREENSHOT; data[1] = depth; @@ -426,7 +434,7 @@ https://github.com/buu342/N64-UNFLoader #else debug_thread_usb(&msg); #endif - + // Send the framebuffer to the USB thread msg.msgtype = MSG_WRITE; msg.datatype = DATATYPE_SCREENSHOT; @@ -438,8 +446,8 @@ https://github.com/buu342/N64-UNFLoader debug_thread_usb(&msg); #endif } - - + + /*============================== _debug_assert Halts the program (assumes expression failed) @@ -447,11 +455,9 @@ https://github.com/buu342/N64-UNFLoader @param The file where the exception ocurred @param The line number where the exception ocurred ==============================*/ - + void _debug_assert(const char* expression, const char* file, int line) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" volatile char crash; // Set the assert data @@ -463,10 +469,25 @@ https://github.com/buu342/N64-UNFLoader #ifdef LIBDRAGON debug_printf("Assertion failed in file '%s', line %d.\n", assert_file, assert_line); #endif - + // Intentionally cause a TLB exception on load/instruction fetch crash = *(volatile char *)1; -#pragma GCC diagnostic pop + (void)crash; + } + + + /*============================== + debug_64drivebutton + Assigns a function to be executed when the 64drive button is pressed. + @param The function pointer to execute + @param Whether or not to execute the function only on pressing (ignore holding the button down) + ==============================*/ + + void debug_64drivebutton(void(*execute)(), char onpress) + { + debug_64dbut_func = execute; + debug_64dbut_debounce = 0; + debug_64dbut_hold = !onpress; } @@ -549,7 +570,10 @@ 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; #ifndef LIBDRAGON @@ -560,6 +584,56 @@ https://github.com/buu342/N64-UNFLoader } + /*============================== + debug_handle_64drivebutton + Handles the 64Drive's button logic + ==============================*/ + + static inline void debug_handle_64drivebutton() + { + static u32 held = 0; + + // If we own a 64Drive + if (usb_getcart() == CART_64DRIVE && debug_64dbut_func != NULL) + { + u64 curtime; + #ifndef LIBDRAGON + curtime = osGetTime(); + #else + curtime = timer_ticks(); + #endif + + // And the debounce time on the 64Drive's button has elapsed + if (debug_64dbut_debounce < curtime) + { + s32 bpoll; + #ifndef LIBDRAGON + osPiReadIo(0xB80002F8, (u32 *)&bpoll); + #else + bpoll = io_read(0xB80002F8); + #endif + bpoll = (bpoll&0xFFFF0000)>>16; + + // If the 64Drive's button has been pressed, then execute the assigned function and set the debounce timer + if (bpoll == 0 && (debug_64dbut_hold || !held)) + { + u64 nexttime; + #ifndef LIBDRAGON + nexttime = OS_USEC_TO_CYCLES(100000); + #else + nexttime = TIMER_TICKS(100000); + #endif + debug_64dbut_debounce = curtime + nexttime; + debug_64dbut_func(); + held = 1; + } + else if (bpoll != 0 && held) + held = 0; + } + } + } + + /*============================== debug_sizecommand Returns the size of the data from this part of the command @@ -606,7 +680,7 @@ https://github.com/buu342/N64-UNFLoader usb_rewind(debug_command_incoming_size[curr]+debug_command_incoming_start[curr]); debug_command_current++; } - + /*============================== debug_commands_setup @@ -631,11 +705,11 @@ https://github.com/buu342/N64-UNFLoader int readsize = BUFFER_SIZE; if (readsize > dataleft) readsize = dataleft; - + // Read a block from USB memset(debug_buffer, 0, BUFFER_SIZE); usb_read(debug_buffer, readsize); - + // Parse the block for (i=0; i 0; i++) { @@ -709,12 +783,12 @@ https://github.com/buu342/N64-UNFLoader Handles the USB thread @param Arbitrary data that the thread can receive ==============================*/ - + static void debug_thread_usb(void *arg) { char errortype = USBERROR_NONE; usbMesg* threadMsg; - + #ifndef LIBDRAGON // Create the message queue for the USB message osCreateMesgQueue(&usbMessageQ, &usbMessageBuf, 1); @@ -722,7 +796,7 @@ https://github.com/buu342/N64-UNFLoader // Set the received thread message to the argument threadMsg = (usbMesg*)arg; #endif - + // Thread loop while (1) { @@ -815,10 +889,12 @@ https://github.com/buu342/N64-UNFLoader 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 #ifdef LIBDRAGON break; @@ -837,7 +913,7 @@ https://github.com/buu342/N64-UNFLoader @param The amount of characters to write @returns The end of the buffer that was written to ==============================*/ - + static void* debug_osSyncPrintf_implementation(void *unused, const char *str, size_t len) { void* ret; @@ -859,7 +935,7 @@ https://github.com/buu342/N64-UNFLoader } #endif - + #if USE_FAULTTHREAD /*============================== @@ -869,7 +945,7 @@ https://github.com/buu342/N64-UNFLoader @param The name of the register @param The registry description to use ==============================*/ - + static void debug_printreg(u32 value, char *name, regDesc *desc) { char first = 1; @@ -897,11 +973,11 @@ https://github.com/buu342/N64-UNFLoader { OSMesg msg; OSThread *curr; - + // Create the message queue for the fault message osCreateMesgQueue(&faultMessageQ, &faultMessageBuf, 1); osSetEventMesg(OS_EVENT_FAULT, &faultMessageQ, (OSMesg)MSG_FAULT); - + // Thread loop while (1) { @@ -913,7 +989,7 @@ https://github.com/buu342/N64-UNFLoader if (curr != NULL) { __OSThreadContext* context = &curr->context; - + // Print the basic info debug_printf("Fault in thread: %d\n\n", curr->id); debug_printf("pc\t\t0x%08x\n", context->pc); @@ -935,7 +1011,7 @@ https://github.com/buu342/N64-UNFLoader debug_printf("s6 0x%016llx s7 0x%016llx t8 0x%016llx\n", context->s6, context->s7, context->t8); debug_printf("t9 0x%016llx gp 0x%016llx sp 0x%016llx\n", context->t9, context->gp, context->sp); debug_printf("s8 0x%016llx ra 0x%016llx\n\n", context->s8, context->ra); - + // Print the floating point registers debug_printreg(context->fpcsr, "fpcsr", fpcsrDesc); debug_printf("\n"); @@ -954,4 +1030,4 @@ https://github.com/buu342/N64-UNFLoader #endif #endif -#endif \ No newline at end of file +#endif diff --git a/src/usb/debug.h b/src/usb/debug.h index 29e690b26..01df9cfa8 100644 --- a/src/usb/debug.h +++ b/src/usb/debug.h @@ -79,6 +79,16 @@ #define debug_assert(expr) (expr) ? ((void)0) : _debug_assert(#expr, __FILE__, __LINE__) + /*============================== + debug_64drivebutton + Assigns a function to be executed when the 64drive button is pressed. + @param The function pointer to execute + @param Whether or not to execute the function only on pressing (ignore holding the button down) + ==============================*/ + + extern void debug_64drivebutton(void(*execute)(), char onpress); + + /*============================== debug_pollcommands Check the USB for incoming commands. @@ -92,7 +102,7 @@ Adds a command for the USB to read. @param The command name @param The command description - @param The function pointer to execute + @param The function pointer to execute ==============================*/ extern void debug_addcommand(char* command, char* description, char*(*execute)()); @@ -136,14 +146,15 @@ // Overwrite library functions with useless macros if debug mode is disabled #define debug_initialize() - #define debug_printf(__VA_ARGS__) + #define debug_printf #define debug_screenshot(a, b, c) #define debug_assert(a) #define debug_pollcommands() #define debug_addcommand(a, b, c) - #define debug_parsecommand() NULL + #define debug_parsecommand(a) NULL #define debug_sizecommand() 0 #define debug_printcommands() + #define debug_64drivebutton(a, b) #define usb_initialize() 0 #define usb_getcart() 0 #define usb_write(a, b, c) diff --git a/src/usb/usb.c b/src/usb/usb.c index 37becf01d..f699bb471 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -23,11 +23,15 @@ https://github.com/buu342/N64-UNFLoader #define BUFFER_SIZE 512 // USB Memory location -#define DEBUG_ADDRESS 0x04000000-DEBUG_ADDRESS_SIZE // Put the debug area at the 63MB area in ROM space +#define DEBUG_ADDRESS (0x04000000 - DEBUG_ADDRESS_SIZE) // Put the debug area at the 64MB - DEBUG_ADDRESS_SIZE area in ROM space // Data header related #define USBHEADER_CREATE(type, left) (((type<<24) | (left & 0x00FFFFFF))) +// Protocol related +#define USBPROTOCOL_VERSION 2 +#define HEARTBEAT_VERSION 1 + /********************************* Libultra macros for libdragon @@ -35,8 +39,12 @@ https://github.com/buu342/N64-UNFLoader #ifdef LIBDRAGON // Useful - #define ALIGN(s, align) (((u32)(s) + ((align)-1)) & ~((align)-1)) - #define MIN(a, b) ((a) < (b) ? (a) : (b)) + #ifndef MIN + #define MIN(a, b) ((a) < (b) ? (a) : (b)) + #endif + #ifndef ALIGN + #define ALIGN(value, align) (((value) + ((typeof(value))(align) - 1)) & ~((typeof(value))(align) - 1)) + #endif #ifndef TRUE #define TRUE 1 #endif @@ -52,150 +60,122 @@ https://github.com/buu342/N64-UNFLoader #define KSEG1 0xA0000000 // Memory translation stuff - #define PHYS_TO_K1(x) ((u32)(x)|KSEG1) - #define IO_WRITE(addr,data) (*(vu32 *)PHYS_TO_K1(addr)=(u32)(data)) - #define IO_READ(addr) (*(vu32 *)PHYS_TO_K1(addr)) + #define PHYS_TO_K1(x) ((u32)(x)|KSEG1) + #define IO_WRITE(addr,data) (*(vu32 *)PHYS_TO_K1(addr)=(u32)(data)) + #define IO_READ(addr) (*(vu32 *)PHYS_TO_K1(addr)) - // PI registers - #define PI_BASE_REG 0x04600000 - #define PI_STATUS_REG (PI_BASE_REG+0x10) - #define PI_STATUS_ERROR 0x04 - #define PI_STATUS_IO_BUSY 0x02 - #define PI_STATUS_DMA_BUSY 0x01 - - #define PI_BSD_DOM1_LAT_REG (PI_BASE_REG+0x14) - #define PI_BSD_DOM1_PWD_REG (PI_BASE_REG+0x18) - #define PI_BSD_DOM1_PGS_REG (PI_BASE_REG+0x1C) - #define PI_BSD_DOM1_RLS_REG (PI_BASE_REG+0x20) - #define PI_BSD_DOM2_LAT_REG (PI_BASE_REG+0x24) - #define PI_BSD_DOM2_PWD_REG (PI_BASE_REG+0x28) - #define PI_BSD_DOM2_PGS_REG (PI_BASE_REG+0x2C) - #define PI_BSD_DOM2_RLS_REG (PI_BASE_REG+0x30) + // Data alignment + #define OS_DCACHE_ROUNDUP_ADDR(x) (void *)(((((u32)(x)+0xf)/0x10)*0x10)) + #define OS_DCACHE_ROUNDUP_SIZE(x) (u32)(((((u32)(x)+0xf)/0x10)*0x10)) #endif -/********************************* - Parallel Interface macros -*********************************/ - -#define N64_PI_ADDRESS 0xA4600000 - -#define N64_PI_RAMADDRESS 0x00 -#define N64_PI_PIADDRESS 0x04 -#define N64_PI_READLENGTH 0x08 -#define N64_PI_WRITELENGTH 0x0C -#define N64_PI_STATUS 0x10 - - /********************************* 64Drive macros *********************************/ -// How many cycles for the 64Drive to wait for data. -// Lowering this might improve performance slightly faster at the expense of USB reading accuracy -#define D64_POLLTIME 2000 +#define D64_COMMAND_TIMEOUT 1000 +#define D64_WRITE_TIMEOUT 1000 -// Cartridge Interface definitions. Obtained from 64Drive's Spec Sheet -#define D64_BASE_ADDRESS 0xB0000000 -#define D64_CIREG_ADDRESS 0x08000000 -#define D64_CIBASE_ADDRESS 0xB8000000 +#define D64_BASE 0x10000000 +#define D64_REGS_BASE 0x18000000 -#define D64_REGISTER_STATUS 0x00000200 -#define D64_REGISTER_COMMAND 0x00000208 -#define D64_REGISTER_LBA 0x00000210 -#define D64_REGISTER_LENGTH 0x00000218 -#define D64_REGISTER_RESULT 0x00000220 +#define D64_REG_STATUS (D64_REGS_BASE + 0x0200) +#define D64_REG_COMMAND (D64_REGS_BASE + 0x0208) -#define D64_REGISTER_MAGIC 0x000002EC -#define D64_REGISTER_VARIANT 0x000002F0 -#define D64_REGISTER_BUTTON 0x000002F8 -#define D64_REGISTER_REVISION 0x000002FC +#define D64_REG_MAGIC (D64_REGS_BASE + 0x02EC) -#define D64_REGISTER_USBCOMSTAT 0x00000400 -#define D64_REGISTER_USBP0R0 0x00000404 -#define D64_REGISTER_USBP1R1 0x00000408 +#define D64_REG_USBCOMSTAT (D64_REGS_BASE + 0x0400) +#define D64_REG_USBP0R0 (D64_REGS_BASE + 0x0404) +#define D64_REG_USBP1R1 (D64_REGS_BASE + 0x0408) -#define D64_ENABLE_ROMWR 0xF0 -#define D64_DISABLE_ROMWR 0xF1 -#define D64_COMMAND_WRITE 0x08 +#define D64_CI_BUSY 0x1000 -// Cartridge Interface return values -#define D64_MAGIC 0x55444556 +#define D64_MAGIC 0x55444556 -#define D64_USB_IDLE 0x00 -#define D64_USB_IDLEUNARMED 0x00 -#define D64_USB_ARMED 0x01 -#define D64_USB_DATA 0x02 -#define D64_USB_ARM 0x0A -#define D64_USB_BUSY 0x0F -#define D64_USB_DISARM 0x0F -#define D64_USB_ARMING 0x0F +#define D64_CI_ENABLE_ROMWR 0xF0 +#define D64_CI_DISABLE_ROMWR 0xF1 -#define D64_CI_IDLE 0x00 -#define D64_CI_BUSY 0x10 -#define D64_CI_WRITE 0x20 +#define D64_CUI_ARM 0x0A +#define D64_CUI_DISARM 0x0F +#define D64_CUI_WRITE 0x08 + +#define D64_CUI_ARM_MASK 0x0F +#define D64_CUI_ARM_IDLE 0x00 +#define D64_CUI_ARM_UNARMED_DATA 0x02 + +#define D64_CUI_WRITE_MASK 0xF0 +#define D64_CUI_WRITE_IDLE 0x00 +#define D64_CUI_WRITE_BUSY 0xF0 /********************************* EverDrive macros *********************************/ +#define ED_TIMEOUT 1000 + #define ED_BASE 0x10000000 #define ED_BASE_ADDRESS 0x1F800000 -#define ED_GET_REGADD(reg) (0xA0000000 | ED_BASE_ADDRESS | (reg)) -#define ED_REG_USBCFG 0x0004 -#define ED_REG_VERSION 0x0014 -#define ED_REG_USBDAT 0x0400 -#define ED_REG_SYSCFG 0x8000 -#define ED_REG_KEY 0x8004 +#define ED_REG_USBCFG (ED_BASE_ADDRESS | 0x0004) +#define ED_REG_VERSION (ED_BASE_ADDRESS | 0x0014) +#define ED_REG_USBDAT (ED_BASE_ADDRESS | 0x0400) +#define ED_REG_SYSCFG (ED_BASE_ADDRESS | 0x8000) +#define ED_REG_KEY (ED_BASE_ADDRESS | 0x8004) -#define ED_USBMODE_RDNOP 0xC400 -#define ED_USBMODE_RD 0xC600 -#define ED_USBMODE_WRNOP 0xC000 -#define ED_USBMODE_WR 0xC200 +#define ED_USBMODE_RDNOP 0xC400 +#define ED_USBMODE_RD 0xC600 +#define ED_USBMODE_WRNOP 0xC000 +#define ED_USBMODE_WR 0xC200 -#define ED_USBSTAT_ACT 0x0200 -#define ED_USBSTAT_RXF 0x0400 -#define ED_USBSTAT_TXE 0x0800 -#define ED_USBSTAT_POWER 0x1000 -#define ED_USBSTAT_BUSY 0x2000 +#define ED_USBSTAT_ACT 0x0200 +#define ED_USBSTAT_RXF 0x0400 +#define ED_USBSTAT_TXE 0x0800 +#define ED_USBSTAT_POWER 0x1000 +#define ED_USBSTAT_BUSY 0x2000 -#define ED_REGKEY 0xAA55 +#define ED_REGKEY 0xAA55 -#define ED25_VERSION 0xED640007 -#define ED3_VERSION 0xED640008 -#define ED7_VERSION 0xED640013 +#define ED25_VERSION 0xED640007 +#define ED3_VERSION 0xED640008 +#define ED7_VERSION 0xED640013 /********************************* - SummerCart64 macros + SC64 macros *********************************/ -#define SC64_SDRAM_BASE (0x10000000) +#define SC64_WRITE_TIMEOUT 1000 -#define SC64_REGS_BASE (0x1FFF0000) -#define SC64_REG_CFG_SR_CMD (SC64_REGS_BASE + 0x00) -#define SC64_REG_CFG_DATA_0 (SC64_REGS_BASE + 0x04) -#define SC64_REG_CFG_DATA_1 (SC64_REGS_BASE + 0x08) -#define SC64_REG_CFG_VERSION (SC64_REGS_BASE + 0x0C) +#define SC64_BASE 0x10000000 +#define SC64_REGS_BASE 0x1FFF0000 -#define SC64_CFG_SR_CMD_ERROR (1 << 28) -#define SC64_CFG_SR_CPU_BUSY (1 << 30) +#define SC64_REG_SR_CMD (SC64_REGS_BASE + 0x00) +#define SC64_REG_DATA_0 (SC64_REGS_BASE + 0x04) +#define SC64_REG_DATA_1 (SC64_REGS_BASE + 0x08) +#define SC64_REG_IDENTIFIER (SC64_REGS_BASE + 0x0C) +#define SC64_REG_KEY (SC64_REGS_BASE + 0x10) -#define SC64_VERSION (0x53437632) +#define SC64_SR_CMD_ERROR (1 << 30) +#define SC64_SR_CMD_BUSY (1 << 31) -#define SC64_CMD_CFG_UPDATE ('C') -#define SC64_CMD_DEBUG_TX_READY ('S') -#define SC64_CMD_DEBUG_TX_DATA ('D') -#define SC64_CMD_DEBUG_RX_READY ('A') -#define SC64_CMD_DEBUG_RX_BUSY ('F') -#define SC64_CMD_DEBUG_RX_DATA ('E') -#define SC64_CMD_DEBUG_RESET ('B') +#define SC64_V2_IDENTIFIER 0x53437632 -#define SC64_CFG_ID_SDRAM_WRITABLE (2) +#define SC64_KEY_RESET 0x00000000 +#define SC64_KEY_UNLOCK_1 0x5F554E4C +#define SC64_KEY_UNLOCK_2 0x4F434B5F -#define SC64_ARGS(args, a0, a1) {args[0] = (a0); args[1] = (a1);} +#define SC64_CMD_CONFIG_SET 'C' +#define SC64_CMD_USB_WRITE_STATUS 'U' +#define SC64_CMD_USB_WRITE 'M' +#define SC64_CMD_USB_READ_STATUS 'u' +#define SC64_CMD_USB_READ 'm' + +#define SC64_CFG_ROM_WRITE_ENABLE 1 + +#define SC64_USB_WRITE_STATUS_BUSY (1 << 31) +#define SC64_USB_READ_STATUS_BUSY (1 << 31) /********************************* @@ -232,19 +212,19 @@ https://github.com/buu342/N64-UNFLoader Function Prototypes *********************************/ -static void usb_findcart(); +static void usb_findcart(void); + static void usb_64drive_write(int datatype, const void* data, int size); -static u32 usb_64drive_poll(); -static void usb_64drive_read(); -static void usb_everdrive_readreg(u32 reg, u32* result); +static u32 usb_64drive_poll(void); +static void usb_64drive_read(void); + static void usb_everdrive_write(int datatype, const void* data, int size); -static u32 usb_everdrive_poll(); -static void usb_everdrive_read(); -static void usb_everdrive_writereg(u64 reg, u32 value); +static u32 usb_everdrive_poll(void); +static void usb_everdrive_read(void); + static void usb_sc64_write(int datatype, const void* data, int size); -static u32 usb_sc64_poll(); -static void usb_sc64_read(); -static u32 usb_sc64_perform_cmd(u8 cmd, u32 *args); +static u32 usb_sc64_poll(void); +static void usb_sc64_read(void); /********************************* @@ -258,14 +238,16 @@ void (*funcPointer_read)(); // USB globals static s8 usb_cart = CART_NONE; -static u8 __attribute__((aligned(16))) usb_buffer[BUFFER_SIZE]; -int usb_datatype = 0; -int usb_datasize = 0; -int usb_dataleft = 0; -int usb_readblock = -1; +static u8 usb_buffer_align[BUFFER_SIZE+16]; // IDO doesn't support GCC's __attribute__((aligned(x))), so this is a workaround +static u8* usb_buffer; +static char usb_didtimeout = FALSE; +static int usb_datatype = 0; +static int usb_datasize = 0; +static int usb_dataleft = 0; +static int usb_readblock = -1; #ifndef LIBDRAGON -// Message globals + // Message globals #if !USE_OSRAW OSMesg dmaMessageBuf; OSIoMesg dmaIOMessageBuf; @@ -285,6 +267,154 @@ int usb_readblock = -1; #endif +/********************************* + I/O Wrapper Functions +*********************************/ + +/*============================== + usb_io_read + Reads a 32-bit value from a + given address using the PI. + @param The address to read from + @return The 4 byte value that was read +==============================*/ + +static inline u32 usb_io_read(u32 pi_address) +{ + #ifndef LIBDRAGON + u32 value; + #if USE_OSRAW + osPiRawReadIo(pi_address, &value); + #else + osPiReadIo(pi_address, &value); + #endif + return value; + #else + return io_read(pi_address); + #endif +} + + +/*============================== + usb_io_write + Writes a 32-bit value to a + given address using the PI. + @param The address to write to + @param The 4 byte value to write +==============================*/ + +static inline void usb_io_write(u32 pi_address, u32 value) +{ + #ifndef LIBDRAGON + #if USE_OSRAW + osPiRawWriteIo(pi_address, value); + #else + osPiWriteIo(pi_address, value); + #endif + #else + io_write(pi_address, value); + #endif +} + + +/*============================== + usb_dma_read + Reads arbitrarily sized data from a + given address using DMA. + @param The buffer to read into + @param The address to read from + @param The size of the data to read +==============================*/ + +static inline void usb_dma_read(void *ram_address, u32 pi_address, size_t size) +{ + #ifndef LIBDRAGON + osWritebackDCache(ram_address, size); + osInvalDCache(ram_address, size); + #if USE_OSRAW + osPiRawStartDma(OS_READ, pi_address, ram_address, size); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, pi_address, ram_address, size, &dmaMessageQ); + osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif + #else + data_cache_hit_writeback_invalidate(ram_address, size); + dma_read(ram_address, pi_address, size); + #endif +} + + +/*============================== + usb_dma_write + writes arbitrarily sized data to a + given address using DMA. + @param The buffer to read from + @param The address to write to + @param The size of the data to write +==============================*/ + +static inline void usb_dma_write(void *ram_address, u32 pi_address, size_t size) +{ + #ifndef LIBDRAGON + osWritebackDCache(ram_address, size); + #if USE_OSRAW + osPiRawStartDma(OS_WRITE, pi_address, ram_address, size); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_WRITE, pi_address, ram_address, size, &dmaMessageQ); + osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif + #else + data_cache_hit_writeback(ram_address, size); + dma_write(ram_address, pi_address, size); + #endif +} + + +/********************************* + Timeout helpers +*********************************/ + +/*============================== + usb_timeout_start + Returns current value of COUNT coprocessor 0 register + @return C0_COUNT value +==============================*/ + +static u32 usb_timeout_start(void) +{ +#ifndef LIBDRAGON + return osGetCount(); +#else + return get_ticks(); +#endif +} + + +/*============================== + usb_timeout_check + Checks if timeout occurred + @param Starting value obtained from usb_timeout_start + @param Timeout duration specified in milliseconds + @return TRUE if timeout occurred, otherwise FALSE +==============================*/ + +static char usb_timeout_check(u32 start_ticks, u32 duration) +{ +#ifndef LIBDRAGON + u64 current_ticks = (u64)osGetCount(); + u64 timeout_ticks = OS_USEC_TO_CYCLES((u64)duration * 1000); +#else + u64 current_ticks = (u64)get_ticks(); + u64 timeout_ticks = (u64)TICKS_FROM_MS(duration); +#endif + if (current_ticks < start_ticks) + current_ticks += 0x100000000ULL; + if (current_ticks >= (start_ticks + timeout_ticks)) + return TRUE; + return FALSE; +} + + /********************************* USB functions *********************************/ @@ -295,9 +425,10 @@ int usb_readblock = -1; @returns 1 if the USB initialization was successful, 0 if not ==============================*/ -char usb_initialize() +char usb_initialize(void) { // Initialize the debug related globals + usb_buffer = (u8*)OS_DCACHE_ROUNDUP_ADDR(usb_buffer_align); memset(usb_buffer, 0, BUFFER_SIZE); #ifndef LIBDRAGON @@ -331,75 +462,84 @@ char usb_initialize() default: return 0; } + + // Send a heartbeat + usb_sendheartbeat(); return 1; } /*============================== usb_findcart - Checks if the game is running on a 64Drive, EverDrive or a SummerCart64. + Checks if the game is running on a 64Drive, EverDrive or a SC64. ==============================*/ -static void usb_findcart() +static void usb_findcart(void) { - u32 buff __attribute__((aligned(8))); + u32 buff; + + // Before we do anything, check that we are using an emulator + #if CHECK_EMULATOR + // Check the RDP clock register. + // Always zero on emulators + if (IO_READ(0xA4100010) == 0) // DPC_CLOCK_REG in Libultra + return; + + // Fallback, harder emulator check. + // The VI has an interesting quirk where its values are mirrored every 0x40 bytes + // It's unlikely that emulators handle this, so we'll write to the VI_TEST_ADDR register and readback 0x40 bytes from its address + // If they don't match, we probably have an emulator + buff = (*(u32*)0xA4400038); + (*(u32*)0xA4400038) = 0x6ABCDEF9; + if ((*(u32*)0xA4400038) != (*(u32*)0xA4400078)) + { + (*(u32*)0xA4400038) = buff; + return; + } + (*(u32*)0xA4400038) = buff; + #endif // Read the cartridge and check if we have a 64Drive. - #ifdef LIBDRAGON - buff = io_read(D64_CIBASE_ADDRESS + D64_REGISTER_MAGIC); - #else - #if USE_OSRAW - osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_MAGIC, &buff); - #else - osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_MAGIC, &buff); - #endif - #endif - if (buff == D64_MAGIC) + if (usb_io_read(D64_REG_MAGIC) == D64_MAGIC) { usb_cart = CART_64DRIVE; return; } - // Read the cartridge and check if we have a SummerCart64. - #ifdef LIBDRAGON - buff = io_read(SC64_REG_CFG_VERSION); - #else - #if USE_OSRAW - osPiRawReadIo(SC64_REG_CFG_VERSION, &buff); - #else - osPiReadIo(SC64_REG_CFG_VERSION, &buff); - #endif - #endif - if (buff == SC64_VERSION) - { - // Reset USB and flush FIFOs - u32 args[2]; - SC64_ARGS(args, 0, 0); - usb_sc64_perform_cmd(SC64_CMD_DEBUG_RESET, args); - usb_cart = CART_SC64; - return; - } - - // Since we didn't find a 64Drive or SummerCart64, let's assume we have an EverDrive + // Since we didn't find a 64Drive let's assume we have an EverDrive // Write the key to unlock the registers, then read the version register - usb_everdrive_writereg(ED_REG_KEY, ED_REGKEY); - usb_everdrive_readreg(ED_REG_VERSION, &buff); - + usb_io_write(ED_REG_KEY, ED_REGKEY); + buff = usb_io_read(ED_REG_VERSION); + // EverDrive 2.5 not compatible if (buff == ED25_VERSION) return; - + // Check if we have an EverDrive if (buff == ED7_VERSION || buff == ED3_VERSION) { // Set the USB mode - usb_everdrive_writereg(ED_REG_SYSCFG, 0); - usb_everdrive_writereg(ED_REG_USBCFG, ED_USBMODE_RDNOP); + usb_io_write(ED_REG_SYSCFG, 0); + usb_io_write(ED_REG_USBCFG, ED_USBMODE_RDNOP); // Set the cart to EverDrive usb_cart = CART_EVERDRIVE; return; } + + // Since we didn't find an EverDrive either let's assume we have a SC64 + // Write the key sequence to unlock the registers, then read the identifier register + usb_io_write(SC64_REG_KEY, SC64_KEY_RESET); + usb_io_write(SC64_REG_KEY, SC64_KEY_UNLOCK_1); + usb_io_write(SC64_REG_KEY, SC64_KEY_UNLOCK_2); + + // Check if we have a SC64 + if (usb_io_read(SC64_REG_IDENTIFIER) == SC64_V2_IDENTIFIER) + { + // Set the cart to SC64 + usb_cart = CART_SC64; + return; + } } @@ -409,7 +549,7 @@ static void usb_findcart() @return The CART macro that corresponds to the identified flashcart ==============================*/ -char usb_getcart() +char usb_getcart(void) { return usb_cart; } @@ -446,7 +586,7 @@ void usb_write(int datatype, const void* data, int size) @return The data header, or 0 ==============================*/ -unsigned long usb_poll() +u32 usb_poll(void) { // If no debug cart exists, stop if (usb_cart == CART_NONE) @@ -559,7 +699,7 @@ void usb_rewind(int nbytes) Purges the incoming USB data ==============================*/ -void usb_purge() +void usb_purge(void) { usb_dataleft = 0; usb_datatype = 0; @@ -568,141 +708,205 @@ void usb_purge() } +/*============================== + usb_timedout + Checks if the USB timed out recently + @return 1 if the USB timed out, 0 if not +==============================*/ + +char usb_timedout() +{ + return usb_didtimeout; +} + + +/*============================== + usb_sendheartbeat + Sends a heartbeat packet to the PC + This is done once automatically at initialization, + but can be called manually to ensure that the + host side tool is aware of the current USB protocol + version. +==============================*/ + +void usb_sendheartbeat() +{ + u8 buffer[4]; + + // First two bytes describe the USB library protocol version + buffer[0] = (u8)(((USBPROTOCOL_VERSION)>>8)&0xFF); + buffer[1] = (u8)(((USBPROTOCOL_VERSION))&0xFF); + + // Next two bytes describe the heartbeat packet version + buffer[2] = (u8)(((HEARTBEAT_VERSION)>>8)&0xFF); + buffer[3] = (u8)(((HEARTBEAT_VERSION))&0xFF); + + // Send through USB + usb_write(DATATYPE_HEARTBEAT, buffer, sizeof(buffer)/sizeof(buffer[0])); +} + + /********************************* 64Drive functions *********************************/ /*============================== usb_64drive_wait - Wait until the 64Drive is ready - @return 0 if success or -1 if failure + Wait until the 64Drive CI is ready + @return FALSE if success or TRUE if failure ==============================*/ -static s8 usb_64drive_wait() +#ifndef LIBDRAGON +static char usb_64drive_wait(void) +#else +char usb_64drive_wait(void) +#endif { - u32 ret __attribute__((aligned(8))); - u32 timeout = 0; // I wanted to use osGetTime() but that requires the VI manager + u32 timeout; // Wait until the cartridge interface is ready + timeout = usb_timeout_start(); do { - #ifdef LIBDRAGON - ret = io_read(D64_CIBASE_ADDRESS + D64_REGISTER_STATUS); - #else - #if USE_OSRAW - osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_STATUS, &ret); - #else - osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_STATUS, &ret); - #endif - #endif - // Took too long, abort - if((timeout++) > 10000) - return -1; + if (usb_timeout_check(timeout, D64_COMMAND_TIMEOUT)) + { + usb_didtimeout = TRUE; + return TRUE; + } } - while((ret >> 8) & D64_CI_BUSY); - (void) timeout; // Needed to stop unused variable warning + while(usb_io_read(D64_REG_STATUS) & D64_CI_BUSY); // Success - return 0; + usb_didtimeout = FALSE; + return FALSE; } /*============================== - usb_64drive_setwritable - Set the write mode on the 64Drive + usb_64drive_set_writable + Set the CARTROM write mode on the 64Drive @param A boolean with whether to enable or disable ==============================*/ -static void usb_64drive_setwritable(u8 enable) +static void usb_64drive_set_writable(u32 enable) { + // Wait until CI is not busy usb_64drive_wait(); - #ifdef LIBDRAGON - io_write(D64_CIBASE_ADDRESS + D64_REGISTER_COMMAND, enable ? D64_ENABLE_ROMWR : D64_DISABLE_ROMWR); - #else - #if USE_OSRAW - osPiRawWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_COMMAND, enable ? D64_ENABLE_ROMWR : D64_DISABLE_ROMWR); - #else - osPiWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_COMMAND, enable ? D64_ENABLE_ROMWR : D64_DISABLE_ROMWR); - #endif - #endif + + // Send enable/disable CARTROM writes command + usb_io_write(D64_REG_COMMAND, enable ? D64_CI_ENABLE_ROMWR : D64_CI_DISABLE_ROMWR); + + // Wait until operation is finished usb_64drive_wait(); } /*============================== - usb_64drive_waitidle - Waits for the 64Drive's USB to be idle + usb_64drive_cui_write + Writes data from buffer in the 64drive through USB + @param Data type + @param Offset in CARTROM memory space + @param Transfer size ==============================*/ -static int usb_64drive_waitidle() +static void usb_64drive_cui_write(u8 datatype, u32 offset, u32 size) { - u32 status __attribute__((aligned(8))); - u32 timeout = 0; + u32 timeout; + + // Start USB write + usb_io_write(D64_REG_USBP0R0, offset >> 1); + usb_io_write(D64_REG_USBP1R1, USBHEADER_CREATE(datatype, ALIGN(size, 4))); // Align size to 32-bits due to bugs in the firmware + usb_io_write(D64_REG_USBCOMSTAT, D64_CUI_WRITE); + + // Spin until the write buffer is free + timeout = usb_timeout_start(); do { - #ifdef LIBDRAGON - status = io_read(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT); - #else - #if USE_OSRAW - osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); - #else - osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); - #endif - #endif - status = (status >> 4) & D64_USB_BUSY; - if (timeout++ > 128) - return 0; + // Took too long, abort + if (usb_timeout_check(timeout, D64_WRITE_TIMEOUT)) + { + usb_didtimeout = TRUE; + return; + } } - while(status != D64_USB_IDLE); - return 1; + while((usb_io_read(D64_REG_USBCOMSTAT) & D64_CUI_WRITE_MASK) != D64_CUI_WRITE_IDLE); } /*============================== - usb_64drive_armstatus - Checks if the 64Drive is armed - @return The arming status + usb_64drive_cui_poll + Checks if there is data waiting to be read from USB FIFO + @return TRUE if data is waiting, FALSE if otherwise ==============================*/ -static u32 usb_64drive_armstatus() +static char usb_64drive_cui_poll(void) { - u32 status __attribute__((aligned(8))); - #ifdef LIBDRAGON - status = io_read(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT); - #else - #if USE_OSRAW - osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); - #else - osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); - #endif - #endif - return status & 0xf; + // Check if we have data waiting in buffer + if ((usb_io_read(D64_REG_USBCOMSTAT) & D64_CUI_ARM_MASK) == D64_CUI_ARM_UNARMED_DATA) + return TRUE; + return FALSE; } /*============================== - usb_64drive_waitdisarmed - Waits for the 64Drive's USB to be disarmed + usb_64drive_cui_read + Reads data from USB FIFO to buffer in the 64drive + @param Offset in CARTROM memory space + @return USB header (datatype + size) ==============================*/ -static void usb_64drive_waitdisarmed() +static u32 usb_64drive_cui_read(u32 offset) { - u32 status __attribute__((aligned(8))); - do + u32 header; + u32 left; + u32 datatype; + u32 size; + + // Arm USB FIFO with 8 byte sized transfer + usb_io_write(D64_REG_USBP0R0, offset >> 1); + usb_io_write(D64_REG_USBP1R1, 8); + usb_io_write(D64_REG_USBCOMSTAT, D64_CUI_ARM); + + // Wait until data is received + while ((usb_io_read(D64_REG_USBCOMSTAT) & D64_CUI_ARM_MASK) != D64_CUI_ARM_UNARMED_DATA) + ; + + // Get datatype and bytes remaining + header = usb_io_read(D64_REG_USBP0R0); + left = usb_io_read(D64_REG_USBP1R1) & 0x00FFFFFF; + datatype = header & 0xFF000000; + size = header & 0x00FFFFFF; + + // Determine if we need to read more data + if (left > 0) { - #ifdef LIBDRAGON - status = io_read(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT); - #else - #if USE_OSRAW - osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); - #else - osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); - #endif - #endif - status &= 0x0F; + // Arm USB FIFO with known transfer size + usb_io_write(D64_REG_USBP0R0, (offset + 8) >> 1); + usb_io_write(D64_REG_USBP1R1, left); + usb_io_write(D64_REG_USBCOMSTAT, D64_CUI_ARM); + + // Wait until data is received + while ((usb_io_read(D64_REG_USBCOMSTAT) & D64_CUI_ARM_MASK) != D64_CUI_ARM_UNARMED_DATA) + ; + + // Calculate total transfer length + size += left; } - while (status != D64_USB_IDLEUNARMED); + + // Disarm USB FIFO + usb_io_write(D64_REG_USBCOMSTAT, D64_CUI_DISARM); + + // 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; + + // Return data header (datatype and size) + return (datatype | size); } @@ -717,141 +921,43 @@ static void usb_64drive_waitdisarmed() static void usb_64drive_write(int datatype, const void* data, int size) { - int left = size; - int read = 0; + u32 left = size; + u32 pi_address = D64_BASE + DEBUG_ADDRESS; - // Spin until the write buffer is free and then set the cartridge to write mode - if (!usb_64drive_waitidle()) + // Return if previous transfer timed out + if ((usb_io_read(D64_REG_USBCOMSTAT) & D64_CUI_WRITE_MASK) == D64_CUI_WRITE_BUSY) + { + usb_didtimeout = TRUE; return; - usb_64drive_setwritable(TRUE); + } + + // Set the cartridge to write mode + usb_64drive_set_writable(TRUE); // Write data to SDRAM until we've finished while (left > 0) { - int block = left; - if (block > BUFFER_SIZE) - block = BUFFER_SIZE; + // Calculate transfer size + u32 block = MIN(left, BUFFER_SIZE); - // Copy the data to the global buffer - memcpy(usb_buffer, (void*)((char*)data+read), block); + // Copy data to PI DMA aligned buffer + memcpy(usb_buffer, data, block); - // If the data was not 32-bit aligned, pad the buffer - if (block < BUFFER_SIZE && size%4 != 0) - { - u32 i; - u32 size_new = (size & ~3)+4; - block += size_new-size; - for (i=size; i> 1); - io_write(D64_CIBASE_ADDRESS + D64_REGISTER_USBP1R1, (size & 0xFFFFFF) | (datatype << 24)); - io_write(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_COMMAND_WRITE); - #else - #if USE_OSRAW - osPiRawWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBP0R0, (DEBUG_ADDRESS) >> 1); - osPiRawWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBP1R1, (size & 0xFFFFFF) | (datatype << 24)); - osPiRawWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_COMMAND_WRITE); - #else - osPiWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBP0R0, (DEBUG_ADDRESS) >> 1); - osPiWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBP1R1, (size & 0xFFFFFF) | (datatype << 24)); - osPiWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_COMMAND_WRITE); - #endif - #endif - - // Spin until the write buffer is free and then disable write mode - usb_64drive_waitidle(); - usb_64drive_setwritable(FALSE); -} - - -/*============================== - usb_64drive_arm - Arms the 64Drive's USB - @param The ROM offset to arm - @param The size of the data to transfer -==============================*/ - -static void usb_64drive_arm(u32 offset, u32 size) -{ - u32 ret __attribute__((aligned(8))); - ret = usb_64drive_armstatus(); - - if (ret != D64_USB_ARMING && ret != D64_USB_ARMED) - { - usb_64drive_waitidle(); - - // Arm the 64Drive, using the ROM space as a buffer - #ifdef LIBDRAGON - io_write(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_USB_ARM); - io_write(D64_CIBASE_ADDRESS + D64_REGISTER_USBP0R0, (offset >> 1)); - io_write(D64_CIBASE_ADDRESS + D64_REGISTER_USBP1R1, (size & 0xFFFFFF)); - #else - #if USE_OSRAW - osPiRawWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_USB_ARM); - osPiRawWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBP0R0, (offset >> 1)); - osPiRawWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBP1R1, (size & 0xFFFFFF)); - #else - osPiWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_USB_ARM); - osPiWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBP0R0, (offset >> 1)); - osPiWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBP1R1, (size & 0xFFFFFF)); - #endif - #endif - } -} - - -/*============================== - usb_64drive_disarm - Disarms the 64Drive's USB -==============================*/ - -static void usb_64drive_disarm() -{ - // Disarm the USB - #ifdef LIBDRAGON - io_write(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_USB_DISARM); - #else - #if USE_OSRAW - osPiRawWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_USB_DISARM); - #else - osPiWriteIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, D64_USB_DISARM); - #endif - #endif - usb_64drive_waitdisarmed(); + usb_64drive_cui_write(datatype, DEBUG_ADDRESS, size); + usb_didtimeout = FALSE; } @@ -862,50 +968,27 @@ static void usb_64drive_disarm() @return The data header, or 0 ==============================*/ -static u32 usb_64drive_poll() +static u32 usb_64drive_poll(void) { - int i; - u32 ret __attribute__((aligned(8))); - - // Arm the USB buffer - usb_64drive_waitidle(); - usb_64drive_setwritable(TRUE); - usb_64drive_arm(DEBUG_ADDRESS, DEBUG_ADDRESS_SIZE); - - // Burn some time to see if any USB data comes in - for (i=0; i 0) { - // Wait for the USB to be ready and then read data - usb_everdrive_usbbusy(); - usb_everdrive_readdata(usb_buffer, ED_GET_REGADD(ED_REG_USBDAT), BUFFER_SIZE); // TODO: Replace with usb_everdrive_readusb? + u32 bytes_do = BUFFER_SIZE; + if (len < BUFFER_SIZE) + bytes_do = len; - // Tell the FPGA we can receive more data - if (len != 0) - usb_everdrive_writereg(ED_REG_USBCFG, ED_USBMODE_RD | BUFFER_SIZE); + // 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_everdrive_writedata(usb_buffer, ED_BASE + DEBUG_ADDRESS + offset, BUFFER_SIZE); - offset += BUFFER_SIZE; + usb_dma_write(usb_buffer, ED_BASE + DEBUG_ADDRESS + offset, bytes_do); + offset += bytes_do; + len -= bytes_do; } // Read the CMP Signal - usb_everdrive_usbbusy(); - usb_everdrive_readusb(buff, 16); + if (usb_everdrive_usbbusy()) + return 0; + usb_everdrive_readusb(buff, 4); if (buff[0] != 'C' || buff[1] != 'M' || buff[2] != 'P' || buff[3] != 'H') { // Something went wrong with the data @@ -1283,114 +1238,89 @@ static u32 usb_everdrive_poll() Reads bytes from the EverDrive ROM into the global buffer with the block offset ==============================*/ -static void usb_everdrive_read() +static void usb_everdrive_read(void) { // Set up DMA transfer between RDRAM and the PI - #ifdef LIBDRAGON - data_cache_hit_writeback_invalidate(usb_buffer, BUFFER_SIZE); - while (dma_busy()); - *(vu32*)0xA4600010 = 3; - dma_read(usb_buffer, ED_BASE + DEBUG_ADDRESS + usb_readblock, BUFFER_SIZE); - data_cache_hit_writeback_invalidate(usb_buffer, BUFFER_SIZE); - #else - osWritebackDCacheAll(); - #if USE_OSRAW - osPiRawStartDma(OS_READ, - ED_BASE + DEBUG_ADDRESS + usb_readblock, usb_buffer, - BUFFER_SIZE); - #else - osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, - ED_BASE + DEBUG_ADDRESS + usb_readblock, usb_buffer, - BUFFER_SIZE, &dmaMessageQ); - (void)osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); - #endif - #endif + usb_dma_read(usb_buffer, ED_BASE + DEBUG_ADDRESS + usb_readblock, BUFFER_SIZE); } /********************************* - SummerCart64 functions + SC64 functions *********************************/ /*============================== - usb_sc64_wait_cpu_busy - Wait until command has been processed - @returns If last command resulted in error + usb_sc64_execute_cmd + Executes specified command in SC64 controller + @param Command ID to execute + @param 2 element array of 32 bit arguments to pass with command, use NULL when argument values are not needed + @param 2 element array of 32 bit values to read command result, use NULL when result values are not needed + @return TRUE if there was error during command execution, otherwise FALSE ==============================*/ -static u32 usb_sc64_wait_cpu_busy(void) +#ifndef LIBDRAGON +static char usb_sc64_execute_cmd(u8 cmd, u32 *args, u32 *result) +#else +char usb_sc64_execute_cmd(u8 cmd, u32 *args, u32 *result) +#endif { u32 sr; + // Write arguments if provided + if (args != NULL) + { + usb_io_write(SC64_REG_DATA_0, args[0]); + usb_io_write(SC64_REG_DATA_1, args[1]); + } + + // Start execution + usb_io_write(SC64_REG_SR_CMD, cmd); + + // Wait for completion do { - #ifdef LIBDRAGON - sr = io_read(SC64_REG_CFG_SR_CMD); - #else - #if USE_OSRAW - osPiRawReadIo(SC64_REG_CFG_SR_CMD, &sr); - #else - osPiReadIo(SC64_REG_CFG_SR_CMD, &sr); - #endif - #endif - } while (sr & SC64_CFG_SR_CPU_BUSY); + sr = usb_io_read(SC64_REG_SR_CMD); + } + while (sr & SC64_SR_CMD_BUSY); - return sr & SC64_CFG_SR_CMD_ERROR; + // Read result if provided + if (result != NULL) + { + result[0] = usb_io_read(SC64_REG_DATA_0); + result[1] = usb_io_read(SC64_REG_DATA_1); + } + + // Return error status + if (sr & SC64_SR_CMD_ERROR) + return TRUE; + return FALSE; } /*============================== - usb_sc64_perform_cmd - Issues command to SC64 and waits for completion - @param Command identifier - @param Pointer to 2 element array of arguments that will be overwritten by command result data - @returns If last command resulted in error + usb_sc64_set_writable + Enable ROM (SDRAM) writes in SC64 + @param A boolean with whether to enable or disable + @return Previous value of setting ==============================*/ -static u32 usb_sc64_perform_cmd(u8 cmd, u32 *args) +static u32 usb_sc64_set_writable(u32 enable) { - u32 error = 0; + u32 args[2]; + u32 result[2]; - error |= usb_sc64_wait_cpu_busy(); + args[0] = SC64_CFG_ROM_WRITE_ENABLE; + args[1] = enable; + if (usb_sc64_execute_cmd(SC64_CMD_CONFIG_SET, args, result)) + return 0; - #ifdef LIBDRAGON - io_write(SC64_REG_CFG_DATA_0, args[0]); - io_write(SC64_REG_CFG_DATA_1, args[1]); - io_write(SC64_REG_CFG_SR_CMD, (u32) cmd); - #else - #if USE_OSRAW - osPiRawWriteIo(SC64_REG_CFG_DATA_0, args[0]); - osPiRawWriteIo(SC64_REG_CFG_DATA_1, args[1]); - osPiRawWriteIo(SC64_REG_CFG_SR_CMD, (u32) cmd); - #else - osPiWriteIo(SC64_REG_CFG_DATA_0, args[0]); - osPiWriteIo(SC64_REG_CFG_DATA_1, args[1]); - osPiWriteIo(SC64_REG_CFG_SR_CMD, (u32) cmd); - #endif - #endif - - error |= usb_sc64_wait_cpu_busy(); - - #ifdef LIBDRAGON - args[0] = io_read(SC64_REG_CFG_DATA_0); - args[1] = io_read(SC64_REG_CFG_DATA_1); - #else - #if USE_OSRAW - osPiRawReadIo(SC64_REG_CFG_DATA_0, &args[0]); - osPiRawReadIo(SC64_REG_CFG_DATA_1, &args[1]); - #else - osPiReadIo(SC64_REG_CFG_DATA_0, &args[0]); - osPiReadIo(SC64_REG_CFG_DATA_1, &args[1]); - #endif - #endif - - return error; + return result[1]; } /*============================== usb_sc64_write - Sends data through USB from the SummerCart64 + Sends data through USB from the SC64 @param The DATATYPE that is being sent @param A buffer with the data to send @param The size of the data being sent @@ -1398,176 +1328,124 @@ static u32 usb_sc64_perform_cmd(u8 cmd, u32 *args) static void usb_sc64_write(int datatype, const void* data, int size) { - u8 dma[4] = {'D', 'M', 'A', '@'}; - u32 header = USBHEADER_CREATE(datatype, size); - u8 cmp[4] = {'C', 'M', 'P', 'H'}; - u8 wrote_cmp = FALSE; - - size_t block_size = MIN(BUFFER_SIZE, DEBUG_ADDRESS_SIZE); - size_t usb_block_max_size = DEBUG_ADDRESS_SIZE; - - u8* data_ptr = (u8*) data; - u32 sdram_address = SC64_SDRAM_BASE + DEBUG_ADDRESS; - - int offset; - int left; - u32 transfer_length; + u32 left = size; + u32 pi_address = SC64_BASE + DEBUG_ADDRESS; + u32 writable_restore; + u32 timeout; u32 args[2]; + u32 result[2]; - // Wait until previous data has been transferred - do + // Return if previous transfer timed out + usb_sc64_execute_cmd(SC64_CMD_USB_WRITE_STATUS, NULL, result); + if (result[0] & SC64_USB_WRITE_STATUS_BUSY) { - usb_sc64_perform_cmd(SC64_CMD_DEBUG_TX_READY, args); - } while (!args[0]); + usb_didtimeout = TRUE; + return; + } - // Enable SDRAM writes - SC64_ARGS(args, SC64_CFG_ID_SDRAM_WRITABLE, TRUE); - usb_sc64_perform_cmd(SC64_CMD_CFG_UPDATE, args); - - // Prepare transfer header - memcpy(usb_buffer, dma, sizeof(dma)); - memcpy(usb_buffer + sizeof(dma), &header, sizeof(header)); - - offset = sizeof(dma) + sizeof(header); - left = size; - transfer_length = 0; + // Enable SDRAM writes and get previous setting + writable_restore = usb_sc64_set_writable(TRUE); while (left > 0) { - // Calculate data copy length - size_t data_length = MIN(MIN(left, block_size - offset), usb_block_max_size - transfer_length); - u32 dma_length; + // Calculate transfer size + u32 block = MIN(left, BUFFER_SIZE); - // Fill buffer - memcpy(usb_buffer + offset, data_ptr, data_length); + // Copy data to PI DMA aligned buffer + memcpy(usb_buffer, data, block); - // Write CMPH at the end of data - if (!wrote_cmp && (left - data_length) <= 0) - { - wrote_cmp = TRUE; - data_ptr = cmp; - offset = MIN(offset + data_length, block_size); - left = sizeof(cmp); - continue; - } + // Copy block of data from RDRAM to SDRAM + usb_dma_write(usb_buffer, pi_address, ALIGN(block, 2)); - // Calculate RDRAM -> PI transfer length - dma_length = ALIGN(offset + data_length, 4); - - // Write data to buffer in SDRAM - #ifdef LIBDRAGON - data_cache_hit_writeback(usb_buffer, dma_length); - dma_write(usb_buffer, sdram_address, dma_length); - #else - osWritebackDCache(usb_buffer, dma_length); - #if USE_OSRAW - osPiRawStartDma(OS_WRITE, sdram_address, usb_buffer, dma_length); - #else - osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_WRITE, sdram_address, usb_buffer, dma_length, &dmaMessageQ); - osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); - #endif - #endif - - // Update pointers and remaining data tracking - data_ptr += data_length; - sdram_address += dma_length; - offset = 0; - left -= data_length; - transfer_length = sdram_address - (SC64_SDRAM_BASE + DEBUG_ADDRESS); - - // Continue filling SDRAM buffer if total length is lower than maximum transfer length or if there's no more data - if ((transfer_length < usb_block_max_size) && (left > 0)) - { - continue; - } - - // Disable SDRAM writes if there's no more data to write - if (left <= 0) - { - SC64_ARGS(args, SC64_CFG_ID_SDRAM_WRITABLE, FALSE); - usb_sc64_perform_cmd(SC64_CMD_CFG_UPDATE, args); - } - - // Start DMA transfer from SDRAM to USB chip - SC64_ARGS(args, DEBUG_ADDRESS, transfer_length); - usb_sc64_perform_cmd(SC64_CMD_DEBUG_TX_DATA, args); - - // Wait for transfer to complete if there's more data to send - if (left > 0) - { - do - { - usb_sc64_perform_cmd(SC64_CMD_DEBUG_TX_READY, args); - } while (!args[0]); - } - - // Reset SDRAM address and transfer length - sdram_address = SC64_SDRAM_BASE + DEBUG_ADDRESS; - transfer_length = 0; + // Update pointers and variables + data += block; + left -= block; + pi_address += block; } + + // Restore previous SDRAM writable setting + usb_sc64_set_writable(writable_restore); + + // Start sending data from buffer in SDRAM + args[0] = SC64_BASE + DEBUG_ADDRESS; + args[1] = USBHEADER_CREATE(datatype, size); + if (usb_sc64_execute_cmd(SC64_CMD_USB_WRITE, args, NULL)) + { + usb_didtimeout = TRUE; + return; // Return if USB write was unsuccessful + } + + // Wait for transfer to end + timeout = usb_timeout_start(); + do + { + // Took too long, abort + if (usb_timeout_check(timeout, SC64_WRITE_TIMEOUT)) + { + usb_didtimeout = TRUE; + return; + } + usb_sc64_execute_cmd(SC64_CMD_USB_WRITE_STATUS, NULL, result); + } + while (result[0] & SC64_USB_WRITE_STATUS_BUSY); + usb_didtimeout = FALSE; } /*============================== usb_sc64_poll - Returns the header of data being received via USB on the SummerCart64 + Returns the header of data being received via USB on the SC64 The first byte contains the data type, the next 3 the number of bytes left to read @return The data header, or 0 ==============================*/ static u32 usb_sc64_poll(void) { + u8 datatype; + u32 size; u32 args[2]; + u32 result[2]; - // Check if there's any data waiting to be serviced - usb_sc64_perform_cmd(SC64_CMD_DEBUG_RX_READY, args); - if (args[0] == 0 && args[1] == 0) { + // Get read status and extract packet info + usb_sc64_execute_cmd(SC64_CMD_USB_READ_STATUS, NULL, result); + datatype = result[0] & 0xFF; + size = result[1] & 0xFFFFFF; + + // Return 0 if there's no data + if (size == 0) return 0; - } // Fill USB read data variables - usb_datatype = USBHEADER_GETTYPE(args[0]); - usb_dataleft = USBHEADER_GETSIZE(args[0]); + usb_datatype = datatype; + usb_dataleft = size; usb_datasize = usb_dataleft; usb_readblock = -1; - // Load data to debug buffer in SDRAM - SC64_ARGS(args, SC64_SDRAM_BASE + DEBUG_ADDRESS, args[1]); - usb_sc64_perform_cmd(SC64_CMD_DEBUG_RX_DATA, args); + // Start receiving data to buffer in SDRAM + args[0] = SC64_BASE + DEBUG_ADDRESS; + args[1] = size; + if (usb_sc64_execute_cmd(SC64_CMD_USB_READ, args, NULL)) + return 0; // Return 0 if USB read was unsuccessful - // Wait until all data has been transferred + // Wait for completion do { - usb_sc64_perform_cmd(SC64_CMD_DEBUG_RX_BUSY, args); - } while (args[0]); + usb_sc64_execute_cmd(SC64_CMD_USB_READ_STATUS, NULL, result); + } + while (result[0] & SC64_USB_READ_STATUS_BUSY); - return USBHEADER_CREATE(usb_datatype, usb_dataleft); + // Return USB header + return USBHEADER_CREATE(datatype, size); } /*============================== usb_sc64_read - Reads bytes from the SummerCart64 ROM into the global buffer with the block offset + Reads bytes from the SC64 SDRAM into the global buffer with the block offset ==============================*/ static void usb_sc64_read(void) { - // Calculate address in SDRAM - u32 sdram_address = SC64_SDRAM_BASE + DEBUG_ADDRESS + usb_readblock; - // Set up DMA transfer between RDRAM and the PI - #ifdef LIBDRAGON - dma_read(usb_buffer, sdram_address, BUFFER_SIZE); - data_cache_hit_invalidate(usb_buffer, BUFFER_SIZE); - #else - #if USE_OSRAW - osPiRawStartDma(OS_READ, sdram_address, usb_buffer, BUFFER_SIZE); - #else - osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, sdram_address, usb_buffer, BUFFER_SIZE, &dmaMessageQ); - osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); - #endif - - // Invalidate cache - osInvalDCache(usb_buffer, BUFFER_SIZE); - #endif + usb_dma_read(usb_buffer, SC64_BASE + DEBUG_ADDRESS + usb_readblock, BUFFER_SIZE); } diff --git a/src/usb/usb.h b/src/usb/usb.h index 813b1b28b..215577468 100644 --- a/src/usb/usb.h +++ b/src/usb/usb.h @@ -1,43 +1,42 @@ #ifndef UNFL_USB_H #define UNFL_USB_H - + +#include "types.h" + /********************************* DataType macros *********************************/ - + // UNCOMMENT THE #DEFINE IF USING LIBDRAGON - //#define 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 - - extern int usb_datatype; - extern int usb_datasize; - extern int usb_dataleft; - extern int usb_readblock; + #define DATATYPE_HEARTBEAT 0x05 + - /********************************* 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)) - + /********************************* USB Functions @@ -48,19 +47,19 @@ Initializes the USB buffers and pointers @return 1 if the USB initialization was successful, 0 if not ==============================*/ - + extern char usb_initialize(); - - + + /*============================== usb_getcart Returns which flashcart is currently connected @return The CART macro that corresponds to the identified flashcart ==============================*/ - + extern char usb_getcart(); - - + + /*============================== usb_write Writes data to the USB. @@ -69,53 +68,74 @@ @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 unsigned long usb_poll(); - - + + 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(); -#endif \ No newline at end of file + + /*============================== + usb_timedout + Checks if the USB timed out recently + @return 1 if the USB timed out, 0 if not + ==============================*/ + + extern char usb_timedout(); + + + /*============================== + usb_sendheartbeat + Sends a heartbeat packet to the PC + This is done once automatically at initialization, + but can be called manually to ensure that the + host side tool is aware of the current USB protocol + version. + ==============================*/ + + extern void usb_sendheartbeat(); + +#endif