libdragon
|
Handle hardware-generated exceptions. More...
Files | |
file | exception.c |
Exception Handler. | |
file | exception.h |
Exception Handler. | |
Data Structures | |
struct | reg_block_t |
Structure representing a register block. More... | |
struct | exception_t |
Structure representing an exception. More... | |
Macros | |
#define | MAX_RESET_HANDLERS 4 |
Maximum number of reset handlers that can be registered. | |
#define | RESET_TIME_LENGTH TICKS_FROM_MS(200) |
Guaranteed length of the reset time. More... | |
Enumerations | |
enum | { EXCEPTION_TYPE_UNKNOWN = 0 , EXCEPTION_TYPE_RESET , EXCEPTION_TYPE_CRITICAL } |
Exception types. More... | |
enum | exception_code_t { EXCEPTION_CODE_INTERRUPT = 0 , EXCEPTION_CODE_TLB_MODIFICATION = 1 , EXCEPTION_CODE_TLB_LOAD_I_MISS = 2 , EXCEPTION_CODE_TLB_STORE_MISS = 3 , EXCEPTION_CODE_LOAD_I_ADDRESS_ERROR = 4 , EXCEPTION_CODE_STORE_ADDRESS_ERROR = 5 , EXCEPTION_CODE_I_BUS_ERROR = 6 , EXCEPTION_CODE_D_BUS_ERROR = 7 , EXCEPTION_CODE_SYS_CALL = 8 , EXCEPTION_CODE_BREAKPOINT = 9 , EXCEPTION_CODE_RESERVED_INSTRUCTION = 10 , EXCEPTION_CODE_COPROCESSOR_UNUSABLE = 11 , EXCEPTION_CODE_ARITHMETIC_OVERFLOW = 12 , EXCEPTION_CODE_TRAP = 13 , EXCEPTION_CODE_FLOATING_POINT = 15 , EXCEPTION_CODE_WATCH = 23 } |
Exception codes. | |
Functions | |
void | register_exception_handler (void(*cb)(exception_t *)) |
Register an exception handler to handle exceptions. More... | |
void | exception_default_handler (exception_t *ex) |
Default exception handler. More... | |
void | __onCriticalException (volatile reg_block_t *regs) |
Respond to a critical exception. | |
void | register_reset_handler (void(*cb)(void)) |
Register a handler that will be called when the user presses the RESET button. More... | |
uint32_t | exception_reset_time (void) |
Check whether the RESET button was pressed and how long we are into the reset process. More... | |
void | __onResetException (volatile reg_block_t *regs) |
Respond to a reset exception. More... | |
Variables | |
volatile reg_block_t | __baseRegAddr |
Base register offset as defined by the interrupt controller. | |
Handle hardware-generated exceptions.
The exception handler traps exceptions generated by hardware. This could be an invalid instruction or invalid memory access exception or it could be a reset exception. In both cases, a handler registered with register_exception_handler will be passed information regarding the exception type and relevant registers.
struct reg_block_t |
Structure representing a register block.
DO NOT modify the order unless editing inthandler.S
struct exception_t |
Structure representing an exception.
Data Fields | ||
---|---|---|
int32_t | type | Exception type. |
exception_code_t | code | Underlying exception code. |
const char * | info | String information of exception. |
volatile reg_block_t * | regs | Registers at point of exception. |
#define RESET_TIME_LENGTH TICKS_FROM_MS(200) |
Guaranteed length of the reset time.
This is the guaranteed length of the reset time, that is the time that goes between the user pressing the reset button, and the CPU actually resetting. See exception_reset_time for more details.
anonymous enum |
void register_exception_handler | ( | void(*)(exception_t *) | cb | ) |
Register an exception handler to handle exceptions.
The registered handle is responsible for clearing any bits that may cause a re-trigger of the same exception and updating the EPC. An important example is the cause bits (12-17) of FCR31 from cop1. To prevent re-triggering the exception they should be cleared by the handler.
To manipulate the registers, update the values in the exception_t struct. They will be restored to appropriate locations when returning from the handler. Setting them directly will not work as expected as they will get overwritten with the values pointed by the struct.
There is only one exception to this, cr (cause register) which is also modified by the int handler before the saved values are restored thus it is only possible to update it through C0_WRITE_CR macro if it is needed. This shouldn't be necessary though as they are already handled by the library.
k0 ($26), k1 ($27) are not saved/restored and will not be available in the handler. Theoretically we can exclude s0-s7 ($16-$23), and gp ($28) to gain some performance as they are already saved by GCC when necessary. The same is true for sp ($29) and ra ($31) but current interrupt handler manipulates them via allocating a new stack and doing a jal. Similarly floating point registers f21-f31 are callee-saved. In the future we may consider removing them from the save state for interrupts (but not for exceptions)
[in] | cb | Callback function to call when exceptions happen |
void exception_default_handler | ( | exception_t * | ex | ) |
Default exception handler.
This handler is installed by default for all exceptions. It initializes the console and dump the exception state to the screen, including the value of all GPR/FPR registers. It then calls abort() to abort execution.
void register_reset_handler | ( | void(*)(void) | cb | ) |
Register a handler that will be called when the user presses the RESET button.
The N64 sends an interrupt when the RESET button is pressed, and then actually resets the console after about ~500ms (but less on some models, see RESET_TIME_LENGTH).
Registering a handler can be used to perform a clean reset. Technically, at the hardware level, it is important that the RCP is completely idle when the reset happens, or it might freeze and require a power-cycle to unfreeze. This means that any I/O, audio, video activity must cease before RESET_TIME_LENGTH has elapsed.
This entry point can be used by the game code to basically halts itself and stops issuing commands. Libdragon itself will register handlers to halt internal modules so to provide a basic good reset experience.
Handlers can use exception_reset_time to read how much has passed since the RESET button was pressed.
cb | Callback to invoke when the reset button is pressed. |
uint32_t exception_reset_time | ( | void | ) |
Check whether the RESET button was pressed and how long we are into the reset process.
This function returns how many ticks have elapsed since the user has pressed the RESET button, or 0 if the user has not pressed it.
It can be used by user code to perform actions during the RESET process (see register_reset_handler). It is also possible to simply poll this value to check at any time if the button has been pressed or not.
The reset process takes about 500ms between the user pressing the RESET button and the CPU being actually reset, though on some consoles it seems to be much less. See RESET_TIME_LENGTH for more information. For the broadest compatibility, please use RESET_TIME_LENGTH to implement the reset logic.
Notice also that the reset process is initiated when the user presses the button, but the reset will not happen until the user releases the button. So keeping the button pressed is a good way to check if the application actually winds down correctly.
void __onResetException | ( | volatile reg_block_t * | regs | ) |
Respond to a reset exception.
Calls the handlers registered by register_reset_handler.