; ; Receive a record - returns with carry bit set if EOT received ; rcvrecd: xra a; Clear error count sta errct rcvrpt: call funchk; Check function keys mvi b,10; 10-seconds to get 1st byte lda frstim ora a jnz rcvr1; If started, skip next line mvi b,3+1; Check every 4 seconds until started rcvr1: call recv; Get any char received jc rcvstot; Timeout error if no char received cpi soh; See if it is SOH jz rcvsoh; Got SOH, get record cpi stx; See if it is STX for 1k blocks jz rcvstx; Got STR, get record cpi cancel; Was it a CTL-X to abort? jz ckcan; If yes, check for aborting ora a; Get another char, if a null jz rcvrpt cpi '{'; V.22 synch char, ignore jz rcvrpt cpi '{' + 80h; V.22 synch char with high bit set jz rcvrpt cpi eot stc rz; with carry on end of transmission cpi crc; Ignore our own char coming back jz rcvrpt cpi ksnd; Ignore our own char coming back jz rcvrpt cpi nak; Ignore our own char coming back jz rcvrpt mvi a,1; Prevents going to modem sta remoff call crlf mov a,b call hexo call ilprt db 'H received not SOH ',0 ; " " ; Didn't get SOH or EOT or did not get valid header so purge the line, ; then send NAK. ; rcvsr: call wait1; Flush incoming call ckabort; Want to quit now? lda frstim; Get first time switch ora a; Has first 'SOH' been received? mvi a,nak jnz rcvsr1; Yes, then send 'NAK' lda crcflg ora a mvi a,nak jz rcvsr1; Not CRC, signal checksum with NAK mvi a,crc; Else tell sender we have 'CRC' call send lda kflg ora a jz rcvsr1; not requesting 1k transmissions mvi a,ksnd; Signal we also have 1k capability rcvsr1: call send lda errct inr a; Increment error count sta errct mov b,a; Keep error count for now lda frstim; Have we gotten under way yet? ora a mov a,b; get error count back jz rcvsr2; Not started yet, exit cpi 10; 10 errors the limit, once under way jnc rcvsabt; Abort if over the limit call rdcount; Display record count before repeating jmp rcvrpt; Less than 10, keep going ; rcvsr2: cpi 10; 10 times for 1k/CRC yet? (40 seconds) jc rcvrpt; Keep trying if less xra a; Else flip to checksum mode sta crcflg mov a,b; Get the count back cpi 15; Another 5 times for checksum? jc rcvrpt; If less, try again, quit at 60 seconds ; " " ; Error limit exceeded, so abort rcvsabt: xra a sta remoff; Allow console output to remote lxi sp,stack; Clear the stack just in case call closfil; Keep whatever we got call ilprt db cr,lf,cr,lf,'++ RECEIVED FILE CANCELLED ++',0 call delfile; Delete received file call erxit; Print 2nd half of message db '++ UNFINISHED FILE DELETED ++','$' ; ; Deletes the received file (used if receive aborts) ; delfile: mvi a,delet call fileop; Delete it inr a rnz; Success, return call erxit db cr,lf,'++ Can''t delete received file ++','$' ; ; Aborts with 1 CTL-X if first time flag is not set, two otherwise ; ckcan: lda frstim; 1st time flag set yet? ora a jz rcvsabt; If not, Abort and close file mvi b,2; Max 2 seconds for extra CTL-X call recv jc rcvrpt; No additional char, ignore CTL-X cpi cancel; If a 2nd CTL-X, abort and close file jz rcvsabt jmp rcvrpt; Else wait for a STX, SOH or timeout ; ; Timed out on receive ; rcvstot: lda frstim; 1st time flag set yet? ora a jz rcvsr; If not, don't show an error sta remoff call ilprt db '++ Timeout waiting for character ++',cr,lf,0 jmp rcvsr; Bump error count, etc. ; ; Got a STX - set KFLG for 1k ; rcvstx: sta kflg; Set 1k flag sta crcflg; Ensure in CRC mode for 1k blocks jmp rcvs1 ; ; Got SOH - get block number, block number complemented ; rcvsoh: xra a sta kflg; If SOH, clear the 1k flag rcvs1: mvi b,5; Timeout = 5 seconds mov a,b; Get something to store sta remoff; Error messages only shown locally sta frstim; Indicate first 'SOH' or 'STX' recvd. call recv; Get block number jc rcvstot; timeout mov d,a; Save block number mvi b,5; Timeout = 5 seconds call recv; Get complement of rcd no. jc rcvstot; Timeout cma cmp d; Same as original block number? jz rcvdata; Yes, get data ; " " ; Got bad record number in header call ilprt db '++ Error in header ++',cr,lf,0 jmp rcvsr; Go check error limit and send NAK ; rcvdata: mov a,d sta rcvcnt; get and save record num. mvi c,0; Init checksum lxi h,0; Init CRC shld crcval; Clear CRC counter call grcdsz; 128/1024 on kflg lhld recptr; Get buffer address ; " " rcvchr: mvi b,5; 5 sec timeout call recv; Get the character jc rcvstot; Timeout mov m,a; Store char inx h; Point to next char dcx d; One less to go mov a,d ora e jnz rcvchr; Not done, get next char lda crcflg; Using 'CRC'? ora a jnz rcvcrc; If yes go get 'CRC' ; " " ; Verify checksum mov d,c; Save checksum mvi b,5; Timeout length call recv; Get checksum jc rcvstot; Timeout cmp d; Checksum ok? jz chksnum; Yes, exit call ilprt db '++ Checksum error ++',cr,lf,0 jmp rcvsr; Go check the error limit and send NAK ; ; Got a record, it's a duplicate if equal to the previous number, ; it's OK if previous + 1 record chksnum: lda rcvcnt; Get received record number mov b,a lda rcdcnt; previous record number cmp b; Previous record repeated? jz rcvack; If yes 'ACK' to catch up inr a; Increment by 1 for 128 char block cmp b; Match this one we just got? jnz abort; No match, stop the sender, exit ret; Else return with carry not set, was ok ; ; Receive the Cyclic Redundancy Check characters (2 bytes) and see if ; the CRC received matches the one calculated. If they match, get next ; record, else send a NAK requesting the record be sent again. ; rcvcrc: mvi e,2; Number of bytes to receive rcvcrc2: mvi b,5; 5 second timeout call recv; Get CRC byte jc rcvstot; Timeout dcr e; Decrement the number of bytes jnz rcvcrc2; Get both bytes call crcchk; Check received CRC against calc'd CRC ora a jz chksnum; CRC ok, go check record numbers call ilprt db '++ CRC error ++',cr,lf,0 jmp rcvsr; Go check error limit and send NAK ; ; Previous record repeated, due to the last ACK being garbaged. ; ACK it so sender will catch up rcvack: call sndack jmp rcvrecd ; ; Send an ACK for the record ; sndack: mvi a,ack jmp send ; ; --------------------------------- ; ; Send the record header ; Send SOH, block number and complemented block number (3 bytes total) ; sndhdr: lda kflg; Sending 1k blocks? ora a mvi a,stx; If yes, send a STX rather than SOH jnz sndhd1 mvi a,soh; Send start of header sndhd1: call send sndhnm: lda rcdcnt; Send the current record number call send lda rcdcnt; Get the record number again cma; Complemented jmp send ; ; Send the data record ; sndrec: mvi c,0; Init checksum lxi h,0; Init CRC shld crcval call grcdsz; 128/1024 on kflg lhld recptr; Get buffer address sendc: mov a,m; Get a char call send; Send it inx h; next char dcx d mov a,e ora d jnz sendc; More ret ; ; Send the CRC or checksum value, whichever appropriate ; sndchk: lda crcflg; See if sending 'CRC' or 'checksum' ora a mov a,c; checksum value jz send; If chksum send it ; " " ; Send the two Cyclic Redundancy Check characters. Call FINCRC to ; calculate the CRC which will be in 'DE' upon return. ; sndcrc: call fincrc; Calculate the 'CRC' for this record mov a,d; Put first 'CRC' byte in accumulator call send; Send it mov a,e; Put second 'CRC' byte in accumulator call send; Send it xra a; Set zero return code ret ; ; After a record has been sent, and accepted, move the pointers ; forward 128 or 1024 characters for the next record. ; setptr: call grcdsz; 128/1024 on kflg lhld recptr; Get the buffer pointer dad d; Increment for the record just sent shld recptr; New buffer address for next block ret ; ; Get current protocol record size (128 or 1024, on KFLG) ; a,f,d,e grcdsz: lxi d,128 lda kflg ora a rz lxi d,1024 ret ; ; After a library transmission has been made, decrement the remaining ; records in that library file, then reset the 1k flag if less than 8 ; remaining. ; setlbr: lda kflg lxi d,-1 ora a jz setl1 lxi d,-8 setl1: lhld rcnt; Alter the records-sent count dad d shld rcnt; One less transmission to go ora a; 'K' flag already zero? rz; If yes, skip the rest ; " " ; See if enough records left to use 1k protocol if requested ; setflg: lhld rcnt mov a,h ora a rnz; 256 or more left mov a,l; Get number of records in 'L' register cpi 8 rnc; If 8 or more, keep going xra a; Reset the 'K' flag sta kflg ret ; ; After a record is sent, a character is returned telling if it was ; received properly or not. An ACK allows the next record to be sent. ; A NAK causes the current record to be resent. If no character (or ; any character other than ACK or NAK) is received after a short wait ; (10 to 12 seconds), a timeout error message is shown and the record ; will be resent. ; gtack: call mdinst jz gtack1; No char ready now call mdinp; Get the char cpi ack rz; ACK, return cpi nak jz gtack2; NAK, print error, then resend cpi cancel; CTL-X to cancel attempt? jz gtcan gtack1: mvi b,1; 1 second for an ACK or NAK call recv; Go wait for a char jc gtack2; timed out cpi ack rz; ACK, return cpi nak jz gtack3; NAK, error cpi cancel; CTL-X to cancel attempt? jz gtcan gtack2: mvi b,12; 12-seconds more for an ACK or NAK call recv; Go wait for a char jc gtatot; timeout cpi ack rz; ACK, return cpi '{' jz gtack2; ignore V.22 sync char cpi '{' + 080h jz gtack2; ignore V.22 sync char w/hi bit cpi cancel; CTL-X to cancel attempt? jnz gtack3 ; " " ; Two or more CTL-X will cancel the file transfer gtcan: mvi b,2; Up to 2 secs. for another CTL-X call recv mvi a,cancel; Get original char back jc gtack3; If no more CTL-X, display the first cpi cancel; Was it a second one? jz ackmsg; If yes, abort the file transfer gtack3: mov b,a; Save the char sta remoff; Send to console only lda chkeot; Sending EOT? ora a jnz ackerr; If yes, don't show error (for KMD) call ilprt db '++ ',0 mov a,b cpi nak jz gtack4 call hexo call ilprt db 'H',0 jmp gtack5 ; gtack4: call ilprt db 'NAK',0 ; " " gtack5: call ilprt db ' received not ACK ++',cr,lf,0 ; " " ; Timeout or error on ACK - bump error count then resend the record ; if error limit is not exceeded ; ackerr: lda accerr; Count accumulated errors on ACK inr a sta accerr lda errct inr a; Bump error count sta errct cpi 10 jnc ackmsg; At limit, send error msg and abort call rdcount; Else show the record count for repeat stc; Make sure carry is set for repeat ret; And go back ; ; ; Reached error limit ; ackmsg: call wait1; Flush input call send3can; Tell remote we are quitting mvi b,1; Wait for remote to perhaps quit too call recv mvi a,bs call send3x; Clear any CTL-X from buffer xra a sta remoff; Show message on remote and local call erxit db cr,lf,'++ FILE TRANSFER ABORTED ++','$' ; ; ; Timed out, with no character - set the carry bit and return ; gtatot: call ilprt db '++ Timeout - no character received ++',cr,lf,0 jmp ackerr ; ; Check the total error count vs. records sent, switch from 1k to 128 ; character transmissions if higher than operator selected value. ; gtratio: lda kflg ora a rz; Not 1k blocks, skip this lda errct; See if we got any errors last record cpi 4 jnc gtratio1; If 4 or more, switch to 128 size lda accerr; See if up to minimum errors yet cpi 3; Had as many as three errors yet? rc; If not, don't get excited too quickly lhld recdno; Get current record number increment lxi d,-8; Have not successfully sent this 1k yet dad d; Subtract the current increment xchg lhld accerr; Number of non-'ACK' errors in HL xchg call dvhlde; Get ratio in BC of records/hit call mspeed; get current speed cpi 5; 1200 baud? mvi a,71-1; for 1200 bps jz gtr1; If 1200, skip next line mvi a,43-1; for 2400 bps gtr1: cmp c; Compare with actual ratio rc; return if less hits than allowed ; " " gtratio1: xra a; Else reset the system to 128 sta kflg call ilprt db cr,lf,'Aborting 1k blocks, too many hits',cr,lf,0 ret ; ckabort: call constat ora a rz call conin cpi cancel rnz ; " " ; Aborts send or receive routines and returns to command line ; abort: lxi sp,stack call wait1; 1- sec delay to clear input call send3can; Show cancelling, remote may quit also call wait1; 1-second delay to clear input mvi a,bs call send3x abortx: xra a; Reset the batch flag sta bchflg call catch call erxit; Exit with abort message db cr,lf,'++ RXMD ABORTED ++','$' ; ; Send 3 cancels send3can: mvi a,cancel ; " " ; Send 3 copies of (a) send3x: call send call send jmp send ; ; Increment record number ; a,f incrno: push h push d lhld rcdcnt; Incr transmission count inx h shld rcdcnt lxi d,1 lda kflg ora a jz incrn1; not 1k blocks, incr by 1 mvi e,8; If 1k blocks incr. by 8 incrn1: lhld recdno; Get current record count dad d; Increment that count properly shld recdno call rdcount pop d pop h ret ; ; Display the record count on the local CRT ; a,f,h,l rdcount: lhld recdno; Get the record number for display mvi a,1 sta remoff; Set local only lda optsav; See if receive or send mode cpi 'R' jz rmsg call ilprt db cr,'Sending # ',0 jmp rest rmsg: call ilprt db cr,'Received # ',0 rest: lhld recdno call decout call ilprt db ' (',0 call t4hex call ilprt db 'h) ',0 jmp funchk; Check for function keys