home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
msvp98b1.lzh
/
MSSSEN.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-05-14
|
65KB
|
1,961 lines
NAME msssen
; File MSSSEN.ASM
include mssdef.h
; Copyright (C) 1985, 1992, Trustees of Columbia University in the
; City of New York. Permission is granted to any individual or institution
; to use this software as long as it is not sold for profit. This copyright
; notice must be retained. This software may not be included in commercial
; products without written permission of Columbia University.
;
; Edit history:
; 6 Sept 1991 version 3.11
; Last edit 22 Feb 1992
; 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, remprn
spmin equ 20 ; Minimum packet size
spmax equ 94 ; Maximum packet size
data segment
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, portval:word, chkparflg:byte
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
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
printhlp db ' Filename and any extra host''s printer paramters$'
printpmt db ' Host printer parameters: ',0 ; asciiz
printas db ' host''s printer parameters, such as /COPIES=2/QUE=HPLJ$'
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 sat5t,sat1t,sat2t,sat3t,sat4t,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
code1 segment
extrn bufclr:far, pakptr:far, bufrel:far, makebuf:far, chkwind:far
extrn getbuf:far, rpack:far, spack:far
code1 ends
code segment
extrn serini:near, comnd:near, init:near
extrn gtnfil:near, gtchr:near, clrbuf: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, winpr:near
extrn prompt:near, intmsg:near, msgmsg:near
extrn strcpy:near, strlen:near, pktsize:near, isfile:near
extrn pcwait:near, ihostr:near, begtim:near, endtim: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
; MAIL filspec user@node
; REMOTE PRINT filespec parameters
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
jmp short send1
REMPRN: mov mailflg,2 ; REMOTE PRINT entry point
mov temp,2
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
cmp temp,2 ; REMOTE PRINT?
jne send2 ; ne = no
mov bx,offset printhlp ; 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: or ax,ax ; any text given?
jz send3 ; z = no, prompt
cmp temp,0 ; Mail or REMOTE PRINT command?
jne send5 ; ne = yes, require address etc
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: push ax
mov ah,cmeol ; get the terminating CR
call comnd
pop ax
jnc send3c ; nc = success
ret ; failure
send3c: or ax,ax ; user's byte count
jz send3 ; z = 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
cmp temp,2 ; REMOTE PRINT?
jne send4a ; ne = no
mov dx,offset printpmt ; ask for host print parameters
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
cmp temp,2 ; REMOTE PRINT?
jne send5a ; ne = no
mov dx,offset printas ; help for printer parameters
send5a: mov ah,cmline ; allow embedded white space
call comnd
jnc send5b ; nc = success
ret ; failure
send5b: cmp temp,2 ; REM Print cmd?
je send6 ; e = yes, allow no parameters
or ax,ax ; text entered?
jz send4 ; z = 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,kssuc ; global status, success
mov windflag,0 ; init windows in use display flag
call makebuf ; make some packet buffers
call bufclr ; clear port buffer of old NAKs
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,kssend ; set DOS error level
or fsta.xstatus,kssend ; set status
mov kstatus,kssend ; 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,kssend ; say send failed
or fsta.xstatus,kssend ; set status
mov kstatus,kssend ; global status
mov dx,offset erms14 ; no response from host
call ermsg ; show message
stc ; return failure
ret
send13: xor ax,ax
mov pktnum,al ; set the packet number to zero
mov fsta.pretry,ax ; set the number of retries to zero
mov numpkt,ax ; number pkts send in this file group
mov flags.cxzflg,al
mov sstate,'S' ; set the state to send initiate
mov chkparflg,1 ; check for unexpected parity
call ihostr ; initialize the host (clear NAKs)
call init ; clear screen and initialize buffers
call clrbuf ; clear serial port buffer of junk
test flags.remflg,dquiet+dserial ; quiet or serial display mode?
jnz send15 ; nz = yes, suppress 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 short 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 short dispatch
dispat2:cmp ah,'F' ; file header state?
jne dispat3 ; ne = no
call sfile ; send file header
jmp short dispatch
dispat3:cmp ah,'a' ; send attributes state?
jne dispat4 ; ne = no
call sattr ; send attributes
jmp short dispatch
dispat4:cmp ah,'D' ; data send state?
jne dispat5 ; ne = no
call sdata ; send data
jmp short dispatch
dispat5:cmp ah,'Z' ; EOF state?
jne dispat6
call seof ; do EOF processing
jmp short dispatch
dispat6:cmp ah,'B' ; end of file group state?
jne dispat7
call seot
jmp short 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,kssend ; say send failed
or fsta.xstatus,kssend ; set status
mov kstatus,kssend ; 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,kssend ; say send failed
or fsta.xstatus,kssend+ksuser ; set status, failed + intervention
mov kstatus,kssend+ksuser ; 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 ; position cursor
xor al,al
mov flags.cxzflg,al ; clear flag for next command
mov auxfile,al ; clear send-as filename buffer
mov flags.xflg,al ; clear to-screen flag
mov diskio.string,al ; clear active filename buffer
mov fsta.xname,al ; 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
xor ax,ax
mov windlow,al ; window lower border
mov pktnum,al ; sequence number to use
mov windflag,al ; 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 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,'D' ; send data as the next state
test trans.capas,8 ; can they do file attributes?
jz sfile6 ; z = no, so cannot do attributes
mov sstate,'a' ; set file attributes as next state
sfile6: 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
jmp giveup ; tell both sides and fail
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
push es
push ds
pop es
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 es
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
xor dh,dh
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
xor ah,ah
mov al,diskio.dta+25 ; yyyyyyym from DOS via file open
shr al,1 ; get year
add ax,1980 ; add bias
xor dx,dx
call lnout ; put year (1990) in buffer
mov ax,word ptr diskio.dta+24 ; yyyyyyyym mmmddddd year+month+day
shr ax,1 ; month to al
xor ah,ah
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
xor ah,ah
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
xor ah,ah
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
xor ah,ah
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
xor ah,ah
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 or REMOTE PRINTing?
jne sat5t1 ; ne = yes
ret
sat5t1: mov byte ptr [di],'+' ; Disposition specification
inc di
mov dx,offset auxfile ; user@host or print param field
call strlen ; get length into cl
push cx ; save address length
inc cl ; include disposition letter M or P
add cl,' ' ; add ascii bias
mov [di],cl ; store in length field
inc di
mov byte ptr [di],'M' ; mail the file
cmp mailflg,2 ; REMOTE PRINT?
jne sat5t2 ; ne = no
mov byte ptr [di],'P' ; say disposition is Print
sat5t2: inc di
pop cx ; recover address length
jcxz sat5tx ; z = empty field
mov si,dx ; parameter 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,xfr_xparent ; 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: xor cx,cx ; 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,kssend ; say send failed
or fsta.xstatus,kssend+ksuser ; set status, failed + intervention
mov kstatus,kssend+ksuser ; 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,kssend ; say send failed
or fsta.xstatus,kssend+ksuser ; set status, failed + intervention
mov kstatus,kssend+ksuser ; 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
or al,al ; ACK in window?
jz resp8 ; z = 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
;;
jmp resp3 ; old ACK, resend oldest pkt
; the above is to accomodate "book Kermits", not a good strategy, but...
;; 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: jmp resp1 ; Unknown packet type, ignore it
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:xor al,al ; 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
jcxz ackna3b ; z = 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 [si].ackdone,0 ; has packet has been acked?
jne nakout4 ; ne = yes
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 short 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 ; count retries, sense user exit
cmp flags.cxzflg,'C' ; user wants abrupt exit?
je cntre2 ; e = yes
cmp flags.cxzflg,'E' ; Error type exit?
je cntre2 ; e = yes
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 short 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
or al,al ; null means none
jnz 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
cmp dtrans.lshift,lock_disable ; locking shift disabled?
je rpar2 ; e = yes
or al,20h ; set locking shift cap (bit #1)
rpar2: 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
push bx ; [7] 8-bit quote character QBIN
mov bx,portval
mov ah,[bx].parflg ; get our parity flag
pop bx
mov al,'Y' ; will quote upon request
cmp dtrans.lshift,lock_force ; locking shift forced?
je spmax1 ; e = yes, force use of 8-bit quotes
cmp ah,parnon ; parity of none?
je spmax1a ; e = yes, reset 8 bit quote character
spmax1: mov al,dqbin ; we want quoting, active
spmax1a:mov trans.ebquot,al ; save quoting state
mov dtrans.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 al,8+4 ; [10] capas bitmap CAPAS
cmp dtrans.lshift,lock_disable ; locking shift disabled?
je spmax2 ; e = yes
or al,20h ; locking shift capa
spmax2: mov trans.capas,al ; [10] 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 al,dtrans.stime ; get user selected stime
mov trans.stime,al ; [2] send timeout value TIME
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
push bx ; [7] 8-bit quote character QBIN
mov bx,portval ; current port settings
mov ah,[bx].parflg ; get our parity flag
pop bx
mov al,'Y' ; say will quote upon request
cmp dtrans.lshift,lock_force ; locking shift forced?
je spar0a ; e = yes, force use of 8-bit quotes
cmp ah,parnon ; parity of none?
je spar0b ; e = yes
spar0a: mov al,dqbin ; we want quoting, active
spar0b: mov trans.ebquot,al ; use proper quote char
mov dtrans.ebquot,al
mov trans.chklen,1 ; [8] initial checksum type CHKT
xor ax,ax
mov trans.rptq,al ; [9] repeat prefix character REPT
mov trans.capas,al ; [10-11] capas bitmap CAPAS
mov trans.capas+1,al
mov trans.lshift,lock_disable ; init locking shift to disabled
mov trans.windo,1 ; [12] window size WINDO
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
or ah,ah ; [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
xor ah,ah ; set long packet to regular size
mov trans.slong,ax
pop ax
or ah,ah ; [2] TIME more data?
jg spar2a ; g = yes
jmp sparx1
spar2a: lodsb ; get the timeout value
dec ah
sub al,' ' ; subtract a space
jge spar2b ; must be non-negative
xor al,al ; negative, so use zero
spar2b: cmp al,trans.rtime ; same as other side's timeout
jne spar2c ; ne = no
inc al ; 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
;
or ah,ah ; [3] NPAD more data?
jg spar3a ; g = yes
jmp sparx1
spar3a: lodsb ; get the number of padding chars
dec ah
sub al,' '
jge spar3b ; must be non-negative
xor al,al
spar3b: mov trans.spad,al ; number of padding chars to send
;
or ah,ah ; [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
xor al,al ; no, use null
spar4b: mov trans.spadch,al
;
or ah,ah ; [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
;
or ah,ah ; [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
;
or ah,ah ; [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
;
or ah,ah ; [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
;
or ah,ah ; [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
;
or ah,ah ; [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
test al,20h ; locking shift proposed?
jz spar11 ; z = no
cmp dtrans.lshift,lock_enable ; have we enabled its negotiation?
jb spar11 ; b = no, (0) disabled
ja spar10b ; a = (2) forced
mov al,trans.ebquot ; get negotiated 8-bit quote char
cmp al,'N' ; did 8-bit quote negotiation fail?
je spar11 ; e = yes, no locking shift agreement
cmp al,'Y' ; did 8-bit quote negotiation fail?
je spar11 ; e = yes, no locking shift agreement
mov trans.lshift,lock_enable ; set state of locking shift
jmp short spar11
spar10b:mov trans.ebquot,dqbin ; force default 8-bit quote prefix
mov trans.lshift,lock_force ; and activate working copy
spar11: or ah,ah ; [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:or ah,ah ; [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: or ah,ah ; [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 short sparx1 ; do final checks on packet length
spar13a:test trans.capas,2 ; do they have long packet capability?
jnz spar13b ; nz = yes
jmp short 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
xor ah,ah
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
xor ch,ch
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
xor ch,ch
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.lshift,lock_force ; forcing locking shift?
je dq5 ; e = yes, force 8-bit quoting
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
cmp al,'Y'
je dq4
call prechk ; in range 33-62, 96-126?
jc dq3 ; c = out of range, do not quote
dq4: 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
dq5: mov al,dqbin ; use default
jmp short dq2
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', '3', or 'B'
jb doc1 ; b = not '1' to '3'
cmp al,'3'
jbe doc2 ; be = ok
cmp al,'B' ; special non-blank 2 byte?
je doc2 ; e = yes
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
xor al,al ; 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:or al,al
jnz 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
cmp trans.chklen,'B'-'0' ; special 'B'?
jne packle3 ; ne = no
sub al,2 ; 'B' is two byte kind
jmp short packle3a
packle3:sub al,trans.chklen ; and minus Checksum chars
packle3a:sbb ah,0 ; borrow propagate
cmp trans.ebquot,'N' ; doing 8-bit Quoting?
je packle0 ; e = no, so we've got our size
cmp trans.ebquot,'Y'
je packle0 ; e = 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 ; e = no, 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
xor dx,dx ; extended numerator
mov cx,10
div cx ; number/10. (AX=quo, DX=rem)
or dx,dx ; remainder non-zero?
jnz pnout1 ; nz = yes
mov dl,'+' ; symbol plus for tens
jmp short 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
xor cx,cx
errpa1: mov al,[bx]
inc bx
cmp al,'$' ; at end of message?
je errpa2 ; e = terminator
or al,al ; this kind of terminator too?
jz errpa2 ; z = 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
xor ax,ax
mov auxfile,al ; clear send-as/mail-to buffer
mov mailflg,al ; clear Mail flag
cmp filopn,al ; 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,kssend ; set DOS error level
or fsta.xstatus,kssend ; set status
mov kstatus,kssend ; global status
stc ; set carry for failure status
ret
giveup endp
code ends
end