#include <cafe/os/OSException.h>

typedef BOOL (*OSExceptionCallback)(OSContext* interruptedContext);

OSExceptionCallback OSSetExceptionCallback(OSExceptionType exceptionType,
                                           OSExceptionCallback callback);


exceptionType Exception type to set callback.
callback Pointer to the new exception callback.

Return Values

The existing callback pointer.

NULL is returned if no previous handler was installed. NULL is a valid value to set the specific exception handler to "do nothing".


OSSetExceptionCallback should be called per thread and per core, on the thread that was created, usually shortly after it was created. The exception handler (DSI, ISI, Program, PerfMon) for a thread is inherited from the parent thread at thread-creation time. The exception callback for a specific thread for a specific core is called by the COS kernel when each exception occurs.

For a program to have a global exception handler to catch crashes, it would be sufficient to call OSSetExceptionCallback once per core from the default threads of each core. The exception handler will be propagated to each created thread.


If a previous non-NULL callback has been registered on a thread, the user-installed handler is responsible for taking the correct action and subsequently calling the previously installed handler. This allows chains of handlers to "override" the behavior of previously installed handlers.

Exception handlers are expected to call the previously installed handler. COS will only call the latest installed handler.


When called, a user-installed exception handler is running within a restricted environment. The stacks are supplied by COS and have a standard size of 4K per core. The current OSContext is the standard COS OSContext for handling exceptions. Floating-point can be invoked during an exception handler, but is not recommended because it causes an additional trip to the kernel to handle enabling MSR[FP].

The callback returns a BOOL indicating whether the callback handled the exception. If the exception was handled, COS resumes execution of the interrupted (thread) context. If the exception was not handled, COS calls the default user-mode exception handler which prints the details of an unhandled exception exception (such as a crash).

Keep It Simple

Nintendo recommends deferring long-duration work until thread-execution time. User-installed callbacks are executed with interrupts OFF. User-installed callbacks must return to the calling function to resume execution on a core.

If resuming execution is desired, user-installed callbacks should minimize the amount of time that interrupts are disabled by deferring work, if possible, to be performed on a higher priority thread than the thread that caused the exception. A general rule is that trivial work should be performed immediately inside the callback and any work lasting substantially longer than 2-5 microseconds should be postponed to execute on a higher-priority thread.

A user-mode handler determines where a DSI occurred and how to handle the DSI by checking the DSI exception-specific fields in the interrupted context:

u32 exception_specific0;  // contains DSISR during DSI
u32 exception_specific1;  // contains DAR during DSI            

Exceptional Situations

Re-entering the user-mode exception handler is an illegal operation in COS. If a user-installed handler needs to cause a thread to run, the handler should signal an event to a thread waiting on the event, or send a message to the message queue to unblock a waiting thread. Re-entry includes causing a DSI within any of the exception handlers.

If the user-mode exception handler is re-entered, any registered user-mode handlers are not called, but the COS standard user-mode handler is invoked instead.

Failure to properly service a DSI-causing thread can cause what initially appear to be infinite loops where the DSI causes an exception handler to be invoked which, due to a defect, fails to handle the DSI but returns TRUE which causes COS to attempt to resume execution. This in turn causes another DSI, ad infinitum. This lack of forward progress is discovered by the COS kernel and the process is terminated.

Non-Maskable Exceptions

DSIs, ISIs, and Program exceptions are not masked by interrupts being OFF. This indicates that a DSI, ISI, or Program exception can happen in the middle of (COS or Game) code, which is extremely sensitive to being interrupted. Exception callbacks can determine whether they received a DSI while interrupts were disabled by inspecting the interrupted context's SRR1 field's EE bit. If SRR1[EE] is 0, COS functions should not be called if the callback is to resume execution.

Supported Exceptions


The performance monitor provides the ability to generate a performance monitor interrupt triggered by a counter overflow condition in one of the performance monitor counter registers (PMC1 - PMC4). For more information, refer to the Espresso user manual that is available from your local Nintendo developer support group website.

A performance monitor demo is located in the directory $CAFE_ROOT/system/src/demo/pmcpu.

Do not allocate or use an OSContext through an address that was mapped by the OSMapMemory function. For more information, see OSMapMemory.

Do Not Call From

Callbacks Do not call this function from any callback function.

Revision History

2014/07/24 Added PM info and demo path.
2014/01/24 Added Caution to Description block.
2013/05/08 Automated cleanup pass.
2012/08/02 Cleanup Pass.
2012/02/29 Initial version.