NAME msssen ; File MSSSEN.ASM include mssdef.h ; Edit history: ; Last edit 14 Jan 1990 ; Sliding Windows public spar, rpar, nout, send, flags, trans, dtrans, packlen public send10, mail, newfn, errpack, sstate, response, ackmsg public pktnum, numpkt, cntretry, sndpak, sparmax spmin equ 20 ; Minimum packet size spmax equ 94 ; Maximum packet size data segment public 'data' extrn fsta:word, auxfile:byte, encbuf:byte, decbuf:byte, maxtry:byte extrn errlev:byte, kstatus:word, diskio:byte, rpacket:byte extrn windflag:byte, rstate:byte, fmtdsp:byte, windlow:byte extrn charids:word flags flginfo <> trans trinfo <> ; negotiated trans information dtrans trinfo <> ; default trans information crlf db cr,lf,'$' ender db bell,bell,'$' cemsg db 'User intervention',0 erms14 db 'No response from the host',0 erms15 db 'File not found',0 erms16 db 'Unknown packet type',0 erms17 db 'Too many retries',0 erms24 db 'Unable to send packet',0 erms25 db 'Host does not support Kermit MAIL command',0 erms26 db 'File rejected by host',0 erms27 db 'Error. No buffers in send routine',0 erms30 db ': File size',0 erms31 db ': Date/time',0 erms32 db ': Mailing refused',0 erms33 db ': File Type',0 erms34 db ': Transfer Char-set',0 erms36 db ': Unknown reason',0 infms1 db cr,' Sending: In progress',cr,lf,'$' infms2 db cr,' Mailing: In progress',cr,lf,'$' infms3 db 'Completed',cr,lf,'$' infms4 db 'Failed',cr,lf,'$' infms5 db 'Remote name is ',0 filhlp db ' A filename (possibly wild)$' filmsg db ' Local Source File or press ENTER for prompts$' remfnm db ' Remote Destination File: ',0 ; asciiz lclfnm db ' Local Source File: ',0 ; asciiz mailhlp db ' Filename mail-address or press ENTER for prompts$' mailto db ' To: ',0 ; asciiz mailtohlp db ' mail address (eg, user@host or host::user)$' mailflg db 0 ; 1 if Mail, 0 if Send command asmsg db ' as ',0 sstate db 0 ; current automata state pktnum db 0 ; packet number sndcnt db 0 ; retry counter for sndpak, internal filopn db 0 ; 1 if disk file is open for reading tempseq db 0 ; target sequence number for responses retry db 0 ; current retry threshold even attlist dw sat1t,sat2t,sat3t,sat4t,sat5t,sat6t,sat7t,0 ;attrib procedures attptr dw 0 ; pointer to items in attlist numpkt dw 0 ; number of packets for file group temp dw 0 temp4 dw 0 ninefive dw 95 ; constant word for long packets data ends code segment public 'code' extrn serini:near, comnd:near, init:near extrn spack:near, rpack:near, gtnfil:near, gtchr:near extrn getfil:near, rprpos:near, prtasz:near, cxerr:near extrn ermsg:near, rtmsg:near, cxmsg:near, stpos:near, decout:near extrn doenc:near, dodec:near, lnout:near, getbuf:near, winpr:near extrn prompt:near, intmsg:near, chkwind:near, msgmsg:near extrn strcpy:near, strlen:near, pktsize:near extrn pcwait:near, ihostr:near, begtim:near, endtim:near, makebuf:near extrn bufclr:near, pakptr:near, bufrel:near,isfile:near assume cs:code, ds:data, es:nothing ; Data structures comments. ; Sent raw text material (typically rpar and filenames) is placed in encbuf, ; which may be encoded by doenc. doenc needs an output buffer provided as ; a pointer generated here via procedure buflist. encbuf is 512 bytes long. ; Sent packet material is placed in buffers pointed at by buflist. These ; buffers are subdivisions of one large buffer bufbuf (private to msscom). ; Proceedure makebuf does the subdivision and initialization of contents. ; Received material is directed to buffer rbuf which is part of structure ; rpacket; rbuf is 128 bytes long. ; Rpack and Spack expect a pointer in SI to the packet data field, done in a ; pktinfo format. ; SEND filespec command ; MAIL filspec user@node command SEND PROC NEAR mov mailflg,0 ; Send command, not Mail command mov temp,0 jmp short send1 ; join common code MAIL: mov mailflg,1 ; set flag for Mail command vs Send mov temp,1 ; temp copy of mailflag send1: mov auxfile,0 ; clear send-as name (in case none) mov dx,offset diskio.string ; address of filename string mov bx,offset filmsg ; help message cmp mailflg,0 ; Mail command? je send2 ; e = no mov mailflg,0 ; clear in case error exit mov bx,offset mailhlp ; help message send2: mov ah,cmword ; get input file spec call comnd jnc send2a ; nc = success ret ; failure send2a: cmp diskio.string,'#' ; first char a replacement for '?'? jne send2b ; ne = no mov diskio.string,'?' ; yes. Change '#' for '?' send2b: cmp ah,0 ; any text given? je send3 ; e = no, prompt cmp temp,0 ; Mail command? jne send5 ; ne = yes, require address mov bx,offset auxfile ; send file under different name? mov dx,offset filhlp ; help mov ah,cmline ; allow embedded white space call comnd jnc send2c ; nc = success ret ; failure send2c: cmp auxfile,'#' ; first char a replacement for '?'? jne send2d ; ne = no mov auxfile,'?' ; change '#' to '?' send2d: jmp send6 ; join common completion code send3: 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 mov ah,cmword ; get filename call comnd ; try again for a local filename jnc send3a ; nc = success ret ; failure send3a: cmp diskio.string,'#' ; first char a replacement for '?'? jne send3b ; ne = no mov diskio.string,'?' ; yes. Change '#' for '?' send3b: mov temp4,ax mov ah,cmeol ; get the terminating CR call comnd jnc send3c ; nc = success ret ; failure send3c: mov ax,temp4 cmp ah,0 ; user's byte count je send3 ; e = nothing was typed, get some send4: mov dx,offset remfnm ; ask for remote name first cmp temp,0 ; Mail command? je send4a ; e = no mov dx,offset mailto ; ask for name@host send4a: call prompt send5: mov bx,offset auxfile ; send file under different name? mov dx,offset filhlp ; help cmp temp,0 ; Mail command? je send5a ; e = no mov dx,offset mailtohlp ; help send5a: mov ah,cmline ; allow embedded white space call comnd jnc send5b ; nc = success ret ; failure send5b: cmp ah,0 ; text entered? je send4 ; e = no, get some send6: mov flags.xflg,0 ; reset flag for normal file send[mtd] mov sstate,0 ; dummy state, must be illegal mov ax,temp ; get temp mailflag mov mailflg,al ; store in secure area for later 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 SEND10: ; SEND10 is an entry point for REMote cmds mov kstatus,0 ; global status, success mov windflag,0 ; init windows in use display flag call makebuf ; make some packet buffers call packlen ; compute packet length call cxerr ; clear Last Error line call cxmsg ; clear Last Message line mov ax,offset diskio.string ; filename to send, can be wild call isfile ; does file exist? jnc send12 ; carry reset = yes, file found cmp sstate,'S' ; was this from a remote GET? jne send11 ; ne = no, print error and continue mov dx,offset erms15 ; file not found mov trans.chklen,1 ; send init checksum is always 1 char call ermsg mov bx,dx call errpack ; go complain mov sstate,'A' ; abort ret send11: mov ah,prstr mov dx,offset crlf int dos mov dx,offset erms15 ; 'file not found' call prtasz or errlev,1 ; set DOS error level or fsta.xstatus,1 ; set status mov kstatus,1 ; global status mov mailflg,0 ; clear Mail flag clc ; pretend successful completion ret send12: call serini ; initialize serial port jnc send13 ; nc = success or errlev,1 ; say send failed or fsta.xstatus,1 ; set status mov kstatus,1 ; global status mov dx,offset erms14 ; no response from host call ermsg ; show message stc ; return failure ret send13: mov pktnum,0 ; set the packet number to zero mov fsta.pretry,0 ; set the number of retries to zero mov numpkt,0 ; number pkts send in this file group mov flags.cxzflg,0 mov sstate,'S' ; set the state to send initiate call ihostr ; initialize the host (clear NAKs) call init ; clear line and initialize buffers test flags.remflg,dquiet+dserial ; quiet or serial display mode? jnz send15 ; nz = yes, surpress msgs call stpos ; show status of file transfer mov dx,offset infms1 ; Sending in progress message cmp mailflg,0 ; Sending, vs Mailing? je send14 ; e = yes, sending mov dx,offset infms2 ; Mailing in progress message send14: mov ah,prstr int dos send15: jmp dispatch ; sstate has initial state ('S') SEND ENDP dispatch proc near ; Dispatch on state variable sstate mov ah,sstate cmp ah,'S' ; send initiate state? jne dispat2 ; ne = no call sinit ; negotiate jmp dispatch dispat2:cmp ah,'F' ; file header state? jne dispat3 ; ne = no call sfile ; send file header jmp dispatch dispat3:cmp ah,'a' ; send attributes state? jne dispat4 ; ne = no call sattr ; send attributes jmp dispatch dispat4:cmp ah,'D' ; data send state? jne dispat5 ; ne = no call sdata ; send data jmp dispatch dispat5:cmp ah,'Z' ; EOF state? jne dispat6 call seof ; do EOF processing jmp dispatch dispat6:cmp ah,'B' ; end of file group state? jne dispat7 call seot jmp dispatch dispat7:cmp ah,'E' ; user intervention ^C or ^E? jne dispat8 ; ne = no call bufclr mov bx,offset cemsg ; user intervention message call errpack ; send error message call intmsg ; show interrupt msg for Control-C-E dispat8:push ax ; 'A' abort or 'C' completion pop ax mov mailflg,0 ; clear Mail flag call bufclr ; release all buffers mov windlow,0 mov pktnum,0 call stpos ; show status of file transfer mov dx,offset infms3 ; Completed message cmp sstate,'C' ; send complete state? je dispat9 ; e = yes, else failure mov dx,offset infms4 ; Failed message or errlev,1 ; say send failed or fsta.xstatus,1 ; set status mov kstatus,1 ; global status dispat9:test flags.remflg,dquiet+dserial ; quiet or serial display mode? jnz dispa9a ; nz = yes, keep going mov ah,prstr ; show completed/failed message int dos dispa9a:cmp flags.cxzflg,0 ; completed normally? je dispa10 ; e = yes or errlev,1 ; say send failed or fsta.xstatus,1+80h ; set status, failed + intervention mov kstatus,1+80h ; global status dispa10:mov ax,1 ; tell statistics this was a send operation call endtim ; stop statistics counter test flags.remflg,dquiet ; quiet display mode? jnz dispa13 ; nz = yes, no talking call intmsg ; show any interruption cmp flags.belflg,0 ; Bell desired? je dispa13 ; e = no mov ah,prstr mov dx,offset ender ; ring bells int dos dispa13:call rprpos mov flags.cxzflg,0 ; clear flag for next command mov auxfile,0 ; clear send-as filename buffer mov flags.xflg,0 ; clear to-screen flag mov diskio.string,0 ; clear active filename buffer mov fsta.xname,0 ; clear statistics external name clc ret ; return to main command parser dispatch endp ; Enter with filespec in diskio.string, external name/mail address in auxfile ; Send Initiate packet SINIT PROC NEAR call begtim ; get tod for start of transfer mov trans.windo,1 ; one window slot before negotiations call makebuf ; remake buffers for new windowing call packlen ; compute packet length mov windlow,0 ; window lower border mov pktnum,0 ; sequence number to use mov windflag,0 ; windows in use display flag mov al,dtrans.xchset ; reset Transmission char set mov trans.xchset,al ; to the current user default mov al,dtrans.xtype ; ditto for File Type mov trans.xtype,al call getbuf ; get a buffer address into si call sparmax ; set up our maximum capabilities call rpar ; put them into the packet mov trans.chklen,1 ; Send init checksum is always 1 char mov [si].pktype,'S' ; send-initiate packet call sndpak ; send the packet jnc sinit2 ; nc = successful send ret ; failure, change state sinit2: mov al,pktnum ; packet just sent mov ah,maxtry ; normal retry threshold add ah,ah add ah,maxtry ; triple the normal threshold call response ; get response jnc sinit3 ; nc = success ret sinit3: push si ; ACK in window mov si,offset rpacket ; point to packet for spar call spar ; parse the received data pop si call makebuf ; remake buffers for new windowing call packlen ; update max send packet size mov pktnum,1 ; next sequence number after 'S' pkts mov windlow,1 ; lowest acceptable sequence received cmp mailflg,0 ; non-zero to do Mail command je sinit4 ; e = send, not mail command cmp flags.attflg,0 ; file attributes disabled? je sinit5 ; e = yes, so no Mail test trans.capas,8 ; can they do file attributes? jz sinit5 ; z = no, so cannot do Mail sinit4: call getfil ; open the file jc sinit4a ; c = error mov filopn,1 ; disk file is open mov sstate,'F' ; set the state to file send ret sinit4a:mov dx,offset erms15 ; file not found jmp giveup ; something is wrong, quit w/msgs ; say Mail not supported by host sinit5: mov dx,offset erms25 ; say no mail today call ermsg ; tell both sides mov sstate,'B' ; go to EOT state ret SINIT ENDP ; Send file header ; Enter with pktnum set for the next transmission, no buffer SFILE PROC NEAR call begtim ; start statistics cmp filopn,1 ; is file open already? je sfile1 ; e = yes call bufrel ; release buffer for SI mov dx,offset erms24 ; cannot send packet jmp giveup ; something is wrong, quit sfile1: mov flags.cxzflg,0 ; clear ^C, ^E, ^X, ^Z flag call cxerr ; clear Last Error line call cxmsg ; clear Last Message line call getbuf ; get a packet buffer, address in si jnc sfile2 ; nc = got one mov dx,offset erms27 ; no buffers jmp giveup ; tell both sides and fail sfile2: mov dx,offset encbuf ; destination = encode source buffer call strlen ; get length (w/o terminator) into cx call doenc ; do encoding; length is in cx mov [si].pktype,'F' ; File header packet cmp flags.xflg,0 ; REMOTE command? (goes to screen) je sfile3 ; e = no mov [si].pktype,'X' ; use X rather than F for REMOTE cmds sfile3: call sndpak ; send the packet jnc sfile4 ; nc = success ret sfile4: mov al,pktnum ; want response for packet just sent mov ah,maxtry ; retry threshold call response ; get response jnc sfile5 ; nc = success (got an ACK) ret sfile5: call ackmsg ; show any message in ACK inc pktnum ; next pkt to send/rcv, from ackpak and pktnum,3fh ; modulo 64 mov sstate,'a' ; set file attributes as next state ret ; return to do next state SFILE ENDP ; Send file attributes. Attributes: file size in bytes and kilobytes, ; file time and date, machine identification. ; Writes output to buffer encbuf. SATTR PROC NEAR cmp flags.attflg,0 ; allowed to do file attributes? je satt0 ; e = no mov attptr,offset attlist ; point at list of attributes procs test trans.capas,8 ; can we do file attributes? jnz satt1 ; nz = yes satt0: mov sstate,'D' ; set the state to send-data ret satt1: mov bx,attptr ; ptr to list of attributes procedures cmp word ptr [bx],0 ; at end of list? je satt0 ; e = yes, do next state call getbuf ; get buffer for sending jnc satt2 ; nc = got one mov dx,offset erms27 ; no buffers call giveup ; tell both sides and fail stc ret satt2: mov di,offset encbuf ; address of a temp data buffer mov byte ptr [di],0 ; start with null terminator push es ; save es around this work push ds pop es ; set es to data segment for es:di push si mov bx,attptr ; ptr to list of attributes routines mov bx,[bx] ; de-reference one level call bx ; do it pop si pop es mov byte ptr [di],0 ; insert null terminator in temp buf mov dx,offset encbuf call strlen ; get length of this attribute to CX cmp cx,trans.maxdat ; longer than any packet? jbe satt3 ; be = no, proceed call bufrel ; release this buffer mov dx,offset erms24 ; "Unable to send packet" call ermsg ; position cursor, display asciiz msg mov flags.cxzflg,'X' ; say skip this file mov sstate,'Z' ; move to EOF state ret satt3: mov ax,[si].datlen ; data length for packet, so far add ax,cx ; plus, maybe, this contribution cmp ax,trans.maxdat ; new info fits into packet? jg satt4 ; g = no, send what we have push si ; preserve packet structure pointer mov di,[si].datadr ; packet buffer beginning add di,[si].datlen ; plus current contents add [si].datlen,cx ; say adding this many bytes mov si,offset encbuf ; temp buffer is source of new info cld ; packet buffer is destination rep movsb ; copy new material to packet buffer pop si add attptr,2 ; next attributes procedure address mov bx,attptr cmp word ptr [bx],0 ; at end of list? jne satt2 ; ne = no, do next attribute proc cmp [si].datlen,0 ; any data to send? jne satt4 ; ne = yes call bufrel ; release the unused buffer mov sstate,'D' ; set the state to send-data ret satt4: call sndatt ; send attributes packet, get response jc satt5 ; c = failure jmp satt1 ; get new buffer, do more attributes satt5: ret ; failure, change state ; Send Attributes packet, local worker sndatt: mov [si].pktype,'A' ; Attributes packet call sndpak ; send the packet jnc sndat1 ; nc = success ret sndat1: mov al,pktnum ; get response for packet just sent mov ah,maxtry ; retry threshold call response ; get response jnc sndat2 ; nc = success ret sndat2: inc pktnum ; sent and ack'd, next seqnum to use and pktnum,3fh cmp rpacket.datlen,0 ; any data in the ACK? je sndat3 ; e = no mov bx,rpacket.datadr ; received data field cmp byte ptr[bx],'N' ; are they refusing this file? je sndat4 ; e = yes, 'N' = refusing the file sndat3: clc ; say success ret ; display file refusal reason sndat4: test flags.remflg,dquiet ; quiet display? jnz sndat7 ; nz = yes mov dx,offset erms26 ; say host rejected the file call ermsg mov cx,rpacket.datlen ; display all reasons sndat5: dec cx ; next byte inc bx ; point to next data field item cmp cx,0 ; anything there? jle sndat7 ; b = no mov ah,[bx] ; get reason code mov dx,offset erms30 cmp ah,'1' ; Byte count? je sndat6 ; e = yes cmp ah,'!' ; Kilobyte count? je sndat6 mov dx,offset erms31 cmp ah,'#' ; Date and Time? je sndat6 ; e = yes mov dx,offset erms32 cmp ah,'+' ; Mail? je sndat6 ; e = yes mov dx,offset erms33 cmp ah,'"' ; File Type? je sndat6 mov dx,offset erms34 cmp ah,'*' ; Transfer Char-set? je sndat6 mov dx,offset erms36 ; unknown reason sndat6: call prtasz ; display reason jmp short sndat5 ; do any other reasons sndat7: mov flags.cxzflg,'X' ; simulate Control-X to discard mov sstate,'Z' ; send EOF with Discard stc ret ; Individual attribute routines. Each expects DI to point to a free storage ; byte in an output buffer and it updates DI to the next free byte. Expects ; ES to be pointing at the data segment. Ok to clobber SI here. sat1t: test flags.attflg,attlen ; can we send length attribute? jz sat1tx ; z = no mov si,di ; remember starting location mov byte ptr [di],'1' ; file length (Bytes) specifier mov dx,diskio.sizehi ; high word of length mov ax,diskio.sizelo ; low word of length add di,2 call lnout ; convert file length, write to [di++] mov cx,di ; compute field length sub cx,si sub cx,2 add cl,32 ; field length to ascii mov [si+1],cl ; length. Done with File Size sat1tx: ret ; Kilobyte attribute sat2t: test flags.attflg,attlen ; can we send length attribute? jz sat2tx ; z = no mov byte ptr[di],'!' ; file length (Kilobytes) specifier inc di mov temp4,di ; remember place for count field inc di ; data field mov dx,diskio.sizehi ; high word of length, from file open mov ax,diskio.sizelo ; low word of length add ax,1023 ; add 1023 to round up adc dx,0 mov al,ah ; do divide by 1024 bytes mov ah,dl mov dl,dh ; divide by 256 part mov dh,0 ror dl,1 ; low bit to carry flag rcr ax,1 ; divide by two, with carry in clc ror dl,1 ; low bit to carry flag rcr ax,1 ; divide by two, with carry in and dl,3fh ; keep low six bits call lnout ; convert file length mov cx,di ; compute field length sub cx,temp4 ; count field location add cl,32-1 ; field length to ascii push di mov di,temp4 ; point at count field mov [di],cl ; store field length pop di ; Done with Kilobyte attribute sat2tx: ret sat3t: test flags.attflg,attdate ; can we send file date and time? jnz sat3t1 ; nz = yes ret sat3t1: cld ; file Date and Time mov al,'#' ; creation date/time specifier stosb ; and point at field length mov al,17+32 ; length of date/time field, to ascii stosb mov ah,0 mov al,diskio.dta+25 ; yyyyyyym from DOS via file open shr al,1 ; get year add ax,1980 ; add bias mov dx,0 call lnout ; put year (1988) in buffer mov ax,word ptr diskio.dta+24 ; yyyyyyyym mmmddddd year+month+day shr ax,1 ; month to al mov ah,0 mov cl,4 shr al,cl ; month to low nibble mov byte ptr[di],'0' ; leading digit inc di cmp al,9 ; more than one digit? jbe sat3t2 ; be = no mov byte ptr[di-1],'1' ; new leading digit sub al,10 ; get remainder sat3t2: add al,'0' ; to ascii stosb ; end of month mov al,diskio.dta+24 ; get day of month and al,1fh ; select day bits mov ah,0 mov cl,10 div cl ; quot = al, rem = ah add ax,'00' ; add ascii bias stosw ; leading digit and end of date mov al,' ' ; space separator stosb mov al,diskio.dta+23 ; hours hhhhhmmm mov cl,3 shr al,cl ; move to low nibble mov ah,0 mov cl,10 div cl ; quot = al, rem = ah add ax,'00' ; add ascii bias stosw ; store hours mov al,':' ; separator stosb mov ax,word ptr diskio.dta+22 ; get minutes: hhhhhmmm mmmsssss mov cl,5 shr ax,cl ; minutes to low byte and al,3fh ; six bits for minutes mov ah,0 mov cl,10 div cl add ax,'00' ; add ascii bias stosw mov al,':' ; separator stosb mov al,byte ptr diskio.dta+22 ; get seconds (double secs really) and al,1fh shl al,1 ; DOS counts by two sec increments mov ah,0 mov cl,10 div cl add ax,'00' ; add ascii bias stosw ret sat4t: mov ax,'".' ; machine indicator(.), 2 data bytes cld stosw mov ax,'8U' ; U8 = Portable O/S, MSDOS stosw ret sat5t: cmp mailflg,0 ; Mailing? jne sat5t1 ; ne = yes ret sat5t1: mov byte ptr [di],'+' ; Mail specification inc di mov dx,offset auxfile ; user@host field call strlen ; get length into cl push cx ; save address length inc cl ; include M for disposition = mail add cl,' ' ; add ascii bias mov [di],cl ; store in length field inc di mov byte ptr [di],'M' ; mail the file inc di pop cx ; recover address length jcxz sat5tx ; z = empty field mov si,dx ; user@host field cld rep movsb ; append address text to field sat5tx: ret sat6t: test flags.attflg,atttype ; can we send File Type attribute? jz sat6tx ; z = no mov al,'"' ; File Type attribute (") cld stosb cmp dtrans.xtype,0 ; Text? jne sat6t1 ; ne = no, likely Binary mov al,3+20h ; three bytes follow stosb mov al,'A' ; A for ascii stosb mov ax,'JM' ; using Control-M and Control-J stosw ; as line delimiters ret sat6t1: mov al,2+20h ; two bytes follow stosb mov ax,'8B' ; "B8" = Binary, 8-bit byte literals stosw sat6tx: ret sat7t: test flags.attflg,attchr ; Character-set allowed? jz sat7tx ; z = no cmp dtrans.xtype,1 ; Binary? je sat7tx ; e = yes, no char-set stuff mov al,'*' ; Encoding strategy cld stosb mov al,1+20h ; length following, say one char stosb mov al,'A' ; assume normal Transparent stosb cmp dtrans.xchset,0 ; is it transparent? je sat7tx ; e = yes mov al,'C' ; say transfer char-set encoding dec di ; replace 'A' with 'C' stosb push bx mov bl,dtrans.xchset ; get def char set index xor bh,bh shl bx,1 ; count words mov bx,charids[bx+2] ; bx points at set [length, string] mov al,[bx] ; get length of ident string mov cl,al ; copy to loop counter xor ch,ch inc al ; add 'C' in attribute add al,20h ; length of string + ascii bias mov byte ptr [di-2],al ; length of attribute push si mov si,bx ; ident string length byte inc si ; text of ident string cld rep movsb ; copy to destination pop si pop bx sat7tx: ret SATTR ENDP ; Send data ; Send main body of file, 'D' state SDATA PROC NEAR cmp flags.cxzflg,0 ; interrupted? je sdata1 ; e = no mov sstate,'Z' ; declare EOF, analyze interrupt there ret sdata1: call getbuf ; get a buffer for sending jnc sdata2 ; nc = success mov al,windlow ; earliest sequence number sent mov ah,maxtry ; retry threshold jmp response ; can't send, try getting responses sdata2: mov [si].pktype,'D' ; send Data packet call gtchr ; fill buffer from file and encode jc sdata3 ; c = failure (no data/EOF, other) cmp [si].datlen,0 ; read any data? je sdata3 ; e = end of data, send 'Z' for EOF call sndpak ; send the packet inc pktnum ; next sequence number to send and pktnum,3fh ; modulo 64 ret sdata3: call bufrel ; release unused buffer mov sstate,'Z' ; at End of File, change to EOF state ret SDATA ENDP ; Send EOF, 'Z' state SEOF PROC NEAR call getbuf ; get a buffer for EOF packet jnc seof1 ; nc = got one, send 'Z' packet mov al,pktnum ; seqnum of next packet to be used dec al ; back up to last used and al,3fh ; sequence number of last sent pkt mov ah,maxtry ; retry threshold jmp response ; get responses to earlier packets seof1: mov cx,0 ; assume no data cmp flags.cxzflg,0 ; interrupted? je seof3 ; e = no, send normal EOF packet call intmsg ; say interrupted mov encbuf,'D' ; Use "D" for discard mov cx,1 ; set data size to 1 or errlev,1 ; say send failed or fsta.xstatus,1+80h ; set status, failed + intervention mov kstatus,1+80h ; global status seof3: call doenc ; encode the packet (cx = count) mov [si].pktype,'Z' ; EOF packet call sndpak ; send the packet jnc seof6 ; nc = success ret seof6: mov al,[si].seqnum ; packet just sent mov ah,maxtry ; retry threshold call response ; get reponse jnc seof7 ; nc = success ret seof7: call ackmsg ; ACK, get/show any embedded message inc pktnum ; next sequence number to send and pktnum,3fh ; modulo 64 ; Heuristic: ACK to 'Z' implies mov al,windlow ; ACKs to all previous packets mov ah,pktnum ; loop limit, next packet seof8: cmp al,ah ; done all "previous" packets? je seof8b ; e = yes call pakptr ; access packet for seqnum in AL jc seof8a ; c = not in use mov si,bx ; point to it call bufrel ; release old buffer (synthetic ACK) seof8a: inc al ; next slot and al,3fh jmp short seof8 seof8b: ; end of Heuristic mov al,pktnum mov windlow,al ; update windlow to next use mov ah,close2 ; close file mov bx,diskio.handle ; file handle int dos mov filopn,0 ; no files open cmp flags.cxzflg,0 ; interrupted? je seof9 ; e = no or errlev,1 ; say send failed or fsta.xstatus,1+80h ; set status, failed + intervention mov kstatus,1+80h ; global status mov ax,1 ; tell statistics this was a send call endtim cmp flags.cxzflg,'Z' ; Control-Z seen? jne seof9 ; ne = no mov flags.cxzflg,0 ; clear the Control-Z mov auxfile,0 ; clear send-as/mail-address buffer mov sstate,'B' ; file group complete state ret seof9: mov ax,1 ; tell statistics this was a send call endtim cmp flags.cxzflg,0 ; interrupted? je seof10 ; e = no cmp flags.cxzflg,'X' ; was Control-X signaled? je seof10 ; e = yes mov sstate,'E' ; not ^X/^Z, must be ^C/^E ret seof10: mov flags.cxzflg,0 ; clear the Control-X cmp mailflg,0 ; mail? jne seof11 ; e = yes, retain address in auxfile mov auxfile,0 ; clear send-as name seof11: call GTNFIL ; get the next file jc seof12 ; c = no more files, do end of group mov filopn,1 ; file opened by gtnfil mov sstate,'F' ; set state to file header send ret seof12: mov sstate,'B' ; set state to file group completed ret SEOF ENDP ; Send EOT SEOT PROC NEAR call getbuf ; get a buffer for sending jnc seot1 ; nc = got one mov al,pktnum ; next sequence number to use dec al ; back up to last used and al,3fh ; get response to what was just sent mov ah,maxtry ; retry threshold jmp response ; get response, stay in this state seot1: mov [si].pktype,'B' ; End of Session packet call sndpak ; send the packet jnc seot2 ; nc = sucess ret seot2: mov al,pktnum ; sequence number just sent mov ah,maxtry ; retry threshold call response ; get a response to it jnc seot3 ; nc = success ret seot3: call ackmsg ; get/show any embedded message inc pktnum ; next sequence number to use and pktnum,3fh ; modulo 64 mov sstate,'C' ; set state to file completed ret SEOT ENDP ; Get response to seqnum in AL, retry AH times if necessary. ; Success: return carry clear and response data in rpacket ; Failure: return carry set and new state in sstate, will send Error packet ; Changes AX, BX, CX response proc near mov tempseq,al ; target sequence number mov retry,ah ; retry threshold mov rpacket.numtry,0 ; no receive retries yet resp1: mov ah,rpacket.numtry ; number of attempts in this routine cmp ah,retry ; done enough? ja resp3 ; yes, feign a timeout push si ; preserve regular packet pointer mov si,offset rpacket ; address of receive packet structure call rpack ; get a packet pop si jnc resp4 ; nc = success cmp flags.cxzflg,'C' ; Control-C typed? je resp2 ; e = yes, quit cmp flags.cxzflg,'E' ; Control-E typed? jne resp3 ; ne = no resp2: mov sstate,'E' ; change to Error state stc ; return failure ret resp3: inc rpacket.numtry ; no packet received, resend oldest mov al,windlow ; get oldest sequence number resp3a: call pakptr ; get packet pointer to seqnum in AL jnc resp3b ; nc = ok, sequence number is in use clc ; packet not in use, simulate success ret resp3b: push si ; resend oldest packet dec numpkt ; a retry is not a new packet sent mov si,bx ; packet pointer, from pakptr call cntretry ; count retries, sense ^C/^E jnc resp3c ; nc = ok to continue pop si ; clean stack ret ; ^C/^E encountered, change states resp3c: mov al,[si].numtry ; times this packet was retried cmp al,retry ; reached the limit? jbe resp3d ; be = no, can do more sends pop si ; clean stack mov dx,offset erms17 ; to many retries jmp giveup ; abort with msgs to local and remote resp3d: mov rpacket.numtry,0 call sndpak ; resend the packet pop si ; clean stack jnc resp1 ; nc = success, retry getting response ret resp4: call acknak ; got packet, get kind of response cmp al,0 ; ACK in window? je resp8 ; e = yes cmp al,1 ; NAK in window? je resp6 ; e = yes, repeat pkt cmp al,3 ; NAK out of window? je resp5 ; e = yes, repeat packet cmp al,4 ; ACK to inactive packet? jne resp4e ; ne = no inc rpacket.numtry ; count tries on reception jmp resp1 ; e = yes, retry reception resp4e: cmp al,5 ; NAK to inactive packet? jne resp4a ; ne = no, leaves "other" types jmp resp1 ; ignore NAK, try again ; other packet types resp4a: cmp rpacket.pktype,'M' ; Message packet? jne resp4b ; ne = no push si mov si,offset rpacket call msgmsg ; display it and discard pop si jmp resp1 ; retry getting an ACK resp4b: cmp rpacket.pktype,'E' ; Error packet? jne resp4c ; ne = no jmp error resp4c: mov dx,offset erms16 ; unknown packet type jmp giveup resp5: cmp trans.windo,1 ; is windowing off? je resp5a ; e = yes, use old heuristic call nakout ; NAK rcvd outside window, resend all inc rpacket.numtry ; count this as an internal retry jmp resp1 ; get more responses resp5a: mov al,rpacket.seqnum ; NAK rcvd outside window, say NAK is dec al ; ACK for preceeding pkt to satisfy and al,3fh ; non-windowing Kermits mov rpacket.seqnum,al ; force seqnum to preceed this NAK mov rpacket.pktype,'Y' ; force packet to look like an ACK jmp short resp4 ; reanalyze our status resp6: mov al,rpacket.seqnum ; single sequence number being NAK'd inc rpacket.numtry ; count this as an internal retry jmp resp3a ; repeat that packet ; ACK in window resp8: mov al,windlow ; try to purge all ack'd packets call pakptr ; get buffer pointer for it into BX jc resp8a ; c = buffer not in use cmp [bx].ackdone,0 ; ack'd yet? je resp8a ; e = no, stop here mov si,bx call bufrel ; ack'd active buffer, release si inc al ; rotate window and al,3fh mov windlow,al jmp short resp8 ; keep purging resp8a: mov al,tempseq ; check for our desired seqnum mov rpacket.numtry,0 cmp al,rpacket.seqnum ; is this the desired object? je resp8b ; e = yes jmp resp1 ; no, read another response resp8b: clc ; return success ret response endp ; Send packet, with retries ; Enter with SI pointing to packet structure. Success returns carry clear. ; Failure, after retries, returns carry set and perhaps a new sstate. sndpak proc near inc numpkt ; number packets sent call pktsize ; report packet qty and size mov sndcnt,0 ; send retry counter, internal cmp [si].pktype,'I' ; do not show windows for I/S je sndpa3 cmp [si].pktype,'S' je sndpa3 call winpr ; show windows in use sndpa3: call spack ; send the packet jc sndpa4 ; nc = failure ret ; return success sndpa4: push ax ; failure, do several retries mov ax,100 ; wait 0.1 seconds call pcwait call cntretry ; show retries on screen inc sndcnt ; internal retry counter mov al,sndcnt cmp al,maxtry ; reached retry limit? pop ax jbe sndpa3 ; be = no, can do more retries mov dx,offset erms24 ; cannot send packet jmp giveup ; set carry, change state sndpak endp ; Check the packet rpacket for an ACK, NAK, or other. ; Returns in CX: ; 0 for ACK to active packet (marks buffer as ACK'd, may rotate window) ; 1 for NAK to active packet ; 2 for an unknown packet type ; 3 for NAK outside the window ; 4 for other ACKs (out of window, to inactive packet) ; 5 for NAKs in window to inactive packets. ; Timeout packet (type 'T') is regarded as a NAK out of the window. ; Marks pkts ACK'd but clears packets only when they rotate below the window. ; Uses registers AX ,BX, and CX. ACKNAK PROC NEAR mov al,rpacket.seqnum ; this packet's sequence number mov ah,rpacket.pktype ; and packet type cmp ah,'Y' ; ack packet? jne ackna2 ; ne = no call pakptr ; is it for an active buffer? jnc ackna1 ; nc = yes mov al,4 ; say ACK for inactive pkt clc ret ackna1: mov [bx].ackdone,1 ; say packet has been acked cmp al,windlow ; ok to rotate window? jne ackna1a ; ne = no inc windlow ; rotate window one slot and windlow,3fh push si ; save pointer mov si,bx ; packet pointer from pakptr call bufrel ; release buffer for SI pop si ackna1a:cmp rpacket.datlen,0 ; any data in the ACK? je ackna1c ; e = no cmp sstate,'F' ; in file header state? je ackna1c ; e = yes, no protocol char mov bx,rpacket.datadr ; look for data in the ACK mov ah,[bx] cmp ah,'C' ; Control-C message? je ackna1b ; e = yes cmp ah,'X' ; quit this file? je ackna1b ; e = yes cmp ah,'Z' ; quit this file group? jne ackna1c ackna1b:mov flags.cxzflg,ah ; store here mov sstate,'Z' ; move to end of file state mov rstate,'Z' ackna1c:mov al,0 ; ack'd ok clc ret ; not an ACK ackna2: cmp ah,'N' ; NAK? je ackna3 ; e = yes cmp ah,'T' ; Timeout? je ackna3a ; e = yes, same as NAK out of window mov al,2 ; else say unknown type clc ret ackna3: inc fsta.nakrcnt ; count received NAK for statistics push si mov si,offset rpacket call chkwind ; check if seqnum is in window pop si cmp cx,0 ; in window? je ackna3b ; e = in window ackna3a:mov al,3 ; say NAK out of window clc ret ackna3b:push bx ; NAK in window, is pkt still active? call pakptr ; seqnum for an active packet? pop bx jc ackna4 ; c = no, ignore NAK as "dead NAK" mov al,1 ; say NAK for active packet clc ret ackna4: mov al,5 ; dead NAKs clc ret ACKNAK ENDP nakout proc near ; NAK out of window, resend all pkts mov al,windlow ; start here mov ah,al add ah,trans.windo and ah,3fh ; top of window+1 nakout1:call pakptr ; get pkt pointer for seqnum in al jc nakout4 ; c = slot not in use mov si,bx ; bx is packet pointer from pakptr cmp al,windlow ; count retries only for windlow jne nakout2 ; ne = not windlow call cntretry ; count retries jc nakout3 ; c = quit now mov cl,[si].numtry cmp cl,retry ; reached the limit yet? ja nakout3 ; a = yes, quit nakout2:push ax call pktsize ; report packet size call sndpak ; resend the packet pop ax jmp nakout4 ; do next packet nakout3:call bufclr ; error exit mov dx,offset erms17 ; too many retries jmp giveup ; too many retries nakout4:inc al ; next sequence number and al,3fh cmp al,ah ; sent all packets? jne nakout1 ; ne = no, repeat more packets ret ; return to do another data send nakout endp cntretry proc near cmp flags.cxzflg,'C' je cntre2 cmp flags.cxzflg,'E' je cntre2 inc fsta.pretry ; increment the number of retries inc [si].numtry ; for this packet too test flags.remflg,dserver ; server mode jnz cntre3 ; nz = yes, writing to their screen cmp flags.xflg,1 ; writing to screen? je cntre1 ; e = yes, skip this cntre3: cmp fmtdsp,0 ; formatted display? je cntre1 ; e = no call rtmsg ; display retries cntre1: clc ret cntre2: mov sstate,'E' ; abort, shared by send and receive mov rstate,'E' ; abort stc ret cntretry endp ; Display message in ACK's to F and D packets. Requires a leading protocol ; char for D packets and expects message to be encoded. ackmsg proc near test flags.remflg,dquiet ; quiet display mode? jnz ackmsgx ; nz = yes, don't write to screen cmp rpacket.datlen,0 ; any embedded message? je ackmsgx ; e = no cmp sstate,'F' ; file header state? je ackmsg1 ; e = yes, no leading protocol char cmp rpacket.datlen,1 ; D packet, skip protocol char je ackmsgx ; e = no displayable information ackmsg1:push si push ax push dx call cxmsg ; clear message space in warning area mov word ptr decbuf,0 ; clear two bytes mov si,offset rpacket ; source address call dodec ; decode message, including X/Z/other mov dx,offset decbuf+1 ; decoded data cmp sstate,'F' ; file header state? jne ackmsg3 ; ne = no push dx mov dx,offset infms5 ; give a leader msg call prtasz pop dx dec dx ; start with first message char ackmsg2:mov bx,dx cmp byte ptr [bx],' ' ; space? jne ackmsg3 ; ne = no inc dx ; next msg char jmp short ackmsg2 ; continue stripping off spaces ackmsg3:call prtasz ; display message mov ah,prstr mov dx,offset crlf int dos pop dx pop ax pop si ackmsgx:mov rpacket.datlen,0 ret ackmsg endp ; newfn -- move replacement name from buffer auxfile to buffer encbuf newfn proc near push si push di cmp auxfile,0 ; sending file under different name? je newfn4 ; e = no, so don't give new name mov si,offset auxfile ; source field mov di,offset fsta.xname ; statistics external name area call strcpy test flags.remflg,dquiet ; quiet display mode? jnz newfn2 ; nz = yes, do not write to screen mov dx,offset asmsg ; display ' as ' cmp mailflg,0 ; mail? je newfn1 ; e = no mov dx,offset mailto ; display ' To: ' newfn1: call prtasz ; display asciiz msg cmp mailflg,0 ; mail? je newfn2 ; e = no mov dx,offset auxfile ; get name call prtasz ; display asciiz string jmp newfn4 ; don't replace filename newfn2: mov si,offset auxfile ; external name mov di,offset encbuf call strcpy ; put into encoder buffer test flags.remflg,dquiet ; quiet display mode (should we print)? jnz newfn5 ; nz = yes mov dx,si call prtasz ; display external name newfn4: test flags.remflg,dserial ; serial display mode? jz newfn5 ; z = no mov dx,offset crlf ; start with cr/lf for serial display mov ah,prstr int dos newfn5: pop di pop si ret newfn endp ; This routine sets up the data for the response to an Init packet ; trans.rxxx are items which have been negotiated. ; Lines marked ;M energize the second CAPAS byte. Leave them as comments ; because earlier versions of MS Kermit (and C Kermit) are confused by it ; (failed to decode bit saying second CAPAS byte follows and thus lose sync). ; Enter with SI = pktinfo pointer ; Returns [si].datlen = length of data field, with packet buffer filled in. RPAR PROC NEAR push ax push bx mov bx,[si].datadr ; data field address mov al,trans.rpsiz ; receive packet size add al,' ' ; add a space to make it printable mov [bx],al ; put it in the packet inc bx ; 1 mov al,trans.rtime ; receive packet time out add al,' ' mov [bx],al inc bx ; 2 mov al,trans.rpad ; number of padding chars add al,' ' mov [bx],al inc bx ; 3 mov al,trans.rpadch ; padding char add al,40h ; Uncontrol it and al,7FH mov [bx],al inc bx ; 4 mov al,trans.reol ; EOL char add al,' ' mov [bx],al inc bx ; 5 mov al,trans.rquote ; quote char mov [bx],al inc bx ; 6 mov al,trans.ebquot ; 8-bit quote char mov [bx],al inc bx ; 7 mov al,trans.chklen ; Length of checksum add al,'0' ; make into a real digit mov [bx],al inc bx ; 8 mov al,trans.rptq ; repeat quote char cmp al,0 ; null means none jne rpar0 mov al,' ' ; send a blank instead rpar0: mov [bx],al inc bx ; 9 mov al,2 ; CAPAS, bit1 = can do long packets cmp flags.attflg,0 ; allowing attributes packets? je rpar1 ; e = no or al,8 ; bit #3, can do file attributes rpar1: or al,4 ; bit #4 can do windows add al,20h ; apply tochar() to byte ;M or al,1 ; say second CAPAS byte follows mov [bx],al inc bx ; 10 ; additional CAPAS go in here ;M mov byte ptr [bx],20h+20h ; Allow M (message) pkts, (#6, bit5) ;M inc bx ; 11 mov al,trans.windo ; number of active window slots (1-31) add al,20h ; apply tochar() mov [bx],al inc bx push dx ; save reg mov ax,trans.rlong ; longest packet 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 [bx],al inc bx add dl,20h ; apply tochar() to remainder mov [bx],dl pop dx ; restore regs inc bx sub bx,[si].datadr ; end minus beginning = length mov [si].datlen,bx ; length of rpar data in packet pop bx pop ax ret RPAR ENDP ; Set maximum capabilities ; dtrans are the defaults (which the user can modify), trans are negotiated ; and active values. SPARMAX PROC NEAR push ax mov al,dtrans.spsiz ; [1] regular packet size MAXL mov trans.spsiz,al mov al,dtrans.stime ; [2] send timeout value TIME mov trans.rtime,al mov al,dtrans.spad ; [3] send padding count NPAD mov trans.spad,al mov al,dtrans.spadch ; [4] send padding character PADC mov trans.spadch,al mov al,dtrans.seol ; [5] EOL character EOL mov trans.seol,al mov al,dtrans.squote ; [6] control quote character QCTL mov trans.squote,al mov al,dtrans.ebquot ; [7] 8-bit quote character QBIN mov trans.ebquot,al mov al,dtrans.chklen ; [8] initial checksum type CHKT mov trans.chklen,al mov al,dtrans.rptq ; [9] repeat prefix character REPT mov trans.rptq,al mov trans.capas,8+4 ; [10-11] capas bitmap CAPAS mov trans.capas+1,20h ; [11] CAPAS+1, Message pkts mov al,dtrans.windo ; [12] window size WINDO mov trans.windo,al mov ax,dtrans.slong ; [13-14] long packet send length, mov trans.slong,ax ; MAXLX1 and MAXLX2 ; mov al,dtrans.rpsiz ; max regular packet we can receive mov trans.rpsiz,al ; for window of one slot mov ax,dtrans.rlong ; max long packet we can receive mov trans.rlong,ax ; for window of one slot pop ax ret SPARMAX ENDP ; This routine reads in all the send init packet information ; Enter with SI = pktinfo address, [si].datlen = packet length ; All regs preserved except AX. ; dtrans.xxx are the default parameters if the other side says nothing ; trans.sxxx are the active negotiated parameters we will use. SPAR PROC NEAR push ax ; set min defaults for no host data mov trans.spsiz,80 ; [1] regular packet size MAXL mov trans.rtime,5 ; [2] send timeout value TIME mov al,dtrans.stime ; get user selected stime mov trans.stime,al mov trans.spad,0 ; [3] send padding count NPAD mov al,dtrans.spadch ; [4] send padding character PADC mov trans.spadch,al mov trans.seol,CR ; [5] EOL character EOL mov trans.squote,'#' ; [6] control quote character QCTL mov trans.ebquot,'Y' ; [7] 8-bit quote character QBIN ; end of the basic items mov al,dtrans.chklen ; [8] initial checksum type CHKT mov trans.chklen,al mov trans.rptq,0 ; [9] repeat prefix character REPT mov trans.capas,0 ; [10-11] capas bitmap CAPAS mov trans.capas+1,0 mov trans.windo,1 ; [12] window size WINDO mov ah,0 mov al,trans.spsiz mov trans.slong,ax ; [13-14] long packet send length, ; MAXLX1 and MAXLX2 ; start negotiations push si ; pktinfo structure pointer mov ax,[si].datlen ; length of received data mov ah,al ; number of args is now in ah mov si,[si].datadr ; pointer to received data cld cmp ah,0 ; [1] MAXL any data? jg spar1a ; g = yes jmp sparx1 spar1a: lodsb ; get the max regular packet size dec ah ; ah = bytes remaining to be examined sub al,' ' ; subtract ascii bias jnc spar1b ; c = old C Kermit error mov al,spmax spar1b: cmp al,dtrans.spsiz ; user limit is less? jbe spar1c ; be = no mov al,dtrans.spsiz ; replace with our lower limit spar1c: cmp al,spmin ; below the minimum? jge spar1d ; ge = no mov al,spmin spar1d: cmp al,spmax ; or above the maximum? jle spar1e ; le = no mov al,spmax spar1e: mov trans.spsiz,al ; save it push ax mov ah,0 ; set long packet to regular size mov trans.slong,ax pop ax cmp ah,0 ; [2] TIME more data? jg spar2a ; g = yes jmp sparx1 spar2a: lodsb ; get the timeout value dec ah sub al,' ' ; subtract a space cmp al,0 jge spar2b ; must be non-negative mov al,0 ; negative, so use zero spar2b: cmp al,trans.rtime ; same as other side's timeout jne spar2c ; ne = no add al,1 ; yes, but make it a little different spar2c: cmp dtrans.stime,dstime ; is current value the default? je spar2d ; e = yes, else user value overrides mov al,dtrans.stime ; get user selected stime spar2d: mov trans.stime,al ; save it ; cmp ah,0 ; [3] NPAD more data? jg spar3a ; g = yes jmp sparx1 spar3a: lodsb ; get the number of padding chars dec ah sub al,' ' cmp al,0 jge spar3b ; must be non-negative mov al,0 spar3b: mov trans.spad,al ; number of padding chars to send ; cmp ah,0 ; [4] PADC more data? jg spar4a ; g = yes jmp sparx1 spar4a: lodsb ; get the padding char dec ah add al,40h ; remove ascii bias and al,7FH cmp al,del ; Delete? je spar4b ; e = yes, then it's OK cmp al,31 ; control char? jbe spar4b ; be = yes, then OK mov al,0 ; no, use null spar4b: mov trans.spadch,al ; cmp ah,0 ; [5] EOL more data? jg spar5a ; g = yes jmp sparx1 spar5a: lodsb ; get the EOL char dec ah sub al,' ' cmp al,31 ; control char? jbe spar5b ; le = yes, then use it mov al,cr ; else use the default spar5b: mov trans.seol,al ; EOL char to be used ; cmp ah,0 ; [6] QCTL more data? jg spar6a ; g = yes jmp sparx1 spar6a: lodsb ; get the quote char dec ah cmp al,' ' ; less than a space? jge spar6b ; ge = no mov al,dsquot ; yes, use default spar6b: cmp al,7eh ; must also be less than a tilde jbe spar6c ; be = is ok mov al,dsquot ; else use default spar6c: mov trans.squote,al ; cmp ah,0 ; [7] QBIN more data? jg spar7a ; g = yes jmp sparx1 spar7a: lodsb ; get other side's 8-bit quote request dec ah call doquo ; and set quote char ; cmp ah,0 ; [8] CHKT more data? jg spar8a ; a = yes jmp sparx1 spar8a: lodsb ; get other side's checksum length dec ah call dochk ; determine what size to use ; cmp ah,0 ; [9] REPT more data? jg spar9a ; g = yes jmp sparx1 spar9a: lodsb ; get other side's repeat count prefix dec ah call dorpt ; negotiate prefix into trans.rptq ; cmp ah,0 ; [10] CAPAS more data? ja spar10a ; g = yes jmp sparx1 spar10a:lodsb ; get CAPAS bitmap from other side dec ah and al,not (1) ; remove least significant bit sub al,20h ; apply unchar() mov trans.capas,al ; store result in active byte ; spar11: cmp ah,0 ; [11] CAPAS+ more data? ja spar11a ; a = yes jmp sparx1 spar11a:test byte ptr [si-1],1 ; is CAPAS byte continued to another? jz spar12 ; z = no lodsb ; get 2nd CAPAS bitmap from other side dec ah and al,not (1) ; remove least significant bit sub al,20h ; apply unchar(). Store nothing mov trans.capas+1,al ; keep second CAPAS byte spar11c:cmp ah,0 ; [11] CAPAS++ more data? ja spar11d ; a = yes jmp sparx1 spar11d:test byte ptr [si-1],1 ; is CAPAS byte continued to another? jz spar12 ; z = no lodsb ; 3rd et seq CAPAS bitmaps dec ah and al,not (1) ; remove least significant bit sub al,20h ; apply unchar(). Store nothing jmp short spar11c ; seek more CAPAS bytes ; spar12: cmp ah,0 ; [12] WINDO more data? jg spar12a ; g = yes jmp sparx1 ; exit spar spar12a:lodsb ; get other side's window size dec ah sub al,20h ; apply unchar() call dewind ; negotiate window size ; cmp ah,2 ; [13-14] MAXL (long packet needs 2) jge spar13a ; ge = enough data to look at push ax ; make long same size as regular xor ah,ah mov al,trans.spsiz ; normal packet size mov trans.slong,ax ; assume not using long packets pop ax ; recover ah jmp sparx1 ; do final checks on packet length spar13a:test trans.capas,2 ; do they have long packet capability? jnz spar13b ; nz = yes jmp sparx1 ; no, skip following l-pkt len fields spar13b:lodsb ; long pkt length, high order byte push ax ; save ah sub al,20h ; apply unchar() xor ah,ah mul ninefive ; times 95 to dx (high), ax (low) mov trans.slong,ax ; store high order part lodsb ; long pkt length, low order byte sub al,20h ; apply unchar() xor ah,ah add ax,trans.slong ; plus high order part mov trans.slong,ax ; store it or ax,ax ; if result is 0 then use regular pkts jnz spar13c ; non-zero, use what they want mov al,trans.spsiz ; else default to regular packet size mov ah,0 mov trans.slong,ax ; and ignore the CAPAS bit (no def 500 bytes) spar13c:cmp ax,dtrans.slong ; longer than we want to do? jbe spar13d ; be = no mov ax,dtrans.slong ; limit to our longest sending size mov trans.slong,ax ; and use it spar13d:pop ax ; recover ah ; Windowing can further shrink pkts sparx1: push cx ; final packet size negotiations push dx mov ax,maxpack ; our max buffer size mov cl,trans.windo ; number of active window slots xor ch,ch jcxz sparx2 ; 0 means 1, for safety here xor dx,dx ; whole buffer / # window slots div cx ; ax = longest windowed pkt possible ; transmitter packet size sparx2: cmp ax,trans.slong ; our slots can be longer than theirs? ja sparx2a ; a = yes, use their shorter length mov trans.slong,ax ; no, use our shorter length sparx2a:mov cl,trans.spsiz ; current regular pkt size mov ch,0 cmp cx,ax ; is regular longer than window slot? jbe sparx3 ; be = no mov trans.spsiz,al ; shrink regular to windowed size ; receiver packet size sparx3: cmp ax,dtrans.rlong ; slot shorter than longest allowed? jae sparx5 ; ae = no mov trans.rlong,ax ; long size we want to receive mov cl,dtrans.rpsiz ; regular packet size user limit mov ch,0 cmp cx,ax ; is regular longer than window too? jbe sparx4 ; be = no mov cl,al ; shrink regular to windowed size sparx4: mov trans.rpsiz,cl ; regular size we want sparx5: pop dx pop cx pop si ; saved at start of spar pop ax ret SPAR ENDP ; Set 8-bit quote character based on my capabilities and the other ; Kermit's request. Quote if one side says Y and the other a legal char ; or if both sides say the same legal char. Enter with AL = their quote char DOQUO PROC NEAR cmp dtrans.ebquot,'N' ; we refuse to do 8-bit quoting? je dq3 ; e = yes, do not quote cmp al,'N' ; 'N' = they refuse quoting? je dq3 ; e = yes, do not quote cmp dtrans.ebquot,'Y' ; can we do it if requested? je dq2 ; e = yes, use their char in al cmp al,'Y' ; 'Y' = they can quote if req'd? jne dq1 ; ne = no, use their particular char mov al,dtrans.ebquot ; we want to use a particular char call prechk ; and they said 'Y', check ours jc dq3 ; c = ours is out of range, no quoting dq1: cmp al,dtrans.ebquot ; active quote vs ours, must match jne dq3 ; ne = mismatch, no quoting dq2: mov trans.ebquot,al ; get active char, ours or theirs call prechk ; in range 33-62, 96-126? jc dq3 ; c = out of range, do not quote cmp al,trans.rquote ; same prefix as control-quote? je dq3 ; e = yes, don't do 8-bit quote cmp al,trans.squote ; same prefix control-quote? je dq3 ; this is illegal too mov trans.ebquot,al ; remember what we decided on ret dq3: mov trans.ebquot,'N' ; quoting will not be done ret DOQUO ENDP ; Check if prefix in AL is in the proper range: 33-62, 96-126. ; Return carry clear if in range, else return carry set. prechk: cmp al,33 jb prechk2 ; b = out of range cmp al,62 jbe prechk1 ; be = in range 33-62 cmp al,96 jb prechk2 ; b = out of range cmp al,126 ja prechk2 ; a = out of range 96-126 prechk1:clc ; carry clear for in range ret prechk2:stc ; carry set for out of range ret ; Set checksum length. AL holds their checksum request. dochk: cmp al,'1' ; Must be '1', '2', or '3' jb doc1 ; b = not '1' to '3' cmp al,'3' jbe doc2 ; be = ok doc1: mov al,'1' ; else use default of '1' doc2: sub al,'0' ; remove ascii bias mov trans.chklen,al ; other side's request is do-able here ret ; 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: mov trans.rptq,0 ; assume will not repeat prefix call prechk ; Is it in the valid range? jnc dorpt1 ; nc = in range mov al,0 ; don't use their value dorpt1: cmp al,trans.squote ; same as the control quote char? je dorpt2 ; e = yes, that's illegal, no repeats cmp al,trans.rquote ; this too? je dorpt2 ; e = no good either cmp al,trans.ebquot ; same as eight bit quote char? je dorpt2 ; e = yes, illegal too, no repeats cmp al,dtrans.rptq ; both sides using same char? jne dorpt2 ; ne = no, that's no good either mov trans.rptq,al ; use repeat quote char now dorpt2: ret ; negotiate window size in al dewind: cmp al,dtrans.windo ; their (al) vs our max window size jbe dewind1 ; be = they want less than we can do mov al,dtrans.windo ; limit to our max size dewind1:cmp al,0 ja dewind2 inc al ; use 1 if 0 dewind2:mov trans.windo,al ; store active window size ret ; 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.slong ; negotiated long packet max size jae packle2 ; ae = use regular packets mov ax,trans.slong ; else use long kind sub ax,3 ; minus extended count & checksum cmp ax,(95*94-1-2) ; longer than Long? jle packle2 ; le = no, Long will do dec ax ; minus one more for extra long count packle2: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 packle0 ; Nope so we've got our size cmp trans.ebquot,'Y' je packle0 ; Not doing it in this case either dec ax ; Another 1 for 8th-bit Quoting. packle0:cmp trans.rptq,0 ; Doing repeat character Quoting? je packle1 ; Nope, so that's all for now dec ax ; minus repeat prefix dec ax ; and repeat count packle1: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,dserver ; server mode? jnz nout2 ; nz = yes, writing to their screen cmp flags.xflg,0 ; receiving to screen? jne nout1 ; ne = yes nout2: 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 cx push dx ; output .........+.........+ etc mov dx,0 ; extended numerator mov cx,10 div cx ; number/10. (AX=quo, DX=rem) 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 pop cx pop ax pnoutx: ret NOUT ENDP ; Decode and display Error packet message. ERROR PROC NEAR mov sstate,'A' ; Set the state to abort push si mov si,offset rpacket ; source address call dodec ; decode to decbuf mov dx,offset decbuf ; where msg got decoded, asciiz call ermsg ; display string pop si stc ; set carry for failure state ret ERROR ENDP ; General routine for sending an error packet. Register BX should ; point to the text of the message being sent in the packet ERRPACK PROC NEAR push cx push di mov di,offset encbuf ; Where to put the message mov cx,0 errpa1: mov al,[bx] inc bx cmp al,'$' ; At end of message? je errpa2 ; e = terminator cmp al,0 ; this kind of terminator too? je errpa2 ; e = terminator inc cx ; count number of chars in msg mov [di],al ; copy message inc di jmp short errpa1 errpa2: push si mov si,offset rpacket ; use response buffer mov al,pktnum mov rpacket.seqnum,al call doenc call pktsize ; report packet size mov rpacket.pktype,'E' ; send an error packet call spack mov rpacket.datlen,0 ; clear response buffer pop si pop di pop cx ret ERRPACK ENDP ; Enter with dx pointing to asciiz error message to be sent giveup proc near call bufclr ; release all buffers call ermsg ; position cursor, display asciiz msg mov bx,dx call errpack ; send error packet mov auxfile,0 ; clear send-as/mail-to buffer mov mailflg,0 ; clear Mail flag cmp filopn,0 ; disk files open? je giveu2 ; e = no so don't do a close mov ah,close2 ; close file push bx mov bx,diskio.handle ; file handle int dos pop bx mov filopn,0 ; say file is closed now giveu2: mov sstate,'A' ; abort state or errlev,1 ; set DOS error level or fsta.xstatus,1 ; set status mov kstatus,1 ; global status stc ; set carry for failure status ret giveup endp code ends end