I'm trying to build an MS-DOS emulator and trying to add support for custom critical error handler. The application overwrites the in-built DOS critical error handler with something like this:
mov dx, seg int24handler
mov ds, dx
mov dx, offset int24handler
mov ax, 2524h
int 21h
Then when various file operations fail I trigger
int 24h
This is fine at passing control to the custom critical error handler, but in some scenarios instead of coming back from the int 24 handler the CS register is corrupt and tries to execute memory where there isn't any code. But the code runs fine in real MS-DOS. I'm currently working on adding a debugging feature to help collect more detail. However in the meantime I want to try and verify my stack is set up like this diagram from Advanced MS-DOS Programming prior to passing control to INT24:
I am trying to work out what equivalent CPU instructions would help set up stack like that diagram. From what I can tell from publicly releases MS-DOS 2.0 source code at https://github.com/microsoft/MS-DOS/blob/04a3d20ff411409ab98474892b2bb1713bde0f7f/v2.0/source/CTRLC.ASM I need to backup and restore SS & SP register. INT 24 is executed with code INT int_fatal_abort
However I don't see how this helps set up the stack as per the diagram? Is there anything I'm missing?
XCHG AX,DI ; Error code in DI, count in AX
AND DI,STECODE ; And off status bits
CMP DI,WRECODE ; Write Protect Error?
JNZ NOSETWRPERR
PUSH AX
MOV AL,ES:[BP.dpb_drive]
MOV BYTE PTR [WPERR],AL ; Flag drive with WP error
POP AX
NOSETWRPERR:
SUB AX,CX ; Number of sectors successfully transferred
ADD DX,AX ; First sector number to retry
PUSH DX
MUL ES:[BP.dpb_sector_size] ; Number of bytes transferred
POP DX
ADD BX,AX ; First address for retry
XOR AH,AH ; Flag disk section in error
CMP DX,ES:[BP.dpb_first_FAT] ; In reserved area?
JB ERRINT
INC AH ; Flag for FAT
CMP DX,ES:[BP.dpb_dir_sector] ; In FAT?
JB ERRINT
INC AH
CMP DX,ES:[BP.dpb_first_sector] ; In directory?
JB ERRINT
INC AH ; Must be in data area
ERRINT:
SHL AH,1 ; Make room for read/write bit
OR AH,BYTE PTR [READOP]
entry FATAL
MOV AL,ES:[BP.dpb_drive] ; Get drive number
entry FATAL1
MOV WORD PTR [EXITHOLD+2],ES
MOV WORD PTR [EXITHOLD],BP ; The only things we preserve
LES SI,ES:[BP.dpb_driver_addr]
MOV BP,ES ; BP:SI points to the device involved
FATALC:
CMP BYTE PTR [ERRORMODE],0
JNZ SETIGN ; No INT 24s if already INT 24
MOV [CONTSTK],SP
PUSH SS
POP ES
ASSUME ES:DOSGROUP
CLI ; Prepare to play with stack
INC BYTE PTR [ERRORMODE] ; Flag INT 24 in progress
DEC BYTE PTR [INDOS] ; INT 24 handler might not return
MOV SS,[user_SS]
ASSUME SS:NOTHING
MOV SP,ES:[user_SP] ; User stack pointer restored
INT int_fatal_abort ; Fatal error interrupt vector, must preserve ES
MOV ES:[user_SP],SP ; restore our stack
MOV ES:[user_SS],SS
MOV SP,ES
MOV SS,SP
ASSUME SS:DOSGROUP
MOV SP,[CONTSTK]
INC BYTE PTR [INDOS] ; Back in the DOS
MOV BYTE PTR [ERRORMODE],0 ; Back from INT 24
STI
IGNRET:
LES BP,[EXITHOLD]
ASSUME ES:NOTHING
CMP AL,2
JZ error_abort
MOV BYTE PTR [WPERR],-1 ;Forget about WP error
return
SETIGN:
XOR AL,AL ;Flag ignore
JMP SHORT IGNRET
error_abort:
PUSH SS
POP DS
ASSUME DS:DOSGROUP
CMP BYTE PTR [CONSWAP],0
JZ NOSWAP2
invoke SWAPBACK
NOSWAP2:
MOV BYTE PTR [exit_Type],Exit_hard_error
MOV DS,[CurrentPDB]
ASSUME DS:NOTHING
