NAME msssen ; File MSSSEN.ASM ; Edit history: ; Last edit: 1 Jan 1988 ; 1 Jan 1988 version 2.30 ; 26 Dec 1987 clean ups [jrd] ; 8 Oct 1987 Ensure error pkts use 1 byte chksum at init stage. [jrd] ; 21 Sept 1987 Add error return to doenc for filenames too long for pkt. [jrd] ; 6 July 1987 Fix bug of 9 Dec below for case of short S/I packets. [jrd] ; 27 June 1987 Correct statistics gathering for last file. [jrd] ; 7 June 1987 Return DOS errlev of 1 if sending fails. [jrd] ; 24 May 1987 Add Delay seconds before starting local Send file. [jrd] ; 7 May 1987 Add statistics gathering functions. [jrd] ; 9 Dec 1986 Version 2.29a ; 9 Dec 1986 Add final check on packet length in Spar to avoid sending ; packets longer than the local user wishes. [jrd] ; 5 Sept 1986 minor cleanup in spar regular pkt size calculation. [jrd] ; 14 August 1986 Fix Send/Receive EOL, padding, pad-char [rjd] ; 3 August 86 modify for long packets, fix 8 bit quoting negotiations. [jrd] ; 26 May 1986 Revise code to permit serial display. [jrd] ; Also, remove case conversion of "as" filename being sent. [rjd] ; [2.29] code frozen on 6 May 1986 [jrd] public spar, rpar, error, error1, nout, send, flags, trans, pack public dodec, doenc, curchk, inichk, packlen, send11, dtrans include mssdef.h spmin equ 20 ; Minimum packet size. spmax equ 94 ; Maximum packet size. datas segment public 'datas' extrn buff:byte, data:byte, filbuf:byte, fsta:word extrn decbuf:byte, chrcnt:word, bufpnt:word, errlev:byte extrn rptq:byte, origr:byte, rptct:byte, rptval:byte extrn diskio:byte, maxtry:byte, imxtry:byte, portval:word flags flginfo <> trans trinfo <> dtrans trinfo <> ; default trans info pack pktinfo <> crlf db cr,lf,'$' ender db bell,bell,'$' erms14 db '?Unable to receive an acknowledgment from the host$' erms15 db '?Unable to find file$' erms20 db 'Unable to send init packet$' erms21 db 'Unable to send file header$' erms22 db 'Unable to send data$' erms23 db 'Unable to send end-of-file packet$' erms24 db 'Unable to send break packet$' infms2 db cr,' Sending: In progress',cr,lf,'$' infms3 db 'Completed',cr,lf,'$' infms4 db 'Failed',cr,lf,'$' infms6 db 'Interrupted$' remmsg1 db 'Kermit-MS: File not found$' filhlp db ' A filename (possibly wild) $' filmsg db ' Local Source File or a carriage return $' remfnm db ' Remote Destination File: $' lclfnm db ' Local Source File: $' curchk db 0 ; Use to store checksum length. inichk db 1 ; Original or set checksum length. siz dw ? ; Size of data from gtchr. difsiz dw 0 ; Size of new exported file name. sendas dw 50 dup(0) ; Buffer for file name. temp dw 0 temp4 dw 0 asmsg db ' as $' filopn db 0 ; Says if disk file is open. ninefive dw 95 ; constant word for long packets datas ends code segment public 'code' extrn serini:near, serrst:near, comnd:near, init:near extrn spack:near, rpack:near, gtnfil:near, gtchr:near extrn getfil:near, clrfln:near, nppos:near, rprpos:near extrn erpos:near, rtpos:near, cxmsg:near, stpos:near, decout:near extrn encode:near, nulref:near, decode:near, nulr:near extrn errpack:near, updrtr:near, clrmod:near, prompt:near extrn prtfn:near, strcpy:near, strlen:near, pktsize:near extrn pcwait:near, ihosts:near, begtim:near, endtim:near assume cs:code, ds:datas ; This routine sets up the data for init packet (either the ; Send_init or ACK packet). ; trans.rxxx are items we are prepared to receive RPAR PROC NEAR mov ah,trans.rpsiz ; Get the receive packet size. add ah,' ' ; Add a space to make it printable. mov [bx],ah ; Put it in the packet. mov ah,trans.rtime ; Get the receive packet time out. add ah,' ' ; Add a space. mov 1[bx],ah ; Put it in the packet. mov ah,trans.rpad ; Get the number of padding chars. add ah,' ' mov 2[bx],ah ; Put it in the packet. mov ah,trans.rpadch ; Get the padding char. add ah,40h ; Uncontrol it. and ah,7FH mov 3[bx],ah ; Put it in the packet. mov ah,trans.reol ; Get the EOL char. add ah,' ' mov 4[bx],ah ; Put it in the packet. mov ah,trans.rquote ; Get the quote char. mov 5[bx],ah ; Put it in the packet. mov ah,trans.ebquot ; Get 8-bit quote char. mov 6[bx],ah ; Add it to the packet. mov ah,trans.chklen ; Length of checksum. add ah,48 ; Make into a real digit. mov 7[bx],ah mov ah,rptq ; Repeat quote char. cmp ah,0 ; Null means no. jne rpar0 mov ah,' ' ; Send a blank instead. rpar0: mov 8[bx],ah ; begin long packet changes mov ah,02h ; CAPAS, bit1 = can do long packets add ah,20h ; apply tochar() to byte mov 9[bx],ah ; add to packet ; additional CAPAS go in here mov ah,20h ; WINDO field, null applied through tochar() mov 10[bx],ah ; put into packet push ax ; save some regs push dx mov ax,trans.rlongp ; long packet length which we can receive xor dx,dx ; clear extended part for division div ninefive ; divide by 95. quo = ax, rem = dx add al,20h ; apply tochar() to quotient mov 11[bx],al ; add to packet add dl,20h ; apply tochar() to remainder mov 12[bx],dl ; add to packet pop dx ; restore regs pop ax mov ah,13 ; 13 bytes of data ret RPAR ENDP ; This routine reads in all the send init packet information. ; Enter with BX/ packet address, AX/ packet length ; This could probably be done much more legibly if it were table ; driven, but I'm afraid to touch it... ; ; dtrans.xxx are the default parameters if the other side says nothing. ; trans.sxxx are the active negotiated parameters we will use for sending. SPAR PROC NEAR mov temp4,ax ; Save the number of arguments cmp ax,1 jge spara ; ge = want more than bare minimum mov ah,dspsiz ; Data not supplied by host, use default jmp short sparc spara: mov ah,[bx] ; Get the max packet size. sub ah,' ' ; Subtract a space. cmp ah,spmin ; Can't be below the minimum. jge sparb mov ah,spmin jmp short sparc sparb: cmp ah,spmax ; Or above the maximum. jle sparc mov ah,spmax sparc: mov trans.spsiz,ah ; Save it. mov ax,temp4 mov ah,dtrans.stime ; pick up default stime cmp al,2 ; Fewer than two pieces? jl spar02 ; yes, use default spar0: cmp ah,dstime ; Is current value the default? jne spar02 ; No, assume changed by user. mov ah,1[bx] ; Get the timeout value. sub ah,' ' ; Subtract a space. cmp ah,0 ja spar01 ; Must be non-negative. mov ah,0 spar01: cmp ah,trans.rtime ; Same as other side's timeout. jne spar02 add ah,5 ; If so, make it a little different. spar02: mov trans.stime,ah ; Save it. mov ax,temp4 mov ah,dtrans.spad ; get default send padding cmp al,3 ; Fewer than three pieces? jl spar11 ; yes, use default spar1: mov ah,2[bx] ; Get the number of padding chars. sub ah,' ' cmp ah,0 jge spar11 ; Must be non-negative. mov ah,0 spar11: mov trans.spad,ah mov ax,temp4 mov ah,dtrans.spadch ; pick up default send pad character cmp al,4 ; Fewer than four pieces? jl spar21 spar2: mov ah,3[bx] ; Get the padding char. add ah,40h ; Re-controlify it. and ah,7FH cmp ah,del ; Delete? je spar21 ; Yes, then it's OK. cmp ah,0 jge spar20 mov ah,0 ; Below zero is no good. jmp spar21 ; Use zero (null). spar20: cmp ah,31 ; Is it a control char? jle spar21 ; Yes, then OK. mov ah,0 ; No, use null. spar21: mov trans.spadch,ah mov ax,temp4 mov ah,dtrans.seol ; get default send eol char cmp al,5 ; Fewer than five pieces? jl spar31 ; yes, use default spar3: mov ah,4[bx] ; Get the EOL char. sub ah,' ' cmp ah,0 jge spar30 ; Cannot be negative. mov ah,cr ; If so, use default of carriage return jmp spar31 spar30: cmp ah,31 ; Is it a control char? jle spar31 ; Yes, then use it. mov ah,cr ; Else, use the default. spar31: mov trans.seol,ah mov ax,temp4 mov ah,dtrans.squote ; send quote cmp al,6 ; Fewer than six pieces? jl spar41 spar4: mov ah,5[bx] ; Get the quote char. cmp ah,' ' ; Less than a space? jge spar40 mov ah,dsquot ; Yes, use default. jmp spar41 spar40: cmp ah,7eh ; Must also be less than a tilde. jle spar41 mov ah,dsquot ; Else, use default. spar41: mov trans.squote,ah cmp al,7 ; Fewer than seven pieces? jge spar5 mov trans.ebquot,'Y' ; Data not supplied by host, use default jmp short spar51 spar5: mov ah,6[bx] ; Get other sides 8-bit quote request call doquo ; And set quote char. spar51: cmp al,8 ; Fewer than eight pieces? jge spar6 mov trans.chklen,1 jmp short spar61 spar6: mov ah,inichk mov trans.chklen,ah ;Checksum length we really want to use mov ah,7[bx] ; Get other sides checksum length. call dochk ; Determine what size to use. spar61: cmp al,9 ; Fewer than nine pieces? jge spar7 mov rptq,0 jmp short spar71 spar7: mov ah,8[bx] ; Get other sides repeat count prefix. mov ch,drpt mov rptq,0 call dorpt ;;; begin long packet changes - update this for strict ELP protocol spar71: mov ah,0 ; get default operating capabilities cmp al,10 ; 10 or more pieces? jl spar81 ; l = no mov ah,9[bx] ; get capas bitmap from other side sub ah,20h ; apply unchar() call decapa ; negotiate them back into ah spar81: mov trans.capas,ah ; store result in active byte mov ah,0 ; setup default window size cmp al,11 ; 11 or more pieces? jl spar9 ; l = no, use default mov ah,10[bx] ; get other side's window size sub ah,20h ; apply unchar() call dewind ; negotiate window size back into ah spar9: mov trans.windo,ah ; store it ; decode window info push cx ; save a reg xor ch,ch mov cl,trans.spsiz ; normal packet size mov trans.slongp,cx ; assume not using long packets pop cx ; restore reg cmp al,13 ; 13 or more pieces (long packet needs two)? jae spar9d ; ae = more to look at mov ax,trans.slongp ; put above size in ax for final checks push ax jmp spar9a ; do final checks (they want longer than us) spar9d: test trans.capas,2 ; do they have long packet capability? jz sparx ; z = no, skip following lp length fields. push ax ; save al xor ah,ah mov al,11[bx] ; long packet length, high order byte sub al,20h ; apply unchar() mul ninefive ; times 95 to dx(hi),ax(lo) mov trans.slongp,ax ; store that much xor ah,ah mov al,12[bx] ; long packet length, low order byte sub al,20h ; apply unchar() add ax,trans.slongp ; plus high order part mov trans.slongp,ax ; store it or ax,ax ; if result is zero then use regular packets jnz spar9a ; non-zero, use what they want mov ah,0 mov al,trans.spsiz ; default to regular packet size mov trans.slongp,ax ; and ignore the CAPAS bit (no def 500 bytes) spar9a: cmp ax,trans.slong ; longer than we want to do? jbe spar9b ; be = no mov ax,trans.slong ; limit to our longest sending size mov trans.slongp,ax ; and use it spar9b: cmp ax,94 ; shorter than normal packet too? ja spar9c ; a = no mov trans.spsiz,al ; update normal packet size, again. spar9c: pop ax ; recover al sparx: ret SPAR ENDP ; Set 8-bit quote character based on my capabilities and the other ; Kermit's request. DOQUO PROC NEAR cmp trans.ebquot,'N' ; Can I do 8-bit quoting at all? je dq3 ; No - so forget it. cmp trans.ebquot,'Y' ; Can I do it if requested? jne dq0 ; No - it's a must that I do it. mov trans.ebquot,ah ; Do whatever he wants. jmp dq1 dq0: cmp ah,'Y' ; I need quoting - can he do it? je dq1 ; Yes - then all is settled. cmp ah,'N' ; No - then don't quote. je dq3 cmp ah,trans.ebquot ; Both need quoting - chars must match jne dq3 dq1: mov ah,trans.ebquot cmp ah,'Y' ; If Y or N, don't validate prefix. je dq2 cmp ah,'N' je dq2 call prechk ; Is it in range 33-62, 96-126? mov ah,'Y' ; Failed, don't do quoting. nop cmp ah,trans.rquote ; Same prefix? je dq3 ; Not allowed, so don't do quoting. cmp ah,trans.squote ; Same prefix here? je dq3 ; This is illegal too. mov trans.ebquot,ah ; Remember what we decided on. dq2: ret dq3: mov trans.ebquot,'N' ; Quoting will not be done. ret DOQUO ENDP ; Check if prefix in AH is in the proper range: 33-62, 96-126. ; RSKP if so else RETURN. prechk: cmp ah,33 jge prec0 ; It's above 33. ret prec0: cmp ah,62 jg prec1 jmp rskp ; And below 62. OK. prec1: cmp ah,96 jge prec2 ; It's above 96. ret prec2: cmp ah,126 jg prec3 jmp rskp ; And below 126. OK. prec3: ret ; Set checksum length. dochk: cmp ah,'1' ; Must be 1, 2, or 3. jl doc1 cmp ah,'3' jle doc2 doc1: mov ah,'1' doc2: sub ah,48 ; Don't want it printable. mov trans.chklen,ah ; other side's request is do-able here cmp ah,trans.chklen ; Do we want the same thing? je dochk0 ; Yes, then we're done. mov trans.chklen,1 ; No, use single character checksum. dochk0: ret ; Just return for now. ; Set repeat count quote character. The one used must be different than ; the control and eight-bit quote characters. Also, both sides must ; use the same character. dorpt: call prechk ; Is it in the valid range? mov ah,0 ; No, don't use their value. nop cmp ah,trans.squote ; Same as the control quote char? je dorpt0 ; Yes, that's illegal, no repeats. cmp ah,trans.rquote ; How about this one? je dorpt0 ; No good. cmp ah,trans.ebquot ; Same as eight bit quote char? je dorpt0 ; Yes, that's illegal too, no repeats. cmp ah,ch ; Are we planning to use same char? jne dorpt0 ; No, that's no good either. mov rptq,ch ; Use repeat quote char now. dorpt0: ret ; negotiate capas byte in ah decapa: ret ; nothing for now ; negotiate window size in ah dewind: xor ah,ah ; no windowing at our end ret ; Send command SEND PROC NEAR mov difsiz,0 ; Assume we'll use original filename. mov byte ptr sendas,0 ; clear sendas name (in case none) mov ah,cmfile ; get an input file spec mov dx,offset diskio.string ; address of filename string mov bx,offset filmsg ; Text of help message. call comnd jmp r ; Give up on bad parse. cmp flags.cxzflg,0 ; ^X, ^Z, ^C typed? je send0 ; e = no, continue or errlev,1 ; say send failed jmp rskp ; yes, quit send0: cmp ah,0 ; any text given? ja send0c ; a = yes mov dx,offset lclfnm ; prompt for local filename call prompt mov dx,offset diskio.string ; reload destination of user's text mov bx,offset filhlp ; help file mov ah,cmfile ; allow paths call comnd ; try again for a local filename jmp r cmp flags.cxzflg,0 ; ^X, ^Z, ^C typed? je send0a ; e = no, continue or errlev,1 ; say send failed jmp rskp ; yes, quit send0a: cmp ah,0 ; user's byte count jne send0b ; something was typed jmp r ; else return (gives "not confirmed" msg) send0b: mov dx,offset remfnm ; ask for remote name first call prompt send0c: mov bx,offset sendas ; See if want to send file under dif name mov dx,offset filhlp ; In case user needs help. mov ah,cmtxt ; allow embedded white space call comnd jmp r cmp flags.cxzflg,0 ; ^X, ^Z, ^C typed? je send1 ; e = no, continue or errlev,1 ; say send failed jmp rskp ; yes, quit send1: mov al,ah ; store count of user's chars. mov ah,0 mov difsiz,ax ; Remember length of new name. mov bx,offset sendas ; look at string again. push es push di push si mov ax,ds ; use segment 'datas' for es: mov es,ax mov si,bx ; look at start of string, remove whitespace send1c: cmp byte ptr [si],0 ; at terminator? je send1d ; e = yes cmp byte ptr [si],' ' ; text (greater than space)? ja send1d ; a = yes. inc si ; look at next char jmp send1c ; look some more send1d: cmp bx,si ; did we find leading whitespace? je send1e ; e = no mov di,bx ; place to copy chars call strcpy ; from ds:si to ds:di send1e: mov dx,bx ; address of string call strlen ; get its new length (returned in cx) mov difsiz,cx ; store it pop si pop di pop es mov ah,trans.sdelay ; seconds to delay before sending shl ah,1 ; times 4*256 to get millisec shl ah,1 ; for pcwait mov al,1 ; set low byte to 1 for no delay case call pcwait ; wait number of millisec in ax mov flags.xflg,0 ; Reset flag for normal file send[mtd] mov flags.cxzflg,0 ; clear interrupt flag too. mov bx,offset diskio.string cmp byte ptr [bx],'#' ; Is first char a replacement for '?'? jne send1f ; ne = no mov byte ptr [bx],'?' ; yes. Change '#' for '?' send1f: mov bx,offset sendas cmp byte ptr [bx],'#' ; Is first char a replacement for '?'? jne snd11a mov byte ptr [bx],'?' ; yes. Change '#' for '?' jmp snd11a ; SEND11 is an entry point for REMote cmds. SEND11: mov flags.nmoflg,0 ; Reset flags from fn parsing. mov difsiz,0 ; clear any old 'sendas' filespec snd11a: mov ah,setdma ; set dta address mov dx,offset diskio.dta int dos mov ah,first2 ; search for first mov cx,0 ; consider only regular files mov dx,offset diskio.string ; full filename, inc paths int dos pushf ; save flags push dx mov ah,setdma ; restore dta to offset buff mov dx,offset buff int dos pop dx popf ; restore flags jnc send16 ; carry reset = file found cmp pack.state,'R' ; was this from a remote GET? jne sen11a ; no, print error and continue mov bx,offset remmsg1 ; else get error message 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 ; go complain mov ah,curchk mov trans.chklen,ah ; Checksum length we want to use. jmp abort ; and abort this sen11a: mov ah,prstr mov dx,offset crlf int dos mov ah,prstr mov dx,offset erms15 ; '?Unable to find file' int dos or errlev,1 ; set DOS error level mov ax,1 ; tell statistics this was a send operation call endtim ; stop statistics counter jmp rskp ; pretend successful completion send16: call serini ; Initialize serial port mov ax,1 ; say this is a send operation call endtim ; get tod for end of transfer call begtim ; get tod for start of transfer mov pack.pktnum,0 ; Set the packet number to zero. mov pack.numtry,0 ; Set the number of tries to zero. mov pack.numpkt,0 ; Set the number of packets to zero. mov pack.numrtr,0 ; Set the number of retries to zero. mov pack.state,'S' ; Set the state to receive initiate. call ihosts ; initialize the host (clear NAKs) call init ; Clear the line and initialize the buffers. test flags.remflg,dquiet+dserial ; quiet or serial display mode? jnz send2 ; nz = yes, suppress 0 retry msg call stpos ; Print status of file transfer. mov ah,prstr ; Be informative. mov dx,offset infms2 int dos send18: test flags.remflg,dquiet+dserial ; quiet or serial display mode? jnz send2 ; nz = yes, suppress 0 retry msg call rtpos ; Position cursor. mov ax,0 ; set retry counts to zero call nout ; Write the number of retries. send2: call nppos ; Number of packets sent. mov ax,pack.numpkt call nout ; Write the packet number. cmp pack.state,'D' ; Are we in the data send state? jne send3 ; ne = no call sdata ; send data jmp send2 send3: cmp pack.state,'F' ; Are we in the file send state? jne send4 call sfile ; Call send file. jmp send2 send4: cmp pack.state,'Z' ; Are we in the EOF state? jne send5 call seof jmp send2 send5: cmp pack.state,'S' ; Are we in the send initiate state? jne send6 call sinit jmp send2 send6: cmp pack.state,'B' ; Are we in the eot state? jne send7 call seot jmp send2 ; Completion processor section send7: push ax mov ax,1 ; tell statistics this was a send operation call endtim ; stop statistics counter call serrst ; Reset serial port. pop ax mov dx,offset infms3 ; Completed message cmp pack.state,'C' ; Are we in the send complete state? je send8 ; e = yes, else failure mov dx,offset infms4 ; Failed message or errlev,1 ; say send failed send8: cmp flags.cxzflg,0 ; completed normally? jne send8b ; ne = no, don't bother with this or errlev,1 ; say send failed send8b: test flags.remflg,dquiet ; quiet display mode? jnz send8f ; nz = yes, no printing. test flags.remflg,dserial ; serial display mode? jnz send8c ; nz = yes, skip positioning push dx call stpos pop dx send8c: mov ah,prstr cmp flags.cxzflg,0 ; Completed or interrupted? je send8d ; e = no interruption mov dx,offset infms6 ; Say transfer was interrupted. send8d: int dos cmp flags.belflg,0 ; Bell desired? je send8e ; e = no mov dx,offset ender ; Ring them bells. int dos send8e: test flags.remflg,dserial ; serial display mode? jnz send8f ; nz = yes, no cursor positioning call clrmod call rprpos send8f: jmp rskp SEND ENDP ; Send routines ; Send initiate SINIT PROC NEAR mov dl,imxtry cmp pack.numtry,dl ; Have we reached the maximum number of tries? jl sinit2 ; l = no test flags.remflg,dquiet ; quiet display mode? jnz sinit1 ; nz = yes. Don't write to screen. call erpos mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. sinit1: 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. mov bx,offset erms20 call errpack ; Send error packet just in case. mov ah,curchk mov trans.chklen,ah ; Checksum length we want to use. jmp abort ; Change the state to abort. sinit2: inc pack.numtry ; Save the updated 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,dtrans.seol ; restore default end-of-line char mov trans.seol,ah mov ah,0 mov pack.argbk1,ax ; Save the number of arguments. mov ax,pack.numpkt ; Get the packet number. mov pack.argblk,ax mov trans.ebquot,'Y' ; say we can do 8 bit quoting push bx mov bx,portval cmp [bx].parflg,parnon ; using parity=none locally? pop bx je sinit2a ; e = no parity mov trans.ebquot,dqbin ; def 8 bit quot, needed with parity sinit2a: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 pktsize ; report packet size mov ah,'S' ; Send initiate packet. call spack ; Send the packet. jmp abort call rpack ; Get a packet. jmp sini23 ; Trashed packet don't change state, retry. push ax mov ah,curchk mov trans.chklen,ah ; Checksum length we want to use. pop ax call acknak ; was it ok? cmp al,0 ; maybe an ack? je sini22 ; yes, go handle it cmp al,1 ; maybe a nak? jne sinit4 ; no, check for error or something ret ; else just return and try again sini22: mov ax,pack.argbk1 mov bx,offset data ; point to data for spar call spar ; Read in the data. call packlen ; Get max send packet size. mov pack.numtry,0 ; Reset the number of tries. mov pack.state,'F' ; Set the state to file send. call getfil ; Open the file. jmp abort ; Something is wrong, die. mov filopn,1 ; Disk file is open. ret sini23: mov ah,curchk ; Restore desired checksum length. mov trans.chklen,ah call updrtr ; Update retry counter. ret ; And retry. sinit4: cmp ah,'E' ; Is it an error packet. jne sinit5 call error sinit5: jmp abort SINIT ENDP ; Send file header SFILE PROC NEAR mov dl,maxtry cmp pack.numtry,dl ; Have we reached the maximum number of tries? jl sfile1 ; l = no test flags.remflg,dquiet ; quiet display mode? jnz sfile0 ; nz = yes. Don't write to screen call erpos mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. sfile0: mov bx,offset erms21 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. sfile1: inc pack.numtry ; Increment it. mov flags.cxzflg,0 ; Clear ^X,^Z flag. mov si,offset diskio.fname ;addr of asciiz filename without paths mov di,offset data ; destination call strcpy ; copy filename there push dx mov dx,offset data call strlen ; get length (w/o terminator) into cx pop dx mov ch,0 test flags.remflg,dquiet ; quiet display mode? jnz sfil13 ; nz = yes, no printing. call prtfn ; print filename in data sfil13: call newfn ; show possible new filename, put length in cx call doenc ; Do encoding; length is in cx. mov ax,pack.pktnum ; Get the packet number. mov pack.argblk,ax mov ah,'F' ; File header packet. cmp flags.xflg,0 ; remote display requested? je sfl13y ; e = no mov ah,'X' ; use X rather than F packet for remote sfl13y: call pktsize ; report packet size call spack ; Send the packet. jmp abort call rpack ; Get a packet. jmp tryagn ; Trashed packet don't change state, retry. call dodec ; Do all decoding. call acknak ; see what they had to say cmp al,0 ; ack'd ok? je sfil14 ; yes, on to next state cmp al,1 ; maybe a nak? jne sfile3 ; no, check for error ret ; if nak, just return and try again sfil14: mov flags.filflg,0FFH ; Indicate file buffer empty. call gtchr jmp sfil16 ; Error go see if its EOF. nop jmp sfil17 ; Got the chars, proceed. sfil16: cmp ah,0FFH ; Is it EOF? je sfl161 jmp abort ; If not give up. sfl161: mov ah,'Z' ; Set the state to EOF. mov pack.state,ah ret sfil17: mov siz,ax mov pack.state,'D' ; Set the state to data send. ret sfile3: cmp ah,'E' ; Is it an error packet. jne sfile4 call error sfile4: jmp abort SFILE ENDP ; Send data SDATA PROC NEAR cmp flags.cxzflg,0 ; Have we seen ^X or ^Z? je sdata2 ; Nope, just continue. cmp flags.cxzflg,'C' ; Stop it all? jne sdata1 ; It was a ^X or ^Z. mov pack.state,'A' ; It was a ^C -- abort ret sdata1: mov pack.state,'Z' ; Else, abort sending the file. ret sdata2: mov dl,maxtry cmp pack.numtry,dl ; Have we reached the maximum number of tries? jl sdata3 ; l = no test flags.remflg,dquiet ; quiet display mode? jnz sdat2a ; nz = yes. Don't write to screen call erpos mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. sdat2a: mov bx,offset erms22 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. sdata3: inc pack.numtry ; Increment it. mov cx,siz ; number to transfer mov si,offset filbuf ; source of characters mov di,offset data ; destination push es ; save es push ds pop es ; make es:di point to datas segment cld rep movsb ; just copy data pop es ; restore reg mov ax,siz ; this is how many were moved sdata7: mov pack.argbk1,ax mov ax,pack.pktnum ; Get the packet number. mov pack.argblk,ax call pktsize ; report packet size mov ah,'D' ; Data packet. call spack ; Send the packet. jmp tryagn ; if can't send it, retry before giving up call rpack ; Get a packet. jmp tryagn ; Trashed packet don't change state, retry. call dodec ; Do all decoding. call acknak ; see if ack or nak, check packet number cmp al,0 ; 0 => ack ok, go on je sdat11 cmp al,1 ; 1 => nak, retry count incremented, try again jne sdat15 ; else look for other packet types... ret ; else return sdat11: cmp pack.argbk1,1 ; any data? jne sdat23 mov bl,data ; get 1st byte cmp bl,'X' ; someone typed control X? je sdat24 ; e = yes. cmp bl,'Z' ; Control Z? Corrects earlier proto error jne sdat23 ; not X or Z, just keep going sdat24: mov flags.cxzflg,bl ; set flag appropriately mov pack.state,'Z' ; simulate eof ret ; and return sdat23: call gtchr jmp sdat12 ; Error go see if its EOF. nop ; make three bytes mov siz,ax ; Save the size of the data gotten. ret sdat12: cmp ah,0FFH ; Is it EOF? je sdat13 jmp abort ; If not give up. sdat13: mov pack.state,'Z' ; Set the state to EOF. ret sdat15: cmp ah,'E' ; Is it an error packet. jne sdat16 call error sdat16: jmp abort SDATA ENDP ; check the current packet for an ack or nak and handle it from any of ; the send states. Returns: 0 if an ack received with the correct expected ; packet number, or if a nak received with the NEXT packet number (the ; packet number is incremented, retry count reset); 1 if a nak or ack ; with a bad packet number is received, retry count is updated and displayed. ; Finally, 2 is returned if anything else is seen. ; ACKNAK PROC NEAR cmp ah,'Y' ; ack packet? jne ackna1 ; no, keep going mov bx,pack.pktnum cmp bx,pack.argblk ; is it what we were expecting? jne ackna2 ; no, update retries and punt ; packet ok, increment packet number ackna0: mov bx,pack.pktnum ; reload packet number (!!!) inc bx and bx,03fh ; increment packet number mov pack.pktnum,bx ; store back inc pack.numpkt ; increment # of packets mov pack.numtry,0 mov al,0 ; ack'd ok ret ; not a 'Y'... ackna1: cmp ah,'N' ; a nak? je ackna5 ; yes, go on mov al,2 ret ; unknown packet type ackna5: mov bx,pack.pktnum inc bx and bx,3fh cmp bx,pack.argblk ; maybe a nak for pktnum+1? je ackna0 ; yes, treat as ack ; nak or bad ack, update retry stuff ackna2: call rtpos ; Position cursor. inc pack.numrtr ; Increment the number of retries mov ax,pack.numrtr call nout ; Write the number of retries. mov al,1 ; nak code inc fsta.nakrcnt ; count received NAK for statistics ret ; and return ACKNAK ENDP ; Send EOF SEOF PROC NEAR mov dl,maxtry cmp pack.numtry,dl ; Have we reached the maximum number of tries? jl seof1 ; l = no test flags.remflg,dquiet; quiet display mode? jnz seof0 ; nz = yes. Don't write to screen call erpos ; Position cursor. mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. seof0: mov bx,offset erms23 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. seof1: inc pack.numtry ; Increment it. mov ax,pack.pktnum ; Get the packet number. mov pack.argblk,ax mov pack.argbk1,0 ; No data. cmp flags.cxzflg,0 ; Seen a ^X or ^Z? je seof11 ; Nope, send normal EOF packet. mov data,'D' ; Use "D" for discard. mov pack.argbk1,1 ; Set data size to 1. seof11: mov cx,pack.argbk1 ; Put size in CX. call doenc ; Encode the packet. call pktsize ; report packet size mov ah,'Z' ; EOF packet. call spack ; Send the packet. jmp abort call rpack ; Get a packet. jmp tryagn ; Trashed packet don't change state, retry. call dodec ; Do decoding. call acknak ; see what they had to say cmp al,0 ; ack? je seof12 ; yes, go close file and proceed cmp al,1 ; maybe a nak? jne seof3 ; no, check for error packet ret ; if nak, just return... seof12: mov ah,close2 ; DOS 2.0 close file push bx mov bx,diskio.handle ; file handle int dos pop bx mov ax,1 ; tell statistics this was a send operation call endtim ; get tod of end of file transfer call GTNFIL ; Get the next file. jmp seof13 ; No more. nop ; make three bytes call begtim ; start statistics counter mov pack.state,'F' ; Set the state to file send. cmp flags.cxzflg,'X' ; Control-X seen? jne seof14 call cxmsg ; Clear out the interrupt msg. seof14: mov flags.cxzflg,0 ; Reset the flag. ret seof13: mov pack.state,'B' ; Set the state to EOT. mov filopn,0 ; No files open. mov difsiz,0 ; clear original filename mov byte ptr sendas,0 ; clear sendas name ret seof3: cmp ah,'E' ; Is it an error packet? jne seof4 call error seof4: jmp abort SEOF ENDP ; Send EOT SEOT PROC NEAR mov dl,maxtry cmp pack.numtry,dl ; Have we reached the maximum number of tries? jl seot1 ; l = no test flags.remflg,dquiet ; quiet display mode? jnz seot0 ; nz = yes. Don't write to screen call erpos ; Position cursor. mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. seot0: mov bx,offset erms24 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. seot1: inc pack.numtry ; Increment it. mov ax,pack.pktnum ; Get the packet number. mov pack.argblk,ax mov pack.argbk1,0 ; No data. call pktsize ; report packet size mov ah,'B' ; EOF packet. call spack ; Send the packet. jmp abort call rpack ; Get a packet. jmp tryagn ; Trashed packet don't change state, retry. call dodec ; Decode packet. call acknak ; see if good ack or nak cmp al,0 ; ack'd ok? je seot12 ; yes, done with this cmp al,1 ; maybe a nak? jne seot3 ; no, check for error ret ; else just return seot12: mov pack.state,'C' ; Set the state to file send. ret seot3: cmp ah,'E' ; Is it an error packet. jne seot4 call error seot4: jmp abort SEOT ENDP tryagn: call updrtr ret ; newfn -- move replacement name from buffer sendas to buffer data newfn: cmp difsiz,0 ; Sending file under different name? je newf4 ; e = no, so don't give new name test flags.remflg,dquiet ; quiet display mode? jnz newfa ; nz = yes. Don't write to screen mov ah,prstr mov dx,offset asmsg ; display ' as ' int dos mov ah,conout ; use printable output newfa: mov si,offset sendas ; Buffer where the name is mov di,offset data mov cx,difsiz ; Length of name. inc cx ; plus null terminator newf0: lodsb ; Get a character into al. stosb test flags.remflg,dquiet ; quiet display mode (should we print)? jnz newf2 ; nz = yes mov dl,al ; set into dl for display int dos ; Print them. newf2: loop newf0 mov cx,difsiz ; Reset the length field. newf4: test flags.remflg,dserial ; serial display mode? jz newf5 ; z = no push ax push dx mov dx,offset crlf ; start with cr/lf for serial display mov ah,prstr int dos pop dx pop ax newf5: ret ; Do encoding. Expect CX to be the data size. doenc: jcxz doen0 mov chrcnt,cx ; Number of bytes of source data mov bufpnt,offset data ; Source of data. mov bx,offset nulref ; Null routine for refilling buffer. mov ah,rptq mov origr,ah ; Save repeat prefix here. mov rptct,1 ; Number of times char is repeated. mov rptval,0 ; Value of repeated char. call encode ; Make a packet with size in AX. nop nop nop mov pack.argbk1,ax ; Store length of data field mov cx,ax call movpak ; Move to data part of packet. cmp chrcnt,0 ; Did all chars fit into the buffer? jne doen1 ; ne = no, we have an error condition clc ; clear c bit for success doen0: ret doen1: stc ; set c bit for did not fit condition ret ; CX is set before this is called. movpak: push es mov ax,ds mov es,ax cld mov si,offset filbuf ; Move from here mov di,offset data ; to here cmp cx,0 jle movpak1 repne movsb movpak1:pop es ret ; Dodecoding. dodec: mov cx,pack.argbk1 ; Size of data. jcxz dodc0 ; z = nothing to transfer push ax ; Save packet size. mov bx,offset data ; Address of data. mov ax,offset nulr ; Routine to dump buffer (null) mov bufpnt,offset decbuf ; Where to put output. mov chrcnt,maxpack ; Buffer size call decode nop nop nop call decmov ; Move decoded data back to "data" buffer. pop ax dodc0: ret ; Move decoded data from decode buffer back to "data". decmov: push si push di push es mov ax,ds mov es,ax cld mov cx,bufpnt ; Last char we added. sub cx,offset decbuf ; Get actual number of characters. mov pack.argbk1,cx ; Remember size of real data. mov si,offset decbuf ; Data is here mov di,offset data ; Move to here cmp cx,0 jle decmov1 ; le = none to do repne movsb ; Copy the data. decmov1:mov al,0 ; Null to end the string. stosb pop es pop di pop si ret ; Abort ABORT PROC NEAR mov difsiz,0 ; clear original filename mov byte ptr sendas,0 ; clear sendas name cmp filopn,0 ; Any disk files open? je abort0 ; No so don't do a close. mov ah,close2 ; DOS 2.0 close file push bx mov bx,diskio.handle ; file handle int dos pop bx mov filopn,0 ; say file is closed now abort0: mov pack.state,'A' ; Otherwise abort. or errlev,1 ; set DOS error level ret ABORT ENDP ; This is where we go if we get an error packet. A call to ERROR ; positions the cursor and prints the message. A call to ERROR1 ; just prints a CRLF and then the message. ERROR PROC NEAR mov pack.state,'A' ; Set the state to abort. test flags.remflg,dquiet ; quiet display mode? jnz errorx ; nz = yes. Don't write to screen call erpos ; Position the cursor. jmp error2 ERROR1: mov ah,prstr ; entry point for Server Generic cmds. mov dx,offset crlf int dos error2: push bx mov bx,pack.argbk1 ; Get the length of the data. add bx,offset data ; Get to the end of the string. mov ah,'$' ; Put a dollar sign at the end. mov [bx],ah mov ah,prstr ; Print the error message. mov dx,offset data int dos pop bx errorx: ret ERROR ENDP ; Set the maximum send data packet size; modified for long packets. PACKLEN PROC NEAR push ax push cx xor ah,ah mov al,trans.spsiz ; Maximum send packet size for Regular pkts. cmp ax,trans.slongp ; negotiated long packet max size jae pack2 ; ae = use regular packets mov ax,trans.slongp ; else use long kind sub ax,3 ; minus extended count & checksum cmp ax,(95*94-1-2) ; longer than Long? jle pack2 ; le = no, Long will do dec ax ; minus one more for extra long count pack2: sub ax,2 ; minus Sequence, Type sub al,trans.chklen ; And minus Checksum chars. sbb ah,0 ; borrow propagate cmp trans.ebquot,'N' ; Doing 8-bit Quoting? je pack0 ; Nope so we've got our size. cmp trans.ebquot,'Y' je pack0 ; Not doing it in this case either. dec ax ; Another 1 for 8th-bit Quoting. pack0: cmp rptq,0 ; Doing repeat character Quoting? je pack1 ; Nope, so that's all for now. dec ax ; minus repeat prefix dec ax ; and repeat count pack1: dec ax ; for last char might being a control code mov trans.maxdat,ax ; Save max length for data field. pop cx pop ax ret PACKLEN ENDP ; Print the number in AX on the screen in decimal rather that hex. NOUT PROC NEAR test flags.remflg,dserial ; serial display mode? jnz pnout ; nz = use "dot and plus" for serial mode test flags.remflg,dquiet ; quiet display mode? jnz nout1 ; nz = yes. Don't write to screen call decout ; call standard decimal output routine nout1: ret pnout: or ax,ax ; display packet in serial display mode jz pnoutx ; z = nothing to display push ax ; for serial mode display push dx ; output .........+.........+ etc mov temp,10 mov dx,0 div temp ; number/10. (AX=quo, DX=rem) push ax ; save around printing push dx ; save around initial printing cmp dx,0 ; remainder non-zero? jne pnout1 ; ne = yes mov dl,'+' ; symbol plus for tens jmp pnout2 ; display it pnout1: mov dl,'.' ; symbol for between tens pnout2: mov ah,conout ; output to console int dos pop dx ; recover remainder pop ax ; recover quotient or dx,dx ; check for multiples of 70, to break lines jnz pnout3 ; nz = non-zero remainder, just exit mov temp,7 ; divide ax by 7 (dx is zero by construction) div temp ; ax = quotient, dx = remainder or dx,dx ; zero remainder? jnz pnout3 ; nz = non-zero remainder, just exit mov ah,prstr ; output cr/lf after every 70th chars mov dx,offset crlf int dos pnout3: pop dx pop ax pnoutx: ret NOUT 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 ; Jumping here is the same as a ret. R PROC NEAR ret R ENDP code ends end