TITLE SCROLLK.COM -- DOS RESIDENT SCROLL LOCK CONTROL
PAGE 60,132

COMMENT* This program provides scrolling control of the video display with
	the Scroll Lock key.  Assemble, Link, and convert the EXE file to a
	COM program. Run the program once on system boot-up then the Scroll
	Lock key can be used to toggle scrolling on and off. If scroll lock
	is ON, the screen will pause after 23 continuous lines are displayed.
	Press the Left shift key to scroll another 23 lines or the Right
	shift key to scroll one line; or press Scroll Lock again to turn OFF
	the lock and the screen will scroll uninterrupted.*

;----------------------------------------------------------------------------;
;This program intercepts calls to the VIDEO_IO routines.  Commands to scroll
;the screen will be intercepted; all others passed through. This routine is
;attached to DOS.
;
;Program by John Socha, 'Softalk', May 1983, pp. 38-49
;
;keyed in by Lee M Buck, Arlington VA
;
;----------------------------------------------------------------------------;


;----------------------------------------------------------------------------;
;Interrupt vectors for video and keyboard routines
;----------------------------------------------------------------------------;

VECTORS SEGMENT AT 0
	ORG	10H*4
VIDEO_INT	LABEL DWORD
	ORG	16H*4
KEYBOARD_INT	LABEL DWORD
VECTORS ENDS


;----------------------------------------------------------------------------;
;ROM BIOS data area for keyboard
;
;Contents of KB_FLAG - bits 7 to 0
;<---	  ---	  ---	  ---	  ---	  ---	  ---	  --->
;  ^INS_STATE	   ^NUM_STATE	   ^ALT_SHIFT	   ^LEFT_SHIFT
;	   ^CAPS_STATE	   ^SCROLL_STATE   ^CTL_STATE	   ^RIGHT_SHIFT
;----------------------------------------------------------------------------;

ROM_BIOS_DATA	SEGMENT AT 40H
	ORG	17H
KB_FLAG		DB	?		;Bit 4 set for scroll lock
ROM_BIOS_DATA	ENDS


;----------------------------------------------------------------------------;
;Initialize vectors and attach to DOS
;----------------------------------------------------------------------------;

CSEG	SEGMENT		PARA
	ASSUME		CS:CSEG
	ORG		100H		;Set starting point for COM file
BEGIN:  JMP		INIT_VECTORS	;Initialize INT 10H and attach to DOS


;----------------------------------------------------------------------------;
;These memory locations store the addresses of the ROM routines for video and
;keyboard I/O routines
;----------------------------------------------------------------------------;

ROM_VIDEO_IO		DD		;Address of the ROM routines
ROM_KEYBOARD_IO		DD
SCROLL_COUNT		DB	0	;Lines scrolled since last pause
LAST_LOCK_STATE		DB	0	;0 if scrl lock off last time checked
MAX_LINES		DB	23	;Scroll by 23 lines, leaving one old
					;line at top before pausing
LAST_LINE		DB		;Last line cursor was on

;----------------------------------------------------------------------------;
;This routine intercepts all calls to the VIDEO_IO routine in ROM.
;Scroll Lock:
;	OFF		This routine passes control directly to the ROM BIOS
;			routine.
;	ON		Functions other than SCROLL UP or SCROLL DOWN are
;			passed directly to the ROM routines. Otherwise, this
;			routine increments the scroll count and checks to see
;			if it exceeds the page size of 23 lines (MAX_LINES).
;			If so, loop until either shift key pressed.
;				Left shift allows scroll of whole window
;				Right shift scrolls one line.
;--------------------------------------------------------------------------;

INTERCEPT_VIDEO		PROC	FAR
	ASSUME  CS:CSEG
	STI				;Turn on interrupts again
	PUSH	DS			;Save registers used
	PUSH	BX
	PUSH	AX

	ASSUME  DS:ROM_BIOS_DATA
	MOV	BX,ROM_BIOS_DATA
	MOV	DS,BX
	MOV	AL,KB_FLAG		;Check state of scroll lock key
	AND	AL,10H			;Isolate scroll lock bit
	ASSUME  DS:CSEG
	MOV	BX,CS			;Data segment for variables (above)
	MOV	DS,BX
	CMP	AL,LAST_LOCK_STATE
	JE	UN_CHANGED		;Scroll lock key hasn't changed
	MOV	BL,MAX_LINES		;Scroll lock key has changed, set to
	MOV	SCROLL_COUNT,BL		;MAX_LINES to stop scrolling
	MOV	LAST_LOCK_STATE,AL	;Save the new scroll lock state
	PUSH	CX			;Now read current cursor position and
	PUSH	DX			;save in LAST_LINE so SCROLLK won't
	PUSH	AX			;freeze in the middle of a line
	MOV	AH,3
	MOV	BH,0
	PUSHF
	CALL	ROM_VIDEO_IO		;This is a pseudo INT 10H call to BIOS
	MOV	LAST_LINE,DH		;to find old cursor position and save
	POP	AX			;in LAST_LINE. (see Tech. Ref. A-43)
	POP	DX
	POP	CX
UN_CHANGED:
	XCHG	AX,BX		;Recover function (AH) and retain scroll lock
	POP	AX
	OR	BL,BL		;Is scroll lock on?
	JZ	TO_VIDEO_IO	;No, jump to the ROM VIDEO_IO routine
	;-----------------------;
	;Scroll Lock ON		;
	;-----------------------;
	CMP	AH,2		;Check for SET CURSOR POSITION function
	JNE	NOPE
	CMP	DH,LAST_LINE	;Is the cursor being moved to the next line?
	MOV	LAST_LINE,DH		;Save new cursor line
	JLE	TO_VIDEO_IO		;No, jump to ROM routine
	JMP	SHORT CHECK_LOCK	;Yes, see if need to lock
NOPE:	CMP	AH,6			;Scroll up?
	JNE	TO_VIDEO_IO		;No, go to ROM routines
CHECK_LOCK:
	INC	SCROLL_COUNT		;Take care of scroll lock
	MOV	BH,MAX_LINES
	CMP	SCROLL_COUNT,BH		;Have we scrolled more than MAX_LINES?
	JL	TO_VIDEO_IO		;Nope, it's ok to scroll
					;Yes, wait until a shift key is hit
	MOV	BL,BH			;Set SCROLL_COUNT to MAX_LINES-1
	DEC	BL			;so we can print one more line
	MOV	SCROLL_COUNT,BL
	ASSUME  DS:ROM_BIOS_DATA
	MOV	BX,ROM_BIOS_DATA
	MOV	DS,BX
LOOP:	MOV	BL,KB_FLAG		;Wait for left or right shift key push
	TEST	BL,10H			;Is scroll lock still on?
	JZ	TO_VIDEO_IO
	AND	BL,3			;Pick off shift key info
	JZ	LOOP			;Stay in loop until shift key pressed
	CMP	BL,1			;Right shift key pressed?
	JE	SCROLL_LINE		;Yes, allow scroll of one line
			    ;No, must be left shift key, so reset scroll count
	XOR	BX,BX
	MOV	SCROLL_COUNT,BL
SCROLL_LINE:

TO_VIDEO_IO:
	POP	BX			;Restore BX register
	PUSHF
	CALL	ROM_VIDEO_IO		;Perform a pseudo INT 10H call to BIOS
	POP DS
	IRET				;interrupt Return
INTERCEPT_VIDEO		ENDP


;----------------------------------------------------------------------------;
;This routine intercepts all calls to KEYBOARD_IO
;If the keyboard function calls for a read (AH=0) then reset the scroll count
;----------------------------------------------------------------------------;

INTERCEPT_KEYBOARD	PROC	FAR
	ASSUME  CS:CSEG,DS:CSEG
	STI				;Turn interrupts on
	PUSH	DS			;Save registers used by this routine
	PUSH	BX
	MOV	BX,CS			;Set up data segment for variables
	MOV	DS,BX
	OR	AH,AH			;Check to see if AH=0
	JNZ	KB1			;Nope, branch to keyboard I/O
	XOR	BX,BX			;Yes, set scroll count to 0
	MOV	SCROLL_COUNT,BL
KB1:	POP BX				;Restore BX register
	ASSUME  DS:NOTHING
	POP	DS
	JMP	ROM_KEYBOARD_IO		;Jump to keyboard routine and return
					;directly to the routine that called
					;this one
INTERCEPT_KEYBOARD	ENDP

;----------------------------------------------------------------------------;
;This section of code saves the old interrupt vectors for the keyboard and
;video I/O routines. These vectors are replaced by the addresses of
;INTERCEPT_VIDEO and INTERCEPT_KEYBOARD PROCS above.
;----------------------------------------------------------------------------;

INIT_VECTORS	PROC	NEAR
	ASSUME  CS:CSEG,DS:CSEG
	MOV	AH,3			;Set LAST_LINE to cursor line number
	XOR	BH,BH
	INT	10H
	MOV	LAST_LINE,DH

	ASSUME  CS:CSEG,DS:VECTORS
	MOV	AX,VECTORS
	MOV	DS,AX

	MOV	AX,VIDEO_INT		;Save the ROM routine address
	MOV	ROM_VIDEO_IO,AX
	MOV	AX,VIDEO_INT[2]
	MOV	ROM_VIDEO_IO[2],AX
	MOV	AX,OFFSET INTERCEPT_VIDEO	;Set video INT 10H to point to
	MOV	VIDEO_INT,AX			;INTERCEPT_VIDEO PROC above.
	MOV	VIDEO_INT[2],CS

	MOV	AX,KEYBOARD_INT		;Save the ROM routine address
	MOV	ROM_KEYBOARD_IO,AX
	MOV	AX,KEYBOARD_INT[2]
	MOV	ROM_KEYBOARD_IO[2],AX
	MOV	AX,OFFSET INTERCEPT_KEYBOARD	;Set keyboard INT 16H to point
	MOV	KEYBOARD_INT,AX			;to INTERCEPT_KEYBOARD PROC
	MOV	KEYBOARD_INT[2],CS

	MOV	DX,OFFSET INIT_VECTORS  ;End of resident portion
	INT	27H			;Terminate but stay resident
INIT_VECTORS	ENDP

CSEG	ENDS
	END	BEGIN

