NAME mssrcv ; File MSSRCV.ASM ; Edit history ; Last edit: 7 Jan 1988 ; 7 Jan 1988 Check for Control-E condition before sending a NAK. [jrd] ; 1 Jan 1988 version 2.30 ; 26 Dec 1987 Clean out unused pack.oldtry, etc., clean up. [jrd] ; 6 Dec 1987 Flush last disk buffer when aborting a transfer. [jrd] ; 8 Oct 1987 Ensure error pkts use 1 byte chksum at init stage. [jrd] ; 27 Aug 1987 Add tests for receiving to screen for error reports [jrd] ; 23 July 1987 Add buffer clear after opening new output file. [jrd] ; 7 June 1987 Add DOS errlev return of 2 for failure to receive. [jrd] ; 7 May 1987 Correct placement of begtim and endtim statistics calls. [jrd] ; 19 Oct 1986 Fix Rinit to use 1 byte checksums on Naks to S packets. ; 1 Oct 1986 Version 2.29a ; 17 Sept 1986 Fix file not being deleted when transfer fails. [jrd] ; 14 August 1986 Allow changing EOL characters. ; 9 August 1986 Allow Control-X/Z exit while getting 'S' packet. [jrd] ; 27 July 1986 Clear file opened flag to prevent unwanted closing of stdin. ; 16 June 1986 Add clearing of "flags.getflg" under read0: to prevent missing ; initial packet read for REC commands. [jrd] ; 26 May 1986 Revise code to permit serial display. [jrd] ; [2.29] code frozen on 6 May 1986 [jrd] public read12, read2, rin21, rfile3, read, updrtr, nak, rrinit include mssdef.h datas segment public 'datas' extrn data:byte, bufpnt:word, chrcnt:word, curchk:byte extrn comand:byte, flags:byte, pack:byte, trans:byte, dtrans:byte extrn diskio:byte, locfil:byte, maxtry:byte, imxtry:byte extrn fsta:word, errlev:byte ermes7 db '?Unable to receive initiate-packet$' ermes8 db '?Unable to receive file name$' ermes9 db '?Unable to receive end of file$' erms10 db '?Unable to receive data$' infms1 db cr,' Receiving: In progress',cr,lf,'$' infms3 db 'Completed',cr,lf,'$' infms4 db 'Failed',cr,lf,'$' infms6 db 'Interrupted',cr,lf,'$' filhlp2 db ' Local path or filename or carriage return$' ender db bell,bell,'$' crlf db cr,lf,'$' temp dw 0 filopn db 0 ; Says if disk file is open. datas ends code segment public 'code' extrn gofil:near, outbuf:near, comnd:near extrn spack:near, rpack:near, serini:near, serrst:near extrn spar:near, rpar:near, init:near, cxmsg:near extrn error:near, ptchr:near, erpos:near, rtpos:near extrn stpos:near, rprpos:near, nppos:near, nout:near extrn dodec:near, doenc:near, errpack:near, intmsg:near extrn send11:near, clrmod:near, ihostr:near extrn begtim:near, endtim:near, pktsize:near assume cs:code, ds:datas ; Update retry count and fall through to send a NAK. NAK0: call updrtr ; Update retry count. nak1: cmp flags.cxzflg,'E' ; Protocol abort sign? jne nak ; ne = no ret ; return to do current ('A') state NAK: mov ax,pack.pktnum ; Get the packet number we're waiting for mov pack.argblk,ax mov pack.argbk1,0 add fsta.nakscnt,1 ; count NAKs sent ;;; mov cx,0 ; No data, but this may change. ;;; call doenc ; So call encode. mov ah,'N' ; NAK that packet. call spack jmp abort nop ; So 'jmp rskp' in SPACK comes here ret ; Go around again. updrtr: cmp pack.state,'A' ; Supposed to abort? je upd0 ; Yes, don't bother with retry count. inc pack.numrtr ; Increment the number of retries. cmp flags.xflg,1 ; Writing to screen? je upd0 ; e = yes, skip this cmp pack.numrtr,0 ; non-zero item to display? je upd0 ; nothing to display call rtpos ; Position cursor. mov ax,pack.numrtr call nout ; Write the number of retries. upd0: ret ; Abort ABORT PROC NEAR cmp filopn,0 ; Disk file open? je abort0 ; e = no so don't close. cmp flags.xflg,1 ; Writing to the screen? je abort0 ; Yes, don't close "file". call outbuf ; flush last buffer to disk, ignore errors. nop nop nop mov ah,close2 ; DOS 2.0 file close push bx mov bx,diskio.handle ; file handle int dos pop bx mov filopn,0 ; say file is no longer open cmp flags.abfflg,0 ; save file after closing? je abort0 ; e = yes push dx mov dx,offset diskio.string ; get back file name mov ah,del2 ; delete the file int dos pop dx abort0: mov pack.state,'A' ; Otherwise abort. mov byte ptr locfil,0 ; clear local filename or errlev,2 ; set DOS error level ret ABORT ENDP ; init variables for read... rrinit proc near mov pack.numpkt,0 ; Set the number of packets to zero. mov pack.numrtr,0 ; Set the number of retries to zero. mov pack.pktnum,0 ; Set the packet number to zero. mov pack.numtry,0 ; Set the number of tries to zero. mov filopn,0 ; say no file opened yet ret rrinit endp ; RECEIVE command -- Some code moved to the GET routine READ PROC NEAR mov flags.nmoflg,0 ; Override file name from other host? mov bx,offset filhlp2 ; Text of help message. mov dx,offset locfil ; local file name string mov byte ptr locfil,0 ; clear it first mov ah,cmfile ; allow path names call comnd jmp r cmp ah,0 ; was an override filename given? je read0 ; e = no mov flags.nmoflg,1 ; yes, set flag = use this filename. read0: mov comand.cmrflg,0 ; Reset flag. mov comand.cmcr,0 mov ah,cmcfm ; Get a confirm. call comnd jmp r mov pack.state,'R' ; Set the state to receive initiate mov flags.getflg,0 ; Reset flag (not a Get command) mov flags.xflg,0 call rrinit ; init variables for read call init ; setup display form call serini ; Initialize serial port call ihostr ; initialize the host mov ax,0 ; tell statistics this was a receive operation call endtim ; get tod of end of file transfer call begtim ; start next statistics group READ12: ; Called by GET & SRVSND, display ok mov flags.cxzflg,0 ; Reset ^X/^Z flag. mov ah,trans.chklen ; get desired checksum length mov curchk,ah ; and remember it here test flags.remflg,dquiet ; quiet display mode? jnz read2 ; nz = yes, no printing cmp flags.destflg,2 ; Receiving to the screen? je read21 ; e = yes, no formatted display call stpos mov ah,prstr ; Be informative. mov dx,offset infms1 int dos test flags.remflg,dserial ; serial display mode? jnz read2 ; nz = yes, skip initial retry display call rtpos ; Position cursor. mov ax,pack.numrtr call nout ; Write the number of retries. READ2: ; Called by GENERIC server command dispatcher cmp flags.xflg,1 ; Are we receiving to the screen? je read21 ; e = skip the screen stuff. call nppos ; Position cursor for number of packets msg. mov ax,pack.numpkt call nout ; Write the number of packets. read21: mov ah,pack.state ; Get the state. cmp ah,'D' ; Data receive state? jne read3 call rdata ; yes, get data packets jmp read2 read3: cmp ah,'F' ; File receive state? jne read4 call rfile ; Call receive file. jmp read2 read4: cmp ah,'R' ; Receive initiate state? jne read5 call rinit jmp read2 ; Receive Complete state processor read5: push ax ; save status in ah xor ax,ax ; tell statistics this is a receive operation call endtim ; stop file timer call serrst ; Reset serial port. mov ah,curchk ; get working checksum mov trans.chklen,ah ; and restore for next file mov byte ptr locfil,0 ; clear local filename pop ax mov dx,offset infms3 ; Completed message cmp ah,'C' ; Receive complete state? je read6 ; e = yes, else receive failed. mov dx,offset infms4 ; Failed message or errlev,2 ; set DOS error level cmp filopn,2 ; file still open? jne read6 ; ne = no. push dx call abort ; close file & maybe delete pop dx read6: cmp flags.xflg,0 ; Did we write to the screen? je read6a ; e = no, so print status. cmp flags.destflg,2 ; Receiving to screen? je read6d ; Yes don't reset. mov flags.xflg,0 ; Reset it. jmp read6d ; Yes, so just return. read6a: test flags.remflg,dquiet ; quiet display mode? jnz read6d ; nz = yes, keep going cmp flags.destflg,2 ; Receiving to the screen? je read6d ; e = yes, no formatted display push dx ; save message pointer call stpos ; Position cursor. pop dx mov ah,prstr cmp flags.cxzflg,0 ; Completed or interrupted? je read6b ; Ended normally. or errlev,2 ; set DOS error level mov dx,offset infms6 ; Say was interrupted. read6b: int dos cmp flags.belflg,0 ; Bell desired? je read6c ; No. mov dx,offset ender ; Ring them bells. int dos read6c: test flags.remflg,dserial ; serial display? jnz read6d ; nz = yes call clrmod ; clear Mode Line call rprpos ; Put prompt here. read6d: jmp rskp READ ENDP ; Receive routines ; Receive init RINIT PROC NEAR mov ah,pack.numtry ; Get the number of tries. cmp ah,imxtry ; Reached the maximum number of tries? jl rinit2 mov dx,offset ermes7 test flags.remflg,dquiet ; quiet display mode? jnz rinit1 ; nz = yes. Don't write to screen. cmp flags.destflg,2 ; Receiving to the screen? je rinit1 ; e = yes, no formatted display call erpos ; Position cursor. mov ah,prstr int dos ; Print an error message. rinit1: mov bx,dx mov ah,trans.chklen mov curchk,ah ; Store checksum length we want to use. mov trans.chklen,1 ; Send init checksum is always 1 char. call errpack ; Send error packet just in case. mov ah,curchk mov trans.chklen,ah ; Reset to desired value. jmp abort ; Change the state to abort. rinit2: inc ah ; Increment it. mov pack.numtry,ah ; Save the updated number of tries. mov ax,pack.argbk2 ; get packet type if here from get cmp flags.getflg,1 ; Have we already read in the packet? je rin21a ; Yes, so don't call RPACK. mov ah,dtrans.seol ; restore default end-of-line char mov trans.seol,ah mov ah,trans.chklen mov curchk,ah ; Save checksum length we want to use. mov trans.chklen,1 ; Use 1 char for init packet. call rpack ; Get a packet. jmp rin22 ; Trashed packet: nak, retry. call pktsize ; report packet size push ax mov ah,curchk mov trans.chklen,ah ; Reset to desired value. pop ax cmp flags.cxzflg,0 ; does the user want out now? jne rinit4 ; ne = yes, quit rin21a: cmp ah,'S' ; Is it a send initiate packet? jne rinit3 ; If not see if its an error. rin21: mov flags.getflg,0 ; Reset flag. mov pack.numtry,0 ; Reset the number of tries. mov ax,pack.argblk ; Returned packet number. (Synchronize them.) inc ax ; Increment it. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov ax,pack.argbk1 ; Get the number of arguments received mov bx,offset data ; Get a pointer to the data call spar ; Get data into the proper variables mov bx,offset data ; Get a pointer to our data block call rpar ; Set up the receive parameters xchg ah,al mov ah,0 mov pack.argbk1,ax ; Store returned number of arguments mov ah,trans.chklen ; Checksum length we'll use. mov curchk,ah ; Save it. mov trans.chklen,1 ; Use 1 char for init packet. mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov ah,curchk ; Checksum length we'll use. mov trans.chklen,ah ; Reset to desired value. mov pack.state,'F' ; Set the state to file send. ret rin22: call nak0 ; nak the packet mov ah,curchk ; and only now change checksum from 1 mov trans.chklen,ah ; Reset to desired value. ret ; try again rinit3: cmp ah,'E' ; Is it an error packet? jne rinit4 ; ne = no call error ; yes rinit4: jmp abort RINIT ENDP ; Receive file RFILE PROC NEAR mov dl,maxtry cmp pack.numtry,dl ; Have we reached the maximum number of tries? jl rfile1 mov dx,offset ermes8 jmp rcverr ; do error exit rfile1: inc pack.numtry ; Save the updated number of tries. call rpack ; Get a packet. jmp nak0 ; Trashed packet: nak, retry. call pktsize ; report packet size cmp ah,'S' ; Is it a send initiate packet? je rfil10 cmp ah,'I' ; An Initialization packet? je rfil10 ; e = yes, don't decode it rfil09: call dodec ; Decode all other incoming packets. jmp rfile2 ; No, try next type. rfil10: mov dl,imxtry ; S and I packets cmp pack.numtry,dl ; Reached the maximum number of tries? jl rfil12 ; If not proceed. mov dx,offset ermes7 jmp rcverr ; do error exit rfil12: mov ax,pack.pktnum ; Get the present packet number. cmp ax,0 ; Had we wrapped around? jne rfilx mov ax,64 rfilx: dec ax ; Decrement. cmp ax,pack.argblk ; Is the packet's number one less than now? je rfil13 jmp nak0 ; No, NAK and try again. rfil13: mov pack.numtry,0 ; Reset the number of tries. mov bx,offset data ; Get a pointer to our data block. call rpar ; Set up the parameter information. xchg ah,al mov ah,0 mov pack.argbk1,ax ; Save the number of arguments. mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rfile2: cmp ah,'Z' ; Is it an EOF packet? jne rfile3 ; No, try next type. mov dl,maxtry ; Z packets cmp pack.numtry,dl ; Have we reached the maximum number of tries? jl rfil21 ; If not proceed. mov dx,offset ermes9 jmp rcverr ; do error exit rfil21: mov ax,pack.pktnum ; Get the present packet number. cmp ax,0 ; Had we wrapped around? jne rfily mov ax,64 rfily: dec ax ; Decrement. cmp ax,pack.argblk ; Is the packet's number one less than now? je rfil24 jmp nak0 ; No, NAK and try again. rfil24: mov pack.numtry,0 mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rfile3: cmp ah,'F' ; Start of file (F or X packet)? je rfil31 ; e = yes. cmp ah,'X' ; Text header packet? jne rfile4 ; Neither one. mov flags.xflg,1 ; 'X', say receiving to the screen rfil31: mov ax,pack.argblk ; Get the packet number. cmp ax,pack.pktnum ; Is it the right packet number? je rfil32 jmp nak1 ; No, NAK it and try again. rfil32: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov filopn,0 ; assume not writing to a disk file mov ax,0 ; tell statistics this was a receive operation call endtim ; get tod & size of file transfer call gofil ; Get a file to write to. jmp abort call begtim ; start next statistics group mov chrcnt,maxpack ; reset output buffer to be empty cmp flags.xflg,0 ; writing to a disk file? jne rfil32a ; ne = no mov filopn,2 ; Disk file open for writing rfil32a: test flags.remflg,dserial ; serial display mode? jz rfil33 ; z = no mov ah,prstr mov dx,offset crlf ; display cr/lf int dos rfil33: mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'D' ; Set the state to data receive. ret rfile4: cmp ah,'B' ; End of transmission? jne rfile5 ; ne = no mov ax,pack.pktnum cmp ax,pack.argblk ; Do we match? je rfil41 jmp nak1 ; No, NAK it and try again. rfil41: mov pack.argbk1,0 ; No data. (Packet number already in argblk). mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'C' ; Set the state to complete. ret rfile5: cmp ah,'E' ; Is it an error packet? jne rfile6 ; ne = no call error rfile6: jmp abort RFILE ENDP ; Receive data RDATA PROC NEAR mov dl,maxtry cmp pack.numtry,dl ; Get the number of tries. jl rdata1 mov dx,offset erms10 jmp rcverr ; do error exit rdata1: inc pack.numtry ; Save the updated number of tries. call rpack ; Get a packet. jmp nak0 ; Trashed packet: nak, retry. call pktsize ; report packet size cmp ah,'D' ; Is it a data packet? je rdat11 ; e = yes call dodec ; Decode data. jmp rdata2 ; No, try next type. ; D packets rdat11: mov ax,pack.pktnum ; Get the present packet number. cmp ax,pack.argblk ; Is the packet's number correct? jz rdat14 mov dl,maxtry cmp pack.numtry,dl ; Have we reached the maximum number of tries? jl rdat12 ; If not proceed. mov dx,offset erms10 jmp rcverr ; do error exit rdat12: mov ax,pack.pktnum cmp ax,0 ; Had we wrapped around? jne rdatx mov ax,64 rdatx: dec ax cmp ax,pack.argblk ; Is the packet's number one less than now? je rdat13 jmp nak0 ; No, NAK it and try again. rdat13: mov pack.numtry,0 ; Reset number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdat14: inc pack.pktnum ; Increment the packet number and pack.pktnum,3fh ; Save modulo 64 of the number inc pack.numpkt ; Increment the number of packets. mov ax,pack.argbk1 ; Get the length of the data. cmp flags.cxzflg,0 ; Has the user typed a ^X or ^Z? je rdt14x ; No, write out the data. cmp flags.abfflg,1 ; Discard incomplete files? je rdat15 ; If yes don't write data out to file. rdt14x: call ptchr ; decode the data and output to file jmp abort ; Unable to write out chars; abort. rdat15: mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (Packet number still in argblk.) cmp flags.cxzflg,0 ; Interrupt file transfer? je rdat16 ; Nope. mov bx,offset data ; Send data in ACK in case remote mov ah,flags.cxzflg ; knows about ^X/^Z. mov [bx],ah ; Put data into the packet. mov pack.argbk1,1 ; Set data size to 1. mov cx,1 call doenc rdat16: mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdata2: cmp ah,'F' ; Start of file? je rdat20 ; e = yes cmp ah,'X' ; Text header packet? jne rdata3 ; No, try next type. rdat20: mov dl,maxtry ; F or X packet cmp pack.numtry,dl ; Reached the max number of tries? jl rdat21 ; If not proceed. mov dx,offset ermes8 jmp rcverr ; do error exit rdat21: mov ax,pack.pktnum cmp ax,0 ; Had we wrapped around? jne rdaty mov ax,64 rdaty: dec ax cmp ax,pack.argblk ; Is the packet's number one less than now? je rdat22 jmp nak0 ; No, NAK it and try again. rdat22: mov pack.numtry,0 ; Reset number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdata3: cmp ah,'Z' ; Is it a EOF packet? je rdat3x ; e = yes jmp rdata4 ; Try and see if its an error. rdat3x: mov ax,pack.pktnum ; Get the present packet number. cmp ax,pack.argblk ; Is the packet's number correct? je rdat32 jmp nak0 ; No, NAK it and try again. rdat32: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt cmp flags.cxzflg,0 ; Do we want to discard the file? jne rdt32x ; Yes. cmp pack.argbk1,1 ; One piece of data? jne rdat33 ; Nope - finish writing out file? cmp data,'D' ; is the data "D" for discard? jne rdat33 ; Nope - write out file. rdt32x: cmp flags.abfflg,0 ; Keep incomplete files? je rdat33 ; Yes, go write it out. cmp flags.xflg,1 ; Writing to the screen? je rdt32y ; Don't close "file". cmp flags.destflg,2 ; file destination = screen? je rdt32y ; e = yes, no file to close push bx mov ah,close2 ; DOS 2.0 file close mov bx,diskio.handle ; file handle int dos ; Kill it, ignore errors. pop bx mov filopn,0 ; File closed now. mov dx,offset diskio.string ; get the filename mov ah,del2 ; DOS 2.0 file delete int dos rdt32y: cmp flags.cxzflg,'X' ; Kill one file or all? jne rdat36 ; No so leave flag alone. call cxmsg ; Clear msg about interrupt. test flags.remflg,dquiet ; quiet display? jnz rdt32z ; nz = yes cmp flags.destflg,2 ; Receiving to the screen? je rdt32z ; e = yes, no formatted display call intmsg rdt32z: mov flags.cxzflg,0 ; Reset - ^X only kills one file jmp rdat36 rdat33: cmp flags.eofcz,0 ; should we write a ^Z? jz rdat35 ; no, keep going cmp flags.xflg,0 ; writing to a file? jne rdat35 ; no, skip ^Z rdt33x: cmp chrcnt,0 ; any space left in output buffer? jg rdat34 ; g = yes call outbuf ; Write out buffer if no room for ^Z. jmp abort rdat34: mov cl,'Z'- 40h ; Put in a ^Z for EOF. push bx mov bx,bufpnt ; Get the dma pointer mov [bx],cl ; Add it. pop bx dec chrcnt rdat35: call outbuf ; Output the last buffer. jmp abort ; Give up if the disk is full. cmp flags.xflg,1 ; Writing to the screen? je rdat37 ; Yes, don't close "file". cmp flags.destflg,2 ; file destination = screen? je rdat37 ; e = yes, no file to close mov ah,close2 ; DOS 2.0 file close push bx mov bx,diskio.handle ; file handle int dos pop bx mov filopn,0 ; File closed now. rdat36: cmp flags.destflg,0 ; Writing to printer? jne rdat37 ; ne = no, skip next part. cmp flags.xflg,1 ; Writing to screen? je rdat37 ; Yes, skip this part. mov dl,ff ; Send a form feed. mov ah,lstout ; Write out to first printer. int dos rdat37: mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'F' ret rdata4: cmp ah,'E' ; Is it an error packet? jne rdata5 ; ne = no call error rdata5: jmp abort RDATA ENDP ; Error exit. Enter with dx pointing to error message. [jrd] rcverr proc near test flags.remflg,dquiet; quiet display mode? jnz rcver1 ; nz = yes. Don't write to screen cmp flags.destflg,2 ; Receiving to the screen? je rcver1 ; e = yes, no formatted display call erpos ; Position cursor. mov ah,prstr int dos ; Print an error message. rcver1: mov bx,dx ; set bx to error message call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rcverr endp ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr. RSKP PROC NEAR pop bp add bp,3 push bp ret RSKP ENDP R PROC NEAR ret R ENDP code ends end