From de5c688e42e4915dc4d0520aeef0de12b9820c8d Mon Sep 17 00:00:00 2001 From: MegaMech Date: Wed, 9 Aug 2023 18:51:37 -0600 Subject: [PATCH] Add crash screen enhancement (#360) * Add crash screen enhancement --- Makefile | 3 +- mk64.ld | 4 + src/crash_screen.c | 32 ++-- src/crash_screen.h | 6 + src/debug/crash_screen_enhancement.c | 221 +++++++++++++++++++++++++++ src/debug/crash_screen_enhancement.h | 37 +++++ src/main.c | 15 +- src/main.h | 2 +- 8 files changed, 301 insertions(+), 19 deletions(-) create mode 100644 src/crash_screen.h create mode 100644 src/debug/crash_screen_enhancement.c create mode 100644 src/debug/crash_screen_enhancement.h diff --git a/Makefile b/Makefile index 49edab053..b090d1abb 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,8 @@ TARGET_N64 ?= 1 COMPILER ?= ido $(eval $(call validate-option,COMPILER,ido gcc)) -# options for debuging. Set this to 1 and modify the macros in include/debug.h +# Add debug tools with 'make DEBUG=1' and modify the macros in include/debug.h +# Run make clean first. Add '#define CRASH_SCREEN_ENHANCEMENT' to the top of main.c DEBUG ?= 0 # VERSION - selects the version of the game to build diff --git a/mk64.ld b/mk64.ld index cab64f258..56d329bfc 100644 --- a/mk64.ld +++ b/mk64.ld @@ -116,6 +116,7 @@ SECTIONS BUILD_DIR/src/audio/external.o(.text); BUILD_DIR/src/audio/port_eu.o(.text); #if DEBUG + BUILD_DIR/src/debug/crash_screen_enhancement.o(.text); BUILD_DIR/src/debug/debug.o(.text); BUILD_DIR/src/os/sprintf.o(.text); #endif @@ -266,6 +267,7 @@ SECTIONS BUILD_DIR/src/audio/playback.o(.data); BUILD_DIR/src/audio/effects.o(.data); #if DEBUG + BUILD_DIR/src/debug/crash_screen_enhancement.o(.data); BUILD_DIR/src/debug/debug.o(.data); BUILD_DIR/src/os/sprintf.o(.data); #endif @@ -311,6 +313,7 @@ SECTIONS BUILD_DIR/src/audio/external.o(.rodata); BUILD_DIR/src/audio/port_eu.o(.rodata); #if DEBUG + BUILD_DIR/src/debug/crash_screen_enhancement.o(.rodata); BUILD_DIR/src/debug/debug.o(.rodata); BUILD_DIR/src/os/sprintf.o(.rodata); #endif @@ -346,6 +349,7 @@ SECTIONS BUILD_DIR/src/code_800AF9B0.o(.bss); BUILD_DIR/src/menus.o(.bss); #if DEBUG + BUILD_DIR/src/debug/crash_screen_enhancement.o(.bss); BUILD_DIR/src/debug/debug.o(.bss); BUILD_DIR/src/os/sprintf.o(.bss); #endif diff --git a/src/crash_screen.c b/src/crash_screen.c index a612f48fd..b78e426fb 100644 --- a/src/crash_screen.c +++ b/src/crash_screen.c @@ -1,12 +1,20 @@ #include #include -#include "types.h" +#include +#include +#include +#include +#include "crash_screen.h" + +#ifdef CRASH_SCREEN_ENHANCEMENT +#include "debug/crash_screen_enhancement.h" +#endif OSThread D_80162790; ALIGNED8 u8 gDebugThreadStack[0x400]; OSMesgQueue D_80162D40; OSMesg D_80162D58; -uintptr_t *pFramebuffer; +u16 *pFramebuffer; s32 sButtonSequenceIndex; #define DRAW_CODE 0xFFFF @@ -212,17 +220,17 @@ void thread9_crash_screen(UNUSED void *arg0) osSetEventMesg(10, &D_80162D40, (OSMesg) 16); sButtonSequenceIndex = 0; - while (1) { + while (TRUE) { osRecvMesg(&D_80162D40, &mesg, 1); thread = get_faulted_thread(); if (thread) { // Run only on the first iteration. if (sCounter == 0) { - crash_screen_draw_square((u16 *)pFramebuffer); - //#define SKIP_DRAW_SQUARE - #ifndef SKIP_DRAW_SQUARE - while(1) + crash_screen_draw_square(pFramebuffer); +//#define SKIP_DRAW_SQUARE +#ifndef SKIP_DRAW_SQUARE + while(TRUE) { read_controllers(); @@ -239,8 +247,12 @@ void thread9_crash_screen(UNUSED void *arg0) break; } } - #endif - crash_screen_draw_info((u16 *) pFramebuffer, thread); +#endif +#ifdef CRASH_SCREEN_ENHANCEMENT + crash_screen_draw(thread); +#else + crash_screen_draw_info(pFramebuffer, thread); +#endif } if (sCounter < 5) { @@ -250,7 +262,7 @@ void thread9_crash_screen(UNUSED void *arg0) } } -void crash_screen_set_framebuffer(uintptr_t *framebuffer) { +void crash_screen_set_framebuffer(u16 *framebuffer) { pFramebuffer = framebuffer; } diff --git a/src/crash_screen.h b/src/crash_screen.h new file mode 100644 index 000000000..7975825d1 --- /dev/null +++ b/src/crash_screen.h @@ -0,0 +1,6 @@ +#include +#include + +extern u16 *pFramebuffer; + +void crash_screen_set_framebuffer(u16*); diff --git a/src/debug/crash_screen_enhancement.c b/src/debug/crash_screen_enhancement.c new file mode 100644 index 000000000..54547908c --- /dev/null +++ b/src/debug/crash_screen_enhancement.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include +#include +#include "../crash_screen.h" +#include "crash_screen_enhancement.h" + +s32 _Printf(char *(*prout)(char *, const char *, size_t), char *dst, const char *fmt, va_list args); + +u32 crashScreenFont2[7 * 9 + 1] = { + 0x70871c30,0x8988a250,0x88808290,0x88831c90,0x888402f8,0x88882210,0x71cf9c10,0xf9cf9c70,0x8228a288,0xf200a288,0x0bc11c78,0x0a222208,0x8a222288,0x71c21c70,0x23c738f8,0x5228a480,0x8a282280,0x8bc822f0,0xfa282280,0x8a28a480,0x8bc738f8,0xf9c89c08,0x82288808,0x82088808,0xf2ef8808,0x82288888,0x82288888,0x81c89c70,0x8a08a270,0x920da288,0xa20ab288,0xc20aaa88,0xa208a688,0x9208a288,0x8be8a270,0xf1cf1cf8,0x8a28a220,0x8a28a020,0xf22f1c20,0x82aa0220,0x82492220,0x81a89c20,0x8a28a288,0x8a28a288,0x8a289488,0x8a2a8850,0x894a9420,0x894aa220,0x70852220,0xf8011000,0x08020800,0x10840400,0x20040470,0x40840400,0x80020800,0xf8011000,0x70800000,0x88822200,0x08820400,0x108f8800,0x20821000,0x00022200,0x20800020 +}; + +extern u64 osClockRate; + +u8 gCrashScreenCharToGlyph[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, -1, -1, -1, 43, -1, -1, 37, 38, -1, 42, + -1, 39, 44, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 36, -1, -1, -1, -1, 40, -1, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, +}; + +char *gCauseDesc[18] = { + "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", +}; + +char *gFpcsrDesc[6] = { + "Unimplemented operation", "Invalid operation", "Division by zero", "Overflow", "Underflow", + "Inexact operation", +}; + +void crash_screen_draw_glyph_enhancement(s32 x, s32 y, s32 glyph) { + const u32 *data; + u16 *ptr; + u32 bit; + u32 rowMask; + s32 i, j; + + data = &crashScreenFont2[glyph / 5 * 7]; + ptr = pFramebuffer + SCREEN_WIDTH * y + x; + + for (i = 0; i < 7; i++) { + bit = 0x80000000U >> ((glyph % 5) * 6); + rowMask = *data++; + + for (j = 0; j < 6; j++) { + *ptr++ = (bit & rowMask) ? 0xffff : 1; + bit >>= 1; + } + ptr += SCREEN_WIDTH - 6; + } +} + +void crash_screen_draw_rect(s32 x, s32 y, s32 w, s32 h) { + u16 *ptr; + s32 i, j; + + ptr = pFramebuffer + SCREEN_WIDTH * y + x; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + // 0xe738 = 0b1110011100111000 + *ptr = ((*ptr & 0xe738) >> 2) | 1; + ptr++; + } + ptr += SCREEN_WIDTH - w; + } +} + +static char *write_to_buf(char *buffer, const char *data, size_t size) { + return (char *) memcpy(buffer, data, size) + size; +} + +void crash_screen_print(s32 x, s32 y, const char *fmt, ...) { + char *ptr; + u32 glyph; + s32 size; + char buf[0x100]; + + va_list args; + va_start(args, fmt); + + size = _Printf(write_to_buf, buf, fmt, args); + + if (size > 0) { + ptr = buf; + + + while (size > 0) { + + + glyph = gCrashScreenCharToGlyph[*ptr & 0x7f]; + + if (glyph != 0xff) { + crash_screen_draw_glyph_enhancement(x, y, glyph); + } + + size--; + + ptr++; + x += 6; + } + } + + va_end(args); +} + +void crash_screen_sleep(s32 ms) { + u64 cycles = ms * 1000LL * osClockRate / 1000000ULL; + osSetTime(0); + while (osGetTime() < cycles) { + } +} + +void crash_screen_print_float_reg(s32 x, s32 y, s32 regNum, void *addr) { + u32 bits; + s32 exponent; + + bits = *(u32 *) addr; + exponent = ((bits & 0x7f800000U) >> 0x17) - 0x7f; + if ((exponent >= -0x7e && exponent <= 0x7f) || bits == 0) { + crash_screen_print(x, y, "F%02d:%.3e", regNum, *(f32 *) addr); + } else { + crash_screen_print(x, y, "F%02d:---------", regNum); + } +} + +void crash_screen_print_fpcsr(u32 fpcsr) { + s32 i; + u32 bit; + + bit = 1 << 17; + crash_screen_print(30, 155, "FPCSR:%08XH", fpcsr); + for (i = 0; i < 6; i++) { + if (fpcsr & bit) { + crash_screen_print(132, 155, "(%s)", gFpcsrDesc[i]); + return; + } + bit >>= 1; + } +} + + +void crash_screen_draw(OSThread* thread) { + s16 cause; + __OSThreadContext *tc = &thread->context; + + cause = (tc->cause >> 2) & 0x1f; + if (cause == 23) { // EXC_WATCH + cause = 16; + } + if (cause == 31) { // EXC_VCED + cause = 17; + } + + osWritebackDCacheAll(); + + crash_screen_draw_rect(25, 20, 270, 25); + crash_screen_print(30, 25, "THREAD:%d (%s)", thread->id, gCauseDesc[cause]); + crash_screen_print(30, 35, "PC:%08XH SR:%08XH VA:%08XH", tc->pc, tc->sr, tc->badvaddr); + crash_screen_sleep(2000); + crash_screen_draw_rect(25, 45, 270, 185); + crash_screen_print(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32) tc->at, (u32) tc->v0, + (u32) tc->v1); + crash_screen_print(30, 60, "A0:%08XH A1:%08XH A2:%08XH", (u32) tc->a0, (u32) tc->a1, + (u32) tc->a2); + crash_screen_print(30, 70, "A3:%08XH T0:%08XH T1:%08XH", (u32) tc->a3, (u32) tc->t0, + (u32) tc->t1); + crash_screen_print(30, 80, "T2:%08XH T3:%08XH T4:%08XH", (u32) tc->t2, (u32) tc->t3, + (u32) tc->t4); + crash_screen_print(30, 90, "T5:%08XH T6:%08XH T7:%08XH", (u32) tc->t5, (u32) tc->t6, + (u32) tc->t7); + crash_screen_print(30, 100, "S0:%08XH S1:%08XH S2:%08XH", (u32) tc->s0, (u32) tc->s1, + (u32) tc->s2); + crash_screen_print(30, 110, "S3:%08XH S4:%08XH S5:%08XH", (u32) tc->s3, (u32) tc->s4, + (u32) tc->s5); + crash_screen_print(30, 120, "S6:%08XH S7:%08XH T8:%08XH", (u32) tc->s6, (u32) tc->s7, + (u32) tc->t8); + crash_screen_print(30, 130, "T9:%08XH GP:%08XH SP:%08XH", (u32) tc->t9, (u32) tc->gp, + (u32) tc->sp); + crash_screen_print(30, 140, "S8:%08XH RA:%08XH", (u32) tc->s8, (u32) tc->ra); + crash_screen_print_fpcsr(tc->fpcsr); + crash_screen_print_float_reg(30, 170, 0, &tc->fp0.f.f_even); + crash_screen_print_float_reg(120, 170, 2, &tc->fp2.f.f_even); + crash_screen_print_float_reg(210, 170, 4, &tc->fp4.f.f_even); + crash_screen_print_float_reg(30, 180, 6, &tc->fp6.f.f_even); + crash_screen_print_float_reg(120, 180, 8, &tc->fp8.f.f_even); + crash_screen_print_float_reg(210, 180, 10, &tc->fp10.f.f_even); + crash_screen_print_float_reg(30, 190, 12, &tc->fp12.f.f_even); + crash_screen_print_float_reg(120, 190, 14, &tc->fp14.f.f_even); + crash_screen_print_float_reg(210, 190, 16, &tc->fp16.f.f_even); + crash_screen_print_float_reg(30, 200, 18, &tc->fp18.f.f_even); + crash_screen_print_float_reg(120, 200, 20, &tc->fp20.f.f_even); + crash_screen_print_float_reg(210, 200, 22, &tc->fp22.f.f_even); + crash_screen_print_float_reg(30, 210, 24, &tc->fp24.f.f_even); + crash_screen_print_float_reg(120, 210, 26, &tc->fp26.f.f_even); + crash_screen_print_float_reg(210, 210, 28, &tc->fp28.f.f_even); + crash_screen_print_float_reg(30, 220, 30, &tc->fp30.f.f_even); + osViBlack(FALSE); + osViSwapBuffer(pFramebuffer); +} diff --git a/src/debug/crash_screen_enhancement.h b/src/debug/crash_screen_enhancement.h new file mode 100644 index 000000000..43fe8e10a --- /dev/null +++ b/src/debug/crash_screen_enhancement.h @@ -0,0 +1,37 @@ +#include +#include + +void crash_screen_draw(OSThread* thread); + +// Add this to the top of main.c or crash_screen.c +//#define CRASH_SCREEN_ENHANCEMENT + + + +/** + * Example of how to force crash screen to run. + * Make sure to include the header crash_screen_enhancement.h in main.h +*/ + +// void display_and_vsync(void) { +// profiler_log_thread5_time(BEFORE_DISPLAY_LISTS); +// osRecvMesg(&gGfxVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK); +// exec_display_list(&gGfxPool->spTask); +// profiler_log_thread5_time(AFTER_DISPLAY_LISTS); +// osRecvMesg(&gGameVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK); +// osViSwapBuffer((void *) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[sRenderedFramebuffer])); +// profiler_log_thread5_time(THREAD5_END); +// osRecvMesg(&gGameVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK); +// crash_screen_set_framebuffer((uintptr_t *) gPhysicalFramebuffers[sRenderedFramebuffer]); + +// crash_screen_draw((u16 *) gPhysicalFramebuffers[sRenderedFramebuffer], &gGameLoopThread); +// Add this line ^ + +// if (++sRenderedFramebuffer == 3) { +// sRenderedFramebuffer = 0; +// } +// if (++sRenderingFramebuffer == 3) { +// sRenderingFramebuffer = 0; +// } +// gGlobalTimer++; +// } diff --git a/src/main.c b/src/main.c index 16a7aac6c..4e3737f37 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,7 @@ #include "actors.h" #include "staff_ghosts.h" #include +#include "crash_screen.h" // Declarations (not in this file) void func_80091B78(void); @@ -133,7 +134,7 @@ Gfx *gDisplayListHead; struct SPTask *gGfxSPTask; s32 D_801502A0; s32 D_801502A4; -u32 gPhysicalFramebuffers[3]; +u16 *gPhysicalFramebuffers[3]; u32 D_801502B4; UNUSED u32 D_801502B8; UNUSED u32 D_801502BC; @@ -435,7 +436,6 @@ void config_gfx_pool(void) { * Yields to the VI framerate twice, locking the game at 30 FPS. * Selects the next framebuffer to be rendered and displayed. */ -void crash_screen_set_framebuffer(uintptr_t*); void display_and_vsync(void) { profiler_log_thread5_time(BEFORE_DISPLAY_LISTS); osRecvMesg(&gGfxVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK); @@ -445,7 +445,8 @@ void display_and_vsync(void) { osViSwapBuffer((void *) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[sRenderedFramebuffer])); profiler_log_thread5_time(THREAD5_END); osRecvMesg(&gGameVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK); - crash_screen_set_framebuffer((uintptr_t *) gPhysicalFramebuffers[sRenderedFramebuffer]); + crash_screen_set_framebuffer(gPhysicalFramebuffers[sRenderedFramebuffer]); + if (++sRenderedFramebuffer == 3) { sRenderedFramebuffer = 0; } @@ -1032,16 +1033,15 @@ void thread3_video(UNUSED void *arg0) { OSMesg msg; UNUSED s32 pad[4]; - gPhysicalFramebuffers[0] = (u32) &gFramebuffer0; - gPhysicalFramebuffers[1] = (u32) &gFramebuffer1; - gPhysicalFramebuffers[2] = (u32) &gFramebuffer2; + gPhysicalFramebuffers[0] = (u16 *) &gFramebuffer0; + gPhysicalFramebuffers[1] = (u16 *) &gFramebuffer1; + gPhysicalFramebuffers[2] = (u16 *) &gFramebuffer2; // Clear framebuffer. framebuffer1 = (u64 *) &gFramebuffer1; for (i = 0; i < 19200; i++) { framebuffer1[i] = 0; } - setup_mesg_queues(); setup_game_memory(); @@ -1144,6 +1144,7 @@ void thread5_game_loop(UNUSED void *arg) { if (!wasSoftReset) { clear_nmi_buffer(); } + set_vblank_handler(2, &gGameVblankHandler, &gGameVblankQueue, (OSMesg) OS_EVENT_SW2); // These variables track stats such as player wins. // In the event of a console reset, it remembers them. diff --git a/src/main.h b/src/main.h index 67e9c6dd4..e2947f75a 100644 --- a/src/main.h +++ b/src/main.h @@ -73,7 +73,7 @@ extern s32 D_800DC56C[]; extern u16 D_80152308; -extern u32 gPhysicalFramebuffers[]; +extern u16 *gPhysicalFramebuffers[]; extern OSIoMesg gDmaIoMesg; extern OSMesg gMainReceivedMesg; extern OSMesgQueue gDmaMesgQueue;