	.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 installing an INT 21h handler that just chains
; to the previous 386|DOS-Extender handler for most functions, but 
; processes function 09h (print string) by writing directly to the
; display with segment 001Ch (the video screen segment).
;

;
; 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
;
_data	segment	

	public	pmhnd_sel,pmhnd_off
pmhnd_off	dd	?	; Previous protected mode handler address
pmhnd_sel	dw	?		;

intro	db	'Demo program (take over INT 21h, for func 09h processing)'
	db	0Dh, 0Ah
	db	'     This string printed with standard INT 21h handler'
	db	0Dh, 0Ah, 0Dh, 0Ah, '$'

hndlr_msg db	'This string printed by our INT 21h handler','$'

chain_msg db	0Dh,0Ah,0Dh,0Ah,'This string printed with INT 21h func 02h'
	db	0Dh, 0Ah, '$'

_data	ends

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

	assume	cs:_codeseg,ds:_data
_codeseg	segment	

	public	main
main	proc	near

;
; Print the introduction string, and install the interrupt handler
;
	lea	edx, intro		; print intro string 
	mov	ah, 09h				; 
	int	21h				;
	mov	cl, 21h			; save original INT 21h handler
	mov	ax, 2502h			; 
	int	21h				;
	mov	pmhnd_sel, es			; 
	mov	pmhnd_off, ebx			;
	push	ds			; install our INT 21h handler
	lea	edx, i21_fcn9_hnd		; 
	mov	ax, cs				;
	mov	ds, ax				;
	mov	cl, 21h				; 
	mov	ax, 2504h			;
	int	21h				; 
	pop	ds				;

;
; Print a string with INT 21h function 09h to demonstrate our handler
; got installed.  
;
	lea	edx, hndlr_msg		; print highlighted string
	mov	ah, 09h				;
	int	21h				;

;
; Use another INT 21h function (02h) to demonstrate that chaining works
;
	lea	ebx, chain_msg		; get addr of string
#loop1:
	mov	dl, byte ptr [ebx]	; get next byte in string
	inc	ebx				;
	cmp	dl, '$'			; branch if end of string
	je	short #done_str			;
	mov	ah, 02h			; print this character
	int	21h				;
	jmp	short #loop1		; continue loop
#done_str:

;
; Restore original INT 21h handler and exit
;
	push	ds			; restore original handler
	mov	edx, pmhnd_off			;
	mov	ax, pmhnd_sel			; 
	mov	ds, ax				;
	mov	cl, 21h				;
	mov	ax, 2504h			; 
	int	21h				;
	pop	ds				;
	
	mov	ax, 4C00h		; exit to DOS
	int	21h				; 

main endp

;****************************************************************************
; I21_FCN9_HND - Replacement INT 21h Handler.  Chains to
;	original handler for any function except 09h (print string).
;
;	For function 09h, prints the string using segment 001Ch (the screen
;	segment), and also uses the high intensity attribute to show it was
;	printed by this handler and not by DOS.
;
; Note: This handler ASSUMES an 80-column text display video mode, page 0
;****************************************************************************
	public	i21_fcn9_hnd
i21_fcn9_hnd	proc	far

	pushfd				; save flags in case of chain
	cmp	ah, 09h			; branch if function 09h
	je	short #func_09			;

;
; Chain to original INT 21h handler, with all registers and flags preserved
;
	sub	esp, 8			; save space for IRETD frame
	push	ds			; save registers we use
	push	eax				;
	mov	ax, SS_DATA		; set DS to our data segment
	mov	ds, ax				; 
	mov	eax, pmhnd_off		; put original handler addr in IRETD
	mov	[esp+8], eax			; frame
	movzx	eax, pmhnd_sel			; 
	mov	[esp+12], eax			;
	pop	eax			; restore registers
	pop	ds				;
	iretd				; chain to prev handler

#func_09:
;
; Print the string at DS:EDX to the display, using segment 001Ch, and
; turning on the high intensity video attribute
;
	assume	ds:nothing
	sti				; re-enable interrupts
	add	esp,4			; pop unneeded flags off stack
	push	eax			; save registers we use
	push	ebx				;
	push	ecx				;
	push	edx				;
	push	es				;
	push	edx			; save string pointer
	mov	ah, 03h			; get cursor position in page 0
	mov	bh,0				;
	int	10h				;
	xor	ebx,ebx			; convert to offset in screen seg
	mov	bl,dh				; (assume 80 cols/row, page 0)
	imul	ebx,160				;
	and	edx,0FFh			;
	add	ebx,edx				;
	pop	edx			; restore string pointer
	mov	ax,SS_SCREEN		; set ES:EBX to screen position
	mov	es,ax				;
	mov	ah,0Fh			; set high intensity video attribute
#loop:
	mov	al,[edx]		; get next char
	cmp	al,'$'			; branch if no more in string
	je	short #exit			;
	mov	es:[ebx],ax		; store char in video buffer
	inc	edx			; bump ptrs & continue loop
	add	ebx,2				;
	jmp	#loop				;

#exit:
	pop	es			; restore regs
	pop	edx				;
	pop	ecx				;
	pop	ebx				;
	pop	eax				;
	iretd				; return to interrupted code

i21_fcn9_hnd	endp

_codeseg	ends

	end main
