;***************************
PAGE    55,132          ;Format .LST listing at 55 lines by 132 columns.
TITLE   TSRKEY Version 0.4 Jan 20 1991 Robert Curtis Davis
SUBTTL  Introduction
;******************************************************************************
;
;       TSRKEY.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:
;	ASM Program template for Terminate-and-Stay-Resident (TSR) programs
;		that are activated by a specified HotKey..
;
;	PURPOSE:
;       Provides a skeletal framework program useful as a starting point
;       in the design of your own HotKey TSRs.
;
;                   E-mail address:
;			  Internet: sonny@trantor.harris-atd.com
;
;                          US Mail:
;                                   430 Bahama Drive
;                                   Indialantic, FL 32903
;
;***************************************************************************
;
; Special thanks to David Kirschbaum, whose Toad Hall Tweaks significantly
; improved TBONES' code:
;
;v0.11	Toad Hall Tweak, 25 Nov 90
; - Idiosyncracy: I like my constant labels all-upper-case
;   and my variable labels lower-case.
; - Load AX with words, not byte-by-byte
; - Load ES directly with environ seg, no need to pass thru AX.
; - Save ES directly to variable, no need to pass thru AX.
; - Let compiler do basic arithmetic (figuring paras of memory to save).
; - Use processes just to be neat.  (That FAR NewInt09 is important!)
; - INS is a reserved word for TASM v1.0. Changed to INSRT.
;**************************************************************************
SUBTTL  Code Segment (Resident)
PAGE
;**************************************************************************
;
CodeSeg		segment
                assume  cs:CodeSeg,ds:CodeSeg
BeginDump       EQU     $       ;This, from Roy Silvernail, makes
                                ;TASM v.1.0 happy below.
;
		org	2CH		;0.11
envseg		label	word		;0.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
					;between Entry: and TSRinit: resident.
;  
; Old Keyboard Interrupt Vector (Int 09h handler address) is stored 
; here during TSR initialization:
oldint09        dd      ?
;
Entry           ENDP                    ;v0.11

; For this HotKey TSR Template, specify Keyboard Interrupt 09h as the Hook:
HOOK09            equ     09h   ;Hooked Interrupt number.
;
bellgate	db	0	;Gate closed (=1) when in Bell routine.
				;Gate open (=0) when not in Bell routine.
;
; EQUs defining Key Flag weights in the Key Flag Byte:
RSHIFT		equ	00000001B		;Right Shift Key Flag weight.
LSHIFT		equ	00000010B		;Left Shift  Key Flag weight.
CTRL		equ	00000100B		;Ctrl        Key Flag weight.
ALT		equ	00001000B		;Alt         Key Flag weight.
;SCROLL          equ     00010000B               ;Scroll Lock Key Flag weight.
;NUM             equ     00100000B               ;Num Lock    Key Flag weight.
;CAPS            equ     01000000B               ;Caps Lock   Key Flag weight.
INSRT		equ	10000000B		;Ins         Key Flag weight.
;*************************************************************************
;       Mask to mask out Num, Caps, and Scroll Lock bits from key flag byte.
LockKeyMask     EQU     10001111B
;
;       Your HotKey is specified here:
;       (This sample HotKey is set for Ctrl-Alt-K)
;
; Specify TSR's HotKey Shift Keys:
KEYFLAGBYTE	equ	CTRL+ALT		;HotKey Flags
;
; Specify TSR's HotKey Scan Code:
HOTKEY          equ     25h                     ;'K' key.
;
;*************************************************************************
SUBTTL User-supplied TSR Routine
PAGE
;*************************************************************************
ROUTINE         PROC    NEAR
;*************************************************************************
;	Code for your HotKey-triggered 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:
;
Enter:
                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.
;
Exit:
                ret                     ;Return from TSR routine.
;
ROUTINE         endp
;
;	End of your HotKeyed TSR routine.
;***************************************************************************
SUBTTL Hooked Interrupts
PAGE
;***************************************************************************
;
NewInt09	PROC	FAR		;v0.01
;
; The following three instructions often are said to "simulate an interrupt"
; that calls the PRIOR interrupt handler routine and then the prior interrupt
; handler's IRET instruction pops the flags and returns here to the point
; after the following CALL instruction.
;    The reason for "simulating the interrupt" here is to give prior (and
; presumably more time-critical) handlers a shot at processing this interrupt
; before we process with this TSR's code.
;
		pushf			;Push flags as a true interrupt would.
                cli                     ;Be sure interrupts are disabled.
		call	CS:oldint09	;Call FAR PTR address of old interrupt
;					;     handler routine.
;
;
                push    ax      ;Prepare to check for Hotkey.
                push    bx      ;Save all registers (DS is already pushed).
                push    cx
                push    dx
                push    si
                push    di
                push    bp
                push    ds
                push    es
;
                push    CS              ;Set up data segment
                pop     DS              ;register to point to code segment.
;
                ASSUME  DS:CodeSeg      ;v0.01
;
;       Determine if the current Keyboard Interrupt (Int09h) occurred
;       because this TSR's HotKey was pressed:
                in      al,60h          ;Get current Key Scan Code.
                cmp     al,HOTKEY       ;Is it HotKey's Scan Code?
                jne     Exit09          ;Exit if not.
                mov     ah,02h          ;Int16h,Fcn02h:GetKEYFLAGBYTE.
                Int     16h             ;Return Key Flag Byte in al.
                and     al,LockKeyMask  ;Mask out Num, Caps, Scroll Lock bits.
                cmp     al,KEYFLAGBYTE  ;Are the HotKey Flags active ?
                jne     Exit09          ;Exit if not.
;
;       At this point, Hotkey is known to have been pressed. First, purge
;       the DOS Keyboard type-ahead buffer of the hot key(s) so they won't
;       be passed on to DOS:
;
ClrKbdBuf:      ;Clear Keyboard buffer:
                mov     ah,01h          ;Get Keyboard buffer status
                int     16h             ;via BIOS Interrupt 16h.
                jz      BufClr          ;Jump if buffer empty.
                mov     ah,00h          ;Get key from buffer (to purge it)
                int     16h             ;via BIOS Interrupt 16h.
                jmp     ClrKbdBuf       ;Loop back to purge another key.
BufClr:
;
; We shall allow other interrupts to occur during our TSR ROUTINE.
; If we didn't allow other interrupts (through the STI instruction),
; we could lock out time-critical interrupts from access to the CPU during
; our TSR routine. However, by allowing interrupts during our routine, we 
; have an increased responsibility to make sure critical portions of our
; own code is not re-entered. (The "bellgate" stuff below is an example
; of a measure necessary to keep us from re-entering our own TSR's code).
; What we really want to do by allowing interrupts is to make the CPU avail-
; able to OTHER critical interrupt service routines WITHOUT swarming all over 
; ourselves through multiple detections of our own HotKey.
;               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      ;Is it clear to re-enter Hotkey code?
                jne     Exit09          ;Exit if not,
                mov     bellgate,1      ;Else, close gate and proceed.
;
                STI                     ;Allow other interrupts in our TSR.
;
                call    ROUTINE         ;All is clear!, so call routine.
;
                mov     CS:bellgate,0   ;Open gate allowing new HotKey detect.
;
Exit09:
                pop     es              ;Restore all registers
                pop     ds
                ASSUME  DS:NOTHING      ;v0.01
                pop     bp
		pop	di
		pop	si
		pop	dx
		pop	cx
		pop	bx
		pop	ax
;
;
;	Return from this TSR's Keyboard Interrupt 09h handler routine:
		iret
;
NewInt09	ENDP			;v0.01
;
;*************************************************************************
;
;	-END OF TSR's RESIDENT CODE-
;	Only the code above will remain locked in memory
;		after the 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     $       ;From Roy Silvernail. Keeps TASM v.1.0 happy.
;
; TSRKEY 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 RAM usage, release from memory the copy of the DOS 
;	Environment passed to this TSR (this assumes, of course, that
;	your Interrupt handler routine will not need to reference this
;	de-allocated Environment):
;
		mov	ES,envseg		;ES=PSP's environment seg v0.11
		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 costs only 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 Interrupt 09h vector:
;
;       Get Old Interrupt Vector:
                mov     ax,3500H+HOOK09         ;Get Hooked interrupt vec v0.11
		int	21h			;Int.Vector in ES:BX via DOS.
;
;	Save Old Interrupt Vector:
                mov     Word Ptr oldint09,bx      ;Save Offset of Old Interrupt.
                mov     word ptr oldint09+2,ES    ;save segment           v0.11
;
;       Install New Interrupt Vector to this TSR's "NewInt09:" Label:
                mov     ax,2500H+HOOK09         ;Set Hooked int vector v0.11
                mov     dx,offset NewInt09      ;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 (TSRinit) minus start of the TSR code
;	(0, our CodeSeg)], round it up to the next whole paragraph ( + 0Fh),
;	and then divide by 16 (SHR 4) to get the number of resident paragraphs:
;
;       Roy Silvernail discovered that the EndDump and BeginDump constants
;       kept his TASM 1.0 assembler happy on the following statement:
                mov     dx,(EndDump-BeginDump+0FH)/16   ;v0.11
;
		mov	ah,31h			;DOS FCN 31h=TSR Call.
		int	21h			;Go Resident via DOS TSR call.
;
PseudoEnv:      DB      ' ',0,0,1,0,'TSRKEY',0
ENVLNGTH	EQU	$-PseudoEnv
;
InstallMsg:
		db	0Dh,0Ah
		db	'YOUR HOT KEY TSR IS NOW INSTALLED.'
		db	0Dh,0Ah
                db      'HotKey => Ctrl-Alt-K'
		db	0Dh,0Ah
		db	0Dh,0Ah
                db      'TSRKEY 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
;
;******************************************************************************
