	.386p
;************************************************************************/
;*	Copyright (C) 1986-1990 Phar Lap Software, Inc.			*/
;*	Unpublished - rights reserved under the Copyright Laws of the	*/
;*	United States.  Use, duplication, or disclosure by the 		*/
;*	Government is subject to restrictions as set forth in 		*/
;*	subparagraph (c)(1)(ii) of the Rights in Technical Data and 	*/
;*	Computer Software clause at 252.227-7013.			*/
;*	Phar Lap Software, Inc., 60 Aberdeen Ave., Cambridge, MA 02138	*/
;************************************************************************/

;
; This program demonstrates 
;

;
; Constants and data structures
;
include	dosx.ah

;
; Segment definitions and ordering.  
;
_codeseg	segment	byte public use32 'code'
_codeseg	ends
_data		segment	dword public use32 'data'
_data		ends
_stack		segment dword stack use32 'stack'
	db	2048 dup (?)	; 2K stack
_stack		ends

;
; Global data
;
	ifdef	NTSTYLE
	extrn	_ExitProcess@4:near
	endif

_data	segment	

	public	pm_cesel,pm_ceoff,rm_cevec
rm_cevec	dd	?	; original real mode critical err vector
pm_ceoff	dd	?	; original protected mode critical err vector
pm_cesel	dw	?		; 
	align	4

afile	db	'a:\foo',0
status_msg db	'Causing critical err by reading from open A: floppy drive'
	db	0Dh,0Ah,'$'
_data	ends

;****************************************************************************
; Program entry point
;****************************************************************************

	assume	cs:_codeseg,ds:_data
_codeseg	segment	

	public	main
main proc	near

;
; Save current real and protected mode critical error vectors, and install our
; critical error handler so it always gets control in protected mode.  We use
; the 2506h system call to do this, because the critical error interrupt is
; always issued in real mode by DOS.
;
	mov	cl, 24h			; save real mode critical error vector
	mov	ax, 2503h			; 
	int	21h				;
	mov	rm_cevec, ebx			; 
	mov	ax, 2502h		; save prot mode critical error vector
	mov	cl,24h				;
	int	21h				; 
	mov	pm_cesel, es			;
	mov	pm_ceoff, ebx			; 
	push	ds			; install our critical error handler 
	lea	edx, crite_hnd			; to always get control in
	mov	ax, cs				; protected mode
	mov	ds, ax				;
	mov	cl,24h				;
	mov	ax, 2506h			; 
	int	21h				;
	pop	ds				; 

;
; Cause a critical error by attempting to open a file on the A: floppy
; disk drive (which will cause a critical error if the floppy door is
; open).
;
	mov	ah, 09h			; tell user we're causing CTRL/C
	lea	edx, status_msg			;
	int	21h				;
	lea	edx,afile		; open file on A: drive
	mov	ax,3D00h			;
	int	21h				;

;
; Restore the original interrupt vectors and exit
;
	mov	cl, 24h			; restore original real mode vector
	mov	ebx, rm_cevec			; 
	mov	ax, 2505h			;
	int	21h				; 
	push	ds			; restore original prot mode vector
	mov	edx, pm_ceoff			; 
	mov	ax, pm_cesel			;
	mov	ds, ax				; 
	mov	cl,24h				;
	mov	ax, 2504h			;
	int	21h				; 
	pop	ds				;

	ifndef	NTSTYLE
	mov	ax, 4C00h		; exit to DOS
	int	21h				; 
	else
	mov	eax,0
	push	eax
	call	_ExitProcess@4
	endif

main endp

;****************************************************************************
; CRITE_HND - Protected Mode Critical Error Handler
;	It just prints out a message saying the error occurred, and then
;	returns, instructing DOS to fail the system call that caused the error.
;
;	To demonstrate how to use the interrupt stack frame to get the
;	original stack when the interrupt occurred, we pick up the 
;	original AX value for the INT 21h call (saved on the critical error
;	stack by DOS) and print out AH to show what call it was that failed.
;****************************************************************************
	public	crite_hnd
crite_hnd	proc	near
;
; Interrupt handler stack frame
;
#FLGS	equ	(dword ptr 52[ebp])	; 386|DOS-Extender flags
#GS	equ	(word ptr 48[ebp])	; original GS
#FS	equ	(word ptr 44[ebp])	; original FS
#DS	equ	(word ptr 40[ebp])	; original DS
#ES	equ	(word ptr 36[ebp])	; original ES
#SS	equ	(word ptr 32[ebp])	; original SS
#ESP	equ	(dword ptr 28[ebp])	; original ESP
#EFLAGS	equ	(dword ptr 24[ebp])	; original EFLAGS
#CS	equ	(dword ptr 20[ebp])	; original CS
#EIP	equ	(dword ptr 16[ebp])	; original EIP
#EBP	equ	(dword ptr [ebp])	; original EBP

;
; This is an interrupt handler, so we must preserve all registers we use.
; Also re-enable interrupts so hardware interrupts aren't blocked.
;
	sti				; re-enable interrupts
	push	ebp			; set up stack frame
	mov	ebp,esp				;
	push	ebx			; save regs, EXCEPT EAX which we
	push	ecx				; return a value in
	push	edx				;
	push	ds				;
	push	es				;
	mov	ax,SS_DATA		; set DS to our data segment
	mov	ds,ax				;

;
; Get the real mode stack address in ES:EBX.  The critical error 
; interrupt should ALWAYS be issued from real mode, but check in case 
; someone coded a software INT 24h in prot mode by mistake.
;
	test	#FLGS,IFL_RMODE		; branch if didn't come from real
	jz	#not_real			; mode
	movzx	ebx,#SS			; get linear addr of real mode stack
	shl	ebx,4				; in conventional memory
	add	ebx,#ESP			;
	mov	ax,SS_DOSMEM		; set ES to DOS memory segment
	mov	es,ax				;

;
; Print out a message, and the AH value for the INT 21h call that failed,
; so the user can see what the call was.
;
; DOS critical error stack frame
;
#DOS_FLAGS equ	(word ptr es:22[ebx])	; interrupt stack frame from real
#DOS_CS	equ	(word ptr es:20[ebx])		; mode INT 21h
#DOS_IP	equ	(word ptr es:18[ebx])		;
#DOS_ES	equ	(word ptr es:16[ebx])	; regs at time INT 21h was issued
#DOS_DS	equ	(word ptr es:14[ebx])		; in real mode
#DOS_BP	equ	(word ptr es:12[ebx])		;
#DOS_DI	equ	(word ptr es:10[ebx])		;
#DOS_SI	equ	(word ptr es:8[ebx])		;
#DOS_DX	equ	(word ptr es:6[ebx])		;
#DOS_CX	equ	(word ptr es:4[ebx])		;
#DOS_BX	equ	(word ptr es:2[ebx])		;
#DOS_AX	equ	(word ptr es:[ebx])		;

_data	segment
ce_msg	db	'Critical error occurred on INT 21h function $'
_data	ends
	mov	ah,9			; print critical err msg
	lea	edx,ce_msg			;
	int	21h				;
	mov	ax,#DOS_AX		; get original AX value
	mov	al,ah			; convert AH to ASCII, save in BX
	call	btohex				;
	mov	bx,ax				;
	mov	ah,2			; output function code & newline
	mov	dl,bl				;
	int	21h				;
	mov	dl,bh				;
	int	21h				;
	mov	dl,0Dh				;
	int	21h				;
	mov	dl,0Ah				;
	int	21h				;

#exit:
	mov	eax,3			; instruct DOS to fail the function
						; (DOS 3.1 and later only)
	pop	es			; restore regs
	pop	ds				;
	pop	edx				;
	pop	ecx				;
	pop	ebx				;
	pop	ebp				;
	iretd				; return from interrupt

#not_real:
;
; Interrupt didn't originate from real mode
;
_data	segment
nreal_msg db	'Critical error interrupt issued in prot mode!!',0Dh,0Ah,'$'
_data	ends
	mov	ah,9			; print error msg & exit
	lea	edx,nreal_msg			;
	int	21h				;
	jmp	#exit				;
crite_hnd	endp

;****************************************************************************
; BTOHEX -- convert byte in AL to ASCII hex in AX, all other regs unchanged
;****************************************************************************
btohex	proc	near	; convert byte to hex
	push	bx			; save regs
	mov	ah,0			; make sure AH is 0
	mov	bx,ax			; save value to convert
	call	ntohex			; get LS hex digit into BL
	xchg	ax,bx				;
	shr	ax,4			; get MS hex digit into AL
	call	ntohex				;
	shl	bx,8			; combine the two digits in AX
	or	ax,bx				;
	pop	bx			; restore regs & exit
	ret
btohex	endp

ntohex	proc	near	; convert nibble to ascii hex
	and	ax,0Fh			; mask out all but low nibble
	cmp	ax,10			; branch if digit from 0 - 9
	jb	short #digit			;
	add	ax,'A' - 10		; digit from A-F
	ret					;
#digit:
	add	ax,'0'			; digit from 0-9
	ret
ntohex	endp
_codeseg	ends

	end	main
