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: 14-DEC-1988 V1.01 Richard B. Johnson
- Added TESTING assembly conditional.
- Removed some redundant code.
- Added routine for stack-overflow test.
-
- 16-DEC-1988 V1.02 Richard B. Johnson
- Changed code in TX_WAIT to use HLT rather than a software-loop.
- Should make the code more machine-independent.
-
- 18-DEC-1988 V1.03 Richard B. Johnson
- Added subroutine CLEAR to help get the UART restarted after missed
- interrupts. Speeds up synchronization after a bad or incomplete block
- has been received.
-
- 19-DEC-1988 V1.04 Richard B. Johnson
- Changed many routines to use in-line code to speed execution.
- The UART hardware interrupt was completely rewritten. Also
- the print-string routines were changed to 'write-file/device'
- MACROS.
-
- 20-DEC-1988 V1.05 Richard B. Johnson
- Added routines to set the UART baud rate. Not usually used, but
- it makes the code more complete. The table for baud-rate divisors
- is placed in the interrupt-buffer to save space during execution.
-
- 29-JAN-1988 V1.06 Richard B. Johnson
- Fixed bug in communications-adapter port table, label, 'TABLE'
- where COM3 was entered twice rather than COM3 then COM4.
- *
- 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
- KBD EQU 16H ; Keyboard BIOS
- MAX EQU 256 ; Max bytes per block
- ONE_SEC EQU 18 ; Clock-ticks/second (about)
- ;
- ; ASCII structures
- ;
- VERS STRUC
- DB 'V1.06'
- @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 [ESC], [CTRL]-C, or [CTRL]-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
- DLAB EQU 10000000B ; Divisor-latch access bit
- ;
- ; 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
- 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
- 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
- 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
- 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
- STI ; Allow interrupts
- ENDM
- ;
- ; Print string addressed by DS:DX. Byte count is in CX.
- ;
- PROMPT MACRO STRING,LENGTH
- MOV DX,STRING ; Point to string
- MOV CX,LENGTH ; Get string length
- MOV BX,1 ; Console 'handle'
- MOV AX,4000H ; Write to file/device
- INT MS_DOS
- 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
- MOV WORD PTR [COMP],0 ; TX 'complete' address.
- STI ; Make sure a TSR didn't disable.
- CLD ; Forwards.
- CALL CHK_CMD ; Check command line
- JNC CMD_OK ; Command line was okay
- MOV AX,4C01H ; Exit with error
- INT MS_DOS ; to DOS
- CMD_OK: 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
- PROMPT <OFFSET PRP0>, STOP ; Print PRP0 to screen
- PROMPT <OFFSET PRP6>, INF_LEN ; Print PRP6 to screen
- MOV SI,OFFSET PRP6 ; Source string
- 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
- PUSH CX ; Save string length
- REP MOVSB ; Put into the buffer
- POP CX ; 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 OR INT_TX) ; 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
- CMP WORD PTR [DIVIS],0 ; see if we have a divisor
- JZ NO_DIV ; None, leave alone
- MOV AL,DLAB ; Raise divisor-latch access bit
- OUT DX,AL
- MOV DX,BX ; Get base port again
- MOV AX,WORD PTR [DIVIS] ; Get divisor
- OUT DX,AL ; Set LSB
- INC DX ; Next port
- MOV AL,AH ; Get MSB
- OUT DX,AL ; Set MSB
- MOV DX,BX ; Get base port again
- ADD DX,LIN_CTL ; Line control register
- NO_DIV: MOV AL,NORM ; 8 bits, 1 stop, no parity
- OUT DX,AL ; Set parameters
- JMP CLEAR ; Implied return
- SET_PRT ENDP
- ;
- ; Read all registers in the UART and clear any interrupts that may
- ; occur.
- ;
- CLEAR PROC NEAR
- STI ; Make sure a TSR didn't disable.
- MOV DX,WORD PTR [BASE] ; Get base port
- MOV CX,6 ; Six registers to read
- RD_EM: IN AL,DX ; Get byte/ throw away
- RESET ; Could interrupt, reset controller
- INC DX ; Ready next port
- LOOP RD_EM ; Continue for all ports
- RET
- CLEAR 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
- JMP CLEAR ; Implied return
- 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,AS_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
- PUSH CX ; Save string length
- MOV SI,OFFSET DBUFF ; Where the string is
- MOV DX,OFFSET AS_MOD ; Point to the string to look for
- MOV BX,AS_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: POP CX ; Restore string length
- MOV SI,OFFSET DBUFF ; Where the string is
- MOV DX,OFFSET AS_RAT ; String to find
- MOV BX,RAT_LEN ; Length of the substring
- CALL SEARCH ; Try to find it
- JNZ CONT4 ; Not found
- MOV DX,SI ; Point to byte after '='
- MOV SI,OFFSET SPEEDS ; Point to table of values
- MOV CX,TAB_LEN ; Length of the table
- MOV BX,3 ; Length of the numbers
- CALL SEARCH ; Try to find a value that matches
- JZ CONT3 ; Divisor found
- PROMPT <OFFSET PRP9>, LEN_P9 ; Print 'not found'
- STC ; Show error
- RET ; Exit
- CONT3: LODSW ; Get the divisor
- MOV WORD PTR [DIVIS],AX ; Save the divisor.
- CONT4: CLC ; Show no error
- RET
- CHK_CMD ENDP
- ;
- ; Set up the interrupt vectors.
- ;
- SET_INT PROC NEAR
- SAVE OLD_CLK, 08H ; Save old clock vector
- SAVE OLD_BRK, 1BH ; Save old break vector
- SAVE OLD_CTC, 23H ; Save old ^C vector
- SAVE OLD_KBD, 16H ; Save keyboard vector
- INDEX INTN, AL ; Get interrupt number
- SAVE OLD_PRT ; Save old interrupt contents
- ;
- SET NEW_CLK, 08H ; 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
- STI ; Make sure a TSR didn't disable.
- 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, 08H ; 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
- PROMPT <OFFSET PRP0>, LEN_P0 ; Print PRP0 to the screen
- TSTLN0: CALL CLEAR ; Reset the UART
- 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
- QUIT: 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
- ;
- ; Check to see if a key has been hit on the keyboard. Check for an
- ; escape of a control-C. Since we wish to keep the interrupts enabled
- ; we will CALL this interrupt by pushing the flags first, then making
- ; a long CALL. The routine ends with an IRET so it is necessary to
- ; push the flags onto the stack before the call.
- ;
- MOV AH,1 ; Check for character at from KBD
- PUSHF ; Dummy "interrupt"
- CALL DWORD PTR [OLD_KBD] ; Returns with an IRET
- JZ TEST3 ; Nothing ready
- MOV AH,0 ; Go get the character
- PUSHF ; Dummy "interrupt"
- CALL DWORD PTR [OLD_KBD] ; Returns with an IRET
- CMP AL,'C'-64 ; Check for control-C
- JZ QUIT ; Going to exit
- CMP AL,27 ; Check for escape
- JZ QUIT ; Exit
- ;
- TEST3: PROMPT <WORD PTR [GD_BD]>, LEN_P1 ; Print string variable
- PROMPT <OFFSET PRP3>, LEN_P3 ; Print stats to the screen
- MOV DI,OFFSET ERRAV ; Point to average error-rate str
- XOR AL,AL ; Flag for <average> RATE
- 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
- PROMPT <OFFSET PRP5>, LEN_P5 ; Print 'carrier fail' string
- RET ; Exit
- ENDIF
- ;
- CAR_OK: CALL CHK_STK ; Check for stack overflow
- JZ STK_OK ; Its okay
- PROMPT <OFFSET PRP8>, LEN_P8 ; Print 'stack overflow'
- JMP QUIT ; Cleanup and exit
- ;
- STK_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
- OR AL,0FFH ; Flag for <peak> RATE
- 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
- PUSH AX ; Save flag
- 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
- POP AX ; Level the stack
- 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
- POP BX ; Flag (was AX when pushed)
- CMP BL,0 ; Check for ave/peak
- JZ AVE ; Calc the percentage
- CMP AX,WORD PTR [OLD_PK] ; See if greater than old
- JNC PEAK ; Its greater
- RET ; Less or equal, ignore
- ;
- PEAK: MOV WORD PTR [OLD_PK],AX ; Update the peak count
- AVE: CALL PERCENT ; Conv
- MOV AX,DX ; Convert overflow
- ; XOR DX,DX ; Ready for multiply
- ; MUL WORD PTR [_10000] ; (REM * 10000) / 65536
- ; MOV CX,10
- ; MUL CX
- ; MOV AX,DX ; Is /65536
- CALL PRECIS ; Finish numerical precision
- RET
- RATE ENDP
- ;
- ; Local clock vector (hardware interrupt).
- ;
- NEW_CLK PROC FAR
- PUSH AX ; Save register
- RESET ; Reset the controller
- POP AX
- SUB BYTE PTR CS:[CLOCK],1 ; Bump the clock
- ADC BYTE 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 (software interrupt)
- ;
- NEW_CTC PROC FAR
- MOV BYTE PTR CS:[ABORT],0FFH ; Set flag
- IRET
- NEW_CTC ENDP
- ;
- ; Local control-BRK vector (software interrupt)
- ;
- NEW_BRK PROC FAR
- MOV BYTE PTR CS:[ABORT],0FFH ; Set flag
- IRET
- NEW_BRK ENDP
- ;
- ; Local modem port interrupt vector (hardware interrupt). The code
- ; is as 'in-line' as I could make it to speed execution.
- ;
- NEW_PRT PROC FAR
- 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
- SUB DX,INT_IDR ; Back to the base port
- TEST AL,00000100B ; Got a byte?
- JNZ GET_DAT ; Yes
- TEST AL,00000010B ; TX holding register empty?
- JNZ PUT_DAT ; Yes
- ;
- GET_STA:
- ADD DX,MOD_STA ; Offset to modem status
- IN AL,DX ; Get modem status
- MOV BYTE PTR [STATUS],AL ; Save modem status
- RESET ; Reset the controller
- POP DS ; Restore registers used
- POP DX
- POP AX
- IRET
- ;
- GET_DAT:
- PUSH BX ; Save index
- IN AL,DX ; Get the byte
- MOV BX,WORD PTR [IBP] ; Get buffer pointer
- MOV BYTE PTR [BX],AL ; Put byte in the buffer
- RESET ; Reset the controller
- ADD WORD PTR [IBP],1 ; Ready next
- SBB WORD PTR [IBP],0 ; Don't wrap around segment!
- MOV BYTE PTR [CLOCK],ONE_SEC ; Reset the clock
- POP BX ; Restore index
- POP DS ; Restore registers used
- POP DX
- POP AX
- IRET
- ;
- PUT_DAT:
- PUSH SI ; Save index
- MOV SI,WORD PTR [TBP] ; Get TX buffer pointer
- CMP SI,WORD PTR [COMP] ; Check limits
- JNC NO_SND ; Don't output the byte
- LODSB ; Get buffer byte
- OUT DX,AL ; Send it out
- MOV WORD PTR [TBP],SI ; Ready next
- NO_SND: RESET ; Reset the controller
- POP SI ; Restore index
- POP DS ; Restore registers used
- POP DX
- POP AX
- IRET
- NEW_PRT ENDP
- ;
- ; Check receive status. Return DX=byte count. ZF = no bytes
- ;
- CHK_STA PROC NEAR
- MOV DX,WORD PTR [IBP] ; Get the interrupt buffer 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 after the last byte 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 BYTE PTR [CLOCK],ONE_SEC ; Set the clock
- AGAIN: CMP BYTE 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 second byte in the buffer. Calculate the completion-
- ; address [COMP] by adding in the byte-count. Send the first byte to
- ; start TX interrupt.
- ;
- SEND PROC NEAR
- CALL TX_WAIT ; Wait for previous TX to complete
- MOV WORD PTR [TBP],OFFSET DBUFF + 1 ; Set TX buffer pointer
- MOV BX,OFFSET DBUFF ; Point to the buffer
- ADD BX,CX ; Calc last address
- MOV WORD PTR [COMP],BX ; Set ending address
- MOV DX,WORD PTR [BASE] ; Pick up UART base address
- MOV AL,BYTE PTR [DBUFF] ; Get first buffer byte
- OUT DX,AL ; TX the first byte
- RET
- 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
- STI ; Make sure a TSR didn't disable.
- WAIT0: MOV BX,WORD PTR [TBP] ; Get the transmitter buffer pointer
- WAIT1: CMP BX,WORD PTR [COMP] ; Check completion pointer
- JNC WAIT2 ; TX is complete
- HLT ; Wait for any interrupt (maybe clock)
- HLT ; Wait for any interrupt (maybe UART)
- CMP BX,WORD PTR [TBP] ; See if pointer changed
- JNZ WAIT0 ; Yes, continue wait
- WAIT2: RET
- TX_WAIT ENDP
- ;
- ; Terminate the program. Put the cursor at the bottom os the screen.
- ;
- FINIS PROC NEAR
- PROMPT <OFFSET PRP4>, LEN_P4 ; Print restore cursor.
- MOV AX,4C00H ; Normal exit
- INT MS_DOS
- FINIS 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.
- ;
- PRECIS PROC NEAR
- MOV CX,10000
- CALL PSUB ; Subtract 10 thousands
- 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
- ;
- 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
- PRECIS ENDP
- ;
- ; Send any incoming bytes 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)
- PROMPT <OFFSET PRP5>, LEN_P5 ; Print 'carrier failed'
- 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
- ;
- ; Check for stack overflow. The user may have TRSs that are using
- ; interrupts which could cause an overflow. We want to abort before
- ; anything bad happens.
- ;
- CHK_STK PROC NEAR
- MOV SI,OFFSET LEVEL ; Point to end of stack area
- MOV DI,OFFSET LEVEL + 8 ; Point to end + 8
- MOV CX,8 ; Check 8 bytes
- REPZ CMPSB ; Should be the same.
- RET
- CHK_STK ENDP
- ;
- ; - Data area follows -
- ;
- 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 EQU $ - PRP0
- 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
- LEN_P0 EQU $ - PRP0
- 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.000000000'
- DB CR,LF
- REV <>
- DB ' Av Error Rate :'
- NRM <>
- DB ' '
- ERRAV DB '0.000000000'
- DB 27,'[9;1H' ; Restore cursor position
- LEN_P3 EQU $ - PRP3
- PRP1 DB CR,' Good'
- LEN_P1 EQU $ - PRP1
- PRP2 DB CR,' Bad '
- LEN_P2 EQU $ - PRP2
- PRP4 DB 27,'[23;1H'
- LEN_P4 EQU $ - PRP4
- STP0 DB ' '
- PRP5 DB 27,'[23;1H'
- DB '- ABORTED - Modem Carrier failed!'
- DB CR,LF
- LEN_P5 EQU $ - PRP5
- 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
- PRP7 DB CR,LF
- SPACES <>
- DB 'Reloading BBS system, please standby......'
- REL_LEN EQU $ - PRP7
- ;
- PRP8 DB 27,'[23;1H'
- DB '- ABORTED - Stack overflow!'
- DB CR,LF
- LEN_P8 EQU $ - PRP8
- ;
- PRP9 DB CR,LF,'Requested baud-rate not supported.'
- LEN_P9 EQU $ - PRP9
- ;
- ; Table of communications adapter parameters.
- ;
- TABLE LABEL BYTE
- COM1 <>
- COM2 <>
- COM3 <>
- COM4 <>
- EVEN ; Align all the WORDS
- ;
- ; Saved interrupt vector addresses.
- ;
- 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 ?
- OLD_KBD LABEL DWORD ; Old keyboard interrupt
- DQ ?
- ;
- _10000 DW 10000 ; For error-rate multiply
- BASE DW 03F8H ; Communications adapter base port
- BLKN DW 0 ; Block number
- COM DW 0 ; Com port offset
- COMP DW ? ; TX completion address
- GD_BD DW STP0 ; Where to start.
- IBP DW ? ; Interrupt buffer pointer
- TBP DW ? ; Transmitter buffer pointer
- TOTTX_H DW ? ; Total bytes transmitted
- TOTTX_L DW ? ; Total bytes transmitted
- TOTRX_H DW ? ; Total bytes received
- TOTRX_L DW ? ; Total bytes received
- OLD_PK DW 0 ; Previous peak error rate
- DIVIS DW ? ; UART Divisor
- CLOCK DB ? ; Local clock
- ABORT DB 0 ; Program abort flag
- OLD_MSK DB ? ; Old controller mask
- OLD_UAR DB ? ; Old UART interrupt mask
- MODE DB 0 ; Normal/mirror mode
- STATUS DB 0FFH ; Modem status
- AS_COM DB 'PORT=' ; Command-line search-string
- AS_LEN EQU $ - AS_COM ; Its length
- AS_MOD DB 'MODE=' ; Command-line search string
- AS_RAT DB 'SPEED=' ; Command-line search string
- RAT_LEN EQU $ - AS_RAT ; Length of the search-string
- ;
- ; Put Stack on a paragraph boundary. Looks better in DEBUG.
- ;
- ORG (($ - START) + 16 ) AND 0FFF0H
- LEVEL DB 32 DUP ('STACK ')
- STKTOP LABEL WORD ; Stack-pointer
- DBUFF DB MAX DUP (?) ; Data buffer
- BUFFER LABEL BYTE ; Interrupt buffer
- SPEEDS DB '150' ; 150 baud rate
- DW 768
- DB '300' ; 300 baud
- DW 384
- DB '600' ; 600 baud
- DW 192
- DB '120' ; 1200 baud
- DW 96
- DB '180' ; 1800 baud
- DW 64
- DB '200' ; 2000 baud
- DW 58
- DB '240' ; 2400 baud
- DW 48
- DB '360' ; 3600 baud
- DW 32
- DB '480' ; 4800 baud
- DW 24
- DB '720' ; 7200 baud
- DW 16
- DB '960' ; 9600 baud
- DW 12
- DB '192' ; 19200 baud
- DW 6
- DB '384' ; 38400 baud
- DW 3
- DB '560' ; 56000 baud
- DW 2
- TAB_LEN EQU $ - SPEEDS
- PSEG ENDS
- END MAIN