Paper Mario DX
Paper Mario (N64) modding
 
Loading...
Searching...
No Matches
crash_screen.c
Go to the documentation of this file.
1#include "common.h"
2#include "stdlib/stdarg.h"
3#include "PR/os_internal_thread.h"
4#include "libc/xstdio.h"
5#include "gcc/string.h"
6#include "dx/backtrace.h"
7#include "include_asset.h"
8
9typedef struct {
10 /* 0x000 */ OSThread thread;
11 /* 0x1B0 */ char stack[0x800];
12 /* 0x9B0 */ OSMesgQueue queue;
13 /* 0x9C8 */ OSMesg mesg;
14 /* 0x9CC */ u16* frameBuf;
15 /* 0x9D0 */ u16 width;
16 /* 0x9D2 */ u16 height;
17} CrashScreen; // size = 0x9D4
18
20
22 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
23 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, -1, -1, -1, 43, -1, -1, 37, 38, -1, 42,
24 -1, 39, 44, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 36, -1, -1, -1, -1, 40, -1, 10,
25 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
26 33, 34, 35, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
27 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
28};
29
30INCLUDE_IMG("crash_screen/font.png", gCrashScreenFont);
31
32// The font image is on 6x7 grid
33#define GLYPH(x, y) (x + (y * 5))
34
35const char* gFaultCauses[18] = {
36 "Interrupt",
37 "TLB modification",
38 "TLB exception on load",
39 "TLB exception on store",
40 "Address error on load",
41 "Address error on store",
42 "Bus error on inst.",
43 "Bus error on data",
44 "System call exception",
45 "Breakpoint exception",
46 "Reserved instruction",
47 "Coprocessor unusable",
48 "Arithmetic overflow",
49 "Trap exception",
50 "Virtual coherency on inst.",
51 "Floating point exception",
52 "Watchpoint exception",
53 "Virtual coherency on data",
54};
55
56const char* gFPCSRFaultCauses[6] = {
57 "Unimplemented operation",
58 "Invalid operation",
59 "Division by zero",
60 "Overflow",
61 "Underflow",
62 "Inexact operation",
63};
64
65char crashScreenAssertMessage[0x100] = {0};
66
67void crash_screen_set_assert_info(const char* message) {
68 strncpy(crashScreenAssertMessage, message, sizeof(crashScreenAssertMessage));
70}
71
72void crash_screen_sleep(s32 ms) {
73 u64 cycles = ms * 1000LL * 46875000LL / 1000000ULL;
74
75 osSetTime(0);
76
77 while (osGetTime() < cycles) {
78 // wait
79 }
80}
81
82void crash_screen_draw_rect(s32 x, s32 y, s32 width, s32 height) {
83 u16* ptr;
84 s32 i;
85 s32 j;
86
87 if (gCrashScreen.width == (SCREEN_WIDTH * 2)) {
88 x <<= 1;
89 y <<= 1;
90 width <<= 1;
91 height <<= 1;
92 }
93
95
96 for (i = 0; i < height; i++) {
97 for (j = 0; j < width; j++) {
98 *ptr = ((*ptr & 0xE738) >> 2) | 1;
99 ptr++;
100 }
101
102 ptr += gCrashScreen.width - width;
103 }
104}
105
107s32 crash_screen_draw_glyph(s32 x, s32 y, s32 glyph) {
108 s32 shift = ((glyph % 5) * 6);
109 u16 width = gCrashScreen.width;
110 const u32* data = &((u32*)gCrashScreenFont)[glyph / 5 * 7];
111 s32 i;
112 s32 j;
113
114 switch (glyph) {
115 case GLYPH(3, 10): // ;
116 case GLYPH(4, 10): // ,
117 y += 1;
118 break;
119 case GLYPH(1, 14): // g
120 case GLYPH(0, 16): // p
121 case GLYPH(1, 16): // q
122 case GLYPH(4, 17): // y
123 y += 2;
124 break;
125 }
126
127 if (width == SCREEN_WIDTH) {
128 u16* ptr = gCrashScreen.frameBuf + (gCrashScreen.width) * y + x;
129
130 for (i = 0; i < 7; i++) {
131 u32 bit = 0x80000000U >> shift;
132 u32 rowMask = *data++;
133
134 for (j = 0; j < 6; j++) {
135 if (bit & rowMask) {
136 *ptr++ = 0xFFFF; // white
137 } else {
138 ptr++; // dont draw
139 }
140 bit >>= 1;
141 }
142
143 ptr += gCrashScreen.width - 6;
144 }
145 } else if (width == (SCREEN_WIDTH * 2)) {
146 u16* ptr = gCrashScreen.frameBuf + (y * 0x500) + (x * 2);
147
148 for (i = 0; i < 7; i++) {
149 u32 bit = 0x80000000U >> shift;
150 u32 rowMask = *data++;
151
152 for (j = 0; j < 6; j++) {
153 u16 temp = (bit & rowMask) ? 0xFFFF : 1;
154
155 ptr[0] = temp;
156 ptr[1] = temp;
157 ptr[(SCREEN_WIDTH * 2)] = temp;
158 ptr[(SCREEN_WIDTH * 2) + 1] = temp;
159 ptr += 2;
160 bit >>= 1;
161 }
162
163 ptr += (0x9E8 / 2);
164 }
165 }
166
167 // Calculate x advance by counting the width of the glyph + 1 pixel of padding
168 if (glyph == GLYPH(2, 15)) return 7; // m - fucked up hack
169 s32 xAdvance = 0;
170 data = &((u32*)gCrashScreenFont)[glyph / 5 * 7];
171 for (i = 0; i < 7; i++) { // 7 rows
172 u32 bit = 0x80000000U >> shift;
173 u32 rowMask = *data++;
174 for (j = 1; j < 6; j++) { // 6 columns
175 if (bit & rowMask) {
176 if (xAdvance < j) {
177 xAdvance = j;
178 }
179 }
180 bit >>= 1;
181 }
182 }
183 return xAdvance + 1;
184}
185
186char* crash_screen_copy_to_buf(char* dest, const char* src, size_t size) {
187 memcpy(dest, src, size);
188 return dest + size;
189}
190
192s32 crash_screen_printf(s32 x, s32 y, const char* fmt, ...) {
193 u8* ptr;
194 u32 glyph;
195 s32 size;
196 u8 buf[0x100];
197 va_list args;
198 s32 ox = x;
199
200 va_start(args, fmt);
201
202 size = _Printf(crash_screen_copy_to_buf, (s8*)buf, fmt, args);
203
204 if (size > 0) {
205 ptr = buf;
206
207 while (size > 0) {
208 u8* charToGlyph = gCrashScreencharToGlyph;
209
210 glyph = charToGlyph[*ptr & 0x7F];
211
212 if (glyph != 0xFF) {
213 crash_screen_draw_glyph(x, y, glyph);
214 }
215
216 x += 6;
217
218 if (*ptr == '\n') {
219 x = ox;
220 y += 10;
221 }
222
223 size--;
224 ptr++;
225 }
226 }
227
228 // If last character was not a newline, move to the next line
229 if (x != ox) {
230 y += 10;
231 }
232 return y;
233
234 va_end(args);
235}
236
238s32 crash_screen_printf_proportional(s32 x, s32 y, const char* fmt, ...) {
239 u8* ptr;
240 u32 glyph;
241 s32 size;
242 u8 buf[0x200];
243 va_list args;
244 s32 ox = x;
245
246 va_start(args, fmt);
247
248 size = _Printf(crash_screen_copy_to_buf, (s8*)buf, fmt, args);
249
250 if (size > 0) {
251 ptr = buf;
252
253 while (size > 0) {
254 u8* charToGlyph = gCrashScreencharToGlyph;
255
256 glyph = charToGlyph[*ptr & 0x7F];
257
258 if (glyph != 0xFF) {
259 x += crash_screen_draw_glyph(x, y, glyph);
260 } else {
261 x += 4;
262 }
263
264 if (*ptr == '\n') {
265 x = ox;
266 y += 10;
267 }
268
269 size--;
270 ptr++;
271 }
272 }
273
274 // If last character was not a newline, move to the next line
275 if (x != ox) {
276 y += 10;
277 }
278 return y;
279
280 va_end(args);
281}
282
283void crash_screen_print_fpr(s32 x, s32 y, s32 regNum, void* addr) {
284 u32 bits = *(u32*)addr;
285 s32 exponent = ((bits & 0x7F800000U) >> 0x17) - 0x7F;
286
287 if ((exponent >= -0x7E && exponent <= 0x7F) || bits == 0) {
288 crash_screen_printf(x, y, "F%02d:%+.3e", regNum, *(f32*)addr);
289 } else {
290 crash_screen_printf(x, y, "F%02d:---------", regNum);
291 }
292}
293
295 s32 i;
296 u32 flag = 0x20000;
297
298 crash_screen_printf(30, 155, "FPCSR:%08XH", value);
299
300 for (i = 0; i < 6;) {
301 if (value & flag) {
302 crash_screen_printf(132, 155, "(%s)", gFPCSRFaultCauses[i]);
303 break;
304 }
305
306 i++;
307 flag >>= 1;
308 }
309}
310
311void crash_screen_draw(OSThread* faultedThread) {
312 s16 causeIndex;
313
314 s32 bt[8];
315 s32 max = backtrace_thread((void**)bt, ARRAY_COUNT(bt), faultedThread);
316 s32 i = 0;
317 static char buf[0x200];
318
319 causeIndex = ((faultedThread->context.cause >> 2) & 0x1F);
320
321 if (causeIndex == 23) {
322 causeIndex = 16;
323 }
324
325 if (causeIndex == 31) {
326 causeIndex = 17;
327 }
328
329 osWritebackDCacheAll();
330
331 s32 x = 10;
332 s32 y = 10;
333
335
336 // Print error message
337 b32 isException = FALSE;
338 if (crashScreenAssertMessage[0] == '\0') {
339 y += crash_screen_printf_proportional(x, y, "Exception in thread %d: %s", faultedThread->id, gFaultCauses[causeIndex]);
340 isException = TRUE;
341 } else {
343 i = 1; // Don't include is_debug_panic line in backtrace.
344 }
345
346 // Print register values
347 // TODO: print registers relevant to the exception
348 if (isException) {
349 __OSThreadContext* ctx = &faultedThread->context;
350 crash_screen_printf_proportional(x, y, "Registers:");
351 y += 10;
352 crash_screen_printf(x, y, " a0 = 0x%08X a1 = 0x%08X", (u32)ctx->a0, (u32)ctx->a1);
353 y += 10;
354 crash_screen_printf(x, y, " a2 = 0x%08X a3 = 0x%08X", (u32)ctx->a2, (u32)ctx->a3);
355 y += 10;
356
357 y += 10;
358 }
359
360 // Print backtrace
361 crash_screen_printf_proportional(x, y, "Call stack:");
362 y += 10;
363 for (; i < max; i++) {
364 backtrace_address_to_string(bt[i], buf);
365 crash_screen_printf_proportional(x, y, " in %s", buf);
366 y += 10;
367 }
368
369 y += 10;
370
371#ifndef DEBUG
372 y += crash_screen_printf_proportional(x, y, "Build with `./configure --debug` for file/line numbers", buf);
373#endif
374
375 osViBlack(0);
376 osViRepeatLine(0);
377 osViSwapBuffer(gCrashScreen.frameBuf);
378
379 /*
380 crash_screen_draw_rect(0, 30, SCREEN_WIDTH, SCREEN_HEIGHT - 30);
381
382 crash_screen_printf(30, 35, "PC:%08XH SR:%08XH VA:%08XH", ctx->pc, ctx->sr, ctx->badvaddr);
383 crash_screen_printf(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32)ctx->at, (u32)ctx->v0, (u32)ctx->v1);
384 crash_screen_printf(30, 45, "A0: %08X A1: %08X A2: %08X", (u32)ctx->a0, (u32)ctx->a1, (u32)ctx->a2);
385 crash_screen_printf(30, 70, "A3:%08XH T0:%08XH T1:%08XH", (u32)ctx->a3, (u32)ctx->t0, (u32)ctx->t1);
386 crash_screen_printf(30, 80, "T2:%08XH T3:%08XH T4:%08XH", (u32)ctx->t2, (u32)ctx->t3, (u32)ctx->t4);
387 crash_screen_printf(30, 90, "T5:%08XH T6:%08XH T7:%08XH", (u32)ctx->t5, (u32)ctx->t6, (u32)ctx->t7);
388 crash_screen_printf(30, 100, "S0:%08XH S1:%08XH S2:%08XH", (u32)ctx->s0, (u32)ctx->s1, (u32)ctx->s2);
389 crash_screen_printf(30, 110, "S3:%08XH S4:%08XH S5:%08XH", (u32)ctx->s3, (u32)ctx->s4, (u32)ctx->s5);
390 crash_screen_printf(30, 120, "S6:%08XH S7:%08XH T8:%08XH", (u32)ctx->s6, (u32)ctx->s7, (u32)ctx->t8);
391 crash_screen_printf(30, 130, "T9:%08XH GP:%08XH SP:%08XH", (u32)ctx->t9, (u32)ctx->gp, (u32)ctx->sp);
392 crash_screen_printf(30, 140, "S8:%08XH RA:%08XH", (u32)ctx->s8, (u32)ctx->ra);
393
394 crash_screen_print_fpcsr(ctx->fpcsr);
395
396 crash_screen_print_fpr(30, 170, 0, &ctx->fp0.f.f_even);
397 crash_screen_print_fpr(120, 170, 2, &ctx->fp2.f.f_even);
398 crash_screen_print_fpr(210, 170, 4, &ctx->fp4.f.f_even);
399 crash_screen_print_fpr(30, 180, 6, &ctx->fp6.f.f_even);
400 crash_screen_print_fpr(120, 180, 8, &ctx->fp8.f.f_even);
401 crash_screen_print_fpr(210, 180, 10, &ctx->fp10.f.f_even);
402 crash_screen_print_fpr(30, 190, 12, &ctx->fp12.f.f_even);
403 crash_screen_print_fpr(120, 190, 14, &ctx->fp14.f.f_even);
404 crash_screen_print_fpr(210, 190, 16, &ctx->fp16.f.f_even);
405 crash_screen_print_fpr(30, 200, 18, &ctx->fp18.f.f_even);
406 crash_screen_print_fpr(120, 200, 20, &ctx->fp20.f.f_even);
407 crash_screen_print_fpr(210, 200, 22, &ctx->fp22.f.f_even);
408 crash_screen_print_fpr(30, 210, 24, &ctx->fp24.f.f_even);
409 crash_screen_print_fpr(120, 210, 26, &ctx->fp26.f.f_even);
410 crash_screen_print_fpr(210, 210, 28, &ctx->fp28.f.f_even);
411 crash_screen_print_fpr(30, 220, 30, &ctx->fp30.f.f_even);
412
413 crash_screen_sleep(500);
414
415 // all of these null terminators needed to pad the rodata section for this file
416 // can potentially fix this problem in another way?
417 crash_screen_printf(210, 140, "MM:%08XH\0\0\0\0\0\0\0\0", *(u32*)ctx->pc);
418 */
419}
420
422 OSThread* thread = __osGetActiveQueue();
423
424 while (thread->priority != -1) {
425 if (thread->priority > 0 && thread->priority < 0x7F && (thread->flags & 3)) {
426 return thread;
427 }
428
429 thread = thread->tlnext;
430 }
431
432 return NULL;
433}
434
435void crash_screen_thread_entry(void* unused) {
436 OSMesg mesg;
437 OSThread* faultedThread;
438
439 osSetEventMesg(OS_EVENT_CPU_BREAK, &gCrashScreen.queue, (OSMesg)1);
440 osSetEventMesg(OS_EVENT_FAULT, &gCrashScreen.queue, (OSMesg)2);
441
442 do {
443 osRecvMesg(&gCrashScreen.queue, &mesg, 1);
444 faultedThread = crash_screen_get_faulted_thread();
445 } while (faultedThread == NULL);
446
447 osStopThread(faultedThread);
448 crash_screen_draw(faultedThread);
449
450 while (TRUE) {}
451}
452
453void crash_screen_set_draw_info(u16* frameBufPtr, s16 width, s16 height) {
454 gCrashScreen.frameBuf = (u16*)((u32)frameBufPtr | 0xA0000000);
455 gCrashScreen.width = width;
456 gCrashScreen.height = height;
457}
458
461 gCrashScreen.height = 16;
462 gCrashScreen.frameBuf = (u16*)((osMemSize | 0xA0000000) - ((SCREEN_WIDTH * SCREEN_HEIGHT) * 2));
463 osCreateMesgQueue(&gCrashScreen.queue, &gCrashScreen.mesg, 1);
464 osCreateThread(&gCrashScreen.thread, 2, crash_screen_thread_entry, NULL,
465 gCrashScreen.stack + sizeof(gCrashScreen.stack), 0x80);
466 osStartThread(&gCrashScreen.thread);
467
468 // gCrashScreencharToGlyph is hard to modify, so we'll just do it here
469 u8 chars[] =
470 "_[]<>"
471 "|{};,"
472 "\"#$&'"
473 "/=@\\`"
474 "abcde"
475 "fghij"
476 "klmno"
477 "pqrst"
478 "uvwxy"
479 "z";
480 s32 i;
481 for (i = 0; i < ARRAY_COUNT(chars); i++) {
482 gCrashScreencharToGlyph[chars[i]] = GLYPH(0, 9) + i;
483 }
484}
485
486// unused
487void crash_screen_printf_with_bg(s16 x, s16 y, const char* fmt, ...) {
488 u8* ptr;
489 u32 glyph;
490 s32 size;
491 u8 buf[0x100];
492 va_list args;
493
494 va_start(args, fmt);
495
496 size = _Printf(crash_screen_copy_to_buf, (s8*)buf, fmt, args);
497
498 if (size > 0) {
499 crash_screen_draw_rect(x - 6, y - 6, (size + 2) * 6, 19);
500 ptr = buf;
501
502 while (size > 0) {
503 u8* charToGlyph = gCrashScreencharToGlyph;
504
505 glyph = charToGlyph[*ptr & 0x7F];
506
507 if (glyph != 0xFF) {
508 crash_screen_draw_glyph(x, y, glyph);
509 }
510
511 x += 6;
512 size--;
513 ptr++;
514 }
515 }
516
517 va_end(args);
518}
int backtrace_thread(void **buffer, int size, OSThread *thread)
Definition backtrace.c:308
void backtrace_address_to_string(u32 address, char *dest)
Converts a function address to a string representation using its name, offset, and file.
Definition backtrace.c:396
s32 b32
char * crash_screen_copy_to_buf(char *dest, const char *src, size_t size)
void crash_screen_print_fpr(s32 x, s32 y, s32 regNum, void *addr)
char stack[0x800]
OSThread thread
BSS CrashScreen gCrashScreen
void crash_screen_thread_entry(void *unused)
void crash_screen_set_assert_info(const char *message)
void crash_screen_printf_with_bg(s16 x, s16 y, const char *fmt,...)
const char * gFaultCauses[18]
#define GLYPH(x, y)
void crash_screen_init(void)
void crash_screen_print_fpcsr(u32 value)
void crash_screen_sleep(s32 ms)
void crash_screen_set_draw_info(u16 *frameBufPtr, s16 width, s16 height)
void crash_screen_draw(OSThread *faultedThread)
const char * gFPCSRFaultCauses[6]
OSMesgQueue queue
char crashScreenAssertMessage[0x100]
s32 crash_screen_printf_proportional(s32 x, s32 y, const char *fmt,...)
OSThread * crash_screen_get_faulted_thread(void)
void crash_screen_draw_rect(s32 x, s32 y, s32 width, s32 height)
u8 gCrashScreencharToGlyph[128]
s32 crash_screen_draw_glyph(s32 x, s32 y, s32 glyph)
s32 crash_screen_printf(s32 x, s32 y, const char *fmt,...)
#define INCLUDE_IMG(FILENAME, SYMBOLNAME)
#define SCREEN_WIDTH
Definition macros.h:105
#define BSS
Definition macros.h:7
#define ARRAY_COUNT(arr)
Definition macros.h:40
#define SCREEN_HEIGHT
Definition macros.h:106