Last active
May 5, 2023 14:21
-
-
Save OrionNavattan/ca1c451587cb3c9f16fb8650101bb7df to your computer and use it in GitHub Desktop.
Rough code for an extension to Vladikcomper's error handler that adds support for handling sub CPU exceptions in Mega CD Mode 1.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; --------------------------------------------------------------- | |
; Exception entry points on sub CPU. Identical to those used on | |
; main CPU with the exception of noting that they originated | |
; from the sub CPU. These are not pointed to directly by the | |
; vector table, but rather via the user jump table. Bus Error has | |
; been removed, as there is no jump table entry for it. | |
; --------------------------------------------------------------- | |
AddressError: | |
__ErrorMessage "SUB: ADDRESS ERROR", _eh_show_sr_usp|_eh_address_error | |
IllegalInstr: | |
__ErrorMessage "SUB: ILLEGAL INSTRUCTION", _eh_show_sr_usp | |
ZeroDivide: | |
__ErrorMessage "SUB: ZERO DIVIDE", _eh_show_sr_usp | |
ChkInstr: | |
__ErrorMessage "SUB: CHK INSTRUCTION", _eh_show_sr_usp | |
TrapvInstr: | |
__ErrorMessage "SUB: TRAPV INSTRUCTION", _eh_show_sr_usp | |
PrivilegeViol: | |
__ErrorMessage "SUB: PRIVILEGE VIOLATION", _eh_show_sr_usp | |
Trace: | |
__ErrorMessage "SUB: TRACE", _eh_show_sr_usp | |
Line1010Emu: | |
__ErrorMessage "SUB: LINE 1010 EMULATOR", _eh_show_sr_usp | |
Line1111Emu: | |
__ErrorMessage "SUB: LINE 1111 EMULATOR", _eh_show_sr_usp | |
ErrorExcept: | |
__ErrorMessage "SUB: ERROR EXCEPTION", _eh_show_sr_usp | |
; --------------------------------------------------------------- | |
; The user init routine of the sub CPU program will need to set | |
; up the jump table entries for the exception vectors. The | |
; following is an example of how to do this. | |
; --------------------------------------------------------------- | |
lea ExceptionPointers(pc),a0 ; pointers to exception entry points | |
lea (_AddressError+2).w,a1 ; first error vector in jump table ($5F42) | |
moveq #10-1,d0 ; 10 vectors total | |
.vectorloop: | |
move.l (a0)+,(a1)+ ; set table entry to point to handler | |
addq.l #2,a1 ; skip over instruction opcode | |
dbf d0,.vectorloop ; repeat for all exception vectors | |
rts | |
ExceptionPointers: | |
dc.l AddressError | |
dc.l IllegalInstr | |
dc.l ZeroDivide | |
dc.l ChkInstr | |
dc.l TrapvInstr | |
dc.l PrivilegeViol | |
dc.l Trace | |
dc.l Line1010Emu | |
dc.l Line1111Emu | |
dc.l ErrorExcept | |
; --------------------------------------------------------------- | |
; Sub CPU error handler module. About all it does is dump the | |
; registers and signal the main CPU that it has crashed. | |
; Identical in both Mode 1 and 2. | |
; --------------------------------------------------------------- | |
ErrorHandler: | |
move #$2700,sr ; disable all interrupts | |
st.b (mcd_sub_flag).w ; set flag to let main CPU know we've crashed (assumes communication protocol includes checking this flag for $FF before sending commands or while waiting for responses) | |
movem.l d0-a6,-(sp) ; dump all registers | |
move.l usp,a0 | |
move.l a0,-(sp) ; dump USP (unnecessary if BIOS is being used, as user mode can not be used with it) | |
.waitmain: | |
cmpi.b #$FF,(mcd_main_flag).w ; has the main CPU noticed? | |
bne.s .waitmain ; if not, branch | |
; Main CPU has noticed | |
move.l sp,(mcd_subcom_0).w ; get address of bottom of stack (including dumped registers) for main CPU | |
sf.b (mcd_sub_flag).w ; clear flag to let main CPU know we are done | |
stop #$2700 ; halt execution | |
; --------------------------------------------------------------- | |
; Main CPU handler for sub CPU exceptions. Entered by means of | |
; one of the trap vectors when it detects that the sub CPU has | |
; crashed. Nearly the same as that used for processing main | |
; CPU exceptions, except it is reading the exception arguments, | |
; register dump and stack frame from the sub CPU's stack. | |
; This assumes that the initial stack set by the BIOS is | |
; still being used, and that the stack is in the first program | |
; RAM bank (which it is by default). Both Mode 1 and Mode 2 should | |
; be supported; the only change being the value of 'program_ram'. | |
; --------------------------------------------------------------- | |
SubCPUError: | |
move #$2700,sr ; disable interrupts | |
move.b (mcd_sub_flag).l,(mcd_main_flag).l ; let sub CPU know we've noticed | |
.waitsub: | |
tst.b (mcd_sub_flag).l ; is the sub CPU done? | |
bne.s .waitsub ; if not, branch | |
.waitsubbus: | |
bset #1,(mcd_reset).l ; request the sub CPU bus | |
bne.s .waitsubbus ; if it has not been granted, wait | |
lea -sizeof_Console_RAM(sp),sp ; allocate memory for console on main CPU stack | |
jsr ErrorHandler_SetupVDP(pc) | |
lea 4(sp),a3 | |
jsr Error_InitConsole(pc) | |
lea Str_SetErrorScreen(pc),a0 | |
jsr Console_Write(pc) | |
movea.l (mcd_subcom_0).l,a4 ; get sub CPU stack bottom address (the start of the dumped registers) | |
adda.l #program_ram,a4 ; a4 = main CPU address for bottom of sub CPU stack | |
move.b (mcd_mem_mode).l,d3 | |
andi.b #(~program_ram_bank)&$FF,d3 ; set program ram bank to 0 | |
move.b d3,(mcd_mem_mode).l | |
; The following code would allow the use of a stack anywhere in program RAM. However, | |
; the custom initial stack pointer value would need to be passed n one of the command | |
; registers instead of read from the sub CPU vector table. | |
; lea (program_ram).l,a4 | |
; movea.l (mcd_subcom_0).l,d1 ; get sub CPU stack bottom address (the start of the dumped registers) | |
; move.l d1,d2 | |
; andi.l #sizeof_prgram-1,d2 ; d2 = offset in program ram window | |
; adda.l d2,a1 ; a1 = main CPU address of stack | |
; move.b (mcd_mem_mode).l,d2 ; get current memory settings | |
; andi.b #(~program_ram_bank)&$FF,d2 ; clear program ram bank bits | |
; swap d1 | |
; ror.b #3,d1 | |
; andi.b #program_ram_bank,d1 | |
; or.b d2,d1 ; d2 = program ram bank where bottom of stack is | |
; move.b d1,(mcd_mem_mode).l | |
move.l a4,-(sp) ; back up sub CPU stack bottom for later | |
lea $40(a4),a4 ; a4 = arguments and exception stack frame ($40 = d0-a6+usp) | |
; Print error description | |
movea.l (a4)+,a1 ; get error text pointer | |
adda.l #program_ram,a1 ; convert to main CPU address | |
lea (a4),a2 ; a2 = load arguments buffer (if present) | |
jsr Console_WriteLine_Formatted(pc) | |
jsr Console_StartNewLine(pc) | |
lea (a2),a4 ; a4 = sub CPU stack frame (after arguments buffer was processed by Console_Write) | |
; Load screen configuration bitfield | |
move.b (a1)+,d6 ; d6 = configuration bitfield | |
bpl.s .align_ok ; if "_eh_align_offset" is not set, branch | |
addq.w #1,a1 ; skip a byte to avoid address error on reading the next word | |
.align_ok: | |
lea (a1),a3 ; a3 may be used to fetch console program address later | |
; Print error address (for address error only) | |
btst #0,d6 ; does error has extended stack frame (Address Error only)? | |
beq.s .skip1 ; if not, branch | |
lea Str_Address(pc),a1 ; a1 = formatted string | |
lea 2(a4),a2 ; a2 = arguments buffer | |
jsr Console_WriteLine_Formatted(pc) | |
addq.w #8,a4 | |
.skip1: | |
; Print error location | |
lea Str_Location(pc),a1 ; a1 = formatted string | |
lea 2(a4),a2 ; a2 = arguments buffer | |
jsr Console_WriteLine_Formatted(pc) | |
; Print module name error occured in | |
lea Str_Module(pc),a1 ; a1 = formatted string | |
lea 2(a4),a2 ; a2 = arguments buffer | |
jsr Console_WriteLine_Formatted(pc) | |
; Print caller | |
movea.l (program_ram),a1 ; a1 = sub CPU initial stack pointer value | |
lea 6(a4),a2 ; a2 = call stack (after exception stack frame) | |
jsr Error_MaskStackBoundaries(pc) | |
jsr Error_GuessCaller(pc) ; d1 = caller | |
lea Str_Caller(pc),a1 ; a1 = formatted string | |
move.l d1,-(sp) | |
lea (sp),a2 ; a2 = arguments buffer | |
jsr Console_WriteLine_Formatted(pc) | |
jsr Console_StartNewLine(pc) | |
addq.w #4,sp ; free argument | |
movea.l (sp)+,a4 ; restore sub stack bottom address | |
lea 4(a4),a2 ; use register buffer as arguments | |
; Print data registers | |
jsr Console_GetPosAsXY(pc) ; d0/d1 = XY-pos | |
move.w d1,-(sp) ; remember line | |
moveq #3,d0 ; left margin for data registers | |
jsr Console_SetPosAsXY(pc) | |
move.w #'d0',d0 ; d0 = 'd0', what a twist !!! | |
moveq #8-1,d5 ; number of registers - 1 | |
jsr Error_DrawRegisters(pc) | |
; Print address registers | |
move.w (sp)+,d1 ; restore line | |
moveq #$11,d0 ; left margin for address registers | |
jsr Console_SetPosAsXY(pc) | |
move.w #'a0',d0 | |
moveq #7-1,d5 ; number of registers - 1 | |
jsr Error_DrawRegisters(pc) | |
; Special case : stack pointer (SP) | |
move.w #'sp',d0 | |
moveq #0,d5 ; number of registers - 1 | |
lea $40(a4),a2 ; a2 = top of stack frame | |
move.l a2,d4 | |
andi.l #$FFFF,d4 ; convert to sub CPU address | |
move.l d4,-(sp) | |
lea (sp),a2 ; a2 = pointer to where address of frame bottom is written | |
jsr Error_DrawRegisters(pc) | |
addq.w #4,sp | |
; Display USP and SR (if requested) | |
btst #1,d6 | |
beq.s .skip2 | |
; Draw 'USP' | |
lea Str_USP(pc),a1 | |
lea (a4),a2 ; a2 = USP dumped by sub CPU | |
jsr Console_Write_Formatted(pc) | |
; Draw 'SR' | |
lea Str_SR(pc),a1 | |
lea $44(a4),a2 ; a2 = top of exception frame | |
btst #0,d6 ; does error has extended stack frame (Address Error only)? | |
beq.s .notaddrerr ; if not, branch | |
addq.w #8,a2 ; skip extended frame | |
.notaddrerr: | |
jsr Console_WriteLine_Formatted(pc) | |
.skip2: | |
jsr Console_GetPosAsXY(pc) ; d0/d1 = XY-pos | |
addq.w #1,d1 ; skip a line | |
moveq #1,d0 ; left margin for data registers | |
jsr Console_SetPosAsXY(pc) | |
jsr Console_StartNewLine(pc) | |
; ----------------- | |
; Stack contents | |
; ----------------- | |
movea.l (program_ram).l,a1 | |
adda.l #program_ram,a1 ; a1 = stack top address in program ram window | |
lea $44(a4),a2 ; a2 = stack bottom | |
;subq.l #1,a1 ; unnecessary here as sub CPU stack top can never be zero | |
bsr.w Error_MaskStackBoundaries | |
jsr Console_GetPosAsXY(pc) ; d0/d1 = XY-pos | |
moveq #28-3,d5 | |
sub.w d1,d5 | |
bmi.s .stack_done | |
bsr.w Error_DrawStackRow_First | |
.stack_loop: | |
jsr Error_DrawStackRow(pc) | |
dbf d5,.stack_loop | |
.stack_done: | |
bra.w Error_IdleLoop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment