crash_handler.s (8875B)
1; Crash handler support 2; Original code by ISSOtm 3; Adapted by Toxa from gb-starter-kit: https://github.com/ISSOtm/gb-starter-kit 4 5 .include "global.s" 6 7 .globl _font_ibm 8 9 SCRN_X = 160 ; Width of screen in pixels 10 SCRN_Y = 144 ; Height of screen in pixels 11 SCRN_X_B = 20 ; Width of screen in bytes 12 SCRN_Y_B = 18 ; Height of screen in bytes 13 14 SCRN_VX = 256 ; Virtual width of screen in pixels 15 SCRN_VY = 256 ; Virtual height of screen in pixels 16 SCRN_VX_B = 32 ; Virtual width of screen in bytes 17 SCRN_VY_B = 32 ; Virtual height of screen in bytes 18 19 20 .area _CRASH_HEADER(ABS) 21 22 .org 0x38 23 di 24 jp ___HandleCrash 25 26 27 .area _HOME 28 29___HandleCrash:: 30 31 ; We will use VRAM as scratch, since we are going to overwrite it for 32 ; screen output anyways. The thing is, we need to turn the LCD off 33 ; *without* affecting flags... fun task, eh? 34 35 ; Note: it's assumed that this was jumped to with IME off. 36 ; Don't call this directly, use `rst Crash`. 37 38 ld (wCrashA), a ; We need to have at least one working register, so... 39 ldh a, (.IE) ; We're also going to overwrite this 40 ld (wCrashIE), a 41 ldh a, (.LCDC) 42 ld (wCrashLCDC), a 43 ld a, #LCDCF_ON ; LCDCF_ON Make sure the LCD is turned on to avoid waiting infinitely 44 ldh (.LCDC), a 45 ld a, #IEF_VBLANK ; IEF_VBLANK 46 ld (.IE), a 47 ld a, #0 ; `xor a` would overwrite flags 48 ld (.IF), a ; No point in backing up that register, it's always changing 49 halt ; With interrupts disabled, this will exit when `IE & IF != 0` 50 nop ; Handle hardware bug if it becomes true *before* starting to execute the instruction (1-cycle window) 51 52 ; We're now in VBlank! So we can now use VRAM as scratch for some cycles 53 ld a, #0 54 ldh (.LCDC), a ; Turn off LCD so VRAM can always be safely accessed 55 ; Save regs 56 ld (vCrashSP), sp 57 ld sp, #vCrashSP 58 push hl 59 push de 60 push bc 61 ld a, (wCrashA) 62 push af 63 64 ; We need to have all the data in bank 0, but we can't guarantee we were there 65 ldh a, (.VBK) 66 ld e, a 67 bit #0, a 68 jr z, .bank0 69 ; Oh noes. We need to copy the data across banks! 70 ld hl, #vCrashAF 71 ld c, #(5 * 2) 72.copyAcross: 73 ld b, (hl) 74 xor a 75 ldh (.VBK), a 76 ld (hl), b 77 inc l ; inc hl 78 inc a ; ld a, 1 79 ldh (.VBK), a 80 dec c 81 jr nz, .copyAcross 82.bank0: 83 xor a 84 ldh (.NR52), a ; Kill sound for this screen 85 86 ldh (.VBK), a 87 ld a, e 88 ld (vCrashVBK), a ; copy vCrashVBK across banks 89 90 ld a, #1 91 ldh (.VBK), a 92 ld hl, #vCrashDumpScreen 93 ld b, #SCRN_Y_B 94.writeAttrRow: 95 xor a 96 ld c, #(SCRN_X_B + 1) 97 rst #0x28 ; .MemsetSmall 98 ld a, l 99 add a, #(SCRN_VX_B - SCRN_X_B - 1) 100 ld l, a 101 dec b 102 jr nz, .writeAttrRow 103 xor a 104 ldh (.VBK), a 105 106 ; Load palettes 107 ld a, #0x03 108 ldh (.BGP), a 109 ld a, #0x80 110 ldh (.BCPS), a 111 xor a 112 ld c, #.BCPD 113 ldh (c), a 114 ldh (c), a 115 dec a ; ld a, $FF 116 ldh (c), a 117 ldh (c), a 118 ldh (c), a 119 ldh (c), a 120 ldh (c), a 121 ldh (c), a 122 123 ld a, #(SCRN_VY - SCRN_Y) 124 ldh (.SCY), a 125 ld a, #(SCRN_VX - SCRN_X - 4) 126 ldh (.SCX), a 127 128 call loadfont 129 130 ; Copy the registers to the dump viewers 131 ld hl, #vDumpHL 132 ld de, #vCrashHL 133 ld c, #4 134 rst #0x30 ; .MemcpySmall 135 136 ; We're now going to draw the screen, top to bottom 137 ld hl, #vCrashDumpScreen 138 139 ; First 3 lines of text 140 ld de, #.header 141 ld b, #3 142.writeHeaderLine: 143 ld a, #0x20 ; " " 144 ld (hl+), a 145 ld c, #19 146 rst #0x30 ; .MemcpySmall 147 ld a, #0x20 ; " " 148 ld (hl+), a 149 ld a, l 150 add a, #(SCRN_VX_B - SCRN_X_B - 1) 151 ld l, a 152 dec b 153 jr nz, .writeHeaderLine 154 155 ; Blank line 156 ld a, #0x20 ; " " 157 ld c, #(SCRN_X_B + 1) 158 rst #0x28 ; .MemsetSmall 159 160 ; AF and console model 161 ld l, #<vCrashDumpScreenRow4 162 ld c, #4 163 rst #0x30 ; .MemcpySmall 164 pop bc 165 call .printHexBC 166 ld c, #8 167 rst #0x30 ; .MemcpySmall 168 ld a, (__cpu) 169 call .printHexA 170 ld a, #0x20 ; " " 171 ld (hl+), a 172 ld (hl+), a 173 ld (hl+), a 174 175 ; BC and DE 176 ld l, #<vCrashDumpScreenRow5 177 ld c, #4 178 rst #0x30 ; .MemcpySmall 179 pop bc 180 call .printHexBC 181 ld c, #6 182 rst #0x30 ; .MemcpySmall 183 pop bc 184 call .printHexBC 185 ld a, #0x20 186 ld (hl+), a 187 ld (hl+), a 188 ld (hl+), a 189 190 ; Now, the two memory dumps 191.writeDump: 192 ld a, l 193 add a, #(SCRN_VX_B - SCRN_X_B - 1) 194 ld l, a 195 ld c, #4 196 rst #0x30 ; .MemcpySmall 197 pop bc 198 push bc 199 call .printHexBC 200 ld de, #.viewStr 201 ld c, #7 202 rst #0x30 ; .MemcpySmall 203 pop de 204 call .printDump 205 ld de, #.spStr 206 bit #7, l 207 jr z, .writeDump 208 209 ld de, #.hwRegsStrs 210 ld l, #<vCrashDumpScreenRow14 211 ld c, #6 212 rst #0x30 ; .MemcpySmall 213 ld a, (wCrashLCDC) 214 call .printHexA 215 ld c, #4 216 rst #0x30 ; .MemcpySmall 217 ldh a, (.KEY1) 218 call .printHexA 219 ld c, #4 220 rst #0x30 ; .MemcpySmall 221 ld a, (wCrashIE) 222 call .printHexA 223 ld (hl), #0x20 ; " " 224 225 ld l, #<vCrashDumpScreenRow15 226 ld c, #7 227 rst #0x30 ; .MemcpySmall 228.writeBank: 229 ld a, #0x20 ; " " 230 ld (hl+), a 231 ld a, (de) 232 inc de 233 ld (hl+), a 234 cp #0x20 ; " " 235 jr z, .banksDone 236 ld a, (de) 237 inc de 238 ld c, a 239 ld a, (de) 240 inc de 241 ld b, a 242 ld a, (bc) 243 call .printHexA 244 jr .writeBank 245.banksDone: 246 247 ; Start displaying 248 ld a, #(LCDCF_ON | LCDCF_BG9C00 | LCDCF_BGON) 249 ldh (.LCDC), a 250 251.loop: 252 ; The code never lags, and IE is equal to IEF_VBLANK 253 xor a 254 ldh (.IF), a 255 halt 256 257 jr .loop 258 259.printHexBC: 260 call .printHexB 261 ld a, c 262.printHexA: 263 ld b, a 264.printHexB: 265 ld a, b 266 and #0xF0 267 swap a 268 add a, #0x30 269 cp #0x3a 270 jr c, 1$ 271 add a, #(0x41 - 0x3a) 2721$: ld (hl+), a 273 ld a, b 274 and #0x0F 275 add a, #0x30 276 cp #0x3a 277 jr c, 2$ 278 add a, #(0x41 - 0x3a) 2792$: ld (hl+), a 280 ret 281 282.printDump: 283 ld b, d 284 ld c, e 285 call .printHexBC 286 ld a, #0x20 ; " " 287 ld (hl+), a 288 ld (hl+), a 289 ld a, e 290 sub #8 291 ld e, a 292 ld a, d 293 sbc #0 294 ld d, a 295.writeDumpLine: 296 ld a, l 297 add a, #(SCRN_VX_B - SCRN_X_B - 1) 298 ld l, a 299 ld a, #0x20 ; " " 300 ld (hl+), a 301.writeDumpWord: 302 ld a, (de) 303 inc de 304 call .printHexA 305 ld a, (de) 306 inc de 307 call .printHexA 308 ld a, #0x20 ; " " 309 ld (hl+), a 310 bit 4, l 311 jr nz, .writeDumpWord 312 ld a, l 313 and #0x7F 314 jr nz, .writeDumpLine 315 ret 316 317loadfont: 318 xor a 319 cpl 320 ld hl, #0x9000 321 ld c, #16 322 rst #0x28 ; .MemsetSmall 323 ld hl, #(0x9000 + ' ' * 16) 324 ld c, #16 325 rst #0x28 ; .MemsetSmall 326 327 ld de, #(_font_ibm + 2 + '0') ; recode table 328 ld hl, #(0x9000 + '0' * 16) ; destination 329 push hl 330 ld c, #(16 * 3) 3311$: 332 ld a, (de) 333 inc de 334 push de 335 336 swap a 337 ld l, a 338 and #0x0f 339 ld h, a 340 ld a, l 341 and #0xf0 342 srl h 343 rra 344 ld l, a 345 ld de, #(_font_ibm + 2 + 128) 346 add hl, de 347 348 ld d, h 349 ld e, l 350 351 ldhl sp, #2 352 ld a, (hl+) 353 ld h, (hl) 354 ld l, a 355 356 ld b, #8 3572$: 358 ld a, (de) 359 cpl 360 inc de 361 ld (hl+), a 362 ld (hl+), a 363 364 dec b 365 jr nz, 2$ 366 367 ld d, h 368 ld a, l 369 ldhl sp, #2 370 ld (hl+), a 371 ld (hl), d 372 373 pop de 374 375 dec c 376 jr nz, 1$ 377 378 add sp, #2 379 ret 380 381.header: 382 ; 0123456789ABCDEFGHI 19 chars 383 .ascii "KERNEL PANIC PLEASE" 384 .ascii "SEND A CLEAR PIC OF" 385 .ascii "THIS SCREEN TO DEVS" 386 .ascii " AF:" 387 .ascii " MODEL:" 388 .ascii " BC:" 389 .ascii " DE:" 390 .ascii " HL:" 391.viewStr: 392 .ascii " VIEW:" 393.spStr: 394 .ascii " SP:" 395.hwRegsStrs: 396 .ascii " LCDC:" 397 .ascii " K1:" 398 .ascii " IE:" 399 .ascii " BANK:" 400 .ascii "R" 401 .dw __current_bank 402 .ascii "V" 403 .dw vCrashVBK 404 .ascii "W" 405 .db .SVBK, 0xff 406 .ascii " " 407 408 409 .area _DATA 410 411wCrashA: 412 .ds 1 ; We need at least one working register, and A allows accessing memory 413wCrashIE: 414 .ds 1 415wCrashLCDC: 416 .ds 1 417 418 419 .area _CRASH_SCRATCH(ABS) 420 421 .org 0x9C00 422 423 ; Put the crash dump screen at the bottom-right of the 9C00 tilemap, since that tends to be unused space 424 .ds SCRN_VX_B * (SCRN_VY_B - SCRN_Y_B - 2) ; 2 rows reserved as scratch space 425 426 .ds SCRN_X_B ; Try not to overwrite the window area 427 .ds 2 * 1 ; Free stack entries (we spill into the above by 1 entry, though :/) 428 ; These are the initial values of the registers 429 ; They are popped off the stack when printed, freeing up stack space 430 431vCrashAF: 432 .ds 2 433vCrashBC: 434 .ds 2 435vCrashDE: 436 .ds 2 437vCrashHL: 438 .ds 2 439vCrashSP: 440 .ds 2 441 442 .ds SCRN_X_B 443vHeldKeys: 444 .ds 1 ; Keys held on previous frame 445vUnlockCounter: 446 .ds 1 ; How many frames until dumps are "unlocked" 447vWhichDump: 448 .ds 1 449vDumpHL: 450 .ds 2 451vDumpSP: 452 .ds 2 453vCrashVBK: 454 .ds 1 455 .ds 4 ; Unused 456 457 .ds SCRN_VX_B - SCRN_X_B - 1 458vCrashDumpScreen: 459vCrashDumpScreenRow0 = vCrashDumpScreen + 1 * SCRN_VX_B 460vCrashDumpScreenRow1 = vCrashDumpScreen + 2 * SCRN_VX_B 461vCrashDumpScreenRow2 = vCrashDumpScreen + 3 * SCRN_VX_B 462vCrashDumpScreenRow3 = vCrashDumpScreen + 4 * SCRN_VX_B 463vCrashDumpScreenRow4 = vCrashDumpScreen + 5 * SCRN_VX_B 464vCrashDumpScreenRow5 = vCrashDumpScreen + 6 * SCRN_VX_B 465vCrashDumpScreenRow6 = vCrashDumpScreen + 7 * SCRN_VX_B 466vCrashDumpScreenRow7 = vCrashDumpScreen + 8 * SCRN_VX_B 467vCrashDumpScreenRow8 = vCrashDumpScreen + 9 * SCRN_VX_B 468vCrashDumpScreenRow9 = vCrashDumpScreen + 10 * SCRN_VX_B 469vCrashDumpScreenRow10 = vCrashDumpScreen + 11 * SCRN_VX_B 470vCrashDumpScreenRow11 = vCrashDumpScreen + 12 * SCRN_VX_B 471vCrashDumpScreenRow12 = vCrashDumpScreen + 13 * SCRN_VX_B 472vCrashDumpScreenRow13 = vCrashDumpScreen + 14 * SCRN_VX_B 473vCrashDumpScreenRow14 = vCrashDumpScreen + 15 * SCRN_VX_B 474vCrashDumpScreenRow15 = vCrashDumpScreen + 16 * SCRN_VX_B 475vCrashDumpScreenRow16 = vCrashDumpScreen + 17 * SCRN_VX_B 476vCrashDumpScreenRow17 = vCrashDumpScreen + 18 * SCRN_VX_B