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