
This file's purpose is to help debugging by explaining the error screen, as well as giving info on the circumstances that can lead to it.


GENERAL NOTES

- Don't trust the F register on the error screen. It will be constant ($C0 IIRC) and I can't do much about that.
- The error screen attempts to preserve the GB's state as much as possible. Thus, for example, only $C000 is modified. It's best to not use this address for storage (though, for example, the Konami cheat uses it).
- To further preserve memory, the error handler sets SP to VRAM, specifically meaning it will trash some of the 9800 tilemap.
- The error screen disables interrupts, again to preserve as much of the state as possible. It also makes it autonomous, meaning it should be as accurate as possible.
- The stack dump automatically dumps memory *around* the current SP value. It may not dump the entire stack.


ERROR #0
"RAM IS RW BUT NOT X"

The game caught an attempt to execute code from RAM, be it VRAM, WRAM, Echo RAM, OAM, FEA0-FEFF, I/O, HRAM or IF.
There are two traps present : one at call-time, and one at run-time.
The call-time trap is placed in `rst $38`, which triggers the error screen if the target is not in ROM. Note that NOP sleds, ROP chains and more advanced trickery will bypass this trap.
The run-time trap is in the Mode 2 ("music") interrupt handler. Basically, this handler will trigger the error screen if it detects its return address is in WRAM. Note that bypassing this trap is fairly simple : prepend a `di` to your WRAM code, and end it with a `reti`.

Error screen walkthrough :
The error screen will differ based on which trap activated. The call-time one will have all registers in the state they were in when `rst $38` was called. The run-time one will have all registers in the state they were in when the interrupt fired. The only difference should be that the run-time trap will have [sp] pointing to RAM... unless a `rst $38` was called from RAM.


ERROR #1
"CANNOT DIV BY ZERO"

The division function DivideHLByDE was called with DE null. Please note that it is possible to bypass this check with DivideHLByDE_KnownDE, but this is only intended if DE is **known** to be non-zero (for example if dividing by a constant).


ERROR #2
"WOOPS, WRONG DOOR"

LoadMap was called with a wrong warp ID (ie. the ID was above the number of warps the map has).


ERROR #3
"WHERE AM I??"

LoadMap was called with a map ID above the last valid ID.


ERROR #4
"INVALID BATTLE TRANSITION"

An invalid battle transition was called.


ERROR #5
"NONEXISTANT ENEMY"

This error is never called. (Yet)


ERROR #6
"BAD THREAD 2 POINTER"

This error occurs when the Thread 2 ID is set to an invalid value (hence an invalid call would occur).


ERROR #ERROR
"UNKNOWN ERROR!!!"

The error screen has been triggered with an invalid error code. Good luck investigating that.
At least the error handler should be completely safe and always behave normally (this being the most "abnormal" state). The opposite would be a shame - imagine ACE induced by the error handler !
