CPU event handlers
This is an advanced topic. Most programs will never need to implement a custom CPU event handler.
The Chombit microprocessor responds to CPU events by temporarily jumping to an event handler memory address, which contains assembly language instructions that perform some action. Afterwards, the handler can optionally restore the entire CPU state and resume executing the original program. The memory address of the handlers are stored in IO::CPU_FAIL_HANDLER, IO::CPU_TRACE_HANDLER, and IO::CPU_REQUEST_HANDLER.
Hybrix debugger handlers
When running your program in the Hybrix website development environment, the CPU event handlers can be intercepted by the debugger:
-
IO::CPU_FAIL_HANDLER: The debugger pauses the program before the event is triggered. If program is unpaused, the handler will be invoked. The Hybrix framework kernel's handler displays an error message and terminates the program. -
IO::CPU_TRACE_HANDLER: The debugger writes the message to the website console, then triggers the CPU event. The Hybrix framework kernel's handler simply returns control to the program. -
IO::CPU_REQUEST_HANDLER: If the request number is a debugger function, the action will be performed, then control is returned to the program without triggering the CPU event at all;IO::CPU_REQUEST_HANDLERis bypassed entirely. Otherwise, theIO::CPU_REQUEST_HANDLERis invoked normally. The Hybrix framework kernel's handler doesn't implement anyREQUESTfunctions; it simply terminates the program with an error.
Event handler invocation
In order to manage critical problems such as a stack fault, the handler gets invoked without reliance on the CPU stack. Instead, the CPU state is saved in four special-purpose MMIO locations, in the following sequence:
- A parameter distinguishing the type of event is written to
IO::CPU_EVENT_VALUE. - The CPU flags byte is written to
IO::CPU_EVENT_FLAGS. - The
IPregister is written toIO::CPU_EVENT_IP. In the case of a CPU fault,CPU_EVENT_IPpoints to the instruction that failed; in all other cases, it points to the subsequent instruction. - The
FPregister is written toIO::CPU_EVENT_FP. - The
IPregister then jumps to the address read fromIO::CPU_FAIL_HANDLER,IO::CPU_TRACE_HANDLER, orIO::CPU_REQUEST_HANDLER.
Each step requires 1 clock cycle, thus the handler invocation adds 5 clock cycles to the execution time of the instruction that caused the event.
Writing an event handler
The code sample below illustrates a typical recipe for implementing an event handler:
@EXAMPLE_EVENT_HANDLER:
MOVE FP, $D0_0000
# CHECK WHAT KIND OF EVENT WE NEED TO HANDLE
COMPARE I:28, 123 # $D0_001C = CPU_EVENT_VALUE
. . .
(YOUR CODE HERE)
. . .
# RESTORE THE ORIGINAL CPU STATE
PUSH I:20 # $D0_0014 = CPU_EVENT_IP
PUSH B:19 # $D0_0013 = CPU_EVENT_FLAGS
PUSH I:24 # $D0_0018 = CPU_EVENT_FP
POP FP
POP FLAGS
POP IP
Important points:
- The
$D0_0080–$D0_00BFmemory range is reserved for usage by the kernel. - By pointing the
FPregister to$D0_0000, the MMIO locations can be accessed directly without need forLOAD/STORE. - To perfectly restore the CPU state including flags, the handler needs to push some values onto the original program's stack.
I/O definitions
CLASS IO_HAND_CONTROLLER # SIZE 8
. . .
# CUSTOM HANDLING FOR "FAIL", "TRACE", "REQUEST" AND OTHER CPU EVENTS
VAR CPU_EVENT_FLAGS: BYTE LOCATED AT $D0_0013
VAR CPU_EVENT_IP: INT LOCATED AT $D0_0014
VAR CPU_EVENT_FP: INT LOCATED AT $D0_0018
VAR CPU_EVENT_VALUE: INT LOCATED AT $D0_001C
VAR CPU_FAIL_HANDLER: INT LOCATED AT $D0_0020
VAR CPU_TRACE_HANDLER: INT LOCATED AT $D0_0024
VAR CPU_REQUEST_HANDLER: INT LOCATED AT $D0_0028
. . .
END MODULE