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