For 8-bit architectures:
Code:
brkHandler:
; PRECONDITIONS:
; 6502 or 65C02 or 65816 operating in emulation mode
; Stack image as follows:
; (open) <-- S points here
; Y-saved
; X-saved
; A-saved
; P-saved
; PCL-saved
; PCH-saved
; PC points to byte _after_ the BRK operand byte.
; Direct- or zero-page locations PCL and PCH adjacent each other to form a pointer
; BRK operand specifies PCL-1 of a ROM jump table placed at EPV_ADDRESS.
; Invoked procedure does NOT use RTI to return, but jumps to RETURN instead.
tsx
lda $0105,x
sec
sbc #1
sta pcl
lda $0106,x
sbc #0
sta pch
lda #>EPV_ADDRESS
pha
txa
tay
lda (pcl),y
pha
rts
RETURN:
pla
tay
pla
tax
pla
rti
EPV_ADDRESS:
jmp console_out
jmp console_in
jmp open_file
...etc...
For 65816-native operation:
Code:
brkHandler:
; PRECONDITIONS:
; 65816 operating in 16-bit native mode
; Stack image as follows:
; (open) <-- S points here
; YL-saved
; YH-saved
; XL-saved
; XH-saved
; AL-saved
; AH-saved
; P-saved
; PCL-saved
; PCH-saved
; K-saved
; PC points to byte _after_ the BRK operand byte.
; BRK operand specifies byte offset into ROM jump table placed at EPV.
; Invoked procedure does NOT use RTI to return, but jumps to RETURN instead.
phd
tsc
tcd
dec 10
lda [10]
and #$00FF
tax
inc 10
jmp (EPV,x)
RETURN:
pld
ply
plx
pla
rti
EPV:
.word console_out
.word console_in
.word open_file
..etc..
In all honesty, though, it's much faster to simply JSR directly to the appropriate ROM JMP table instruction (for 6502) or to LDX the byte offset and JSR a common entry point into ROM.
I like how CP/M handles the entry point problem: CALL 5 always works to invoke a DOS API function, because locations $0005..$0007 are preset to a JP directly into an undisclosed OS dispatcher routine at the time a program is launched. An OS for the 65816 can do the same thing: place a JMP or JML instruction at location $00..$03; the extra indirection adds only 3 cycles to the call, not including the JMP (abs,X) inside the OS itself. It'll be much faster than using BRK.