;============================================================================= ; 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