home *** CD-ROM | disk | FTP | other *** search
- ;
- ;
- TITLE * NOISE.ASM
- ;
- ;
- COMMENT *
- Purpose: Checks the transmission quality of phone lines.
-
- Created: 28-NOV-1988 V1.00 Richard B. Johnson
-
- Modified:
- *
- FALSE EQU 0
- TRUE EQU NOT FALSE
- TESTING EQU FALSE ; "TRUE" disables carrier-det.
- CR EQU 0DH ; ASCII
- LF EQU 0AH
- MS_DOS EQU 21H ; Dos function calls
- MAX EQU 256 ; Max bytes per block
- ;
- ; ASCII structures
- ;
- VERS STRUC
- DB 'V1.00'
- @VERS DB ' '
- VERS ENDS
- HEAD1 STRUC
- DB ' C O M M U N I C A T I O N S'
- @HEAD1 DB ' '
- HEAD1 ENDS
- ;
- HEAD2 STRUC
- DB ' Line Transmission Quality Monitor'
- @HEAD2 DB ' '
- HEAD2 ENDS
- ;
- HEAD3 STRUC
- DB ' Version'
- @HEAD3 DB ' '
- HEAD3 ENDS
- ;
- HEAD4 STRUC
- DB ' Created 2-DEC-1988 Richard B. Johnson'
- @HEAD4 DB ' '
- HEAD4 ENDS
- ;
- HEAD5 STRUC
- DB ' - Use Control-C or Control-Break to end -'
- @HEAD5 DB ' '
- HEAD5 ENDS
- ;
- NRM STRUC
- DB 27,'[0m'
- NRM ENDS
- ;
- REV STRUC
- DB 27,'[7m'
- REV ENDS
- ;
- SPACES STRUC
- DB 15 DUP (' ')
- SPACES ENDS
- ;
- ; Interrupt control equates.
- ;
- INT_CTL EQU 21H ; Interrupt controller address
- INT_NOS EQU 20H ; Non-specific end-of-interrupt
- INT_RES EQU 20H ; Interrupt controller reset addr.
- INT_1 EQU 0CH ; For COM1
- INT_2 EQU 0BH ; For COM2
- IRQ4 EQU 11101111B ; For COM1
- IRQ3 EQU 11110111B ; For COM2
- ;
- ; 8250 UART equates.
- ;
- MOD_STA EQU 6 ; Offset to modem-status register
- LIN_STA EQU 5 ; Offset to line-status register
- MOD_CTL EQU 4 ; Offset to modem-control register
- LIN_CTL EQU 3 ; Offset to line-control register
- INT_IDR EQU 2 ; Interrupt identification register
- INT_ENA EQU 1 ; Offset to interrupt-enable reg
- INT_RC EQU 00000001B ; Interrupt on received chars
- INT_TX EQU 00000010B ; Interrupt on TX empty
- INT_MS EQU 00001000B ; Interrupt on modem status change
- DR EQU 00000001B ; Data ready
- DTR EQU 00000001B ; Data terminal ready
- RTS EQU 00000010B ; Request to send
- NORM EQU 00000011B ; 8-bits, no parity, 1-stop bit
- TRI EQU 00001100B ; Tri-state enable
- THRE EQU 00100000B ; Transmitter holding register ready.
- RLSD EQU 10000000B ; Received line signal detect
- ;
- ; Structures for communications adapters.
- ;
- COM1 STRUC
- BPRT DW 03F8H ; Base port
- INTN DB INT_1 ; Interrupt number
- IMSK DB IRQ4 ; Controller mask
- COM1 ENDS
- ;
- COM2 STRUC
- COM2PRT DW 02F8H ; Base port
- COM2INT DB INT_2 ; Interrupt number
- COM2MSK DB IRQ3 ; Controller mask
- COM2 ENDS
- ;
- COM3 STRUC
- COM3PRT DW 03E8H ; Base port
- COM3INT DB INT_1 ; Interrupt number
- COM3MSK DB IRQ4 ; Controller mask
- COM3 ENDS
- ;
- COM4 STRUC
- COM4PRT DW 02E8H ; Base port
- COM4INT DB INT_2 ; Interrupt number
- COM4MSK DB IRQ3 ; Controller mask
- COM4 ENDS
- ;
- ; - MACROS -
- ; Save interrupt vector(s) address
- ;
- SAVE MACRO ADDR,VECT
- LOCAL VECT,ADDR
- PUSH ES ; Save segment
- MOV AH,35H ; Get vector function
- IFNB <VECT>
- MOV AL,VECT ; Interrupt number
- ENDIF
- INT MS_DOS ; Get the vector, returns in ES:BX
- MOV WORD PTR [ADDR],BX ; Save the offset
- MOV WORD PTR [ADDR+2],ES ; Save the segment
- POP ES ; Restore the segment
- ENDM
- ;
- ; Set an interrupt vector address
- ;
- SET MACRO ADDR,VECT
- LOCAL VECT,ADDR
- MOV AH,25H ; Set vector function
- IFNB <VECT>
- MOV AL,VECT ; Interrupt number
- ENDIF
- MOV DX,OFFSET ADDR ; Point to address
- INT MS_DOS ; Set the vector, returns in ES:BX
- ENDM
- ;
- ; Restore previously-saved interrupt vectors.
- ;
- RESTORE MACRO ADDR,VECT
- LOCAL VECT,ADDR
- MOV AH,25H ; Set vector function
- IFNB <VECT>
- MOV AL,VECT ; Interrupt number
- ENDIF
- PUSH DS ; Save data segment
- LDS DX,ADDR ; Get old address
- INT MS_DOS ; Set the vector
- POP DS
- ENDM
- ;
- ; Index into the table of communications port parameters
- ;
- INDEX MACRO PARAM,REG
- LOCAL PARAM,REG
- MOV BX,WORD PTR [COM] ; Get port number index
- SHL BX,1 ; Times 2
- SHL BX,1 ; Times 4
- ADD BX,OFFSET TABLE+PARAM ; Offset into the table
- MOV REG,[BX]
- ENDM
- ;
- ; Reset interrupt controller.
- ;
- RESET MACRO
- MOV AL,INT_NOS ; Non-specific end-of-interrupt
- OUT INT_RES,AL ; Reset the controller
- ENDM
- ;
- ; Program code segment.
- ;
- PSEG SEGMENT PARA PUBLIC 'CODE'
- START EQU $ ; Equate for stack offset
- ASSUME CS:PSEG, DS:PSEG, ES:PSEG, SS:NOTHING
- ORG 100H ; Entry point
- MAIN PROC NEAR
- MOV SP,OFFSET STKTOP ; Keep in a safe place
- MOV WORD PTR [IBP],OFFSET BUFFER ; Set RX buffer pointer
- MOV WORD PTR [TBP],OFFSET DBUFF ; Set TX buffer pointer
- CALL CHK_CMD ; Check command line
- CALL CHK_COM ; Check for communications port
- CALL SET_INT ; Set up interrupts
- CALL SET_PRT ; Set up communications port
- CMP BYTE PTR [MODE],0 ; Check for mode
- JZ MAIN0 ; Normal user-test mode
- MOV BYTE PTR [STOP],'$' ; Put in a stopper
- MOV DX,OFFSET PRP0 ; Point to heading
- CALL PROMPT ; Print to screen
- MOV DX,OFFSET PRP6 ; Point to the message
- MOV SI,DX ; Same location
- CALL PROMPT ; Print to the screen
- MOV DI,OFFSET DBUFF ; Where to put the string
- MOV CX,INF_LEN ; Length of string
- REP MOVSB ; Put into the buffer
- MOV CX,INF_LEN ; Length of string
- CALL SEND ; Send prompt string to remote
- CALL MIRROR ; Remote mirror mode
- MOV SI,OFFSET PRP7 ; Point to 'reloading BBS' prompt
- MOV DI,OFFSET DBUFF ; Where to put the string
- MOV CX,REL_LEN ; Length of the string
- REP MOVSB ; Put into the buffer
- MOV CX,REL_LEN ; Length of the string
- CALL SEND ; Send to remote
- JMP SHORT MAIN1 ; Exit
- MAIN0: CALL TST_LIN ; Test the line
- MAIN1: CALL TX_WAIT ; Wait for TX buffer to clear
- CALL RES_PRT ; Restore port parameters
- CALL RES_INT ; Restore interrupts
- JMP FINIS ; Exit the program
- MAIN ENDP
- ;
- ; Get the modem status of the UART and save it.
- ;
- CHK_COM PROC NEAR
- INDEX BPRT DX ; Get base port
- MOV WORD PTR [BASE],DX ; Set it up
- ADD DX,MOD_STA ; Offset to modem status
- IN AL,DX ; Get modem status
- MOV BYTE PTR [STATUS],AL ; Save present status
- RET
- CHK_COM ENDP
- ;
- ; Set the communications port up for interrupt control.
- ;
- SET_PRT PROC NEAR
- MOV DX,WORD PTR [BASE] ; Get base port
- MOV BX,DX ; Save port
- ADD DX,INT_ENA ; Pick up interrupt enable register
- IN AL,DX ; Get the enable mask
- MOV BYTE PTR [OLD_UAR],AL ; Save old mask
- MOV AL,(INT_RC OR INT_MS) ; Mask for UART interrupts
- OUT DX,AL ; Set interrupt enable mask
- MOV DX,BX ; Get base port again
- ADD DX,MOD_CTL ; To modem-control register
- MOV AL,(RTS OR DTR OR TRI) ; Turn on output lines
- OUT DX,AL ; Set modem control
- MOV DX,BX ; Get base port again
- ADD DX,LIN_CTL ; Line control register
- MOV AL,NORM ; 8 bits, 1 stop, no parity
- OUT DX,AL ; Set parameters
- XOR CX,CX ; 64k (really)
- LOOP $ ; Spin 64k
- MOV DX,BX ; Get base port again
- MOV CX,6 ; Six registers to read
- RD_ALL: IN AL,DX ; Get byte/ throw away
- RESET ; Could interrupt, reset controller
- INC DX ; Ready next port
- LOOP RD_ALL ; Continue for all ports
- RET
- SET_PRT ENDP
- ;
- ; Restore communications port parameters.
- ;
- RES_PRT PROC NEAR
- MOV DX,WORD PTR [BASE] ; Get base port
- ADD DX,INT_ENA ; Pick up interrupt eable register
- MOV AL,BYTE PTR [OLD_UAR] ; Get old UART mask
- OUT DX,AL ; Restore the mask
- SUB DX,INT_ENA ; Back to base port
- MOV CX,6 ; Six registers to read
- RD_AL: IN AL,DX ; Get byte/ throw away
- INC DX ; Ready next port
- LOOP RD_AL ; Continue for all ports
- RET
- RES_PRT ENDP
- ;
- ; Check the command line to determine if we are master/slave.
- ;
- CHK_CMD PROC NEAR
- CMP BYTE PTR DS:[80H],0 ; See is anything typed
- JNZ CONT0 ; Yes
- RET ; No, use defaults
- CONT0: MOV SI,80H ; Point to command line
- LODSB ; Get bytes typed
- CBW
- MOV CX,AX ; Use as a count
- PUSH CX ; Save count
- MOV DI,OFFSET DBUFF ; Where to put the string
- CALL MAP ; Map to upper case
- POP CX ; Restore count
- PUSH CX ; Save again
- MOV SI,OFFSET DBUFF ; Where the string is
- MOV DX,OFFSET AS_COM ; Point to the string to look for
- MOV BX,SER_LEN ; Get search length
- CALL SEARCH ; Search the string
- JNZ CONT1 ; Not fount
- LODSB ; Get found byte
- SUB AL,'0' ; Remove ASCII bias
- JBE CONT1 ; Out of range
- DEC AL ; Change to an offset
- JZ CONT1 ; Out of range
- CMP AL,3 ; Check high range
- JG CONT1 ; Out of range
- CBW
- MOV WORD PTR [COM],AX ; New com-port offset
- CONT1: POP CX ; Restore string length
- MOV SI,OFFSET DBUFF ; Where the string is
- MOV DX,OFFSET AS_MOD ; Point to the string to look for
- MOV BX,SER_LEN ; Get search length
- CALL SEARCH ; Search the string
- JNZ CONT2 ; Not found
- LODSB ; Get found byte
- CMP AL,'M' ; Want to mirror?
- JNZ CONT2 ; No
- MOV BYTE PTR [MODE],0FFH ; Set mirror flag
- CONT2: RET
- CHK_CMD ENDP
- ;
- ; Set up the interrupt vectors.
- ;
- SET_INT PROC NEAR
- SAVE OLD_CLK 1CH ; Save old clock vector
- SAVE OLD_BRK 1BH ; Save old break vector
- SAVE OLD_CTC 23H ; Save old ^C vector
- INDEX INTN AL ; Get interrupt number
- SAVE OLD_PRT ; Save old interrupt contents
- ;
- SET NEW_CLK 1CH ; Set new clock vector
- SET NEW_BRK 1BH ; Set new break vector
- SET NEW_CTC 23H ; Set new ^C vector
- INDEX INTN AL ; Get interrupt number
- SET NEW_PRT ; Set new interrupt vector.
- ;
- IN AL,INT_CTL ; Get present interrupt mask
- MOV BYTE PTR [OLD_MSK],AL ; Save old interrupt mask
- INDEX IMSK AH ; Get interrupt mask
- AND AL,AH ; Reset the bit
- OUT INT_CTL,AL ; Un-mask the controller
- RET
- SET_INT ENDP
- ;
- ; Restore interrupt vectors to their like-new condition.
- ;
- RES_INT PROC NEAR
- MOV AL,BYTE PTR [OLD_MSK] ; Get old interrupt mask
- OUT INT_CTL,AL ; Restore old mask
- RESTORE OLD_CLK 1CH ; Restore old clock vector
- RESTORE OLD_BRK 1BH ; Restore old break vector
- RESTORE OLD_CTC 23H ; Restore old ^C vector
- INDEX INTN AL ; Get interrupt number
- RESTORE OLD_PRT ; Restore old interrupt contents
- RET
- RES_INT ENDP
- ;
- ; Clear the interrupt buffer.
- ;
- CLR_BUF PROC NEAR
- MOV WORD PTR [IBP],OFFSET BUFFER ; Reset buffer pointer
- RET
- CLR_BUF ENDP
- ;
- ; Test the line. If we are the slave, simply loop to send everything
- ; received. Abort when a control-C is received.
- ;
- TST_LIN PROC NEAR
- MOV DX,OFFSET PRP0 ; Point to sign-on
- CALL PROMPT ; Print to screen
- TSTLN0: MOV CX,MAX ; Bytes to send
- MOV DI,OFFSET DBUFF ; Where to build the string
- XOR AL,AL ; Start at zero
- TEST0: CMP AL,'C'-64 ; Don't send ^C
- JNZ OKAY ; Was okay
- INC AL ; Next character
- OKAY: STOSB ; Put character in the buffer
- INC AL ; Ready next character
- LOOP TEST0 ; Continue for whole string
- ;
- TEST1: CMP BYTE PTR [ABORT],0 ; Check for an abort
- JZ TEST2 ; Abort
- MOV CX,10 ; Bytes to send
- MOV DI,OFFSET DBUFF ; Where to build string
- MOV AL,'C'-64 ; Control-C
- REP STOSB ; Put in buffer
- MOV CX,10 ; Bytes to send
- CALL SEND ; Send them
- RET
- ;
- TEST2: CALL CLR_BUF ; Clear receive buffer
- MOV CX,MAX ; Bytes to send
- CALL SEND ; Send them
- MOV DX,WORD PTR [GD_BD] ; Show good/bad data
- CALL PROMPT ; Print to screen
- MOV DX,OFFSET PRP3 ; Point to stats
- CALL PROMPT ; Print to screen
- MOV DI,OFFSET ERRAV ; Point to average error-rate str
- CALL RATE ; Convert
- MOV DX,WORD PTR [TOTTX_H] ; Get bytes transmitted H
- MOV AX,WORD PTR [TOTTX_L] ; Get bytes transmitted L
- SUB AX,WORD PTR [TOTRX_L] ; Subtract low bytes received
- SBB DX,WORD PTR [TOTRX_H] ; Take care of borrow
- MOV DI,OFFSET BBYTE ; Where to build the string
- CALL ASCII ; Convert to ASCII
- INC WORD PTR [BLKN] ; Bump count
- ADD WORD PTR [TOTTX_L],MAX ; Update count
- ADC WORD PTR [TOTTX_H],0 ; Update high word
- MOV DI,OFFSET BLKS ; Where to build the string
- XOR DX,DX ; Zero high word
- MOV AX,WORD PTR [BLKN] ; Get the block number
- CALL ASCII ; Build the strin
- MOV DI,OFFSET BTX ; Where to build the string
- MOV DX,WORD PTR [TOTTX_H] ; Get high word
- MOV AX,WORD PTR [TOTTX_L] ; Get low word
- CALL ASCII ; Convert
- ;
- IF (NOT TESTING)
- TEST BYTE PTR [STATUS],RLSD ; Get modem status
- JNZ CAR_OK ; Carrier okay
- MOV DX,OFFSET PRP5 ; Point to 'carrier failed
- CALL PROMPT ; Print to screen
- RET ; Exit
- ENDIF
- ;
- CAR_OK: MOV CX,MAX ; Bytes to receive
- CALL RECV ; Receive the bytes
- PUSH AX ; Save for now
- ADD WORD PTR [TOTRX_L],AX ; Update count
- ADC WORD PTR [TOTRX_H],0 ; Update high word
- MOV DI,OFFSET BRX ; Where to build the string
- MOV DX,WORD PTR [TOTRX_H] ; Get high word
- MOV AX,WORD PTR [TOTRX_L] ; Get low word
- CALL ASCII ; Convert
- POP AX ; Restore received byte count
- CMP AX,MAX ; Get them all?
- JNZ BAD ; No
- CALL CHK_BUF ; Check buffer bytes
- JNZ BAD ; Bad data
- MOV WORD PTR [GD_BD],OFFSET PRP1
- JMP TEST1 ; Continue
- ;
- BAD: MOV WORD PTR [GD_BD],OFFSET PRP2
- MOV DI,OFFSET LBRX ; String for received bytes
- CALL BINARY ; Convert AL to binary-ASCII
- MOV DI,OFFSET LBTX ; String for transmitted bytes
- MOV AL,AH ; Get what bytes should be
- CALL BINARY
- MOV DI,OFFSET ERRAT ; Point to peak error-rate string
- CALL RATE ; Calculate
- JMP TSTLN0 ; Continue
- TST_LIN ENDP
- ;
- ; Check the bytes in the buffer to see if they are the same as
- ; transmitted. Subtract bytes after the first mis-compare from the
- ; total received bytes.
- ;
- CHK_BUF PROC NEAR
- MOV AH,0 ; Start with a NULL
- MOV SI,OFFSET DBUFF ; Point to data buffer
- MOV CX,MAX ; Bytes to check
- CHK_B0: LODSB ; Get buffered byte
- CMP AH,'C'-64 ; Compare with ^C
- JNZ CHK_B1 ; No
- INC AH ; Bypass ^C
- CHK_B1: CMP AH,AL ; Check for a match
- JNZ NOGOOD ; No match
- INC AH ; Ready next check byte
- LOOP CHK_B0 ; Continue
- XOR AH,AH ; Set flags for okay
- NOGOOD: PUSHF ; Save flage
- SUB WORD PTR [TOTRX_L],CX ; Update count
- SBB WORD PTR [TOTRX_H],0 ; Update high word
- POPF ; Restore flags
- RET
- CHK_BUF ENDP
- ;
- ; Calculate the error rate.
- ;
- RATE PROC NEAR
- MOV AX,WORD PTR [TOTTX_L] ; Total bytes transmitted
- MOV DX,WORD PTR [TOTTX_H] ; Total bytes transmitted
- MOV BX,WORD PTR [TOTRX_L] ; Total bytes received
- MOV CX,WORD PTR [TOTRX_H] ; Total bytes received
- ;
- RATE0: CMP DX,0 ; Check for register clear
- JNZ HIGH0 ; Not clear
- CMP CX,0 ; Check for register clear
- JZ RATE3 ; Its clear
- HIGH0: CLC
- RCR DX,1 ; Shift the high bits over
- RCR AX,1 ; Shift the low bits over
- RATE1: CLC
- RCR CX,1 ; Move over high bits
- RCR BX,1 ; Move low bits over
- RATE2: JMP SHORT RATE0 ; Continue
- RATE3: CMP AX,0 ; Check for div by zero
- JNZ RATE4 ; Not zero, continue
- RET ; Zero, exit
- ;
- RATE4: PUSH AX ; Save bytes transmitted
- SUB AX,BX ; AX = bad bytes
- MUL WORD PTR [_10000] ; Scale the bad bytes
- POP BX ; Restore bytes transmitted
- DIV BX
- CALL PERCENT ; Conv
- RET
- RATE ENDP
- ;
- ; Local clock vector.
- ;
- NEW_CLK PROC FAR
- PUSH AX ; Save register
- RESET ; Reset the controller
- POP AX
- STI ; Allow interrupts
- SUB WORD PTR CS:[CLOCK],1 ; Bump the clock
- ADC WORD PTR CS:[CLOCK],0 ; If we went below zero
- JMP DWORD PTR CS:[OLD_CLK] ; Continue to old address
- NEW_CLK ENDP
- ;
- ; Local control-C vector
- ;
- NEW_CTC PROC FAR
- PUSH AX ; Save register
- RESET ; Reset the controller
- POP AX
- MOV BYTE PTR CS:[ABORT],0FFH ; Set flag
- IRET
- NEW_CTC ENDP
- ;
- ; Local control-BRK vector
- ;
- NEW_BRK PROC FAR
- PUSH AX ; Save register
- RESET ; Reset the controller
- POP AX
- MOV BYTE PTR CS:[ABORT],0FFH ; Set flag
- IRET
- NEW_BRK ENDP
- ;
- ; Local modem port interrupt vector.
- ;
- NEW_PRT PROC NEAR
- PUSH AX ; Save registers used
- PUSH DX
- PUSH DS
- ;
- PUSH CS
- POP DS ; DS=CS
- MOV DX,WORD PTR [BASE] ; Get base port
- ADD DX,INT_IDR ; Offset to Interrupt ident
- IN AL,DX ; Get status byte
- TEST AL,00000001B ; Check if our interrupt
- JNZ INT_END ; No
- CMP AL,00000110B ; Is it prior (1) ?
- JZ INT_END ; Yes, Just exit (was not enabled)
- MOV DX,OFFSET INT_END ; Get 'return address'
- PUSH DX ; Push on stack
- TEST AL,00000100B ; Got a byte?
- JNZ GET_DAT ; Yes
- TEST AL,00000010B ; TX holding register empty
- JNZ PUT_DAT ; Yes
- JMP SHORT GET_STA ; Must be modem status
- ;
- INT_END:
- RESET ; Reset the controller
- STI ; Allow interrupts
- POP DS ; Restore registers used
- POP DX
- POP AX
- IRET
- NEW_PRT ENDP
- ;
- ; Get Data from UART. Put in memory. Update count and reset timeout.
- ; According to the 8250 UART documentation by Western Digital, the
- ; reset condition is obtained by reading a byte from the RX buffer.
- ; Since a byte must have been completely assembled by the UART for
- ; this interrupt to be generated, it is not necessary to read the
- ; line-status register to determine if the byte is ready.
- ;
- GET_DAT PROC NEAR
- PUSH BX ; Save index
- MOV DX,WORD PTR [BASE] ; Get the base port
- IN AL,DX ; Get the byte
- MOV BX,WORD PTR [IBP] ; Get buffer pointer
- MOV BYTE PTR [BX],AL ; Put byte in the buffer
- ADD WORD PTR [IBP],1 ; Ready next
- SBB WORD PTR [IBP],0 ; Don't wrap around segment!
- MOV WORD PTR [CLOCK],18 ; Reset the clock
- POP BX ; Restore index
- RET
- GET_DAT ENDP
- ;
- ; Send byte addressed by [TBP] out via the UART. Update address.
- ; When all bytes have been transmitted, disable the interrupt mask.
- ; According to the 8250 documentation by Western Digital, the reset
- ; condition of the TX buffer empty interrupt is obtained simply by
- ; reading the interrupt identification register or writing to the
- ; TX output register. Therefore, it is not necessary to reset the
- ; interrupt-enable mask when the complete output string has been sent.
- ; Unfortunately, with some UARTs, this is not always the case. Failure
- ; to disable the mask will cause continuous interrupts to be generated,
- ; resulting in a stack-overflow and crash or, at the least, a severe
- ; reduction in through-put with many received characters being lost.
- ;
- PUT_DAT PROC NEAR
- PUSH BX ; Save index
- MOV DX,WORD PTR [BASE] ; Get base port
- MOV BX,WORD PTR [TBP] ; Get TX buffer pointer
- CMP BX,WORD PTR [COMP] ; Check limits
- JC SND_OK ; Output the byte
- ADD DX,INT_ENA ; Offset to interrupt enable
- IN AL,DX ; Get UART enable mask
- AND AL,(NOT INT_TX) ; Reset the TX enable bit
- OUT DX,AL ; To UART
- JMP SHORT NO_SND ; Home, James
- SND_OK: MOV AL,BYTE PTR [BX] ; Get buffer byte
- OUT DX,AL ; Send it out
- INC WORD PTR [TBP] ; Ready next
- NO_SND: POP BX ; Restore index
- RET
- PUT_DAT ENDP
- ;
- ; Get/save modem status. This interrupt is generated at any time the
- ; modem-status changes. We will simply save the changed status-byte
- ; so the program may read it at some later time.
- ;
- GET_STA PROC NEAR
- MOV DX,WORD PTR [BASE] ; Pick up base port
- ADD DX,MOD_STA ; Offset to modem status
- IN AL,DX ; Get modem status
- MOV BYTE PTR [STATUS],AL ; Save modem carrier status
- RET
- GET_STA ENDP
- ;
- ; Check receive status. Return DX=byte count. ZF = no bytes
- ;
- CHK_STA PROC NEAR
- MOV DX,WORD PTR [IBP] ; Get the byte pointer
- SUB DX,OFFSET BUFFER ; Starting address
- RET
- CHK_STA ENDP
- ;
- ; Get CX bytes from interrupt buffer and put at address DBUFF.
- ; Upon return, AX shows actual bytes transferred. Return in
- ; 1 second if not all bytes received.
- ;
- RECV PROC NEAR
- MOV SI,OFFSET BUFFER ; Where the bytes should be
- MOV DI,OFFSET DBUFF ; Where to put the bytes
- XOR DX,DX ; Zero byte count
- MOV WORD PTR [CLOCK],18 ; Set the clock
- AGAIN: CMP WORD PTR [CLOCK],0 ; Have we timed out?
- JZ SOMEB ; Yes
- CALL CHK_STA ; Check for bytes available
- CMP DX,CX ; Same as requested?
- JC AGAIN ; No wait
- ;
- SOMEB: MOV CX,DX ; Get real byte count
- JCXZ NONE ; No bytes
- REP MOVSB ; Transfer the bytes
- ;
- NONE: MOV AX,DI ; Get ending address
- SUB AX,OFFSET DBUFF ; Subtract starting address
- RET
- RECV ENDP
- ;
- ; Send CX bytes, from buffer DBUFF. Set the transmitter-buffer-pointer
- ; [TBP] to the first byte in the buffer. Calculate the completion-
- ; address [COMP] by adding in the byte-count. Enable the TX interrupt
- ; mask in the UART. If an interrupt is immediately, generated, exit the
- ; routine. Otherwise, send the first byte by calling the output routine.
- ;
- SEND PROC NEAR
- CALL TX_WAIT ; Wait for previous TX to complete
- MOV BX,OFFSET DBUFF ; Point to the buffer
- MOV WORD PTR [TBP],BX ; Set TX buffer pointer
- ADD BX,CX ; Calc last address
- MOV WORD PTR [COMP],BX ; Set ending address
- MOV DX,WORD PTR [BASE] ; Get base port
- ADD DX,INT_ENA ; Offset to interrupt enable reg
- SEND0: IN AL,DX ; Get UART mask
- OR AL,INT_TX ; Set UART for TX interrupt
- OUT DX,AL ; To UART should interrupt now!
- PUSH AX ; Give time to interrupt
- POP AX
- CMP WORD PTR [TBP],OFFSET DBUFF ; See if transfer started
- JNZ SEND1 ; Was interrupted, is working
- JMP PUT_DAT ; Must start it ourselves
- SEND1: RET ; - implied return -
- SEND ENDP
- ;
- ; Wait for TX to complete sending the string. If [TBP] and [COMP]
- ; are the same, transmission is complete. It may never complete so
- ; don't hang the system by waiting forever.
- ;
- TX_WAIT PROC NEAR
- PUSH CX ; Save count
- WAIT0: MOV BX,WORD PTR [TBP] ; Get the transmitter buffer pointer
- XOR CX,CX ; 64k spin
- WAIT1: CMP BX,WORD PTR [COMP] ; Check completion pointer
- JNC WAIT2 ; TX is complete
- LOOP WAIT1 ; Continue checking
- CMP BX,WORD PTR [TBP] ; See if pointer changed
- JNZ WAIT0 ; Yes, continue wait
- WAIT2: POP CX ; Restore count
- RET
- TX_WAIT ENDP
- ;
- ; Terminate the program.
- ;
- FINIS PROC NEAR
- MOV DX,OFFSET PRP4 ; Restore cursor position
- CALL PROMPT ; Print to screen
- MOV AX,4C00H ; Normal exit
- INT MS_DOS
- FINIS ENDP
- ;
- ; Print string addressed by DX until '$'
- ;
- PROMPT PROC NEAR
- MOV AH,9
- INT MS_DOS
- RET
- PROMPT ENDP
- ;
- ; Convert binary in AL to ASCII-binary addressed by [DI]. DI and
- ; CX is destroyed. All others saved.
- ;
- BINARY PROC NEAR
- PUSH AX
- MOV CX,8 ; Number of bits to display.
- MOV AH,AL ; Save the number
- BIN0: MOV AL,'0' ; Assume a '0'
- RCR AH,1 ; Into the carry
- ADC AL,0 ; Becomes '1' if a carry
- STOSB ; Save in the string
- LOOP BIN0 ; Do all bits
- POP AX
- RET
- BINARY ENDP
- ;
- ; Create a 'percent' string from binary in AX. String is addressed
- ; by DI.
- ;
- PERCENT PROC NEAR
- MOV CX,10000
- CALL PSUB ; Subtract 10 thousands
- MOV BYTE PTR [DI],'.' ; Put in a decimal point
- INC DI ; Ready next destination
- MOV CX,1000 ; Subtract 1 thousands
- CALL PSUB
- MOV CX,100 ; Subtract hundreds
- CALL PSUB
- MOV CX,10 ; Subtract tens
- CALL PSUB
- ADD AL,'0' ; Add ASCII bias to remainder
- STOSB ; Save in string
- RET
- ;
- PSUB PROC NEAR
- MOV BL,'0'-1 ; ASCII bias - 1
- PSUB0: INC BL ; Powers of 10 counter
- SUB AX,CX ; Subtract powers of 10
- JNC PSUB0 ; Continue until too much
- ADD AX,CX ; Too much, add back
- MOV BYTE PTR [DI],BL ; Put ASCII byte in memory
- INC DI ; Ready next
- RET
- PSUB ENDP
- PERCENT ENDP
- ;
- ; Send any incoming byte out the modem port until a ^C
- ;
- MIRROR PROC NEAR
- CALL CLR_BUF ; Clear interrupt buffer
- MOV SI,OFFSET BUFFER ; Interrupt buffer
- MOV DI,OFFSET DBUFF ; Where to put new bytes when ready
- MIR1: TEST BYTE PTR [STATUS],RLSD ; Get modem status
- JNZ MIR2 ; Carrier okay
- IF (NOT TESTING)
- MOV DX,OFFSET PRP5 ; Point to 'carrier failed
- CALL PROMPT ; Print to screen
- RET ; Dead, exit
- ENDIF
- ;
- MIR2: CMP BYTE PTR [ABORT],0 ; Do we want to abort
- JZ MIR3 ; No
- ;
- BYE: MOV DI,OFFSET DBUFF ; Data buffer
- MOV AL,'C'-64 ; Control-C
- MOV CX,10 ; Bytes to send
- REP STOSB ; Put in the buffer
- MOV CX,10 ; 10 Bytes to send
- CALL SEND ; Send the bytes
- RET
- ;
- MIR3: CALL CHK_STA ; Check for bytes ready
- JZ MIR1 ; Nothing there
- MOV CX,DX ; Ask for all bytes available
- CLI ; No new bytes (please!!)
- REP MOVSB ; Transfer the bytes
- CALL CLR_BUF ; Ready for next group
- STI ; Allow new bytes
- MOV CX,DX ; Bytes received
- PUSH CX ; Save byte-count
- MOV AL,'C'-64 ; Check for an abort
- MOV DI,OFFSET DBUFF ; Where the bytes
- REPNZ SCASB ; Check for ^C
- POP CX ; Restore count
- JNZ MIR4 ; Was no ^C (s)
- RET ; ^C received, exit
- MIR4: CALL SEND ; Send string
- JMP SHORT MIRROR ; Continue
- MIRROR ENDP
- ;
- ; Print double precision number in DX:AX. The string is addressed by DI
- ; All registers including BP are destroyed by this call.
- ;
- ASCII PROC NEAR
- MOV BP,0000 ; Leading zero flag
- MOV CX,3B9AH ; Get billions
- MOV BX,0CA00H
- CALL SUBTR ; Subtract them out
- CALL COMMA ; Put in a comma
- MOV CX,05F5H ; Get hundred-millions
- MOV BX,0E100H
- CALL SUBTR ; Subtract them out
- MOV CX,0098H ; Get ten-millions
- MOV BX,9680H
- CALL SUBTR ; Subtract them out
- MOV CX,000FH ; Get millions
- MOV BX,4240H
- CALL SUBTR ; Subtract them out
- CALL COMMA ; Put in a comma
- MOV CX,0001H ; Get hundred-thousands
- MOV BX,86A0H
- CALL SUBTR ; Subtract them out
- MOV CX,0000H ; Get ten-thousands
- MOV BX,2710H
- CALL SUBTR ; Subtract them out
- MOV CX,0000H ; Get thousands
- MOV BX,03E8H
- CALL SUBTR ; Subtract them out
- CALL COMMA ; Put in a comma
- MOV CX,0000H ; Get hundreds
- MOV BX,0064H
- CALL SUBTR ; Subtract them out
- MOV CX,0000H ; Get tens
- MOV BX,000AH
- CALL SUBTR ; Subtract them out
- ADD AL,'0' ; Add bias to residual
- STOSB ; Put in the string
- RET
- ;
- SUBTR: MOV SI,'0'-1 ; We are out of registers!
- SUBTR1: INC SI ; Counter
- SUB AX,BX ; Dword subtraction
- SBB DX,CX
- JNB SUBTR1 ; Continue until a carry
- ADD AX,BX ; One too many, add back
- ADC DX,CX ; and the remainder
- CMP BP,0 ; See if we printed anything yet
- JNZ SUBTR2 ; No, can't be a leading zero
- CMP SI,'0' ; See if its a zero
- JZ SUBTR3 ; Yes, don't print them
- ;
- SUBTR2: INC BP ; We are now 'printing' a character
- PUSH AX ; Save accumulator
- MOV AX,SI ; Get index
- STOSB ; Put in string
- POP AX ; Restore accumulator
- SUBTR3: RET
- ;
- COMMA: CMP BP,0 ; Any bytes printed?
- JZ PASS ; No, then no commas
- PUSH AX ; Yes, save
- MOV AL,',' ; Get a comma
- STOSB ; Into string
- POP AX ; Restore
- PASS: RET
- ASCII ENDP
- ;
- ; Search string addressed by SI for a substring addressed by DX.
- ; String length = CX, Substring length = BX. Return ZR if string
- ; is found.
- ;
- SEARCH PROC NEAR
- PUSH CX ; Save string length
- MOV DI,DX ; Get location of substring
- MOV CX,BX ; Get substring length
- REPZ CMPSB ; Check for match
- POP CX ; Restore string length
- JZ FOUND
- LOOP SEARCH ; Continue for whole string
- INC CX ; Set non-zero
- FOUND: RET
- SEARCH ENDP
- ;
- ;
- ; Map string addressed by SI to upper case. Put in address defined
- ; by DI. CX = byte count.
- ;
- MAP PROC NEAR
- LODSB ; Get byte
- CMP AL,'z' ; Check high range
- JG NOMAP ; Not a lower-case letter
- CMP AL,'a' ; Check low range
- JL NOMAP ; Not a lower-case letter
- AND AL,95 ; Reset lower-case bits
- NOMAP: STOSB
- LOOP MAP ; Do all bytes
- RET
- MAP ENDP
- ;
- OLD_PRT LABEL DWORD ; Old UART interrupt vector
- DQ ?
- OLD_CTC LABEL DWORD ; Old control-C vector
- DQ ?
- OLD_BRK LABEL DWORD ; Old control-break vector
- DQ ?
- OLD_CLK LABEL DWORD ; Old timer-tick vector
- DQ ?
- PRP0 DB CR,LF,'You need to install your ANSI.SYS driver!'
- DB 27,'[2J'
- DB (80 - @HEAD1)/2 DUP (' ')
- HEAD1 <>
- DB CR,LF
- DB (80 - @HEAD2)/2 DUP (' ')
- HEAD2 <>
- DB CR,LF
- DB (80 - (@HEAD3+@VERS))/2 DUP (' ')
- HEAD3 <>
- VERS <>
- DB CR,LF
- DB (80 - @HEAD4)/2 DUP (' ')
- HEAD4 <>
- DB CR,LF
- DB (80 - @HEAD5)/2 DUP (' ')
- HEAD5 <>
- DB CR,LF
- DB CR,LF
- STOP REV <>
- DB ' Block number '
- NRM <>
- DB ' '
- REV <>
- DB ' Bytes transmitted '
- NRM <>
- DB ' '
- REV <>
- DB ' Bytes received '
- NRM <>
- DB ' '
- REV <>
- DB ' Bad bytes '
- NRM <>
- DB CR,LF,LF
- DB '$'
- PRP3 DB ' '
- BLKS DB ' '
- BTX DB ' '
- BRX DB ' '
- BBYTE DB ' '
- DB CR,LF,LF
- REV <>
- DB ' Last Bad byte '
- NRM <>
- DB CR,LF
- REV <>
- DB ' Transmitted :'
- NRM <>
- DB ' '
- LBTX DB '- NONE -'
- DB CR,LF
- REV <>
- DB ' Received :'
- NRM <>
- DB ' '
- LBRX DB '- NONE -'
- DB CR,LF
- REV <>
- DB ' Pk Error Rate :'
- NRM <>
- DB ' '
- ERRAT DB '0.0000'
- DB CR,LF
- REV <>
- DB ' Av Error Rate :'
- NRM <>
- DB ' '
- ERRAV DB '0.0000'
- DB 27,'[9;1H' ; Restore cursor position
- DB '$'
- PRP1 DB CR,' Good$'
- PRP2 DB CR,' Bad $'
- PRP4 DB 27,'[23;1H'
- STP0 DB '$'
- PRP5 DB 27,'[23;1H'
- DB '- ABORTED - Modem Carrier failed!'
- DB CR,LF
- DB '$'
- PRP6 DB CR,LF
- SPACES <>
- DB 'At this time, the system is set up like a'
- DB CR,LF
- SPACES <>
- DB 'mirror. Everything sent will be reflected'
- DB CR,LF
- SPACES <>
- DB 'back.'
- DB CR,LF
- DB CR,LF
- SPACES <>
- DB 'Execute your local NOISE.COM program now.'
- DB CR,LF
- SPACES <>
- DB 'If you don''t have this program, abort with'
- DB CR,LF
- SPACES <>
- DB '^C and download it first!'
- DB CR,LF,LF
- INF_LEN EQU $ - PRP6
- DB '$'
- PRP7 DB CR,LF
- SPACES <>
- DB 'Reloading BBS system, please standby......'
- REL_LEN EQU $ - PRP7
- BLKN DW 0 ; Block number
- TOTTX_H DW ? ; Total bytes transmitted
- TOTTX_L DW ? ; Total bytes transmitted
- TOTRX_H DW ? ; Total bytes received
- TOTRX_L DW ? ; Total bytes received
- ;
- ;
- ; Table of communications adapter parameters.
- ;
- TABLE LABEL BYTE
- COM1 <>
- COM2 <>
- COM3 <>
- COM3 <>
- BASE DW 03F8H ; Communications adapter base port
- GD_BD DW STP0 ; Where to start.
- IBP DW ? ; Interrupt buffer pointer
- TBP DW 0 ; Transmitter buffer pointer
- COMP DW 0 ; TX completion address
- ABORT DB 0 ; Program abort flag
- CLOCK DW ? ; Local clock
- OLD_MSK DB ? ; Old controller mask
- OLD_UAR DB ? ; Old UART interrupt mask
- COM DW 0 ; Com port offset
- MODE DB 0 ; Normal/mirror mode
- STATUS DB 0FFH ; Modem status
- _10000 DW 10000 ; For error-rate multiply
- AS_COM DB 'PORT=' ; Command-line search-string
- SER_LEN EQU $ - AS_COM ; Its length
- AS_MOD DB 'MODE=' ; Command-line search string
- ;
- ; Put Stack on a paragraph boundary. Looks better in DEBUG.
- ;
- ORG (($ - START) + 16 ) AND 0FFF0H
- DB 32 DUP ('STACK ')
- STKTOP LABEL WORD ; Stack-pointer
- DBUFF DB MAX DUP (?) ; Data buffer
- BUFFER LABEL BYTE ; Interrupt buffer
- PSEG ENDS
- END MAIN