;-----------------------------------------------------------------------;
; Disk Light creates an on-screen version of the disk light that is	;
; usually on disk drives.  The difference, however, is that this light	;
; will only be on as long as it takes to read or write to the disk.  In ;
; other words, it does not stay on while the disk spins without any	;
; activity.								;
;									;
; This program intercepts the INT 13h vector, which is the entry point	;
; for the ROM BIOS's diskette routine.	On entry, Disklite displays	;
; the drive letter in the upper-right corner of the screen, and 	;
; restores this section of the screen on exit.				;
;-----------------------------------------------------------------------;


;-----------------------------------------------------------------------;
; Here is the DISKLITE's entry point.  It jumps to the initialization	;
; routine which is at the very end so we can throw it out of memory	;
; after we've used it.							;
;-----------------------------------------------------------------------;
CODE_SEG	SEGMENT
	ASSUME	CS:CODE_SEG, DS:CODE_SEG
	ORG	100h			;Reserve for DOS Program Segment Prefix
BEGIN:	JMP	INIT_VECTORS

AUTHOR_STRING		DB	"Installed Disklite, by John Socha"
			DB	0Dh, 0Ah, '$'


ROM_DISKETTE_INT	DD	?

DISPLAY_BASE		DW	?
OLD_DISPLAY_CHARS	DB	4 DUP (?)
DISPLAY_CHARS		DB	'A', 70h, ':', 70h
NUM_FLOPPIES		DB	?		;Number of floppy drives

UPPER_LEFT	EQU	(80 - 2) * 2		;Offset to drive light

;-----------------------------------------------------------------------;
; This procedure intercepts calls to the ROM BIOS's diskette I/O	;
; vector, and it does several things:					;
;									;
;	1. Checks to see if the screen is in an 80 column text mode	;
;	   so we can write to the screen.  Disklite won't write any	;
;	   characters to the screen if it's not in an 80 column mode.	;
;	2. Displays the disk drive letter, "A:" for example, in the	;
;	   upper-right corner of the screen.				;
;	3. Calls the old ROM BIOS routine to do the actual work.	;
;	4. Restores the two characters in the upper-right corner of the ;
;	   screen.							;
;-----------------------------------------------------------------------;
INTERCEPT_DISKETTE_INT	PROC	FAR
	Assume	CS:CODE_SEG, DS:Nothing
	PUSHF				;Save the old flags
	PUSH	AX
	PUSH	SI
	PUSH	DI
	PUSH	DS
	PUSH	ES
	CALL	GET_DISPLAY_BASE	;Calculates the screen's display base
	CALL	SAVE_SCREEN		;Save two chars in upper right
	CALL	DISPLAY_DRIVE_LETTER	;Display the drive letter
	POP	ES
	POP	DS
	POP	DI
	POP	SI
	POP	AX
	POPF				;Restore the old flags

	PUSHF				;Simulate an INT call	
	CALL	ROM_DISKETTE_INT	; to the old ROM BIOS routine

	PUSHF				;Save the returned flags
	PUSH	AX
	PUSH	SI
	PUSH	DI
	PUSH	DS
	PUSH	ES
	LEA	SI,OLD_DISPLAY_CHARS	;Point to the old screen image
	CALL	WRITE_TO_SCREEN 	;Restore two chars in upper right
	POP	ES
	POP	DS
	POP	DI
	POP	SI
	POP	AX
	POPF				;Recover the returned flags
	RET	2			;Leave the status flags intact
INTERCEPT_DISKETTE_INT	ENDP


;-----------------------------------------------------------------------;
; This procedure calculates the segment address for the display adapter ;
; that we're using.							;
;									;
; Destroys:	AX							;
;-----------------------------------------------------------------------;
GET_DISPLAY_BASE	PROC	NEAR
	Assume	CS:CODE_SEG, DS:Nothing
	INT	11h			;Get the current equipment flag
	AND	AX,30h			;Isolate the display flags
	CMP	AX,30h			;Is this a monochrome display?
	MOV	AX,0B800h		;Set for a color graphics adapter
	JNE	DONE_GET_BASE		;Color graphics, base already set
	MOV	AX,0B000h		;Set for monochrome display
DONE_GET_BASE:
	MOV	DISPLAY_BASE,AX 	;Save this display base
	RET
GET_DISPLAY_BASE	ENDP


;-----------------------------------------------------------------------;
; This procedure saves the two characters in the upper right corner of	;
; the screen so that we can restore them later. 			;
;									;
; Destroys:	AX, SI, DI, DS, ES					;
;-----------------------------------------------------------------------;
SAVE_SCREEN	PROC	NEAR
	Assume	CS:CODE_SEG, DS:Nothing
	MOV	SI,UPPER_LEFT		;Read chars from the screen
	LEA	DI,OLD_DISPLAY_CHARS	;Write chars to local memory
	MOV	AX,DISPLAY_BASE 	;Get segment address of screen
	MOV	DS,AX
	MOV	AX,CS			;Point to the local data
	MOV	ES,AX
	CLD				;Set for auto-increment
	MOVSW				;Move two characters
	MOVSW
	RET
SAVE_SCREEN	ENDP


;-----------------------------------------------------------------------;
; This procedure displays the drive letter in the upper-right corner of ;
; the screen.								;
;									;
; Destroys:	AX, SI							;
;-----------------------------------------------------------------------;
DISPLAY_DRIVE_LETTER	PROC	NEAR
	Assume	CS:CODE_SEG, DS:Nothing
	MOV	AL,DL			;Get the drive number
	CMP	AL,80h			;Is this a hard disk drive?
	JB	DISPLAY_LETTER		;No, then continue
	SUB	AL,80h			;Convert to hard disk number
	ADD	AL,NUM_FLOPPIES 	;Convert to correct disk number
DISPLAY_LETTER:
	ADD	AL,'A'			;Convert this into a drive letter
	LEA	SI,DISPLAY_CHARS	;Point to new char image
	MOV	CS:[SI],AL		;Save this character
	CALL	WRITE_TO_SCREEN
	RET
DISPLAY_DRIVE_LETTER	ENDP


;-----------------------------------------------------------------------;
; This procedure writes two characters in the upper-right corner of the ;
; screen.								;
;									;
; On entry:	CS:SI	Screen image for two characters 		;
; Destroys:	AX, SI, DI, DS, ES					;
;-----------------------------------------------------------------------;
WRITE_TO_SCREEN 	PROC	NEAR
	Assume	CS:CODE_SEG, DS:Nothing
	MOV	DI,UPPER_LEFT		;Write chars to the screen
	MOV	AX,DISPLAY_BASE 	;Get segment address of screen
	MOV	ES,AX
	MOV	AX,CS			;Point to the local data
	MOV	DS,AX
	CLD				;Set for auto-increment
	MOVSW				;Move two characters
	MOVSW
	RET
WRITE_TO_SCREEN 	ENDP

;-----------------------------------------------------------------------;
; This procedure daisy-chains Disklite onto the diskette I/O vector	;
; so that we can monitor the disk activity.				;
;-----------------------------------------------------------------------;
INIT_VECTORS	PROC	NEAR
	Assume	CS:CODE_SEG, DS:CODE_SEG
	LEA	DX,AUTHOR_STRING	;Print out the author notice
	MOV	AH,9			;Display this string
	INT	21h

	CALL	GET_NUM_FLOPPIES	;See how many floppy drives installed

	MOV	AH,35h			;Ask for an interrupt vector
	MOV	AL,13h			;Get the vector for INT 13h
	INT	21h			;Put vector in ES:BX
	MOV	Word Ptr ROM_DISKETTE_INT,BX
	MOV	Word Ptr ROM_DISKETTE_INT[2],ES

	MOV	AH,25h			;Ask to set an interrupt vector
	MOV	AL,13h			;Set the INT 13h vector to DS:DX
	MOV	DX,Offset INTERCEPT_DISKETTE_INT
	INT	21h			;Set INT 13h to point to our procedure
	
	MOV	DX,Offset INIT_VECTORS	;End of resident portion
	INT	27h			;Terminate but stay resident
INIT_VECTORS	ENDP


;-----------------------------------------------------------------------;
; This procedure determines how many logical floppy disk drives are in	;
; the system.  The next drive letter will be used for hard disk drives. ;
;-----------------------------------------------------------------------;
GET_NUM_FLOPPIES	PROC	NEAR
	Assume	CS:CODE_SEG, DS:CODE_SEG
	INT	11h			;Get the equipment flag
	MOV	CL,6
	SHR	AX,CL			;Right justify num of floppies
	AND	AL,3			;Strip all the other flags
	INC	AL			;Returns 0 for 1 floppy
	CMP	AL,1			;Is this a one floppy system?
	JA	DONE_GET_FLOPPIES	;No, then this is the correct number
	MOV	AL,2			;Yes, there are 2 logical drives
DONE_GET_FLOPPIES:
	MOV	NUM_FLOPPIES,AL 	;Save this number
	RET
GET_NUM_FLOPPIES	ENDP


CODE_SEG	ENDS

	END	BEGIN
