    NAME Exception_Handler

; this contains the exception handler

    PUBLIC  SetException, Exception

CGROUP GROUP CODE
DGROUP GROUP DATA

state STRUC
      sip DW ?
      scs DW ?
      ssp DW ?
      sbp DW ?
      sds DW ?
state ENDS

numExceptions EQU 4

DATA SEGMENT PUBLIC 'DATA'

RATBL state numExceptions DUP (<>)

DATA ENDS


CODE SEGMENT PUBLIC 'CODE'
    ASSUME  CS:CODE, DS:DATA

$EJ

; SetException : PROCEDURE (number)
;
; THIS WORKS FOR EXCEPTIONS 0 TO 3


SetException PROC FAR

    POP CX     ; RETURN ADDRESS OFFSET
    POP BX     ; RETURN ADRESS SEGMENT
    POP SI     ; EXCEPTION NUMBER

    MOV AX,SI
    SHL AX,1   ; ax = 2 * num
    SHL SI,1
    SHL SI,1
    SHL SI,1   ; si = 8 * num
    ADD SI,AX  ; si = 10 * num

    MOV AX,DATA
    MOV ES,ax

    MOV ES:WORD PTR RATBL[SI].sip,CX
    MOV ES:WORD PTR RATBL[SI].scs,BX
    MOV ES:WORD PTR RATBL[SI].ssp,SP
    MOV ES:WORD PTR RATBL[SI].sbp,BP
    MOV ES:WORD PTR RATBL[SI].sds,DS

; RETURN OKCODE

    AND AX,0
    JMP ES:DWORD PTR RATBL[SI].sip

SetException ENDP


; Exception : PROCEDURE (num, error)
;
;

Exception PROC FAR

    POP CX   ; RETURN ADDRESS OFFSET
    POP BX   ; RETURN ADDRESS SEGMENT
    POP AX   ; CODE
    POP SI   ; EXCEPTION NUMBER
    CMP AX,0
    JE OKCODE

; THE CODE WAS AN ERROR. RETURN TO THE ADDRESS SPECIFIED
; BY THE SetException ROUTINE WITH AX = CODE. RESTORE THE
; STACK.

    MOV DX,AX    ; save error code
    MOV AX,SI
    SHL AX,1   ; ax = 2 * num
    SHL SI,1
    SHL SI,1
    SHL SI,1   ; si = 8 * num
    ADD SI,AX  ; si = 10 * num

    MOV AX,DATA
    MOV ES, AX
    MOV SP, ES:WORD PTR RATBL[SI].ssp
    MOV BP, ES:WORD PTR RATBL[SI].sbp
    MOV DS, ES:WORD PTR RATBL[SI].sds
    
    MOV AX,DX     ; restore error code
    JMP ES:DWORD PTR RATBL[SI].sip

OKCODE:
    PUSH BX
    PUSH CX
    RET

Exception ENDP

CODE ENDS

END
