;************************
PAGE    55,132          ;Format .LST listing file at 55 lines by 132 columns.
TITLE   TSRINT Version 0.4 Jan 20 1991 Robert Curtis Davis
SUBTTL  Introduction
;******************************************************************************
;
;       TSRINT.ASM      Version 0.4     Jan 20 91
;       A part of the TBONES software package.
;
;       Copyright (C) 1990, 1991 by Robert Curtis Davis,
;	All Rights Reserved.
;
;	DESCRIPTION:
;	Terminate-and-Stay-Resident (TSR) Program Template
;	for TSRs which are triggered by a specified Interrupt.
;	Such TSRs are said to "hook" the specified Interrupt.
;       This is a VERY basic program with few bells or whistles.
;
;	PURPOSE:
;	Provides a skeletal framework program for use in the design
;	of your own Hooked Interrupt TSRs.
;
;		
;		E-Mail address:
;                     Internet: sonny@trantor.harris-atd.com
;
;                      US Mail:
;                               430 Bahama Drive
;                               Indialantic, FL 32903
;
;	Use this as a starting point in the design of your TSR programs.
;
;*************************************************************************
;
; Special thanks to David Kirschbaum, whose Toad Hall Tweaks significantly
; improved T-BONES' code:
;
;v0.11, Toad Hall Tweak (25 Nov 90)
; - Idiosyncracies: I like my constant labels in all-upper-case (HOOK)
;   and my variable labels in lower-case (oldint).
; - Load ES directly with the PSP's environment segment.
;   No need to pass it through AX.
; - Load AX as a word rather than byte-by-byte.
; - Let the compiler do the arithmetic (figuring length of TSR code)
; - Use PROC NEAR and PROC FAR  (1) just for good programming practice,
;   and (2) to clarify the difference between that FAR interrupt server
;   and the other NEAR processes.
;**************************************************************************
SUBTTL  Code Segment (Resident)
PAGE
;**************************************************************************
;
CodeSeg         segment
                assume CS:CodeSeg,DS:CodeSeg
BeginDump       EQU     $       ;Roy Silvernail: Keep TASM 1.0 happy in
                                ;computing number of resident paragraphs.
;
		org	2CH		;ORG in PSP to pick up env. segment.
envseg		label	word		;environment segment		v0.11
;
		org	100h		;ORG for all COM programs.
;
Entry           PROC    NEAR            ;v0.11
		jmp	TSRinit		;Jump over resident portion and
					;initialize things and make code
					;from CodeSeg to TSRinit resident.
Entry           ENDP                    ;v0.11
;  
;	Old Interrupt Vector (Hooked Interrupt handler's original address) 
;	is stored here during TSR initialization:
oldint		dd	?	
;
;*************************************************************************
;
; Define which BIOS or DOS Interrupt this TSR is to hook into:
; For this TSR Template, choose the PrtScrn Interrupt 05h:
HOOK		equ	05h			;Hooked Interrupt number.
;
;	Any EQUates and storage areas needed by your New Interrupt handler
;	can be placed here also:
;
bellgate	db	0	;Gate closed (=1) when in Bell routine.
				;Gate open (=0) when not in Bell routine.
;*************************************************************************
;
		ASSUME	CS:CodeSeg,DS:NOTHING	;v0.11
NewInt          PROC    FAR                     ;v0.11
;
                pushf                   ;Simulate interrupt call to oldint:
                cli
                call    CS:oldint
;
                push    DS              ;Preserve DS.
;
                push    CS              ;Set up DS to CodeSeg   v0.11
                pop     DS
                ASSUME  DS:CodeSeg
;
;               The Bell "gate" shenanighans below keeps the MASSIVE number of
;               interrupts that can occur during the computer-scale
;               long, LONG time needed to ring the bell from overflowing the
;               system's internal stack (because of so many Int 10 calls) when
;               you hold down the Print Screen key for too long.
;
;               This "gate" technique is a good one to keep in
;               mind whenever you have a code region in an interrupt handler
;               that needs to be protected from re-entry:
;
                cmp     bellgate,0      ;Clear to enter routine?
                jne     BusyExit        ;If gate not open, Exit.
                mov     bellgate,1      ;Else, close gate & enter routine.
;
; Allow other interrupts:
                sti
;
                push    ax      ;Entry to TSR Routine:
                push    bx      ;Save all registers (DS is already pushed).
		push	cx
		push	dx
		push	si
		push	di
		push	bp
                push    es
;
;*************************************************************************
;	Your code for the New Hooked Interrupt Handler TSR routine  
;	GOES HERE:
;	( Here, a dummy routine has been placed which simply rings the
;	  terminal Bell whenever the TSR is triggered. )
;
;	Announce this dummy TSR's trigger by a Bell signal:
;
		mov	al,07h			;al = ASCII Bell. 
		mov	bh,0			;Video page.
		mov	cx,1			;No. of bytes to write.
		mov	ah,0Eh			;BIOS Int10,OEh=TTY Screen.
		Int	10h			;Write ASCII Bell to screen.
;
;	End of Hooked Interrupt handler routine.
;***************************************************************************
Exit:
                mov     bellgate,0
                pop     es                      ;Restore all registers
		pop	bp
		pop	di
		pop	si
		pop	dx
		pop	cx
		pop	bx
		pop	ax
;
BusyExit:
                pop     ds
                iret
;
NewInt	ENDP					;v0.11
;	-END OF TSR's RESIDENT CODE-
;	Only the code above will remain locked in memory after the
;		TSR initialization performed below.
;*************************************************************************
SUBTTL  TSR  Initialization Code (Nonresident). The "BOOSTER".
PAGE
;*************************************************************************
;	BEGINNING OF TSR's INITIALIZATION CODE:
;       The following code is protected in RAM *ONLY* during initialization   
;               of the TSR that occurs when the TSR name is first entered    
;               at the DOS command level. All the following code is abandonned 
;               unprotected in RAM after the Terminate-and-Stay-Resident       
;               call to Function 31h of DOS Interrupt 21h below. This          
;               is allowed to happen because the code's work is complete at 
;               that point. The code will be overwritten as the memory which   
;               it temporarily occupied is needed by DOS for other purposes.   
;		 I have seen this following section of code colorfully called
;		the TSR "Booster". And this is quite appropriate since the code
;		sits here, strapped to the very end of the code. It is of use
;		only during the initialization of the TSR, when it is used to
;		put the TSR into "orbit" (residency), and after which it is
;		"jettisoned" by the DOS TSR call, Int 21h, Fcn 31h.
;                                                                              
TSRinit         PROC    NEAR    ;v0.11
EndDump         EQU     $       ;Roy Silvernail: Keep TASM 1.0 happy in
                                ;computing number of resident paragraphs.
;
; TSRINT requires DOS Version 2 or later. Be sure DOS Version 1 not used:
;
;       Get DOS Version Number:
                mov     ah,30h                  ;Fcn 30h = Get DOS Version
                int     21h                     ;DOS Version = al.ah
;
;       If this is DOS v.1.x, this TSR cannot work, so go print message
;       and exit without installing:
                cmp     al,1         ;Is this DOS Version 1.x?
                ja      DOSverOK     ;If not, DOS version is OK.
;
DOSver1:
;If here, DOS Version 1.x is being run and TSR won't work, so bail out:
;
		mov	dx,OFFSET BailOutMsg	;TBONES needs DOS 2.x or later.
		mov	ah,09h			;Say we're sorry, but NO GO
		int	21h			;via DOS.
                pop     bx                      ;Clear stack.
		int	20h			;Terminate without installing
						;in only way DOS 1.x knows.
;
BailOutMsg:
                db      0Dh,0Ah
                db      'Sorry. TSRBONES needs DOS v.2+. You have v.1.x'
                db      0Dh,0Ah,'$'
;
DOSverOK:
;       If here, DOS version is 2.0 or later. TSR can work, so proceed.
;
;       To conserve memory, release from memory the copy of the DOS
;	Environment passed to this TSR (this, of course, assumes that
;	your Interrupt handler will not be written to reference this
;	de-allocated Environment. If you are going to need it, don't
;	de-allocate it.):
;
		mov	ES,envseg		;get environment segment v0.11
						;from Program Segment Prefix.
		mov	ah,49h			;DOS Fcn 49h = Release Memory
		int	21h			;Release it via DOS interrupt.
;
; In order to make the TSR's command name show under the "owner" column in 
;	the "MAPMEM" command of Kim Kokkonen's excellent TSR Mark/Release 
;       package, allocate a tiny 1-paragraph "Pseudo-Environment" here which
;       contains nothing but the TSR name. This only costs 16 bytes in
;       TSR resident code:
;
; Allocate the memory needed by the tiny 'Pseudo-Environment":
		mov	bx,1			;Allocate one parag. (16bytes)
		mov	ah,48h			;and return allocation
		int	21h			;segment in ax via DOS call.
;
                mov     ES,ax                   ;Pseudo-Env. Segment to ES.
		mov	si,OFFSET PseudoEnv	;si=source string OFFSET.
		mov	di,0			;di=destination string OFFSET.
		mov	cx,ENVLNGTH		;cx=Bytes in Pseudo-Env.string.
		cld				;Forward string move direction.
		rep	movsb	;Move Pseudo-Env. string @ DS:si to ES:di
;
; Set PSP's Environment segment pointer to point to tiny Pseudo-Environment.
		mov	envseg,ES	
;
;*****************************************************************************
; Hook the Interrupt:
;
;	Get Old Interrupt Vector:
		mov	ax,3500H + HOOK		;AH=DOS Fcn 35H, Get Int Vec
						;AL=Hooked Interrupt number
		int	21h			;Old Int Vect.in ES:BX via DOS.
;
;	Save Old Interrupt Vector:
		mov	Word Ptr oldint,bx	;Save Offset of Old Interrupt.
		mov	Word Ptr oldint+2,ES	;save old interrupt seg	v0.11
;
;	Install New Interrupt Vector to this TSR's "NewInt:" Label:
		mov	ax,2500H + HOOK		;AH=DOS Fcn 25H=Set Int vec
						; v0.11
						;AL=Hooked Interrupt number
		mov	dx,offset NewInt	;dx=Offset of New Int Handler.
		int	21h			;Set New Int via DOS.
;
; Announce the TSR's Installation:
		mov	dx,Offset InstallMsg	;dx points to message.
		mov	ah,09h			;DOS Fcn. 09h=Display String.
		int	21h			;Display String via DOS.
;
; Lock resident code in memory via Terminate-and-Stay-Resident (TSR) DOS call:
;
;v0.11	DX requires size of resident code (in 16-byte paragraphs)
;	This awkward construct is required to keep
;	DOS Function 31h happy.  Notice how we first compute
;	the length of the TSR code in bytes [i.e., end of
;       the TSR code (EndDump) minus start of the TSR code
;       (0, our BeginDump)], round it up to the next whole paragraph ( + 0Fh),
;	and then divide by 16 (SHR 4) to get the number of resident paragraphs:
;
;       Thanks to Roy Silvernail, who made TASM 1.0 happy by defining the
;               constants BeginDump and EndDump, and using them in the
;               following statement:
;
                mov     dx,(EndDump-BeginDump+0FH)/16   ;DX = No.Paragraphs.
;
		mov	ah,31h			;DOS FCN 31h = TSR Call.
		int	21h			;Go Resident via DOS TSR call.
						;Goodbye, Booster! 
;
PseudoEnv:      DB      ' ',0,0,1,0,'TSRINT',0
ENVLNGTH	EQU	$-PseudoEnv
;
InstallMsg:
		db	0Dh,0Ah
		db	'YOUR HOOKED INTERRUPT TSR IS NOW INSTALLED.'
		db	0Dh,0Ah
		db	'Hooked Interrupt => PrtScrn (Int 05h)'
		db	0Dh,0Ah,0Dh,0Ah
                db      'TSRINT Version 0.4'
		db	0Dh,0Ah
                db      'Copyright (C) 1990, 1991 by Robert Curtis Davis'
		db	0Dh,0Ah,'$'
;
TSRinit         ENDP    ;v0.11

CodeSeg		ends
		end	Entry
;
;*****************************************************************************

