;-----------------------------------------------------------------------;
; STATLINE, by John Socha, Copyright 1986 Ziff-Davis Publishing Co.	;
;									;
; Here are the two interrupt vectors that we take over.  The first	;
; interrupt, INT 9, is the hardware interrupt for the keyboard, and	;
; it's called every time you push or release a key.			;
;									;
; The other interrupt, INT 10h, points to the ROM BIOS routines that	;
; handle all of the screen I/O.  We intercept it so that we can watch	;
; for commands to change the display mode.  Since STATLINE only works	;
; in 80x25 text modes, STATLINE turns itself off as long as the screen	;
; is in a different mode.						;
;-----------------------------------------------------------------------;
VECTORS		SEGMENT AT 0h
	ORG	9h*4
KEYBOARD_INT_VECTOR	LABEL	DWORD	;Keyboard interrupt
	ORG	10h*4
VIDEO_IO_VECTOR		LABEL	DWORD	;ROM BIOS Video I/O function calls
VECTORS		ENDS


;-----------------------------------------------------------------------;
; STATLINE uses the following flags from the ROM BIOS's data area to	;
; control the screen.							;
;-----------------------------------------------------------------------;
ROM_BIOS_DATA	SEGMENT AT 40h
	ORG	10h
EQUIP_FLAG	DB	?		;Equipment installed
	ORG	17h
KBD_FLAG	DB	?		;Used to determine display type
	ORG	4Eh
CRT_START	DW	?		;Starting address in buffer
	ORG	63h
ADDR_6845	DW	?		;3x8 register, where x is B or D
ROM_BIOS_DATA	ENDS


;-----------------------------------------------------------------------;
; This section of the ROM in a COMPAQ contains the 6 bytes 'COMPAQ'	;
; which we can use to identify a COMPAQ computer.  COMPAQ computers	;
; have a display adapter that uses the monochrome display, but with	;
; registers and memory the same as a color graphics adapter.		;
;-----------------------------------------------------------------------;
COMPAQ_CO	EQU	4F43h		;ASCII code of 'CO'

COMPAQ_SEG	SEGMENT AT 0F000h
	ORG	0FFEAh
COMPAQ_ID	DW	?		;We should find 'CO' (4F43h) here
COMPAQ_SEG	ENDS


;-----------------------------------------------------------------------;
; Here is the STATLINE's entry point.  It jumps to the initialization	;
; routine which is at the very end so that 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 Statline, by John Socha"
			DB	0Dh, 0Ah, '$'

OLD_KBD_FLAG		DB	0	;Most recent upper nibble of status
VIDEO_MODE		DB	?	;Current video mode
STATUS_LINE_ENABLED	DB	1	;0 when we're in graphics modes

;-----------------------------------------------------------------------;
; The following table contains the character/attribute pairs for the	;
; states of the Scroll Lock, Num Lock, and Caps Lock keys.		;
;									;
; The first word contains the address for the character, and the second	;
; word contains the character/attribute pair.				;
;-----------------------------------------------------------------------;
ENTRY	STRUC
OFFSET_FROM_TOP		DW	?	;Offset from start of display memory
ATTRIBUTE_CHARACTER	DB	?	;Attribute and character code
ENTRY	ENDS

FLAG_CHARACTER_TABLE	LABEL	WORD
	ENTRY	<4084,' '>		;Scroll Lock, ' '
	ENTRY	<4084,12h>		;Scroll Lock, double-ended arrow
	ENTRY	<4080,' '>		;Num Lock, ' '
	ENTRY	<4080,23h>		;Num lock, '#'
	ENTRY	<4076,' '>		;Caps Lock, ' '
	ENTRY	<4076,18h>		;Caps Lock, up arrow

ROM_KEYBOARD_INT	DD	?
ROM_VIDEO_IO_INT	DD	?

;-----------------------------------------------------------------------;
; This procedure sends control off to the ROM BIOS routine, then checks	;
; the shift-lock flags on return and writes the new flag characters to	;
; the screen.								;
;-----------------------------------------------------------------------;
INTERCEPT_KEYBOARD_INT	PROC	FAR
	ASSUME	CS:CODE_SEG, DS:NOTHING
	PUSHF				;Simulate INT with PUSHF and CALL
	CALL	ROM_KEYBOARD_INT	;Let ROM do the work
	CMP	STATUS_LINE_ENABLED,1	;See if Status line enabled.
	JNE	NO_STATUS_LINE		;Not enabled.
	CALL	CHECK_STATUS_FLAGS	;Check flags and update status line
NO_STATUS_LINE:
	IRET
INTERCEPT_KEYBOARD_INT	ENDP


;-----------------------------------------------------------------------;
; This procedure checks the current setting of KBD_FLAGS against the	;
; last setting, and if the flags have changed, it updates the status	;
; line display.  Check_status_flags also updates Old_kbd_flag.		;
;-----------------------------------------------------------------------;
CHECK_STATUS_FLAGS	PROC	NEAR
	PUSH	AX			;Save all the registers we use
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	DS
	PUSH	ES
	ASSUME	CS:CODE_SEG, DS:ROM_BIOS_DATA
	MOV	AX,ROM_BIOS_DATA	;Set DS so it points to BIOS data area
	MOV	DS,AX
	MOV	DX,ADDR_6845		;Get the base address for the 6845
	MOV	AX,0B800h		;Segment address of graphics adapter
	CMP	DX,3D0h			;Is this the color graphics adapter?
	JAE	IS_COLOR_BOARD      ;It's a color board, so don't change AX
	MOV	AX,0B000h           ;Segment address for monochrome adapter
IS_COLOR_BOARD:
	MOV	ES,AX			;Use extra segment for display memory

	ADD	DX,03DAh-03D4h		;Point to status register
	MOV	BL,KBD_FLAG		;Get flag information
	MOV	CL,4	            ;Put shift lock flags into lower nibble
	SHR	BL,CL
	ASSUME	CS:CODE_SEG, DS:CODE_SEG
	MOV	AX,CS			;Set DS to the local data (in CS)
	MOV	DS,AX
	CMP	BL,OLD_KBD_FLAG		;Have any of the status flags changed?
	JE	FLAGS_HAVENT_CHANGED	;No, then do nothing
	MOV	OLD_KBD_FLAG,BL	    ;Flags have changed, update status line
	MOV	SI,Offset FLAG_CHARACTER_TABLE

	MOV	CX,3			;Repeat for three shift lock keys
SHIFT_LOCK_LOOP:
	PUSH	SI
	SHR	BL,1			;Get next flag in carry
	JNC	READ_OFFSET		;Flag was 0, SI Ok
	ADD	SI,3			;Skip over information
READ_OFFSET:
	MOV	DI,[SI]			;Get Offset
	MOV	AL,[SI+2]		;Get character for this flag
	
	PUSH	CX			;Save the CX register
	MOV	CL,AL			;Save the character in CL
WAIT_FOR_NON_RETRACE:
	IN	AL,DX			;Read status
	TEST	AL,8			;In vertical retrace?
	JNZ	WAIT_FOR_NON_RETRACE	;Wait for vertical retrace to finish
WAIT_FOR_RETRACE:
	IN	AL,DX			;Read status
	TEST	AL,8			;In vertical retrace?
	JZ	WAIT_FOR_RETRACE	;No, then wait for vertical retrace
	MOV	AL,CL			;Get the character we want to write
	STOSB				;Write character to the screen
	POP	CX			;Recover the old value of CX

	POP	SI
	ADD	SI,6			;Skip to next pair of entries
	LOOP	SHIFT_LOCK_LOOP

FLAGS_HAVENT_CHANGED:
	POP	ES
	POP	DS
	POP	DI
	POP	SI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
CHECK_STATUS_FLAGS	ENDP


;-----------------------------------------------------------------------;
; This procedure reprograms the 6845 whenever a program switches modes	;
; into an 80x25 text mode.						;
;-----------------------------------------------------------------------;
INTERCEPT_VIDEO_IO	PROC	FAR
	ASSUME	CS:CODE_SEG, DS:NOTHING
	OR	AH,AH			;Check if called for SET MODE
	JZ	SET_MODE		;It's a SET MODE call
	JMP	ROM_VIDEO_IO_INT	;Not a SET MODE, call ROM BIOS
SET_MODE:
	MOV	VIDEO_MODE,AL		;Save new video mode
	MOV	STATUS_LINE_ENABLED,AH	;Disable STATLINE during mode change
	PUSHF				;Simulate INT with PUSHF, CALL
	CALL	ROM_VIDEO_IO_INT	;Let ROM BIOS change video mode
	CALL	REPROGRAM_6845		;Create 26th line again
	MOV	OLD_KBD_FLAG,0		;Flags will be clear in new mode
	CALL	CHECK_STATUS_FLAGS	;Make sure we show current flags
	IRET
INTERCEPT_VIDEO_IO	ENDP



;-----------------------------------------------------------------------;
; The following tables describe how to reprogram the 6845 registers.	;
; Each pair describes how to change one register.  The first number is	;
; the register that we want to reprogram, while the second number is	;
; the new value for that register.  Reprogram_6845 stops when it sees	;
; a 0 for the register number.						;
;-----------------------------------------------------------------------;
MONOCHROME_TABLE	LABEL	BYTE
	DB	4,26			;Vertical total, 26 lines
	DB	5,3			;Vertical total adjust, scan lines
	DB	6,26			;Vertical displayed, 26 lines
	DB	7,26			;Vertical sync position, lines
	DB	0			;End of Monochrome table
	
COLOR_GRAPHICS_TABLE:
	DB	6,26			;Vertical displayed
	DB	0			;End of color graphics adapter table

;-----------------------------------------------------------------------;
; This procedure reprograms the 6845 so that it will show 26 rather	;
; than 25 lines.  It uses the values from one of the tables above.	;
;									;
; This procedure also handles the COMPAQ, which is a special case.  The	;
; COMPAQ uses a monochrome-type display, so it needs to be reprogrammed	;
; like a monochrome display, yet writes should be to 3Dxh registers	;
; rather than the 3Bxh registers used for an IBM monochrome display.	;
;									;
; F000:FFEA holds the word 'COMPAQ' on COMPAQ computers, so we can tell	;
; when we're running on a COMPAQ.					;
;-----------------------------------------------------------------------;
REPROGRAM_6845	PROC	NEAR
	ASSUME	CS:CODE_SEG, DS:CODE_SEG
	PUSH	AX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DS
	MOV	AX,CS
	MOV	DS,AX			;Set up Data Seg
	MOV	STATUS_LINE_ENABLED,0	;Initially disable status line
	MOV	AL,VIDEO_MODE		;Check video mode
	CMP	AL,1			;In graphics mode?
	JLE	GRAPHICS_MODE		;Yes, don't reprogram 6845
	CMP	AL,3			;Is display in a text mode?
	JLE	TEXT_MODE		;Yes, then we can reprogram 6845
	CMP	AL,7			;Is it in the monochrome mode?
	JNE	GRAPHICS_MODE		;No, then don't reprogram 6845
TEXT_MODE:				;Yes, reprogram 6845
	MOV	STATUS_LINE_ENABLED,1	;Enable status line in text modes
	MOV	SI,Offset MONOCHROME_TABLE
	MOV	DX,3B4h			;6845 registers for monochrome display
	PUSH	DS
	MOV	AX,ROM_BIOS_DATA	;Read equipment flag from low memory
	MOV	DS,AX
	ASSUME	CS:CODE_SEG, DS:ROM_BIOS_DATA
	MOV	AL,EQUIP_FLAG
	AND	AL,30h			;Isolate CRT switches
	CMP	AL,30h			;Is it the monochrome display?
	POP	DS
	JE	SET_REGISTERS		;Yes, the registers are correct
	MOV	DX,3D4h		    ;No, set registers for graphics adapter
	PUSH	DS
	MOV	AX,COMPAQ_SEG		;Now check to see if this is a COMPAQ
	MOV	DS,AX
	ASSUME	CS:CODE_SEG, DS:COMPAQ_SEG
	CMP	COMPAQ_ID, COMPAQ_CO	;Is this the 'CO' from COMPAQ?
	POP	DS			;Restore old DS
	ASSUME	CS:CODE_SEG, DS:CODE_SEG
	JE	SET_REGISTERS		;Is a COMPAQ, use monochrome data
	MOV	SI,Offset COLOR_GRAPHICS_TABLE	;No, use color graphics data

SET_REGISTERS:
	CLD				;Clear direction flag for increment
ADAPTER_LOOP:
	LODSB				;Get register number
	OR	AL,AL			;Are we at the end of the table?
	JZ	END_OF_ADAPTER_TABLE	;Yes, we're almost done
	OUT	DX,AL			;No, select this register
	INC	DX			;Point to data register
	LODSB				;Get new register value
	OUT	DX,AL			;Set the register to its new value
	DEC	DX			;Point back to address register
	JMP	ADAPTER_LOOP		;Get the next register/value pair

END_OF_ADAPTER_TABLE:
GRAPHICS_MODE:
	POP	DS
	POP	SI
	POP	DX
	POP	CX
	POP	AX
	RET
REPROGRAM_6845	ENDP


;-----------------------------------------------------------------------;
; This procedure initializes the interrupt vectors and the 6845		;
; registers.  It initializes both the Monochrome and color graphics	;
; adapter address so it will work on the COMPAQ as well as IBM PCs	;
;-----------------------------------------------------------------------;
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

	MOV	AH,15			;Check current video mode
	INT	10h			;Call VIDEO_IO ROM BIOS routine
	MOV	VIDEO_MODE,AL
	CALL	REPROGRAM_6845
	CALL	CHECK_STATUS_FLAGS	;Display any flags now on

	ASSUME	CS:CODE_SEG, DS:VECTORS
	MOV	AX,VECTORS		;Set up the data segment for vectors
	MOV	DS,AX
	CLI				;Don't allow interrupts

	MOV	AX,Word Ptr KEYBOARD_INT_VECTOR
	MOV	Word Ptr ROM_KEYBOARD_INT,AX
	MOV	AX,Word Ptr KEYBOARD_INT_VECTOR[2]
	MOV	Word Ptr ROM_KEYBOARD_INT[2],AX
	MOV	Word Ptr KEYBOARD_INT_VECTOR, Offset INTERCEPT_KEYBOARD_INT
	MOV	Word Ptr KEYBOARD_INT_VECTOR[2],CS

	MOV	AX,Word Ptr VIDEO_IO_VECTOR
	MOV	Word Ptr ROM_VIDEO_IO_INT,AX
	MOV	AX,Word Ptr VIDEO_IO_VECTOR[2]
	MOV	Word Ptr ROM_VIDEO_IO_INT[2],AX
	MOV	Word Ptr VIDEO_IO_VECTOR, Offset INTERCEPT_VIDEO_IO
	MOV	Word Ptr VIDEO_IO_VECTOR[2],CS
	
	STI				;Allow interrupts again
	MOV	DX,Offset INIT_VECTORS	;End of resident portion
	INT	27h			;Terminate but stay resident
INIT_VECTORS	ENDP


CODE_SEG	ENDS

	END	BEGIN
