From: addinall@stallion.oz.au (Mark Addinall)
Newsgroups: comp.os.msdos.programmer
Subject: Re: Need help on TSR's
Date: 16 Apr 92 04:32:58 GMT


In <1992Apr15.235445.9454@sequent.com> furballs@sequent.com writes:

>In article <1992Apr15.033850.16536@uwm.edu> markh@csd4.csd.uwm.edu (Mark William Hopkins) writes:
>>In article <oddt.4@sofus.dhhalden.no> oddt@sofus.dhhalden.no (ODD ROBERT TORJUSSEN) writes:
>>>We've come so far in the development, that the 'prototype' tsr is up running 
>>>and seems to work ok. We're started to add some 'features' to this tsr.
[deleted]
>>If it's being run on a DOS, the problem is likely the DOS.  System calls are
>>usually critical regions in DOS.  If two processes happen to be concurrently
>>accessing the same critical region, they could destroy the integrity of the DOS.
>>
>>That might be the problem.

sure is !!! :-)

>DOS is the problem. Whenever you run a renegade process under DOS,
>(a TSR is a good example), DOS does not know of it's existence and
[deleted]
>Having done both, I can recommend #1 as the greatest time saver and
>least stress inducer. These guys have already done the home work.
>Don't reinvent the wheel.

We always seem to ;-)
I am sure that the product you mentioned is first class, a netter
rarely reccomends anything :-)
Anyhoo, I see a lot of postings about how to manage TSR's from time to
time and I thought that this might help. I've only just finished writing
the thing myself, but I may be off the net for a while and unable to
post the final (debugged ;-) version.
Good Luck :-)

----------------------------CUT HERE----------------------------------------
COMMENT &
+----------------------------------------------------------------------------+
| FILE        :  TSR.ASM                                                |
| AUTHOR      :  M. ADDINALL                                                 |
| DATE        :  01/04/92                                                    |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Checks will be included in this TSR, i.e. no TSR recursion, possible       |
| DOS re-entrant calls etc. This TSR will also clean up after itself. This   |
| is the assembler part of the TSR, three functions, _tsr_init, _is_inst     |
| and _un_inst will be available to a C system. Described in functions.      |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 01/04/92   | MA   | Initial coding                                         |
|                                                                            |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &



;;---------------------------------------------------------------------------
;; set up code and data groups to use
;;---------------------------------------------------------------------------

IGROUP	group	_TEXT			;;combination of program segments
DGROUP	group	const,_bss,_data	;;combination of data segments

	assume	CS:IGROUP, DS:DGROUP, ES:DGROUP, SS:DGROUP

;;---------------------------------------------------------------------------
;; set up segment for read-only constants
;;---------------------------------------------------------------------------

CONST	segment	word	public	'CONST'
CONST	ends

;;--------------------------------------------------------------------------
;; set up area for uninitialised static variables
;;--------------------------------------------------------------------------

_BSS	segment	word	public	'BSS'
_BSS	ends


;;--------------------------------------------------------------------------
;; set up area for initialised and global static variables
;;--------------------------------------------------------------------------

_DATA	segment	word	public	'DATA'

	extrn	__psp	:	word	;; segment address of the C driver

_DATA	ends


;;-------------------------------------------------------------------------
;; constants
;;-------------------------------------------------------------------------

TRUE		EQU	1H
FALSE		EQU	0H
DOS_INT		EQU	21H
VID_REQ		EQU	10H
TERM_FUNC	EQU	31H
SOFT_INT	EQU	62H
HARD_INT	EQU	05H
DOS_SHOW	EQU	09H
CR		EQU	10H
LF		EQU	0DH
GET_DTA		EQU	2FH
SET_DTA		EQU	1AH
GET_INDOS	EQU	34H
GET_PSP		EQU	51H
SET_PSP		EQU	50H		
GET_INT_5	EQU	3505H
GET_INT_62	EQU   	3562H
GET_INT_13	EQU	3513H
SET_INT_5	EQU	2505H
SET_INT_62	EQU	2562H
SET_INT_13	EQU   	2513H
ENVIRONMENT	EQU   	02CH
REL_MEMORY 	EQU	49H
INSTALLED	EQU 	1H
NOT_INST	EQU	0H
NO_FUNC		EQU	0FFFFH

PARAGRAPH	EQU	0064
ID_LEN		EQU	0004		;; make it compatible with CICS
					;; hey hey...

;;-------------------------------------------------------------------------
;; macros
;;-------------------------------------------------------------------------

;;----------------------------
say		MACRO	string		;; output macro

	push	ax			;; AX onto stack
	push	bx			;; ditto for BX

	mov	bx,offset string	;; dope start of string
	mov	ah,DOS_SHOW		;; function request, display 
					;; string
	int	DOS_INT			;; do it
	pop	bx			;; restore BX
	pop	ax			;; ditto for AX

ENDM



;;-------------------
place_stack	MACRO

	push	bp			;; place BP on the stack
	mov	bp,sp			;; and dope stack pointer

ENDM


;;-------------------
store_registers	MACRO

	mov	cs:c_ss,ss		;; get stack segment
	mov	cs:c_sp,sp		;; stack pointer
	mov	cs:c_es,es		;; extra segment
	mov	cs:c_ds,ds		;; and data segment, all safe now :-)

ENDM


;;-------------------
fetch_id	MACRO
local	j


	mov	si,frame.idptr0		;; DS:SI points to string id
	push	cs			;; CS onto the stack
	pop	es			;; and restore as ES
	mov	di,offset id_buff	;; ES:DI points to storage
	mov	cx,ID_LEN		;; prime counter
j:	lodsb				;; get a character
	stosb				;; and push into storage
	or	al,al			;; end of string ?
	loopne	j			;; while ( ch != 0 && cx != 0 );

ENDM



;;---------------------------
fetch_parameters	MACRO

	mov	ax,frame.fktptr0	;; get the pointer to the C TSR func
	mov	cs:tsr_adr0,ax		;; and save it
	mov	ax,frame.fktptr1	;; get pointer to the second function
	mov	cs:tsr_adr1,ax		;; and save it

ENDM



;;-------------------
fetch_dta	MACRO

	mov	ah,GET_DTA		;; function request, get DTA address
	int	DOS_INT			;; do it dos
	mov	cs:c_dta_ofs,bx		;; store offset of DTA pointer
	mov	cs:c_dta_seg,es		;; and its segment in memory

ENDM




;;-------------------
fetch_indos	MACRO

	mov	ah,GET_INDOS		;; function request, get INDOS flag
	int	DOS_INT			;; do it dos
	mov	cs:idos_ofs,bx		;; store offset of INDOS flag pointer
	mov	cs:idos_seg,es		;; and its segment in memory

ENDM


;;---------------------------
fetch_interrupts	MACRO

	mov	ax,GET_INT_5		;; get interrupt vector 05H
	int	DOS_INT			;; do it DOS
	mov	cs:int5_ofs,bx		;; save the interrupt offset
	mov	cs:int5_seg,es		;; and save the segment

	mov	ax,GET_INT_13		;; get interrupt vector 13H
	int	DOS_INT			;; DISK BIOS 
	mov	cs:int13_ofs,bx		;; save the interrupt offset
	mov	cs:int13_seg,es		;; and save the segment

	mov	ax,GET_INT_62		;; and now for the SINT 62
	int	DOS_INT			;; hello DOS
	mov	cs:int62_ofs,bx		;; store offset
	mov	cs:int62_seg,es		;; and the segment

ENDM



;;---------------------------
install_interrupts	MACRO

	push	ds			;; save data segment
	mov	ax,cs			;; CS to AX
	mov	ds,ax			;; and onto DS
	mov	ax,SET_INT_5		;; dope function request
	mov	dx,offset int5		;; DS:DX has the address of handler
	int	DOS_INT			;; do it DOS
	mov	ax,SET_INT_13		;; dope function request
	mov	dx,offset int13		;; DS:DX has the address of handler
	int	DOS_INT			;; do it DOS
	mov	ax,SET_INT_62		;; dope function request
	mov	dx,offset int62		;; DS:DX has the address of handler
	int	DOS_INT			;; hello DOS
	pop	ds			;; restore data segment

ENDM


;;-----------------------
calculate_memory	MACRO

	xor	ax,ax			;; determine current break address
	push	ax			;; as argument for SBRK on the stack
	call	_sbrk			;; call C function
	pop	ax			;; get argument off stack
	mov	cs:break_addr,ax	;; store for later
	add	ax,frame.heap0		;; add required heap memory
	mov	dx,ax			;; get the break address into DX
	add	dx,15			;; avoid integer division loss
	mov	cl,4			;; shift 4 times to the right then..
	shr	dx,cl			;; divide by 16
	mov	ax,ds			;; dope data segment
	mov	bx,__psp		;; and the segment of the PSP
	mov	cs:c_psp,bx		;; and store for later use
	sub	ax,bx			;; subtract DS from PSP
	add	dx,ax			;; add to the number of paragraphs

ENDM



;;---------------------------
terminate_resident	MACRO


	mov	al,0			;; clobber AL, who cares really
	mov	ah,TERM_FUNC		;; set up function request
	int	DOS_INT			;; and go to sleep

ENDM


;;---------------------------
stack_context_switch	MACRO

	mov	cs:ce_seg,es		;; set up for context switch to C
	mov	cs:u_prg_ss,ss		;; save stack segment
	mov	cs:u_prg_sp,sp		;; and the stack pointer

	;; critical ...

	cli				;; disable interrupts
	mov	ss,es:c_ss		;; activate the TSR
	mov	sp,es:c_sp		;; program
	sti				;; and turn interrupts back on

	;; end critical ...

ENDM



;;---------------------------
dta_context_switch	MACRO

	push	es			;; ES onto stack
	mov	ah, GET_DTA		;; function request, get DTA
	int	DOS_INT			;; do it DOS
	mov	cs:u_prg_ss,ss		;; save the current stack
	mov	cs:u_prg_sp,sp		;; segment and pointer
	pop	es			;; ES off stack

ENDM



;;---------------------------
psp_context_switch	MACRO

	mov	ah,GET_PSP		;; function request, get PSP
	mov	bx,es:c_psp		;; and dope the seg addr of the PSP
	int	DOS_INT			;; do it DOS

ENDM




;;---------------------------
new_dta_context_switch	MACRO

	push	ds			;; DS onto the stack
	push	es			;; ES onto the stack
	mov	ah,SET_DTA		;; function request, set DTA
	mov	dx,es:c_dta_ofs		;; get offset addres of new DTA	
	mov	ds,es:c_dta_seg		;; and the segment address
	int	DOS_INT			;; do it DOS

ENDM



;;---------------------------
execute_end_function	MACRO

	mov	ds,es:c_ds		;; set segment register
	mov	es,es:c_es		;; registers for the C program
	call	cs:[ce_ptr]		;; and call the function.
					;; This pointer will probably
					;; reset the board. If you guys
					;; are going to do everything in
					;; C, you'll be OK. If you write
					;; external or inline assembler,
					;; make sure you don't blow all these
					;; carefully constructed segment
					;; stores and critical context changes
ENDM



;;---------------------------
restore_dta		MACRO

	mov	ah,SET_DTA		;; function request, set DTA
	mov	dx,cs:u_dta_ofs		;; dope the offset and segment
	mov	ds,cs:u_dta_seg		;; address of the interrupted program
	int	DOS_INT			;; do it DOS
	pop	es			;; segment addr of TSR from stack
	pop	ds			;; restore TSR DS from stack


ENDM




;;---------------------------
restore_psp		MACRO

	mov	ah,SET_PSP		;; function request, set PSP
	mov	bx,__psp		;; load seg address of the PSP
	int	DOS_INT			;; do it DOS

ENDM





;;---------------------------
restore_stack		MACRO


;; critical ...

	cli				;; interrupts off
	mov	ss,cs:u_prg_ss		;; restore stack segment
	mov	sp,cs:u_prg_sp		;; and stack pointer
	sti				;; interrupts on, phew :-)


ENDM





;;---------------------------
restore_interrupts	MACRO

;; critical ...

	cli				;; interrupts off

	mov	ds,es:int5_seg		;; get the interrupt segment
	mov	dx,es:int5_ofs		;; and offset
	mov	ax,SET_INT_5		;; function request, set interrupt vect
	int	DOS_INT			;; do it DOS
	mov	ds,es:int62_seg		;; get the interrupt segment
	mov	dx,es:int62_ofs		;; and offset
	mov	ax,SET_INT_62		;; function request, set interrupt vect
	int	DOS_INT			;; do it DOS
	mov	ds,es:int13_seg		;; get the interrupt segment
	mov	dx,es:int13_ofs		;; and offset
	mov	ax,SET_INT_13		;; function request, set interrupt vect
	int	DOS_INT			;; do it DOS

	sti				;; interrupts on, pant ;-|

ENDM





;;---------------------------
release_memory 		MACRO

	mov	es,es:c_psp		;; segment address of the TSR PSP
	mov	cx,es			;; and save it in the counter
	mov	es,es:[ENVIRONMENT]	;; segment address of environment
	mov	ah,REL_MEMORY		;; function request, release memory
	int	DOS_INT			;; and free it up

	mov	es,cx			;; restore es
	mov	ah,REL_MEMORY		;; function request, release memory
	int	DOS_INT			;; do it DOS

ENDM


;;---------------------------
set_recurse_flag MACRO bool

	mov	cs:recurse,bool		;; toggle recursion boolean

ENDM



;;---------------------------
change_context		 MACRO

	mov	cs:u_prg_ss,ss		;; save current stack segment
	mov	cs:u_prg_sp,sp		;; and the stack pointer
	mov	ss,cs:c_ss		;; activate the C segment
	mov	sp,cs:c_sp		;; and the C stack pointer

ENDM



;;---------------------------
push_the_cpu 		MACRO


	push	ax			;; AX onto stack
	push	bx			;; BX onto stack
	push	cx			;; CX onto stack
	push	dx			;; DX onto stack
	push	bp			;; BP onto stack
	push	si			;; SI onto stack
	push	di			;; DI onto stack
	push	ds			;; DS onto stack
	push	es			;; ES onto stack

ENDM



;;---------------------------
steal_some_stack 	MACRO
local	i

	mov	cx,PARAGRAPH		;; dope loop counter
	mov	ds,cs:u_prg_ss		;; set DS:DI to the end
	mov	si,cs:u_prg_sp		;; of the DOS stack
i:	push	word	ptr	[si]	;; save a word from the DOS stack
	inc	si			;; onto the C stack and set SI to
	inc	si			;; to the next stack word
	loop	i			;; push a paragraph

ENDM



;;---------------------------
get_start_psp 		MACRO

	mov	ah,GET_PSP		;; function request, get address of PSP
	int	DOS_INT			;; get DOS to do it
	mov	cs:u_psp,bx		;; save address of psp

ENDM



;;---------------------------
get_start_dta 		MACRO

	mov	ah,GET_DTA		;; function request, get address of DTA
	int	DOS_INT			;; get DOS to do it
	mov	cs:u_dta_ofs,bx		;; store the address of the DTA of
	mov	cs:u_dta_seg,es		;; of the interrupted program

ENDM



;;---------------------------
set_start_psp 		MACRO

	mov	ah,SET_PSP		;; function request, set PSP
	mov	bx,cs:c_psp		;; dope the address of the C PSP
	int	DOS_INT			;; get DOS to do it

ENDM



;;---------------------------
set_start_dta 		MACRO

	mov	ah,SET_DTA		;; function request, set DTA
	mov	dx,cs:c_dta_ofs		;; get the offset address of new DTA
	mov	ds,cs:c_dta_seg		;; and the segment address
	int	DOS_INT			;; and do it DOS :-)

ENDM



;;---------------------------
set_segments 		MACRO

	mov	ds,cs:c_ds		;; set segment register for
	mov	es,cs:c_es		;; the C program

ENDM



;;---------------------------
reset_dta 		MACRO

	mov	ah,SET_DTA		;; function request, set DTA
	mov	dx,cs:u_dta_ofs		;; load the offset and segment
	mov	ds,cs:u_dta_seg		;; of the interrupted program
	int	DOS_INT			;; get DOS to do it

ENDM



;;---------------------------
reset_psp 		MACRO

	mov	ah,SET_PSP		;; function request, set PSP
	mov	bx,cs:u_psp		;; reset segment address of PSP
	int	DOS_INT			;; do it DOS...

ENDM



;;---------------------------
restore_dos_stack 	MACRO
local k

	mov	cx,PARAGRAPH		;; dope the counter
	mov	ds,cs:u_prg_ss		;; load DS:SI with the address of
	mov	si,cs:u_prg_sp		;; the DOS stack
	add	si, (PARAGRAPH * 2)	;; SI to the start of DOS stack
k:	dec	si			;; SI to point to the previous
	dec	si			;; stack word
	pop	word	ptr	[si]	;; get the word back on DOS stack
	loop	k			;; until finished

ENDM



;;---------------------------
pull_the_cpu 		MACRO


	pop	es			;; ES off the stack
	pop	ds			;; DS off the stack
	pop	di			;; DI off the stack
	pop	si			;; SI off the stack
	pop	bp			;; BP off the stack
	pop	dx			;; DX off the stack
	pop	cx			;; CX off the stack
	pop	bx			;; BX off the stack
	pop	ax			;; AX off the stack


ENDM



;;---------------------------
reset_stack_pointer 	MACRO

	mov	ss,cs:u_prg_ss		;; reset stack segment to program
	mov	sp,cs:u_prg_sp		;; reset stack pointer to program

ENDM





;;---------------------------
is_dos_alive	MACRO	bool

local	ok

	mov	bool,FALSE		;; FALSE is good for this routine
	push	ds			;; save DS onto the stack
	push	bx			;; save BX onto the stack
	lds	bx,cs:idos_ptr		;; DS:BX now points to the indos flag
	cmp	byte ptr [bx],FALSE	;; is DOS active
	je	ok			;; no worries
	mov	bool,TRUE		;; sad but true, DOS is alive
ok:	pop	bx			;; BX back from stack
	pop	ds			;; DS back from stack

ENDM



;;------------------------------------------------------------------------
;; ok, let's start the code segment
;;------------------------------------------------------------------------

_TEXT	segment	byte	public	'CODE'	;; the program segment


;;------------------------------------------------------------------------
;; references to external C functions
;;------------------------------------------------------------------------

extrn	_sbrk	:	near 


;;-----------------------------------------------------------------------
;; declare our public function accessable to C
;;-----------------------------------------------------------------------

public	_tsr_init
public	_is_installed
public	_uninstall



;;------------------------------------------------------------------------
;; declare and initialise some variables
;;------------------------------------------------------------------------


id_buff		db	(ID_LEN + 1)	dup(0)	;; ID string buffer for TSR
ce_ptr		EQU	this	dword		;; points to call_end
ce_ofs		dw	offset	call_end	;; offset of routine call_end
ce_seg		dw	?			;; space for call_end segment

c_ss		dw	0		;; C stack segment
c_sp		dw	0		;; C stack pointer
c_ds		dw	0		;; C data segment
c_es		dw	0		;; C extra segment

c_dta_ofs	dw	0		;; DTA address of the program
c_dta_seg	dw	0		;; and DTA segment

c_psp		dw	0		;; psp of the application program
break_addr	dw	0		;; break address of the heap
tsr_adr0	dw	0		;; address of the C TSR function
tsr_adr1	dw	0		;; and the second function

recurse		db	FALSE		;; no recursive TSR's please
in_bios		db	FALSE		;; wise not to interrupt the BIOS
in_dos		db	FALSE		;; or DOS for that matter
success		db	TRUE		;; eternal bloody optimist
in_5		db	FALSE		;; interrupt type flag

idos_ptr	EQU	this	dword	;; pointer to the DOS indos flag
idos_ofs	dw	0		;; pointer offset address
idos_seg	dw	0		;; pointer segment address

int5_ptr	EQU	this	dword	;; activation hardware interrupt
int5_ofs	dw	0		;; offset of the pointer
int5_seg	dw	0		;; pointer segment

int62_ptr	EQU	this	dword	;; activation software interrupt
int62_ofs	dw	0		;; offset of the pointer
int62_seg	dw	0		;; pointer segment address

int13_ptr	EQU	this	dword	;; get the BIOS disk interrupt
int13_ofs	dw	0		;; offset of the pointer
int13_seg	dw	0		;; pointer segment address

u_dta_ofs	dw	0		;; DTA address of interrupted
u_dta_seg	dw	0		;; program
u_psp		dw	0		;; segment address of psp of same
u_prg_ss	dw	0		;; stack segment of same
u_prg_sp	dw	0		;; stack pointer of same


hello		db	CR,LF
		db	"IO TSR Version 1.nothing         ",CR,LF
		db	"--------------Loading------------",CR,LF
		db	"Using IRQ5  && SINT62            ",CR,LF,"$"

COMMENT &
+----------------------------------------------------------------------------+
| FUNCTION    :  _tsr_init                                                   |
| AUTHOR      :  M. ADDINALL                                                 |
| DATE        :  01/04/92                                                    |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Abstract                                                                   |
+----------------------------------------------------------------------------+
| void tsr_init(  void (fkt *) (void),                                       |
|                 void (fk2 *) (void),
|                 unsigned heap_byte,                                        |
|                 char * id_string    ) ;                                    |
|                                                                            |
| Called from the C driver, this function initialises the TSR. The function  |
| is_inst will already have been called to stop the event of a recursive     |
| TSR load (if the programmers awake, that is).                              |
|                                                                            |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 04/03/92   | MA   | Initial coding                                         |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &



_tsr_init	proc	near

sframe0		struc		;; structure for accessing stack
bp0		dw	?	;; store for BP
ret_adr0	dw	?	;; return address
fktptr0		dw	?	;; pointer to C TSR function
fktptr1		dw	?	;; another C function
heap0		dw	?	;; heap bytes required
idptr0		dw	?	;; pointer to the ID string
sframe0		ends		;; end of structure

frame	EQU	[ bp - bp0 ]	;; calculate this code frame

	say	hello		;; hello world
	place_stack		;; position stack pointer
	store_registers		;; fetch register data into memory
	fetch_id		;; load the identification string
	fetch_parameters	;; get the function arguments
	fetch_dta		;; get the DTA address of the C program
	fetch_indos		;; get the address of the INDOS flag
	fetch_interrupts	;; get the addresses of the interrupts
	install_interrupts	;; put our interrupt handlers in
	calculate_memory	;; figure out how much memory to use
	terminate_resident	;; go sleepy bo bo's. Passive TSR loaded :-)

_tsr_init	endp



COMMENT &
+----------------------------------------------------------------------------+
| FUNCTION    :  _is_installed                                               |
| AUTHOR      :  M. ADDINALL                                                 |
| DATE        :  01/04/92                                                    |
| SYSTEM      :  MS-DOS Stallion IO Drivers                                  |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Abstract                                                                   |
+----------------------------------------------------------------------------+
| BOOL _is_installed(  unsigned bp,                                          |
|                      void * return_address,                                |
|                      char * id_string    );                                |
|                                                                            |
| Called by a C program to see if this thing is already running,             |
|                                                                            |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 01/04/92   | MA   | Initial coding                                         |
|                                                                            |
|                                                                            |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &



_is_installed	proc	near

	sframe1	struc			;; structure to get stack
	bp1		dw	?	;; hold BP
	ret_adr1	dw	?	;; return address
	idptr1		dw	?	;; pointer to ID string
	sframe1		ends		;; close the structure

	frame	EQU	[bp - bp1]	;; establish code frame

	place_stack			;; position stack pointer
	push	di			;; DI onto stack
	push	si			;; SI onto stack
	push	es			;; ES onto stack

	mov	ax,GET_INT_5		;; dope function request
	int	DOS_INT			;; do it DOS
	mov	di,offset id_buff 	;; ES:DI points to ID_BUFF
	mov	si,frame.idptr1		;; DS:SI points to the ID_STRING passed
	mov	cx,NOT_INST	    	;; dope return value

search:					;; string compare label

	lodsb				;; load a character from string
	cmp	al,es:[di]		;; compare strings
	jne	ninst			;; failed, not installed
	inc	di			;; increment string pointer
	or	al,al			;; end of string ?
	jne	search			;; no, keep comparing
	mov	cl,INSTALLED		;; we're installed already :-)

ninst:                   	        ;; exit label target

	mov	ax,cx			;; prime return code
	pop	es			;; restore ES
	pop	si			;; restore SI
	pop	di			;; restore DI
	pop	bp			;; restore BP
	ret				;; and bugger off

_is_installed	endp






COMMENT &
+----------------------------------------------------------------------------+
| FUNCTION    :  _call_end                                                   |
| AUTHOR      :  M. ADDINALL                                                 |
| DATE        :  01/04/92                                                    |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Abstract                                                                   |
+----------------------------------------------------------------------------+
| void _call_end( void ) ;                                                   |
|                                                                            |
|                                                                            |
|                                                                            |
| Calls the end function on reinstallation of the TSR. Internal use          |
| only, not intended to be public.                                           |
|                                                                            |
|                                                                            |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 01/04/92   | MA   | Initial coding                                         |
|                                                                            |
|                                                                            |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &



call_end	proc	far

	call	di			;; call the end function
	ret				;; back to the caller

call_end	endp






COMMENT &
+----------------------------------------------------------------------------+
| FUNCTION    :  _uninstall                                                  |
| AUTHOR      :  M. ADDINALL                                                 |
| DATE        :  01/04/92                                                    |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Abstract                                                                   |
+----------------------------------------------------------------------------+
| void _uninstall( void ( endfkt * ) ( void ) ) ;                            |
|                                                                            |
|                                                                            |
|                                                                            |
| Calls the end function on reinstallation of the TSR.                       |
| If the value 0xffff is passed to this function, no end_function will       |
| be called. This function releases allocated memory.                        |
|                                                                            |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 01/04/92   | MA   | Initial coding                                         |
|                                                                            |
|                                                                            |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &



_uninstall	proc	far	

	sframe2	struc
	bp2		dw	?	;; storage for BP
	ret_adr2	dw	?	;; return address
	ftnptr2		dw	?	;; pointer to the end function
	sframe2	ends

	frame	EQU	[ bp - bp2 ]	;; calculate code frame
	assume	es:IGROUP		;; allow access to CS through ES

	place_stack			;; update stack pointer
	push	di			;; DI onto stack
	push	si			;; SI onto stack
	push	ds			;; DS onto stack
	push	es			;; ES onto stack

	mov	ax,GET_INT_5		;; get current interrupt vector
	int	DOS_INT			;; for int 5

	mov	di,frame.ftnptr2	;; get address of end function
	cmp	di,NO_FUNC		;; -1 parameter ?
	jne	call_function		;; work to do, jump a few bytes
	jmp	no_function		;; brain dead assembler
					;; dontcha hate that :-)
call_function:				;; dopey target
	stack_context_switch		;; activate TSR stack
	dta_context_switch		;; set up current DTA
	psp_context_switch		;; set up current PSP
	new_dta_context_switch		;; now the new DTA
	execute_end_function		;; carry out the code
	restore_dta			;; fetch DTA back
	restore_psp			;; fetch PSP back
	restore_stack			;; fetch stack back

no_function:

	restore_interrupts		;; fetch interrupts back
	release_memory			;; release allocated memory

	pop	es			;; ES off stack
	pop	ds			;; DS off stack
	pop	si			;; SI off stack
	pop	di			;; DI off stack
	pop	bp			;; BP off stack

	ret				;; and bugger off
	assume	es:DGROUP		;; combine ES with DGROUP again
 
_uninstall	endp







COMMENT &
+----------------------------------------------------------------------------+
| FUNCTION    :  _start_tsr                                                  |
| DATE        :  06/04/92                                                    |
| SYSTEM      :  MS-DOS Stallion IO Drivers                                  |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Abstract                                                                   |
+----------------------------------------------------------------------------+
|void _start_tsr(void);                                                      |
|                                                                            |
|This procedure will be called on the first instance of an interrupt         |
|recieved by the resident TSR code lurking around in memory. For speed       |
|considerations on start ( i.e. the ramp time this procedure needs on        |
|start as opposed to the initial incoming interrupt rate on the bus ),       |
|we may make this one public to be called from the application, although,    |
|depending on when this is loaded into memory, a whole bunch of INT13H's     |
|might be generated before the call to start communications. Who knows.      |
|                                                                            |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 06/04/92   | MA   | Initial coding                                         |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &



_start_tsr	proc	near

	cli				;; no interrupts at this point
	set_recurse_flag	TRUE	;; no recursive TSR's please :-)
	change_context			;; activate the C program stack
	push_the_cpu			;; push all registers on stack
	steal_some_stack		;; get 64 words of the DOS stack
	get_start_psp			;; fetch initial PSP
	get_start_dta			;; fetch initial DTA
	set_start_psp			;; install start psp
	set_start_dta			;; install start dta
	set_segments			;; set segment register for C
	cmp	in_5,FALSE		;; is this an interrupt 05
	jne	do_a_62			;; nope, do some other stuff
	sti				;; turn interrupts back on
	call	cs:tsr_adr0		;; call the TSR routine for int05
	cli				;; interrupts back off for a moment
	jmp	all_done		;; get out of here
do_a_62:				;; target for interrupt 62 code
	sti				;; interrupts back on
	call	cs:tsr_adr1		;; call TSR routines for interrupt 62
	cli				;; interrupts off
all_done:				;; exit target for the interrupt 5 code
	reset_dta			;; put the DTA back to normal
	reset_psp			;; put the PSP back to normal
	restore_dos_stack		;; return stack we stole :-)
	pull_the_cpu			;; pop all registers offa stack
	reset_stack_pointer		;; tidy up ss:sp
	set_recurse_flag	FALSE	;; reset TSR recursion flag
	sti				;; enable interrupts now
	ret				;; return to caller

_start_tsr	endp








COMMENT &
+----------------------------------------------------------------------------+
| FUNCTION    :  int13                                                       |
| AUTHOR      :  M. ADDINALL                                                 |
| DATE        :  06/04/92                                                    |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Abstract                                                                   |
+----------------------------------------------------------------------------+
| This is a stub that replaces the entry point to the BIOS int13. Of course  |
| int 13 is DASD BIOS activity. As this interrupt could be active for a      |
| number of cycles, we want the rest of the system to know that the BIOS     |
| is servicing a disk request. No more dissapearing database, heh guys ;-)   |
|                                                                            |
|                                                                            |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 06/04/92   | MA   | Initial coding                                         |
|                                                                            |
|                                                                            |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &

int13	proc	far

	mov	cs:in_bios,TRUE		;; toggle BIOS active flag 
	pushf				;; call the one and only true
	call	cs:int13_ptr		;; BIOS service
	mov	cs:in_bios,FALSE	;; all over, safe to go on
	ret	2			;; clean up stack from flag push

int13	endp









COMMENT &
+----------------------------------------------------------------------------+
| FUNCTION    :  int5                                                        |
| AUTHOR      :  M. ADDINALL                                                 |
| DATE        :  06/04/92                                                    |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Abstract                                                                   |
+----------------------------------------------------------------------------+
|This interrupt routine will service the interrupts generated by the         |
|xxxxxxxx X.25 communication device. The generation of this interrupt is     |
|the action of STREAMS based software running on the X.25 board. It is       |
|the responsibility of this procedure to intercept the interrupt and to      |
|call the appropriate X.25 PDK function running on the HOST machine.         |
|                                                                            |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 06/04/92   | MA   | Initial coding                                         |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &

int5	proc	far


					;; OK Antonio && Kieth,
					;; what do you want to happen
					;; in here. Please read source,
					;; questions gratefully received,
					;; white-board session now on
					;; where to go 
					;; cheers :-) Mark...
	mov	in_5,TRUE		;; we're in an int 5 routine
	mov	success,TRUE		;; assume we'll suceed :-)
	cmp	recurse,FALSE		;; are we recursing
	jne 	fail			;; re-entrancy is naughty
	cmp	in_bios,FALSE		;; are we in a disk write ?
	jne	fail			;; don't interrupt the BIOS
	is_dos_alive in_dos		;; is there a DOS function happening ?
	cmp	in_dos,FALSE		;; well ?
	jne	fail			;; don't interrupt active processes

	call	_start_tsr		;; ok, do the stuff
	jmp	fin_05			;; get out

fail:	mov	success,FALSE		;; damn, something went wrong

fin_05:	mov	ah,0			;; blat AH, who cares
	mov	al,success		;; did we do anything useful ?
	push	ax			;; tell the world we could(n't)
					;; process this interrupt :-(
	mov	in_5,FALSE		;; no longer in an interrupt 5
	iret				;; and back to caller

int5	endp











COMMENT &
+----------------------------------------------------------------------------+
| FUNCTION    :  int62                                                       |
| AUTHOR      :  M. ADDINALL                                                 |
| DATE        :  06/04/92                                                    |
| VERSION     :  1.0                                                         |
+----------------------------------------------------------------------------+
| Abstract                                                                   |
+----------------------------------------------------------------------------+
|This function will respond to a software interrupt. This interrupt would    |
|have been generated by an application call to the X.25 PDK through the      |
|X.25 PDK API/NPI. The responsibility of this function is to respond to      |
|the interrupt and to respond to the appropriate X.25 PDK function that is   |
|running on the HOST machine. It will check to see if we are re-entrant,     |
|and the activity level of the DASD BIOS before using system resource.       |
|                                                                            |
+------------+------+--------------------------------------------------------+
| CHANGE     | WHO  | REASON FOR CHANGE                                      |
+------------+------+--------------------------------------------------------+
| 06/04/92   | MA   | Initial coding                                         |
|                                                                            |
+----------------------------------------------------------------------------+
END-COMMENT &

int62	proc	far


					;; OK Antonio && Kieth,
					;; what do you want to happen
					;; in here. Please read source,
					;; questions gratefully received,
					;; white-board session now on
					;; where to go 
					;; cheers :-) Mark...
	mov	success,TRUE		;; assume we'll suceed :-)
	cmp	recurse,FALSE		;; are we recursing
	jne 	fail			;; re-entrancy is naughty
	cmp	in_bios,FALSE		;; are we in a disk write ?
	jne	fail			;; don't interrupt the BIOS
	is_dos_alive in_dos		;; is there a DOS function happening ?
	cmp	in_dos,FALSE		;; well ?
	jne	fail62			;; don't interrupt active processes

	call	_start_tsr		;; ok, do the stuff
	jmp	fin_62			;; get out

fail62:	mov	success,FALSE		;; damn, something went wrong

fin_62:	mov	ah,0			;; blat AH, ho hum...
	mov	al,success		;; send the world a message
	push	ax			;; whether we could or couldn't
					;; process this interrupt :-(
	iret				;; and back to caller

int62	endp





;;--------------------------------------------------------------------------
;; ok, we'll end the code segment and give the linker an entry point
;;--------------------------------------------------------------------------

_TEXT	ends			;; end of the code segment
	end			;; end of the program


;;--------------------------------------------------------------------------
;; end of file <tsr.asm>
;;--------------------------------------------------------------------------

---------------------------CUT HERE-----------------------------------------


I hope the concepts are clear. I worked from a tsr I developed some time
ago for another company. A lot of the mechanics a due to an exellent book
'PC System Programming', Abacus press, by Micheal Tischer. I would
recommend that anyone wanting to do serious system work on a PC get this
book. It describes TSR's, device drivers, the BIOS interface etc., etc.