diff options
| author | Derek Hensley <hensley.derek58@gmail.com> | 2023-08-28 09:59:31 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-28 09:59:31 -0700 |
| commit | 1e3511efea9cf4379a04fca77f6a00cb170da28c (patch) | |
| tree | 7650c04105d35d867939d19e3421102ae511102f | |
| parent | fcfbb6c52feec4a01b46f0c2bcc037af373d4fe9 (diff) | |
Fault + Fault_drawer (#77)
* Fault OK
* fault_drawer OK
* Cleanup
* Cleanup pt 2
* Forgot to save
* PR
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | include/boot_variables.h | 7 | ||||
| -rw-r--r-- | include/fault.h | 128 | ||||
| -rw-r--r-- | include/m_thread.h | 3 | ||||
| -rw-r--r-- | include/terminal.h | 40 | ||||
| -rw-r--r-- | linker_scripts/jp/symbol_addrs_boot.txt | 113 | ||||
| -rw-r--r-- | src/boot/fault.c | 1333 | ||||
| -rw-r--r-- | src/boot/fault_drawer.c | 318 | ||||
| -rw-r--r-- | src/boot/ovlmgr.c | 2 | ||||
| -rw-r--r-- | src/boot/z_std_dma.c | 2 | ||||
| -rw-r--r-- | src/code/m_actor.c | 4 | ||||
| -rw-r--r-- | src/code/m_actor_dlftbls.c | 8 | ||||
| -rw-r--r-- | src/code/m_malloc.c | 4 | ||||
| -rw-r--r-- | src/code/m_submenu.c | 8 | ||||
| -rw-r--r-- | yamls/jp/boot.yaml | 13 |
15 files changed, 1840 insertions, 146 deletions
@@ -226,6 +226,9 @@ build/src/boot/libc64/%.o: OPTFLAGS := -O2 # per-file flags +build/src/boot/fault.o: CFLAGS += -trapuv +build/src/boot/fault_drawer.o: CFLAGS += -trapuv + # cc & asm-processor build/src/%.o: CC := $(ASM_PROC) $(ASM_PROC_FLAGS) $(CC) -- $(AS) $(ASFLAGS) -- diff --git a/include/boot_variables.h b/include/boot_variables.h index 8345c2c..8aa1cbe 100644 --- a/include/boot_variables.h +++ b/include/boot_variables.h @@ -14,8 +14,6 @@ // extern UNK_TYPE D_FLT_8003BBCC_jp; extern OSPiHandle* carthandle; // TODO: Determine where this goes -// extern UNK_TYPE D_8003BBF0_jp; -// extern UNK_TYPE D_8003BC38_jp; // extern UNK_TYPE D_8003BC50_jp; // extern UNK_TYPE D_8003BC90_jp; // extern UNK_TYPE D_8003BC94_jp; @@ -245,11 +243,6 @@ extern OSPiHandle* carthandle; // TODO: Determine where this goes // extern UNK_TYPE B_80040AC8_jp; // extern UNK_TYPE B_80040ACC_jp; // extern UNK_TYPE B_80040AD0_jp; -// extern UNK_TYPE B_80040AE0_jp; -// extern UNK_TYPE B_80040AE4_jp; -// extern UNK_TYPE B_80040AE8_jp; -// extern UNK_TYPE B_800410E8_jp; -// extern UNK_TYPE B_80041108_jp; // extern UNK_TYPE B_800418D6_jp; extern struct_800418D8* B_800418D8_jp; // extern UNK_TYPE B_80041960_jp; diff --git a/include/fault.h b/include/fault.h index b24f732..4cf3c51 100644 --- a/include/fault.h +++ b/include/fault.h @@ -2,8 +2,31 @@ #define FAULT_H #include "ultra64.h" +#include "unk.h" #include "attributes.h" +#include "libc/stdarg.h" #include "libc/stdint.h" +#include "libu64/pad.h" +#include "stack.h" + +// These are the same as the 3-bit ansi color codes +#define FAULT_COLOR_BLACK 0 +#define FAULT_COLOR_RED 1 +#define FAULT_COLOR_GREEN 2 +#define FAULT_COLOR_YELLOW 3 +#define FAULT_COLOR_BLUE 4 +#define FAULT_COLOR_MAGENTA 5 +#define FAULT_COLOR_CYAN 6 +#define FAULT_COLOR_WHITE 7 +// Additional color codes +#define FAULT_COLOR_DARK_GRAY 8 +#define FAULT_COLOR_LIGHT_GRAY 9 + +#define FAULT_COLOR_STRINGIFY(s) #s +#define FAULT_COLOR_EXPAND_AND_STRINGIFY(s) FAULT_COLOR_STRINGIFY(s) + +#define FAULT_ESC '\x1A' +#define FAULT_COLOR(n) "\x1A" FAULT_COLOR_EXPAND_AND_STRINGIFY(FAULT_COLOR_ ## n) typedef void (*FaultClientCallback)(void*, void*); @@ -26,67 +49,52 @@ typedef struct FaultAddrConvClient { /* 0x8 */ void* arg; } FaultAddrConvClient; // size = 0xC -// void Fault_SleepImpl(); -// void Fault_ClientProcessThread(); -// void Fault_ClientRunTask(); -// void Fault_ProcessClient(); -void Fault_AddClient(FaultClient* client, FaultClientCallback callback, void* arg0, void* arg1); -void Fault_RemoveClient(FaultClient* client); -void Fault_AddAddrConvClient(FaultAddrConvClient* client, FaultAddrConvClientCallback callback, void* arg); -void Fault_RemoveAddrConvClient(FaultAddrConvClient* client); -// void Fault_ConvertAddress(); -// void Fault_Sleep(); -// void Fault_PadCallback(); -// void Fault_UpdatePadImpl(); -// void Fault_WaitForInputImpl(); -// void Fault_WaitForInput(); -// void Fault_DrawRec(); -// void Fault_FillScreenBlack(); -// void Fault_FillScreenRed(); -// void Fault_DrawCornerRec(); -// void Fault_PrintFReg(); -// void Fault_LogFReg(); -// void Fault_PrintFPCSR(); -// void Fault_LogFPCSR(); -// void Fault_PrintThreadContext(); -// void Fault_LogThreadContext(); -// void Fault_FindFaultedThread(); -// void Fault_Wait5Seconds(); -// void Fault_WaitForButtonCombo(); -// void Fault_DrawMemDumpContents(); -// void Fault_DrawMemDump(); -// void Fault_WalkStack(); -// void Fault_DrawStackTrace(); -// void Fault_LogStackTrace(); -// void Fault_ResumeThread(); -// void Fault_DisplayFrameBuffer(); -// void Fault_ProcessClients(); -// void Fault_UpdatePad(); -// void Fault_ThreadEntry(); -// void Fault_SetFrameBuffer(); -// void Fault_Init(); -// void Fault_HungupFaultClient(); -NORETURN void Fault_AddHungupAndCrashImpl(const char* exp1, const char* exp2); -// NORETURN void Fault_AddHungupAndCrash(); - -// void FaultDrawer_SetOsSyncPrintfEnabled(); -// void FaultDrawer_DrawRecImpl(); -// void FaultDrawer_DrawChar(); -// void FaultDrawer_ColorToPrintColor(); -// void FaultDrawer_UpdatePrintColor(); -// void FaultDrawer_SetForeColor(); -// void FaultDrawer_SetBackColor(); +typedef struct FaultMgr { + /* 0x000 */ OSThread thread; + /* 0x1B0 */ STACK(stack, 0x600); // probably an unused internal thread stack for `fault_ClientRunTask`/`clientThreadSp` + /* 0x7B0 */ OSMesgQueue queue; + /* 0x7C8 */ OSMesg msg[1]; + /* 0x7CC */ u8 exit; + /* 0x7CD */ u8 msgId; + /* 0x7CE */ u8 faultHandlerEnabled; + /* 0x7CF */ u8 autoScroll; + /* 0x7D0 */ OSThread* faultedThread; + /* 0x7D4 */ void (*padCallback)(Input* inputs); + /* 0x7D8 */ FaultClient* clients; + /* 0x7DC */ FaultAddrConvClient* addrConvClients; + /* 0x7E0 */ UNK_TYPE1 unk_7E0[0x4]; + /* 0x7E4 */ Input inputs[MAXCONTROLLERS]; + /* 0x844 */ void* fb; + /* 0x848 */ void* clientThreadSp; +} FaultMgr; // size = 0x850 + + +void fault_AddClient(FaultClient* client, FaultClientCallback callback, void* arg0, void* arg1); +void fault_RemoveClient(FaultClient* client); +void fault_AddressConverterAddClient(FaultAddrConvClient* client, FaultAddrConvClientCallback callback, void* arg); +void fault_AddressConverterRemoveClient(FaultAddrConvClient* client); +void fault_WaitForInput(void); +void fault_FillScreenBlack(void); +void fault_SetFrameBuffer(void* fb, u16 w, u16 h); +void fault_Init(void); +NORETURN void fault_AddHungupAndCrashImpl(const char* exp1, const char* exp2); +NORETURN void fault_AddHungupAndCrash(const char* file, s32 line); + +void FaultDrawer_SetOsSyncPrintfEnabled(u32 enabled); +void FaultDrawer_DrawRecImpl(s32 xStart, s32 yStart, s32 xEnd, s32 yEnd, u16 color); +void FaultDrawer_SetForeColor(u16 color); +void FaultDrawer_SetBackColor(u16 color); void FaultDrawer_SetFontColor(u16 color); void FaultDrawer_SetCharPad(s8 padW, s8 padH); -void FaultDrawer_SetCursor(s32 arg0, s32 arg1); -// void FaultDrawer_FillScreen(); -// void FaultDrawer_PrintCallback(); -// void FaultDrawer_VPrintf(); -void FaultDrawer_Printf(const char* fmt, ...); -// void FaultDrawer_DrawText(); -// void FaultDrawer_SetDrawerFB(); -// void FaultDrawer_SetInputCallback(); -// void FaultDrawer_WritebackFBDCache(); -// void FaultDrawer_Init(); +void FaultDrawer_SetCursor(s32 x, s32 y); +void FaultDrawer_FillScreen(void); +s32 FaultDrawer_VPrintf(const char* fmt, va_list args); +s32 FaultDrawer_Printf(const char* fmt, ...); +void FaultDrawer_DrawText(s32 x, s32 y, const char* fmt, ...); +void FaultDrawer_SetDrawerFB(void* fb, u16 w, u16 h); +void FaultDrawer_SetInputCallback(void (*callback)(void)); +void FaultDrawer_Init(void); + +extern FaultMgr gFaultMgr; #endif diff --git a/include/m_thread.h b/include/m_thread.h index 45cb851..19f6e1f 100644 --- a/include/m_thread.h +++ b/include/m_thread.h @@ -2,6 +2,7 @@ #define M_THREAD_H #define M_THREAD_ID_IDLE 1 +#define M_THREAD_ID_FAULT 2 #define M_THREAD_ID_MAIN 3 #define M_THREAD_ID_DMAMGR 8 #define M_THREAD_ID_IRQMGR 9 @@ -10,6 +11,8 @@ #define M_PRIORITY_IDLE 12 #define M_PRIORITY_MAIN 12 #define M_PRIORITY_DMAMGR 17 +#define M_PRIORITY_FAULT_CLIENT (OS_PRIORITY_APPMAX - 1) +#define M_PRIORITY_FAULT OS_PRIORITY_APPMAX #endif diff --git a/include/terminal.h b/include/terminal.h new file mode 100644 index 0000000..f4ed8d6 --- /dev/null +++ b/include/terminal.h @@ -0,0 +1,40 @@ +#ifndef TERMINAL_H +#define TERMINAL_H + +// VT_: Helper macros for printing colored text to terminals, using ANSI escape codes +// VT stands for Virtual Terminal + +// 3-bit color codes +#define VT_COLOR_BLACK 0 +#define VT_COLOR_RED 1 +#define VT_COLOR_GREEN 2 +#define VT_COLOR_YELLOW 3 +#define VT_COLOR_BLUE 4 +#define VT_COLOR_MAGENTA 5 +#define VT_COLOR_CYAN 6 +#define VT_COLOR_WHITE 7 + +#define VT_COLOR_FOREGROUND 3 +#define VT_COLOR_BACKGROUND 4 + +#define VT_COLOR_EXPAND0(type, color) #type #color +#define VT_COLOR_EXPAND1(type, color) VT_COLOR_EXPAND0(type, color) +#define VT_COLOR(type, color) VT_COLOR_EXPAND1(VT_COLOR_##type, VT_COLOR_##color) + +#define VT_ESC "\x1b" +#define VT_CSI "[" +#define VT_CUP(x, y) VT_ESC VT_CSI y ";" x "H" +#define VT_ED(n) VT_ESC VT_CSI #n "J" +#define VT_SGR(n) VT_ESC VT_CSI n "m" + +// Add more macros if necessary +#define VT_COL(back, fore) VT_SGR(VT_COLOR(BACKGROUND, back) ";" VT_COLOR(FOREGROUND, fore)) +#define VT_FGCOL(color) VT_SGR(VT_COLOR(FOREGROUND, color)) +#define VT_BGCOL(color) VT_SGR(VT_COLOR(BACKGROUND, color)) +#define VT_RST VT_SGR("") +#define VT_CLS VT_ED(2) + +// ASCII BEL character, plays an alert tone +#define BEL '\a' + +#endif diff --git a/linker_scripts/jp/symbol_addrs_boot.txt b/linker_scripts/jp/symbol_addrs_boot.txt index 38ae7bc..654bfe8 100644 --- a/linker_scripts/jp/symbol_addrs_boot.txt +++ b/linker_scripts/jp/symbol_addrs_boot.txt @@ -46,48 +46,48 @@ osSyncPrintfUnused = 0x800272C0; // type:func osSyncPrintf = 0x800272DC; // type:func rmonPrintf = 0x800272F8; // type:func -Fault_SleepImpl = 0x80027320; // type:func -Fault_ClientProcessThread = 0x80027390; // type:func -Fault_ClientRunTask = 0x80027404; // type:func -Fault_ProcessClient = 0x80027588; // type:func -Fault_AddClient = 0x800275DC; // type:func -Fault_RemoveClient = 0x800276C0; // type:func -Fault_AddAddrConvClient = 0x800277B8; // type:func -Fault_RemoveAddrConvClient = 0x80027890; // type:func -Fault_ConvertAddress = 0x80027988; // type:func -Fault_Sleep = 0x80027A3C; // type:func -Fault_PadCallback = 0x80027A78; // type:func -Fault_UpdatePadImpl = 0x80027AB4; // type:func -Fault_WaitForInputImpl = 0x80027B00; // type:func -Fault_WaitForInput = 0x80027C4C; // type:func -Fault_DrawRec = 0x80027C98; // type:func -Fault_FillScreenBlack = 0x80027CF8; // type:func -Fault_FillScreenRed = 0x80027D4C; // type:func -Fault_DrawCornerRec = 0x80027DA0; // type:func -Fault_PrintFReg = 0x80027DF4; // type:func -Fault_LogFReg = 0x80027EA0; // type:func -Fault_PrintFPCSR = 0x80027F50; // type:func -Fault_LogFPCSR = 0x80027FF4; // type:func -Fault_PrintThreadContext = 0x8002808C; // type:func -Fault_LogThreadContext = 0x800283A8; // type:func -Fault_FindFaultedThread = 0x800286A4; // type:func -Fault_Wait5Seconds = 0x8002872C; // type:func -Fault_WaitForButtonCombo = 0x800287BC; // type:func -Fault_DrawMemDumpContents = 0x80028A88; // type:func -Fault_DrawMemDump = 0x80028BF4; // type:func -Fault_WalkStack = 0x80028DF8; // type:func -Fault_DrawStackTrace = 0x80028F88; // type:func -Fault_LogStackTrace = 0x80029108; // type:func -Fault_ResumeThread = 0x80029278; // type:func -Fault_DisplayFrameBuffer = 0x800292F4; // type:func -Fault_ProcessClients = 0x800293B4; // type:func -Fault_UpdatePad = 0x80029494; // type:func -Fault_ThreadEntry = 0x800294D0; // type:func -Fault_SetFrameBuffer = 0x80029830; // type:func -Fault_Init = 0x80029884; // type:func -Fault_HungupFaultClient = 0x800299B8; // type:func -Fault_AddHungupAndCrashImpl = 0x80029AB4; // type:func -Fault_AddHungupAndCrash = 0x80029B0C; // type:func +fault_SleepImpl = 0x80027320; // type:func +fault_ClientProcessThread = 0x80027390; // type:func +fault_ClientRunTask = 0x80027404; // type:func +fault_ProcessClient = 0x80027588; // type:func +fault_AddClient = 0x800275DC; // type:func +fault_RemoveClient = 0x800276C0; // type:func +fault_AddressConverterAddClient = 0x800277B8; // type:func +fault_AddressConverterRemoveClient = 0x80027890; // type:func +fault_ConvertAddress = 0x80027988; // type:func +fault_Sleep = 0x80027A3C; // type:func +fault_PadCallback = 0x80027A78; // type:func +fault_UpdatePadImpl = 0x80027AB4; // type:func +fault_WaitForInputImpl = 0x80027B00; // type:func +fault_WaitForInput = 0x80027C4C; // type:func +fault_DrawRec = 0x80027C98; // type:func +fault_FillScreenBlack = 0x80027CF8; // type:func +fault_FillScreenRed = 0x80027D4C; // type:func +fault_DrawCornerRec = 0x80027DA0; // type:func +fault_PrintFReg = 0x80027DF4; // type:func +fault_LogFReg = 0x80027EA0; // type:func +fault_PrintFPCSR = 0x80027F50; // type:func +fault_LogFPCSR = 0x80027FF4; // type:func +fault_PrintThreadContext = 0x8002808C; // type:func +fault_LogThreadContext = 0x800283A8; // type:func +FindFaultedThread = 0x800286A4; // type:func +fault_Wait5Seconds = 0x8002872C; // type:func +fault_WaitForButtonCombo = 0x800287BC; // type:func +fault_DrawMemDumpContents = 0x80028A88; // type:func +fault_DrawMemDump = 0x80028BF4; // type:func +fault_WalkStack = 0x80028DF8; // type:func +fault_DrawStackTrace = 0x80028F88; // type:func +fault_LogStackTrace = 0x80029108; // type:func +fault_ResumeThread = 0x80029278; // type:func +fault_DisplayFrameBuffer = 0x800292F4; // type:func +fault_ProcessClients = 0x800293B4; // type:func +fault_UpdatePad = 0x80029494; // type:func +fault_ThreadEntry = 0x800294D0; // type:func +fault_SetFrameBuffer = 0x80029830; // type:func +fault_Init = 0x80029884; // type:func +fault_HungupFaultClient = 0x800299B8; // type:func +fault_AddHungupAndCrashImpl = 0x80029AB4; // type:func +fault_AddHungupAndCrash = 0x80029B0C; // type:func FaultDrawer_SetOsSyncPrintfEnabled = 0x80029B70; // type:func FaultDrawer_DrawRecImpl = 0x80029B80; // type:func @@ -272,8 +272,8 @@ gViConfigXScale = 0x8003BBC8; // type:f32 gViConfigYScale = 0x8003BBCC; // type:f32 carthandle = 0x8003BBD0; // gDmaMgrDmaBuffSize = 0x8003BBE0; // -D_8003BBF0_jp = 0x8003BBF0; // -D_8003BC38_jp = 0x8003BC38; // +sExceptionNames = 0x8003BBF0; // +sFpExceptionNames = 0x8003BC38; // D_8003BC50_jp = 0x8003BC50; // D_8003BC90_jp = 0x8003BC90; // size:0x4 @@ -458,7 +458,7 @@ RO_STR_8003DE00_jp = 0x8003DE00; // type:asciz RO_STR_8003DE04_jp = 0x8003DE04; // type:asciz RO_STR_8003DE0C_jp = 0x8003DE0C; // type:asciz jtbl_8003DE1C_jp = 0x8003DE1C; // type:jtbl -RO_8003DE50_jp = 0x8003DE50; // +sFaultDrawerFont = 0x8003DE50; // RO_STR_8003E250_jp = 0x8003E250; // type:asciz RO_STR_8003E254_jp = 0x8003E254; // type:asciz RO_STR_8003E25C_jp = 0x8003E25C; // type:asciz @@ -545,21 +545,14 @@ B_80040AC4_jp = 0x80040AC4; // B_80040AC8_jp = 0x80040AC8; // B_80040ACC_jp = 0x80040ACC; // B_80040AD0_jp = 0x80040AD0; // -B_80040AE0_jp = 0x80040AE0; // -B_80040AE4_jp = 0x80040AE4; // type:u8 -B_80040AE8_jp = 0x80040AE8; // -B_800410E8_jp = 0x800410E8; // -B_80041108_jp = 0x80041108; // type:u8 -B_800418D6_jp = 0x800418D6; // type:s8 -B_800418D8_jp = 0x800418D8; // -B_80041960_jp = 0x80041960; // type:s8 -B_80041964_jp = 0x80041964; // type:u16 -B_80041970_jp = 0x80041970; // type:u16 -B_80041972_jp = 0x80041972; // type:u16 -B_80041980_jp = 0x80041980; // type:u16 -B_80041982_jp = 0x80041982; // type:u16 -B_80041995_jp = 0x80041995; // type:s8 -B_80041998_jp = 0x80041998; // + +sFaultInstance = 0x80040AE0; // +sFaultAwaitingInput = 0x80040AE4; // type:u8 +sFaultStack = 0x80040AE8; // +sFaultThreadInfo = 0x800410E8; // +gFaultMgr = 0x80041108; // size:0x850 + +sFaultDrawer = 0x80041960; // size:0x3C __gfxprint_default_flags = 0x800419A0; // type:u8 size:0x1 diff --git a/src/boot/fault.c b/src/boot/fault.c new file mode 100644 index 0000000..9844630 --- /dev/null +++ b/src/boot/fault.c @@ -0,0 +1,1333 @@ +/** + * @file fault.c + * + * This file implements the screen that may be viewed when the game crashes. + * This is the second version of the crash screen, originally used in Majora's Mask. + * + * When the game crashes, a red bar will be drawn to the top-left of the screen, indicating that the + * crash screen is available for use. Once this bar appears, it is possible to open the crash screen + * with the following button combination: + * + * (L & R & Z) + DPad-Up + C-Down + C-Up + DPad-Down + DPad-Left + C-Left + C-Right + DPad-Right + (B & A & START) + * + * When entering this button combination, buttons that are &'d together must all be pressed together. + * The L & R presses and B & A presses may be interchanged in the order they are pressed. + * + * "Clients" may be registered with the crash screen to extend its functionality. There are + * two kinds of client, "Client" and "AddressConverterClient". Clients contribute one or + * more pages to the crash debugger, while Address Converter Clients allow the crash screen to look up + * the virtual addresses of dynamically allocated overlays. + * + * The crash screen has multiple pages: + * - Thread Context + * This page shows information about the thread on which the program crashed. It displays + * the cause of the crash, state of general-purpose registers, state of floating-point registers + * and the floating-point status register. If a floating-point exception caused the crash, it will + * be displayed next to the floating-point status register. + * - Stack Trace + * This page displays a full backtrace from the crashing function back to the start of the thread. It + * displays the Program Counter for each function and, if applicable, the Virtual Program Counter + * for relocated functions in overlays. + * - Client Pages + * After the stack trace page, currently registered clients are processed and their pages are displayed. + * - Memory Dump + * This page implements a scrollable memory dump. + * - End Screen + * This page informs you that there are no more pages to display. + * + * To navigate the pages, START and A may be used to advance to the next page, and L toggles whether to + * automatically scroll to the next page after some time has passed. + * DPad-Up may be pressed to enable sending fault pages over osSyncPrintf as well as displaying them on-screen. + * DPad-Down disables sending fault pages over osSyncPrintf. + */ +#include "libc/stdbool.h" +#include "PR/os_internal.h" +#include "libc/alloca.h" +#include "libc64/sleep.h" +#include "m_controller.h" +#include "m_thread.h" +#include "macros.h" +#include "terminal.h" +#include "padmgr.h" +#include "stack.h" +#include "libu64/stackcheck.h" + +// Bss put before fault.h as a hacky way to bypass bss reordering +struct FaultMgr* sFaultInstance; +u8 sFaultAwaitingInput; +STACK(sFaultStack, 0x600); +StackEntry sFaultThreadInfo; +struct FaultMgr gFaultMgr; + +#include "fault.h" + +const char* sExceptionNames[] = { + "Interrupt", + "TLB modification", + "TLB exception on load", + "TLB exception on store", + "Address error on load", + "Address error on store", + "Bus error on inst.", + "Bus error on data", + "System call exception", + "Breakpoint exception", + "Reserved instruction", + "Coprocessor unusable", + "Arithmetic overflow", + "Trap exception", + "Virtual coherency on inst.", + "Floating point exception", + "Watchpoint exception", + "Virtual coherency on data", +}; + +const char* sFpExceptionNames[] = { + "Unimplemented operation", "Invalid operation", "Division by zero", "Overflow", "Underflow", "Inexact operation", +}; + +// TODO: Should be in a proper header somewhere else? +extern void __osCleanupThread(void); + +typedef struct { + /* 0x00 */ s32 (*callback)(void*, void*); + /* 0x04 */ void* arg0; + /* 0x08 */ void* arg1; + /* 0x0C */ s32 ret; + /* 0x10 */ OSMesgQueue* queue; + /* 0x14 */ OSMesg msg; +} FaultClientTask; // size = 0x18 + +void fault_SleepImpl(u32 msec) { + u64 cycles = (msec * OS_CPU_COUNTER) / 1000ULL; + + csleep(cycles); +} + +void fault_ClientProcessThread(void* arg) { + FaultClientTask* task = (FaultClientTask*)arg; + + // Run the callback + if (task->callback != NULL) { + task->ret = task->callback(task->arg0, task->arg1); + } + + // Send completion notification + if (task->queue != NULL) { + osSendMesg(task->queue, task->msg, OS_MESG_BLOCK); + } +} + +void fault_ClientRunTask(FaultClientTask* task) { + OSMesgQueue queue; + OSMesg msg[1]; + OSMesg recMsg; + OSThread* thread = NULL; + OSTimer timer; + u32 timerMsgVal = 666; + + osCreateMesgQueue(&queue, msg, ARRAY_COUNT(msg)); + task->queue = &queue; + task->msg = NULL; + + if (sFaultInstance->clientThreadSp != NULL) { + // Run the fault client callback on a separate thread + thread = alloca(sizeof(OSThread)); + + osCreateThread(thread, M_THREAD_ID_FAULT, fault_ClientProcessThread, task, sFaultInstance->clientThreadSp, + M_PRIORITY_FAULT_CLIENT); + osStartThread(thread); + } else { + // Run the fault client callback on this thread + fault_ClientProcessThread(task); + } + + // Await done + while (true) { + osSetTimer(&timer, OS_USEC_TO_CYCLES(1000000), 0, &queue, (OSMesg)timerMsgVal); + osRecvMesg(&queue, &recMsg, OS_MESG_BLOCK); + + if (recMsg != (OSMesg)666) { + break; + } + + if (!sFaultAwaitingInput) { + task->ret = -1; + break; + } + } + + osStopTimer(&timer); + + // Destroy thread if a thread was used + if (thread != NULL) { + osStopThread(thread); + osDestroyThread(thread); + } +} + +s32 fault_ProcessClient(void* callback, void* arg0, void* arg1) { + FaultClientTask task; + + task.callback = callback; + task.arg0 = arg0; + task.arg1 = arg1; + task.ret = 0; + fault_ClientRunTask(&task); + return task.ret; +} + +/** + * Registers a fault client. + * + * Clients contribute at least one page to the crash screen, drawn by `callback`. + * Arguments are passed on to the callback through `arg0` and `arg1`. + * + * The callback is intended to be + * `void (*callback)(void* arg0, void* arg1)` + */ +void fault_AddClient(FaultClient* client, FaultClientCallback callback, void* arg0, void* arg1) { + OSIntMask mask; + s32 alreadyExists = false; + + mask = osSetIntMask(OS_IM_NONE); + + // Ensure the client is not already registered + { + FaultClient* iterClient = sFaultInstance->clients; + + while (iterClient != NULL) { + if (iterClient == client) { + alreadyExists = true; + goto end; + } + iterClient = iterClient->next; + } + } + + client->callback = callback; + client->arg0 = arg0; + client->arg1 = arg1; + client->next = sFaultInstance->clients; + sFaultInstance->clients = client; + +end: + osSetIntMask(mask); + if (alreadyExists) { + // "fault_AddClient: %08x is already in the list" + osSyncPrintf(VT_COL(RED, WHITE) "fault_AddClient: %08x は既にリスト中にある\n" VT_RST, client); + } +} + +/** + * Removes a fault client so that the page is no longer displayed if a crash occurs. + */ +void fault_RemoveClient(FaultClient* client) { + FaultClient* iterClient = sFaultInstance->clients; + FaultClient* lastClient = NULL; + OSIntMask mask; + s32 listIsEmpty = false; + + mask = osSetIntMask(OS_IM_NONE); + + while (iterClient != NULL) { + if (iterClient == client) { + if (lastClient != NULL) { + lastClient->next = client->next; + } else { + sFaultInstance->clients = client; + if (sFaultInstance->clients) { + sFaultInstance->clients = client->next; + } else { + listIsEmpty = true; + } + } + break; + } + lastClient = iterClient; + iterClient = iterClient->next; + } + + osSetIntMask(mask); + + if (listIsEmpty) { + // "fault_RemoveClient: %08x list inconsistent" + osSyncPrintf(VT_COL(RED, WHITE) "fault_RemoveClient: %08x リスト不整合です\n" VT_RST, client); + } +} + +/** + * Registers an address converter client. This enables the crash screen to look up virtual + * addresses of overlays relocated during runtime. Address conversion is carried out by + * `callback`, which either returns a virtual address or NULL if the address could not + * be converted. + * + * The callback is intended to be + * `uintptr_t (*callback)(uintptr_t addr, void* arg)` + * The callback may return 0 if it could not convert the address + * The callback may return -1 to be unregistered + */ +void fault_AddressConverterAddClient(FaultAddrConvClient* client, FaultAddrConvClientCallback callback, void* arg) { + OSIntMask mask; + s32 alreadyExists = false; + + mask = osSetIntMask(OS_IM_NONE); + + // Ensure the client is not already registered + { + FaultAddrConvClient* iterClient = sFaultInstance->addrConvClients; + + while (iterClient != NULL) { + if (iterClient == client) { + alreadyExists = true; + goto end; + } + iterClient = iterClient->next; + } + } + + client->callback = callback; + client->arg = arg; + client->next = sFaultInstance->addrConvClients; + sFaultInstance->addrConvClients = client; + +end: + osSetIntMask(mask); + if (alreadyExists) { + // "fault_AddressConverterAddClient: %08x is already in the list" + osSyncPrintf(VT_COL(RED, WHITE) "fault_AddressConverterAddClient: %08x は既にリスト中にある\n" VT_RST, client); + } +} + +void fault_AddressConverterRemoveClient(FaultAddrConvClient* client) { + FaultAddrConvClient* iterClient = sFaultInstance->addrConvClients; + FaultAddrConvClient* lastClient = NULL; + OSIntMask mask; + s32 listIsEmpty = false; + + mask = osSetIntMask(OS_IM_NONE); + + while (iterClient != NULL) { + if (iterClient == client) { + if (lastClient != NULL) { + lastClient->next = client->next; + } else { + sFaultInstance->addrConvClients = client; + + if (sFaultInstance->addrConvClients != NULL) { + sFaultInstance->addrConvClients = client->next; + } else { + listIsEmpty = true; + } + } + break; + } + lastClient = iterClient; + iterClient = iterClient->next; + } + + osSetIntMask(mask); + + if (listIsEmpty) { + // "fault_AddressConverterRemoveClient: %08x is already in the list" + osSyncPrintf(VT_COL(RED, WHITE) "fault_AddressConverterRemoveClient: %08x は既にリスト中にある\n" VT_RST, + client); + } +} + +/** + * Converts `addr` to a virtual address via the registered + * address converter clients + */ +uintptr_t fault_ConvertAddress(uintptr_t addr) { + s32 ret; + FaultAddrConvClient* client = sFaultInstance->addrConvClients; + + while (client != NULL) { + if (client->callback != NULL) { + ret = fault_ProcessClient(client->callback, (void*)addr, client->arg); + if (ret == -1) { + fault_AddressConverterRemoveClient(client); + } else if (ret != 0) { + return (uintptr_t)ret; + } + } + client = client->next; + } + + return 0; +} + +void fault_Sleep(u32 msec) { + fault_SleepImpl(msec); +} + +void fault_PadCallback(Input* inputs) { + padmgr_RequestPadData(inputs, false); +} + +void fault_UpdatePadImpl(void) { + sFaultInstance->padCallback(sFaultInstance->inputs); +} + +/** + * Awaits user input + * + * L toggles auto-scroll + * DPad-Up enables osSyncPrintf output + * DPad-Down disables osSyncPrintf output + * A and DPad-Right continues and returns true + * DPad-Left continues and returns false + */ +u32 fault_WaitForInputImpl(void) { + Input* input = &sFaultInstance->inputs[0]; + s32 count = 600; + u32 pressedBtn; + + while (true) { + fault_Sleep(1000 / 60); + fault_UpdatePadImpl(); + + pressedBtn = input->press.button; + + if (pressedBtn == L_TRIG) { + sFaultInstance->autoScroll = !sFaultInstance->autoScroll; + } + + if (sFaultInstance->autoScroll) { + if (count-- < 1) { + return false; + } + } else { + if (pressedBtn == A_BUTTON || pressedBtn == R_JPAD) { + return false; + } + + if (pressedBtn == L_JPAD) { + return true; + } + + if (pressedBtn == U_JPAD) { + FaultDrawer_SetOsSyncPrintfEnabled(true); + } + + if (pressedBtn == D_JPAD) { + FaultDrawer_SetOsSyncPrintfEnabled(false); + } + } + } +} + +void fault_WaitForInput(void) { + sFaultAwaitingInput = true; + fault_WaitForInputImpl(); + sFaultAwaitingInput = false; +} + +void fault_DrawRec(s32 x, s32 y, s32 w, s32 h, u16 color) { + FaultDrawer_DrawRecImpl(x, y, x + w - 1, y + h - 1, color); +} + +void fault_FillScreenBlack(void) { + FaultDrawer_SetForeColor(GPACK_RGBA5551(255, 255, 255, 1)); + FaultDrawer_SetBackColor(GPACK_RGBA5551(0, 0, 0, 1)); + FaultDrawer_FillScreen(); + FaultDrawer_SetBackColor(GPACK_RGBA5551(0, 0, 0, 0)); +} + +void fault_FillScreenRed(void) { + FaultDrawer_SetForeColor(GPACK_RGBA5551(255, 255, 255, 1)); + FaultDrawer_SetBackColor(GPACK_RGBA5551(240, 0, 0, 1)); + FaultDrawer_FillScreen(); + FaultDrawer_SetBackColor(GPACK_RGBA5551(0, 0, 0, 0)); +} + +void fault_DrawCornerRec(u16 color) { + fault_DrawRec(22, 16, 8, 1, color); +} + +void fault_PrintFReg(s32 idx, f32* value) { + u32 raw = *(u32*)value; + s32 exp = ((raw & 0x7F800000) >> 0x17) - 0x7F; + + if ((exp >= -0x7E && exp < 0x80) || raw == 0) { + FaultDrawer_Printf("F%02d:%14.7e ", idx, *value); + } else { + // Print subnormal floats as their ieee-754 hex representation + FaultDrawer_Printf("F%02d: %08x(16) ", idx, raw); + } +} + +void fault_LogFReg(s32 idx, f32* value) { + u32 raw = *(u32*)value; + s32 exp = ((raw & 0x7F800000) >> 0x17) - 0x7F; + + if ((exp >= -0x7E && exp < 0x80) || raw == 0) { + osSyncPrintf("F%02d:%14.7e ", idx, *value); + } else { + osSyncPrintf("F%02d: %08x(16) ", idx, *(u32*)value); + } +} + +void fault_PrintFPCSR(u32 value) { + s32 i; + u32 flag = FPCSR_CE; + + FaultDrawer_Printf("FPCSR:%08xH ", value); + + // Go through each of the six causes and print the name of + // the first cause that is set + for (i = 0; i < ARRAY_COUNT(sFpExceptionNames); i++) { + if (value & flag) { + FaultDrawer_Printf("(%s)", sFpExceptionNames[i]); + break; + } + flag >>= 1; + } + FaultDrawer_Printf("\n"); +} + +void fault_LogFPCSR(u32 value) { + s32 i; + u32 flag = FPCSR_CE; + + osSyncPrintf("FPCSR:%08xH ", value); + for (i = 0; i < ARRAY_COUNT(sFpExceptionNames); i++) { + if (value & flag) { + osSyncPrintf("(%s)\n", sFpExceptionNames[i]); + break; + } + flag >>= 1; + } +} + +void fault_PrintThreadContext(OSThread* thread) { + __OSThreadContext* ctx; + s16 causeStrIdx = _SHIFTR((u32)thread->context.cause, 2, 5); + + if (causeStrIdx == (EXC_WATCH >> CAUSE_EXCSHIFT)) { + causeStrIdx = 16; + } + if (causeStrIdx == (EXC_VCED >> CAUSE_EXCSHIFT)) { + causeStrIdx = 17; + } + + FaultDrawer_FillScreen(); + FaultDrawer_SetCharPad(-2, 4); + FaultDrawer_SetCursor(22, 20); + + ctx = &thread->context; + FaultDrawer_Printf("THREAD:%d (%d:%s)\n", thread->id, causeStrIdx, sExceptionNames[causeStrIdx]); + FaultDrawer_SetCharPad(-1, 0); + + FaultDrawer_Printf("PC:%08xH SR:%08xH VA:%08xH\n", (u32)ctx->pc, (u32)ctx->sr, (u32)ctx->badvaddr); + FaultDrawer_Printf("AT:%08xH V0:%08xH V1:%08xH\n", (u32)ctx->at, (u32)ctx->v0, (u32)ctx->v1); + FaultDrawer_Printf("A0:%08xH A1:%08xH A2:%08xH\n", (u32)ctx->a0, (u32)ctx->a1, (u32)ctx->a2); + FaultDrawer_Printf("A3:%08xH T0:%08xH T1:%08xH\n", (u32)ctx->a3, (u32)ctx->t0, (u32)ctx->t1); + FaultDrawer_Printf("T2:%08xH T3:%08xH T4:%08xH\n", (u32)ctx->t2, (u32)ctx->t3, (u32)ctx->t4); + FaultDrawer_Printf("T5:%08xH T6:%08xH T7:%08xH\n", (u32)ctx->t5, (u32)ctx->t6, (u32)ctx->t7); + FaultDrawer_Printf("S0:%08xH S1:%08xH S2:%08xH\n", (u32)ctx->s0, (u32)ctx->s1, (u32)ctx->s2); + FaultDrawer_Printf("S3:%08xH S4:%08xH S5:%08xH\n", (u32)ctx->s3, (u32)ctx->s4, (u32)ctx->s5); + FaultDrawer_Printf("S6:%08xH S7:%08xH T8:%08xH\n", (u32)ctx->s6, (u32)ctx->s7, (u32)ctx->t8); + FaultDrawer_Printf("T9:%08xH GP:%08xH SP:%08xH\n", (u32)ctx->t9, (u32)ctx->gp, (u32)ctx->sp); + FaultDrawer_Printf("S8:%08xH RA:%08xH LO:%08xH\n\n", (u32)ctx->s8, (u32)ctx->ra, (u32)ctx->lo); + + fault_PrintFPCSR(ctx->fpcsr); + FaultDrawer_Printf("\n"); + + fault_PrintFReg(0, &ctx->fp0.f.f_even); + fault_PrintFReg(2, &ctx->fp2.f.f_even); + FaultDrawer_Printf("\n"); + fault_PrintFReg(4, &ctx->fp4.f.f_even); + fault_PrintFReg(6, &ctx->fp6.f.f_even); + FaultDrawer_Printf("\n"); + fault_PrintFReg(8, &ctx->fp8.f.f_even); + fault_PrintFReg(10, &ctx->fp10.f.f_even); + FaultDrawer_Printf("\n"); + fault_PrintFReg(12, &ctx->fp12.f.f_even); + fault_PrintFReg(14, &ctx->fp14.f.f_even); + FaultDrawer_Printf("\n"); + fault_PrintFReg(16, &ctx->fp16.f.f_even); + fault_PrintFReg(18, &ctx->fp18.f.f_even); + FaultDrawer_Printf("\n"); + fault_PrintFReg(20, &ctx->fp20.f.f_even); + fault_PrintFReg(22, &ctx->fp22.f.f_even); + FaultDrawer_Printf("\n"); + fault_PrintFReg(24, &ctx->fp24.f.f_even); + fault_PrintFReg(26, &ctx->fp26.f.f_even); + FaultDrawer_Printf("\n"); + fault_PrintFReg(28, &ctx->fp28.f.f_even); + fault_PrintFReg(30, &ctx->fp30.f.f_even); + FaultDrawer_Printf("\n"); + + FaultDrawer_SetCharPad(0, 0); +} + +void fault_LogThreadContext(OSThread* thread) { + __OSThreadContext* ctx; + s16 causeStrIdx = _SHIFTR((u32)thread->context.cause, 2, 5); + + if (causeStrIdx == (EXC_WATCH >> CAUSE_EXCSHIFT)) { + causeStrIdx = 16; + } + if (causeStrIdx == (EXC_VCED >> CAUSE_EXCSHIFT)) { + causeStrIdx = 17; + } + + ctx = &thread->context; + osSyncPrintf("\n"); + osSyncPrintf("THREAD ID:%d (%d:%s)\n", thread->id, causeStrIdx, sExceptionNames[causeStrIdx]); + + osSyncPrintf("PC:%08xH SR:%08xH VA:%08xH\n", (u32)ctx->pc, (u32)ctx->sr, (u32)ctx->badvaddr); + osSyncPrintf("AT:%08xH V0:%08xH V1:%08xH\n", (u32)ctx->at, (u32)ctx->v0, (u32)ctx->v1); + osSyncPrintf("A0:%08xH A1:%08xH A2:%08xH\n", (u32)ctx->a0, (u32)ctx->a1, (u32)ctx->a2); + osSyncPrintf("A3:%08xH T0:%08xH T1:%08xH\n", (u32)ctx->a3, (u32)ctx->t0, (u32)ctx->t1); + osSyncPrintf("T2:%08xH T3:%08xH T4:%08xH\n", (u32)ctx->t2, (u32)ctx->t3, (u32)ctx->t4); + osSyncPrintf("T5:%08xH T6:%08xH T7:%08xH\n", (u32)ctx->t5, (u32)ctx->t6, (u32)ctx->t7); + osSyncPrintf("S0:%08xH S1:%08xH S2:%08xH\n", (u32)ctx->s0, (u32)ctx->s1, (u32)ctx->s2); + osSyncPrintf("S3:%08xH S4:%08xH S5:%08xH\n", (u32)ctx->s3, (u32)ctx->s4, (u32)ctx->s5); + osSyncPrintf("S6:%08xH S7:%08xH T8:%08xH\n", (u32)ctx->s6, (u32)ctx->s7, (u32)ctx->t8); + osSyncPrintf("T9:%08xH GP:%08xH SP:%08xH\n", (u32)ctx->t9, (u32)ctx->gp, (u32)ctx->sp); + osSyncPrintf("S8:%08xH RA:%08xH LO:%08xH\n", (u32)ctx->s8, (u32)ctx->ra, (u32)ctx->lo); + osSyncPrintf("\n"); + + fault_LogFPCSR(ctx->fpcsr); + osSyncPrintf("\n"); + + fault_LogFReg(0, &ctx->fp0.f.f_even); + fault_LogFReg(2, &ctx->fp2.f.f_even); + osSyncPrintf("\n"); + fault_LogFReg(4, &ctx->fp4.f.f_even); + fault_LogFReg(6, &ctx->fp6.f.f_even); + osSyncPrintf("\n"); + fault_LogFReg(8, &ctx->fp8.f.f_even); + fault_LogFReg(10, &ctx->fp10.f.f_even); + osSyncPrintf("\n"); + fault_LogFReg(12, &ctx->fp12.f.f_even); + fault_LogFReg(14, &ctx->fp14.f.f_even); + osSyncPrintf("\n"); + fault_LogFReg(16, &ctx->fp16.f.f_even); + fault_LogFReg(18, &ctx->fp18.f.f_even); + osSyncPrintf("\n"); + fault_LogFReg(20, &ctx->fp20.f.f_even); + fault_LogFReg(22, &ctx->fp22.f.f_even); + osSyncPrintf("\n"); + fault_LogFReg(24, &ctx->fp24.f.f_even); + fault_LogFReg(26, &ctx->fp26.f.f_even); + osSyncPrintf("\n"); + fault_LogFReg(28, &ctx->fp28.f.f_even); + fault_LogFReg(30, &ctx->fp30.f.f_even); + osSyncPrintf("\n"); +} + +/** + * Iterates through the active thread queue for a user thread with either + * the CPU break or Fault flag set. + */ +OSThread* FindFaultedThread(void) { + OSThread* thread = __osGetActiveQueue(); + + // -1 indicates the end of the thread queue + while (thread->priority != -1) { + if (thread->priority > OS_PRIORITY_IDLE && thread->priority < OS_PRIORITY_APPMAX && + (thread->flags & (OS_FLAG_CPU_BREAK | OS_FLAG_FAULT))) { + return thread; + } + thread = thread->tlnext; + } + return NULL; +} + +void fault_Wait5Seconds(void) { + UNUSED s32 pad; + OSTime start = osGetTime(); + + do { + fault_Sleep(1000 / 60); + } while ((osGetTime() - start) < OS_USEC_TO_CYCLES(5000000) + 1); + + sFaultInstance->autoScroll = true; +} + +/** + * Waits for the following button combination to be entered before returning: + * + * (L & R & Z) + DPad-Up + C-Down + C-Up + DPad-Down + DPad-Left + C-Left + C-Right + DPad-Right + (B & A & START) + */ +void fault_WaitForButtonCombo(void) { + Input* input = &sFaultInstance->inputs[0]; + s32 state; + u32 s1; + u32 s2; + u32 pressedBtn; + u32 curBtn; + + //! FAKE: + if (1) {} + if (1) {} + + osSyncPrintf( + VT_FGCOL(WHITE) "KeyWaitB (LRZ " VT_FGCOL(WHITE) "上" VT_FGCOL(YELLOW) "下 " VT_FGCOL(YELLOW) "上" VT_FGCOL(WHITE) "下 " VT_FGCOL(WHITE) "左" VT_FGCOL( + YELLOW) "左 " VT_FGCOL(YELLOW) "右" VT_FGCOL(WHITE) "右 " VT_FGCOL(GREEN) "B" VT_FGCOL(BLUE) "A" VT_FGCOL(RED) "START" VT_FGCOL(WHITE) ")" VT_RST + "\n"); + osSyncPrintf(VT_FGCOL(WHITE) "KeyWaitB'(LR左" VT_FGCOL(YELLOW) "右 +" VT_FGCOL(RED) "START" VT_FGCOL( + WHITE) ")" VT_RST "\n"); + + FaultDrawer_SetForeColor(GPACK_RGBA5551(255, 255, 255, 1)); + FaultDrawer_SetBackColor(GPACK_RGBA5551(0, 0, 0, 1)); + + state = 0; + s1 = 0; + s2 = 1; + + while (state != 11) { + fault_Sleep(1000 / 60); + fault_UpdatePadImpl(); + + pressedBtn = input->press.button; + curBtn = input->cur.button; + + if (curBtn == 0 && s1 == s2) { + s1 = 0; + } else if (pressedBtn != 0) { + if (s1 == s2) { + state = 0; + } + + switch (state) { + case 0: + if (curBtn == (Z_TRIG | L_TRIG | R_TRIG) && pressedBtn == Z_TRIG) { + state = s2; + s1 = s2; + } + break; + + case 1: + if (pressedBtn == U_JPAD) { + state = 2; + } else { + state = 0; + } + break; + + case 2: + if (pressedBtn == D_CBUTTONS) { + state = 3; + s1 = s2; + } else { + state = 0; + } + break; + + case 3: + if (pressedBtn == U_CBUTTONS) { + state = 4; + } else { + state = 0; + } + break; + + case 4: + if (pressedBtn == D_JPAD) { + state = 5; + s1 = s2; + } else { + state = 0; + } + break; + + case 5: + if (pressedBtn == L_JPAD) { + state = 6; + } else { + state = 0; + } + break; + + case 6: + if (pressedBtn == L_CBUTTONS) { + state = 7; + s1 = s2; + } else { + state = 0; + } + break; + + case 7: + if (pressedBtn == R_CBUTTONS) { + state = 8; + } else { + state = 0; + } + break; + + case 8: + if (pressedBtn == R_JPAD) { + state = 9; + s1 = s2; + } else { + state = 0; + } + break; + + case 9: + if (pressedBtn == (A_BUTTON | B_BUTTON)) { + state = 10; + } else if (pressedBtn == A_BUTTON) { + state = 0x5B; + } else if (pressedBtn == B_BUTTON) { + state = 0x5C; + } else { + state = 0; + } + break; + + case 0x5B: + if (pressedBtn == B_BUTTON) { + state = 10; + } else { + state = 0; + } + break; + + case 0x5C: + if (pressedBtn == A_BUTTON) { + state = 10; + } else { + state = 0; + } + break; + + case 10: + if (pressedBtn == START_BUTTON) { + state = 11; + } else { + state = 0; + } + break; + } + } + + osWritebackDCacheAll(); + } +} + +void fault_DrawMemDumpContents(const char* title, uintptr_t addr, UNUSED u32 arg2) { + uintptr_t alignedAddr = addr; + u32* writeAddr; + s32 y; + s32 x; + + // Ensure address is within the bounds of RDRAM (fault_DrawMemDump has already done this) + if (alignedAddr < K0BASE) { + alignedAddr = K0BASE; + } + // 8MB RAM, leave room to display 0x100 bytes on the final page + //! @bug The loop below draws 22 * 4 * 4 = 0x160 bytes per page. Due to this, by scrolling further than + //! 0x807FFEA0 some invalid bytes are read from outside of 8MB RDRAM space. This does not cause a crash, + //! however the values it displays are meaningless. On N64 hardware these invalid addresses are read as 0. + if (alignedAddr > K0BASE + 0x800000 - 0x100) { + alignedAddr = K0BASE + 0x800000 - 0x100; + } + + // Ensure address is word-aligned + alignedAddr &= ~3; + writeAddr = (u32*)alignedAddr; + + // Reset screen + fault_FillScreenBlack(); + FaultDrawer_SetCharPad(-2, 0); + + FaultDrawer_DrawText(36, 18, "%s %08x", title != NULL ? title : "PrintDump", alignedAddr); + + // Draw memory page contents + if (alignedAddr >= K0BASE && alignedAddr < K2BASE) { + for (y = 0; y < 22; y++) { + FaultDrawer_DrawText(24, 28 + y * 9, "%06x", writeAddr); + for (x = 0; x < 4; x++) { + FaultDrawer_DrawText(82 + x * 52, 28 + y * 9, "%08x", *writeAddr++); + } + } + } + + FaultDrawer_SetCharPad(0, 0); +} + +/** + * Draws the memory dump page. + * + * DPad-Up scrolls up. + * DPad-Down scrolls down. + * Holding Z while scrolling speeds up scrolling by a factor of 0x10. + * Holding B while scrolling speeds up scrolling by a factor of 0x100. + * + * L toggles auto-scrolling pages. + * START and A move on to the next page. + * + * @param pc Program counter, pressing C-Up jumps to this address + * @param sp Stack pointer, pressing C-Down jumps to this address + * @param cLeftJump Unused parameter, pressing C-Left jumps to this address + * @param cRightJump Unused parameter, pressing C-Right jumps to this address + */ +void fault_DrawMemDump(uintptr_t pc, uintptr_t sp, uintptr_t cLeftJump, uintptr_t cRightJump) { + Input* input = &sFaultInstance->inputs[0]; + uintptr_t addr = pc; + s32 scrollCountdown; + u32 off; + + do { + scrollCountdown = 0; + // Ensure address is within the bounds of RDRAM + if (addr < K0BASE) { + addr = K0BASE; + } + // 8MB RAM, leave room to display 0x100 bytes on the final page + if (addr > K0BASE + 0x800000 - 0x100) { + addr = K0BASE + 0x800000 - 0x100; + } + + // Align the address to 0x10 bytes and draw the page contents + addr &= ~0xF; + fault_DrawMemDumpContents("Dump", addr, 0); + scrollCountdown = 600; + + while (sFaultInstance->autoScroll) { + // Count down until it's time to move on to the next page + if (scrollCountdown == 0) { + return; + } + + scrollCountdown--; + fault_Sleep(1000 / 60); + fault_UpdatePadImpl(); + if (CHECK_BTN_ALL(input->press.button, L_TRIG)) { + // Disable auto-scrolling + sFaultInstance->autoScroll = false; + } + } + + // Wait for input + do { + fault_Sleep(1000 / 60); + fault_UpdatePadImpl(); + } while (input->press.button == 0); + + // Move to next page + if (CHECK_BTN_ALL(input->press.button, START_BUTTON) || CHECK_BTN_ALL(input->cur.button, A_BUTTON)) { + return; + } + + // Memory dump controls + off = 0x10; + + if (CHECK_BTN_ALL(input->cur.button, Z_TRIG)) { + off *= 0x10; + } + + if (CHECK_BTN_ALL(input->cur.button, B_BUTTON)) { + off *= 0x100; + } + + if (CHECK_BTN_ALL(input->press.button, U_JPAD)) { + addr -= off; + } + + if (CHECK_BTN_ALL(input->press.button, D_JPAD)) { + addr += off; + } + + if (CHECK_BTN_ALL(input->press.button, U_CBUTTONS)) { + addr = pc; + } + + if (CHECK_BTN_ALL(input->press.button, D_CBUTTONS)) { + addr = sp; + } + + if (CHECK_BTN_ALL(input->press.button, L_CBUTTONS)) { + addr = cLeftJump; + } + + if (CHECK_BTN_ALL(input->press.button, R_CBUTTONS)) { + addr = cRightJump; + } + } while (!CHECK_BTN_ALL(input->press.button, L_TRIG)); + + // Resume auto-scroll and move to next page + sFaultInstance->autoScroll = true; +} + +/** + * Searches a single function's stack frame for the function it was called from. + * There are two cases that must be covered: Leaf and non-leaf functions. + * + * A leaf function is one that does not call any other function, in this case the + * return address need not be saved to the stack. Since a leaf function does not + * call other functions, only the function the stack trace begins in could possibly + * be a leaf function, in which case the return address is in the thread context's + * $ra already, as it never left. + * + * The procedure is therefore + * - Iterate instructions + * - Once jr $ra is found, set pc to $ra + * - Done after delay slot + * + * A non-leaf function calls other functions, it is necessary for the return address + * to be saved to the stack. In these functions, it is important to keep track of the + * stack frame size of each function. + * + * The procedure is therefore + * - Iterate instructions + * - If lw $ra <imm>($sp) is found, fetch the saved $ra from stack memory + * - If addiu $sp, $sp, <imm> is found, modify $sp by the immediate value + * - If jr $ra is found, set pc to $ra + * - Done after delay slot + * + * Note that searching for one jr $ra is sufficient, as only leaf functions can have + * multiple jr $ra in the same function. + * + * There is also additional handling for eret and j. Neither of these instructions + * appear in IDO compiled C, however do show up in the exception handler. It is not + * possible to backtrace through an eret as an interrupt can occur at any time, so + * there is no choice but to give up here. For j instructions, they can be followed + * and the backtrace may continue as normal. + */ +void fault_WalkStack(uintptr_t* spPtr, uintptr_t* pcPtr, uintptr_t* raPtr) { + uintptr_t sp = *spPtr; + uintptr_t pc = *pcPtr; + uintptr_t ra = *raPtr; + s32 count = 0x10000; // maximum number of instructions to search through + u32 lastInsn; + u32 insn; + u16 insnHi; + s16 insnLo; + u32 imm; + + // ensure $sp and $ra are aligned and valid pointers, if they aren't a stack + // trace cannot be generated + if (sp % 4 != 0 || !IS_KSEG0(sp) || ra % 4 != 0 || !IS_KSEG0(ra)) { + *raPtr = *pcPtr = *spPtr = 0; + return; + } + + // ensure pc is aligned and a valid pointer, if not a stack trace cannot + // be generated + if (pc % 4 != 0 || !IS_KSEG0(pc)) { + *pcPtr = ra; + return; + } + + lastInsn = 0; + while (true) { + insn = *(u32*)K0_TO_K1(pc); + insnHi = insn >> 16; + insnLo = insn & 0xFFFF; + imm = insnLo; + + if (insnHi == 0x8FBF) { + // lw $ra, <imm>($sp) + // read return address saved on the stack + ra = *(uintptr_t*)K0_TO_K1(sp + imm); + } else if (insnHi == 0x27BD) { + // addiu $sp, $sp, <imm> + // stack pointer increment or decrement + sp += imm; + } else if (insn == 0x42000018) { + // eret + // cannot backtrace through an eret, give up + ra = pc = sp = 0; + goto done; + } + if (lastInsn == 0x03E00008) { + // jr $ra + // return to previous function + pc = ra; + goto done; + } else if (lastInsn >> 26 == 2) { + // j <target> + // extract jump target + pc = pc >> 28 << 28 | lastInsn << 6 >> 4; + goto done; + } + + lastInsn = insn; + pc += sizeof(u32); + if (count == 0) { + break; + } + count--; + } + // Hit the maximum number of instructions to search, give up + ra = pc = sp = 0; + +done: + *spPtr = sp; + *pcPtr = pc; + *raPtr = ra; +} + +/** + * Draws the stack trace page contents for the specified thread + */ +void fault_DrawStackTrace(OSThread* thread, s32 x, s32 y, s32 height) { + s32 line; + uintptr_t sp = thread->context.sp; + uintptr_t ra = thread->context.ra; + uintptr_t pc = thread->context.pc; + uintptr_t addr; + + FaultDrawer_DrawText(x, y, "SP PC (VPC)"); + + // Backtrace from the current function to the start of the thread + for (line = 1; line < height && (ra != 0 || sp != 0) && pc != (uintptr_t)__osCleanupThread; line++) { + FaultDrawer_DrawText(x, y + line * 8, "%08x %08x", sp, pc); + // Convert relocated address to virtual address if applicable + addr = fault_ConvertAddress(pc); + if (addr != 0) { + FaultDrawer_Printf(" -> %08x", addr); + } + // Search one function for the previous function + fault_WalkStack(&sp, &pc, &ra); + } +} + +void fault_LogStackTrace(OSThread* thread, s32 height) { + s32 line; + uintptr_t sp = thread->context.sp; + uintptr_t ra = thread->context.ra; + uintptr_t pc = thread->context.pc; + uintptr_t addr; + UNUSED s32 pad; + + osSyncPrintf("STACK TRACE\nSP PC (VPC)\n"); + for (line = 1; line < height && (ra != 0 || sp != 0) && pc != (uintptr_t)__osCleanupThread; line++) { + osSyncPrintf("%08x %08x", sp, pc); + addr = fault_ConvertAddress(pc); + if (addr != 0) { + osSyncPrintf(" -> %08x", addr); + } + osSyncPrintf("\n"); + fault_WalkStack(&sp, &pc, &ra); + } +} + +void fault_ResumeThread(OSThread* thread) { + thread->context.cause = 0; + thread->context.fpcsr = 0; + thread->context.pc += sizeof(u32); + *((u32*)thread->context.pc) = 0x0000000D; // write in a break instruction + osWritebackDCache((void*)thread->context.pc, 4); + osInvalICache((void*)thread->context.pc, 4); + osStartThread(thread); +} + +void fault_DisplayFrameBuffer(void) { + void* fb; + + osViSetYScale(1.0f); + osViSetMode(&osViModeNtscLan1); + osViSetSpecialFeatures(OS_VI_GAMMA_OFF | OS_VI_DITHER_FILTER_ON); + osViBlack(false); + + if (sFaultInstance->fb != NULL) { + fb = sFaultInstance->fb; + } else { + fb = osViGetNextFramebuffer(); + if ((uintptr_t)fb == K0BASE) { + fb = (void*)(PHYS_TO_K0(osMemSize) - sizeof(u16[SCREEN_HEIGHT][SCREEN_WIDTH])); + } + } + + osViSwapBuffer(fb); + FaultDrawer_SetDrawerFB(fb, SCREEN_WIDTH, SCREEN_HEIGHT); +} + +/** + * Runs all registered fault clients. Each fault client displays a page + * on the crash screen. + */ +void fault_ProcessClients(void) { + FaultClient* client = sFaultInstance->clients; + s32 idx = 0; + + while (client != NULL) { + if (client->callback != NULL) { + fault_FillScreenBlack(); + FaultDrawer_SetCharPad(-2, 0); + FaultDrawer_Printf(FAULT_COLOR(DARK_GRAY) "CallBack (%d) %08x %08x %08x\n" FAULT_COLOR(WHITE), idx++, + client, client->arg0, client->arg1); + FaultDrawer_SetCharPad(0, 0); + fault_ProcessClient(client->callback, client->arg0, client->arg1); + fault_WaitForInput(); + fault_DisplayFrameBuffer(); + } + client = client->next; + } +} + +void fault_UpdatePad(void) { + fault_UpdatePadImpl(); +} + +#define FAULT_MSG_CPU_BREAK ((OSMesg)1) +#define FAULT_MSG_FAULT ((OSMesg)2) +#define FAULT_MSG_UNK ((OSMesg)3) + +void fault_ThreadEntry(UNUSED void* arg) { + OSMesg msg; + OSThread* faultedThread; + UNUSED s32 pad; + + // Direct OS event messages to the fault event queue + osSetEventMesg(OS_EVENT_CPU_BREAK, &sFaultInstance->queue, FAULT_MSG_CPU_BREAK); + osSetEventMesg(OS_EVENT_FAULT, &sFaultInstance->queue, FAULT_MSG_FAULT); + + while (true) { + do { + // Wait for a thread to hit a fault + osRecvMesg(&sFaultInstance->queue, &msg, OS_MESG_BLOCK); + + if (msg == FAULT_MSG_CPU_BREAK) { + sFaultInstance->msgId = (u32)FAULT_MSG_CPU_BREAK; + // "Fault manager: OS_EVENT_CPU_BREAK received" + osSyncPrintf("フォルトマネージャ:OS_EVENT_CPU_BREAKを受信しました\n"); + } else if (msg == FAULT_MSG_FAULT) { + sFaultInstance->msgId = (u32)FAULT_MSG_FAULT; + // "Fault manager: OS_EVENT_FAULT received" + osSyncPrintf("フォルトマネージャ:OS_EVENT_FAULTを受信しました\n"); + } else if (msg == FAULT_MSG_UNK) { + fault_UpdatePad(); + faultedThread = NULL; + continue; + } else { + sFaultInstance->msgId = (u32)FAULT_MSG_UNK; + // "Fault manager: received an unknown message" + osSyncPrintf("フォルトマネージャ:不明なメッセージを受信しました\n"); + } + + faultedThread = __osGetCurrFaultedThread(); + osSyncPrintf("__osGetCurrFaultedThread()=%08x\n", faultedThread); + + if (faultedThread == NULL) { + faultedThread = FindFaultedThread(); + osSyncPrintf("FindFaultedThread()=%08x\n", faultedThread); + } + } while (faultedThread == NULL); + + // Disable floating-point related exceptions + __osSetFpcCsr(__osGetFpcCsr() & ~(FPCSR_EV | FPCSR_EZ | FPCSR_EO | FPCSR_EU | FPCSR_EI)); + sFaultInstance->faultedThread = faultedThread; + + while (!sFaultInstance->faultHandlerEnabled) { + fault_Sleep(1000); + } + fault_Sleep(1000 / 2); + + // Show fault framebuffer + fault_DisplayFrameBuffer(); + + if (sFaultInstance->autoScroll) { + fault_Wait5Seconds(); + } else { + // Draw error bar signifying the crash screen is available + fault_DrawCornerRec(GPACK_RGBA5551(255, 0, 0, 1)); + fault_WaitForButtonCombo(); + } + + // Set auto-scrolling and default colors + sFaultInstance->autoScroll = true; + FaultDrawer_SetForeColor(GPACK_RGBA5551(255, 255, 255, 1)); + FaultDrawer_SetBackColor(GPACK_RGBA5551(0, 0, 0, 0)); + + // Draw pages + do { + // Thread context page + fault_PrintThreadContext(faultedThread); + fault_LogThreadContext(faultedThread); + fault_WaitForInput(); + // Stack trace page + fault_FillScreenBlack(); + FaultDrawer_DrawText(120, 16, "STACK TRACE"); + fault_DrawStackTrace(faultedThread, 36, 24, 22); + fault_LogStackTrace(faultedThread, 50); + fault_WaitForInput(); + // Client pages + fault_ProcessClients(); + // Memory dump page + fault_DrawMemDump(faultedThread->context.pc - 0x100, (uintptr_t)faultedThread->context.sp, 0, 0); + // End page + fault_FillScreenRed(); + FaultDrawer_DrawText(64, 80, " CONGRATURATIONS! "); + FaultDrawer_DrawText(64, 90, "All Pages are displayed."); + FaultDrawer_DrawText(64, 100, " THANK YOU! "); + FaultDrawer_DrawText(64, 110, " You are great debugger!"); + fault_WaitForInput(); + } while (!sFaultInstance->exit); + + while (!sFaultInstance->exit) {} + + fault_ResumeThread(faultedThread); + } +} + +void fault_SetFrameBuffer(void* fb, u16 w, u16 h) { + sFaultInstance->fb = fb; + FaultDrawer_SetDrawerFB(fb, w, h); +} + +void fault_Init(void) { + sFaultInstance = &gFaultMgr; + bzero(sFaultInstance, sizeof(FaultMgr)); + FaultDrawer_Init(); + FaultDrawer_SetInputCallback(fault_WaitForInput); + sFaultInstance->exit = false; + sFaultInstance->msgId = 0; + sFaultInstance->faultHandlerEnabled = false; + sFaultInstance->faultedThread = NULL; + sFaultInstance->padCallback = fault_PadCallback; + sFaultInstance->clients = NULL; + sFaultInstance->autoScroll = false; + gFaultMgr.faultHandlerEnabled = true; + osCreateMesgQueue(&sFaultInstance->queue, sFaultInstance->msg, ARRAY_COUNT(sFaultInstance->msg)); + StackCheck_Init(&sFaultThreadInfo, sFaultStack, STACK_TOP(sFaultStack), 0, 0x100, "fault"); + osCreateThread(&sFaultInstance->thread, M_THREAD_ID_FAULT, fault_ThreadEntry, NULL, STACK_TOP(sFaultStack), + M_PRIORITY_FAULT); + osStartThread(&sFaultInstance->thread); +} + +/** + * Fault page for Hungup crashes. Displays the thread id and two messages + * specified in arguments to `fault_AddHungupAndCrashImpl`. + */ +void fault_HungupFaultClient(const char* exp1, const char* exp2) { + osSyncPrintf("HungUp on Thread %d\n", osGetThreadId(NULL)); + osSyncPrintf("%s\n", exp1 != NULL ? exp1 : "(NULL)"); + osSyncPrintf("%s\n", exp2 != NULL ? exp2 : "(NULL)"); + FaultDrawer_Printf("HungUp on Thread %d\n", osGetThreadId(NULL)); + FaultDrawer_Printf("%s\n", exp1 != NULL ? exp1 : "(NULL)"); + FaultDrawer_Printf("%s\n", exp2 != NULL ? exp2 : "(NULL)"); +} + +/** + * Immediately crashes the current thread, for cases where an irrecoverable + * error occurs. The parameters specify two messages detailing the error, one + * or both may be NULL. + */ +void fault_AddHungupAndCrashImpl(const char* exp1, const char* exp2) { + FaultClient client; + UNUSED s32 pad; + + fault_AddClient(&client, (void*)fault_HungupFaultClient, (void*)exp1, (void*)exp2); + *(u32*)0x11111111 = 0; // trigger an exception via unaligned memory access +} + +/** + * Like `fault_AddHungupAndCrashImpl`, however provides a fixed message containing + * filename and line number + */ +void fault_AddHungupAndCrash(const char* file, s32 line) { + char msg[256]; + + sprintf(msg, "HungUp %s:%d", file, line); + fault_AddHungupAndCrashImpl(msg, NULL); +} diff --git a/src/boot/fault_drawer.c b/src/boot/fault_drawer.c new file mode 100644 index 0000000..1df4080 --- /dev/null +++ b/src/boot/fault_drawer.c @@ -0,0 +1,318 @@ +/** + * @file fault_drawer.c + * + * Implements routines for drawing text with a fixed font directly to a framebuffer, used in displaying + * the crash screen implemented by fault.c + */ +#include "fault.h" +#include "macros.h" +#include "libc64/aprintf.h" +#include "libc/stdarg.h" +#include "libc/stdbool.h" +#include "terminal.h" + +typedef struct { + /* 0x00 */ u16* fb; + /* 0x04 */ u16 w; + /* 0x06 */ u16 h; + /* 0x08 */ u16 yStart; + /* 0x0A */ u16 yEnd; + /* 0x0C */ u16 xStart; + /* 0x0E */ u16 xEnd; + /* 0x10 */ u16 foreColor; + /* 0x12 */ u16 backColor; + /* 0x14 */ u16 cursorX; + /* 0x16 */ u16 cursorY; + /* 0x18 */ const u32* fontData; + /* 0x1C */ u8 charW; + /* 0x1D */ u8 charH; + /* 0x1E */ s8 charWPad; + /* 0x1F */ s8 charHPad; + /* 0x20 */ u16 printColors[10]; + /* 0x34 */ u8 escCode; // bool + /* 0x35 */ u8 osSyncPrintfEnabled; + /* 0x38 */ void (*inputCallback)(void); +} FaultDrawer; // size = 0x3C + +// TODO: Extract sFaultDrawerFont +extern const u32 sFaultDrawerFont[]; +#pragma GLOBAL_ASM("asm/jp/nonmatchings/boot/fault_drawer/sFaultDrawerFont.s") + +#define FAULT_DRAWER_CURSOR_X 22 +#define FAULT_DRAWER_CURSOR_Y 16 + +FaultDrawer sFaultDrawerDefault = { + (u16*)(PHYS_TO_K0(0x400000) - sizeof(u16[SCREEN_HEIGHT][SCREEN_WIDTH])), + SCREEN_WIDTH, + SCREEN_HEIGHT, + FAULT_DRAWER_CURSOR_Y, + SCREEN_HEIGHT - FAULT_DRAWER_CURSOR_Y - 1, + FAULT_DRAWER_CURSOR_X, + SCREEN_WIDTH - FAULT_DRAWER_CURSOR_X - 1, + GPACK_RGBA5551(255, 255, 255, 1), + GPACK_RGBA5551(0, 0, 0, 0), + FAULT_DRAWER_CURSOR_X, + FAULT_DRAWER_CURSOR_Y, + sFaultDrawerFont, + 8, + 8, + 0, + 0, + { + GPACK_RGBA5551(0, 0, 0, 1), + GPACK_RGBA5551(255, 0, 0, 1), + GPACK_RGBA5551(0, 255, 0, 1), + GPACK_RGBA5551(255, 255, 0, 1), + GPACK_RGBA5551(0, 0, 255, 1), + GPACK_RGBA5551(255, 0, 255, 1), + GPACK_RGBA5551(0, 255, 255, 1), + GPACK_RGBA5551(255, 255, 255, 1), + GPACK_RGBA5551(120, 120, 120, 1), + GPACK_RGBA5551(176, 176, 176, 1), + }, + false, + false, + NULL, +}; + +FaultDrawer sFaultDrawer; + +void FaultDrawer_SetOsSyncPrintfEnabled(u32 enabled) { + sFaultDrawer.osSyncPrintfEnabled = enabled; +} + +void FaultDrawer_DrawRecImpl(s32 xStart, s32 yStart, s32 xEnd, s32 yEnd, u16 color) { + u16* fb; + s32 x; + s32 y; + s32 xDiff = sFaultDrawer.w - xStart; + s32 yDiff = sFaultDrawer.h - yStart; + s32 xSize = xEnd - xStart + 1; + s32 ySize = yEnd - yStart + 1; + + if ((xDiff > 0) && (yDiff > 0)) { + if (xDiff < xSize) { + xSize = xDiff; + } + + if (yDiff < ySize) { + ySize = yDiff; + } + + fb = sFaultDrawer.fb + sFaultDrawer.w * yStart + xStart; + for (y = 0; y < ySize; y++) { + for (x = 0; x < xSize; x++) { + *fb++ = color; + } + fb += sFaultDrawer.w - xSize; + } + + osWritebackDCacheAll(); + } +} + +void FaultDrawer_DrawChar(char c) { + FaultDrawer* faultDrawer = &sFaultDrawer; + s32 x; + s32 y; + u32 data; + s32 cursorX = sFaultDrawer.cursorX; + s32 cursorY = sFaultDrawer.cursorY; + s32 shift = c % 4; + const u32* dataPtr = &faultDrawer->fontData[((c / 8) * 16) + ((c & 4) >> 2)]; + u16* fb = sFaultDrawer.fb + (sFaultDrawer.w * cursorY) + cursorX; + + if ((sFaultDrawer.xStart <= cursorX) && ((sFaultDrawer.charW + cursorX - 1) <= sFaultDrawer.xEnd) && + (sFaultDrawer.yStart <= cursorY) && ((sFaultDrawer.charH + cursorY - 1) <= sFaultDrawer.yEnd)) { + for (y = 0; y < sFaultDrawer.charH; y++) { + u32 mask = 0x10000000 << shift; + + data = *dataPtr; + for (x = 0; x < sFaultDrawer.charW; x++) { + if (mask & data) { + fb[x] = sFaultDrawer.foreColor; + } else if (sFaultDrawer.backColor & 1) { + fb[x] = sFaultDrawer.backColor; + } + mask >>= 4; + } + fb += sFaultDrawer.w; + dataPtr += 2; + } + } +} + +s32 FaultDrawer_ColorToPrintColor(u16 color) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(sFaultDrawer.printColors); i++) { + if (color == sFaultDrawer.printColors[i]) { + return i; + } + } + return -1; +} + +void FaultDrawer_UpdatePrintColor(void) { + s32 idx; + + if (sFaultDrawer.osSyncPrintfEnabled) { + osSyncPrintf(VT_RST); + + idx = FaultDrawer_ColorToPrintColor(sFaultDrawer.foreColor); + if (idx >= 0 && idx < ARRAY_COUNT(sFaultDrawer.printColors) - 2) { + osSyncPrintf(VT_SGR("3%d"), idx); + } + + idx = FaultDrawer_ColorToPrintColor(sFaultDrawer.backColor); + if (idx >= 0 && idx < ARRAY_COUNT(sFaultDrawer.printColors) - 2) { + osSyncPrintf(VT_SGR("4%d"), idx); + } + } +} + +void FaultDrawer_SetForeColor(u16 color) { + sFaultDrawer.foreColor = color; + FaultDrawer_UpdatePrintColor(); +} + +void FaultDrawer_SetBackColor(u16 color) { + sFaultDrawer.backColor = color; + FaultDrawer_UpdatePrintColor(); +} + +void FaultDrawer_SetFontColor(u16 color) { + FaultDrawer_SetForeColor(color | 1); // force alpha to be set +} + +void FaultDrawer_SetCharPad(s8 padW, s8 padH) { + sFaultDrawer.charWPad = padW; + sFaultDrawer.charHPad = padH; +} + +void FaultDrawer_SetCursor(s32 x, s32 y) { + if (sFaultDrawer.osSyncPrintfEnabled) { + osSyncPrintf(VT_CUP("%d", "%d"), (y - sFaultDrawer.yStart) / (sFaultDrawer.charH + sFaultDrawer.charHPad), + (x - sFaultDrawer.xStart) / (sFaultDrawer.charW + sFaultDrawer.charWPad)); + } + sFaultDrawer.cursorX = x; + sFaultDrawer.cursorY = y; +} + +void FaultDrawer_FillScreen(void) { + if (sFaultDrawer.osSyncPrintfEnabled) { + osSyncPrintf(VT_CLS); + } + + FaultDrawer_DrawRecImpl(sFaultDrawer.xStart, sFaultDrawer.yStart, sFaultDrawer.xEnd, sFaultDrawer.yEnd, + sFaultDrawer.backColor | 1); + FaultDrawer_SetCursor(sFaultDrawer.xStart, sFaultDrawer.yStart); +} + +char* FaultDrawer_PrintCallback(char* arg, const char* str, size_t count) { + for (; count != 0; count--, str++) { + s32 curXStart; + s32 curXEnd; + + if (sFaultDrawer.escCode) { + sFaultDrawer.escCode = false; + if (*str > '0' && *str <= '9') { + FaultDrawer_SetForeColor(sFaultDrawer.printColors[*str - '0']); + } + + curXStart = sFaultDrawer.cursorX; + curXEnd = sFaultDrawer.xEnd - sFaultDrawer.charW; + } else { + switch (*str) { + case '\n': + if (sFaultDrawer.osSyncPrintfEnabled) { + osSyncPrintf("\n"); + } + + sFaultDrawer.cursorX = sFaultDrawer.w; + curXStart = sFaultDrawer.cursorX; + curXEnd = sFaultDrawer.xEnd - sFaultDrawer.charW; + break; + + case FAULT_ESC: + sFaultDrawer.escCode = true; + curXStart = sFaultDrawer.cursorX; + curXEnd = sFaultDrawer.xEnd - sFaultDrawer.charW; + break; + + default: + if (sFaultDrawer.osSyncPrintfEnabled) { + osSyncPrintf("%c", *str); + } + + FaultDrawer_DrawChar(*str); + sFaultDrawer.cursorX += sFaultDrawer.charW + sFaultDrawer.charWPad; + + curXStart = sFaultDrawer.cursorX; + curXEnd = sFaultDrawer.xEnd - sFaultDrawer.charW; + break; + } + } + + if (curXEnd <= curXStart) { + sFaultDrawer.cursorX = sFaultDrawer.xStart; + sFaultDrawer.cursorY += sFaultDrawer.charH + sFaultDrawer.charHPad; + if (sFaultDrawer.yEnd - sFaultDrawer.charH <= sFaultDrawer.cursorY) { + if (sFaultDrawer.inputCallback) { + sFaultDrawer.inputCallback(); + FaultDrawer_FillScreen(); + } + sFaultDrawer.cursorY = sFaultDrawer.yStart; + } + } + } + osWritebackDCacheAll(); + + return arg; +} + +const char D_8003E280[] = "(null)"; + +s32 FaultDrawer_VPrintf(const char* fmt, va_list args) { + return _Printf(FaultDrawer_PrintCallback, (void*)&sFaultDrawer, fmt, args); +} + +s32 FaultDrawer_Printf(const char* fmt, ...) { + s32 ret; + va_list args; + va_start(args, fmt); + + ret = FaultDrawer_VPrintf(fmt, args); + + va_end(args); + return ret; +} + +void FaultDrawer_DrawText(s32 x, s32 y, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + FaultDrawer_SetCursor(x, y); + FaultDrawer_VPrintf(fmt, args); + + va_end(args); +} + +void FaultDrawer_SetDrawerFB(void* fb, u16 w, u16 h) { + sFaultDrawer.fb = fb; + sFaultDrawer.w = w; + sFaultDrawer.h = h; +} + +void FaultDrawer_SetInputCallback(void (*callback)(void)) { + sFaultDrawer.inputCallback = callback; +} + +void FaultDrawer_WritebackFBDCache(void) { + osWritebackDCache(sFaultDrawer.fb, sFaultDrawer.w * sFaultDrawer.h * sizeof(u16)); +} + +void FaultDrawer_Init(void) { + bcopy(&sFaultDrawerDefault, &sFaultDrawer, sizeof(FaultDrawer)); + sFaultDrawer.fb = (u16*)(PHYS_TO_K0(osMemSize) - sizeof(u16[SCREEN_HEIGHT][SCREEN_WIDTH])); +} diff --git a/src/boot/ovlmgr.c b/src/boot/ovlmgr.c index 9e20d96..d0abf57 100644 --- a/src/boot/ovlmgr.c +++ b/src/boot/ovlmgr.c @@ -49,7 +49,7 @@ void ovlmgr_LoadImpl(RomOffset vromStart, UNUSED RomOffset vromEndUnused, void* if (ovlRelocs == NULL) { sprintf(ovlSizeStr, "%ld", ovlSize); - Fault_AddHungupAndCrashImpl("ovlmgr: Out of Memory", ovlSizeStr); + fault_AddHungupAndCrashImpl("ovlmgr: Out of Memory", ovlSizeStr); } Overlay_Load(vromStart, vromEnd, ovlStart, ovlEnd, vramStart, vramEnd, allocatedRamAddr, ovlRelocs); diff --git a/src/boot/z_std_dma.c b/src/boot/z_std_dma.c index 04184d9..05acca8 100644 --- a/src/boot/z_std_dma.c +++ b/src/boot/z_std_dma.c @@ -59,7 +59,7 @@ NORETURN void DmaMgr_Error(DmaRequest* req, const char* file, const char* errorN } sprintf(buff2, "%07X %08X %X %.50s", vrom, vram, size, (file != NULL) ? file : "???"); - Fault_AddHungupAndCrashImpl(buff1, buff2); + fault_AddHungupAndCrashImpl(buff1, buff2); } /** diff --git a/src/code/m_actor.c b/src/code/m_actor.c index 1762c01..7398d4b 100644 --- a/src/code/m_actor.c +++ b/src/code/m_actor.c @@ -253,7 +253,7 @@ void Actor_draw(Game_Play* game_play, Actor* actor) { FaultClient faultClient; LightsN* light; - Fault_AddClient(&faultClient, func_80056380_jp, actor, "Actor_draw"); + fault_AddClient(&faultClient, func_80056380_jp, actor, "Actor_draw"); //! FAKE if (1) {} @@ -289,7 +289,7 @@ void Actor_draw(Game_Play* game_play, Actor* actor) { CLOSE_DISPS(game_play->state.gfxCtx); - Fault_RemoveClient(&faultClient); + fault_RemoveClient(&faultClient); } s32 Actor_draw_actor_no_culling_check(Actor* actor) { diff --git a/src/code/m_actor_dlftbls.c b/src/code/m_actor_dlftbls.c index c502323..affc057 100644 --- a/src/code/m_actor_dlftbls.c +++ b/src/code/m_actor_dlftbls.c @@ -96,12 +96,12 @@ FaultAddrConvClient sActorOverlayTableFaultAddrConvClient; void actor_dlftbls_init(void) { actor_dlftbls_num = ACTOR_ID_MAX; - Fault_AddClient(&sActorOverlayTableFaultClient, ActorOverlayTable_FaultClient, NULL, NULL); - Fault_AddAddrConvClient(&sActorOverlayTableFaultAddrConvClient, ActorOverlayTable_FaultAddrConv, NULL); + fault_AddClient(&sActorOverlayTableFaultClient, ActorOverlayTable_FaultClient, NULL, NULL); + fault_AddressConverterAddClient(&sActorOverlayTableFaultAddrConvClient, ActorOverlayTable_FaultAddrConv, NULL); } void actor_dlftbls_cleanup(void) { - Fault_RemoveClient(&sActorOverlayTableFaultClient); - Fault_RemoveAddrConvClient(&sActorOverlayTableFaultAddrConvClient); + fault_RemoveClient(&sActorOverlayTableFaultClient); + fault_AddressConverterRemoveClient(&sActorOverlayTableFaultAddrConvClient); actor_dlftbls_num = 0; } diff --git a/src/code/m_malloc.c b/src/code/m_malloc.c index a63ac74..69a20b1 100644 --- a/src/code/m_malloc.c +++ b/src/code/m_malloc.c @@ -42,11 +42,11 @@ s32 zelda_CheckArena(void) { void zelda_InitArena(void* heap, size_t size) { __osMallocInit(&zelda_arena, heap, size); - Fault_AddClient(&B_80141FC8_jp, (void*)ArenaImpl_FaultClient, &zelda_arena, NULL); + fault_AddClient(&B_80141FC8_jp, (void*)ArenaImpl_FaultClient, &zelda_arena, NULL); } void zelda_CleanupArena(void) { - Fault_RemoveClient(&B_80141FC8_jp); + fault_RemoveClient(&B_80141FC8_jp); __osMallocCleanup(&zelda_arena); } diff --git a/src/code/m_submenu.c b/src/code/m_submenu.c index 7419545..5352ebe 100644 --- a/src/code/m_submenu.c +++ b/src/code/m_submenu.c @@ -297,13 +297,13 @@ dummy_label_255895: D_8010DCE4_jp = var_s0; SubmenuArea_visit = NULL; - Fault_AddClient(&B_80144670_jp, func_800C47B4_jp, NULL, NULL); - Fault_AddAddrConvClient(&B_80144680_jp, func_800C497C_jp, NULL); + fault_AddClient(&B_80144670_jp, func_800C47B4_jp, NULL, NULL); + fault_AddressConverterAddClient(&B_80144680_jp, func_800C497C_jp, NULL); } void mSM_submenu_ovlptr_cleanup(mSM* submenu) { - Fault_RemoveClient(&B_80144670_jp); - Fault_RemoveAddrConvClient(&B_80144680_jp); + fault_RemoveClient(&B_80144670_jp); + fault_AddressConverterRemoveClient(&B_80144680_jp); if (SubmenuArea_visit != NULL) { SubmenuArea_DoUnlink(SubmenuArea_visit, submenu); SubmenuArea_visit = NULL; diff --git a/yamls/jp/boot.yaml b/yamls/jp/boot.yaml index de6c6ae..1f9c22c 100644 --- a/yamls/jp/boot.yaml +++ b/yamls/jp/boot.yaml @@ -13,8 +13,8 @@ - [0x0017F0, c, boot/z_std_dma] - [0x0022A0, asm, boot/yaz0] - [0x0026C0, c, boot/syncprintf] - - [0x002720, asm, boot/fault] - - [0x004F70, asm, boot/fault_drawer] + - [0x002720, c, boot/fault] + - [0x004F70, c, boot/fault_drawer] - [0x0059F0, c, boot/libu64/pad] - [0x005BE0, asm, boot/libu64/stackcheck] @@ -216,7 +216,8 @@ - [0x016FD0, data, boot/016FD0] - [0x016FE0, .data, boot/z_std_dma] - - [0x016FF0, data, boot/016FF0] + - [0x016FF0, .data, boot/fault] + - [0x017050, .data, boot/fault_drawer] - [0x017090, data, boot/libu64/stackcheck] @@ -263,8 +264,8 @@ - [0x0185B0, .rodata, boot/ovlmgr] - [0x0185D0, .rodata, boot/z_std_dma] - [0x0187F0, rodata, boot/0187F0] - - [0x018840, rodata, boot/018840] - - [0x019250, rodata, boot/019250] + - [0x018840, .rodata, boot/fault] + - [0x019250, .rodata, boot/fault_drawer] # libu64 - [0x019690, rodata, boot/libu64/gfxprint] @@ -294,6 +295,8 @@ - { start: 0x019D40, type: .bss, vram: 0x8003FF30, name: boot/viconfig } - { start: 0x019D40, type: .bss, vram: 0x8003FF40, name: boot/z_std_dma } - { start: 0x019D40, type: bss, vram: 0x800406C0, name: boot/800406C0 } + - { start: 0x019D40, type: .bss, vram: 0x80040AE0, name: boot/fault } + - { start: 0x019D40, type: .bss, vram: 0x80041960, name: boot/fault_drawer } # libu64 - { start: 0x019D40, type: bss, vram: 0x800419A0, name: boot/libu64/gfxprint } |
