home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
simtel
/
sigm
/
vols000
/
vol048
/
xmodem46.asm
< prev
Wrap
Assembly Source File
|
1984-04-29
|
40KB
|
1,581 lines
;
; XMODEM.ASM V4.6, by Keith Petersen, W8SDZ
; (revised 10/19/81)
;
; REMOTE CP/M - CP/M FILE TRANSFER PROGRAM
;
;Based on MODEM.ASM V2.0, by Ward Christensen.
;This program is intended for use on remote CP/M
;systems where it is important that the initialization
;of the modem not be changed, such as when using
;the PMMIBYE program. The baud rate and number of bits
;remains the same as whatever was set previously.
;There is no disconnect, terminal or echo option.
;
;Updates/fixes (in reverse order to minimize reading time):
;
;10/19/81 Corrected numerous 'IN MODCTL2' errors for the DC
; Hayes modem. Added DC Hayes detection of framing
; errors, overrun errors, and parity errors (if
; parity is used) for the receive file routine.
; (Bill Aten)
;
;10/12/81 Added code to implement Cyclic Redundancy
; Checking for both receiving and sending files.
; The CRC can only be specified by the operator
; on the receive file option as a secondary
; option of 'C' (XMODEM RC FN.FT). When CRC is
; in effect, an initial 'C' instead of a NAK will
; be sent to the sender to start things off.
; The 'C' will be the signal to the sender
; (hopefully a version of MODEM that implements
; this CRC convention) that CRC is in effect.
; CRC will then take the place of the checksum
; checking for data validity. The CRC should
; make file transfers as far as data integrity is
; concerned better than 99.99% error free. The
; crc macro, CRC120, was used to implement CRC
; in this program and its equivalent version of
; MODEM. Acknowledgements and thanks to Paul
; Hansknecht who designed and wrote CRC120.
;
;07/01/81 REDID H8/H89 EQUATES TESTED PROGRAM USING BOTH
; SYSTEMS AND CHANGED VER TO 4.4 (AL OLANDER)
;
;06/28/81 INSTALL H8/H89 EQUATES AND CHANGE EXTERNAL
; MODEM EQUATES TO "EXTMOD". (L. SHIPINSKI)
;
;05/30/81 Added IF PMMI/ENDIF to RCVERR routine to eliminate
; 'undefined symbol' error when set for DCH modem.
; (Dave Hardy)
;
;05/07/81 Changed signon revision number.
; Cleaned up file. (KBP)
;
;05/01/81 Added detection of framing errors, overrun
; errors, and parity errors (if parity is used)
; for the receive file routine. This feature
; is only active for the PMMI modem, since I
; do not know what the modem status bits are
; for IDS and D.C. Hayes modems. If there
; is one of the above errors, the line will
; be purged for that block and a NAK will be
; sent to the sender for that block. This was
; added to help catch those transmission errors
; that are not always caught by the checksum.
; This error checking is in addition to the
; checksum routine. (John Mahr)
;
;02/17/81 Added test for "f2" tagged files in OPENOK
; for MP/M version 1.1 compatiblity, which
; doesn't allow Ctl-C or Ctl-S in "f1" tagged
; files. (Tim Nicholas)
;
;02/16/81 Added hex to file size display. Now reports
; size in both decimal and (xxxxH) hex. Thanks
; to Ben Bronson for the idea. (Tim Nicholas)
;
;02/15/81 Added a software timer to the carrier test
; added in SEND and RECV routines. This will
; now abort only if carrier is lost for a
; period of 15 seconds. This is only essential
; for those using external modems with certain
; SIO's, but will provide the PMMI/DCH user
; faster recovery in a lost carrier situation
; as well. Approx 15 seconds plus 15 seconds
; in BYE.COM, compared to 3 minutes at 300
; baud with earlier revisions. Thanks to Ben
; Bronson for his aid in developing this
; revision. (Tim Nicholas)
;
;02/14/81 Corrected error in last update which read
; the incorrect port for PMMI in the added
; carrier test. (Tim Nicholas)
;
;01/31/81 Added equates and code for a carrier test.
; Test performed in modem I/O routines. This
; is required since loss of carrier will go
; undetected by BYE.COM, if the loss occurs
; after a sucessful XMODEM signon, when using
; an external modem and SIO. (Tim Nicholas)
;
;01/17/81 Re-wrote routine to calculate file size so
; that it works correctly on v2.X systems with
; extent folding (non-zero extent mask). (BRR)
;
;12/06/80 Re-wrote routine to calculate file size,
; added decimal print of file size. (KBP)
;
;12/05/80 Corrected error in use of ext byte that pre-
; vented files greater than one extent from
; being sent. Ron Fowler
;
;12/03/80 Corrected file extent length display. Now
; reports correct number of records for files
; longer than one extent. Display is now
; double precision (xxxxH). Also made some
; cosmetic changes by re-arranging the equates.
; By Tim Nicholas
;
;10/28/80 Cleaned up file. (KBP)
;
;10/23/80 Expanded conditional assembly of NOCOM routines
; into NOCOMS, NOLBS, and NOCOMR equates, to allow
; separate conditional assembly of tests for sending
; .COM files, sending .??# files, and receiving .COM
; files, respectively. (Dave Hardy)
;
;10/15/80 Added traps for ambiguous file name or
; none at all. (KBP)
;
;09/09/80 Added conditional assembly to prevent filetypes
; '.COM' or '.??#' from being sent to distant end
; and added conditional assembly of test for '.COM'
; filetype on receive as well. See 'NOCOM' below.
; Any filetype ending in '#' will not be sent by
; this program if 'NOCOM' is set to TRUE. J.SEYMOUR
;
;NOTE: If you add improvements or otherwise update
;this program, please modem a copy of the new file
;to "TECHNICAL CBBS" in Dearborn, Michigan - phone
;313-846-6127 (110, 300, 450 or 600 baud). Use the
;filename XMODEM.NEW. (KBP)
;
FALSE EQU 0
TRUE EQU NOT FALSE
;
;-----------------------------------------------------
; --- Conditional Assembly Options --- ;
;------------------------------------------------------
;
STDCPM EQU TRUE ;TRUE, IS STANDARD CP/M
ALTCPM EQU FALSE ;TRUE, IS TRS-80 OR H8 W/O 0-ORG
;
PMMI EQU FALSE ;TRUE, IS PMMI
DCH EQU TRUE ;TRUE, IS D.C. HAYES
H8 EQU FALSE ;TRUE, IS H8/H89 W/INS8250 MODEM CHIP
EXTMOD EQU FALSE ;TRUE, IS NONE OF THE ABOVE!
;
NOCOMS EQU TRUE ;TRUE, NO .COM FILES SENT
NOLBS EQU TRUE ;TRUE, NO .??# FILES SENT
NOCOMR EQU TRUE ;TRUE, NO .COM FILES RECEIVED
;
FASTCLK EQU TRUE ;PUT TRUE HERE FOR 4 MHZ CLOCK
;
FRNTPNL EQU FALSE ;TO DISPLAY STATUS ON FRONT PANEL
PANEL EQU 0FFH ;DEFAULT ADDRESS OF FRONT PANEL
;
;------------------------------------------------------
; --- Modem Port Equates --- ;
;------------------------------------------------------
;
IF PMMI
MODCTLP EQU 0C0H ;PMMI VALUES(base port addr)
MODSNDB EQU 1 ;BIT TO TEST FOR SEND
MODSNDR EQU 1 ;VALUE WHEN READY
MODRCVB EQU 2 ;BIT TO TEST FOR RECEIVE
MODRCVR EQU 2 ;VALUE WHEN READY
MODDCDB EQU 4 ;CARRIER DETECT BIT
MODDCDA EQU 0 ;VALUE WHEN ACTIVE
MODPARE EQU 08H ;VALUE FOR PARITY ERROR
MODOVRE EQU 10H ;VALUE FOR OVERRUN ERROR
MODFRME EQU 20H ;VALUE FOR FRAMING ERROR
MODDATP EQU MODCTLP+1;DATA PORT
BAUDRP EQU MODCTLP+2;BAUD RATE OUTPUT/MODEM STATUS
MODCTL2 EQU MODCTLP+3;SECOND CTL PORT
ENDIF
;
IF H8
MODCTLP EQU 0DDH ;H8/H89 VALUES (LSR-LINE STATUS REG.)
MODSNDB EQU 20H ;TEST FOR SEND (LSR-THRE)
MODSNDR EQU 20H ;VALUE WHEN READY
MODRCVB EQU 01H ;TEST FOR RECIEVE (LSR-DR)
MODRCVR EQU 01H ;VALUE WHEN READY
MODDCDB EQU 20H ;CARRIER DETECT BIT (MSR-CTS)
MODDCDA EQU 20H ;VALUE WHEN ACTIVE
MODPARE EQU 04H ;VALUE FOR PARITY ERROR (LSR-PE)
MODOVRE EQU 02H ;VALUE FOR OVERRUN ERROR (LSR-OR)
MODFRME EQU 08H ;VALUE FOR FRAMING ERROE (LSR-FE)
MODDATP EQU 0D8H ;DATA PORT, SEND OR RECIEVE
BAUDRP EQU 0DDH ;BAUD RATE PORT (DALB IN LCR MUST=1)
MODCTL2 EQU 0DEH ;MODEM STATUS REGISTER (MSR)
MODCTL1 EQU 0DBH ;LINE CONTROL REGISTER (LCR)
ENDIF
;
IF DCH
MODCTLP EQU 82H ;D. C. HAYES VALUES
MODSNDB EQU 2 ;BIT TO TEST FOR SEND
MODSNDR EQU 2 ;VALUE WHEN READY
MODRCVB EQU 1 ;BIT TO TEST FOR RECEIVE
MODRCVR EQU 1 ;VALUE WHEN READY
MODDCDB EQU 40H ;CARRIER DETECT BIT
MODDCDA EQU 40H ;VALUE WHEN ACTIVE
MODPARE EQU 04H ;VALUE FOR PARITY ERROR
MODOVRE EQU 10H ;VALUE FOR OVERRUN ERROR
MODFRME EQU 08H ;VALUE FOR FRAMING ERROR
MODDATP EQU 80H ;DATA PORT
MODCTL2 EQU 81H ;SECOND CTL PORT
ENDIF
;
;---> NOTE: DCD (Carrier Detect) values above are for
; the Micromodem 100. For DC-Hayes 80-103
; the values are different.
; MODDCDB EQU 1 ;Carrier bit (CTS).
; MODDCDA EQU 1 ;Active value.
;
;
;
;If you are using an external modem (not S-100 plug-in)
;change these equates for your modem port requirements
;
IF EXTMOD
MODCTLP EQU 35H ;PUT YOUR MODEM STATUS PORT HERE
MODSNDB EQU 01H ;YOUR BIT TO TEST FOR SEND
MODSNDR EQU 01H ;YOUR VALUE WHEN READY
MODRCVB EQU 02H ;YOUR BIT TO TEST FOR RECEIVE
MODRCVR EQU 02H ;YOUR VALUE WHEN READY
MODDCDB EQU 1 ;CARRIER DETECT BIT
MODDCDA EQU 1 ;VALUE WHEN ACTIVE
MODDATP EQU 34H ;YOUR MODEM DATA PORT
MODCTL2 EQU 36H ;SECOND CONTROL/STATUS PORT.
ENDIF ;END OF EXTERNAL MODEM EQUATES
;
; --- End of Options ---
;------------------------------------------------------
;
ERRLIM EQU 10 ;MAX ALLOWABLE ERRORS (10 STANDARD)
;
;Define ASCII characters used
;
SOH EQU 1 ;START OF HEADER
EOT EQU 4 ;END OF TRANSMISSION
ACK EQU 6 ;ACKNOWLEDGE
NAK EQU 15H ;NEG ACKNOWLEDGE
CRC EQU 'C' ;CRC REQUEST CHARACTER
CAN EQU 18H ;CONTROL-X FOR CANCEL
LF EQU 10 ;LINEFEED
CR EQU 13 ;CARRIAGE RETURN
;
IF STDCPM
BASE EQU 0 ;CP/M BASE ADDRESS
ENDIF
;
IF ALTCPM
BASE EQU 4200H ;ALTERNATE CP/M BASE ADDRESS
ENDIF
;
ORG BASE+100H
;
;Init private stack
LXI H,0 ;HL=0
DAD SP ;HL=STACK FROM CP/M
SHLD STACK ;..SAVE IT
LXI SP,STACK ;SP=MY STACK
CALL ILPRT ;PRINT:
DB CR,LF
DB 'XMODEM ver 4.6',CR,LF,0
;
;GET OPTION
;
LDA FCB+2 ;SECONDARY OPTION?
CPI 'C' ;CRC CHECKING REQUESTED?
JNZ CHKOPTN ;NO, GO CHECK PRIMARY
LDA FCB+1 ;GET PRIMARY OPTION
CPI 'R' ;CRC VALID ONLY FOR RECEIVE
JNZ OPTNERR ;PRT MSG, ABORT
XRA A ;ZERO ACCUM
STA CRCFLG ;TURN ON CRC FLAG
;
CHKOPTN LDA FCB+1 ;GET OPTION (S or R)
PUSH PSW ;SAVE OPTION
;
;Move the filename from FCB2 to FCB1
;
CALL MOVEFCB
;
;Gobble up garbage chars from the line
;prior to receive or send
;
IN MODDATP
IN MODDATP
;
;Jump to appropriate function
;
POP PSW ;GET OPTION
;
CPI 'S' ;SEND..
JZ SENDFIL ;..A FILE?
;
CPI 'R' ;RECEIVE..
JZ RCVFIL ;..A FILE?
;
;Invalid option
;
OPTNERR CALL ERXIT ;EXIT W/ERROR
DB '++INVALID OPTION ON XMODEM '
DB 'COMMAND++',CR,LF
DB 'Must be S for SEND; or R or RC '
DB 'for RECEIVE',CR,LF,'$'
;
* * * * * * * * * * * * * * * * * * * * *
* *
* SENDFIL: SENDS A CP/M FILE *
* *
* * * * * * * * * * * * * * * * * * * * *
;
;The CP/M file specified in the XMODEM command
;is transferred over the phone to another
;computer running MODEM with the "R" (receive)
;option. The data is sent one sector at a
;time with headers and checksums, and re-
;transmission on errors.
;
SENDFIL CALL TRAP ;CHECK FOR NO NAME OR AMBIG. NAME
CALL CNREC ;COMPUTE # OF RECORDS.
CALL OPENFIL ;OPEN THE FILE
MVI E,80 ;WAIT 80 SEC..
CALL WAITNAK ;..FOR INITIAL NAK
;
SENDLP CALL RDSECT ;READ A SECTOR
JC SENDEOF ;SEND EOF IF DONE
CALL INCRSNO ;BUMP SECTOR #
XRA A ;ZERO ERROR..
STA ERRCT ;..COUNT
;
SENDRPT CALL SENDHDR ;SEND A HEADER
CALL SENDSEC ;SEND DATA SECTOR
LDA CRCFLG ;GET CRC FLAG
ORA A ;CRC IN EFFECT?
CZ SENDCRC ;YES, SEND CRC
CNZ SENDCKS ;NO, SEND CKSUM
CALL GETACK ;GET THE ACK
JC SENDRPT ;REPEAT IF NO ACK
JMP SENDLP ;LOOP UNTIL EOF
;
;File sent, send EOT's
;
SENDEOF MVI A,EOT ;SEND..
CALL SEND ;..AN EOT
CALL GETACK ;GET THE ACK
JC SENDEOF ;LOOP IF NO ACK
JMP EXIT ;ALL DONE
;
* * * * * * * * * * * * * * * * * * * * *
* *
* RCVFIL: RECEIVE A FILE *
* *
* * * * * * * * * * * * * * * * * * * * *
;
;Receives a file in block format as sent
;by another person doing "MODEM S FN.FT".
;Can be invoked by 'XMODEM R FN.FT' or
;by 'XMODEM RC FN.FT' if CRC is to be used.
;
RCVFIL CALL TRAP ;CHECK FOR NO NAME OR AMBIG. NAME
;
IF NOCOMR
LXI H,FCB+9 ;POINT TO FILETYPE
MVI A,'C' ;1ST LETTER
CMP M ;IS IT C ?
JNZ CONTINU ;IF NOT, CONTINUE NORMALLY
INX H ;GET 2ND LETTER
MVI A,'O' ;2ND LETTER
CMP M ;IS IT O ?
JNZ CONTINU ;IF NOT, CONTINUE NORMALLY
INX H ;GET 3RD LETTER
MVI A,'M' ;3RD LETTER
CMP M ;IS IT M ?
JNZ CONTINU ;IF NOT, CONTINUE NORMALLY
CALL ERXIT ;EXIT, PRINT ERROR MESSAGE
DB '++CAN''T RECEIVE A .COM FILE++'
DB CR,LF,CR,LF
DB 'Rename filetype ".OBJ" and try again'
DB CR,LF,'$'
ENDIF
;
CONTINU CALL CHEKFIL ;SEE IF FILE EXISTS
CALL MAKEFIL ;..THEN MAKE NEW
CALL ILPRT ;PRINT:
DB 'FILE OPEN - READY TO RECEIVE',CR,LF,0
;
RCVLP CALL RCVSECT ;GET A SECTOR
JC RCVEOT ;GOT EOT
CALL WRSECT ;WRITE THE SECTOR
CALL INCRSNO ;BUMP SECTOR #
CALL SENDACK ;ACK THE SECTOR
JMP RCVLP ;LOOP UNTIL EOF
;
;Got EOT on sector - flush buffers, end
;
RCVEOT CALL WRBLOCK ;WRITE THE LAST BLOCK
CALL SENDACK ;ACK THE SECTOR
CALL CLOSFIL ;CLOSE THE FILE
JMP EXIT ;ALL DONE
;
* * * * * * * * * * * * * * * * * * * * *
* *
* SUBROUTINES *
* *
* * * * * * * * * * * * * * * * * * * * *
;
;----> TRAP: Check for no file name or ambiguous name
;
TRAP LXI H,FCB+1 ;POINT TO FILE NAME
MOV A,M ;GET FIRST CHAR OF FILE NAME
CPI ' ' ;ANY THERE?
JNZ ATRAP ;YES, CHECK FOR AMBIGOUS FILE NAME
CALL ERXIT ;PRINT MSG, EXIT
DB '++NO FILE NAME SPECIFIED++',CR,LF,'$'
;
ATRAP MVI B,11 ;11 CHARS TO CHECK
;
TRLOOP MOV A,M ;GET CHAR FROM FCB
CPI '?' ;AMBIGUOUS?
JZ TRERR ;YES, EXIT WITH ERROR MSG
INX H ;POINT TO NEXT CHAR
DCR B ;ONE LESS TO GO
JNZ TRLOOP ;NOT DONE, CHECK SOME MORE
RET ;NO AMBIGUOUS NAME, RETURN
;
TRERR CALL ERXIT ;PRINT MSG, EXIT
DB '++CAN''T USE WILD CARD OPTIONS++',CR,LF,'$'
;
;----> RCVSECT: Receive a sector
;
;Returns with carry set if EOT received.
;
RCVSECT XRA A ;GET 0
STA ERRCT ;INIT ERROR COUNT
;
RCVRPT:
IF PMMI OR H8 OR DCH
XRA A ;GET 0
STA ERRCDE ;CLEAR RECEIVE ERROR CODE
ENDIF
;
MVI B,10 ;10 SEC TIMEOUT
CALL RECV ;GET SOH/EOT
JC RCVSTOT ;TIMEOUT
;
IF PMMI OR H8 OR DCH
CALL RCVERR ;TRANS ERROR?
JC RCVSERR ;CARRY SET IF ERROR
ENDIF
;
CPI SOH ;GET SOH?
JZ RCVSOH ;..YES
;
;Earlier versions of MODEM program send some nulls -
;ignore them
;
ORA A ;00 FROM SPEED CHECK?
JZ RCVRPT ;YES, IGNORE IT
CPI EOT ;END OF TRANSFER?
STC ;RETURN WITH CARRY..
RZ ;..SET IF EOT
;
;Didn't get SOH or EOT -
; -or-
;Didn't get valid header - purge the line,
;then send NAK.
;
RCVSERR MVI B,1 ;WAIT FOR 1 SEC..
CALL RECV ;..WITH NO CHARS
JNC RCVSERR ;LOOP UNTIL SENDER DONE
LDA CRCFLG ;GET CRC FLAG
ORA A ;CRC IN EFFECT?
MVI A,NAK ;PUT NAK IN ACCUM
JNZ RCVSER2 ;NO, SEND THE NAK
LDA FIRSTIME;GET FIRST TIME SWITCH
ORA A ;HAS FIRST SOH BEEN RECEIVED?
MVI A,NAK ;PUT NAK IN ACCUM
JZ RCVSER2 ;YES, THEN SEND NAK
MVI A,CRC ;TELL SENDER CRC IS IN EFFECT
;
RCVSER2 CALL SEND ;..THE NAK or CRC request
LDA ERRCT ;ABORT IF..
INR A ;..WE HAVE REACHED..
STA ERRCT ;..THE ERROR..
CPI ERRLIM ;..LIMIT?
JC RCVRPT ;..NO, TRY AGAIN
;
;10 errors in a row -
;
RCVSABT CALL CLOSFIL ;KEEP WHATEVER WE GOT
CALL ERXIT
DB '++UNABLE TO RECEIVE BLOCK '
DB '- ABORTING++',CR,LF,'$'
;
;Timed out on receive
;
RCVSTOT JMP RCVSERR ;BUMP ERR CT, ETC.
;
;---->RCVERR: Checks to see if framing error, overrun, or
; parity error occurred.
; 1. Error code (ERRCDE) was set in recv routine
; 2. ERRCDE=0 for no errors, ERRCDE<>0 for errors
; 3. If there has been an error, this routine sets
; the carry bit on.
;
IF PMMI OR H8 OR DCH
RCVERR PUSH PSW ;SAVE CHAR TRANSMITTED
LDA ERRCDE ;GET RECEIVE ERR CODE
ANA A ;IS IT ZERO?
JZ RCVERR2 ;YES, NO ERROR
POP PSW ;RESTORE CHAR TRANSMITTED
STC ;SET CARRY ON FOR ERROR
RET
;
RCVERR2 POP PSW ;RESTORE CHAR TRANSMITTED
ORA A ;CLEAR CARRY BIT
RET
ENDIF
;
;Got SOH - get block #, block # complemented
;
RCVSOH XRA A ;ZERO ACCUM
STA FIRSTIME;INDICATE FIRST SOH RECV'D
MVI B,1 ;TIMEOUT = 1 SEC
CALL RECV ;GET SECTOR
JC RCVSTOT ;GOT TIMEOUT
;
IF PMMI OR H8 OR DCH
CALL RCVERR ;TRANS ERROR?
JC RCVSERR ;CARRY SET IF ERROR
ENDIF
;
MOV D,A ;D=BLK #
MVI B,1 ;TIMEOUT = 1 SEC
CALL RECV ;GET CMA'D SECT #
JC RCVSTOT ;TIMEOUT
;
IF PMMI OR H8 OR DCH
CALL RCVERR ;TRANS ERROR?
JC RCVSERR ;CARRY SET IF ERROR
ENDIF
;
CMA ;CALC COMPLEMENT
CMP D ;GOOD SECTOR #?
JZ RCVDATA ;YES, GET DATA
;
;Got bad sector #
;
JMP RCVSERR ;BUMP ERROR CT.
;
RCVDATA MOV A,D ;GET SECTOR #
STA RCVSNO ;SAVE IT
MVI C,0 ;INIT CKSUM
CALL CLRCRC ;CLEAR CRC COUNTER
LXI H,BASE+80H ;POINT TO BUFFER
;
RCVCHR MVI B,1 ;1 SEC TIMEOUT
CALL RECV ;GET CHAR
JC RCVSTOT ;TIMEOUT
;
IF PMMI OR H8 OR DCH
CALL RCVERR ;TRANS ERROR?
JC RCVSERR ;CARRY SET IF ERROR
ENDIF
;
MOV M,A ;STORE CHAR
INR L ;DONE?
JNZ RCVCHR ;NO, LOOP
LDA CRCFLG ;GET CRC FLAG
ORA A ;CRC IN EFFECT?
JZ RCVCRC ;YES, TO RECEIVE CRC
;
;Verify checksum
;
MOV D,C ;SAVE CHECKSUM
MVI B,1 ;TIMEOUT LEN.
CALL RECV ;GET CHECKSUM
JC RCVSTOT ;TIMEOUT
;
IF PMMI OR H8 OR DCH
CALL RCVERR ;TRANS ERROR?
JC RCVSERR ;CARRY SET IF ERROR
ENDIF
;
CMP D ;CHECKSUM OK?
JNZ RCVSERR ;NO, ERROR
;
;Got a sector, it's a duplicate if = previous,
; or OK if = 1 + previous sector
;
CHKSNUM LDA RCVSNO ;GET RECEIVED
MOV B,A ;SAVE IT
LDA SECTNO ;GET PREV
CMP B ;PREV REPEATED?
JZ RECVACK ;ACK TO CATCH UP
INR A ;CALC NEXT SECTOR #
CMP B ;MATCH?
JNZ ABORT ;NO MATCH - STOP SENDER, EXIT
RET ;CARRY OFF - NO ERRORS
;
;----> RCVCRC: Receive the cyclic redundancy check
; characters (2 bytes), and see if the crc
; received matches the one calculated.
; If they match, get next sector, else
; send a NAK requesting the sector be
; resent.
;
RCVCRC MVI E,2 ;NUMBER OF BYTES TO RECEIVE
;
RCVCRC2 MVI B,1 ;1 SEC TIMEOUT
CALL RECV ;GET CRC BYTE
JC RCVSTOT ;TIMEOUT
;
IF PMMI OR H8 OR DCH
CALL RCVERR ;TRANSMISSION ERROR?
JC RCVSERR ;YES, IF CARRY IS ON
ENDIF
;
DCR E ;DECREMENT NUM OF BYTES
JNZ RCVCRC2 ;GET BOTH BYTES
CALL CHKCRC ;CHECK RCVD CRC AGAINST CALC'D CRC
ORA A ;IS CRC OKAY?
JZ CHKSNUM ;YES, GO CHECK SECTOR NUMBERS
JMP RCVSERR ;GO CHECK ERROR LIMIT AND SEND NAK
;
;Previous sector repeated, due to the last ACK
;being garbaged. ACK it so sender will catch up
;
RECVACK CALL SENDACK ;SEND THE ACK,
JMP RCVSECT ;GET NEXT BLOCK
;
;Send an ACK for the sector
;
SENDACK MVI A,ACK ;GET ACK
CALL SEND ;..AND SEND IT
RET
;
;----> SENDHDR: Send the sector header
;
;SEND: (SOH) (block #) (complemented block #)
;
SENDHDR MVI A,SOH ;SEND..
CALL SEND ;..SOH,
LDA SECTNO ;THEN SEND..
CALL SEND ;..SECTOR #
LDA SECTNO ;THEN SECTOR #
CMA ;..COMPLEMENTED..
CALL SEND ;..SECTOR #
RET ;FROM SENDHDR
;
;----> SENDSEC: Send the data sector
;
SENDSEC MVI C,0 ;INIT CKSUM
CALL CLRCRC ;CLEAR THE CRC COUNTER
LXI H,BASE+80H ;POINT TO BUFFER
;
SENDC MOV A,M ;GET A CHAR
CALL SEND ;SEND IT
INR L ;POINT TO NEXT CHAR
JNZ SENDC ;LOOP IF <100H
RET ;FROM SENDSEC
;
;----> SENDCKS: Send the checksum
;
SENDCKS MOV A,C ;SEND THE..
CALL SEND ;..CHECKSUM
RET ;FROM SENDCKS
;
;----> SENDCRC: Send the two Cyclic Redundancy
; Check characters. Call FINCRC
; to calc the CRC which will be in
; d,e regs upon return.
;
SENDCRC CALL FINCRC ;CALC THE CRC FOR THIS SECTOR
MOV A,D ;PUT FIRST CRC BYTE IN ACCUM
CALL SEND ;SEND IT
MOV A,E ;PUT SECOND CRC BYTE IN ACCUM
CALL SEND ;SEND IT
XRA A ;SET ZERO RETURN CODE
RET
;
;----> GETACK: Get the ACK on the sector
;
;Returns with carry clear if ACK received.
;If an ACK is not received, the error count
;is incremented, and if less than "ERRLIM",
;carry is set and control returns. If the
;error count is at "ERRLIM", the program
;aborts.
;
GETACK MVI B,10 ;WAIT 10 SECONDS MAX
CALL RECVDG ;RECV W/GARBAGE COLLECT
JC GETATOT ;TIMED OUT
CPI ACK ;OK? (CARRY OFF IF =)
RZ ;YES, RET FROM GETACK
;
;Timeout or error on ACK - bump error count
;
ACKERR LDA ERRCT ;GET COUNT
INR A ;BUMP IT
STA ERRCT ;SAVE BACK
CPI ERRLIM ;AT LIMIT?
RC ;NOT AT LIMIT
;
;Reached error limit
;
CSABORT CALL ERXIT
DB '++CAN''T SEND SECTOR '
DB '- ABORTING++',CR,LF,'$'
;
;Timeout getting ACK
;
GETATOT JMP ACKERR ;NO MSG
;
ABORT LXI SP,STACK
;
ABORTL MVI B,1 ;1 SEC. W/O CHARS.
CALL RECV
JNC ABORTL ;LOOP UNTIL SENDER DONE
MVI A,CAN ;CONTROL X
CALL SEND ;STOP SENDING END
;
ABORTW MVI B,1 ;1 SEC W/O CHARS.
CALL RECV
JNC ABORTW ;LOOP UNTIL SENDER DONE
MVI A,' ' ;GET A SPACE...
CALL SEND ;TO CLEAR OUT CONTROL X
CALL ERXIT ;EXIT WITH ABORT MSG
DB 'XMODEM PROGRAM CANCELLED',CR,LF,'$'
;
;----> INCRSNO: Increment sector #
;
INCRSNO LDA SECTNO ;INCR..
INR A ;..SECT..
STA SECTNO ;..NUMBER
RET
;
;----> CHEKFIL: See if file exists
;
;If it exists, say use a different name.
;
CHEKFIL LXI D,FCB ;POINT TO CTL BLOCK
MVI C,SRCHF ;SEE IF IT..
CALL BDOS ;..EXISTS
INR A ;FOUND?
RZ ;..NO, RETURN
CALL ERXIT ;EXIT, PRINT ERROR MESSAGE
DB '++FILE EXISTS - USE A DIFFERENT NAME++'
DB CR,LF,'$'
;
;----> MAKEFIL: Makes the file to be received
;
MAKEFIL XRA A ;SET EXT & REC # TO 0
STA FCBEXT
STA FCBSNO
LXI D,FCB ;POINT TO FCB
MVI C,MAKE ;GET BDOS FNC
CALL BDOS ;TO THE MAKE
INR A ;FF=BAD?
RNZ ;OPEN OK
;Directory full - can't make file
CALL ERXIT
DB '++ERROR - CAN''T MAKE FILE++',CR,LF
DB 'Directory must be full',CR,LF,'$'
;
;----> CNREC: Computes record count, and saves it
; until successful file OPEN.
;
;LOOK UP THE FCB IN THE DIRECTORY
CNREC MVI A,'?' ;MATCH ALL EXTENTS
STA FCBEXT
MVI A,0FFH
STA MAXEXT ;INIT MAX EXT NO.
MVI C,SRCHF ;GET 'SEARCH FIRST' FNC
LXI D,FCB
CALL BDOS ;READ FIRST
INR A ;WERE THERE ANY?
JNZ SOME ;GOT SOME
CALL ERXIT
DB '++FILE NOT FOUND++$'
;
;READ MORE DIRECTORY ENTRIES
MOREDIR MVI C,SRCHN ;SEARCH NEXT
LXI D,FCB
CALL BDOS ;READ DIR ENTRY
INR A ;CHECK FOR END (0FFH)
JNZ SOME ;NOT END OF DIR...PROCESS EXTENT
LDA MAXEXT ;HIT END...GET HIGHEST EXTENT NO. SEEN
MOV L,A ;WHICH GIVES EXTENT COUNT - 1
MVI H,0
MOV D,H
LDA RCNT ;GET RECORD COUNT OF MAX EXTENT SEEN
MOV E,A ;SAVE IT IN DE
DAD H
DAD H ;MULTIPLY # OF EXTENTS - 1
DAD H ; TIMES 128
DAD H
DAD H
DAD H
DAD H
DAD D ;ADD IN SIZE OF LAST EXTENT
SHLD RCNT ;SAVE TOTAL RECORD COUNT
RET ;AND EXIT
;
;POINT TO DIRECTORY ENTRY
SOME DCR A ;UNDO PREV 'INR A'
ANI 3 ;MAKE MODULUS 4
ADD A ;MULTIPLY...
ADD A ;..BY 32 BECAUSE
ADD A ;..EACH DIRECTORY
ADD A ;..ENTRY IS 32
ADD A ;..BYTES LONG
LXI H,BASE+80H ;POINT TO BUFFER
ADD L ;POINT TO ENTRY
ADI 15 ;OFFSET TO RECORD COUNT
MOV L,A ;HL NOW POINTS TO REC COUNT
MOV B,M ;GET RECORD COUNT
DCX H
DCX H ;BACK DOWN TO EXTENT NUMBER
DCX H
LDA MAXEXT ;COMPARE WITH CURRENT MAX.
ORA A ;IF NO MAX YET
JM BIGGER ;THEN SAVE RECORD COUNT ANYWAY
CMP M
JNC MOREDIR
;
BIGGER: MOV A,B ;SAVE NEW RECORD COUNT
STA RCNT
MOV A,M ;SAVE NEW MAX. EXTENT NO.
STA MAXEXT
JMP MOREDIR ;GO FIND MORE EXTENTS
;
;----> OPENFIL: Opens the file to be sent
;
OPENFIL XRA A ;SET EXT & REC # TO 0 FOR PROPER OPEN
STA FCBEXT
STA FCBSNO
LXI D,FCB ;POINT TO FILE
MVI C,OPEN ;GET FUNCTION
CALL BDOS ;OPEN IT
INR A ;OPEN OK?
JNZ OPENOK ;..YES
CALL ERXIT ;..NO, ABORT
DB '++OPEN ERROR++',CR,LF,'$'
;
;Check for distribution-protected file
;
OPENOK LDA FCB+1 ;FIRST CHAR OF FILE NAME
ANI 80H ;CHECK BIT 7
JNZ OPENOT ;If on, file can't be sent.
LDA FCB+2 ;Also check "f2" for tag.
ANI 80H ;Is it set?
JZ OPENOK2 ;If not, ok to send file.
;
OPENOT CALL ERXIT ;EXIT W/MESSAGE
DB '++THIS FILE IS NOT FOR DISTRIBUTION, SORRY++'
DB CR,LF,'$'
;
OPENOK2 EQU $
;
IF NOLBS OR NOCOMS ;CHECK FOR SEND RESTRICTIONS
LXI H,FCB+11
MOV A,M ;CHECK FOR PROTECT ATTR
ANI 7FH ;REMOVE CP/M 2.x ATTRS
ENDIF ;NOLBS OR NOCOMS
;
IF NOLBS ;DON'T ALLOW '#' TO BE SENT.
CPI '#' ;CHK FOR '#' AS LAST FIRST
JZ OPENOT ;IF '#', CAN'T SEND, SHOW WHY
ENDIF ;NOLBS
;
IF NOCOMS ;DON'T ALLOW .COM TO BE SENT
CPI 'M' ;IF NOT, CHK FOR '.COM'
JNZ OPENOK3 ;IF NOT, OK TO SEND
DCX H
MOV A,M ;CHK NEXT CHAR
ANI 7FH ;STRIP ATTRIBUTES
CPI 'O' ; 'O'?
JNZ OPENOK3 ;IF NOT, OK TO SEND
DCX H
MOV A,M ;NOW CHK FIRST CHAR
ANI 7FH ;STRIP ATTRIBUTES
CPI 'C' ; 'C' AS IN '.COM'?
JNZ OPENOK3 ;IF NOT, CONTINUE
CALL ERXIT ;EXIT W/MESSAGE
DB '++CAN''T SEND A .COM FILE++'
DB CR,LF,'$'
ENDIF ;NOCOMS
;
OPENOK3 CALL ILPRT ;PRINT:
DB 'FILE OPEN - SIZE: ',0
LHLD RCNT ; Get record count.
CALL DECOUT ;PRINT DECIMAL NUMBER OF SECTORS
CALL ILPRT ;Print:
DB ' (',0
CALL DHXOUT ;Now print size in hex.
CALL ILPRT ;PRINT:
DB 'H) SECTORS',CR,LF,0
RET
;
;----> CLOSFIL: Closes the received file
;
CLOSFIL LXI D,FCB ;POINT TO FILE
MVI C,CLOSE ;GET FUNCTION
CALL BDOS ;CLOSE IT
INR A ;CLOSE OK?
RNZ ;..YES, RETURN
CALL ERXIT ;..NO, ABORT
DB '++CAN''T CLOSE FILE++',CR,LF,'$'
;
;
;----> DECOUT: Decimal output routine
;
DECOUT: PUSH B
PUSH D
PUSH H
LXI B,-10
LXI D,-1
;
DECOU2: DAD B
INX D
JC DECOU2
LXI B,10
DAD B
XCHG
MOV A,H
ORA L
CNZ DECOUT
MOV A,E
ADI '0'
CALL CTYPE
POP H
POP D
POP B
RET
;
;----> DHXOUT: - double precision hex output routine.
; Call with hex value in HL.
;
DHXOUT PUSH H ;Save H,L
PUSH PSW ;Save A
MOV A,H ;Get MS byte.
CALL HEXO ;Output hi order byte.
MOV A,L ;Get LS byte.
CALL HEXO ;Output lo order byte.
POP PSW ;Restore A
POP H ;Restore H,L
RET ;Return to caller.
;
;
;----> RDSECT: Reads a sector
;
;For speed, this routine buffers up 16
;sectors at a time.
;
RDSECT LDA SECINBF ;GET # SECT IN BUFF.
DCR A ;DECREMENT..
STA SECINBF ;..IT
JM RDBLOCK ;EXHAUSTED? NEED MORE.
LHLD SECPTR ;GET POINTER
LXI D,BASE+80H ;TO DATA
CALL MOVE128 ;MOVE TO BUFFER
SHLD SECPTR ;SAVE BUFFER POINTER
RET ;FROM "READSEC"
;
;Buffer is empty - read in another block of 16
;
RDBLOCK LDA EOFLG ;GED EOF FLAG
CPI 1 ;IS IT SET?
STC ;TO SHOW EOF
RZ ;GOT EOF
MVI C,0 ;SECTORS IN BLOCK
LXI D,DBUF ;TO DISK BUFFER
;
RDSECLP PUSH B
PUSH D
MVI C,STDMA ;SET DMA..
CALL BDOS ;..ADDR
LXI D,FCB
MVI C,READ
CALL BDOS
POP D
POP B
ORA A ;READ OK?
JZ RDSECOK ;YES
DCR A ;EOF?
JZ REOF ;GOT EOF
;
;Read error
;
CALL ERXIT
DB '++FILE READ ERROR++',CR,LF,'$'
;
RDSECOK LXI H,80H ;ADD LENGTH OF ONE SECTOR...
DAD D ;...TO NEXT BUFF
XCHG ;BUFF TO DE
INR C ;MORE SECTORS?
MOV A,C ;GET COUNT
CPI 16 ;DONE?
JZ RDBFULL ;..YES, BUFF IS FULL
JMP RDSECLP ;READ MORE
;
REOF MVI A,1
STA EOFLG ;SET EOF FLAG
MOV A,C
;
;Buffer is full, or got EOF
;
RDBFULL STA SECINBF ;STORE SECTOR COUNT
LXI H,DBUF ;INIT BUFFER..
SHLD SECPTR ;..POINTER
LXI D,BASE+80H ;RESET..
MVI C,STDMA ;..DMA..
CALL BDOS ;..ADDR
JMP RDSECT ;PASS SECT TO CALLER
;
;----> WRSECT: Write a sector
;
;Writes the sector into a buffer. When 16
;have been written, writes the block to disk.
;
;Entry point "WRBLOCK" flushes the buffer at EOF.
;
WRSECT LHLD SECPTR ;GET BUFF ADDR
XCHG ;TO DE FOR MOVE
LXI H,BASE+80H ;FROM HERE
CALL MOVE128 ;MOVE TO BUFFER
XCHG ;SAVE NEXT..
SHLD SECPTR ;..BLOCK POINTER
LDA SECINBF ;BUMP THE..
INR A ;..SECTOR #..
STA SECINBF ;..IN THE BUFF
CPI 16 ;HAVE WE 16?
RNZ ;NO, RETURN
;
;----> WRBLOCK: Writes a block to disk
;
WRBLOCK LDA SECINBF ;# SECT IN BUFFER
ORA A ;0 MEANS END OF FILE
RZ ;NONE TO WRITE
MOV C,A ;SAVE COUNT
LXI D,DBUF ;POINT TO DISK BUFF
;
DKWRLP PUSH H
PUSH D
PUSH B
MVI C,STDMA ;SET DMA
CALL BDOS ;TO BUFFER
LXI D,FCB ;THEN WRITE
MVI C,WRITE ;..THE..
CALL BDOS ;..BLOCK
POP B
POP D
POP H
ORA A
JNZ WRERR ;OOPS, ERROR
LXI H,80H ;LENGTH OF 1 SECT
DAD D ;HL= NEXT BUFF
XCHG ;TO DE FOR SETDMA
DCR C ;MORE SECTORS?
JNZ DKWRLP ;..YES, LOOP
XRA A ;GET A ZERO
STA SECINBF ;RESET # OF SECTORS
LXI H,DBUF ;RESET BUFFER..
SHLD SECPTR ;..POINTER
;
RSDMA LXI D,BASE+80H ;RESET..
MVI C,STDMA ;..DMA..
CALL BDOS ;..ADDR
RET
;
WRERR CALL RSDMA ;RESET DMA TO NORM.
MVI C,CAN ;CANCEL..
CALL SEND ;..SENDER
CALL ERXIT ;EXIT W/MSG:
DB '++ERROR WRITING FILE++',CR,LF,'$'
;
;----> RECV: Receive a character
;
;Timeout time is in B, in seconds. Entry via
;"RECVDG" deletes garbage characters on the
;line. For example, having just sent a sector,
;calling RECVDG will delete any line-noise-induced
;characters "long" before the ACK/NAK would
;be received.
;
RECVDG EQU $ ;RECEIVE W/GARBAGE DELETE
IN MODDATP ;GET A CHAR
IN MODDATP ;..TOTALLY PURGE UART
;
RECV PUSH D ;SAVE
;
IF FASTCLK ;4MHZ?
MOV A,B ;GET TIME REQUEST
ADD A ;DOUBLE IT
MOV B,A ;NEW TIME IN B
ENDIF
;
MSEC LXI D,50000 ;1 SEC DCR COUNT
;
IF NOT DCH
MWTI IN MODCTLP ;CHECK STATUS
ENDIF
;
IF DCH
MWTI IN MODCTL2 ;CHECK STATUS
ENDIF
;
IF PMMI AND FRNTPNL
OUT PANEL ;DISPLAY STATUS ON PANEL LIGHTS
ENDIF
;
ANI MODRCVB ;ISOLATE BIT
CPI MODRCVR ;READY?
JZ MCHAR ;GOT CHAR
DCR E ;COUNT..
JNZ MWTI ;..DOWN..
DCR D ;..FOR..
JNZ MWTI ;..TIMEOUT
DCR B ;MORE SECONDS?
JNZ MSEC ;YES, WAIT
;
;Test for the presence of carrier - if none, go to
;CARCK and continue testing for 15 seconds. If carrier
;returns, continue. If is doesn't return, exit.
;
IF EXTMOD OR H8 OR DCH
IN MODCTL2 ;READ MODEM STATUS
ENDIF
;
IF PMMI
IN BAUDRP ;READ MODEM STATUS
ENDIF
;
IF PMMI AND FRNTPNL
OUT PANEL ;DISPLAY STATUS ON PANEL LIGHTS
ENDIF
;
ANI MODDCDB ;CARRIER DETECT MASK
CPI MODDCDA ;IS IT STILL ON?
CNZ CARCK ;IF NOT, TEST FOR 15 SECONDS
;
;Modem timed out receiving - but carrier still on.
;
POP D ;RESTORE D,E
STC ;CARRY SHOWS TIMEOUT
RET
;
;Got character from modem
;
MCHAR:
IF PMMI OR H8 OR DCH
;Check to see if there was a framing error,
;overrun, or parity error.
;
IF PMMI OR H8
IN MODCTLP ;GET MODEM STATUS
ENDIF
;
IF DCH
IN MODCTL2 ;GET MODEM STATUS
ENDIF
;
MOV D,A ;SAVE STATUS
ANI MODFRME ;FRAMING ERROR?
CPI MODFRME
JNZ MCHAR2 ;NO, CHECK FOR OVERRUN
LDA ERRCDE ;GET RECV ERR CODE
ORI MODFRME ;TURN ON RECV ERR CODE
STA ERRCDE ;PUT IT BACK
;
MCHAR2: MOV A,D ;RESTORE MODEM STATUS
ANI MODOVRE ;OVERRUN?
CPI MODOVRE
JNZ MCHAR3 ;NO, CHECK FOR PARITY ERROR
LDA ERRCDE
ORI MODOVRE ;TURN ON RECV ERR CODE
STA ERRCDE
;
MCHAR3: MOV A,D ;RESTORE MODEM STATUS
ANI MODPARE ;PARITY ERROR?
CPI MODPARE
JNZ MCHAR4 ;NO, GET DATA CHAR
LDA ERRCDE
ORI MODPARE
STA ERRCDE
;
MCHAR4:
ENDIF ;PMMI OR H8 OR DCH
;
;Get data char
;
IN MODDATP ;READ THE CHAR
POP D ;RESTORE DE
;
;Calc checksum and CRC
;
PUSH PSW ;SAVE THE CHAR
CALL UPDCRC ;CALC CRC
ADD C ;ADD TO CHECKSUM
MOV C,A ;SAVE CHECKSUM
POP PSW ;RESTORE CHAR
ORA A ;CARRY OFF: NO ERROR
RET ;FROM "RECV"
;
;CARCK - common 15 second carrier test for RECV and
;SEND. If carrier returns within 15 seconds, normal
;program execution continues. Else, it will abort
;to CP/M via EXIT.
;
CARCK MVI E,150 ;VALUE FOR 15 SECOND DELAY
;
CARCK1 CALL DELAY ;KILL .1 SECONDS
;
IF EXTMOD OR H8 OR DCH
IN MODCTL2 ;READ MODEM STATUS
ENDIF
;
IF PMMI
IN BAUDRP ;READ MODEM STATUS
ENDIF
;
IF PMMI AND FRNTPNL
OUT PANEL ;DISPLAY STATUS
ENDIF
;
ANI MODDCDB ;CARRIER DETECT MASK
CPI MODDCDA ;IS IT STILL ON?
RZ ;RETURN IF CARRIER ON
DCR E ;HAS 15 SECONDS EXPIRED?
JNZ CARCK1 ;IF NOT, CONTINUE TESTING
JMP EXIT ;ELSE, ABORT TO CP/M.
;
;DELAY - 100 millisecond delay.
;
DELAY PUSH B ;SAVE B,C
;
IF FASTCLK ;IF 4MHZ CLOCK
LXI B,16667 ;VALUE FOR 100MS DELAY
ENDIF
;
IF NOT FASTCLK
LXI B,8334 ;VALUE FOR 100MS DELAY
ENDIF
;
DELAY2 DCX B ;UPDATE COUNT
MOV A,B ;GET MS BYTE
ORA C ;COUNT = ZERO?
JNZ DELAY2 ;IF NOT, CONTINUE
POP B ;RESTORE B,C
RET ;RETURN TO CARCK1.
;
;----> SEND: Send a character to the modem
;
SEND PUSH PSW ;SAVE THE CHARACTER
CALL UPDCRC ;calc the crc
ADD C ;CALC CKSUM
MOV C,A ;SAVE CKSUM
;
IF NOT DCH
SENDW IN MODCTLP ;GET STATUS
ENDIF
;
IF DCH
SENDW IN MODCTL2 ;GET STATUS
ENDIF
;
IF PMMI AND FRNTPNL
OUT PANEL ;DISPLAY STATUS
ENDIF
;
ANI MODSNDB ;ISOLATE READY BIT
CPI MODSNDR ;READY?
JZ SENDR ;..YES, GO SEND
;
;Xmit status not ready, so test for carrier before
;looping - if lost, go to CARCK and give it up to 15
;seconds to return. If it doesn't return abort via
;EXIT.
;
PUSH D ;Save D,E
;
IF EXTMOD OR H8 OR DCH
IN MODCTL2 ;READ MODEM STATUS
ENDIF
;
IF PMMI
IN BAUDRP ;READ MODEM STATUS
ENDIF
;
IF PMMI AND FRNTPNL
OUT PANEL ;DISPLAY STATUS
ENDIF
;
ANI MODDCDB ;CARRIER DETECT MASK
CPI MODDCDA ;IS IT STILL ON?
CNZ CARCK ;IF NOT, CONTINUE TESTING IT
POP D ;RESTORE D,E
JMP SENDW ;ELSE, WAIT FOR XMIT READY.
;
;Xmit status ready, carrier still on - send the data.
;
SENDR POP PSW ;GET CHAR
OUT MODDATP ;OUTPUT IT
RET ;FROM "SEND"
;
;----> WAITNAK: Waits for initial NAK
;
;To ensure no data is sent until the receiving
;program is ready, this routine waits for the
;first timeout-NAK or the letter 'C' for CRC
;from the receiver. If CRC is in effect, then
;Cyclic Redundancy Checks are used instead of
;checksums.
;(E) contains the # of seconds to wait.
;
WAITNAK MVI B,1 ;TIMEOUT DELAY
CALL RECV ;DID WE GET..
CPI NAK ;..A NAK?
RZ ;YES, SEND BLOCK
CPI CRC ;CRC INDICATED?
JZ WAITCRC ;YES, GO PUT CRC IN EFFECT
DCR E ;80 TRIES?
JZ ABORT ;YES, ABORT
JMP WAITNAK ;NO, LOOP
;
;----> WAITCRC: Turn on CRC Flag
;
WAITCRC XRA A ;ZERO ACCUM
STA CRCFLG ;TURN ON CRC OPT
RET
;
;----> MOVEFCB: Moves FCB(2) to FCB
;
;In order to make the XMODEM command 'natural',
;i.e. XMODEM SEND FILENAME (MODEM S FN.FT) rather
;than XMODEM FILENAME SEND (MODEM FN.FT S), this
;routine moves the filename from the second FCB
;to the first.
;
MOVEFCB LXI H,FCB+16 ;FROM
LXI D,FCB ;TO
MVI B,16 ;LEN
CALL MOVE ;DO THE MOVE
XRA A ;GET 0
STA FCBSNO ;ZERO SECTOR #
STA FCBEXT ;..AND EXTENT
RET
;
CTYPE PUSH B ;SAVE..
PUSH D ;..ALL..
PUSH H ;..REGS
MOV E,A ;CHAR TO E
MVI C,WRCON ;GET BDOS FNC
CALL BDOS ;PRIN THE CHR
POP H ;RESTORE..
POP D ;..ALL..
POP B ;..REGS
RET ;FROM "CTYPE"
;
HEXO PUSH PSW ;SAVE FOR RIGHT DIGIT
RAR ;RIGHT..
RAR ;..JUSTIFY..
RAR ;..LEFT..
RAR ;..DIGIT..
CALL NIBBL ;PRINT LEFT DIGIT
POP PSW ;RESTORE RIGHT
;
NIBBL ANI 0FH ;ISOLATE DIGIT
CPI 10 ;IS IT <10?
JC ISNUM ;YES, NOT ALPHA
ADI 7 ;ADD ALPHA BIAS
;
ISNUM ADI '0' ;MAKE PRINTABLE
JMP CTYPE ;..THEN TYPE IT
;
;----> ILPRT: Inline print of message
;
;The call to ILPRT is followed by a message,
;binary 0 as the end.
;
ILPRT XTHL ;SAVE HL, GET HL=MSG
;
ILPLP MOV A,M ;GET CHAR
ORA A ;END OF MSG?
JZ ILPRET ;..YES, RETURN
CALL CTYPE ;TYPE THE MSG
INX H ;TO NEXT CHAR
JMP ILPLP ;LOOP
;
ILPRET XTHL ;RESTORE HL
RET ;PAST MSG
;
;----> ERXIT: Exit printing message following call
;
ERXIT POP D ;GET MESSAGE
MVI C,PRINT ;GET BDOS FNC
CALL BDOS ;PRINT MESSAGE
;
EXIT LHLD STACK ;GET ORIGINAL STACK
SPHL ;RESTORE IT
RET ;--EXIT-- TO CP/M
;
;Move 128 characters
;
MOVE128 MVI B,128 ;SET MOVE COUNT
;
;Move from (HL) to (DE) length in (B)
;
MOVE MOV A,M ;GET A CHAR
STAX D ;STORE IT
INX H ;TO NEXT "FROM"
INX D ;TO NEXT "TO"
DCR B ;MORE?
JNZ MOVE ;..YES, LOOP
RET ;..NO, RETURN
;
;************************************************************************
;* CRCSUBS (Cyclic Redundancy Code Subroutines) version 1.20 *
;* 8080 Mnemonics *
;* *
;* These subroutines will compute and check a true 16-bit *
;* Cyclic Redundancy Code for a message of arbitrary length. *
;* *
;* The use of this scheme will guarantee detection of all *
;* single and double bit errors, all errors with an odd *
;* number of error bits, all burst errors of length 16 or *
;* less, 99.9969% of all 17-bit error bursts, and 99.9984% *
;* of all possible longer error bursts. (Ref: Computer *
;* Networks, Andrew S. Tanenbaum, Prentiss-Hall, 1981) *
;* *
;* *
;* There are four entry points, which are used as follows: *
;* *
;* CLRCRC - A call to this entry resets the CRC accumulator. *
;* It must be called at the start of each message. *
;* *
;* Entry Parameters: None. *
;* *
;* Exit Conditions: CRC accumulator cleared. *
;* All registers preserved. *
;* *
;* *
;* UPDCRC - A call to this entry updates the CRC accumulator. *
;* It must be called once for each byte in the *
;* message for which the CRC is being calculated. *
;* *
;* Entry Parameters: (A) = a byte to be included *
;* in the CRC calculation. *
;* *
;* Exit Conditions: CRC accumulator updated. *
;* All registers preserved. *
;* *
;* *
;* FINCRC - A call to this entry finishes the CRC calculation *
;* for a message which is to be TRANSMITTED. It must *
;* be called after the last byte of the message has *
;* been passed thru UPDCRC. It returns the calculated *
;* CRC bytes, which must be transmitted as the final *
;* two bytes of the message (first D, then E). *
;* *
;* Entry Parameters: None. *
;* *
;* Exit Conditions: (DE) = calculated CRC bytes. *
;* All other registers preserved. *
;* *
;* *
;* CHKCRC - A call to this routine checks the CRC bytes of *
;* a RECEIVED message and returns a code to indicate *
;* whether the message was received correctly. It must *
;* be called after the message AND the two CRC bytes *
;* have been received AND passed thru UPDCRC. *
;* *
;* Entry Parameters: None. *
;* *
;* Exit Conditions: (A) = 0 if message ok. *
;* (A) = -1 if message garbled. *
;* All other registers preserved. *
;* *
;************************************************************************
;* *
;* Designed & coded by Paul Hansknecht, June 13, 1981 *
;* *
;* *
;* Copyright (c) 1981, Carpenter Associates *
;* Box 451 *
;* Bloomfield Hills, MI 48013 *
;* 313/855-3074 *
;* *
;* This program may be freely reproduced for non-profit use. *
;* *
;************************************************************************
;
; ENTRY CLRCRC,UPDCRC,FINCRC,CHKCRC
;
CLRCRC: EQU $ ; Reset CRC Accumulator for a new message.
PUSH H
LXI H,0
SHLD CRCVAL
POP H
RET
;
UPDCRC: EQU $ ; Update CRC Accumulator using byte in (A).
PUSH PSW
PUSH B
PUSH H
MVI B,8
MOV C,A
LHLD CRCVAL
UPDLOOP:MOV A,C
RLC
MOV C,A
MOV A,L
RAL
MOV L,A
MOV A,H
RAL
MOV H,A
JNC SKIPIT
MOV A,H ; The generator is X^16 + X^12 + X^5 + 1
XRI 10H ; as recommended by CCITT.
MOV H,A ; An alternate generator which is often
MOV A,L ; used in synchronous transmission protocols
XRI 21H ; is X^16 + X^15 + X^2 + 1. This may be
MOV L,A ; used by substituting XOR 80H for XOR 10H
SKIPIT: DCR B ; and XOR 05H for XOR 21H in the adjacent code.
JNZ UPDLOOP
SHLD CRCVAL
POP H
POP B
POP PSW
RET
;
FINCRC: EQU $ ; Finish CRC calc for outbound message.
PUSH PSW
XRA A
CALL UPDCRC
CALL UPDCRC
PUSH H
LHLD CRCVAL
MOV D,H
MOV E,L
POP H
POP PSW
RET
;
CHKCRC: EQU $ ; Check CRC bytes of received message.
PUSH H
LHLD CRCVAL
MOV A,H
ORA L
POP H
RZ
MVI A,0FFh
RET
;
;
CRCVAL DW 0
;
;
;
;Temporary storage area
;
MAXEXT DB 0 ;HIGHEST EXTENT NO. SEEN IN FILE SIZE CALC.
RCNT DW 0 ;RECORD COUNT
RCVSNO DB 0 ;SECT # RECEIVED
SECTNO DB 0 ;CURRENT SECTO