From 68818044db4f6c644ca8be80f0ad4b54a7587f44 Mon Sep 17 00:00:00 2001 From: Dragorn421 Date: Wed, 28 Aug 2024 09:38:42 +0200 Subject: [ntsc-1.2] Decompile fault.c (#2046) * wip decomp ntsc-1.2 fault.c * more ntsc-1.2 fault.c * decompiled fault_v1.c * name fault_v1 functions, wip * try to clean up build system (two versions of the fault system coexisting) * cleanup * cleanup2 * fix build gc-eu-mq-dbg * match 2 more, ty anon * matched * review * more review * fixup spec * `(uintptr_t)ptr op int` instead of `ptr op (type*)int` * move fault.h out of global headers, properly include fault.h and versions.h * compile all fault_*.c files regardless of version by overriding FAULT_VERSION * n64 FaultMgr.framebuffer s32 -> u16* * FaultMgr.framebuffer -> FaultMgr.fb * make gc FaultMgr.fb u16* (yes, thats everything) * bss * fix Fault_Printf return type * noop FaultDrawer_SetFontColor, FaultDrawer_SetCharPad in fault_n64 * fault_color_ stuff is only for fault_gc * rm empty line in makefile * I guess `D_80105A90_unknown` is `[sg]TotalAllocFailures` * bss * `Fault_WaitInput` -> `Fault_WaitForInput` * use named fault funcs in pointers array * FAULT_OOT{N64,GC} -> FAULT_{N64,GC} * Apply suggestions from code review Co-authored-by: Derek Hensley Co-authored-by: Anghelo Carvajal * review, sync fault_{gc,n64} * Apply `FPCSR_CE` suggestions from code review Co-authored-by: Derek Hensley --------- Co-authored-by: Derek Hensley Co-authored-by: Anghelo Carvajal --- src/code/__osMalloc.c | 5 +- src/code/code_n64dd_800AD4C0.c | 7 +- src/code/fault.c | 1325 --------------------------------------- src/code/fault_drawer.c | 340 ---------- src/code/fault_gc.c | 1338 ++++++++++++++++++++++++++++++++++++++++ src/code/fault_gc_drawer.c | 350 +++++++++++ src/code/fault_n64.c | 845 +++++++++++++++++++++++++ src/code/game.c | 3 + src/code/graph.c | 1 + src/code/main.c | 1 + src/code/padmgr.c | 5 +- src/code/sched.c | 1 + src/code/sys_matrix.c | 3 + src/code/z_actor.c | 1 + src/code/z_actor_dlftbls.c | 1 + src/code/z_collision_check.c | 6 +- src/code/z_message.c | 1 + src/code/z_play.c | 5 + 18 files changed, 2563 insertions(+), 1675 deletions(-) delete mode 100644 src/code/fault.c delete mode 100644 src/code/fault_drawer.c create mode 100644 src/code/fault_gc.c create mode 100644 src/code/fault_gc_drawer.c create mode 100644 src/code/fault_n64.c (limited to 'src/code') diff --git a/src/code/__osMalloc.c b/src/code/__osMalloc.c index 951a4b077..69a39f872 100644 --- a/src/code/__osMalloc.c +++ b/src/code/__osMalloc.c @@ -1,4 +1,5 @@ #include "global.h" +#include "fault.h" #include "terminal.h" #define FILL_ALLOC_BLOCK_FLAG (1 << 0) @@ -53,12 +54,12 @@ #define CHECK_FREE_BLOCK(arena, node) (void)0 // Number of allocation failures across all arenas. -u32 sTotalAllocFailures = 0; +u32 gTotalAllocFailures = 0; // "Arena_failcnt" #define CHECK_ALLOC_FAILURE(arena, ptr) \ do { \ if ((ptr) == NULL) { \ - sTotalAllocFailures++; \ + gTotalAllocFailures++; \ (arena)->allocFailures++; \ } \ } while (0) diff --git a/src/code/code_n64dd_800AD4C0.c b/src/code/code_n64dd_800AD4C0.c index 091597a04..a5c9005e1 100644 --- a/src/code/code_n64dd_800AD4C0.c +++ b/src/code/code_n64dd_800AD4C0.c @@ -1,10 +1,9 @@ #include "global.h" +#include "fault.h" #include "n64dd.h" // TODO functions of unknown prototype extern char func_801C8510_unknown[]; -extern char func_800AE170_unknown[]; -extern char func_800ADCD8_unknown[]; extern char osGetIntMask[]; extern char osSetTime[]; @@ -14,8 +13,8 @@ n64ddStruct_800FF4B0_pointers D_800FF4B0 = { NULL, Fault_RemoveClient, Fault_AddClient, - func_800AE170_unknown, - func_800ADCD8_unknown, + FaultDrawer_DrawText, + Fault_WaitForInput, Fault_AddHungupAndCrashImpl, Fault_AddHungupAndCrash, func_800ADC08, diff --git a/src/code/fault.c b/src/code/fault.c deleted file mode 100644 index 3251bb832..000000000 --- a/src/code/fault.c +++ /dev/null @@ -1,1325 +0,0 @@ -/** - * @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, DPad-Right or 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 "global.h" -#include "terminal.h" -#include "alloca.h" - -#pragma increment_block_number "gc-eu:64 gc-eu-mq:64 gc-eu-mq-dbg:0 gc-jp:64 gc-jp-ce:64 gc-jp-mq:64 gc-us:64" \ - "gc-us-mq:64" - -void FaultDrawer_Init(void); -void FaultDrawer_SetOsSyncPrintfEnabled(u32 enabled); -void FaultDrawer_DrawRecImpl(s32 xStart, s32 yStart, s32 xEnd, s32 yEnd, u16 color); -void FaultDrawer_FillScreen(void); -void FaultDrawer_SetInputCallback(void (*callback)(void)); -void FaultDrawer_SetDrawerFB(void* fb, u16 w, u16 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", -}; - -FaultMgr* sFaultInstance; -u8 sFaultAwaitingInput; -STACK(sFaultStack, 0x600); -StackEntry sFaultThreadInfo; -FaultMgr gFaultMgr; - -typedef struct FaultClientTask { - /* 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; - - Sleep_Cycles(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; - OSMesg recMsg; - OSThread* thread = NULL; - OSTimer timer; - u32 timerMsgVal = 666; - - osCreateMesgQueue(&queue, &msg, 1); - 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, THREAD_ID_FAULT, Fault_ClientProcessThread, task, sFaultInstance->clientThreadSp, - THREAD_PRI_FAULT_CLIENT); - osStartThread(thread); - } else { - // Run the fault client callback on this thread - Fault_ClientProcessThread(task); - } - - // Await done - while (true) { - osSetTimer(&timer, OS_SEC_TO_CYCLES(1), 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, void* 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) { - 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) { - 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_AddAddrConvClient(FaultAddrConvClient* client, void* 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) { - osSyncPrintf(VT_COL(RED, WHITE) "fault_AddressConverterAddClient: %08x は既にリスト中にある\n" VT_RST, client); - } -} - -void Fault_RemoveAddrConvClient(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) { - 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_RemoveAddrConvClient(client); - } else if (ret != 0) { - return (uintptr_t)ret; - } - } - client = client->next; - } - - return 0; -} - -void Fault_Sleep(u32 msec) { - Fault_SleepImpl(msec); -} - -#ifndef AVOID_UB -void PadMgr_RequestPadData(Input* inputs, s32 gameRequest); -#endif - -void Fault_PadCallback(Input* inputs) { - //! @bug This function is not called correctly, it is missing a leading PadMgr* argument. This - //! renders the crash screen unusable. - //! In Majora's Mask, PadMgr functions were changed to not require this argument, and this was - //! likely just not addressed when backporting. -#ifndef AVOID_UB - PadMgr_RequestPadData(inputs, false); -#else - // Guarantee crashing behavior: false -> NULL, previous value in a2 is more often non-zero than zero - PadMgr_RequestPadData((PadMgr*)inputs, NULL, true); -#endif -} - -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 == BTN_L) { - sFaultInstance->autoScroll = !sFaultInstance->autoScroll; - } - - if (sFaultInstance->autoScroll) { - if (count-- < 1) { - return false; - } - } else { - if (pressedBtn == BTN_A || pressedBtn == BTN_DRIGHT) { - return false; - } - - if (pressedBtn == BTN_DLEFT) { - return true; - } - - if (pressedBtn == BTN_DUP) { - FaultDrawer_SetOsSyncPrintfEnabled(true); - } - - if (pressedBtn == BTN_DDOWN) { - 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 == 23) { // Watchpoint - causeStrIdx = 16; - } - if (causeStrIdx == 31) { // Virtual coherency on data - 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 == 23) { // Watchpoint - causeStrIdx = 16; - } - if (causeStrIdx == 31) { // Virtual coherency on data - 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* Fault_FindFaultedThread(void) { - OSThread* thread = __osGetActiveQueue(); - - // OS_PRIORITY_THREADTAIL indicates the end of the thread queue - while (thread->priority != OS_PRIORITY_THREADTAIL) { - 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) { - s32 pad; - OSTime start = osGetTime(); - - do { - Fault_Sleep(1000 / 60); - } while ((osGetTime() - start) < OS_SEC_TO_CYCLES(5) + 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; - - 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 == (BTN_Z | BTN_L | BTN_R) && pressedBtn == BTN_Z) { - state = s2; - s1 = s2; - } - break; - case 1: - if (pressedBtn == BTN_DUP) { - state = 2; - } else { - state = 0; - } - break; - case 2: - if (pressedBtn == BTN_CDOWN) { - state = 3; - s1 = s2; - } else { - state = 0; - } - break; - case 3: - if (pressedBtn == BTN_CUP) { - state = 4; - } else { - state = 0; - } - break; - case 4: - if (pressedBtn == BTN_DDOWN) { - state = 5; - s1 = s2; - } else { - state = 0; - } - break; - case 5: - if (pressedBtn == BTN_DLEFT) { - state = 6; - } else { - state = 0; - } - break; - case 6: - if (pressedBtn == BTN_CLEFT) { - state = 7; - s1 = s2; - } else { - state = 0; - } - break; - case 7: - if (pressedBtn == BTN_CRIGHT) { - state = 8; - } else { - state = 0; - } - break; - case 8: - if (pressedBtn == BTN_DRIGHT) { - state = 9; - s1 = s2; - } else { - state = 0; - } - break; - case 9: - if (pressedBtn == (BTN_A | BTN_B)) { - state = 10; - } else if (pressedBtn == BTN_A) { - state = 0x5B; - } else if (pressedBtn == BTN_B) { - state = 0x5C; - } else { - state = 0; - } - break; - case 0x5B: - if (pressedBtn == BTN_B) { - state = 10; - } else { - state = 0; - } - break; - case 0x5C: - if (pressedBtn == BTN_A) { - state = 10; - } else { - state = 0; - } - break; - case 10: - if (pressedBtn == BTN_START) { - state = 11; - } else { - state = 0; - } - break; - } - } - - osWritebackDCacheAll(); - } -} - -void Fault_DrawMemDumpContents(const char* title, uintptr_t addr, 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, BTN_L)) { - // 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 - //! @bug DPad-Right does not move to the next page, unlike when on any other page - // START moving to the next page is unique to this page. - if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->cur.button, BTN_A)) { - return; - } - - // Memory dump controls - - off = 0x10; - if (CHECK_BTN_ALL(input->cur.button, BTN_Z)) { - off *= 0x10; - } - - if (CHECK_BTN_ALL(input->cur.button, BTN_B)) { - off *= 0x100; - } - - if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { - addr -= off; - } - if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { - addr += off; - } - if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { - addr = pc; - } - if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { - addr = sp; - } - if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { - addr = cLeftJump; - } - if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { - addr = cRightJump; - } - } while (!CHECK_BTN_ALL(input->press.button, BTN_L)); - - // 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 ($sp) is found, fetch the saved $ra from stack memory - * - If addiu $sp, $sp, 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, ($sp) - // read return address saved on the stack - ra = *(uintptr_t*)K0_TO_K1(sp + imm); - } else if (insnHi == 0x27BD) { - // addiu $sp, $sp, - // 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 - // 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; - 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(void* arg) { - OSMesg msg; - OSThread* faultedThread; - 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; - osSyncPrintf("フォルトマネージャ:OS_EVENT_CPU_BREAKを受信しました\n"); - } else if (msg == FAULT_MSG_FAULT) { - sFaultInstance->msgId = (u32)FAULT_MSG_FAULT; - osSyncPrintf("フォルトマネージャ:OS_EVENT_FAULTを受信しました\n"); - } else if (msg == FAULT_MSG_UNK) { - Fault_UpdatePad(); - faultedThread = NULL; - continue; - } else { - sFaultInstance->msgId = (u32)FAULT_MSG_UNK; - osSyncPrintf("フォルトマネージャ:不明なメッセージを受信しました\n"); - } - - faultedThread = __osGetCurrFaultedThread(); - osSyncPrintf("__osGetCurrFaultedThread()=%08x\n", faultedThread); - - if (faultedThread == NULL) { - faultedThread = Fault_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, 1); - StackCheck_Init(&sFaultThreadInfo, sFaultStack, STACK_TOP(sFaultStack), 0, 0x100, "fault"); - osCreateThread(&sFaultInstance->thread, THREAD_ID_FAULT, Fault_ThreadEntry, NULL, STACK_TOP(sFaultStack), - THREAD_PRI_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. - */ -NORETURN void Fault_AddHungupAndCrashImpl(const char* exp1, const char* exp2) { - FaultClient client; - s32 pad; - - Fault_AddClient(&client, Fault_HungupFaultClient, (void*)exp1, (void*)exp2); - *(u32*)0x11111111 = 0; // trigger an exception via unaligned memory access - - // Since the above line triggers an exception and transfers execution to the fault handler - // this function does not return and the rest of the function is unreachable. -#ifdef __GNUC__ - __builtin_unreachable(); -#endif -} - -/** - * Like `Fault_AddHungupAndCrashImpl`, however provides a fixed message containing - * filename and line number - */ -NORETURN void Fault_AddHungupAndCrash(const char* file, int line) { - char msg[256]; - - sprintf(msg, "HungUp %s:%d", file, line); - Fault_AddHungupAndCrashImpl(msg, NULL); -} diff --git a/src/code/fault_drawer.c b/src/code/fault_drawer.c deleted file mode 100644 index 6b85edb43..000000000 --- a/src/code/fault_drawer.c +++ /dev/null @@ -1,340 +0,0 @@ -/** - * @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 "global.h" -#include "terminal.h" - -typedef struct FaultDrawer { - /* 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 - -const u32 sFaultDrawerFont[] = { - 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, 0x06611DC0, 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, - 0x05589760, 0x0DD22990, 0x05599770, 0x04DFFD40, 0x026EF700, 0x00000000, 0x00000000, 0x08BFFB00, 0x0EFFFFC0, - 0x0BF00FB0, 0x0FF00330, 0x0FF00FF0, 0x0FF00220, 0x0CFBBF60, 0x0FFCCE20, 0x0DD44FF0, 0x0FF00220, 0x0FF00FF0, - 0x0FF00330, 0x0CFBBF40, 0x0EF77740, 0x00000000, 0x00000000, 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, 0x06611DC0, - 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, 0x05589760, 0x0DD22990, 0x05599770, 0x04DFFD40, - 0x026EF700, 0x00000000, 0x00000000, 0x08BFFB00, 0x000DE000, 0x0BF00FB0, 0x005DE600, 0x0FF00FF0, 0x055CC660, - 0x0CFBBF60, 0x773FF377, 0x0DD44FF0, 0xBB3FF3BB, 0x0FF00FF0, 0x099CCAA0, 0x0CFBBF40, 0x009DEA00, 0x00000000, - 0x000DE000, 0x04C22C40, 0x028D5020, 0x0CCAACC0, 0x21F91710, 0x04C22C40, 0x12493400, 0x00820800, 0x01975110, - 0x088A8880, 0x04615241, 0x00800800, 0x43117530, 0x00A20800, 0x60055600, 0x00000000, 0x04400040, 0x00221100, - 0x00000080, 0x000FB000, 0x00000880, 0x040DA400, 0x00008800, 0x08CDE880, 0x022AA220, 0x08CDE880, 0x02AA2220, - 0x040DA400, 0x0CD10000, 0x000FB000, 0x8C510000, 0x00221100, 0x81100000, 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, - 0x06611DC0, 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, 0x05589760, 0x0DD22990, 0x05599770, - 0x04DFFD40, 0x026EF700, 0x00000000, 0x00000000, 0x00333300, 0x04489980, 0x033CC330, 0x00CD1088, 0x033CC330, - 0x02BF62A8, 0x00333320, 0x01104C80, 0x01100330, 0x0015C800, 0x033CC330, 0x02673220, 0x003FF300, 0x04409900, - 0x00880000, 0x00000000, 0x05DFFD10, 0x07FFFF60, 0x1CE00EC1, 0x0FF00990, 0x1EE11661, 0x0FF00110, 0x1EF45621, - 0x0FF66710, 0x1EF23661, 0x0FF08990, 0x1EF10FE1, 0x0FF00990, 0x16ECCE21, 0x07FBBB20, 0x01111110, 0x00000000, - 0x09B66FD0, 0x27D88E60, 0x0992ED10, 0x2FF02EE0, 0x099AE510, 0x2FF62EE0, 0x099B7510, 0x2FD64EE0, 0x0DDAE510, - 0x2FD04EE0, 0x0DD2ED10, 0x2FD00EE0, 0x09F66F90, 0x27D99F70, 0x00000000, 0x00000000, 0x07FFFF00, 0x8F711FF0, - 0x2FD00FF0, 0x8F711FF0, 0x2FD00770, 0x8E611EE0, 0x27DDDF60, 0x8E691EE0, 0x27764AA0, 0x8EE99EE0, 0x2FD06E80, - 0x8AE7FEA0, 0x07FA8E60, 0x88277A80, 0x00000000, 0x00000000, 0x077CCFF0, 0x13266011, 0x077CCFF0, 0x03766510, - 0x0239D720, 0x04533540, 0x002FF200, 0x01133110, 0x005FB100, 0x00033000, 0x055EE550, 0x01133110, 0x055EEDD0, - 0x02233000, 0x00088880, 0x8AABB888, 0x00001100, 0x00044510, 0x04623320, 0x00440110, 0x04C89AA0, 0x00EEAB10, - 0x0CE66720, 0x0EF55FB0, 0x0EE00660, 0x0BF62B90, 0x0EE00660, 0x03FC8990, 0x04EEEEA0, 0x00773BB0, 0x00000000, - 0x08888800, 0x09900000, 0x00111000, 0x09922440, 0x00011000, 0x09908800, 0x26EFDE20, 0x099BB540, 0x2EC33CE2, - 0x0D9A2550, 0x2EC33CE2, 0x0DDAA550, 0x2EC33CE2, 0x09D6ED10, 0x26CBBC62, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00011000, 0x05FBFFE0, 0x8E6116E8, 0x0FF40330, 0x8F7117F8, 0x07FC8B30, 0x8E6996E8, - 0x05733BA0, 0x8A6DD6A8, 0x0DD88A20, 0x08A779B2, 0x01100220, 0x00000000, 0x00000080, 0x8A011000, 0x00000800, - 0x80A11000, 0x07744F70, 0x80A99000, 0x0231DF20, 0x84E60004, 0x0027DA20, 0xC8AA4C40, 0x00573B20, 0x00A11800, - 0x05546F50, 0x00A99800, 0x02222080, 0x02001888, -}; - -#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; -char D_8016B6C0[0x20]; - -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, 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) { - u16* fb; - s32 x, y; - const u32* dataPtr; - u32 data; - s32 cursorX = sFaultDrawer.cursorX; - s32 cursorY = sFaultDrawer.cursorY; - const u32** fontData = &sFaultDrawer.fontData; - s32 shift = c % 4; - - dataPtr = &fontData[0][(((c / 8) * 16) + ((c & 4) >> 2))]; - 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); -} - -void* FaultDrawer_PrintCallback(void* 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; -} - -s32 FaultDrawer_VPrintf(const char* fmt, va_list args) { - return _Printf(FaultDrawer_PrintCallback, &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/code/fault_gc.c b/src/code/fault_gc.c new file mode 100644 index 000000000..f78bc152d --- /dev/null +++ b/src/code/fault_gc.c @@ -0,0 +1,1338 @@ +/** + * @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, DPad-Right or 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 versions.h first and redefine FAULT_VERSION +// This allows this file to compile even when versions.h uses FAULT_N64 +#include "versions.h" +#undef FAULT_VERSION +#define FAULT_VERSION FAULT_GC + +#include "global.h" +#include "fault.h" +#include "terminal.h" +#include "alloca.h" + +#pragma increment_block_number "gc-eu:64 gc-eu-mq:64 gc-eu-mq-dbg:222 gc-jp:64 gc-jp-ce:64 gc-jp-mq:64 gc-us:64" \ + "gc-us-mq:64" + +void FaultDrawer_Init(void); +void FaultDrawer_SetOsSyncPrintfEnabled(u32 enabled); +void FaultDrawer_DrawRecImpl(s32 xStart, s32 yStart, s32 xEnd, s32 yEnd, u16 color); +void FaultDrawer_FillScreen(void); +void FaultDrawer_SetInputCallback(void (*callback)(void)); +void FaultDrawer_SetDrawerFB(void* fb, u16 w, u16 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", +}; + +FaultMgr* sFaultInstance; +u8 sFaultAwaitingInput; +STACK(sFaultStack, 0x600); +StackEntry sFaultThreadInfo; +FaultMgr gFaultMgr; + +typedef struct FaultClientTask { + /* 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; + + Sleep_Cycles(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; + OSMesg recMsg; + OSThread* thread = NULL; + OSTimer timer; + u32 timerMsgVal = 666; + + osCreateMesgQueue(&queue, &msg, 1); + 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, THREAD_ID_FAULT, Fault_ClientProcessThread, task, sFaultInstance->clientThreadSp, + THREAD_PRI_FAULT_CLIENT); + osStartThread(thread); + } else { + // Run the fault client callback on this thread + Fault_ClientProcessThread(task); + } + + // Await done + while (true) { + osSetTimer(&timer, OS_SEC_TO_CYCLES(1), 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, void* 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) { + 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) { + 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_AddAddrConvClient(FaultAddrConvClient* client, void* 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) { + osSyncPrintf(VT_COL(RED, WHITE) "fault_AddressConverterAddClient: %08x は既にリスト中にある\n" VT_RST, client); + } +} + +void Fault_RemoveAddrConvClient(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) { + 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_RemoveAddrConvClient(client); + } else if (ret != 0) { + return (uintptr_t)ret; + } + } + client = client->next; + } + + return 0; +} + +void Fault_Sleep(u32 msec) { + Fault_SleepImpl(msec); +} + +#ifndef AVOID_UB +void PadMgr_RequestPadData(Input* inputs, s32 gameRequest); +#endif + +void Fault_PadCallback(Input* inputs) { + //! @bug This function is not called correctly, it is missing a leading PadMgr* argument. This + //! renders the crash screen unusable. + //! In Majora's Mask, PadMgr functions were changed to not require this argument, and this was + //! likely just not addressed when backporting. +#ifndef AVOID_UB + PadMgr_RequestPadData(inputs, false); +#else + // Guarantee crashing behavior: false -> NULL, previous value in a2 is more often non-zero than zero + PadMgr_RequestPadData((PadMgr*)inputs, NULL, true); +#endif +} + +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 == BTN_L) { + sFaultInstance->autoScroll = !sFaultInstance->autoScroll; + } + + if (sFaultInstance->autoScroll) { + if (count-- < 1) { + return false; + } + } else { + if (pressedBtn == BTN_A || pressedBtn == BTN_DRIGHT) { + return false; + } + + if (pressedBtn == BTN_DLEFT) { + return true; + } + + if (pressedBtn == BTN_DUP) { + FaultDrawer_SetOsSyncPrintfEnabled(true); + } + + if (pressedBtn == BTN_DDOWN) { + 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) >> 23) - 127; + + if ((exp > -127 && exp <= 127) || 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) >> 23) - 127; + + if ((exp > -127 && exp <= 127) || 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* Fault_FindFaultedThread(void) { + OSThread* thread = __osGetActiveQueue(); + + // OS_PRIORITY_THREADTAIL indicates the end of the thread queue + while (thread->priority != OS_PRIORITY_THREADTAIL) { + 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) { + s32 pad; + OSTime start = osGetTime(); + + do { + Fault_Sleep(1000 / 60); + } while ((osGetTime() - start) < OS_SEC_TO_CYCLES(5) + 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; + + if (1) {} + if (1) {} + + // KeyWaitB (LRZ Up Down Up Down Left Left Right Right B A START) + 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"); + // KeyWaitB'(LR Left Right START) + 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 == (BTN_Z | BTN_L | BTN_R) && pressedBtn == BTN_Z) { + state = s2; + s1 = s2; + } + break; + case 1: + if (pressedBtn == BTN_DUP) { + state = 2; + } else { + state = 0; + } + break; + case 2: + if (pressedBtn == BTN_CDOWN) { + state = 3; + s1 = s2; + } else { + state = 0; + } + break; + case 3: + if (pressedBtn == BTN_CUP) { + state = 4; + } else { + state = 0; + } + break; + case 4: + if (pressedBtn == BTN_DDOWN) { + state = 5; + s1 = s2; + } else { + state = 0; + } + break; + case 5: + if (pressedBtn == BTN_DLEFT) { + state = 6; + } else { + state = 0; + } + break; + case 6: + if (pressedBtn == BTN_CLEFT) { + state = 7; + s1 = s2; + } else { + state = 0; + } + break; + case 7: + if (pressedBtn == BTN_CRIGHT) { + state = 8; + } else { + state = 0; + } + break; + case 8: + if (pressedBtn == BTN_DRIGHT) { + state = 9; + s1 = s2; + } else { + state = 0; + } + break; + case 9: + if (pressedBtn == (BTN_A | BTN_B)) { + state = 10; + } else if (pressedBtn == BTN_A) { + state = 0x5B; + } else if (pressedBtn == BTN_B) { + state = 0x5C; + } else { + state = 0; + } + break; + case 0x5B: + if (pressedBtn == BTN_B) { + state = 10; + } else { + state = 0; + } + break; + case 0x5C: + if (pressedBtn == BTN_A) { + state = 10; + } else { + state = 0; + } + break; + case 10: + if (pressedBtn == BTN_START) { + state = 11; + } else { + state = 0; + } + break; + } + } + + osWritebackDCacheAll(); + } +} + +void Fault_DrawMemDumpContents(const char* title, uintptr_t addr, 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, BTN_L)) { + // 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 + //! @bug DPad-Right does not move to the next page, unlike when on any other page + // START moving to the next page is unique to this page. + if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->cur.button, BTN_A)) { + return; + } + + // Memory dump controls + + off = 0x10; + if (CHECK_BTN_ALL(input->cur.button, BTN_Z)) { + off *= 0x10; + } + + if (CHECK_BTN_ALL(input->cur.button, BTN_B)) { + off *= 0x100; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + addr -= off; + } + if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + addr += off; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + addr = pc; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + addr = sp; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + addr = cLeftJump; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + addr = cRightJump; + } + } while (!CHECK_BTN_ALL(input->press.button, BTN_L)); + + // 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 ($sp) is found, fetch the saved $ra from stack memory + * - If addiu $sp, $sp, 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, ($sp) + // read return address saved on the stack + ra = *(uintptr_t*)K0_TO_K1(sp + imm); + } else if (insnHi == 0x27BD) { + // addiu $sp, $sp, + // 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 + // 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; + 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(void* arg) { + OSMesg msg; + OSThread* faultedThread; + 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: Unknown message received + osSyncPrintf("フォルトマネージャ:不明なメッセージを受信しました\n"); + } + + faultedThread = __osGetCurrFaultedThread(); + osSyncPrintf("__osGetCurrFaultedThread()=%08x\n", faultedThread); + + if (faultedThread == NULL) { + faultedThread = Fault_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, 1); + StackCheck_Init(&sFaultThreadInfo, sFaultStack, STACK_TOP(sFaultStack), 0, 0x100, "fault"); + osCreateThread(&sFaultInstance->thread, THREAD_ID_FAULT, Fault_ThreadEntry, NULL, STACK_TOP(sFaultStack), + THREAD_PRI_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. + */ +NORETURN void Fault_AddHungupAndCrashImpl(const char* exp1, const char* exp2) { + FaultClient client; + s32 pad; + + Fault_AddClient(&client, Fault_HungupFaultClient, (void*)exp1, (void*)exp2); + *(u32*)0x11111111 = 0; // trigger an exception via unaligned memory access + + // Since the above line triggers an exception and transfers execution to the fault handler + // this function does not return and the rest of the function is unreachable. +#ifdef __GNUC__ + __builtin_unreachable(); +#endif +} + +/** + * Like `Fault_AddHungupAndCrashImpl`, however provides a fixed message containing + * filename and line number + */ +NORETURN void Fault_AddHungupAndCrash(const char* file, int line) { + char msg[256]; + + sprintf(msg, "HungUp %s:%d", file, line); + Fault_AddHungupAndCrashImpl(msg, NULL); +} diff --git a/src/code/fault_gc_drawer.c b/src/code/fault_gc_drawer.c new file mode 100644 index 000000000..bb53bb133 --- /dev/null +++ b/src/code/fault_gc_drawer.c @@ -0,0 +1,350 @@ +/** + * @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 versions.h first and redefine FAULT_VERSION +// This allows this file to compile even when versions.h uses FAULT_N64 +#include "versions.h" +#undef FAULT_VERSION +#define FAULT_VERSION FAULT_GC + +#include "global.h" +#include "fault.h" +#include "terminal.h" + +typedef struct FaultDrawer { + /* 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 + +const u32 sFaultDrawerFont[] = { + 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, 0x06611DC0, 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, + 0x05589760, 0x0DD22990, 0x05599770, 0x04DFFD40, 0x026EF700, 0x00000000, 0x00000000, 0x08BFFB00, 0x0EFFFFC0, + 0x0BF00FB0, 0x0FF00330, 0x0FF00FF0, 0x0FF00220, 0x0CFBBF60, 0x0FFCCE20, 0x0DD44FF0, 0x0FF00220, 0x0FF00FF0, + 0x0FF00330, 0x0CFBBF40, 0x0EF77740, 0x00000000, 0x00000000, 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, 0x06611DC0, + 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, 0x05589760, 0x0DD22990, 0x05599770, 0x04DFFD40, + 0x026EF700, 0x00000000, 0x00000000, 0x08BFFB00, 0x000DE000, 0x0BF00FB0, 0x005DE600, 0x0FF00FF0, 0x055CC660, + 0x0CFBBF60, 0x773FF377, 0x0DD44FF0, 0xBB3FF3BB, 0x0FF00FF0, 0x099CCAA0, 0x0CFBBF40, 0x009DEA00, 0x00000000, + 0x000DE000, 0x04C22C40, 0x028D5020, 0x0CCAACC0, 0x21F91710, 0x04C22C40, 0x12493400, 0x00820800, 0x01975110, + 0x088A8880, 0x04615241, 0x00800800, 0x43117530, 0x00A20800, 0x60055600, 0x00000000, 0x04400040, 0x00221100, + 0x00000080, 0x000FB000, 0x00000880, 0x040DA400, 0x00008800, 0x08CDE880, 0x022AA220, 0x08CDE880, 0x02AA2220, + 0x040DA400, 0x0CD10000, 0x000FB000, 0x8C510000, 0x00221100, 0x81100000, 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, + 0x06611DC0, 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, 0x05589760, 0x0DD22990, 0x05599770, + 0x04DFFD40, 0x026EF700, 0x00000000, 0x00000000, 0x00333300, 0x04489980, 0x033CC330, 0x00CD1088, 0x033CC330, + 0x02BF62A8, 0x00333320, 0x01104C80, 0x01100330, 0x0015C800, 0x033CC330, 0x02673220, 0x003FF300, 0x04409900, + 0x00880000, 0x00000000, 0x05DFFD10, 0x07FFFF60, 0x1CE00EC1, 0x0FF00990, 0x1EE11661, 0x0FF00110, 0x1EF45621, + 0x0FF66710, 0x1EF23661, 0x0FF08990, 0x1EF10FE1, 0x0FF00990, 0x16ECCE21, 0x07FBBB20, 0x01111110, 0x00000000, + 0x09B66FD0, 0x27D88E60, 0x0992ED10, 0x2FF02EE0, 0x099AE510, 0x2FF62EE0, 0x099B7510, 0x2FD64EE0, 0x0DDAE510, + 0x2FD04EE0, 0x0DD2ED10, 0x2FD00EE0, 0x09F66F90, 0x27D99F70, 0x00000000, 0x00000000, 0x07FFFF00, 0x8F711FF0, + 0x2FD00FF0, 0x8F711FF0, 0x2FD00770, 0x8E611EE0, 0x27DDDF60, 0x8E691EE0, 0x27764AA0, 0x8EE99EE0, 0x2FD06E80, + 0x8AE7FEA0, 0x07FA8E60, 0x88277A80, 0x00000000, 0x00000000, 0x077CCFF0, 0x13266011, 0x077CCFF0, 0x03766510, + 0x0239D720, 0x04533540, 0x002FF200, 0x01133110, 0x005FB100, 0x00033000, 0x055EE550, 0x01133110, 0x055EEDD0, + 0x02233000, 0x00088880, 0x8AABB888, 0x00001100, 0x00044510, 0x04623320, 0x00440110, 0x04C89AA0, 0x00EEAB10, + 0x0CE66720, 0x0EF55FB0, 0x0EE00660, 0x0BF62B90, 0x0EE00660, 0x03FC8990, 0x04EEEEA0, 0x00773BB0, 0x00000000, + 0x08888800, 0x09900000, 0x00111000, 0x09922440, 0x00011000, 0x09908800, 0x26EFDE20, 0x099BB540, 0x2EC33CE2, + 0x0D9A2550, 0x2EC33CE2, 0x0DDAA550, 0x2EC33CE2, 0x09D6ED10, 0x26CBBC62, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00011000, 0x05FBFFE0, 0x8E6116E8, 0x0FF40330, 0x8F7117F8, 0x07FC8B30, 0x8E6996E8, + 0x05733BA0, 0x8A6DD6A8, 0x0DD88A20, 0x08A779B2, 0x01100220, 0x00000000, 0x00000080, 0x8A011000, 0x00000800, + 0x80A11000, 0x07744F70, 0x80A99000, 0x0231DF20, 0x84E60004, 0x0027DA20, 0xC8AA4C40, 0x00573B20, 0x00A11800, + 0x05546F50, 0x00A99800, 0x02222080, 0x02001888, +}; + +#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, +}; + +#pragma increment_block_number "gc-eu:128 gc-eu-mq:128" + +FaultDrawer sFaultDrawer; +char D_8016B6C0[0x20]; + +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, 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) { + u16* fb; + s32 x, y; + const u32* dataPtr; + u32 data; + s32 cursorX = sFaultDrawer.cursorX; + s32 cursorY = sFaultDrawer.cursorY; + const u32** fontData = &sFaultDrawer.fontData; + s32 shift = c % 4; + + dataPtr = &fontData[0][(((c / 8) * 16) + ((c & 4) >> 2))]; + 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); +} + +void* FaultDrawer_PrintCallback(void* 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; +} + +s32 FaultDrawer_VPrintf(const char* fmt, va_list args) { + return _Printf(FaultDrawer_PrintCallback, &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/code/fault_n64.c b/src/code/fault_n64.c new file mode 100644 index 000000000..74e963377 --- /dev/null +++ b/src/code/fault_n64.c @@ -0,0 +1,845 @@ +// Include versions.h first and redefine FAULT_VERSION +// This allows this file to compile even when versions.h uses FAULT_GC +#include "versions.h" +#undef FAULT_VERSION +#define FAULT_VERSION FAULT_N64 + +#include "global.h" +#include "fault.h" +#include "terminal.h" + +typedef struct FaultMgr { + OSThread thread; + char unk_1B0[0x400]; + OSMesgQueue queue; + OSMesg msg; + u16* fb; + u16 fbWidth; + u16 fbDepth; + FaultClient* clients; +} FaultMgr; // size = 0x5D8 + +typedef struct FaultCursorCoords { + s32 x; + s32 y; +} FaultCursorCoords; // size = 0x8 + +u32 sFaultDrawerFont[] = { + 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, 0x06611DC0, 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, + 0x05589760, 0x0DD22990, 0x05599770, 0x04DFFD40, 0x026EF700, 0x00000000, 0x00000000, 0x08BFFB00, 0x0EFFFFC0, + 0x0BF00FB0, 0x0FF00330, 0x0FF00FF0, 0x0FF00220, 0x0CFBBF60, 0x0FFCCE20, 0x0DD44FF0, 0x0FF00220, 0x0FF00FF0, + 0x0FF00330, 0x0CFBBF40, 0x0EF77740, 0x00000000, 0x00000000, 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, 0x06611DC0, + 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, 0x05589760, 0x0DD22990, 0x05599770, 0x04DFFD40, + 0x026EF700, 0x00000000, 0x00000000, 0x08BFFB00, 0x000DE000, 0x0BF00FB0, 0x005DE600, 0x0FF00FF0, 0x055CC660, + 0x0CFBBF60, 0x773FF377, 0x0DD44FF0, 0xBB3FF3BB, 0x0FF00FF0, 0x099CCAA0, 0x0CFBBF40, 0x009DEA00, 0x00000000, + 0x000DE000, 0x04C22C40, 0x028D5020, 0x0CCAACC0, 0x21F91710, 0x04C22C40, 0x12493400, 0x00820800, 0x01975110, + 0x088A8880, 0x04615241, 0x00800800, 0x43117530, 0x00A20800, 0x60055600, 0x00000000, 0x04400040, 0x00221100, + 0x00000080, 0x000FB000, 0x00000880, 0x040DA400, 0x00008800, 0x08CDE880, 0x022AA220, 0x08CDE880, 0x02AA2220, + 0x040DA400, 0x0CD10000, 0x000FB000, 0x8C510000, 0x00221100, 0x81100000, 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, + 0x06611DC0, 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, 0x05589760, 0x0DD22990, 0x05599770, + 0x04DFFD40, 0x026EF700, 0x00000000, 0x00000000, 0x00333300, 0x04489980, 0x033CC330, 0x00CD1088, 0x033CC330, + 0x02BF62A8, 0x00333320, 0x01104C80, 0x01100330, 0x0015C800, 0x033CC330, 0x02673220, 0x003FF300, 0x04409900, + 0x00880000, 0x00000000, 0x05DFFD10, 0x07FFFF60, 0x1CE00EC1, 0x0FF00990, 0x1EE11661, 0x0FF00110, 0x1EF45621, + 0x0FF66710, 0x1EF23661, 0x0FF08990, 0x1EF10FE1, 0x0FF00990, 0x16ECCE21, 0x07FBBB20, 0x01111110, 0x00000000, + 0x09B66FD0, 0x27D88E60, 0x0992ED10, 0x2FF02EE0, 0x099AE510, 0x2FF62EE0, 0x099B7510, 0x2FD64EE0, 0x0DDAE510, + 0x2FD04EE0, 0x0DD2ED10, 0x2FD00EE0, 0x09F66F90, 0x27D99F70, 0x00000000, 0x00000000, 0x07FFFF00, 0x8F711FF0, + 0x2FD00FF0, 0x8F711FF0, 0x2FD00770, 0x8E611EE0, 0x27DDDF60, 0x8E691EE0, 0x27764AA0, 0x8EE99EE0, 0x2FD06E80, + 0x8AE7FEA0, 0x07FA8E60, 0x88277A80, 0x00000000, 0x00000000, 0x077CCFF0, 0x13266011, 0x077CCFF0, 0x03766510, + 0x0239D720, 0x04533540, 0x002FF200, 0x01133110, 0x005FB100, 0x00033000, 0x055EE550, 0x01133110, 0x055EEDD0, + 0x02233000, 0x00088880, 0x8AABB888, 0x00001100, 0x00044510, 0x04623320, 0x00440110, 0x04C89AA0, 0x00EEAB10, + 0x0CE66720, 0x0EF55FB0, 0x0EE00660, 0x0BF62B90, 0x0EE00660, 0x03FC8990, 0x04EEEEA0, 0x00773BB0, 0x00000000, + 0x08888800, 0x09900000, 0x00111000, 0x09922440, 0x00011000, 0x09908800, 0x26EFDE20, 0x099BB540, 0x2EC33CE2, + 0x0D9A2550, 0x2EC33CE2, 0x0DDAA550, 0x2EC33CE2, 0x09D6ED10, 0x26CBBC62, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00011000, 0x05FBFFE0, 0x8E6116E8, 0x0FF40330, 0x8F7117F8, 0x07FC8B30, 0x8E6996E8, + 0x05733BA0, 0x8A6DD6A8, 0x0DD88A20, 0x08A779B2, 0x01100220, 0x00000000, 0x00000080, 0x8A011000, 0x00000800, + 0x80A11000, 0x07744F70, 0x80A99000, 0x0231DF20, 0x84E60004, 0x0027DA20, 0xC8AA4C40, 0x00573B20, 0x00A11800, + 0x05546F50, 0x00A99800, 0x02222080, 0x02001888, +}; + +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", +}; + +u16 sFaultFontColor = GPACK_RGBA5551(255, 255, 255, 1); + +Input sFaultInputs[MAXCONTROLLERS]; + +FaultMgr gFaultMgr; + +STACK(sFaultStack, 0x400); +StackEntry sFaultStackInfo; +FaultCursorCoords sFaultCursorPos; + +vs32 sFaultExit; +vs32 gFaultMsgId; +vs32 sFaultDisplayEnable; +OSThread* sFaultFaultedThread; +s32 B_80122570[0x10]; + +void Fault_SleepImpl(u32 ms) { + Sleep_Msec(ms); +} + +void Fault_WaitForInputImpl(void) { + Input* inputs = sFaultInputs; + u16 btnPress; + + do { + Fault_SleepImpl(0x10); + PadMgr_RequestPadData(&gPadMgr, inputs, 0); + btnPress = inputs[0].press.button; + } while (!CHECK_BTN_ANY(btnPress, (BTN_A | BTN_B | BTN_START | BTN_CRIGHT | BTN_CLEFT | BTN_CDOWN | BTN_CUP))); +} + +void Fault_WaitForInput(void) { + Fault_WaitForInputImpl(); +} + +void Fault_DrawRec(s32 x, s32 y, s32 w, s32 h, u16 color) { + s32 i; + s32 j; + u16* fbPtr; + + fbPtr = gFaultMgr.fb + (gFaultMgr.fbWidth * y) + x; + + for (i = 0; i < h; i++) { + j = 0; + for (j = 0; j < w; j++) { + *fbPtr = color; + fbPtr++; + } + fbPtr += gFaultMgr.fbWidth - w; + } + osWritebackDCacheAll(); +} + +void Fault_DrawRecBlack(s32 x, s32 y, s32 w, s32 h) { + Fault_DrawRec(x, y, w, h, GPACK_RGBA5551(0, 0, 0, 1)); +} + +void Fault_DrawCharImpl(s32 x, s32 y, char c) { + u32* dataPtr; + s32 shift = c % 4; + s32 i; + s32 j; + u16* fbPtr; + u32 data; + + dataPtr = sFaultDrawerFont + ((c / 8 * 0x10) + ((c & 4) >> 2)); + fbPtr = gFaultMgr.fb + (gFaultMgr.fbWidth * y) + x; + + for (i = 0; i < 8; i++) { + u32 mask; + + mask = (1 << 28) << shift; + data = *dataPtr; + for (j = 0; j < 8; j++) { + if (mask & data) { + *fbPtr = sFaultFontColor; + } + fbPtr++; + mask >>= 4; + } + dataPtr += 2; + fbPtr += gFaultMgr.fbWidth - 8; + } + osWritebackDCacheAll(); +} + +void Fault_DrawChar(s32 x, s32 y, u8 c) { + Fault_DrawCharImpl(x, y, c); +} + +void Fault_DrawCornerRec(u16 color) { + Fault_DrawRec(22, 16, 8, 1, color); +} + +void Fault_DrawCornerRecRed(void) { + Fault_DrawCornerRec(GPACK_RGBA5551(255, 0, 0, 1)); +} + +void Fault_DrawCornerRecYellow(void) { + Fault_DrawCornerRec(GPACK_RGBA5551(255, 255, 0, 1)); +} + +void func_800AE05C(void) { +} + +void* Fault_PrintCallbackDraw(void* arg, const char* str, size_t len) { + FaultCursorCoords* coords = arg; + + for (; len != 0; len--, str++) { + if (*str == '\n') { + coords->x = 320; + } else { + Fault_DrawChar(coords->x, coords->y, *str); + coords->x += 6; + } + if (coords->x > 276) { + coords->x = 22; + coords->y += 8; + if (coords->y > 208) { + Fault_WaitForInputImpl(); + Fault_DrawRecBlack(22, 16, 276, 208); + coords->y = 16; + } + } + } + return coords; +} + +void Fault_DrawText(s32 x, s32 y, const char* fmt, ...) { + va_list args; + FaultCursorCoords coords; + + va_start(args, fmt); + + coords.x = x - 8; + coords.y = y; + _Printf(Fault_PrintCallbackDraw, &coords, fmt, args); + + va_end(args); +} + +void Fault_SetCursor(s32 x, s32 y) { + sFaultCursorPos.x = x; + sFaultCursorPos.y = y; +} + +void func_800AE1F8(void) { + Fault_DrawRecBlack(22, 16, 276, 208); + sFaultCursorPos.x = 22; + sFaultCursorPos.y = 16; +} + +s32 Fault_Printf(const char* fmt, ...) { + s32 ret; + va_list args; + + va_start(args, fmt); + ret = _Printf(Fault_PrintCallbackDraw, &sFaultCursorPos, fmt, args); + va_end(args); + + return ret; +} + +void Fault_PrintFReg(s32 x, s32 y, s32 idx, f32* value) { + u32 raw = *(u32*)value; + s32 exp = ((raw & 0x7F800000) >> 23) - 127; + + if (((exp > -127) && (exp <= 127)) || (raw == 0)) { + Fault_DrawText(x, y, "F%02d:%.7e", idx, *value); + } else { + Fault_DrawText(x, y, "F%02d:-------------", idx); + } +} + +void Fault_LogFReg(s32 idx, f32* value) { + u32 raw = *(u32*)value; + s32 exp = ((raw & 0x7F800000) >> 23) - 127; + + if (((exp >= -0x7E) && (exp < 0x80)) || (raw == 0)) { + osSyncPrintf("F%02d:%.7e ", idx, *value); + } else { + osSyncPrintf("F%02d:------------- ", idx); + } +} + +void Fault_PrintFPCSR(s32 x, s32 y, s32 value) { + s32 i; + u32 mask = FPCSR_CE; + + Fault_DrawText(x, y, "FPCSR:%08xH", value); + + for (i = 0; i < 6; i++) { + if (value & mask) { + Fault_DrawText(x + 100, y, "(%s)", sFpExceptionNames[i]); + break; + } + + mask >>= 1; + } +} + +void Fault_LogFPCSR(s32 value) { + s32 i; + u32 mask = FPCSR_CE; + + osSyncPrintf("FPCSR:%08xH ", value); + + for (i = 0; i < 6; i++) { + if (value & mask) { + osSyncPrintf("(%s)\n", sFpExceptionNames[i]); + break; + } + + mask >>= 1; + } +} + +void Fault_PrintThreadContext(OSThread* thread) { + __OSThreadContext* ctx = &thread->context; + s32 y; + 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; + } + + Fault_DrawRecBlack(22, 16, 276, 24); + + y = 20; + Fault_DrawText(30, y, "THREAD:%d (%d:%s)", thread->id, causeStrIdx, sExceptionNames[causeStrIdx]); + + y += 9; + Fault_DrawText(30, y, "PC:%08xH SR:%08xH VA:%08xH", ctx->pc, ctx->sr, ctx->badvaddr); + + y += 13; + Fault_DrawRecBlack(22, 40, 276, 184); + Fault_DrawText(30, y, "AT:%08xH V0:%08xH V1:%08xH", (u32)ctx->at, (u32)ctx->v0, (u32)ctx->v1); + y += 9; + Fault_DrawText(30, y, "A0:%08xH A1:%08xH A2:%08xH", (u32)ctx->a0, (u32)ctx->a1, (u32)ctx->a2); + y += 9; + Fault_DrawText(30, y, "A3:%08xH T0:%08xH T1:%08xH", (u32)ctx->a3, (u32)ctx->t0, (u32)ctx->t1); + y += 9; + Fault_DrawText(30, y, "T2:%08xH T3:%08xH T4:%08xH", (u32)ctx->t2, (u32)ctx->t3, (u32)ctx->t4); + y += 9; + Fault_DrawText(30, y, "T5:%08xH T6:%08xH T7:%08xH", (u32)ctx->t5, (u32)ctx->t6, (u32)ctx->t7); + y += 9; + Fault_DrawText(30, y, "S0:%08xH S1:%08xH S2:%08xH", (u32)ctx->s0, (u32)ctx->s1, (u32)ctx->s2); + y += 9; + Fault_DrawText(30, y, "S3:%08xH S4:%08xH S5:%08xH", (u32)ctx->s3, (u32)ctx->s4, (u32)ctx->s5); + y += 9; + Fault_DrawText(30, y, "S6:%08xH S7:%08xH T8:%08xH", (u32)ctx->s6, (u32)ctx->s7, (u32)ctx->t8); + y += 9; + Fault_DrawText(30, y, "T9:%08xH GP:%08xH SP:%08xH", (u32)ctx->t9, (u32)ctx->gp, (u32)ctx->sp); + y += 9; + Fault_DrawText(30, y, "S8:%08xH RA:%08xH LO:%08xH", (u32)ctx->s8, (u32)ctx->ra, (u32)ctx->lo); + + y += 13; + Fault_PrintFPCSR(30, y, ctx->fpcsr); + + y += 13; + Fault_PrintFReg(30, y, 0, &ctx->fp0.f.f_even); + Fault_PrintFReg(160, y, 2, &ctx->fp2.f.f_even); + y += 9; + Fault_PrintFReg(30, y, 4, &ctx->fp4.f.f_even); + Fault_PrintFReg(160, y, 6, &ctx->fp6.f.f_even); + y += 9; + Fault_PrintFReg(30, y, 8, &ctx->fp8.f.f_even); + Fault_PrintFReg(160, y, 10, &ctx->fp10.f.f_even); + y += 9; + Fault_PrintFReg(30, y, 12, &ctx->fp12.f.f_even); + Fault_PrintFReg(160, y, 14, &ctx->fp14.f.f_even); + y += 9; + Fault_PrintFReg(30, y, 16, &ctx->fp16.f.f_even); + Fault_PrintFReg(160, y, 18, &ctx->fp18.f.f_even); + y += 9; + Fault_PrintFReg(30, y, 20, &ctx->fp20.f.f_even); + Fault_PrintFReg(160, y, 22, &ctx->fp22.f.f_even); + y += 9; + Fault_PrintFReg(30, y, 24, &ctx->fp24.f.f_even); + Fault_PrintFReg(160, y, 26, &ctx->fp26.f.f_even); + y += 9; + Fault_PrintFReg(30, y, 28, &ctx->fp28.f.f_even); + Fault_PrintFReg(160, y, 30, &ctx->fp30.f.f_even); + + osWritebackDCacheAll(); +} + +void Fault_LogThreadContext(OSThread* thread) { + __OSThreadContext* ctx = &thread->context; + 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; + } + + osSyncPrintf("\n"); + osSyncPrintf("THREAD ID:%d (%d:%s)\n", thread->id, causeStrIdx, sExceptionNames[causeStrIdx]); + osSyncPrintf("PC:%08xH SR:%08xH VA:%08xH\n", ctx->pc, ctx->sr, 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((s32)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"); +} + +OSThread* Fault_FindFaultedThread(void) { + OSThread* thread = __osGetActiveQueue(); + + // OS_PRIORITY_THREADTAIL indicates the end of the thread queue + while (thread->priority != OS_PRIORITY_THREADTAIL) { + 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_WaitForButtonCombo(void) { + Input* inputs = sFaultInputs; + s32 btnPress; + s32 btnCur; + OSTime comboStartTime; + s32 x; + s32 y; + s32 count; + s32 pad[4]; + + // KeyWaitB (LRZ Up Down Up Down Left Left Right Right B A START) + 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"); + x = 0; + y = 0; + count = 0; + while (x != 11) { + if ((count % 30) == 1) {} + if ((count % 30) == 0) { + Fault_DrawCornerRecYellow(); + } + count++; + + Fault_SleepImpl(1000 / 60); + PadMgr_RequestPadData(&gPadMgr, inputs, 0); + btnCur = inputs[0].cur.button; + btnPress = inputs[0].press.button; + if ((btnCur == 0) && (y == 1)) { + y = 0; + osSyncPrintf("x=%d y=%d\n", x, y); + } else if (btnPress != 0) { + if (y == 1) { + x = 0; + osSyncPrintf("x=%d y=%d\n", x, y); + } + switch (x) { + case 0: + if ((btnCur == (BTN_Z | BTN_L | BTN_R)) && (btnPress == BTN_Z)) { + x = 1; + y = 1; + comboStartTime = osGetTime(); + } + break; + + case 1: + if (btnPress == BTN_DUP) { + x = 2; + } else { + x = 0; + } + break; + + case 2: + if (btnPress == BTN_CDOWN) { + x = 3; + y = 1; + } else { + x = 0; + } + break; + + case 3: + if (btnPress == BTN_CUP) { + x = 4; + } else { + x = 0; + } + break; + + case 4: + if (btnPress == BTN_DDOWN) { + x = 5; + y = 1; + } else { + x = 0; + } + break; + + case 5: + if (btnPress == BTN_DLEFT) { + x = 6; + } else { + x = 0; + } + break; + + case 6: + if (btnPress == BTN_CLEFT) { + x = 7; + y = 1; + } else { + x = 0; + } + break; + + case 7: + if (btnPress == BTN_CRIGHT) { + x = 8; + } else { + x = 0; + } + break; + + case 8: + if (btnPress == BTN_DRIGHT) { + x = 9; + y = 1; + } else { + x = 0; + } + break; + + case 9: + if (btnPress == (BTN_A | BTN_B)) { + x = 10; + } else if (btnPress == BTN_A) { + x = 91; + } else if (btnPress == BTN_B) { + x = 92; + } else { + x = 0; + } + break; + + case 91: + if (btnPress == BTN_B) { + x = 10; + } else { + x = 0; + } + break; + + case 92: + if (btnPress == BTN_A) { + x = 10; + } else { + x = 0; + } + break; + + case 10: + if ((btnCur == (BTN_A | BTN_B | BTN_START)) && (btnPress == BTN_START)) { + f32 comboTimeSeconds = OS_CYCLES_TO_USEC(osGetTime() - comboStartTime) / 1000000.0f; + + // Input time %f seconds + osSyncPrintf("入力時間 %f 秒\n", comboTimeSeconds); + if (comboTimeSeconds <= 50.0f) { + x = 11; + } else { + x = 0; + } + } else { + x = 0; + } + break; + + default: + break; + } + } + } +} + +void func_800AF0E0(void) { + s32 i; + + Fault_DrawRecBlack(22, 16, 276, 208); + Fault_DrawText(40, 30, "SegmentBaseAddress"); + + for (i = 0; i < ARRAY_COUNT(gSegments); i++) { + Fault_DrawText(40, 40 + 8 * i, "%2d:%08x", i, gSegments[i]); + } + Fault_DrawText(40, 180, "Arena_failcnt = %d", gTotalAllocFailures); +} + +void Fault_DrawMemDumpContents(const char* title, void* memory, u32 arg2) { + s32 x; + s32 y; + u32* ptr = (u32*)((uintptr_t)memory & ~3); + + Fault_DrawRecBlack(22, 16, 276, 208); + Fault_DrawText(36, 18, "%s %08x", title != NULL ? title : "PrintDump", ptr); + + if (((uintptr_t)ptr >= PHYS_TO_K0(0x000000)) && ((uintptr_t)ptr <= PHYS_TO_K0(0x400000))) { + for (y = 28; y < 226; y += 9) { + Fault_DrawText(28, y, "%06x", ptr); + for (x = 82; x < 290; x += 52) { + Fault_DrawText(x, y, "%08x", *(ptr++)); + } + } + } +} + +void Fault_DrawMemDumpPC(OSThread* thread) { + u32* pc = (u32*)thread->context.pc; + + if (((uintptr_t)pc >= 0x80000020) && ((uintptr_t)pc <= 0x80400000)) { + Fault_DrawMemDumpContents("PC Trace", pc - 0x20, 0); + } +} + +void Fault_DrawMemDumpSP(OSThread* thread) { + u32* sp = (u32*)(u32)thread->context.sp; + + if (((uintptr_t)sp >= 0x80000020) && ((uintptr_t)sp <= 0x80400000)) { + Fault_DrawMemDumpContents("Stack Trace", sp, 0); + } +} + +void func_800AF3DC(void) { + s32 i; + + Fault_DrawRecBlack(22, 16, 276, 208); + Fault_DrawText(36, 18, "ROM DEBUG"); + + for (i = 0; i < ARRAY_COUNT(B_80122570); i++) { + Fault_DrawText(((i % 4) * 52) + 82, ((i / 4) * 9) + 28, "%08x", B_80122570[i]); + } +} + +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 func_800AF558(void) { + osViBlack(false); + if ((uintptr_t)osViGetCurrentFramebuffer() >= 0x80100000) { + gFaultMgr.fb = osViGetCurrentFramebuffer(); + } else { + gFaultMgr.fb = (u16*)(PHYS_TO_K0(osMemSize) - sizeof(u16[SCREEN_HEIGHT][SCREEN_WIDTH])); + osViSwapBuffer(gFaultMgr.fb); + } +} + +void Fault_AddClient(FaultClient* client, void* callback, void* arg0, void* arg1) { + FaultMgr* faultMgr = &gFaultMgr; + OSIntMask mask; + + mask = osSetIntMask(OS_IM_NONE); + + client->callback = callback; + client->arg0 = arg0; + client->arg1 = arg1; + client->next = faultMgr->clients; + faultMgr->clients = client; + + osSetIntMask(mask); +} + +void Fault_RemoveClient(FaultClient* client) { + u32 mask; + FaultClient* iterClient = gFaultMgr.clients; + FaultClient* lastClient = NULL; + + mask = osSetIntMask(OS_IM_NONE); + while (iterClient != NULL) { + if (iterClient == client) { + if (lastClient != NULL) { + lastClient->next = client->next; + } else { + gFaultMgr.clients = client->next; + } + break; + } else { + lastClient = iterClient; + iterClient = iterClient->next; + } + } + osSetIntMask(mask); +} + +void Fault_ProcessClients(void) { + s32 i; + FaultClient* client; + + client = gFaultMgr.clients; + i = 0; + while (client != NULL) { + if (client->callback != NULL) { + Fault_DrawRecBlack(22, 16, 276, 208); + Fault_DrawText(30, 20, "CallBack (%d) %08x %08x %08x", i++, client, client->arg0, client->arg1); + ((void (*)(void*, void*))client->callback)(client->arg0, client->arg1); + Fault_WaitForInput(); + } + client = client->next; + } +} + +#define FAULT_MSG_CPU_BREAK ((OSMesg)1) +#define FAULT_MSG_FAULT ((OSMesg)2) +#define FAULT_MSG_UNK ((OSMesg)3) + +void Fault_ThreadEntry(void* arg0) { + OSMesg msg; + OSThread* faultedThread; + + osSetEventMesg(OS_EVENT_CPU_BREAK, &gFaultMgr.queue, FAULT_MSG_CPU_BREAK); + osSetEventMesg(OS_EVENT_FAULT, &gFaultMgr.queue, FAULT_MSG_FAULT); + while (true) { + do { + osRecvMesg(&gFaultMgr.queue, &msg, OS_MESG_BLOCK); + if (msg == FAULT_MSG_CPU_BREAK) { + gFaultMsgId = (s32)FAULT_MSG_CPU_BREAK; + // Fault Manager: OS_EVENT_CPU_BREAK received + osSyncPrintf("フォルトマネージャ:OS_EVENT_CPU_BREAKを受信しました\n"); + } else if (msg == FAULT_MSG_FAULT) { + gFaultMsgId = (s32)FAULT_MSG_FAULT; + // Fault Manager: OS_EVENT_FAULT received + osSyncPrintf("フォルトマネージャ:OS_EVENT_FAULTを受信しました\n"); + } else { + gFaultMsgId = (s32)FAULT_MSG_UNK; + // Fault Manager: Unknown message received + osSyncPrintf("フォルトマネージャ:不明なメッセージを受信しました\n"); + } + faultedThread = __osGetCurrFaultedThread(); + osSyncPrintf("__osGetCurrFaultedThread()=%08x\n", faultedThread); + if (faultedThread == NULL) { + faultedThread = Fault_FindFaultedThread(); + osSyncPrintf("FindFaultedThread()=%08x\n", faultedThread); + } + } while (faultedThread == NULL); + sFaultFaultedThread = faultedThread; + Fault_LogThreadContext(faultedThread); + osSyncPrintf("%d %s %d:%s = %d\n", osGetThreadId(NULL), "fault.c", 1454, "fault_display_enable", + sFaultDisplayEnable); + while (!sFaultDisplayEnable) { + Fault_SleepImpl(1000); + } + Fault_SleepImpl(500); + Fault_DrawCornerRecRed(); + Fault_WaitForButtonCombo(); + do { + func_800AF558(); + Fault_PrintThreadContext(faultedThread); + Fault_WaitForInput(); + func_800AF0E0(); + Fault_WaitForInput(); + func_800AF3DC(); + Fault_WaitForInput(); + Fault_DrawMemDumpSP(faultedThread); + Fault_WaitForInput(); + Fault_DrawMemDumpPC(faultedThread); + Fault_WaitForInput(); + Fault_ProcessClients(); + } while (!sFaultExit); + while (!sFaultExit) {} + Fault_ResumeThread(faultedThread); + } +} + +void Fault_SetFrameBuffer(void* fb, u16 w, u16 h) { + gFaultMgr.fb = fb; + gFaultMgr.fbWidth = w; + gFaultMgr.fbDepth = h; +} + +void Fault_Init(void) { + sFaultDisplayEnable = 1; + gFaultMgr.fb = (u16*)(PHYS_TO_K0(osMemSize) - sizeof(u16[SCREEN_HEIGHT][SCREEN_WIDTH])); + gFaultMgr.fbWidth = SCREEN_WIDTH; + gFaultMgr.fbDepth = 16; + gFaultMgr.clients = 0; + osCreateMesgQueue(&gFaultMgr.queue, &gFaultMgr.msg, 1); + StackCheck_Init(&sFaultStackInfo, sFaultStack, STACK_TOP(sFaultStack), 0, 0x100, "fault"); + osCreateThread(&gFaultMgr.thread, THREAD_ID_FAULT, Fault_ThreadEntry, NULL, STACK_TOP(sFaultStack), + THREAD_PRI_FAULT); + osStartThread(&gFaultMgr.thread); +} + +void Fault_AddHungupAndCrashImpl(const char* exp1, const char* exp2) { + gFaultMsgId = 4; + osSyncPrintf("HungUp on Thread %d", osGetThreadId(NULL)); + osSyncPrintf("%s\n", exp1 != NULL ? exp1 : "(NULL)"); + osSyncPrintf("%s\n", exp2 != NULL ? exp2 : "(NULL)"); + while (sFaultDisplayEnable == 0) { + Fault_SleepImpl(1000); + } + Fault_SleepImpl(500); + Fault_WaitForButtonCombo(); + do { + func_800AF558(); + Fault_DrawRecBlack(22, 16, 276, 34); + Fault_DrawText(24, 18, "HungUp on Thread %d", osGetThreadId(NULL)); + Fault_DrawText(24, 28, "%s", exp1 != NULL ? exp1 : "(NULL)"); + Fault_DrawText(24, 38, "%s", exp2 != NULL ? exp2 : "(NULL)"); + Fault_WaitForInput(); + Fault_ProcessClients(); + } while (true); +} + +void Fault_AddHungupAndCrash(const char* file, int line) { + char msg[256]; + + sprintf(msg, "HungUp %s:%d", file, line); + Fault_AddHungupAndCrashImpl(msg, NULL); +} diff --git a/src/code/game.c b/src/code/game.c index 7a53acdda..37283ac5a 100644 --- a/src/code/game.c +++ b/src/code/game.c @@ -1,4 +1,7 @@ #include "global.h" +#if OOT_DEBUG +#include "fault.h" +#endif #include "terminal.h" #pragma increment_block_number "gc-eu:128 gc-eu-mq:128 gc-jp:128 gc-jp-ce:128 gc-jp-mq:128 gc-us:128 gc-us-mq:128" diff --git a/src/code/graph.c b/src/code/graph.c index f6cf66b12..1f9ba5e30 100644 --- a/src/code/graph.c +++ b/src/code/graph.c @@ -1,4 +1,5 @@ #include "global.h" +#include "fault.h" #include "terminal.h" #define GFXPOOL_HEAD_MAGIC 0x1234 diff --git a/src/code/main.c b/src/code/main.c index 2e46ae825..fa6b5f672 100644 --- a/src/code/main.c +++ b/src/code/main.c @@ -1,4 +1,5 @@ #include "global.h" +#include "fault.h" #include "terminal.h" #include "versions.h" #if PLATFORM_N64 diff --git a/src/code/padmgr.c b/src/code/padmgr.c index 75fb125ba..75db1fd4f 100644 --- a/src/code/padmgr.c +++ b/src/code/padmgr.c @@ -29,6 +29,7 @@ * done while waiting for this operation to complete. */ #include "global.h" +#include "fault.h" #include "terminal.h" #define PADMGR_LOG(controllerNum, msg) \ @@ -236,7 +237,7 @@ void PadMgr_RumbleStop(PadMgr* padMgr) { if (osMotorInit(serialEventQueue, &padMgr->rumblePfs[i], i) == 0) { // If there is a rumble pak attached to this controller, stop it - if (gFaultMgr.msgId == 0 && padMgr->rumbleOnTimer != 0) { + if (FAULT_MSG_ID == 0 && padMgr->rumbleOnTimer != 0) { PADMGR_LOG(i, T("振動パック 停止", "Stop rumble pak")); } osMotorStop(&padMgr->rumblePfs[i]); @@ -399,7 +400,7 @@ void PadMgr_HandleRetrace(PadMgr* padMgr) { } padMgr->validCtrlrsMask = mask; - if (gFaultMgr.msgId != 0) { + if (FAULT_MSG_ID != 0) { // If fault is active, no rumble PadMgr_RumbleStop(padMgr); } else if (padMgr->rumbleOffTimer > 0) { diff --git a/src/code/sched.c b/src/code/sched.c index ef34a4f6a..1deaa74b2 100644 --- a/src/code/sched.c +++ b/src/code/sched.c @@ -40,6 +40,7 @@ * @see irqmgr.c */ #include "global.h" +#include "fault.h" #define RSP_DONE_MSG 667 #define RDP_DONE_MSG 668 diff --git a/src/code/sys_matrix.c b/src/code/sys_matrix.c index 550b39e4e..ce9846086 100644 --- a/src/code/sys_matrix.c +++ b/src/code/sys_matrix.c @@ -1,4 +1,7 @@ #include "global.h" +#if OOT_DEBUG +#include "fault.h" +#endif // clang-format off Mtx gMtxClear = gdSPDefMtx( diff --git a/src/code/z_actor.c b/src/code/z_actor.c index f531ba6d6..ef90f6002 100644 --- a/src/code/z_actor.c +++ b/src/code/z_actor.c @@ -1,4 +1,5 @@ #include "global.h" +#include "fault.h" #include "quake.h" #include "terminal.h" diff --git a/src/code/z_actor_dlftbls.c b/src/code/z_actor_dlftbls.c index ff162db4b..b5a00068b 100644 --- a/src/code/z_actor_dlftbls.c +++ b/src/code/z_actor_dlftbls.c @@ -1,4 +1,5 @@ #include "global.h" +#include "fault.h" // Linker symbol declarations (used in the table below) #define DEFINE_ACTOR(name, _1, _2, _3) DECLARE_OVERLAY_SEGMENT(name) diff --git a/src/code/z_collision_check.c b/src/code/z_collision_check.c index 1680d9066..b80ac0bb5 100644 --- a/src/code/z_collision_check.c +++ b/src/code/z_collision_check.c @@ -5,6 +5,8 @@ #include "overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h" +#pragma increment_block_number "gc-eu:64 gc-eu-mq:64 gc-jp:64 gc-jp-ce:64 gc-jp-mq:64 gc-us:64 gc-us-mq:64" + typedef s32 (*ColChkResetFunc)(PlayState*, Collider*); typedef void (*ColChkApplyFunc)(PlayState*, CollisionCheckContext*, Collider*); typedef void (*ColChkVsFunc)(PlayState*, CollisionCheckContext*, Collider*, Collider*); @@ -2193,6 +2195,8 @@ void CollisionCheck_ATTrisVsACCyl(PlayState* play, CollisionCheckContext* colChk } } +#pragma increment_block_number "gc-eu:252 gc-eu-mq:252 gc-jp:252 gc-jp-ce:252 gc-jp-mq:252 gc-us:252 gc-us-mq:252" + void CollisionCheck_ATCylVsACQuad(PlayState* play, CollisionCheckContext* colChkCtx, Collider* atCol, Collider* acCol) { static TriNorm tri1; static TriNorm tri2; @@ -2694,8 +2698,6 @@ typedef enum ColChkMassType { /* 2 */ MASSTYPE_NORMAL } ColChkMassType; -#pragma increment_block_number "gc-eu:252 gc-eu-mq:252 gc-jp:252 gc-jp-ce:252 gc-jp-mq:252 gc-us:252 gc-us-mq:252" - /** * Get mass type. Immovable colliders cannot be pushed, while heavy colliders can only be pushed by heavy and immovable * colliders. diff --git a/src/code/z_message.c b/src/code/z_message.c index 3f8bc19b9..a610f036f 100644 --- a/src/code/z_message.c +++ b/src/code/z_message.c @@ -1,6 +1,7 @@ #include "global.h" #include "message_data_static.h" #include "terminal.h" +#include "versions.h" #include "assets/textures/parameter_static/parameter_static.h" #include "versions.h" diff --git a/src/code/z_play.c b/src/code/z_play.c index ee39e07b7..0c106f610 100644 --- a/src/code/z_play.c +++ b/src/code/z_play.c @@ -1,11 +1,16 @@ #include "global.h" +#if OOT_DEBUG +#include "fault.h" +#endif #include "quake.h" #include "terminal.h" #include "versions.h" #include "z64frame_advance.h" +#pragma increment_block_number "gc-eu:8 gc-eu-mq:8 gc-jp:8 gc-jp-ce:8 gc-jp-mq:8 gc-us:8 gc-us-mq:8" + TransitionTile gTransitionTile; s32 gTransitionTileState; VisMono gPlayVisMono; -- cgit v1.2.3