Add very basic UNFLoader support (#169)

* basic unfloader support (no gdb yet)

* fix build issues
This commit is contained in:
Yanis
2025-11-04 20:38:45 +01:00
committed by GitHub
parent aca7849a0f
commit a843da9d79
15 changed files with 3998 additions and 0 deletions

View File

@@ -251,3 +251,15 @@ endif
# same as above and start listening to the IS-Viewer
sc64v: sc64
$(SC64_DEPLOYER) debug --isv 0x03FF0000
### UNFLoader Settings ###
# TODO: download this automatically
UNFLOADER ?= UNFLoader
# upload the build
unf: rom
ifeq ($(UNFLOADER),)
$(error sc64deployer path not set. Set UNFLOADER in the Makefile or define it as an environment variable)
endif
$(UNFLOADER) -r $(ROM) -d

View File

@@ -84,4 +84,10 @@
*/
#define ENABLE_DEBUG_BOOT true
/**
* Enable UNFLoader support
* This is required to use the command system.
*/
#define ENABLE_UNF true
#endif

View File

@@ -40,6 +40,7 @@
#undef ENABLE_MOTION_BLUR_DEBUG
#undef ENABLE_HACKER_DEBUG
#undef ENABLE_PROFILER
#undef ENABLE_UNF
#define SKIP_N64_BOOT_LOGO false
#define BOOT_TO_SCENE false
@@ -67,6 +68,7 @@
#define ENABLE_MOTION_BLUR_DEBUG false
#define ENABLE_HACKER_DEBUG false
#define ENABLE_PROFILER false
#define ENABLE_UNF false
#ifndef NDEBUG
#define NDEBUG

View File

@@ -92,6 +92,12 @@ DECLARE_ROM_SEGMENT(debug)
DECLARE_BSS_SEGMENT(debug)
#endif
#if ENABLE_UNF
DECLARE_SEGMENT(usb)
DECLARE_ROM_SEGMENT(usb)
DECLARE_BSS_SEGMENT(usb)
#endif
// N64-only, these are not wrapped in an `#if PLATFORM_N64`
// so that the N64DD code can always be built.
DECLARE_SEGMENT(n64dd)

180
include/usb/debug.h Normal file
View File

@@ -0,0 +1,180 @@
#ifndef UNFL_DEBUG_H
#define UNFL_DEBUG_H
/*********************************
Settings macros
*********************************/
// Enable/Disable debug
#ifndef DEBUG_MODE
#define DEBUG_MODE 1 // Enable/Disable debug mode
#endif
// Settings
#define DEBUG_INIT_MSG 1 // Print a message when debug mode has initialized
#define AUTOPOLL_ENABLED 1 // Automatically poll the USB on a timer
#define AUTOPOLL_TIME 200 // Time (in milliseconds) between auto polls
#define USE_FAULTTHREAD 1 // Create a fault detection thread (libultra only)
#define USE_RDBTHREAD 0 // Create a remote debugger thread
#define OVERWRITE_OSPRINT 1 // Replaces osSyncPrintf calls with debug_printf (libultra only)
#define MAX_COMMANDS 25 // The max amount of user defined commands possible
// USB thread definitions (libultra only)
#define USB_THREAD_ID 14
#define USB_THREAD_PRI 126
#define USB_THREAD_STACK 0x2000
// Fault thread definitions (libultra only)
#define FAULT_THREAD_ID 13
#define FAULT_THREAD_PRI 125
#define FAULT_THREAD_STACK 0x2000
// Remote debugger thread definitions (libultra only)
#define RDB_THREAD_ID 15
#define RDB_THREAD_PRI 124
#define RDB_THREAD_STACK 0x2000
/*********************************
Debug Functions
*********************************/
#if DEBUG_MODE
/*==============================
debug_initialize
Initializes the debug and USB library.
Should be called last during game initialization.
==============================*/
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_dumpbinary
Dumps a binary file through USB
@param The file to dump
@param The size of the file
==============================*/
extern void debug_dumpbinary(void* file, int size);
/*==============================
debug_screenshot
Sends the currently displayed framebuffer through USB.
DOES NOT PAUSE DRAWING THREAD! Using outside the drawing
thread may lead to a screenshot with visible tearing
==============================*/
extern void debug_screenshot();
/*==============================
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_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.
==============================*/
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 (void)
#define debug_screenshot(a, b, c)
#define debug_assert(a)
#define debug_pollcommands()
#define debug_addcommand(a, b, c)
#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)
#define usb_poll() 0
#define usb_read(a, b)
#define usb_skip(a)
#define usb_rewind(a)
#define usb_purge()
#endif
#endif

139
include/usb/usb.h Normal file
View File

@@ -0,0 +1,139 @@
#ifndef UNFL_USB_H
#define UNFL_USB_H
/*********************************
DataType macros
*********************************/
// UNCOMMENT THE #DEFINE IF USING LIBDRAGON
//#define LIBDRAGON
// Settings
#define USE_OSRAW 0 // Use if you're doing USB operations without the PI Manager (libultra only)
#define DEBUG_ADDRESS_SIZE 8*1024*1024 // Max size of USB I/O. The bigger this value, the more ROM you lose!
#define CHECK_EMULATOR 0 // Stops the USB library from working if it detects an emulator to prevent problems
// Cart definitions
#define CART_NONE 0
#define CART_64DRIVE 1
#define CART_EVERDRIVE 2
#define CART_SC64 3
// Data types defintions
#define DATATYPE_TEXT 0x01
#define DATATYPE_RAWBINARY 0x02
#define DATATYPE_HEADER 0x03
#define DATATYPE_SCREENSHOT 0x04
#define DATATYPE_HEARTBEAT 0x05
#define DATATYPE_RDBPACKET 0x06
/*********************************
Convenience macros
*********************************/
// Use these to conveniently read the header from usb_poll()
#define USBHEADER_GETTYPE(header) (((header) & 0xFF000000) >> 24)
#define USBHEADER_GETSIZE(header) (((header) & 0x00FFFFFF))
/*********************************
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(void);
/*==============================
usb_getcart
Returns which flashcart is currently connected
@return The CART macro that corresponds to the identified flashcart
==============================*/
extern char usb_getcart(void);
/*==============================
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 unsigned long usb_poll(void);
/*==============================
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 nbytes);
/*==============================
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(void);
/*==============================
usb_timedout
Checks if the USB timed out recently
@return 1 if the USB timed out, 0 if not
==============================*/
extern char usb_timedout(void);
/*==============================
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(void);
#endif

View File

@@ -53,6 +53,7 @@
include "$(BUILD_DIR)/src/libultra/gu/cosf.o"
include "$(BUILD_DIR)/src/libultra/gu/libm_vals.o"
include "$(BUILD_DIR)/src/libultra/gu/coss.o"
include "$(BUILD_DIR)/src/libultra/os/settime.o"
include "$(BUILD_DIR)/src/libultra/io/visetevent.o"
#if DEBUG_FEATURES
include "$(BUILD_DIR)/src/libultra/io/pfsisplug.o"

View File

@@ -877,6 +877,19 @@ beginseg
include "$(BUILD_DIR)/src/debug/profiler.o"
#endif
include "$(BUILD_DIR)/src/debug/inventory_editor.o"
#if ENABLE_UNF
include "$(BUILD_DIR)/src/debug/commands.o"
#endif
endseg
#endif
#if ENABLE_UNF
beginseg
name "usb"
compress
include "$(BUILD_DIR)/src/usb/missing_libultra_functions.o"
include "$(BUILD_DIR)/src/usb/usb.o"
include "$(BUILD_DIR)/src/usb/debug.o"
endseg
#endif

View File

@@ -70,6 +70,14 @@ void Main_ThreadEntry(void* arg) {
PRINTF("[HackerOoT:Info]: Completed!\n");
#endif
#if ENABLE_UNF
PRINTF("[HackerOoT:Info]: Loading 'usb' segment...\n");
DMA_REQUEST_SYNC(_usbSegmentStart, (uintptr_t)_usbSegmentRomStart, _usbSegmentRomEnd - _usbSegmentRomStart,
__BASE_FILE__, __LINE__);
bzero(_usbSegmentBssStart, _usbSegmentBssEnd - _usbSegmentBssStart);
PRINTF("[HackerOoT:Info]: Completed!\n");
#endif
Main(arg);
PRINTF(T("mainx 実行終了\n", "mainx execution finished\n"));
}

View File

@@ -474,6 +474,10 @@ void Graph_Update(GraphicsContext* gfxCtx, GameState* gameState) {
}
}
#if ENABLE_UNF
void Commands_Init(void);
#endif
void Graph_ThreadEntry(void* arg0) {
GraphicsContext gfxCtx;
GameState* gameState;
@@ -489,6 +493,10 @@ void Graph_ThreadEntry(void* arg0) {
PRINTF(T("グラフィックスレッド実行開始\n", "Start graphic thread execution\n"));
Graph_Init(&gfxCtx);
#if ENABLE_UNF
Commands_Init();
#endif
while (nextOvl != NULL) {
ovl = nextOvl;
Overlay_LoadGameState(ovl);

41
src/debug/commands.c Normal file
View File

@@ -0,0 +1,41 @@
#include "ultra64.h"
#include "array_count.h"
#include "usb/debug.h"
#include "config.h"
#if ENABLE_UNF
typedef struct CommandDesc {
char* name;
char* desc;
char* (*callback)(void);
} CommandDesc;
char* command_example() {
return "The example command executed successfully.";
}
// to add a command, create the function and add an entry for it here, you shouldn't require any other change
CommandDesc sCommandList[] = {
{ "example", "example command", command_example },
};
void Commands_Init(void) {
debug_initialize();
if (ARRAY_COUNT(sCommandList) < MAX_COMMANDS) {
u8 i;
for (i = 0; i < ARRAY_COUNT(sCommandList); i++) {
CommandDesc* cmd = &sCommandList[i];
debug_addcommand(cmd->name, cmd->desc, cmd->callback);
}
debug_printcommands();
} else {
debug_printf("Too many commands! Ignoring...");
}
}
#endif

View File

@@ -1,6 +1,8 @@
#include "ultra64.h"
#include "ultra64/bcp.h"
void* __printfunc = NULL;
typedef struct __osExceptionVector {
u32 inst1; // lui $k0, %hi(__osException)
u32 inst2; // addiu $k0, $k0, %lo(__osException)

1990
src/usb/debug.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
#include "ultra64.h"
#include "ultra64/internal.h"
char* strpbrk(const char* string1, const char* string2) {
char* b;
while (*string1) {
b = (char*)string2;
while (*b) {
if (*b++ == *string1) {
return (char*)string1;
}
}
string1++;
}
return NULL;
}
char* strtok(char* string1, const char* string2) {
static char* ptr = NULL;
char* a;
if (string1 != NULL) {
ptr = string1;
}
if (ptr == NULL) {
return NULL;
}
a = ptr;
ptr = strpbrk(ptr, string2);
if (ptr != NULL) {
*ptr++ = '\0';
}
return a;
}
char* strcpy(char* str1, const char* str2) {
char* p;
p = str1;
while (*str2) {
*p++ = *str2++;
}
*p = '\0';
return str1;
}
int strncmp(const char* s1, const char* s2, size_t n) {
while (n && *s1 && (*s1 == *s2)) {
++s1;
++s2;
--n;
}
if (n == 0) {
return 0;
} else {
return (*(unsigned char*)s1 - *(unsigned char*)s2);
}
}
#define WAIT_ON_IOBUSY(stat) \
{ \
stat = IO_READ(PI_STATUS_REG); \
while (stat & (PI_STATUS_IO_BUSY | PI_STATUS_DMA_BUSY)) \
stat = IO_READ(PI_STATUS_REG); \
} \
(void)0
s32 __osPiRawReadIo(u32 devAddr, u32* data) {
register u32 stat;
WAIT_ON_IOBUSY(stat);
*data = IO_READ((u32)osRomBase | devAddr);
return 0;
}
s32 __osPiRawWriteIo(u32 devAddr, u32 data) {
register u32 stat;
WAIT_ON_IOBUSY(stat);
IO_WRITE((u32)osRomBase | devAddr, data);
return 0;
}
s32 osPiWriteIo(u32 devAddr, u32 data) {
register s32 ret;
#ifdef _DEBUG
if (devAddr & 0x3) {
__osError(ERR_OSPIWRITEIO, 1, devAddr);
return -1;
}
#endif
__osPiGetAccess();
ret = __osPiRawWriteIo(devAddr, data);
__osPiRelAccess();
return ret;
}
s32 osPiReadIo(u32 devAddr, u32* data) {
register s32 ret;
#ifdef _DEBUG
if (devAddr & 0x3) {
__osError(ERR_OSPIREADIO, 1, devAddr);
return -1;
}
#endif
__osPiGetAccess();
ret = __osPiRawReadIo(devAddr, data);
__osPiRelAccess();
return ret;
}
s32 osPiStartDma(OSIoMesg* mb, s32 priority, s32 direction, u32 devAddr, void* dramAddr, u32 size, OSMesgQueue* mq) {
register s32 ret;
if (!__osPiDevMgr.active) {
return -1;
}
if (direction == OS_READ) {
mb->hdr.type = OS_MESG_TYPE_DMAREAD;
} else {
mb->hdr.type = OS_MESG_TYPE_DMAWRITE;
}
mb->hdr.pri = priority;
mb->hdr.retQueue = mq;
mb->dramAddr = dramAddr;
mb->devAddr = devAddr;
mb->size = size;
mb->piHandle = NULL;
if (priority == OS_MESG_PRI_HIGH) {
ret = osJamMesg(osPiGetCmdQueue(), (OSMesg)mb, OS_MESG_NOBLOCK);
} else {
ret = osSendMesg(osPiGetCmdQueue(), (OSMesg)mb, OS_MESG_NOBLOCK);
}
return ret;
}

1444
src/usb/usb.c Normal file

File diff suppressed because it is too large Load Diff