Metropoli BBS
VIEWER: lites.asm MODE: TEXT (ASCII)
;=============================================================================
; LITES displays the realtime status of the UART's DTR, RTS, CTS, DSR, DCD,
; and RI lines as well as its Baud rate and data format settings.  Syntax is:
;	LITES [comport] [U]
; where comport is a number indicating which COM port is to be monitored,
; and U uninstalls the program from memory.
;=============================================================================
CODE		SEGMENT	PARA PUBLIC 'CODE'
		ASSUME	CS:CODE
		ORG	100H
BEGIN:		JMP	INITIALIZE

PROGRAM		DB	"LITES 1.0 (c) 1989 Ziff Communications Co.",13,10
AUTHOR		DB	"PC Magazine ",254," Jeff Prosise",13,10
HOTKEY		DB	"Hotkey is Alt-L",32,32,32,13,10,"$"

;-----------------------------------------------------------------------------
; Patch points to modify the program's operation.
;-----------------------------------------------------------------------------
KEYCODE		DB	26H			;scan code for "L" key
VIDEO_ATTR	DB	70H			;status line video attribute
ROWNUM		DB	0			;row number of status line
COL_OFFSET	DB	0			;offset from right margin
TICKS		DB	2			;ticks between refreshes
COMPORT		DW	0			;COM port designator (COM1)

;-----------------------------------------------------------------------------
; Other program variables.
;-----------------------------------------------------------------------------
INT09H		DD	?			;interrupt 9h vector
INT08H		DD	?			;interrupt 8h vector
COUNTER		DB	0FFH			;refresh counter

PINS		DB	?			;bit 0 = DTR, 1 = RTS
						;    2 = CTS, 3 = DSR
						;    4 = RI, 5 = DCD

BAUDRATE	DW	?			;Baud rate
PARITY		DB	?			;parity setting
DATABITS	DB	?			;number of data bits
STOPBITS	DB	?			;number of stop bits

VIDEOSEG	DW	?			;video segment address
VIDEOADDR	DW	?			;video offset address
SYNCFLAG	DB	?			;0 = no video sync, 1 = sync

PINTEXT		DB	"DTRRTSCTSDSRRI",32,"DCD"

;=============================================================================
; KBINT intercepts and handles the keyboard interrupt 9h.
;=============================================================================
KBINT		PROC	FAR

		PUSHF				;save flags
		PUSH	AX			;save AX

		STI				;interrupts on
		IN	AL,60H			;get scan code from keyboard
		CMP	AL,CS:[KEYCODE]		;exit to BIOS if it's not
		JNE	NOT_HOT			;  the hotkey
		MOV	AH,2			;get keyboard shift state
		INT	16H
		AND	AL,0FH			;mask off the upper 4 bits
		CMP	AL,8			;exit if Alt isn't held down
		JNE	NOT_HOT
		MOV	AH,0FH			;get current video mode
		INT	10H
		CMP	AL,7			;abort if not in text mode
		JE	RESET
		CMP	AL,4
		JB	RESET
NOT_HOT:	POP	AX			;exit to BIOS int 9 handler
		POPF
		JMP	CS:INT09H
;
;Reset the keyboard controller and interrupt controller.
;
RESET:		CLI				;interrupts off
		IN	AL,61H			;reset the keyboard controller
		MOV	AH,AL
		OR	AL,80H
		OUT	61H,AL
		XCHG	AH,AL
		MOV	AL,20H			;signal EOI to interrupt
		OUT	20H,AL			;  controller
		STI				;interrupts back on
		PUSH	BX			;save registers
		PUSH	CX
		PUSH	DX
		PUSH	SI
		PUSH	DI
		PUSH	DS
		PUSH	ES
		PUSH	BP
;
;Deactivate the status line if it is currently displayed.
;
		CMP	CS:[COUNTER],0FFH	;branch if status line is
		JE	SETVIDEO		;  dormant
		MOV	CS:[COUNTER],0FFH	;disable counter
		MOV	AX,CS			;point DS:SI to buffer
		MOV	DS,AX
		MOV	SI,OFFSET INITIALIZE
		MOV	ES,CS:[VIDEOSEG]	;point ES:DI to video memory
		MOV	DI,CS:[VIDEOADDR]
		MOV	CX,32			
		CALL	VIDEO_XFER		;erase the status line
		JMP	SHORT KB_EXIT
;
;Initialize video parameters.
;
SETVIDEO:	MOV	CS:[VIDEOSEG],0B800H	;set default video parms
		MOV	CS:[SYNCFLAG],0
		MOV	AX,40H			;point ES to BIOS data
		MOV	ES,AX			;  area
		TEST	BYTE PTR ES:[63H],20H	;set video segment value
		JZ	COLOR			;  (monochrome or color)
		MOV	CS:[VIDEOSEG],0B000H
		JMP	SHORT NOSYNC
COLOR:		MOV	AH,12H			;if a color adapter is
		MOV	BL,10H			;  detected, determine
		INT	10H			;  whether or not it's
		CMP	BL,10H			;  a CGA and set SYNCFLAG
		JNE	NOSYNC			;  if it is
		MOV	CS:[SYNCFLAG],1
NOSYNC:		MOV	AX,ES:[4AH]		;calculate offset address
		SHL	AX,1			;  within video segment
		MOV	BL,CS:[ROWNUM]		;multiply number of columns
		MUL	BL			;  by row number
		MOV	BX,ES:[4AH]		;add column offset
		SUB	BL,32
		SUB	BL,CS:[COL_OFFSET]
		SHL	BL,1
		ADD	AX,BX
		ADD	AX,ES:[4EH]		;add page address
		MOV	CS:[VIDEOADDR],AX	;store it
;
;Save the portion of the screen that will be overwritten by the status line.
;
		MOV	DS,CS:[VIDEOSEG]	;point DS:SI to video memory
		MOV	SI,CS:[VIDEOADDR]
		MOV	AX,CS			;point ES:DI to save buffer
		MOV	ES,AX
		MOV	DI,OFFSET INITIALIZE
		MOV	CX,32
		CALL	VIDEO_XFER		;block move
;
;Activate the status line, restore registers, and exit.
;
		MOV	AL,CS:[TICKS]		;enable counter
		MOV	CS:[COUNTER],AL
KB_EXIT:	POP	BP			;restore registers
		POP	ES
		POP	DS
		POP	DI
		POP	SI
		POP	DX
		POP	CX
		POP	BX
		POP	AX			;restore AX and flags
		POPF
		IRET
KBINT		ENDP

;=============================================================================
; TIMERINT intercepts and handles the timer tick interrupt 8h.
;=============================================================================
TIMERINT	PROC	FAR
		PUSHF				;push flags for int call
		CALL	CS:INT08H			;call old interrupt handler
		CMP	CS:[COUNTER],0FFH	;exit if status line is
		JE	TIMER_EXIT		;  dormant
		DEC	CS:[COUNTER]		;decrement timer
		JNZ	TIMER_EXIT		;exit if nonzero
		PUSH	AX
		MOV	AL,CS:[TICKS]		;reset counter
		MOV	CS:[COUNTER],AL
		PUSH	BX			;save remaining registers
		PUSH	CX
		PUSH	DX
		PUSH	SI
		PUSH	DI
		PUSH	DS
		PUSH	ES
		STI				;enable interrupts
		CALL	SETPARMS		;set output variables
		CALL	REFRESH			;refresh the status display
		POP	ES			;restore registers and exit
		POP	DS
		POP	DI
		POP	SI
		POP	DX
		POP	CX
		POP	BX
		POP	AX
TIMER_EXIT:	IRET				;exit
TIMERINT	ENDP

;-----------------------------------------------------------------------------
; SETPARMS is called by TIMERINT to set UART parameter values.
;-----------------------------------------------------------------------------
PSETTINGS	DB	"NONE"
DLAB_REG	DB	?

SETPARMS	PROC	NEAR
		MOV	DX,CS:[COMPORT]		;get UART base address
		ADD	DX,3			;point DX to Data Format
		PUSH	DX
		IN	AL,DX			;read Data Format byte
		MOV	CS:[DLAB_REG],AL	;save it
		OR	AL,80H			;set DLAB
		OUT	DX,AL
		SUB	DX,3			;read divisor LSB
		IN	AL,DX
		MOV	BL,AL
		INC	DX
		IN	AL,DX			;read divisor MSB
		MOV	BH,AL
		OR	BX,BX			;don't divide if BX = 0
		JNZ	SP1
		MOV	WORD PTR CS:[BAUDRATE],0
		JMP	SHORT SP2
SP1:		MOV	AX,0C200H		;calculate Baud rate from
		MOV	DX,1			;  clock and divisor
		DIV	BX
		MOV	CS:[BAUDRATE],AX	;save it
SP2:		POP	DX			;restore Data Format register
		MOV	AL,CS:[DLAB_REG]
		OUT	DX,AL
;
;Determine what the parity, data bits, and stop bits settings are.
;
		MOV	AL,CS:[DLAB_REG]	;data bits in bits 0 and 1
		AND	AL,3
		ADD	AL,5
		MOV	CS:[DATABITS],AL
		MOV	AL,CS:[DLAB_REG]	;stop bits in bit 2
		SHR	AL,1
		SHR	AL,1
		AND	AL,1
		ADD	AL,1
		MOV	CS:[STOPBITS],AL
		MOV	BL,CS:[DLAB_REG]	;parity in bits 3 and 4
		MOV	CL,3
		SHR	BL,CL
		AND	BL,3
		XOR	BH,BH
		MOV	AL,BYTE PTR CS:[PSETTINGS][BX]
		MOV	CS:[PARITY],AL
;
;Determine the states of the CTS, DSR, DCD, and RI input pins.
;
		MOV	DX,CS:[COMPORT]		;point DX to Modem Status
		ADD	DX,6
		IN	AL,DX			;read register
		AND	AL,0F0H			;mask off the lower 4 bits
		SHR	AL,1			;then save them
		SHR	AL,1
		MOV	CS:[PINS],AL
;
;Determine the states of the RTS and DTR output pins.
;
		SUB	DX,2			;point DX to Modem Control
		IN	AL,DX			;read register
		AND	AL,3			;strip the upper 6 bits
		OR	CS:[PINS],AL		;store in PINS byte
		RET
SETPARMS	ENDP

;-----------------------------------------------------------------------------
; REFRESH is called by TIMERINT to display the status line.
;-----------------------------------------------------------------------------
REFRESH		PROC	NEAR
		MOV	AX,CS			;copy the underlying line
		MOV	DS,AX			;  into the status buffer
		MOV	ES,AX
		MOV	SI,OFFSET INITIALIZE
		MOV	DI,OFFSET INITIALIZE + 64
		MOV	CX,32
		CLD
		REP	MOVSW
;
;Construct the portion of the string pertaining to the 6 pins.
;
		MOV	SI,OFFSET PINTEXT		;point SI to text
		MOV	DI,OFFSET INITIALIZE + 64	;point DI to buffer
		MOV	BL,1
		MOV	AH,CS:[VIDEO_ATTR]
		MOV	CX,6			;6 pins to test
RLOOP1:		PUSH	CX
		PUSH	SI
		PUSH	DI
		TEST	CS:[PINS],BL		;copy pin name to buffer if
		JZ	PINLOW			;  corresponding bit is set
		MOV	CX,3
RLOOP2:		LODSB
		STOSW
		LOOP	RLOOP2
PINLOW:		POP	DI
		POP	SI
		SHL	BL,1
		ADD	SI,3
		ADD	DI,8
		POP	CX
		LOOP	RLOOP1			;loop unitl all 6 are done
;
;Add Baud rate, parity, data bits, and stop bits indicators to the string.
;
		MOV	AX,CS:[BAUDRATE]	;Baud rate
		CALL	BIN2ASC
		MOV	DI,OFFSET INITIALIZE + 64 + 58
		MOV	AL,CS:[PARITY]		;parity
		MOV	AH,CS:[VIDEO_ATTR]
		STOSW
		MOV	AL,CS:[DATABITS]	;data bits
		ADD	AL,30H
		STOSW
		MOV	AL,CS:[STOPBITS]	;stop bits
		ADD	AL,30H
		STOSW
;
;Display the status string.
;
		MOV	SI,OFFSET INITIALIZE + 64
		MOV	ES,CS:[VIDEOSEG]
		MOV	DI,CS:[VIDEOADDR]
		MOV	CX,32
		CALL	VIDEO_XFER
		RET
REFRESH		ENDP

;-----------------------------------------------------------------------------
; VIDEO_XFER transfers a block of data to or from the video buffer.
;   Entry:  DS:SI - source
;           ES:DI - destination
;           CX    - number of words
;-----------------------------------------------------------------------------
VIDEO_XFER	PROC	NEAR
		CLD				;clear DF
		TEST	CS:[SYNCFLAG],1		;don't wait if this isn't
		JZ	NOWAIT			;  a CGA
		MOV	DX,3DAH			;wait if it is
MWAIT1:		IN	AL,DX			;wait for non-retrace period
		TEST	AL,8
		JNZ	MWAIT1
MWAIT2:		IN	AL,DX			;wait for the next vertical
		TEST	AL,8			;  retrace
		JZ	MWAIT2
NOWAIT:		CLI				;interrupts off
		REP	MOVSW			;block move
		STI				;interrupts back on
		RET
VIDEO_XFER	ENDP

;-----------------------------------------------------------------------------
; BIN2ASC converts a single binary word value into its ASCII decimal
; equivalent and writes it to the designated output buffer.
;   Entry:  AX    - binary value
;           ES:DI - destination
;-----------------------------------------------------------------------------
BASE		DW	10			;base 10 divisor

BIN2ASC		PROC	NEAR
		XOR	CX,CX			;zero digit counter
BALOOP1:	INC	CX			;increment counter
		XOR	DX,DX
		DIV	CS:[BASE]		;divide by base
		PUSH	DX			;save remainder on stack
		OR	AX,AX			;loop until quotient is zero
		JNZ	BALOOP1
BALOOP2:	POP	AX			;pull digits back off the
		ADD	AL,30H			;  stack and output them
		MOV	AH,CS:[VIDEO_ATTR]
		STOSW
		LOOP	BALOOP2
		RET
BIN2ASC		ENDP

;=============================================================================
; INITIALIZE installs or uninstalls the program.
;=============================================================================
ERRMSG1		DB	"Usage: LITES [comport] [U]$"
ERRMSG2		DB	"Not Installed$"
ERRMSG3		DB	"Cannot Uninstall$"
ERRMSG4		DB	"Already Installed$"
ERRMSG5		DB	"Invalid COM Port$"
OUTTEXT		DB	"Uninstalled$"
INSTALLED	DB	0

INITIALIZE	PROC	NEAR
		ASSUME	CS:CODE, DS:CODE
;
;See if a copy of LITES is already resident in memory.
;
		CLD				;clear DF for string ops
		MOV	WORD PTR [BEGIN],0	;initialize fingerprint
		XOR	BX,BX			;zero BX for start
		MOV	AX,CS			;keep CS value in AX
INIT1:		INC	BX			;increment search segment value
		MOV	ES,BX
		CMP	AX,BX			;not installed if current
		JE	PARSE1			;  segment is reached
		MOV	SI,OFFSET BEGIN		;search this segment for ASCII
		MOV	DI,SI			;  fingerprint
		MOV	CX,16
		REPE	CMPSB
		JNE	INIT1			;loop back if not found
		MOV	INSTALLED,1		;set installed flag
;
;Parse the command line for entries.
;
PARSE1:		MOV	SI,81H			;point SI to command line
PARSE2:		LODSB				;get a character
		CMP	AL,20H			;skip it if it's a space
		JE	PARSE2
		CMP	AL,0DH			;exit loop when a carriage
		JE	INSTALL			;  return is encountered
		CMP	AL,"0"			;branch if numeral is found
		JB	ERROR1
		CMP	AL,"9"
		JBE	CHECK_COM
		AND	AL,0DFH			;capitalize the character
		CMP	AL,"U"			;branch to uninstall code if
		JE	UNINSTALL		;  character is a "U"
;
;An error was encountered in parsing.  Display error message and exit.
;
ERROR1:		MOV	DX,OFFSET ERRMSG1	;load message address
ERROR2:		MOV	AH,9			;display error message
		INT	21H
		MOV	AX,4C01H		;exit with ERRORLEVEL = 1
		INT	21H
;
;Make sure the COM port entered on the command line exists.
;
CHECK_COM:	SUB	AL,"1"			;normalize the entry
		MOV	BL,AL			;save it
		INT	11H			;determine number of COM
		SHR	AH,1			;  ports installed
		AND	AH,07H
		MOV	DX,OFFSET ERRMSG5
		CMP	AH,BL			;exit on error if COM port
		JNA	ERROR2			;  number is invalid
		MOV	BYTE PTR COMPORT,BL	;save port designator
		JMP	SHORT INSTALL		;go to install routine
;
;Uninstall the program.
;
UNINSTALL:	MOV	DX,OFFSET ERRMSG2	;error if program isn't
		CMP	INSTALLED,0		;  installed
		JE	ERROR2
		CALL	REMOVE			;call uninstall routine
		MOV	DX,OFFSET ERRMSG3	;error if uninstall failed
		JC	ERROR2
		MOV	DX,OFFSET OUTTEXT	;display "Uninstalled"
		MOV	AH,9			;  message
		INT	21H
		MOV	AX,4C00H		;exit with ERRORLEVEL = 0
		INT	21H
;
;Make sure LITES isn't already installed, then get the UART base address.
;
INSTALL:	MOV	DX,OFFSET ERRMSG4	;exit on error if program
		CMP	INSTALLED,0		;  is already installed
		JNE	ERROR2
		MOV	AX,40H			;get UART address from BIOS
		MOV	ES,AX			;  data area using the port
		XOR	DI,DI			;  number as an index into
		MOV	BX,COMPORT		;  the table
		SHL	BX,1
		MOV	AX,WORD PTR ES:[DI][BX]
		MOV	COMPORT,AX
;
;Hook into interrupts 9h and 8h and deallocate the environment block.
;
		MOV	AX,3509H		;save old interrupt 9h vector
		INT	21H
		MOV	WORD PTR INT09H,BX
		MOV	WORD PTR INT09H[2],ES
		MOV	AX,2509H		;then set the new 9h vector
		MOV	DX,OFFSET KBINT
		INT	21H
		MOV	AX,3508H		;save old interrupt 8h vector
		INT	21H
		MOV	WORD PTR INT08H,BX
		MOV	WORD PTR INT08H[2],ES
		MOV	AX,2508H		;then set the new 8h vector
		MOV	DX,OFFSET TIMERINT
		INT	21H
		MOV	AX,DS:[2CH]		;deallocate the program's
		MOV	ES,AX			;  environment block
		MOV	AH,49H
		INT	21H
;
;Display copyright notice, then terminate and remain resident in memory.
;
		MOV	AH,9			;display copyright and hotkey
		MOV	DX,OFFSET PROGRAM	;  information
		INT	21H
		MOV	AX,3100H
		MOV	DX,(OFFSET INITIALIZE - OFFSET CODE + 15 + 128) SHR 4
		INT	21H
INITIALIZE	ENDP

;-----------------------------------------------------------------------------
; REMOVE deallocates the memory block addressed by ES and restores the
; interrupt vectors displaced on installation.
;   Entry:  ES - segment to release
;   Exit:   CF clear - program uninstalled
;	    CF set   - can't uninstall
;-----------------------------------------------------------------------------
REMOVE          PROC    NEAR
		MOV	CX,ES			;abort if either interrupt
		MOV	AX,3509H		;  vector has been altered
		INT	21H			;  since installation
		MOV	AX,ES
		CMP	AX,CX
		JNE	REMOVE_ERROR
		MOV	AX,3508H
		INT	21H
		MOV	AX,ES
		CMP	AX,CX
		JNE	REMOVE_ERROR
;
;Free memory given to the original program block.
;
		MOV	ES,CX			;Ask DOS to free it
		MOV     AH,49H
		INT     21H
		JC	REMOVE_ERROR		;exit if call failed
;
;Restore the interrupt 9h and 8h vectors to their installation values.
;
		PUSH    DS
		ASSUME  DS:NOTHING
		MOV	AX,2509H		;restore interrupt 9h vector
		LDS	DX,ES:[INT09H]
		INT	21H
		MOV	AX,2508H		;restore interrupt 8h vector
		LDS	DX,ES:[INT08H]
		INT	21H
		POP     DS
		ASSUME  DS:CODE
		NOT     WORD PTR ES:[BEGIN]     ;destroy ASCII fingerprint
		CLC				;clear CF for exit
		RET
REMOVE_ERROR:	STC				;set CF to indicate program
		RET				;  couldn't be uninstalled
REMOVE          ENDP

CODE		ENDS
		END	BEGIN
[ RETURN TO DIRECTORY ]