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 osViBlack(0);
372 osViRepeatLine(0);
373 osViSwapBuffer(gCrashScreen.frameBuf);
374
375 /*
376 crash_screen_draw_rect(0, 30, SCREEN_WIDTH, SCREEN_HEIGHT - 30);
377
378 crash_screen_printf(30, 35, "PC:%08XH SR:%08XH VA:%08XH", ctx->pc, ctx->sr, ctx->badvaddr);
379 crash_screen_printf(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32)ctx->at, (u32)ctx->v0, (u32)ctx->v1);
380 crash_screen_printf(30, 45, "A0: %08X A1: %08X A2: %08X", (u32)ctx->a0, (u32)ctx->a1, (u32)ctx->a2);
381 crash_screen_printf(30, 70, "A3:%08XH T0:%08XH T1:%08XH", (u32)ctx->a3, (u32)ctx->t0, (u32)ctx->t1);
382 crash_screen_printf(30, 80, "T2:%08XH T3:%08XH T4:%08XH", (u32)ctx->t2, (u32)ctx->t3, (u32)ctx->t4);
383 crash_screen_printf(30, 90, "T5:%08XH T6:%08XH T7:%08XH", (u32)ctx->t5, (u32)ctx->t6, (u32)ctx->t7);
384 crash_screen_printf(30, 100, "S0:%08XH S1:%08XH S2:%08XH", (u32)ctx->s0, (u32)ctx->s1, (u32)ctx->s2);
385 crash_screen_printf(30, 110, "S3:%08XH S4:%08XH S5:%08XH", (u32)ctx->s3, (u32)ctx->s4, (u32)ctx->s5);
386 crash_screen_printf(30, 120, "S6:%08XH S7:%08XH T8:%08XH", (u32)ctx->s6, (u32)ctx->s7, (u32)ctx->t8);
387 crash_screen_printf(30, 130, "T9:%08XH GP:%08XH SP:%08XH", (u32)ctx->t9, (u32)ctx->gp, (u32)ctx->sp);
388 crash_screen_printf(30, 140, "S8:%08XH RA:%08XH", (u32)ctx->s8, (u32)ctx->ra);
389
390 crash_screen_print_fpcsr(ctx->fpcsr);
391
392 crash_screen_print_fpr(30, 170, 0, &ctx->fp0.f.f_even);
393 crash_screen_print_fpr(120, 170, 2, &ctx->fp2.f.f_even);
394 crash_screen_print_fpr(210, 170, 4, &ctx->fp4.f.f_even);
395 crash_screen_print_fpr(30, 180, 6, &ctx->fp6.f.f_even);
396 crash_screen_print_fpr(120, 180, 8, &ctx->fp8.f.f_even);
397 crash_screen_print_fpr(210, 180, 10, &ctx->fp10.f.f_even);
398 crash_screen_print_fpr(30, 190, 12, &ctx->fp12.f.f_even);
399 crash_screen_print_fpr(120, 190, 14, &ctx->fp14.f.f_even);
400 crash_screen_print_fpr(210, 190, 16, &ctx->fp16.f.f_even);
401 crash_screen_print_fpr(30, 200, 18, &ctx->fp18.f.f_even);
402 crash_screen_print_fpr(120, 200, 20, &ctx->fp20.f.f_even);
403 crash_screen_print_fpr(210, 200, 22, &ctx->fp22.f.f_even);
404 crash_screen_print_fpr(30, 210, 24, &ctx->fp24.f.f_even);
405 crash_screen_print_fpr(120, 210, 26, &ctx->fp26.f.f_even);
406 crash_screen_print_fpr(210, 210, 28, &ctx->fp28.f.f_even);
407 crash_screen_print_fpr(30, 220, 30, &ctx->fp30.f.f_even);
408
409 crash_screen_sleep(500);
410
411 // all of these null terminators needed to pad the rodata section for this file
412 // can potentially fix this problem in another way?
413 crash_screen_printf(210, 140, "MM:%08XH\0\0\0\0\0\0\0\0", *(u32*)ctx->pc);
414 */
415}
416
418 OSThread* thread = __osGetActiveQueue();
419
420 while (thread->priority != -1) {
421 if (thread->priority > 0 && thread->priority < 0x7F && (thread->flags & 3)) {
422 return thread;
423 }
424
425 thread = thread->tlnext;
426 }
427
428 return NULL;
429}
430
431void crash_screen_thread_entry(void* unused) {
432 OSMesg mesg;
433 OSThread* faultedThread;
434
435 osSetEventMesg(OS_EVENT_CPU_BREAK, &gCrashScreen.queue, (OSMesg)1);
436 osSetEventMesg(OS_EVENT_FAULT, &gCrashScreen.queue, (OSMesg)2);
437
438 do {
439 osRecvMesg(&gCrashScreen.queue, &mesg, 1);
440 faultedThread = crash_screen_get_faulted_thread();
441 } while (faultedThread == NULL);
442
443 osStopThread(faultedThread);
444 crash_screen_draw(faultedThread);
445
446 while (TRUE) {}
447}
448
449void crash_screen_set_draw_info(u16* frameBufPtr, s16 width, s16 height) {
450 gCrashScreen.frameBuf = (u16*)((u32)frameBufPtr | 0xA0000000);
451 gCrashScreen.width = width;
452 gCrashScreen.height = height;
453}
454
457 gCrashScreen.height = 16;
458 gCrashScreen.frameBuf = (u16*)((osMemSize | 0xA0000000) - ((SCREEN_WIDTH * SCREEN_HEIGHT) * 2));
459 osCreateMesgQueue(&gCrashScreen.queue, &gCrashScreen.mesg, 1);
460 osCreateThread(&gCrashScreen.thread, 2, crash_screen_thread_entry, NULL,
461 gCrashScreen.stack + sizeof(gCrashScreen.stack), 0x80);
462 osStartThread(&gCrashScreen.thread);
463
464 // gCrashScreencharToGlyph is hard to modify, so we'll just do it here
465 u8 chars[] =
466 "_[]<>"
467 "|{};,"
468 "\"#$&'"
469 "/=@\\`"
470 "abcde"
471 "fghij"
472 "klmno"
473 "pqrst"
474 "uvwxy"
475 "z";
476 s32 i;
477 for (i = 0; i < ARRAY_COUNT(chars); i++) {
478 gCrashScreencharToGlyph[chars[i]] = GLYPH(0, 9) + i;
479 }
480}
481
482// unused
483void crash_screen_printf_with_bg(s16 x, s16 y, const char* fmt, ...) {
484 u8* ptr;
485 u32 glyph;
486 s32 size;
487 u8 buf[0x100];
488 va_list args;
489
490 va_start(args, fmt);
491
492 size = _Printf(crash_screen_copy_to_buf, (s8*)buf, fmt, args);
493
494 if (size > 0) {
495 crash_screen_draw_rect(x - 6, y - 6, (size + 2) * 6, 19);
496 ptr = buf;
497
498 while (size > 0) {
499 u8* charToGlyph = gCrashScreencharToGlyph;
500
501 glyph = charToGlyph[*ptr & 0x7F];
502
503 if (glyph != 0xFF) {
504 crash_screen_draw_glyph(x, y, glyph);
505 }
506
507 x += 6;
508 size--;
509 ptr++;
510 }
511 }
512
513 va_end(args);
514}
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:109
#define BSS
Definition macros.h:7
#define ARRAY_COUNT(arr)
Definition macros.h:40
#define SCREEN_HEIGHT
Definition macros.h:110