From 5b97e1072a36683358471c7b783a6d5fc9fa532a Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Wed, 9 Dec 2020 16:38:26 -0500 Subject: [PATCH] Add (BROKEN) UNFLoader --- Makefile | 18 +- include/libc/string.h | 4 + sm64.ld | 14 + src/game/game_init.c | 6 + src/game/hvqm.h | 1 + src/game/main.c | 2 + src/usb/debug.c | 914 ++++++++++++++++++++++++ src/usb/debug.h | 148 ++++ src/usb/memcmp.c | 34 + src/usb/memset.c | 27 + src/usb/strcat.c | 29 + src/usb/strncmp.c | 34 + src/usb/usb.c | 1545 +++++++++++++++++++++++++++++++++++++++++ src/usb/usb.h | 119 ++++ 14 files changed, 2894 insertions(+), 1 deletion(-) create mode 100644 src/usb/debug.c create mode 100644 src/usb/debug.h create mode 100755 src/usb/memcmp.c create mode 100755 src/usb/memset.c create mode 100755 src/usb/strcat.c create mode 100755 src/usb/strncmp.c create mode 100644 src/usb/usb.c create mode 100644 src/usb/usb.h diff --git a/Makefile b/Makefile index 91cef3f7..c4611696 100644 --- a/Makefile +++ b/Makefile @@ -136,6 +136,17 @@ ifeq ($(filter $(TARGET_STRING), sm64.jp.f3d_old sm64.us.f3d_old sm64.eu.f3d_new COMPARE := 0 endif + +# UNF - whether to use UNFLoader flashcart library +# 1 - includes code in ROM +# 0 - does not +UNF ?= 0 +$(eval $(call validate-option,UNF,0 1)) + +ifeq ($(UNF),1) + DEFINES += UNF=1 +endif + # Whether to hide commands or not VERBOSE ?= 0 ifeq ($(VERBOSE),0) @@ -219,7 +230,7 @@ ACTOR_DIR := actors LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h))) # Directories containing source files -SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets asm lib sound +SRC_DIRS := src src/usb src/engine src/game src/audio src/menu src/buffers actors levels bin data assets asm lib sound BIN_DIRS := bin bin/$(VERSION) # File dependencies and variables for specific files @@ -464,6 +475,11 @@ else endif endif +$(BUILD_DIR)/src/usb/usb.o: OPT_FLAGS := -O0 +$(BUILD_DIR)/src/usb/usb.o: CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-function +$(BUILD_DIR)/src/usb/debug.o: OPT_FLAGS := -O0 +$(BUILD_DIR)/src/usb/debug.o: CFLAGS += -Wno-unused-parameter -Wno-maybe-uninitialized + ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(GODDARD_SRC_DIRS) $(ULTRA_SRC_DIRS) $(ULTRA_BIN_DIRS) $(BIN_DIRS) $(TEXTURE_DIRS) $(TEXT_DIRS) $(SOUND_SAMPLE_DIRS) $(addprefix levels/,$(LEVEL_DIRS)) rsp include) $(YAY0_DIR) $(addprefix $(YAY0_DIR)/,$(VERSION)) $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/sequences/$(VERSION) # Make sure build directory exists before compiling anything diff --git a/include/libc/string.h b/include/libc/string.h index 183409eb..009d30f1 100644 --- a/include/libc/string.h +++ b/include/libc/string.h @@ -6,5 +6,9 @@ void *memcpy(void *dst, const void *src, size_t size); size_t strlen(const char *str); char *strchr(const char *str, s32 ch); +int memcmp(const void *buf1, const void *buf2, size_t count); +void *memset(void* dst, int c, size_t count); +char *strcat(char *str1, const char *str2); +int strncmp(const char *str1, const char *str2, int n); #endif diff --git a/sm64.ld b/sm64.ld index 7af9b0ee..c77f9510 100755 --- a/sm64.ld +++ b/sm64.ld @@ -116,6 +116,9 @@ SECTIONS BUILD_DIR/asm/entry.o(.text); BUILD_DIR/src/game*.o(.text); +#ifdef UNF + BUILD_DIR/src/usb*.o(.text); +#endif BUILD_DIR/src/audio*.o(.text); BUILD_DIR/asm/llmuldiv_gcc.o(.text); BUILD_DIR/asm/slidec.o(.text); @@ -129,6 +132,10 @@ SECTIONS /* data */ BUILD_DIR/src/game*.o(.data*); BUILD_DIR/src/game*.o(.sdata*); +#ifdef UNF + BUILD_DIR/src/usb*.o(.data*); + BUILD_DIR/src/usb*.o(.sdata*); +#endif BUILD_DIR/src/audio*.o(.data*); BUILD_DIR/src/audio*.o(.sdata*); @@ -141,6 +148,9 @@ SECTIONS /* rodata */ BUILD_DIR/src/game*.o(.rodata*); +#ifdef UNF + BUILD_DIR/src/usb*.o(.rodata*); +#endif BUILD_DIR/src/audio*.o(.rodata*); */libultra_rom.a:*.o(.rodata*); @@ -154,6 +164,8 @@ SECTIONS { BUILD_DIR/src/game*.o(.bss*); BUILD_DIR/src/game*.o(.sbss*); + BUILD_DIR/src/usb*.o(.bss*); + BUILD_DIR/src/usb*.o(.sbss*); BUILD_DIR/src/audio*.o(.bss*); BUILD_DIR/src/audio*.o(.sbss*); @@ -355,11 +367,13 @@ SECTIONS BUILD_DIR/data/capcom.o(.data); } END_SEG(capcom) +#if 0 BEGIN_SEG(sein, __romPos) SUBALIGN(2) { BUILD_DIR/data/sein.o(.data); } END_SEG(sein) +#endif /* Discard everything not specifically mentioned above. */ /DISCARD/ : { diff --git a/src/game/game_init.c b/src/game/game_init.c index b8e3f512..8e6d3fc2 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -19,6 +19,9 @@ #include "segment2.h" #include "segment_symbols.h" #include "rumble_init.h" +#include "hvqm.h" +#include "usb/usb.h" +#include "usb/debug.h" #include // FIXME: I'm not sure all of these variables belong in this file, but I don't @@ -601,6 +604,9 @@ void thread5_game_loop(UNUSED void *arg) { setup_game_memory(); #if ENABLE_RUMBLE init_rumble_pak_scheduler_queue(); +#endif +#ifdef UNF + debug_initialize(); #endif init_controllers(); #if ENABLE_RUMBLE diff --git a/src/game/hvqm.h b/src/game/hvqm.h index 73bd8656..40a734d6 100644 --- a/src/game/hvqm.h +++ b/src/game/hvqm.h @@ -46,6 +46,7 @@ void tkStart(tkRewindProc rewind, u32 samples_per_sec); void tkPushVideoframe(void *vaddr, u32 *statP, u64 disptime); u64 tkGetTime(void); void tkStop(void); +void createHvqmThread(void); diff --git a/src/game/main.c b/src/game/main.c index 654b7bf1..766ac35d 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -12,6 +12,8 @@ #include "segments.h" #include "main.h" #include "rumble_init.h" +#include "usb/usb.h" +#include "usb/debug.h" // Message IDs #define MESG_SP_COMPLETE 100 diff --git a/src/usb/debug.c b/src/usb/debug.c new file mode 100644 index 00000000..2c44b26d --- /dev/null +++ b/src/usb/debug.c @@ -0,0 +1,914 @@ +/*************************************************************** + debug.c + +A basic debug library that makes use of the USB library for N64 +flashcarts. +https://github.com/buu342/N64-UNFLoader +***************************************************************/ + +#include +#include // Located in ultra\GCC\MIPSE\INCLUDE +#include // Needed for Crash's Linux toolchain +#include // Needed for Crash's Linux toolchain +#include "debug.h" + + +#if DEBUG_MODE + + /********************************* + Definitions + *********************************/ + + #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 HASHTABLE_SIZE 7 + #define COMMAND_TOKENS 10 + #define BUFFER_SIZE 256 + + + /********************************* + Structs + *********************************/ + + // Register struct + typedef struct + { + u32 mask; + u32 value; + char *string; + } regDesc; + + // Thread message struct + typedef struct + { + int msgtype; + int datatype; + void* buff; + int size; + } usbMesg; + + // Debug command struct + typedef struct + { + char* command; + char* description; + char* (*execute)(); + void* next; + } debugCommand; + + + /********************************* + Function Prototypes + *********************************/ + + // Threads + #if USE_FAULTTHREAD + 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 *str, const char *buf, size_t n); + #endif + + + /********************************* + Globals + *********************************/ + + // Function pointers + #if OVERWRITE_OSPRINT + extern void* __printfunc; + #endif + + // Debug globals + static char debug_initialized = 0; + static char debug_buffer[BUFFER_SIZE]; + + // Commands hashtable related + static debugCommand* debug_commands_hashtable[HASHTABLE_SIZE]; + static debugCommand debug_commands_elements[MAX_COMMANDS]; + 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; + + // Assertion globals + static int assert_line = 0; + static const char* assert_file = NULL; + static const char* assert_expr = NULL; + + // 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)]; + + // 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, ""} + }; + + + /********************************* + Debug functions + *********************************/ + + /*============================== + debug_initialize + Initializes the debug library + ==============================*/ + + void debug_initialize() + { + // Initialize the USB functions + if (!usb_initialize()) + return; + + // Overwrite osSyncPrintf + #if OVERWRITE_OSPRINT + __printfunc = (void*)debug_osSyncPrintf_implementation; + #endif + + // Initialize the fault thread + #if USE_FAULTTHREAD + osCreateThread(&faultThread, FAULT_THREAD_ID, debug_thread_fault, 0, + (faultThreadStack+FAULT_THREAD_STACK/sizeof(u64)), + FAULT_THREAD_PRI); + 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); + + // Mark the debug mode as initialized + debug_initialized = 1; + #if DEBUG_INIT_MSG + debug_printf("Debug mode initialized!\n\n"); + #endif + } + + + /*============================== + debug_printf + Prints a formatted message to the developer's command prompt. + Supports up to 256 characters. + @param A string to print + @param variadic arguments to print as well + ==============================*/ + + void debug_printf(const char* message, ...) + { + usbMesg msg; + va_list args; + int i, j=0, delimcount; + int size = strlen(message); + char isdelim = FALSE; + char delim[8] = {0}; + memset(debug_buffer, 0, 256); + + // Ensure debug mode is initialized + if (!debug_initialized) + return; + + // Get the variadic arguments + va_start(args, message); + + // Build the string + for (i=0; inext != NULL) + slot = slot->next; + slot->next = &debug_commands_elements[debug_commands_count]; + } + else + debug_commands_hashtable[entry] = &debug_commands_elements[debug_commands_count]; + + // Fill this spot with info about this command + debug_commands_elements[debug_commands_count].command = command; + debug_commands_elements[debug_commands_count].description = description; + debug_commands_elements[debug_commands_count].execute = execute; + debug_commands_count++; + } + + + /*============================== + debug_printcommands + Prints a list of commands to the developer's command prompt. + ==============================*/ + + void debug_printcommands() + { + int i; + + // Ensure debug mode is initialized + if (!debug_initialized) + return; + + // Ensure there are commands to print + if (debug_commands_count == 0) + return; + + // Print the commands + debug_printf("Available USB commands\n----------------------\n"); + for (i=0; i 0) + { + 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++) + { + // If we're not reading a file + int offset = datasize-dataleft; + u8 tok = debug_command_totaltokens; + + // Decide what to do based on the current character + switch (debug_buffer[i]) + { + case ' ': + case '\0': + if (filestep < 2) + { + if (debug_command_incoming_start[tok] != -1) + { + debug_command_incoming_size[tok] = offset-debug_command_incoming_start[tok]; + debug_command_totaltokens++; + } + + if (debug_buffer[i] == '\0') + dataleft = 0; + break; + } + case '@': + filestep++; + if (filestep < 3) + break; + default: + // Decide what to do based on the file handle + if (filestep == 0 && debug_command_incoming_start[tok] == -1) + { + // Store the data offsets and sizes in the global command buffers + debug_command_incoming_start[tok] = offset; + } + else if (filestep == 1) + { + // Get the filesize + filesize = filesize*10 + debug_buffer[i]-'0'; + } + else if (filestep > 1) + { + // Store the file offsets and sizes in the global command buffers + debug_command_incoming_start[tok] = offset; + debug_command_incoming_size[tok] = filesize; + debug_command_totaltokens++; + + // Skip a bunch of bytes + if ((readsize-i)-filesize < 0) + usb_skip(filesize-(readsize-i)); + dataleft -= filesize; + i += filesize; + filesize = 0; + filestep = 0; + } + break; + } + dataleft--; + } + } + + // Rewind the USB fully + usb_rewind(datasize); + } + + + /*============================== + debug_thread_usb + 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; + + // Create the message queue for the USB message + osCreateMesgQueue(&usbMessageQ, &usbMessageBuf, 1); + + // Thread loop + while (1) + { + // Wait for a USB message to arrive + osRecvMesg(&usbMessageQ, (OSMesg *)&threadMsg, OS_MESG_BLOCK); + + // Ensure there's no data in the USB (which handles MSG_READ) + while (usb_poll() != 0) + { + int header = usb_poll(); + debugCommand* entry; + + // Ensure we're receiving a text command + if (USBHEADER_GETTYPE(header) != DATATYPE_TEXT) + { + errortype = USBERROR_NOTTEXT; + usb_purge(); + break; + } + + // Initialize the command trackers + debug_command_totaltokens = 0; + debug_command_current = 0; + + // Break the USB command into parts + debug_commands_setup(); + + // Ensure we don't read past our buffer + if (debug_sizecommand() > BUFFER_SIZE) + { + errortype = USBERROR_TOOMUCH; + usb_purge(); + break; + } + + // Read from the USB to retrieve the command name + debug_parsecommand(debug_buffer); + + // Iterate through the hashtable to see if we find the command + entry = debug_commands_hashtable[debug_buffer[0]%HASHTABLE_SIZE]; + while (entry != NULL) + { + // If we found the command + if (!strncmp(debug_buffer, entry->command, debug_command_incoming_size[0])) + { + // Execute the command function and exit the while loop + debug_command_error = entry->execute(); + if (debug_command_error != NULL) + errortype = USBERROR_CUSTOM; + usb_purge(); + break; + } + entry = entry->next; + } + + // If no command was found + if (entry == NULL) + { + // Purge the USB contents and print unknown command + usb_purge(); + errortype = USBERROR_UNKNOWN; + } + } + + // Spit out an error if there was one during the command parsing + if (errortype != USBERROR_NONE) + { + switch (errortype) + { + case USBERROR_NOTTEXT: + usb_write(DATATYPE_TEXT, "Error: USB data was not text\n", 29+1); + break; + case USBERROR_UNKNOWN: + usb_write(DATATYPE_TEXT, "Error: Unknown command\n", 23+1); + break; + case USBERROR_TOOMUCH: + usb_write(DATATYPE_TEXT, "Error: Command too large\n", 25+1); + break; + case USBERROR_CUSTOM: + usb_write(DATATYPE_TEXT, debug_command_error, strlen(debug_command_error)+1); + usb_write(DATATYPE_TEXT, "\n", 1+1); + break; + } + errortype = USBERROR_NONE; + } + + // Handle the other USB messages + switch (threadMsg->msgtype) + { + case MSG_WRITE: + usb_write(threadMsg->datatype, threadMsg->buff, threadMsg->size); + break; + } + } + } + + + #if OVERWRITE_OSPRINT + + /*============================== + debug_osSyncPrintf_implementation + Overwrites osSyncPrintf calls with this one + Doesn't support variadic arguments + ==============================*/ + + static void* debug_osSyncPrintf_implementation(void *str, const char *buf, size_t n) + { + debug_printf(buf); + return NULL; + } + + #endif + + + #if USE_FAULTTHREAD + + /*============================== + debug_printreg + Prints info about a register + @param The value of the register + @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; + debug_printf("%s\t\t0x%08x <", name, value); + while (desc->mask != 0) + { + if ((value & desc->mask) == desc->value) + { + (first) ? (first = 0) : ((void)debug_printf(",")); + debug_printf("%s", desc->string); + } + desc++; + } + debug_printf(">\n"); + } + + + /*============================== + debug_thread_fault + Handles the fault thread + @param Arbitrary data that the thread can receive + ==============================*/ + + static void debug_thread_fault(void *arg) + { + 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) + { + // Wait for a fault message to arrive + osRecvMesg(&faultMessageQ, (OSMesg *)&msg, OS_MESG_BLOCK); + + // Get the faulted thread + curr = (OSThread *)__osGetCurrFaultedThread(); + 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); + 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); + + // Print the registers + debug_printf("at 0x%016llx v0 0x%016llx v1 0x%016llx\n", context->at, context->v0, context->v1); + debug_printf("a0 0x%016llx a1 0x%016llx a2 0x%016llx\n", context->a0, context->a1, context->a2); + debug_printf("a3 0x%016llx t0 0x%016llx t1 0x%016llx\n", context->a3, context->t0, context->t1); + debug_printf("t2 0x%016llx t3 0x%016llx t4 0x%016llx\n", context->t2, context->t3, context->t4); + debug_printf("t5 0x%016llx t6 0x%016llx t7 0x%016llx\n", context->t5, context->t6, context->t7); + debug_printf("s0 0x%016llx s1 0x%016llx s2 0x%016llx\n", context->s0, context->s1, context->s2); + debug_printf("s3 0x%016llx s4 0x%016llx s5 0x%016llx\n", context->s3, context->s4, context->s5); + 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"); + debug_printf("d0 %.15e\td2 %.15e\n", context->fp0.d, context->fp2.d); + debug_printf("d4 %.15e\td6 %.15e\n", context->fp4.d, context->fp6.d); + debug_printf("d8 %.15e\td10 %.15e\n", context->fp8.d, context->fp10.d); + debug_printf("d12 %.15e\td14 %.15e\n", context->fp12.d, context->fp14.d); + debug_printf("d16 %.15e\td18 %.15e\n", context->fp16.d, context->fp18.d); + debug_printf("d20 %.15e\td22 %.15e\n", context->fp20.d, context->fp22.d); + debug_printf("d24 %.15e\td26 %.15e\n", context->fp24.d, context->fp26.d); + debug_printf("d28 %.15e\td30 %.15e\n", context->fp28.d, context->fp30.d); + } + } + } + + #endif + +#endif \ No newline at end of file diff --git a/src/usb/debug.h b/src/usb/debug.h new file mode 100644 index 00000000..8b363cdc --- /dev/null +++ b/src/usb/debug.h @@ -0,0 +1,148 @@ +#ifndef UNFL_DEBUG_H +#define UNFL_DEBUG_H + + /********************************* + Settings macros + *********************************/ + + // Settings + #define DEBUG_MODE 1 // Enable/Disable debug mode + #define DEBUG_INIT_MSG 0 // Print a message when debug mode has initialized + #define USE_FAULTTHREAD 1 // Create a fault detection thread + #define OVERWRITE_OSPRINT 0 // Replaces osSyncPrintf calls with debug_printf + #define MAX_COMMANDS 25 // The max amount of user defined commands possible + + // Fault thread definitions + #define FAULT_THREAD_ID 13 + #define FAULT_THREAD_PRI 125 + #define FAULT_THREAD_STACK 0x2000 + + // USB thread definitions + #define USB_THREAD_ID 14 + #define USB_THREAD_PRI 126 + #define USB_THREAD_STACK 0x2000 + + + /********************************* + Debug Functions + *********************************/ + + #if DEBUG_MODE + + /*============================== + debug_initialize + Initializes the debug and USB library. + ==============================*/ + + extern void debug_initialize(); + + + /*============================== + debug_printf + Prints a formatted message to the developer's command prompt. + Supports up to 256 characters. + @param A string to print + @param variadic arguments to print as well + ==============================*/ + + extern void debug_printf(const char* message, ...); + + + /*============================== + debug_screenshot + Sends the currently displayed framebuffer through USB. + @param The size of each pixel of the framebuffer in bytes + Typically 4 if 32-bit or 2 if 16-bit + @param The width of the framebuffer + @param The height of the framebuffer + ==============================*/ + + extern void debug_screenshot(int size, int w, int h); + + + /*============================== + debug_assert + Halts the program if the expression fails. + @param The expression to test + ==============================*/ + + #define debug_assert(expr) (expr) ? ((void)0) : _debug_assert(#expr, __FILE__, __LINE__) + + + /*============================== + debug_pollcommands + Check the USB for incoming commands. + ==============================*/ + + extern void debug_pollcommands(); + + + /*============================== + debug_addcommand + Adds a command for the USB to read. + @param The command name + @param The command description + @param The function pointer to execute + ==============================*/ + + extern void debug_addcommand(char* command, char* description, char*(*execute)()); + + + /*============================== + debug_parsecommand + Stores the next part of the incoming command into the provided buffer. + Make sure the buffer can fit the amount of data from debug_sizecommand! + If you pass NULL, it skips this command. + @param The buffer to store the data in + ==============================*/ + + extern void debug_parsecommand(void* buffer); + + + /*============================== + debug_sizecommand + Returns the size of the data from this part of the command. + @return The size of the data in bytes, or 0 + ==============================*/ + + extern int debug_sizecommand(); + + + /*============================== + debug_printcommands + Prints a list of commands to the developer's command prompt. + ==============================*/ + + extern void debug_printcommands(); + + + // Ignore this, use the macro instead + extern void _debug_assert(const char* expression, const char* file, int line); + + // Include usb.h automatically + #include "usb.h" + + #else + + // Overwrite library functions with useless macros if debug mode is disabled + #define debug_initialize() + #define debug_printf(a, __VA_ARGS__) + #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_sizecommand() 0 + #define debug_printcommands() + #define usb_initialize() 0 + #define usb_getcart() 0 + #define usb_write(a, b, c) + #define usb_poll() 0 + #define usb_read(a, b) + #define usb_skip(a) + #define usb_rewind(a) + #define usb_purge() + + #endif + +#endif \ No newline at end of file diff --git a/src/usb/memcmp.c b/src/usb/memcmp.c new file mode 100755 index 00000000..1b25e1b9 --- /dev/null +++ b/src/usb/memcmp.c @@ -0,0 +1,34 @@ +/* +============================================================================= + Copyright (C) 1997-1999 NINTENDO Co.,Ltd. + + $RCSfile: memcmp.c,v $ + $Revision: 1.1 $ + $Date: 1999/04/14 07:18:40 $ +============================================================================= +function name: memcmp +----------------------------------------------------------------------------- +format: #include + int memcmp(const void *buf1, const void *buf2, size_t count) +argument: buf1 pointer to be compared + buf2 pointer to be compared + count the number of bytes to be compared +return value: if less than 0 buf1 is smaller than buf2 + if 0 buf1 is equal to buf2 + if more than 0 buf1 is larger than buf2 +explanation: compares the contents of pointer buf1 and pointer buf2 by the number of count bytes +----------------------------------------------------------------------------- +*/ +#include + +int memcmp(const void *buf1, const void *buf2, size_t count) +{ + char *b1 = (char *)buf1, *b2 = (char *)buf2; + while(count) { + if (*b1 != *b2) return *b1 - *b2; + b1 ++; + b2 ++; + count--; + } + return 0; +} diff --git a/src/usb/memset.c b/src/usb/memset.c new file mode 100755 index 00000000..45e0688c --- /dev/null +++ b/src/usb/memset.c @@ -0,0 +1,27 @@ +/* +============================================================================= + Copyright (C) 1997-1999 NINTENDO Co.,Ltd. + + $RCSfile: memset.c,v $ + $Revision: 1.1 $ + $Date: 1999/04/14 07:18:41 $ +============================================================================= +function name: memset +----------------------------------------------------------------------------- +format: #include + void *memset(void* dst, int c, size_t count); +argument: dst pointer of destination to be written + c byte data to be written + count the number of characters to be written +return value: pointer of destination to be written, dst +explanation: writes byte data c from the pointer dst indicates by the amount of count +----------------------------------------------------------------------------- +*/ +#include + +void *memset(void* dst, int c, size_t count) +{ + char *p = (char *)dst; + while(count--) *p++ = c; + return dst; +} diff --git a/src/usb/strcat.c b/src/usb/strcat.c new file mode 100755 index 00000000..ca731c48 --- /dev/null +++ b/src/usb/strcat.c @@ -0,0 +1,29 @@ +/* +============================================================================= + Copyright (C) 1997-1999 NINTENDO Co.,Ltd. + + $RCSfile: strcat.c,v $ + $Revision: 1.1 $ + $Date: 1999/04/14 07:18:41 $ +============================================================================= +function name: strcat +----------------------------------------------------------------------------- +format: #include + char *strcat(char *str1, const char *str2); +argument: str1 character string buffer to be added + str2 character string buffer to add +return value: pointer of str1 +explanation: adds character string, str2 to the end of character string, str1 +----------------------------------------------------------------------------- +*/ +#include + +char *strcat(char *str1, const char *str2) +{ + char *p; + p = str1; + while(*p) p++; + while(*str2) *p++ = *str2++; + *p = '\0'; + return str1; +} diff --git a/src/usb/strncmp.c b/src/usb/strncmp.c new file mode 100755 index 00000000..dd9f7d80 --- /dev/null +++ b/src/usb/strncmp.c @@ -0,0 +1,34 @@ +/* +============================================================================= + Copyright (C) 1997-1999 NINTENDO Co.,Ltd. + + $RCSfile: strncmp.c,v $ + $Revision: 1.1 $ + $Date: 1999/04/14 07:18:42 $ +============================================================================= +function name: strncmp +----------------------------------------------------------------------------- +format : #include + int strncmp(const char *str1, const char *str2, int n) +argument: str1 character string buffer to be compared + str2 character string buffer to be compared + n character number to be compared +return value: if less than 0, str1 is smaller than str2 + if 0 str1 is equal to str2 + if more than 0, str1 is larger than str2 +explanation: compares character string, str1 with character string, str2 by + n character +----------------------------------------------------------------------------- +*/ +#include + +int strncmp(const char *str1, const char *str2, int n) +{ + char c1 = 1,c2; + while(c1 && n--) { + c1=*str1++; + c2=*str2++; + if (c1 != c2) return c1 - c2; + } + return 0; +} diff --git a/src/usb/usb.c b/src/usb/usb.c new file mode 100644 index 00000000..0a8e1daf --- /dev/null +++ b/src/usb/usb.c @@ -0,0 +1,1545 @@ +/*************************************************************** + usb.c + +Allows USB communication between an N64 flashcart and the PC +using UNFLoader. +https://github.com/buu342/N64-UNFLoader +***************************************************************/ + +#include +#include +#include "usb.h" +#define ALIGN(s, align) (((u32)(s) + ((align)-1)) & ~((align)-1)) + + +/********************************* + Data macros +*********************************/ + +// Input/Output buffer size. Always keep it at 512 +#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 + +// Data header related +#define USBHEADER_CREATE(type, left) (((type<<24) | (left & 0x00FFFFFF))) + + +/********************************* + 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 +*********************************/ + +// 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_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_REGISTER_MAGIC 0x000002EC +#define D64_REGISTER_VARIANT 0x000002F0 +#define D64_REGISTER_BUTTON 0x000002F8 +#define D64_REGISTER_REVISION 0x000002FC + +#define D64_REGISTER_USBCOMSTAT 0x00000400 +#define D64_REGISTER_USBP0R0 0x00000404 +#define D64_REGISTER_USBP1R1 0x00000408 + +#define D64_ENABLE_ROMWR 0xF0 +#define D64_DISABLE_ROMWR 0xF1 +#define D64_COMMAND_WRITE 0x08 + +// Cartridge Interface return values +#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_IDLE 0x00 +#define D64_CI_BUSY 0x10 +#define D64_CI_WRITE 0x20 + + +/********************************* + EverDrive macros +*********************************/ + +#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_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_REGKEY 0xAA55 + +#define ED3_VERSION 0xED640008 +#define ED7_VERSION 0xED640013 + + +/********************************* + SummerCart64 macros +*********************************/ + +#define SC64_SDRAM_BASE 0x10000000 + +#define SC64_BANK_ROM 1 + +#define SC64_REGS_BASE 0x1E000000 +#define SC64_REG_SCR (SC64_REGS_BASE + 0x00) +#define SC64_REG_VERSION (SC64_REGS_BASE + 0x08) +#define SC64_REG_USB_SCR (SC64_REGS_BASE + 0x10) +#define SC64_REG_USB_DMA_ADDR (SC64_REGS_BASE + 0x14) +#define SC64_REG_USB_DMA_LEN (SC64_REGS_BASE + 0x18) + +#define SC64_MEM_BASE (SC64_REGS_BASE + 0x1000) +#define SC64_MEM_USB_FIFO_BASE (SC64_MEM_BASE + 0x0000) +#define SC64_MEM_USB_FIFO_LEN (4 * 1024) + +#define SC64_SCR_SDRAM_WRITE_EN (1 << 0) + +#define SC64_VERSION_A 0x53363461 + +#define SC64_USB_STATUS_BUSY (1 << 0) +#define SC64_USB_STATUS_READY (1 << 1) +#define SC64_USB_CONTROL_START (1 << 0) +#define SC64_USB_CONTROL_FIFO_FLUSH (1 << 2) + +#define SC64_USB_BANK_ADDR(b, a) ((((b) & 0xF) << 28) | ((a) & 0x3FFFFFF)) +#define SC64_USB_LENGTH(l) (ALIGN((l), 4) / 4) +#define SC64_USB_DMA_MAX_LEN (2 * 1024 * 1024) +#define SC64_USB_FIFO_ITEMS(s) (((s) >> 3) & 0x7FF) + + +/********************************* + Function Prototypes +*********************************/ + +static void usb_findcart(); +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 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 void usb_sc64_write(int datatype, const void* data, int size); +static u32 usb_sc64_poll(); +static void usb_sc64_read(); + + +/********************************* + Globals +*********************************/ + +// Function pointers +void (*funcPointer_write)(int datatype, const void* data, int size); +u32 (*funcPointer_poll)(); +void (*funcPointer_read)(); + +// USB globals +static s8 usb_cart = CART_NONE; +static u8 usb_buffer[BUFFER_SIZE*3] __attribute__((aligned(16))); +int usb_datatype = 0; +int usb_datasize = 0; +int usb_dataleft = 0; +int usb_readblock = -1; + +// Message globals +#if !USE_OSRAW + OSMesg dmaMessageBuf; + OSIoMesg dmaIOMessageBuf; + OSMesgQueue dmaMessageQ; +#endif + +// osPiRaw +#if USE_OSRAW + extern s32 __osPiRawWriteIo(u32, u32); + extern s32 __osPiRawReadIo(u32, u32 *); + extern s32 __osPiRawStartDma(s32, u32, void *, u32); + + #define osPiRawWriteIo(a, b) __osPiRawWriteIo(a, b) + #define osPiRawReadIo(a, b) __osPiRawReadIo(a, b) + #define osPiRawStartDma(a, b, c, d) __osPiRawStartDma(a, b, c, d) +#endif + + +/********************************* + USB functions +*********************************/ + +/*============================== + usb_initialize + Initializes the USB buffers and pointers + @returns 1 if the USB initialization was successful, 0 if not +==============================*/ + +char usb_initialize() +{ + // Initialize the debug related globals + memset(usb_buffer, 0, BUFFER_SIZE); + + // Create the message queue + #if !USE_OSRAW + osCreateMesgQueue(&dmaMessageQ, &dmaMessageBuf, 1); + #endif + + // Find the flashcart + usb_findcart(); + + // Set the function pointers based on the flashcart + switch (usb_cart) + { + case CART_64DRIVE: + funcPointer_write = usb_64drive_write; + funcPointer_poll = usb_64drive_poll; + funcPointer_read = usb_64drive_read; + break; + case CART_EVERDRIVE: + funcPointer_write = usb_everdrive_write; + funcPointer_poll = usb_everdrive_poll; + funcPointer_read = usb_everdrive_read; + break; + case CART_SC64: + funcPointer_write = usb_sc64_write; + funcPointer_poll = usb_sc64_poll; + funcPointer_read = usb_sc64_read; + break; + default: + return 0; + } + return 1; +} + + +/*============================== + usb_findcart + Checks if the game is running on a 64Drive, EverDrive or a SummerCart64. +==============================*/ + +static void usb_findcart() +{ + u32 buff; + + // Read the cartridge and check if we have a 64Drive. + #if USE_OSRAW + osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_MAGIC, &buff); + #else + osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_MAGIC, &buff); + #endif + if (buff == D64_MAGIC) + { + usb_cart = CART_64DRIVE; + return; + } + + // Read the cartridge and check if we have a SummerCart64. + #if USE_OSRAW + osPiRawReadIo(SC64_REG_VERSION, &buff); + #else + osPiReadIo(SC64_REG_VERSION, &buff); + #endif + if (buff == SC64_VERSION_A) + { + usb_cart = CART_SC64; + return; + } + + // Since we didn't find a 64Drive or SummerCart64, 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); + + // Check if we have an EverDrive + if (buff == ED7_VERSION || buff == ED3_VERSION) + { + // Initialize the PI + IO_WRITE(PI_STATUS_REG, 3); + IO_WRITE(PI_BSD_DOM1_LAT_REG, 0x40); + IO_WRITE(PI_BSD_DOM1_PWD_REG, 0x12); + IO_WRITE(PI_BSD_DOM1_PGS_REG, 0x07); + IO_WRITE(PI_BSD_DOM1_RLS_REG, 0x03); + IO_WRITE(PI_BSD_DOM2_LAT_REG, 0x05); + IO_WRITE(PI_BSD_DOM2_PWD_REG, 0x0C); + IO_WRITE(PI_BSD_DOM2_PGS_REG, 0x0D); + IO_WRITE(PI_BSD_DOM2_RLS_REG, 0x02); + IO_WRITE(PI_BSD_DOM1_LAT_REG, 0x04); + IO_WRITE(PI_BSD_DOM1_PWD_REG, 0x0C); + + // Set the USB mode + usb_everdrive_writereg(ED_REG_SYSCFG, 0); + usb_everdrive_writereg(ED_REG_USBCFG, ED_USBMODE_RDNOP); + + // Set the cart to EverDrive + usb_cart = CART_EVERDRIVE; + return; + } +} + + +/*============================== + usb_getcart + Returns which flashcart is currently connected + @return The CART macro that corresponds to the identified flashcart +==============================*/ + +char usb_getcart() +{ + return usb_cart; +} + + +/*============================== + usb_write + Writes data to the USB. + Will not write if there is data to read from USB + @param The DATATYPE that is being sent + @param A buffer with the data to send + @param The size of the data being sent +==============================*/ + +void usb_write(int datatype, const void* data, int size) +{ + // If no debug cart exists, stop + if (usb_cart == CART_NONE) + return; + + // If there's data to read first, stop + if (usb_dataleft != 0) + return; + + // Call the correct write function + funcPointer_write(datatype, data, 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 +==============================*/ + +u32 usb_poll() +{ + // If no debug cart exists, stop + if (usb_cart == CART_NONE) + return 0; + + // If we're out of USB data to read, we don't need the header info anymore + if (usb_dataleft <= 0) + { + usb_dataleft = 0; + usb_datatype = 0; + usb_datasize = 0; + usb_readblock = -1; + } + + // If there's still data that needs to be read, return the header with the data left + if (usb_dataleft != 0) + return USBHEADER_CREATE(usb_datatype, usb_dataleft); + + // Call the correct read function + return funcPointer_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 +==============================*/ + +void usb_read(void* buffer, int nbytes) +{ + int read = 0; + int left = nbytes; + int offset = usb_datasize-usb_dataleft; + int copystart = offset%BUFFER_SIZE; + int block = BUFFER_SIZE-copystart; + int blockoffset = (offset/BUFFER_SIZE)*BUFFER_SIZE; + + // If no debug cart exists, stop + if (usb_cart == CART_NONE) + return; + + // If there's no data to read, stop + if (usb_dataleft == 0) + return; + + // Read chunks from ROM + while (left > 0) + { + // Ensure we don't read too much data + if (left > usb_dataleft) + 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); + + // Increment/decrement all our counters + read += block; + left -= block; + usb_dataleft -= block; + blockoffset += BUFFER_SIZE; + block = BUFFER_SIZE; + copystart = 0; + } +} + + +/*============================== + usb_skip + Skips a USB read by the specified amount of bytes + @param The number of bytes to skip +==============================*/ + +void usb_skip(int nbytes) +{ + // Subtract the amount of bytes to skip to the data pointers + usb_dataleft -= nbytes; + if (usb_dataleft < 0) + usb_dataleft = 0; +} + + +/*============================== + usb_rewind + Rewinds a USB read by the specified amount of bytes + @param The number of bytes to rewind +==============================*/ + +void usb_rewind(int nbytes) +{ + // Add the amount of bytes to rewind to the data pointers + usb_dataleft += nbytes; + if (usb_dataleft > usb_datasize) + usb_dataleft = usb_datasize; +} + + +/*============================== + usb_purge + Purges the incoming USB data +==============================*/ + +void usb_purge() +{ + usb_dataleft = 0; + usb_datatype = 0; + usb_datasize = 0; + usb_readblock = -1; +} + + +/********************************* + 64Drive functions +*********************************/ + +/*============================== + usb_64drive_wait + Wait until the 64Drive is ready + @return 0 if success or -1 if failure +==============================*/ + +static s8 usb_64drive_wait() +{ + u32 ret; + u32 timeout = 0; // I wanted to use osGetTime() but that requires the VI manager + + // Wait until the cartridge interface is ready + do + { + #if USE_OSRAW + osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_STATUS, &ret); + #else + osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_STATUS, &ret); + #endif + + // Took too long, abort + if((timeout++) > 1000000) + return -1; + } + while((ret >> 8) & D64_CI_BUSY); + + // Success + return 0; +} + + +/*============================== + usb_64drive_setwritable + Set the write mode on the 64Drive + @param A boolean with whether to enable or disable +==============================*/ + +static void usb_64drive_setwritable(u8 enable) +{ + usb_64drive_wait(); + #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 + usb_64drive_wait(); +} + + +/*============================== + usb_64drive_waitidle + Waits for the 64Drive's USB to be idle +==============================*/ + +static void usb_64drive_waitidle() +{ + u32 status; + do + { + #if USE_OSRAW + osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); + #else + osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); + #endif + status = (status >> 4) & D64_USB_BUSY; + } + while(status != D64_USB_IDLE); +} + + +/*============================== + usb_64drive_armstatus + Checks if the 64Drive is armed + @return The arming status +==============================*/ + +static u32 usb_64drive_armstatus() +{ + u32 status; + #if USE_OSRAW + osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); + #else + osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); + #endif + return status & 0xf; +} + + +/*============================== + usb_64drive_waitdisarmed + Waits for the 64Drive's USB to be disarmed +==============================*/ + +static void usb_64drive_waitdisarmed() +{ + u32 status; + do + { + #if USE_OSRAW + osPiRawReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); + #else + osPiReadIo(D64_CIBASE_ADDRESS + D64_REGISTER_USBCOMSTAT, &status); + #endif + status &= 0x0F; + } + while (status != D64_USB_IDLEUNARMED); +} + + +/*============================== + usb_64drive_write + Sends data through USB from the 64Drive + Will not write if there is data to read from USB + @param The DATATYPE that is being sent + @param A buffer with the data to send + @param The size of the data being sent +==============================*/ + +static void usb_64drive_write(int datatype, const void* data, int size) +{ + int left = size; + int read = 0; + + // Spin until the write buffer is free and then set the cartridge to write mode + usb_64drive_waitidle(); + usb_64drive_setwritable(TRUE); + + // Write data to SDRAM until we've finished + while (left > 0) + { + int block = left; + if (block > BUFFER_SIZE) + block = BUFFER_SIZE; + + // Copy the data to the global buffer + memcpy(usb_buffer, (void*)((char*)data+read), 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); + 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 + + // 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 = 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 + #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 + } +} + + +/*============================== + usb_64drive_disarm + Disarms the 64Drive's USB +==============================*/ + +static void usb_64drive_disarm() +{ + // Disarm the USB + #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 + usb_64drive_waitdisarmed(); +} + + +/*============================== + usb_64drive_poll + Returns the header of data being received via USB on the 64Drive + 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_64drive_poll() +{ + u32 ret; + + // Arm the USB buffer + usb_64drive_waitidle(); + usb_64drive_setwritable(TRUE); + usb_64drive_arm(DEBUG_ADDRESS, DEBUG_ADDRESS_SIZE); + + // If there's data to service + if (usb_64drive_armstatus() == D64_USB_DATA) + { + char buff[8]; + u32 copyleft; + + // Read ROM to get the data header + osWritebackDCacheAll(); + #if USE_OSRAW + osPiRawStartDma(OS_READ, + D64_BASE_ADDRESS + DEBUG_ADDRESS, buff, + 8); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, + D64_BASE_ADDRESS + DEBUG_ADDRESS, buff, + 8, &dmaMessageQ); + (void)osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif + + // Ensure we got a USB request and this isn't something else + if (buff[0] != 'D' && buff[1] != 'M' && buff[2] != 'A' && buff[3] != '@') + return 0; + + // Get the data header + ret = buff[4] << 24 | buff[5] << 16 | buff[6] << 8 | buff[7]; + usb_datatype = USBHEADER_GETTYPE(ret); + usb_dataleft = USBHEADER_GETSIZE(ret); + usb_datasize = usb_dataleft; + usb_readblock = -1; + copyleft = usb_dataleft; + + // Copy data from USB to ROM + while (copyleft > 0) + { + u32 block = copyleft%BUFFER_SIZE; + if (block == 0) + block = BUFFER_SIZE; + + // Arm the 64Drive's USB + usb_64drive_arm(DEBUG_ADDRESS, DEBUG_ADDRESS_SIZE); + + // Wait for data to arrive + while (usb_64drive_armstatus() != D64_USB_DATA) + ; + + // Put the data in the correct ROM offset + osWritebackDCacheAll(); + #if USE_OSRAW + osPiRawStartDma(OS_READ, + D64_BASE_ADDRESS + DEBUG_ADDRESS, usb_buffer, + BUFFER_SIZE); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, + D64_BASE_ADDRESS + DEBUG_ADDRESS, usb_buffer, + BUFFER_SIZE, &dmaMessageQ); + (void)osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif + osWritebackDCacheAll(); + #if USE_OSRAW + osPiRawStartDma(OS_WRITE, + D64_BASE_ADDRESS + DEBUG_ADDRESS + (copyleft-block), usb_buffer, + BUFFER_SIZE); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_WRITE, + D64_BASE_ADDRESS + DEBUG_ADDRESS + (copyleft-block), usb_buffer, + BUFFER_SIZE, &dmaMessageQ); + (void)osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif + + copyleft -= block; + } + + // Return the data header + usb_64drive_waitidle(); + usb_64drive_setwritable(FALSE); + return USBHEADER_CREATE(usb_datatype, usb_datasize); + } + + // Disarm the USB if no data arrived + usb_64drive_disarm(); + usb_64drive_waitidle(); + usb_64drive_setwritable(FALSE); + return 0; +} + + +/*============================== + usb_64drive_read + Reads bytes from the 64Drive ROM into the global buffer with the block offset +==============================*/ + +static void usb_64drive_read() +{ + // Set up DMA transfer between RDRAM and the PI + osWritebackDCacheAll(); + #if USE_OSRAW + osPiRawStartDma(OS_READ, + D64_BASE_ADDRESS + DEBUG_ADDRESS + usb_readblock, usb_buffer, + BUFFER_SIZE); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, + D64_BASE_ADDRESS + DEBUG_ADDRESS + usb_readblock, usb_buffer, + BUFFER_SIZE, &dmaMessageQ); + (void)osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif +} + + +/********************************* + EverDrive functions +*********************************/ + +/*============================== + usb_everdrive_wait_pidma + Spins until the EverDrive's DMA is ready +==============================*/ + +static void usb_everdrive_wait_pidma() +{ + u32 status; + do + { + status = *(volatile unsigned long *)(N64_PI_ADDRESS + N64_PI_STATUS); + status &= (PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY); + } + while (status); +} + + +/*============================== + usb_everdrive_readdata + Reads data from a specific address on the EverDrive + @param The buffer with the data + @param The register address to write to the PI + @param The size of the data +==============================*/ + +static void usb_everdrive_readdata(void* buff, u32 pi_address, u32 len) +{ + // Correct the PI address + pi_address &= 0x1FFFFFFF; + + // Set up DMA transfer between RDRAM and the PI + osInvalDCache(buff, len); + #if USE_OSRAW + osPiRawStartDma(OS_READ, + pi_address, buff, + len); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, + pi_address, buff, + len, &dmaMessageQ); + (void)osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif + + // Write the data to the PI + usb_everdrive_wait_pidma(); + IO_WRITE(PI_STATUS_REG, 3); + *(volatile unsigned long *)(N64_PI_ADDRESS + N64_PI_RAMADDRESS) = (u32)buff; + *(volatile unsigned long *)(N64_PI_ADDRESS + N64_PI_PIADDRESS) = pi_address; + *(volatile unsigned long *)(N64_PI_ADDRESS + N64_PI_READLENGTH) = len-1; + usb_everdrive_wait_pidma(); +} + + +/*============================== + usb_everdrive_readreg + Reads data from a specific register on the EverDrive + @param The register to read from + @param A pointer to write the read value to +==============================*/ + +static void usb_everdrive_readreg(u32 reg, u32* result) +{ + usb_everdrive_readdata(result, ED_GET_REGADD(reg), sizeof(u32)); +} + + +/*============================== + usb_everdrive_writedata + Writes data to a specific address on the EverDrive + @param A buffer with the data to write + @param The register address to write to the PI + @param The length of the data +==============================*/ + +static void usb_everdrive_writedata(void* buff, u32 pi_address, u32 len) +{ + // Correct the PI address + pi_address &= 0x1FFFFFFF; + + // Set up DMA transfer between RDRAM and the PI + osWritebackDCache(buff, len); + #if USE_OSRAW + osPiRawStartDma(OS_WRITE, + pi_address, buff, + len); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_WRITE, + pi_address, buff, + len, &dmaMessageQ); + (void)osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif + + // Write the data to the PI + usb_everdrive_wait_pidma(); + IO_WRITE(PI_STATUS_REG, 3); + *(volatile unsigned long *)(N64_PI_ADDRESS + N64_PI_RAMADDRESS) = (u32)buff; + *(volatile unsigned long *)(N64_PI_ADDRESS + N64_PI_PIADDRESS) = pi_address; + *(volatile unsigned long *)(N64_PI_ADDRESS + N64_PI_WRITELENGTH) = len-1; + usb_everdrive_wait_pidma(); +} + + +/*============================== + usb_everdrive_writereg + Writes data to a specific register on the EverDrive + @param The register to write to + @param The value to write to the register +==============================*/ + +static void usb_everdrive_writereg(u64 reg, u32 value) +{ + usb_everdrive_writedata(&value, ED_GET_REGADD(reg), sizeof(u32)); +} + + +/*============================== + usb_everdrive_usbbusy + Spins until the USB is no longer busy +==============================*/ + +static void usb_everdrive_usbbusy() +{ + u32 val; + do + { + usb_everdrive_readreg(ED_REG_USBCFG, &val); + } + while ((val & ED_USBSTAT_ACT) != 0); +} + + +/*============================== + usb_everdrive_canread + Checks if the EverDrive's USB can read + @return 1 if it can read, 0 if not +==============================*/ + +static u8 usb_everdrive_canread() +{ + u32 val, status = ED_USBSTAT_POWER; + + // Read the USB register and check its status + usb_everdrive_readreg(ED_REG_USBCFG, &val); + status = val & (ED_USBSTAT_POWER | ED_USBSTAT_RXF); + return status == ED_USBSTAT_POWER; +} + + +/*============================== + usb_everdrive_readusb + Reads from the EverDrive USB buffer + @param The buffer to put the read data in + @param The number of bytes to read +==============================*/ + +static void usb_everdrive_readusb(void* buffer, int size) +{ + u16 block, addr; + + 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_everdrive_writereg(ED_REG_USBCFG, ED_USBMODE_RD | addr); + + // Wait for the FPGA to transfer the data to its internal buffer + usb_everdrive_usbbusy(); + + // Read from the internal buffer and store it in our buffer + usb_everdrive_readdata(buffer, ED_GET_REGADD(ED_REG_USBDAT + addr), block); + buffer = (char*)buffer + block; + size -= block; + } +} + + +/*============================== + usb_everdrive_write + Sends data through USB from the EverDrive + Will not write if there is data to read from USB + @param The DATATYPE that is being sent + @param A buffer with the data to send + @param The size of the data being sent +==============================*/ + +static void usb_everdrive_write(int datatype, const void* data, int size) +{ + char wrotecmp = 0; + char cmp[] = {'C', 'M', 'P', 'H'}; + int read = 0; + 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'; + usb_buffer[2] = 'A'; + usb_buffer[3] = '@'; + usb_buffer[4] = (header >> 24) & 0xFF; + 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) + { + int block = left; + 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) + { + left = 4; + offset = block+offset; + data = cmp; + wrotecmp = 1; + read = 0; + continue; + } + + // Ensure the data is 16 byte aligned and the block address is correct + blocksend = (block+offset)+15 - ((block+offset)+15)%16; + baddr = BUFFER_SIZE - blocksend; + + // Set USB to write mode and send data through USB + usb_everdrive_writereg(ED_REG_USBCFG, ED_USBMODE_WRNOP); + usb_everdrive_writedata(usb_buffer, ED_GET_REGADD(ED_REG_USBDAT + baddr), blocksend); + + // Set USB to write mode with the new address and wait for USB to end + usb_everdrive_writereg(ED_REG_USBCFG, ED_USBMODE_WR | baddr); + usb_everdrive_usbbusy(); + + // Keep track of what we've read so far + left -= block; + read += block; + offset = 0; + } +} + + +/*============================== + usb_everdrive_poll + Returns the header of data being received via USB on the EverDrive + 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_everdrive_poll() +{ + char buff[16]; + int len; + int offset = 0; + + // Wait for the USB to be ready + usb_everdrive_usbbusy(); + + // 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, 16); + 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_dataleft = usb_datasize; + usb_readblock = -1; + + // Begin receiving data + usb_everdrive_writereg(ED_REG_USBCFG, ED_USBMODE_RD | BUFFER_SIZE); + len = (usb_datasize + BUFFER_SIZE-usb_datasize%BUFFER_SIZE)/BUFFER_SIZE; + + // While there's data to service + while (len--) + { + // 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? + + // Tell the FPGA we can receive more data + if (len != 0) + usb_everdrive_writereg(ED_REG_USBCFG, ED_USBMODE_RD | BUFFER_SIZE); + + // Copy received block to ROM + usb_everdrive_writedata(usb_buffer, ED_BASE + DEBUG_ADDRESS + offset, BUFFER_SIZE); + offset += BUFFER_SIZE; + } + + // Read the CMP Signal + usb_everdrive_usbbusy(); + usb_everdrive_readusb(buff, 16); + if (buff[0] != 'C' || buff[1] != 'M' || buff[2] != 'P' || buff[3] != 'H') + { + // Something went wrong with the data + usb_datatype = 0; + usb_datasize = 0; + usb_dataleft = 0; + usb_readblock = -1; + return 0; + } + + // Return the data header + return USBHEADER_CREATE(usb_datatype, usb_datasize); +} + + +/*============================== + usb_everdrive_read + Reads bytes from the EverDrive ROM into the global buffer with the block offset +==============================*/ + +static void usb_everdrive_read() +{ + // Set up DMA transfer between RDRAM and the PI + 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 +} + + +/********************************* + SummerCart64 functions +*********************************/ + + +/*============================== + usb_sc64_read_usb_scr + Reads SummerCart64 REG_USB_SCR register + @return value of REG_USB_SCR register +==============================*/ + +static u32 usb_sc64_read_usb_scr(void) +{ + u32 usb_scr; + + #if USE_OSRAW + osPiRawReadIo(SC64_REG_USB_SCR, &usb_scr); + #else + osPiReadIo(SC64_REG_USB_SCR, &usb_scr); + #endif + + return usb_scr; +} + + +/*============================== + usb_sc64_read_usb_fifo + Loads one element from USB FIFO + @return value popped from USB FIFO +==============================*/ + +static u32 usb_sc64_read_usb_fifo(void) +{ + u32 data; + + #if USE_OSRAW + osPiRawReadIo(SC64_MEM_USB_FIFO_BASE, &data); + #else + osPiReadIo(SC64_MEM_USB_FIFO_BASE, &data); + #endif + + return data; +} + + +/*============================== + usb_sc64_waitidle + Waits for the SummerCart64 USB interface to be idle + @return 0 if interface is ready, -1 if USB cable is not connected +==============================*/ + +static s8 usb_sc64_waitidle(void) +{ + u32 usb_scr; + + do + { + usb_scr = usb_sc64_read_usb_scr(); + if (!(usb_scr & SC64_USB_STATUS_READY)) { + // Reset usb_cart type if USB cable is not connected + usb_cart = CART_NONE; + return -1; + } + } while (usb_scr & SC64_USB_STATUS_BUSY); + + return 0; +} + + +/*============================== + usb_sc64_waitdata + Waits for the SummerCart64 USB FIFO to contain specified amount of data or for full FIFO + @param length in bytes + @return number of available bytes in FIFO, -1 if USB cable is not connected +==============================*/ + +static s32 usb_sc64_waitdata(u32 length) +{ + u32 usb_scr; + u32 wait_length = ALIGN(MIN(length, SC64_MEM_USB_FIFO_LEN), 4); + u32 bytes = 0; + + do + { + usb_scr = usb_sc64_read_usb_scr(); + if (!(usb_scr & SC64_USB_STATUS_READY)) { + // Reset usb_cart type if USB cable is not connected + usb_cart = CART_NONE; + return -1; + } + bytes = SC64_USB_FIFO_ITEMS(usb_scr) * 4; + } while (bytes < wait_length); + + return (s32) bytes; +} + + +/*============================== + usb_sc64_setwritable + Enable ROM (SDRAM) writes in SummerCart64 + @param A boolean with whether to enable or disable +==============================*/ + +static void usb_sc64_setwritable(u8 enable) +{ + u32 scr; + + #if USE_OSRAW + osPiRawReadIo(SC64_REG_SCR, &scr); + osPiRawWriteIo(SC64_REG_SCR, enable ? (scr | SC64_SCR_SDRAM_WRITE_EN) : (scr & (~SC64_SCR_SDRAM_WRITE_EN))); + #else + osPiReadIo(SC64_REG_SCR, &scr); + osPiWriteIo(SC64_REG_SCR, enable ? (scr | SC64_SCR_SDRAM_WRITE_EN) : (scr & (~SC64_SCR_SDRAM_WRITE_EN))); + #endif +} + + +/*============================== + usb_sc64_write + Sends data through USB from the SummerCart64 + @param The DATATYPE that is being sent + @param A buffer with the data to send + @param The size of the data being sent +==============================*/ + +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 = MIN(DEBUG_ADDRESS_SIZE, SC64_USB_DMA_MAX_LEN); + + u8* data_ptr = (u8*) data; + u32 sdram_address = SC64_SDRAM_BASE + DEBUG_ADDRESS; + + int offset; + int left; + u32 transfer_length; + + // Wait until ready + if (usb_sc64_waitidle()) + { + // Do nothing if USB cable is not connected + return; + } + + // Enable SDRAM writes + usb_sc64_setwritable(TRUE); + + // 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; + + 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; + + // Fill buffer + memcpy(usb_buffer + offset, data_ptr, data_length); + + // 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; + } + + // Calculate RDRAM -> PI transfer length + dma_length = ALIGN(offset + data_length, 4); + + // Write data to buffer in SDRAM + 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 + + // 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) + { + usb_sc64_setwritable(FALSE); + } + + // Setup hardware registers + #if USE_OSRAW + osPiRawWriteIo(SC64_REG_USB_DMA_ADDR, SC64_USB_BANK_ADDR(SC64_BANK_ROM, DEBUG_ADDRESS)); + osPiRawWriteIo(SC64_REG_USB_DMA_LEN, SC64_USB_LENGTH(transfer_length)); + osPiRawWriteIo(SC64_REG_USB_SCR, SC64_USB_CONTROL_START); + #else + osPiWriteIo(SC64_REG_USB_DMA_ADDR, SC64_USB_BANK_ADDR(SC64_BANK_ROM, DEBUG_ADDRESS)); + osPiWriteIo(SC64_REG_USB_DMA_LEN, SC64_USB_LENGTH(transfer_length)); + osPiWriteIo(SC64_REG_USB_SCR, SC64_USB_CONTROL_START); + #endif + + // Wait for transfer to complete if there's more data to send + if (left > 0) + { + if (usb_sc64_waitidle()) + { + // Disable SDRAM writes + usb_sc64_setwritable(FALSE); + + // Stop sending data if USB cable has been disconnected + return; + } + } + + // Reset SDRAM address and transfer length + sdram_address = SC64_SDRAM_BASE + DEBUG_ADDRESS; + transfer_length = 0; + } +} + + +/*============================== + usb_sc64_poll + Returns the header of data being received via USB on the SummerCart64 + 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) +{ + u32 buff, sdram_address; + int left; + + // Load how many 32 bit words are in FIFO + u32 fifo_items = SC64_USB_FIFO_ITEMS(usb_sc64_read_usb_scr()); + + // Check data if there's at least DMA@ and header in FIFO + if (fifo_items >= 2) + { + // Load and check DMA@ identifier + buff = usb_sc64_read_usb_fifo(); + if (memcmp(&buff, "DMA@", 4) != 0) + { + // Return if identifier is wrong + return 0; + } + + // Load header + buff = usb_sc64_read_usb_fifo(); + + // Fill USB read data variables + usb_datatype = USBHEADER_GETTYPE(buff); + usb_dataleft = USBHEADER_GETSIZE(buff); + usb_datasize = usb_dataleft; + usb_readblock = -1; + + // Calculate copy length, data size + CMPH identifier aligned to 4 bytes + left = ALIGN(usb_datasize + 4, 4); + + // Starting address in SDRAM + sdram_address = SC64_SDRAM_BASE + DEBUG_ADDRESS; + + // Enable SDRAM writes + usb_sc64_setwritable(TRUE); + + // Copy data until finished + while (left > 0) + { + // Calculate transfer length + s32 dma_length = MIN(left, BUFFER_SIZE); + + // Wait for data in FIFO + dma_length = usb_sc64_waitdata(dma_length); + if (dma_length < 0) + { + // Disable SDRAM writes + usb_sc64_setwritable(FALSE); + + // Stop waiting for data if USB cable has been disconnected + return 0; + } + + // Load data from FIFO to buffer in RDRAM + #if USE_OSRAW + osPiRawStartDma(OS_READ, SC64_MEM_USB_FIFO_BASE, usb_buffer, dma_length); + #else + osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, SC64_MEM_USB_FIFO_BASE, usb_buffer, dma_length, &dmaMessageQ); + osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK); + #endif + + // Copy data from RDRAM to SDRAM + #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 + + // Update tracking variables + left -= dma_length; + sdram_address += dma_length; + } + + // Disable SDRAM writes + usb_sc64_setwritable(FALSE); + + // Return USB header + return USBHEADER_CREATE(usb_datatype, usb_dataleft); + } + + // Return no USB header if FIFO is empty + return 0; +} + + +/*============================== + usb_sc64_read + Reads bytes from the SummerCart64 ROM 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 + #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); +} diff --git a/src/usb/usb.h b/src/usb/usb.h new file mode 100644 index 00000000..b5bca55e --- /dev/null +++ b/src/usb/usb.h @@ -0,0 +1,119 @@ +#ifndef UNFL_USB_H +#define UNFL_USB_H + + /********************************* + DataType macros + *********************************/ + + // Settings + #define USE_OSRAW 0 // Use if you're doing USB operations without the PI Manager + #define DEBUG_ADDRESS_SIZE 1*1024*1024 // Max size of USB I/O. The bigger this value, the more ROM you lose! + + // 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; + + + + /********************************* + 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 + *********************************/ + + /*============================== + usb_initialize + 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. + Will not write if there is data to read from USB + @param The DATATYPE that is being sent + @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(); + +#endif \ No newline at end of file