libdragon
Files | Data Structures | Macros | Enumerations | Functions | Variables

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.
 

Detailed Description

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.


Data Structure Documentation

◆ reg_block_t

struct reg_block_t

Structure representing a register block.

DO NOT modify the order unless editing inthandler.S

Data Fields
uint64_t gpr[32] General purpose registers 1-32.
uint64_t hi HI.
uint64_t lo LO.
uint32_t sr SR.
uint32_t cr CR (NOTE: can't modify this from an exception handler)
uint32_t epc represents EPC - COP0 register $14

The coprocessor 0 (system control coprocessor - COP0) register $14 is the return from exception program counter. For asynchronous exceptions it points to the place to continue execution whereas for synchronous (caused by code) exceptions, point to the instruction causing the fault condition, which needs correction in the exception handler. This member is for reading/writing its value.

uint32_t fc31 FC31.
uint64_t fpr[32] Floating point registers 1-32.

◆ exception_t

struct exception_t

Structure representing an exception.

Data Fields
int32_t type Exception type.
See also
EXCEPTION_TYPE_RESET, EXCEPTION_TYPE_CRITICAL
exception_code_t code Underlying exception code.
const char * info String information of exception.
volatile reg_block_t * regs Registers at point of exception.

Macro Definition Documentation

◆ RESET_TIME_LENGTH

#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.

Note
The general knowledge about this is that the reset time should be 500 ms. Testing on different consoles show that, while most seem to reset after 500 ms, a few EU models reset after 200ms. So we define the timer shorter for greater compatibility.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum

Exception types.

Enumerator
EXCEPTION_TYPE_UNKNOWN 

Unknown exception.

EXCEPTION_TYPE_RESET 

Reset exception.

EXCEPTION_TYPE_CRITICAL 

Critical exception.

Function Documentation

◆ register_exception_handler()

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)

Parameters
[in]cbCallback function to call when exceptions happen

◆ exception_default_handler()

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.

◆ register_reset_handler()

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.

Parameters
cbCallback to invoke when the reset button is pressed.
Note
Reset handlers are called under interrupt.

◆ exception_reset_time()

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.

Returns
Ticks elapsed since RESET button was pressed, or 0 if the RESET button was not pressed.
See also
register_reset_handler
RESET_TIME_LENGTH

◆ __onResetException()

void __onResetException ( volatile reg_block_t regs)

Respond to a reset exception.

Calls the handlers registered by register_reset_handler.