Skip to content

Instantly share code, notes, and snippets.

@OrionNavattan
Last active May 5, 2023 14:21
Show Gist options
  • Save OrionNavattan/ca1c451587cb3c9f16fb8650101bb7df to your computer and use it in GitHub Desktop.
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.
; ---------------------------------------------------------------
; 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