;
;----------------------------------------
; DOSMON - DOS Activity Monitor
;----------------------------------------
;
; This program will terminate and stay resident.  It intercepts the
; DOS service interrupt and displays status information on the
; screen during DOS service execution periods.
;
; S.H.Smith, 15-may-86
; converted to real assembly, sped up, and made smaller by 
; jack velte 1301 441-2008  velte@mimsy.umd.edu
;

RESLEN    equ  init - dosmon + 256

code	segment	byte public 'text'
	assume cs:code, ds:code
	org	100h

;----------------------------------------
; startup entry point
;----------------------------------------
dosmon	proc	near
	jmp short init	;install
dosmon	endp

; segment address for display memory
; filled in by startup code
display_seg	dw	?
twiddle_idx	dw	?	; twiddle display index
twiddle_char	db 'Ä\³/'	; twiddle display characters

;----------------------------------------
; new DOS service interrupt
;----------------------------------------
;
new21 proc far
	push ds
	push es
	push bx		; save entry registers
	push ax

	mov ax, cs:display_seg		; get display segment into es
	mov es, ax
;
;----------------------------------------
; prepare display with blanks
;
	mov ax, 0f20h		;space with attribute of 15
	mov es:[94h], ax
	mov es:[96h], ax	;set attribute of the function code locations
	mov es:[98h], ax
;
;----------------------------------------
; display the rotating twiddle character
;
	mov bx, cs:twiddle_idx	;get twiddle index
	inc bx
	mov al, bl		;advance to next twiddle position
	and al, 3		;bx=bx mod 4 to rotate through twiddles
	mov bl, al
	mov cs:twiddle_idx, bx	;save next twiddle index

	mov ah, cs:[twiddle_char + bx] 	;get the next twiddle character
	mov es:[94h], ah		;put twiddle status on screen
;
;----------------------------------------
; display the DOS service number in hex
;
	pop bx		
	mov ax, bx		;get the dos function code into ah

	and ah, 0fh
	add ah, 30h		;convert low digit to hex

	cmp ah, 3ah		;handle A..F
	jb foo
	add ah, 7
foo:
	mov es:[98h], ah	;and set LSB on screen

	mov ax, bx		;get fresh copy of function code into ah

	shr ah, 1		;move down bits for high digit
	shr ah, 1
	shr ah, 1
	shr ah, 1		;make high byte hex and put on screen
	and ah, 0fh
	add ah, 30h

	cmp ah, 3ah		;handle A..F
	jb bar
	add ah, 7
bar:
	mov es:[96h], ah		;set MSB on screen

;----------------------------------------
; perform the DOS service function
;
	mov ax, bx	; we've been saving it here.
	pop bx
	pop es		;restore initial entry registers
	pop ds
;
	db 0eah		;   pass to original int21 code
int21ofs	dw ?	; *** self-modifying code:
int21seg	dw ?	; *** JMP FAR PTR xxxx:yyyy

new21	endp


;----------------------------------------
; startup code
;----------------------------------------
; determine where the video ram is.  this is done by putting a special
; character on the screen and then looking for it in the various video
; ram locations.
;
init proc near
	push cs
	pop ds		; get cs in ds

	mov ah, 3	;get cursor position
	int 10h		;video bios service

	push ax
	push bx
	push dx		 ;save it for later

	mov dx, 0
	mov ah, 2		 ;home the cursor
	int 10h

	mov ah, 0Ah
	mov al, 88h		 ;display a funny char at cursor
	mov cx, 1
	int 10h
;
; move cursor back to original position
;
	pop dx
	pop bx
	pop ax
	mov ah, 2
	int 10h
;
; look for MONO video ram
;
	mov dl, 88h
	mov ax, 0b000h
	mov es,ax
	cmp dl, es:[0]
	jz foundvideo
;
; look for COLOR video ram
;
	mov ax, 0b800h
	mov es, ax
	cmp dl, es:[0]
	jz foundvideo
;
; couldn't find video ram; display a message and abort
;
	lea dx, errormsg
	mov ah,9
	int 21h		 ;display error message

	mov ah, 4ch	; terminate with error code
	mov al, 1
	int 21h

errormsg:
	db "ERROR: Can't find display memory$"


foundvideo:
	mov cs:display_seg, ax		 ;set the display segment
;
;----------------------------------------
; display the program signon message now that we are sure
; that we can be installed
;
	lea dx, signon_msg
	mov ah, 9
	int 21h		 ;display signon message

	mov	es, ds:[002Ch]
	mov	ah, 49h         ; free up our copy of the environment
	int	21h

;----------------------------------------
; now install new interrupt handler
;
	mov ax,0
	mov es,ax
;
; save old DOS service vector
;
	mov	ax, 3521h	   
	int	21h
	mov	int21ofs, bx	    ; save old int21
	mov	int21seg, es	    ;
	mov	ax, 2521h	    ; install new vector
	lea	dx, new21	    ; ptr to our routine
	int	21h

;
; set last resident code offset
; and terminate-and-stay-resident
;
	mov	dx, RESLEN      ; size of our interrupt handler
        test    dl,0Fh             ; see if it's on a paragraph boundary
	sahf
	mov	cl, 4
	shr	dx, cl		; convert bytes to paragraphs
	lahf			; paragraph aligned?
	jz     gotsr		;   yes, we're done
	inc	dx		; round to next paragraph
gotsr:	mov	ax, 3100h       ; terminate and stay resident
	int	21h
init	endp


; a signature in bytes
signon_msg:
	db 0ah, 0dh, "DOSMON - DOS Activity Monitor", 0ah, 0dh
	db "S.H.Smith, 15-May-86", 0ah, 0dh
	db "jack velte, may 89", 0ah, 0dh, "$"

code	  ends
	      end	    dosmon
