home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
extra
/
nyenhuis3.arc
/
MSSRCV.ASM
< prev
next >
Wrap
Assembly Source File
|
1990-01-16
|
38KB
|
1,162 lines
NAME mssrcv
; File MSSRCV.ASM
include mssdef.h
; Edit history
; Last edit 16 Jan 1990
; Sliding Windows
public read2, read, rrinit, ackpak, nakpak, rstate
setattr equ 57h ; DOS get/set file's date and time
data segment public 'data'
extrn encbuf:byte, decbuf:byte, fmtdsp:byte, flags:byte, trans:byte
extrn dtrans:byte, sstate:byte, diskio:byte, auxfile:byte
extrn maxtry:byte, fsta:word, errlev:byte, kstatus:word
extrn rpacket:byte, wrpmsg:byte, numpkt:word, windlow:byte
extrn charids:word, windflag:byte
cemsg db 'User intervention',0
ermes6 db 'Unknown packet type',0
erms11 db 'Not enough disk space for file',0
erms13 db 'Unable to send reply',0
erms14 db 'No response from the host',0
erms15 db 'Error. No buffers in receive routine',0
erms29 db 'Rejecting file: ',0
erms30 db 'File size',0
erms31 db 'Date/time',0
erms32 db 'Mailer request',0
erms33 db 'File Type',0
erms34 db 'Transfer Char-set',0
erms36 db 'Unknown reason',0
infms1 db cr,' Receiving: In progress',cr,lf,'$'
infms3 db 'Completed',cr,lf,'$'
infms4 db 'Failed',cr,lf,'$'
infms6 db 'Interrupted',cr,lf,'$'
infms7 db 'Discarding $'
donemsg db '100%$'
filhlp2 db ' Local path or filename or carriage return$'
ender db bell,bell,'$'
crlf db cr,lf,'$'
badrcv db 0 ; local retry counter
filopn db 0 ; non-zero if disk file is open
ftime db 0,0 ; file time (defaults to 00:00:00)
fdate db 0,0 ; file date (defaults to 1 Jan 1980)
attrib db 0 ; attribute code causing file rejection
rstate db 0 ; state of automata
temp dw 0
data ends
code segment public 'code'
extrn gofil:near, comnd:near, cntretry:near, chkwind:near
extrn spack:near, rpack:near, serini:near, spar:near, rpar:near
extrn init:near, cxmsg:near, cxerr:near, perpos:near
extrn ptchr:near, ermsg:near, winpr:near, dskspace:near
extrn stpos:near, rprpos:near, pakdup:near, packlen:near
extrn dodec:near, doenc:near, errpack:near, intmsg:near
extrn ihostr:near, getbuf:near, prtasz:near, begtim:near
extrn endtim:near, pktsize:near,strlen:near,strcpy:near
extrn bufclr:near, bufrel:near, pakptr:near, msgmsg:near
extrn makebuf:near, clrbuf:near, pcwait:near, firstfree:near
assume cs:code, ds:data, es:nothing
; Data structures comments.
; Received packet material is placed in buffers pointed at by [si].bufadr;
; SI is typically used as a pointer to a pktinfo packet structure.
; Sent packet material (typically ACK/NAKs) is placed in a standard packet
; structure named rpacket.
; Rpack and Spack expect a pointer in SI to the pktinfo structure for the
; packet.
; RECEIVE command
READ PROC NEAR
mov bx,offset filhlp2 ; help message
mov dx,offset auxfile ; local file name string
mov byte ptr auxfile,0 ; clear it first
mov ah,cmword ; local override filename/path
call comnd
jc read1a ; c = failure
mov ah,cmeol ; get a confirm
call comnd
jc read1a ; c = failure
mov rstate,'R' ; set state to receive initiate
mov flags.xflg,0
call serini ; initialize serial port
jnc read1b ; nc = success
or errlev,2 ; set DOS error level
or fsta.xstatus,2 ; set status, failed
or kstatus,2 ; global status
test flags.remflg,dquiet ; quiet display mode?
jnz read1a ; nz = yes. Don't write to screen
mov ah,prstr
mov dx,offset infms4 ; Failed message
int dos
stc
read1a: ret ; return failure
read1b: call rrinit ; init variables for read
call clrbuf ; clear serial port buffer
call ihostr ; initialize the host
cmp flags.destflg,2 ; destination is screen?
je read2 ; e = yes
call init ; setup display form
; Called by GET & SRVSND, display ok
READ2: mov kstatus,0 ; global status, success
mov windflag,0 ; init windows in use display flag
mov numpkt,0 ; set the number of packets to zero
mov fsta.pretry,0 ; clear total retry counter
call cxerr ; clear Last Error line
call cxmsg ; clear Last Message line
call begtim ; start next statistics group
mov flags.cxzflg,0 ; reset ^X/^Z flag
cmp fmtdsp,0 ; formatted display?
je read2a ; e = no
call stpos
mov ah,prstr ; Receiving in progress msg
mov dx,offset infms1
int dos
read2a: jmp dispatch
READ ENDP
; Call the appropriate action routines for each state of the protocol machine.
; State is held in byte rstate. Enter at label dispatch.
dispatch proc near ; dispatch on state variable rstate
mov ah,rstate ; get current state
cmp ah,'R' ; Receive initiate state?
jne dispat2 ; ne = no
call rinit
jmp dispatch
dispat2:cmp ah,'F' ; File header receive state?
jne dispat3
call rfile ; receive file header
jmp dispatch
dispat3:cmp ah,'D' ; Data receive state?
jne dispat4
call rdata ; get data packets
jmp dispatch
dispat4:cmp ah,'Z' ; EOF?
jne dispat5
call reof ; do EOF wrapup
jmp dispatch
dispat5:cmp ah,'E' ; ^C or ^E abort?
jne dispat6 ; 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
; Receive Complete state processor
dispat6:cmp rstate,'C' ; completed normally?
jne dispat6a ; ne = no
cmp flags.cxzflg,0 ; interrupted?
je dispat7 ; e = no, ended normally
dispat6a:or errlev,2 ; set DOS error level
or fsta.xstatus,2+80h ; set status, failed + intervention
or kstatus,2+80h ; global status
dispat7:xor ax,ax ; tell statistics this is a receive operation
call endtim ; stop file statistics accumulator
call bufclr ; release all buffers
mov windlow,0
cmp rstate,'C' ; receive complete state?
je dispat8 ; e = yes
or errlev,2 ; Failed, set DOS error level
or fsta.xstatus,2 ; set status, failed
or kstatus,2 ; global status
call fileclose ; close output file
call filedel ; delete incomplete file
dispat8:cmp flags.destflg,2 ; receiving to screen?
je dispa11 ; e = yes, nothing to clean up
test flags.remflg,dquiet+dserial ; quiet or serial display mode?
jnz dispa11 ; nz = yes, keep going
cmp flags.xflg,0 ; writing to the screen?
jne dispa11 ; ne = yes
call stpos ; position cursor to status line
mov dx,offset infms3 ; completed message
cmp rstate,'C' ; receive complete state?
je dispa10 ; e = yes
mov dx,offset infms4 ; failed message
cmp flags.cxzflg,0 ; interrupted?
je dispa10 ; e = no, ended normally
mov dx,offset infms6 ; interrupted message
dispa10:mov ah,prstr
int dos
cmp flags.belflg,0 ; bell desired?
je dispa11 ; e = no
mov ah,prstr
mov dx,offset ender ; ring the bell
int dos
dispa11:call rprpos ; put cursor at reprompt position
mov flags.cxzflg,0 ; clear flag for next command
mov auxfile,0 ; clear receive-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 ; return to ultimate caller, success
ret
dispatch endp
; Receive routines
; Receive initiate packet (tolerates E F M S X Y types)
RINIT PROC NEAR
mov windlow,0 ; lowest acceptable packet number
mov trans.chklen,1 ; Use 1 char for init packet
call rcvpak ; get a packet
jnc rinit2 ; nc = success
ret
rinit2: mov ah,[si].pktype ; examine packet type
cmp ah,'S' ; Send initiate packet?
je rinit6 ; e = yes, process 'S' packet
cmp ah,'M' ; Message packet?
jne rinit4 ; ne = no
call msgmsg ; display message
mov trans.chklen,1 ; send Init checksum is always 1 char
call ackpak0 ; ack and release packet
ret
rinit4: cmp ah,'F' ; File receive?
je rinit5 ; e = yes
cmp ah,'X' ; File receive to screen?
je rinit5 ; e = yes
cmp ah,'Y' ; ACK to a REMOTE command?
jne rinit4a ; ne = no
call msgmsg ; show any message in the ACK
mov rstate,'C' ; Completed state
ret
rinit4a:cmp ah,'N' ; old NAK from a server?
jne rinit4b ; ne = no
call bufrel ; yes, release this packet buffer
ret ; and ignore it
rinit4b:mov dx,offset ermes6 ; say unknown packet type
jmp giveup ; tell the other side
rinit5: mov rstate,'F' ; File header receive state
ret
; 'S' packet received
rinit6: call spar ; negotiate parameters
push si
mov si,offset rpacket ; build response in this packet
call rpar ; report negotiated parameters
pop si
mov ah,trans.chklen ; negotiated checksum length
push ax ; save it
mov trans.chklen,1 ; use 1 char for init packet reply
mov rstate,'F' ; set state to file header
call ackpak ; ack with negotiated data
pop ax ; recover working checksum
mov trans.chklen,ah
call makebuf ; remake buffering for new windowing
call packlen ; compute packet length
ret
RINIT ENDP
; Receive file header (tolerates E F M X Z types)
RFILE PROC NEAR
call rcvpak ; receive next packet
jnc rfile1 ; nc = success
ret
rfile1: cmp [si].pktype,'Z' ; EOF?
jne rfile2 ; ne = no, try next type
mov rstate,'Z' ; change to EOF state, SI is valid pkt
ret
rfile2: cmp [si].pktype,'F' ; file header (F or X packet)?
je rfil3a ; e = yes, 'F' pkt
cmp [si].pktype,'X' ; visual display header?
je rfile3 ; e = yes, 'X' pkt
jmp rfile5 ; neither one
rfile3: mov flags.xflg,1 ; 'X', say receiving to the screen
rfil3a: mov filopn,0 ; assume not writing to a disk file
call dodec ; decode packet
call cxerr ; clear Last Error line
call cxmsg ; clear Last Message line
call begtim ; start statistics gathering
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 gofil ; open the output file
jnc rfile4 ; nc = success
jmp giveup ; failure, dx has message pointer
rfile4: mov filopn,2 ; say file is open for writing
push si
push di
mov si,offset decbuf ; local filename is here
mov di,offset encbuf ; destination is encoding buffer
mov byte ptr [di],' ' ; leave space for protocol char
inc di ; so other Kermits do not react
call strcpy ; copy it, to echo local name to host
dec di
mov dx,di
call strlen ; get length to cx for doenc
mov si,offset rpacket ; use this packet buffer
call doenc ; encode buffer, cx gets length
pop di
pop si
mov rstate,'D' ; set the state to data receive
jmp ackpak ; ack the packet, with filename
rfile5: mov ah,[si].pktype ; get reponse packet type
cmp ah,'B' ; 'B' End Of Transmission?
jne rfile6 ; ne = no
mov rstate,'C' ; set state to Complete
jmp ackpak0 ; ack the packet
rfile6: cmp ah,'M' ; Message packet?
jne rfile7 ; ne = no
call msgmsg ; display message
jmp ackpak0 ; ack packet, stay in this state
rfile7: mov dx,offset ermes6 ; unknown packet type
jmp giveup ; tell both sides and quit
RFILE ENDP
; Get file attributes from packet
; Recognize file size in bytes and kilobytes (used if bytes missing),
; file time and date. Reject Mail commands. Return carry clear for success,
; carry set for failure. If rejecting place reason code in byte attrib.
GETATT PROC NEAR
mov bx,[si].datadr ; pointer to data field
getat0: push bx
sub bx,[si].datadr ; bx = length to examine
cmp bx,[si].datlen ; are we beyond end of data?
pop bx
jl getat1 ; l = not yet
clc
ret ; has carry clear for success
getat1: cmp byte ptr [bx],'1' ; Byte length field?
jne getat2 ; ne = no
mov al,[bx] ; remember attribute
mov attrib,al
inc bx ; pointer
test flags.attflg,attlen ; allowed to examine file length?
jnz getat1a ; nz = yes
jmp getatunk ; z = no, ignore
getat1a:push si
call getas ; get file size
call spchk ; check available disk space
pop si
jnc getat0 ; nc = have enough space for file
ret ; return failure
getat2: cmp byte ptr [bx],'!' ; Kilobyte length field?
jne getat3 ; ne = no
test flags.attflg,attlen ; allowed to examine file length?
jnz getat2b ; nz = yes
getat2a:jmp getatunk ; z = no, ignore
getat2b:mov al,[bx] ; remember attribute
mov attrib,al
inc bx ; pointer
call getak ; get file size
jc getat2a ; carry means decode rejected
call spchk ; check available disk space
jnc short getat0
ret ; return failure
getat3: cmp byte ptr [bx],'#' ; date field?
jne getat4 ; ne = no
mov word ptr ftime,0 ; clear time and date fields
mov word ptr fdate,0
test flags.attflg,attdate ; allowed to update file date/time?
jnz getat3a ; nz = yes
jmp getatunk ; z = no, ignore
getat3a:mov al,[bx] ; remember attribute
mov attrib,al
inc bx
call getatd ; get file date
jmp short getat0
getat4: cmp byte ptr [bx],'+' ; Disposition?
jne getat5 ; ne = no
mov al,[bx] ; remember attribute
mov attrib,al
cmp byte ptr [bx+2],'M' ; Mail indicator?
je getat4a ; e = yes, fail
jmp getatunk ; ne = no, ignore field
getat4a:stc ; set carry for failure
ret
getat5: cmp byte ptr [bx],'"' ; File Type?
jne getat6 ; ne = no
test flags.attflg,atttype ; allowed to examine file type?
jnz getat5a ; nz = yes
jmp getatunk ; z = no, ignore
getat5a:mov attrib,'"' ; remember attribute
inc bx ; length field
xor ch,ch
mov cl,[bx] ; get length
inc bx
sub cl,20h ; remove ascii bias
jc getat5d ; c = error in length, fail
cmp byte ptr [bx],'A' ; Type letter (A, B, I), Ascii?
jne getat5b ; ne = no
mov trans.xtype,0 ; say Ascii/Text file type
add bx,cx ; step to next field
jmp getat0 ; next item please
getat5b:cmp byte ptr [bx],'B' ; "B" Binary?
jne getat5d ; ne = no, fail
cmp cl,2 ; full "B8"?
jb getat5c ; b = no, just "B"
cmp byte ptr [bx+1],'8' ; proper length?
jne getat5d ; ne = no
getat5c:mov trans.xtype,1 ; say Binary
add bx,cx ; step to next field
jmp getat0 ; next item please
getat5d:stc ; set carry for rejection
ret
getat6: cmp byte ptr [bx],'*' ; character set usage?
jne getat6d ; ne = no
test flags.attflg,attchr ; allowed to examine char-set?
jnz getat6a ; nz = yes
getat6d:jmp getatunk ; z = no, ignore
getat6a:mov attrib,'*' ; remember attribute
inc bx ; length field
xor ch,ch
mov cl,[bx] ; get length
inc bx
sub cl,20h ; remove ascii bias
js getat6c ; c = length error, fail
mov trans.xchset,0 ; assume Transparent Transfer char-set
cmp byte ptr [bx],'A' ; Normal Transparent?
jne getat6b ; be = not Transparent
add bx,cx ; point at next field
clc
jmp getat0
getat6b:cmp byte ptr [bx],'C' ; character set?
je getat7 ; e = yes
getat6c:stc ; set carry for rejection
ret
getat7: push si ; examine transfer character set
mov si,bx ; point at first data character
add bx,cx ; point bx beyond the text
dec cx ; deduct leading 'C' char from count
push bx ; save bx
mov bx,offset charids ; point to array of char set info
mov ax,[bx] ; number of members
mov temp,ax ; loop counter
mov trans.xchset,0 ; assume xfer char-set 0 (Transparent)
getat7a:add bx,2 ; point to a member's address
mov di,[bx] ; point at member [length, string]
cmp cl,[di] ; string lengths the same?
jne getat7b ; ne = no, try the next member
inc si ; skip the 'C'
inc di ; point at ident string
cld
push es
push ds
pop es ; set es:di to data segment
push cx ; save incoming count
push si ; save incoming string pointer
repe cmpsb ; compare cx characters
pop si
pop cx
pop es
jne getat7b ; ne = idents do not match
pop bx ; a match, use current trans.xchset
pop si
clc
jmp getat0 ; success
getat7b:inc trans.xchset ; try next set
dec temp ; one less member to consider
jnz getat7a ; nz = more members to try
pop bx ; failure to find a match
pop si
mov trans.xchset,0 ; use Transparent for unknown char set
cmp flags.unkchs,0 ; keep the file?
je getat7c ; e = yes, regardless of unk char set
stc ; set carry for rejection
ret
getat7c:jmp getat0 ; report success anyway
; workers for above
getatunk:inc bx ; Unknown. Look at length field
mov al,[bx]
sub al,' ' ; remove ascii bias
xor ah,ah
inc ax ; include length field byte
add bx,ax ; skip to next attribute
jmp getat0
; Decode File length (Byte) field
getas: mov cl,[bx] ; length of file size field
inc bx ; point at file size data
sub cl,' ' ; remove ascii bias
mov ch,0
mov ax,0 ; current length, bytes
mov dx,0
getas2: push cx
shl dx,1 ; high word of size, times two
mov di,dx ; save
shl dx,1
shl dx,1 ; times 8
add dx,di ; yields dx * 10
mov di,dx ; save dx
mov dx,0
mov cx,10 ; also clears ch
mul cx ; scale up previous result in ax
mov cl,[bx] ; get a digit
inc bx
sub cl,'0' ; remove ascii bias
add ax,cx ; add to current length
adc dx,0 ; extend result to dx
add dx,di ; plus old high part
pop cx
loop getas2
mov diskio.sizelo,ax ; low order word
mov diskio.sizehi,dx ; high order word
ret
; Decode Kilobyte attribute
getak: mov ax,diskio.sizelo ; current filesize, low word
add ax,diskio.sizehi
cmp ax,0 ; zero if not used yet
je getak1 ; e = not used before
dec bx ; backup pointer
stc ; set carry to ignore this field
ret
getak1: call getas ; parse as if Byte field
mov ax,diskio.sizelo ; get low word of size
mov dx,diskio.sizehi ; high word
mov dh,dl ; times 256
mov dl,ah
mov ah,al
mov al,0
shl dx,1 ; times four to make times 1024
shl dx,1
rol ax,1 ; two high bits of ah to al
rol ax,1
and al,3 ; keep them
or dl,al ; insert into high word
mov al,0
mov diskio.sizehi,dx ; store high word
mov diskio.sizelo,ax ; store low word
clc ; clear carry
ret
; File date and time
getatd: mov word ptr ftime,1 ; two seconds past midnight
mov word ptr fdate,0
mov dl,[bx] ; field length
mov dh,0
sub dl,' ' ; remove ascii bias
inc bx ; next field
add dx,bx ; where next field begins
mov temp,dx ; save in temp
cmp byte ptr[bx+6],' ' ; short form date (yymmdd)?
je getad2 ; e = yes
add bx,2 ; skip century digits (19)
getad2: mov ax,10
mov dx,[bx] ; get year tens and units digits
add bx,2 ; dl has tens, dh has units
sub dx,'00' ; remove ascii bias
mul dl ; ax = high digit times ten
add al,dh ; units digit
sub ax,80 ; remove rest of 1980 bias
jns getad2a ; ns = no sign = non-negative result
mov ax,0 ; don't store less than 1980
getad2a:shl al,1 ; adjust for DOS bit format
mov fdate+1,al
mov ax,[bx] ; get month digits
add bx,2
sub ax,'00' ; remove ascii bias
cmp al,0 ; tens digit set?
je getad2b ; e = no
add ah,10 ; add to units digit
getad2b:cmp ah,8 ; high bit of month set?
jb getad3 ; b = no
or fdate+1,1
sub ah,8 ; and deduct it here
getad3: mov cl,5
shl ah,cl ; normalize months bits
mov fdate,ah
mov dx,[bx] ; do day of the month
add bx,2 ; dh has units, dl has tens digit
sub dx,'00' ; remove ascii bias
mov ax,10
mul dl ; ax = ten times tens digit
add al,dh ; plus units digit
or fdate,al
cmp bx,temp ; are we at the end of this field?
jae getad5 ; ae = yes, prematurely
inc bx ; skip space separator
mov ax,10 ; prepare for hours
mov dx,[bx] ; hh digits
add bx,2
sub dx,'00' ; remove ascii bias
mul dl ; 10*high digit of hours
add al,dh ; plus low digit of hours
mov cl,3 ; normalize bits
shl al,cl
mov ftime+1,al ; store hours
inc bx ; skip colon
mov ax,10 ; prepare for minutes
mov dx,[bx] ; mm digits
add bx,2
sub dx,'00' ; remove ascii bias
mul dl ; 10*high digit of minutes
add al,dh ; plus low digit of minutes
mov ah,0
mov cl,5 ; normalize bits
shl ax,cl
or ftime+1,ah ; high part of minutes
mov ftime,al ; low part of minutes
cmp bx,temp ; are we at the end of this field
jae getad5 ; ae = yes, quit here
inc bx ; skip colon
mov ax,10 ; prepare for seconds
mov dx,[bx] ; ss digits
add bx,2
sub dx,'00' ; remove ascii bias
mul dl ; 10*high digit of seconds
add al,dh ; plus low digit of seconds
shr al,1 ; store as double-seconds for DOS
or ftime,al ; store seconds
getad5: ret
GETATT ENDP
; Receive data (tolerates A D E M Z types)
RDATA PROC NEAR
call rcvpak ; get next packet
jnc rdata1 ; nc = success
ret ; else return to do new state
rdata1: mov ah,[si].pktype ; check packet type
cmp ah,'D' ; Data packet?
je rdata3 ; e = yes
cmp ah,'A' ; Attributes packet?
je rdata4 ; e = yes
cmp ah,'M' ; Message packet?
jne rdat2 ; ne = no
call msgmsg ; display message
jmp ackpak0 ; ack the packet, stay in this state
rdat2: cmp ah,'Z' ; EOF packet?
jne rdat2a ; ne = no
mov rstate,'Z' ; next state is EOF, do not ack yet
ret
rdat2a: mov dx,offset ermes6 ; Unknown packet type
jmp giveup
rdata3: call ptchr ; decode 'D' packet, output to file
jc rdat3a ; c = failure to write output
jmp ackpak0 ; ack the packet, stay in this state
rdat3a: mov dx,offset erms11 ; cannot store all the data
jmp giveup ; tell the other side
; 'A' packet, analyze
rdata4: call getatt ; get file attributes from packet
mov cx,0 ; reply length, assume 0/nothing
jnc rdat4b ; nc = success, attributes accepted
mov cx,2 ; 2 bytes, declining the file
mov encbuf,'N' ; decline the transfer
mov al,attrib ; get attribute causing rejection
mov encbuf+1,al ; report rejection reason to sender
or fsta.xstatus,2 ; set status, failed
mov kstatus,2 ; global status, failed
test flags.remflg,dquiet ; quiet display?
jnz rdat4b ; nz = yes
push si
push cx
push ax
mov dx,offset erms29 ; say rejecting the file
call ermsg ; show rejecting file, then reason
pop ax
mov dx,offset erms30
cmp al,'1' ; Byte count?
je rdat4a ; e = yes
cmp al,'!' ; Kilobyte count?
je rdat4a ; e = yes
mov dx,offset erms31
cmp al,'#' ; Date and Time?
je rdat4a ; e = yes
mov dx,offset erms32
cmp al,'+' ; Mail?
je rdat4a ; e = yes
mov dx,offset erms33
cmp al,'"' ; File Type?
je rdat4a
mov dx,offset erms34
cmp al,'*' ; Transfer Char-set?
je rdat4a
mov dx,offset erms36 ; unknown reason
rdat4a: call prtasz ; display reason
pop cx
pop si
rdat4b: push si
mov si,offset rpacket ; encode to this packet
call doenc ; do encoding
pop si
jmp ackpak ; ACK the attributes packet
rdata endp
; End of File processor (expects Z type to have been received elsewhere)
; Enter with packet pointer in SI to a 'Z' packet.
reof proc near ; 'Z' End of File packet
cmp flags.cxzflg,0 ; interrupted?
jne reof3 ; ne = yes, no 100% done indicator
cmp fmtdsp,0 ; formatted screen?
je reof5 ; e = no, no message
cmp wrpmsg,0 ; written Percentage done yet?
je reof5 ; e = no
call perpos ; position cursor to percent done
mov dx,offset donemsg ; say 100%
mov ah,prstr
int dos
jmp short reof5 ; file close common code
reof3: call intmsg ; show interrupt msg on local screen
or errlev,2 ; set DOS error level
or fsta.xstatus,2+80h ; set status, failed + intervention
mov kstatus,2+80h ; global status
cmp flags.cxzflg,'X' ; kill one file?
jne reof5 ; ne = no
mov flags.cxzflg,0 ; clear ^X so next file survives
; common code for file closing
reof5: call fileclose ; close the file
call dodec ; decode incoming packet to decbuf
cmp decbuf,'D' ; is the data "D" for discard?
jne reof7 ; ne = no, write out file
call filedel ; delete file incomplete file
or errlev,2 ; set DOS error level
or fsta.xstatus,2+80h ; set status, failed + intervention
mov kstatus,2+80h ; global status
reof7: mov rstate,'F'
call ackpak0 ; acknowledge the packet
mov ax,0 ; tell statistics this was a receive
call endtim
mov diskio.string,0 ; clear file name
ret
reof endp
; init variables for read
rrinit proc near
call makebuf ; construct & clear all buffer slots
call packlen ; compute packet length
mov numpkt,0 ; set the number of packets to zero
mov windlow,0 ; starting sequence number of zero
mov fsta.pretry,0 ; set the number of retries to zero
mov filopn,0 ; say no file opened yet
mov windflag,0 ; windows in use init flag
mov fmtdsp,0 ; no formatted display yet
ret
rrinit endp
; Deliver packets organized by sequence number.
; Delivers a packet pointer in SI whose sequence number matches windlow.
; If necessary a new packet is requested from the packet recognizer. Failures
; to receive are managed here and may generate NAKs. Updates formatted screen.
; Store packets which do not match windlow, process duplicates and strays.
; Error packet and ^C/^E interrupts are detected and managed here.
; Return success with carry clear and SI holding the packet structure address.
; Return failure with carry set, maybe with a new rstate.
rcvpak proc near
mov al,windlow ; sequence number we want
call pakptr ; find pkt pointer with this seqnum
mov si,bx ; the packet pointer
jnc rcvpa1a ; nc = got one, else read fresh pkt
call getbuf ; get a new buffer address into si
jnc rcvpa1 ; nc = success
mov bx,offset erms15 ; insufficient buffers
jmp giveup
rcvpa1: call winpr ; show window slots in use
call rpack ; receive a packet, si has buffer ptr
jc rcvpa2 ; c = failure to receive, analyze
inc numpkt ; increment the number of packets
cmp flags.xflg,0 ; receiving to screen?
jne rcvpa1a ; ne = yes, skip displaying
cmp flags.destflg,2 ; destination is screen?
je rcvpa1a ; e = yes
call pktsize ; report packet qty and size
rcvpa1a:jmp rcvpa6 ; success, validate
; ------------------- failure to receive any packet -------------------------
; Reception failed. What to do?
rcvpa2: call cntretry ; update retries, detect ^C, ^E
jc rcvpa2a ; c = exit now from ^C, ^E
call bufrel ; discard unused buffer
inc badrcv ; count receive retries
mov al,badrcv ; count # bad receptions in a row
cmp al,maxtry ; too many?
jb rcvpa4 ; b = not yet, NAK intelligently
mov dx,offset erms14 ; no response from host
jmp giveup ; tell the other side
rcvpa2a:call bufrel ; discard unwanted buffer
stc ; set carry for failure
ret ; move to Error state
; do NAKing
rcvpa4: mov al,windlow ; Timeout or Crunched packet
add al,trans.windo ; find next slot after last good
dec al
and al,3fh ; start at window high
mov ah,-1 ; set a not-found marker
mov cl,trans.windo ; cx = number of slots to examine
xor ch,ch
rcvpa4a:call pakptr ; sequence number (in AL) in use?
jnc rcvpa4b ; nc = yes, stop here
mov ah,al ; remember seqnum of highest vacancy
dec al ; work backward in sequence numbers
and al,3fh
loop rcvpa4a
rcvpa4b:mov al,ah ; last-found empty slot (-1 = none)
cmp ah,-1 ; found a vacant slot?
jne rcvpa4c ; ne = no, else use first free seqnum
call firstfree ; set AL to first open slot
jc rcvpa4d ; c = no free slots, an error
rcvpa4c:mov rpacket.seqnum,al ; NAK this unused sequence number
call nakpak ; NAK using rpacket
jc rcvpa4d ; c = failure on sending operation
stc ; rcv failure, stay in current state
ret
rcvpa4d:mov dx,offset erms13 ; failure, cannot send reply
jmp giveup ; show msg, change states
; ------------------------- received a packet ------------------------------
; remove duplicates, validate sequence number
rcvpa6: cmp [si].pktype,'E' ; Error packet? Accept w/any seqnum
jne rcvpa6a ; ne = no
jmp error ; display message, change states
rcvpa6a:mov al,[si].seqnum ; this packet's sequence number
mov rpacket.seqnum,al ; save here for reply
call pakdup ; set ah to number of copies
cmp ah,1 ; more than one copy?
jbe rcvpa7 ; be = no, just one
call bufrel ; discard duplicate
mov al,rpacket.seqnum ; recover current sequence number
call pakptr ; get packet pointer for original
mov si,bx ; should not fail if pakdup works ok
jnc rcvpa7 ; nc = ok, work on the original again
ret ; say failure, stay in current state
rcvpa7: call chkwind ; validate sequence number (cx=status)
jc rcvpa7b ; c = outside current window
mov al,[si].seqnum ; get sequence number again
cmp al,windlow ; is it the desired sequence number?
jne rcvpa7a ; ne = no, do not change states yet
mov badrcv,0 ; clear retry counter
clc
ret ; return success, SI has packet ptr
rcvpa7a:stc ; not desired pkt, stay in this state
ret ; do not increment retry counter here
rcvpa7b:cmp cx,0 ; inside previous window?
jg rcvpa7c ; g = outside any window, ignore it
cmp [si].pktype,'I' ; let 'I' and 'S' pkts be reported
je rcvpa7d ; even if in previous window, to
cmp [si].pktype,'S' ; accomodate lost ack w/data
je rcvpa7d
call ackpak0 ; previous window, ack and ignore it
stc ; rcv failure, stay in current state
ret
rcvpa7d:mov rstate,'R' ; redo initialization when 'I'/'S'
stc ; are observed, keep current pkt
ret
rcvpa7c:call bufrel ; ignore packet outside of any window
stc ; rcv failure, stay in current state
ret
rcvpak endp
; Send ACK packet. Enter with rpacket data field set up.
; ACKPAK sends ack with data, ACKPAK0 sends ack without data.
ackpak proc near ; send an ACK packet
cmp rpacket.datlen,0 ; really just no data?
jne ackpa2 ; ne = no, send prepared ACK packet
ackpak0:mov rpacket.datlen,0 ; no data
cmp flags.cxzflg,0 ; user interruption?
je ackpa2 ; e = no
push cx ; yes, send the interrupt character
push si
mov si,offset rpacket
mov cl,flags.cxzflg ; send this so host knows about ^X/^Z
mov encbuf,cl ; put datum into the encode buffer
mov cx,1 ; data size of 1 byte
call doenc ; encode, char count is in cx
pop si
pop cx
ackpa2: mov rpacket.pktype,'Y' ; ack packet
mov rpacket.numtry,0
ackpa3: push si
mov si,offset rpacket
call spack ; send the packet
pop si
jnc ackpa4 ; nc = success
cmp flags.cxzflg,'C' ; Control-C abort?
je ackpa3a ; e = yes, quit now
cmp flags.cxzflg,'E' ; Control-E abort?
je ackpa3a ; e = yes, quit now
push ax ; send failure, retry
mov ax,100 ; 0.1 sec
call pcwait ; small wait between retries
inc rpacket.numtry
mov al,rpacket.numtry
cmp al,maxtry ; exceeded retry limit?
pop ax
jbe ackpa3 ; be = ok to try again
mov sstate,'A' ; set states to abort
mov rstate,'A'
mov rpacket.numtry,0
mov dx,offset erms13 ; unable to send reply
jmp giveup
ackpa3a:stc ; set carry for failure
ret
ackpa4: mov al,rpacket.seqnum ; success
mov rpacket.datlen,0 ; clear old contents
call pakptr ; acking an active buffer?
jc ackpa5 ; c = no such seqnum, stray ack
push si
mov si,bx ; packet pointer from pakptr
call bufrel ; release ack'ed packet
pop si
mov rpacket.numtry,0
cmp al,windlow ; acking window low?
jne ackpa5 ; ne = no
mov al,windlow ; yes, rotate the window
inc al
and al,3fh
mov windlow,al
ackpa5: clc
ret
ackpak endp
; Send a NAK. Uses rpacket structure.
NAKPAK proc near
mov rpacket.numtry,0
nakpa2: push si
mov si,offset rpacket
mov [si].datlen,0 ; no data
inc fsta.nakscnt ; count NAKs sent
mov [si].pktype,'N' ; NAK that packet
call spack
pop si
jc nakpa3 ; c = failure
mov rpacket.numtry,0
clc
ret ; return success
nakpa3: cmp flags.cxzflg,'C' ; Control-C abort?
je nakpa3a ; e = yes, quit now
cmp flags.cxzflg,'E' ; Control-E abort?
je nakpa3a ; e = yes, quit now
push ax ; send failure, retry
mov ax,100 ; wait 0.1 second
call pcwait
inc rpacket.numtry ; count attempts to respond
mov al,rpacket.numtry
cmp al,maxtry ; tried enough times?
pop ax
jbe nakpa2 ; be = ok to try again
mov sstate,'A' ; set states to abort
mov rstate,'A'
mov rpacket.numtry,0
mov dx,offset erms13 ; unable to send reply
jmp giveup
nakpa3a:stc
ret ; return failure
NAKPAK ENDP
; Close, but do not delete, output file. Update file attributes,
; add Control-Z or Control-L, if needed.
fileclose proc near
cmp filopn,0 ; is a file open?
jne filec0 ; ne = yes
ret
filec0: cmp flags.xflg,0 ; receiving to screen?
jne filec2 ; ne = yes
cmp flags.destflg,1 ; destination is disk?
jne filec1 ; ne = no
cmp flags.eofcz,0 ; should we write a ^Z?
je filec1 ; e = no, keep going
cmp trans.xtype,0 ; test mode tranfer?
jne filec2 ; ne = no, binary, no ^Z
push si
mov rpacket.datlen,1 ; one byte to decode and write
mov si,rpacket.datadr ; source buffer address
mov byte ptr[si],'Z'-40h ; put Control-Z in buffer
mov si,offset rpacket ; address for decoder
call ptchr ; decode and write to output
pop si
filec1: cmp flags.destflg,0 ; file destination is printer?
jne filec2 ; ne = no, skip next part
push si
mov rpacket.datlen,1 ; one byte to decode and write
mov si,rpacket.datadr ; source buffer address
mov byte ptr [si],'L'-40h ; put Control-L (FF) in buffer
mov si,offset rpacket ; address for decoder
call ptchr ; decode and write to output
pop si
filec2: mov ah,write2 ; write to file
xor cx,cx ; write 0 bytes to truncate length
mov bx,diskio.handle ; file handle
cmp bx,0 ; valid handle?
jl filec5 ; l = no
int dos
xor al,al ; get device info
mov ah,ioctl
int dos
test dl,80h ; bit set if handle is for a device
jnz filec4 ; nz = non-disk, no file attributes
; do file attributes and close
mov cx,word ptr ftime ; new time
mov dx,word ptr fdate ; new date
mov word ptr fdate,0
mov word ptr ftime,0 ; clear current time/date attributes
mov ax,cx
or ax,dx
jz filec4 ; z = no attributes to set
or cx,cx ; time set as null?
jnz filec3 ; nz = no
inc cl ; two seconds past midnight
filec3: mov ah,setattr ; set file date/time attributes
mov al,1 ; set, not get
mov bx,diskio.handle ; file handle
int dos ; end of file attributes
filec4: mov bx,diskio.handle ; file handle
push dx ; save dx
mov ah,close2 ; close file
int dos
pop dx
mov filopn,0 ; say file is closed
filec5: ret
fileclose endp
; Delete file whose asciiz name is in diskio.string
filedel proc near
mov dx,offset diskio.string ; file name, asciiz
cmp diskio.string,0 ; filename present?
je filede2 ; e = no
cmp flags.abfflg,0 ; keep incomplete file?
je filede2 ; e = yes
test flags.remflg,dquiet ; quiet display?
jnz filede1 ; nz = yes
cmp flags.xflg,0 ; receiving to screen?
jne filede1 ; ne = yes, no message
push dx
call cxmsg ; clear Last message line
mov dx,offset infms7 ; saying Discarding file
mov ah,prstr
int dos
pop dx
call prtasz ; show filename
filede1:mov ah,del2 ; delete the file
int dos
filede2:ret
filedel endp
; Error exit. Enter with dx pointing to asciiz error message.
; Sends 'E' Error packet and shows message on screen. Changes state to 'A'.
; Always returns with carry set.
giveup proc near
cmp flags.destflg,2 ; receiving to the screen?
je giveu1 ; e = yes, no formatted display
call ermsg ; show msg on error line
giveu1: mov bx,dx ; set bx to error message
call errpack ; send error packet just in case
mov rstate,'A' ; change the state to abort
stc ; set carry
ret
giveup endp
; ERROR sets abort state, positions the cursor and displays the Error message.
ERROR PROC NEAR
mov rstate,'A' ; set state to abort
call dodec ; decode to decbuf
mov dx,offset decbuf ; where msg got decoded, asciiz
call ermsg ; show string
stc ; set carry for failure state
ret
ERROR ENDP
; Called by GETATT in receiver code to verify sufficient disk space.
; Gets file path from diskio.string setup in mssfil, remote size in diskio
; from getatt, and whether a disk file or not via ioctl on the file handle.
; Returns carry clear if enough space.
spchk proc near ; check for enough disk space
push ax
push bx
push cx
push dx
mov ah,ioctl ; ask DOS about this file handle
mov al,0 ; get info
mov bx,diskio.handle
int dos
test dl,80h ; handle is a disk file?
jnz spchk5b ; nz = no, always enough space
and dl,01fh ; get current drive from bits 5-0
add dl,'A' ; convert to a letter
mov cl,dl ; cl holds drive letter
call dskspace ; calculate space into dx:ax
jc spchk6 ; c = error
push ax ; save low word of bytes
push dx ; save high word, dx:ax
mov dx,diskio.sizehi ; high word of file size dx:ax
mov ax,diskio.sizelo ; low word
mov cx,dx ; copy size long word to cx:bx
mov bx,ax
shr bx,1 ; divide long word by two
shr cx,1
jnc spchk2 ; nc = no carry down
or bx,8000h ; get carry down
spchk2: shr bx,1 ; divide by two again
shr cx,1
jnc spchk3
or bx,8000h ; get carry down
spchk3: shr bx,1 ; divide long word by two
shr cx,1
jnc spchk4 ; nc = no carry down
or bx,8000h ; get carry down
spchk4: shr bx,1 ; divide long word by two
shr cx,1
jnc spchk5 ; nc = no carry down
or bx,8000h ; get carry down
spchk5: add ax,bx ; form dx:ax = (17/16) * dx:ax
adc dx,cx
pop cx ; high word of disk space
pop bx ; low word
sub bx,ax ; minus inflated file size, low word
sbb cx,dx ; and high word
js spchk6 ; s = not enough space for file
spchk5b:clc
jmp short spchk7 ; enough space
spchk6: stc
spchk7: pop dx
pop cx
pop bx
pop ax
ret
spchk endp
code ends
end