DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T m

⟦a84642682⟧ TextFile

    Length: 50561 (0xc581)
    Types: TextFile
    Names: »mssrcv.asm«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦71044c191⟧ »EurOpenD3/misc/kermit.ms-2.32.tar.Z« 
        └─⟦31f2d420d⟧ 
            └─⟦this⟧ »mssrcv.asm« 

TextFile

        NAME    mssrcv
; File MSSRCV.ASM
; Edit history
; Last edit 24 Nov 1988
; 24 Nov 1988 Add 100% screen msg done upon successful file reception.
; 21 Nov 1988 Version 2.32
; 12 Nov 1988 Modify disk space arithmetic for DOS 4 # sectors > 64K.
; 4 Aug 1988 Correct filesize test for attributes packets. Let ioctl tell
;  us the block device ident (for bridged or joined drives), from Henrik
;  Levokwetz.
; 1 July 1988 Version 2.31
; 12 June 1988 Add error recovery if serial port does not initialize.
; 31 May 1988 Use proc spchk to check available disk space
;  when using file attributes.
; 15 May 1988 Add kstatus global status word
; 23 Feb 1988 Add Mail command. [jrd]
; 14 Feb 1988 Add Attributes packets. [jrd]
; 27 Jan 1988 Remove serrst call, done now in mssker idle loop. [jrd]
; 7 Jan 1988 Check for Control-E condition before sending a NAK. [jrd]
; 1 Jan 1988 version 2.30
; 26 Dec 1987 Clean out unused pack.oldtry, etc., clean up. [jrd]
; 6 Dec 1987 Flush last disk buffer when aborting a transfer. [jrd]
; 8 Oct 1987 Ensure error pkts use 1 byte chksum at init stage. [jrd]
; 27 Aug 1987 Add tests for receiving to screen for error reports [jrd]
; 23 July 1987 Add buffer clear after opening new output file. [jrd]
; 7 June 1987 Add DOS errlev return of 2 for failure to receive. [jrd]
; 7 May 1987 Correct placement of begtim and endtim statistics calls. [jrd]
; 19 Oct 1986 Fix Rinit to use 1 byte checksums on Naks to S packets.
; 1 Oct 1986 Version 2.29a
; 17 Sept 1986 Fix file not being deleted when transfer fails. [jrd]
; 14 August 1986 Allow changing EOL characters.
; 9 August 1986 Allow Control-X/Z exit while getting 'S' packet. [jrd]
; 27 July 1986 Clear file opened flag to prevent unwanted closing of stdin.
; 16 June 1986 Add clearing of "flags.getflg" under read0: to prevent missing
;  initial packet read for REC commands. [jrd]
; 26 May 1986 Revise code to permit serial display. [jrd]
; [2.29] code frozen on 6 May 1986 [jrd]

        public  read12, read2, rin21, rfile3, read, updrtr, nak, rrinit
        include mssdef.h

datas   segment public 'datas'
        extrn   data:byte, bufpnt:word, chrcnt:word, curchk:byte, fmtdsp:byte
        extrn   flags:byte, pack:byte, trans:byte, dtrans:byte
        extrn   diskio:byte, locfil:byte, maxtry:byte, imxtry:byte
        extrn   fsta:word, errlev:byte, ofilsz:word, kstatus:word

setattr equ     57h                     ; DOS get/set file's date and time

ermes7  db      '?Unable to receive initiate-packet$'
ermes8  db      '?Unable to receive file name$'
ermes9  db      '?Unable to receive end of file$'
erms10  db      '?Unable to receive data$'
erms11  db      'Not enough disk space for file$'
infms1  db      cr,'           Receiving: In progress',cr,lf,'$'
infms3  db      'Completed',cr,lf,'$'
infms4  db      'Failed',cr,lf,'$'
infms6  db      'Interrupted',cr,lf,'$'
donemsg db      '100%$'
filhlp2 db      ' Local path or filename or carriage return$'
ender   db      bell,bell,'$'
crlf    db      cr,lf,'$'
temp    dw      0
filopn  db      0               ; Says if disk file is open
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
datas   ends

code    segment public 'code'
        extrn   gofil:near, outbuf:near, comnd:near
        extrn   spack:near, rpack:near, serini:near, serrst:near
        extrn   spar:near, rpar:near, init:near, cxmsg:near, perpos:near
        extrn   error:near, error1:near, ptchr:near, erpos:near, rtpos:near
        extrn   stpos:near, rprpos:near, nppos:near, nout:near
        extrn   dodec:near, doenc:near, errpack:near, intmsg:near
        extrn   send11:near, clrmod:near, ihostr:near
        extrn   begtim:near, endtim:near, pktsize:near,strlen:near,strcpy:near
        assume  cs:code, ds:datas

; Update retry count and fall through to send a NAK

NAK0:   call    updrtr                  ; Update retry count
nak1:   cmp     flags.cxzflg,'E'        ; Protocol abort sign?
        jne     nak2                    ; ne = no
        ret                             ; return to do current ('A') state
nak2:   cmp     flags.cxzflg,'C'        ; Control-C abort?
        jne     nak                     ; ne = no
        mov     pack.state,'A'          ; set Abort state
        ret

NAK:    mov     ax,pack.pktnum       ; Get the packet number we're waiting for
        mov     pack.seqnum,ax
        mov     pack.datlen,0           ; no data
        add     fsta.nakscnt,1          ; count NAKs sent
        mov     ah,'N'                  ; NAK that packet
        call    spack
         jmp    abort                   ; failed
         nop
        ret

updrtr: cmp     pack.state,'A'          ; Supposed to abort?
        je      upd0                    ; Yes, don't bother with retry count
        inc     pack.numrtr             ; Increment the number of retries
        cmp     flags.xflg,1            ; Writing to screen?
        je      upd0                    ; e = yes, skip this
        cmp     pack.numrtr,0           ; non-zero item to display?
        je      upd0                    ; nothing to display
        push    ax                      ; save packet type in ah
        call    rtpos                   ; Position cursor
        mov     ax,pack.numrtr
        call    nout                    ; Write the number of retries
        pop     ax                      ; recover packet type in ah
upd0:   ret

;       Abort
ABORT   PROC    NEAR
        cmp     filopn,0                ; Disk file open?
        je      abort0                  ; e = no so don't close
        cmp     flags.xflg,1            ; Writing to the screen?
        je      abort0                  ; Yes, don't close "file"
        call    outbuf          ; flush last buffer to disk, ignore errors
         nop
         nop
         nop
        mov     ah,close2               ; DOS 2.0 file close
        push    bx
        mov     bx,diskio.handle        ; file handle
        int     dos
        pop     bx
        mov     filopn,0                ; say file is no longer open
        cmp     flags.abfflg,0          ; save file after closing?
        je      abort0                  ; e = yes
        push    dx
        mov     dx,offset diskio.string ; get back file name
        mov     ah,del2                 ; delete the file
        int     dos
        pop     dx
abort0: mov     pack.state,'A'          ; Otherwise abort
        mov     byte ptr locfil,0       ; clear local filename
        or      errlev,2                ; set DOS error level
        or      fsta.xstatus,2          ; set status
        mov     kstatus,2               ; global status
        xor     ax,ax           ; tell statistics this is a receive operation
        call    endtim                  ; stop file timer
        ret
ABORT   ENDP

ackpak  proc    near                    ; send an ACK packet
        mov     ah,'Y'                  ; ack packet
        call    spack
         jmp    abort                   ; failed
         nop
        ret
ackpak  endp

; init variables for read...
rrinit  proc    near
        mov     pack.numpkt,0           ; Set the number of packets to zero
        mov     pack.numrtr,0           ; Set the number of retries to zero
        mov     pack.pktnum,0           ; Set the packet number to zero
        mov     pack.numtry,0           ; Set the number of tries to zero
        mov     filopn,0                ; say no file opened yet
        ret
rrinit  endp

;       RECEIVE command  --  Some code moved to the GET routine

READ    PROC    NEAR
        mov     flags.nmoflg,0          ; Override file name from other host
        mov     bx,offset filhlp2       ; Text of help message
        mov     dx,offset locfil        ; local file name string
        mov     byte ptr locfil,0       ; clear it first
        mov     ah,cmfile               ; allow path names
        call    comnd
         ret
         nop
         nop
        cmp     ah,0                    ; was an override filename given?
        je      read0                   ; e = no
        mov     flags.nmoflg,1          ; yes, set flag = use this filename
read0:  mov     ah,cmcfm                ; Get a confirm
        call    comnd
         ret
         nop
         nop
        mov     pack.state,'R'  ; Set the state to receive initiate
        mov     flags.getflg,0          ; Reset flag (not a Get command)
        mov     flags.xflg,0
        call    serini                  ; initialize serial port
        jnc     read0b                  ; 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     read0a                  ; nz = yes. Don't write to screen
        mov     ah,prstr
        mov     dx,offset infms4        ; Failed message
        int     dos
read0a: ret                             ; return failure
read0b: call    rrinit                  ; init variables for read
        call    init                    ; setup display form
        call    ihostr                  ; initialize the host

READ12:                                 ; Called by GET & SRVSND, display ok
        mov     kstatus,0               ; global status, success
        call    begtim                  ; start next statistics group
        mov     flags.cxzflg,0          ; Reset ^X/^Z flag
        mov     ah,trans.chklen         ; get desired checksum length
        mov     curchk,ah               ; and remember it here
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     read2                   ; nz = yes, no printing
        cmp     flags.destflg,2         ; Receiving to the screen?
        je      read21                  ; e = yes, no formatted display
        call    stpos
        mov     ah,prstr                ; Be informative
        mov     dx,offset infms1
        int     dos
        test    flags.remflg,dserial    ; serial display mode?
        jnz     read2                   ; nz = yes, skip initial retry display
        call    rtpos                   ; Position cursor
        mov     ax,pack.numrtr
        call    nout                    ; Write the number of retries

READ2:                          ; Called by GENERIC server command dispatcher
        cmp     flags.xflg,1            ; Are we receiving to the screen?
        je      read21                  ; e = skip the screen stuff
        call    nppos            ; Position cursor for number of packets msg
        mov     ax,pack.numpkt
        call    nout                    ; Write the number of packets
read21: mov     ah,pack.state           ; Get the state
        cmp     ah,'D'                  ; Data receive state?
        jne     read3
        call    rdata                   ; yes, get data packets
        jmp     read2
read3:  cmp     ah,'F'                  ; File receive state?
        jne     read4
        call    rfile                   ; Call receive file
        jmp     read2
read4:  cmp     ah,'R'                  ; Receive initiate state?
        jne     read5                   ; ne = no
        call    rinit
        jmp     read2
                                        ; Receive Complete state processor
read5:  push    ax                      ; save status in ah
        cmp     flags.cxzflg,0          ; Completed or interrupted?
        je      read5a                  ; e = ended normally
        or      errlev,2                ; set DOS error level
        or      fsta.xstatus,2+80h      ; set status, failed + intervention
        or      kstatus,2+80h           ; global status
read5a: push    ax
        xor     ax,ax           ; tell statistics this is a receive operation
        call    endtim                  ; stop file timer
        pop     ax
        mov     ah,curchk               ; get working checksum
        mov     trans.chklen,ah         ; and restore for next file
        mov     byte ptr locfil,0       ; clear local filename
        pop     ax                      ; recover status in ah
        mov     dx,offset infms3        ; Completed message
        cmp     ah,'C'                  ; Receive complete state?
        je      read6                   ; e = yes, else receive failed
        or      errlev,2                ; set DOS error level
        or      fsta.xstatus,2          ; set status, failed
        or      kstatus,2               ; global status
        mov     dx,offset infms4        ; Failed message
        cmp     filopn,2                ; file still open?
        jne     read6                   ; ne = no
        push    dx
        call    abort                   ; close file & maybe delete
        pop     dx
read6:  cmp     flags.xflg,0            ; Did we write to the screen?
        je      read6a                  ; e = no, so print status
        cmp     flags.destflg,2         ; Receiving to screen?
        je      read6d                  ; Yes don't reset
        mov     flags.xflg,0            ; Reset it
        jmp     read6d                  ; Yes, so just return
read6a: test    flags.remflg,dquiet     ; quiet display mode?
        jnz     read6d                  ; nz = yes, keep going
        cmp     flags.destflg,2         ; Receiving to the screen?
        je      read6d                  ; e = yes, no formatted display
        push    dx                      ; save message pointer
        call    stpos                   ; Position cursor
        pop     dx
        mov     ah,prstr
        cmp     flags.cxzflg,0          ; Completed or interrupted?
        je      read6b                  ; Ended normally
        mov     dx,offset infms6        ; Say was interrupted
read6b: int     dos
        cmp     flags.belflg,0          ; Bell desired?
        je      read6c                  ; No
        mov     dx,offset ender         ; Ring them bells
        int     dos
read6c: test    flags.remflg,dserial    ; serial display?
        jnz     read6d                  ; nz = yes
        call    clrmod                  ; clear Mode Line
        call    rprpos                  ; Put prompt here
read6d: jmp     rskp
READ    ENDP

;       Receive routines

;       Receive init
RINIT   PROC    NEAR
        mov     ah,pack.numtry          ; Get the number of tries
        cmp     ah,imxtry               ; Reached the maximum number of tries?
        jl      rinit2
        mov     dx,offset ermes7
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     rinit1                  ; nz = yes. Don't write to screen
        cmp     flags.destflg,2         ; Receiving to the screen?
        je      rinit1                  ; e = yes, no formatted display
        call    erpos                   ; Position cursor
        mov     ah,prstr
        int     dos                     ; Print an error message
rinit1: mov     bx,dx
        mov     ah,trans.chklen
        mov     curchk,ah               ; Store checksum length we want to use
        mov     trans.chklen,1          ; Send init checksum is always 1 char
        call    errpack                 ; Send error packet just in case
        mov     ah,curchk
        mov     trans.chklen,ah         ; Reset to desired value
        jmp     abort                   ; Change the state to abort
rinit2: inc     ah                      ; Increment it
        mov     pack.numtry,ah          ; Save the updated number of tries
        mov     ah,flags.getflg         ; Get cmd? (holds get pkt type in ah)
        cmp     ah,0                    ; Have we already read in the packet?
        jne     rin21a                  ; ne = yes, so don't call RPACK
        mov     ah,dtrans.seol          ; restore default end-of-line char
        mov     trans.seol,ah
        mov     ah,trans.chklen
        mov     curchk,ah               ; Save checksum length we want to use
        mov     trans.chklen,1          ; Use 1 char for init packet
        call    rpack                   ; Get a packet
         jmp    rin22                   ; Trashed packet: nak, retry
        call    pktsize                 ; report packet size
        push    ax
        mov     ah,curchk
        mov     trans.chklen,ah         ; Reset to desired value
        pop     ax
        cmp     flags.cxzflg,0          ; does the user want out now?
        jne     rinit4                  ; ne = yes, quit
rin21a: cmp     ah,'S'                  ; Is it a send initiate packet?
        jne     rinit3                  ; If not see if its an error
rin21:  mov     flags.getflg,0          ; Reset flag
        mov     pack.numtry,0           ; Reset the number of tries
        mov     ax,pack.seqnum  ; Returned packet number. (Synchronize them.)
        inc     ax                      ; Increment it
        and     ax,3FH                  ; Turn off the two high order bits
        mov     pack.pktnum,ax          ; Save modulo 64 of the number
        inc     pack.numpkt             ; Increment the number of packets
        mov     ax,pack.datlen          ; Get the number of arguments received
        mov     bx,offset data          ; Get a pointer to the data
        call    spar                    ; Get data into the proper variables
        mov     bx,offset data          ; Get a pointer to our data block
        call    rpar                    ; Set up the receive parameters
        xchg    ah,al
        mov     ah,0
        mov     pack.datlen,ax          ; Store returned number of arguments
        mov     ah,trans.chklen         ; Checksum length we'll use
        mov     curchk,ah               ; Save it
        mov     trans.chklen,1          ; Use 1 char for init packet
        call    ackpak                  ; acknowledge the packet
        mov     ah,curchk               ; Checksum length we'll use
        mov     trans.chklen,ah         ; Reset to desired value
        mov     pack.state,'F'          ; Set the state to file send
        ret
rin22:  call    nak0                    ; nak the packet
        mov     ah,curchk               ; and only now change checksum from 1
        mov     trans.chklen,ah         ; Reset to desired value
        ret                             ; try again

rinit3: cmp     ah,'M'                  ; Message packet?
        jne     rinit3e                 ; ne = no
        call    dodec                   ; decode it
        call    error1                  ; display it
        ret
rinit3e: cmp    ah,'E'                  ; Is it an error packet?
        jne     rinit4                  ; ne = no
        call    error                   ; yes
rinit4: jmp     abort
RINIT   ENDP


;       Receive file

RFILE   PROC    NEAR
        mov     dl,maxtry
        cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
        jl      rfile1
        mov     dx,offset ermes8
        jmp     rcverr                  ; do error exit
rfile1: inc     pack.numtry             ; Save the updated number of tries
        call    rpack                   ; Get a packet
         jmp    nak0                    ;  Trashed packet: nak, retry
        call    pktsize                 ; report packet size
        cmp     ah,'S'                  ; Is it a send initiate packet?
        je      rfil10
        cmp     ah,'I'                  ; An Initialization packet?
        je      rfil10                  ; e = yes, don't decode it
        call    dodec                   ; Decode all other incoming packets
        jmp     rfile2                  ;  No, try next type
rfil10: mov     dl,imxtry               ; S and I packets
        cmp     pack.numtry,dl          ; Reached the maximum number of tries?
        jl      rfil12                  ; If not proceed
        mov     dx,offset ermes7
        jmp     rcverr                  ; do error exit
rfil12: mov     ax,pack.pktnum          ; Get the present packet number
        dec     ax                      ; Decrement
        and     ax,3fh                  ; do module 64
        cmp     ax,pack.seqnum  ; Is the packet's number one less than now?
        je      rfil13
        jmp     nak0                    ; No, NAK and try again
rfil13: mov     pack.numtry,0           ; Reset the number of tries
        mov     bx,offset data          ; Get a pointer to our data block
        call    rpar                    ; Set up the parameter information
        xchg    ah,al
        mov     ah,0
        mov     pack.datlen,ax          ; Save the number of arguments
        jmp     ackpak                  ; acknowledge the packet

rfile2: cmp     ah,'Z'                  ; Is it an EOF packet?
        jne     rfile3                  ;  No, try next type
        mov     dl,maxtry               ; Z packets
        cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
        jl      rfil21                  ; If not proceed
        mov     dx,offset ermes9
        jmp     rcverr                  ; do error exit
rfil21: mov     ax,pack.pktnum          ; Get the present packet number
        dec     ax                      ; Decrement
        and     ax,3fh                  ; do modulo 64
        cmp     ax,pack.seqnum  ; Is the packet's number one less than now?
        je      rfil24
        jmp     nak0                    ; No, NAK and try again
rfil24: mov     pack.numtry,0
        mov     pack.datlen,0   ; No data. (The packet number is in seqnum.)
        jmp     ackpak                  ; acknowledge the packet

rfile3: cmp     ah,'F'                  ; Start of file (F or X packet)?
        je      rfil31                  ; e = yes
        cmp     ah,'X'                  ; Text header packet?
        jne     rfile4                  ; Neither one.
        mov     flags.xflg,1            ; 'X', say receiving to the screen
rfil31: mov     ax,pack.seqnum          ; Get the packet number
        cmp     ax,pack.pktnum          ; Is it the right packet number?
        je      rfil32
        jmp     nak1                    ; No, NAK it and try again
rfil32: inc     ax                      ; Increment the packet number
        and     ax,3FH                  ; Turn off the two high order bits
        mov     pack.pktnum,ax          ; Save modulo 64 of the number
        inc     pack.numpkt             ; Increment the number of packets
        mov     filopn,0                ; assume not writing to a disk file
        call    dodec                   ; Decode incoming packet for filename
        call    gofil                   ; Get a file to write to
         jmp    abort
        mov     chrcnt,maxpack          ; reset output buffer to be empty
        cmp     flags.xflg,0            ; writing to a disk file?
        jne     rfil32a                 ; ne = no
        mov     filopn,2                ; Disk file open for writing
rfil32a:
        test    flags.remflg,dserial    ; serial display mode?
        jz      rfil33                  ; z = no
        mov     ah,prstr
        mov     dx,offset crlf          ; display cr/lf
        int     dos
rfil33:
        mov     pack.state,'D'          ; Set the state to data receive
        mov     pack.numtry,0           ; Reset the number of tries
        mov     pack.datlen,0   ; No data.  (The packet number is in seqnum.)
        jmp     ackpak                  ; acknowledge the packet

rfile4: cmp     ah,'B'                  ; End of transmission?
        jne     rfile5                  ; ne = no
        mov     ax,pack.pktnum
        cmp     ax,pack.seqnum          ; Do we match?
        je      rfil41
        jmp     nak1                    ; No, NAK it and try again
rfil41: mov     pack.state,'C'          ; Set the state to complete
        mov     pack.datlen,0   ; No data.  (Packet number already in seqnum)
        jmp     ackpak                  ; acknowledge the packet

rfile5: cmp     ah,'M'                  ; Message packet?
        jne     rfile5e                 ; ne = no
        call    dodec                   ; decode it
        jmp     error1                  ; display it and return

rfile5e:cmp     ah,'E'                  ; Is it an error packet?
        jne     rfile6                  ; ne = no
        call    error
rfile6: jmp     abort
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 temp.
GETATT  PROC    NEAR
        mov     bx,offset data          ; pointer
getat0: push    bx
        sub     bx,offset data
        cmp     bx,pack.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
        call    getas                   ; get file size
        call    spchk                   ; check available disk space
        jnc     getat0
        ret                             ; return failure

getat2: cmp     byte ptr [bx],'!'       ; kilobyte length field?
        jne     getat3                  ; ne = no
        mov     al,[bx]                 ; remember attribute
        mov     attrib,al
        inc     bx                      ; pointer
        call    getak                   ; get file size
        jc      getat5;;;2a                     ; carry means decode rejected
        call    spchk                   ; check available disk space
        jnc     short getat0
getat2a:ret                             ; return failure

getat3: cmp     byte ptr [bx],'#'       ; date field?
        jne     getat4                  ; ne = no
        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
        jne     getat5                  ; ne = no, ignore field
        stc                             ; set carry for failure
        ret

getat5: inc     bx                      ; look at length field
        mov     al,[bx]
        sub     al,' '                  ; remove ascii bias
        mov     ah,0
        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     ofilsz+2,ax             ; low order word
        mov     ofilsz,dx               ; high order word
        ret
                                        ; Decode Kilobyte attribute
getak:  mov     ax,ofilsz+2             ; current filesize, low word
        add     ax,ofilsz
        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,ofilsz+2             ; get low word of size
        mov     dx,ofilsz               ; 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     ofilsz,dx               ; store high word
        mov     ofilsz+2,ax             ; store low word
        clc                             ; clear carry
        ret

getatd:                                 ; File date and time
        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
        mov     ax,0                    ; recover file date and time
        mov     dh,10                   ; multiplier
        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
getad4: 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

RDATA   PROC    NEAR
        mov     dl,maxtry
        cmp     pack.numtry,dl          ; Get the number of tries
        jl      rdata1
        mov     dx,offset erms10
        jmp     rcverr                  ; do error exit
rdata1: inc     pack.numtry             ; Save the updated number of tries
        call    rpack                   ; Get a packet
         jmp    nak0                    ;  Trashed packet: nak, retry
         nop
        call    pktsize                 ; report packet size
        cmp     ah,'D'                  ; Is it a data packet?
        je      rdat11                  ; e = yes
        cmp     ah,'A'                  ; Attributes packet?
        jne     rdata7                  ; ne = no
        mov     ax,pack.pktnum
        cmp     ax,pack.seqnum          ; Do we match?
        je      rdata6
        jmp     nak1                    ; No, NAK it and try again
rdata6: call    getatt                  ; get file attributes from packet
        mov     pack.numtry,0           ; Reset number of tries
        jnc     rdata6a                 ; nc = success
        mov     pack.datlen,2   ; 2 bytes (Packet number already in seqnum)
        mov     data,'N'                ; Decline the transfer
        mov     al,attrib               ; get attribute causing rejection
        mov     data+1,al               ; report rejection reason to sender
        or      fsta.xstatus,2          ; set status, failed
        mov     kstatus,2               ; global status
        jmp     short rdata6b
rdata6a:mov     pack.datlen,0   ; No data. (Packet number already in seqnum)
rdata6b:mov     ax,pack.pktnum
        inc     ax                      ; Increment the packet number
        and     ax,3FH                  ; Turn off the two high order bits
        mov     pack.pktnum,ax          ; Save modulo 64 of the number
        inc     pack.numpkt             ; Increment the number of packets
        jmp     ackpak                  ; acknowledge the packet

rdata7: call    dodec                   ; Decode data
        jmp     rdata2                  ; try next type
                                        ; D packets
rdat11: mov     ax,pack.pktnum          ; Get the present packet number
        cmp     ax,pack.seqnum          ; Is the packet's number correct?
        jz      rdat14
        mov     dl,maxtry
        cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
        jl      rdat12                  ; If not proceed
        mov     dx,offset erms10
        jmp     rcverr                  ; do error exit
rdat12: mov     ax,pack.pktnum
        dec     ax
        and     ax,3fh                  ; do modulo 64
        cmp     ax,pack.seqnum  ; Is the packet's number one less than now?
        je      rdat13
        jmp     nak0                    ; No, NAK it and try again
rdat13: mov     pack.numtry,0           ; Reset number of tries
        mov     pack.datlen,0   ; No data.  (The packet number is in seqnum.)
        jmp     ackpak                  ; acknowledge the packet

rdat14: inc     pack.pktnum             ; Increment the packet number
        and     pack.pktnum,3fh         ; Save modulo 64 of the number
        inc     pack.numpkt             ; Increment the number of packets
        mov     ax,pack.datlen          ; Get the length of the data
        cmp     flags.cxzflg,0          ; Has the user typed a ^X or ^Z?
        je      rdt14x                  ; No, write out the data
        or      fsta.xstatus,2+80h      ; set status, failed + intervention
        mov     kstatus,2+80h           ; global status
        cmp     flags.abfflg,1          ; Discard incomplete files?
        je      rdat15          ; If yes don't write data out to file
rdt14x: call    ptchr                   ; decode the data and output to file
         jmp    abort                   ;  Unable to write out chars; abort
rdat15: mov     pack.numtry,0           ; Reset the number of tries
        mov     pack.datlen,0   ; No data.  (Packet number still in seqnum.)
        cmp     flags.cxzflg,0          ; Interrupt file transfer?
        je      rdat16                  ; Nope
        or      fsta.xstatus,2+80h      ; set status, failed + intervention
        mov     kstatus,2+80h           ; global status
        mov     bx,offset data          ; Send data in ACK in case remote
        mov     ah,flags.cxzflg         ;  knows about ^X/^Z
        mov     [bx],ah                 ; Put data into the packet
        mov     pack.datlen,1           ; Set data size to 1
        mov     cx,1
        call    doenc
rdat16: jmp     ackpak                  ; acknowledge the packet

rdata2: cmp     ah,'F'                  ; Start of file?
        je      rdat20                  ; e = yes
        cmp     ah,'X'                  ; Text header packet?
        jne     rdata3                  ;  No, try next type
rdat20: mov     dl,maxtry               ; F or X packet
        cmp     pack.numtry,dl          ; Reached the max number of tries?
        jl      rdat21                  ; If not proceed
        mov     dx,offset ermes8
        jmp     rcverr                  ; do error exit
rdat21: mov     ax,pack.pktnum
        dec     ax
        and     ax,3fh                  ; modulo 64
        cmp     ax,pack.seqnum  ; Is the packet's number one less than now?
        je      rdat22
        jmp     nak0                    ; No, NAK it and try again
rdat22: mov     pack.numtry,0           ; Reset number of tries
        mov     pack.datlen,0   ; No data.  (The packet number is in seqnum.)
        jmp     ackpak                  ; acknowledge the packet

rdata3: cmp     ah,'Z'                  ; Is it a EOF packet?
        je      rdat3x                  ; e = yes
        jmp     rdata4                  ; Try and see if its an error
rdat3x: mov     ax,pack.pktnum          ; Get the present packet number
        cmp     ax,pack.seqnum          ; Is the packet's number correct?
        je      rdat32
        jmp     nak0                    ; No, NAK it and try again
rdat32: inc     ax                      ; Increment the packet number
        and     ax,3FH                  ; Turn off the two high order bits
        mov     pack.pktnum,ax          ; Save modulo 64 of the number
        inc     pack.numpkt
        call    dodec                   ; Decode incoming packet
        cmp     flags.cxzflg,0          ; Do we want to discard the file?
        jne     rdt32x                  ; ne = yes
        cmp     fmtdsp,0                ; formatted screen?
        je      rdat32a                 ; e = no, no message
        mov     ax,ofilsz               ; high word of attributes file size
        or      ax,ofilsz+2             ; low word
        cmp     ax,0                    ; was file size given by other side?
        je      rdat32a                 ; e = no
        call    perpos                  ; position cursor to percent done
        mov     dx,offset donemsg       ; say 100%
        mov     ah,prstr
        int     dos
rdat32a:cmp     pack.datlen,1           ; One piece of data?
        jne     rdat33                  ; Nope - finish writing out file?
        cmp     data,'D'                ; is the data "D" for discard?
        jne     rdat33                  ; Nope - write out file
rdt32x: cmp     flags.abfflg,0          ; Keep incomplete files?
        je      rdat33                  ; Yes, go write it out
        cmp     flags.xflg,1            ; Writing to the screen?
        je      rdt32y                  ; Don't close "file"
        cmp     flags.destflg,2         ; file destination = screen?
        je      rdt32y                  ; e = yes, no file to close
        push    bx
        mov     ah,close2               ; DOS 2.0 file close
        mov     bx,diskio.handle        ; file handle
        int     dos                     ; Kill it, ignore errors
        pop     bx
        mov     filopn,0                ; File closed now
        mov     dx,offset diskio.string ; get the filename
        mov     ah,del2                 ; DOS 2.0 file delete
        int     dos
rdt32y: cmp     flags.cxzflg,'X'        ; Kill one file or all?
        je      rdt32ya                 ; e = one (^X)
        jmp     rdat36                  ; No so leave flag alone
rdt32ya:call    cxmsg                   ; Clear msg about interrupt
        or      errlev,2                ; set DOS error level
        or      fsta.xstatus,2+80h      ; set status, failed + intervention
        mov     kstatus,2+80h           ; global status
        test    flags.remflg,dquiet     ; quiet display?
        jnz     rdt32z                  ; nz = yes
        cmp     flags.destflg,2         ; Receiving to the screen?
        je      rdt32z                  ; e = yes, no formatted display
        call    intmsg
rdt32z: mov     flags.cxzflg,0          ; Reset - ^X only kills one file
        jmp     rdat36
rdat33: cmp     flags.eofcz,0           ; should we write a ^Z?
        jz      rdat35                  ; no, keep going
        cmp     flags.xflg,0            ; writing to a file?
        jne     rdat35                  ; no, skip ^Z
rdt33x: cmp     chrcnt,0                ; any space left in output buffer?
        jg      rdat34                  ; g = yes
        call    outbuf                  ; Write out buffer if no room for ^Z
         jmp    abort
rdat34: mov     cl,'Z'- 40h             ; Put in a ^Z for EOF
        push    bx
        mov     bx,bufpnt               ; Get the dma pointer
        mov     [bx],cl                 ; Add it
        pop     bx
        dec     chrcnt
rdat35: call    outbuf                  ; Output the last buffer
         jmp    abort                   ; Give up if the disk is full
        cmp     flags.xflg,1            ; Writing to the screen?
        je      rdat37                  ; Yes, don't close "file"
        cmp     flags.destflg,2         ; file destination = screen?
        je      rdat37                  ; e = yes, no file to close
        push    bx                      ; 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      rdat35b                 ; z = no attributes to set
        cmp     cx,0                    ; time set as null?
        jne     rdat35a                 ; ne = no
        inc     cl                      ; two seconds past midnight
rdat35a: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
rdat35b:mov     ah,close2               ; DOS 2.0 file close
        mov     bx,diskio.handle        ; file handle
        int     dos
        pop     bx
        mov     filopn,0                ; File closed now
rdat36: cmp     flags.destflg,0         ; Writing to printer?
        jne     rdat37                  ; ne = no, skip next part
        cmp     flags.xflg,1            ; Writing to screen?
        je      rdat37                  ; Yes, skip this part
        mov     dl,ff                   ; Send a form feed
        mov     ah,lstout               ; Write out to first printer
        int     dos
rdat37: mov     pack.numtry,0           ; Reset the number of tries
        mov     pack.datlen,0   ; No data.  (The packet number is in seqnum.)
        call    ackpak                  ; acknowledge the packet
        mov     pack.state,'F'
        mov     ax,0            ; tell statistics this was a receive operation
        call    endtim                  ; get tod & size of file transfer
        ret
rdata4: cmp     ah,'M'                  ; Message packet?
        jne     rdata4e                 ; ne = no
        call    dodec                   ; decode it
        jmp     error1                  ; display it and return

rdata4e: cmp    ah,'E'                  ; Is it an error packet?
        jne     rdata5                  ; ne = no
        call    error
rdata5: jmp     abort
RDATA   ENDP

; Error exit. Enter with dx pointing to error message. [jrd]
rcverr  proc    near
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     rcver1                  ; nz = yes. Don't write to screen
        cmp     flags.destflg,2         ; Receiving to the screen?
        je      rcver1                  ; e = yes, no formatted display
        call    erpos                   ; Position cursor
        mov     ah,prstr
        int     dos                     ; Print an error message
rcver1: mov     bx,dx                   ; set bx to error message
        call    errpack                 ; Send error packet just in case
        jmp     abort                   ; Change the state to abort
rcverr  endp

; Called by GETATT in receiver code to verify sufficient disk space.
; Gets file path from diskio.string setup in mssfil, remote size in ofilsz
; 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
        inc     dl
        and     dl,01fh                 ; get current drive from bits 5-0
        mov     ah,36h                  ; get disk free space
        int     dos
        cmp     ax,0ffffh               ; error response?
        je      spchk6                  ; e = yes
        mul     bx                      ; sectors/cluster * clusters = sectors
        mov     bx,dx                   ; save high word of sectors (> 64K)
        mul     cx                      ; bytes = sectors * bytes/sector
        push    ax                      ; save low word of bytes
        mov     ax,bx                   ; recall sectors high word
        mov     bx,dx                   ; save current bytes high word
        mul     cx                      ; high word sectors * bytes/sector
        add     ax,bx                   ; new high bytes + old high bytes
        push    ax                      ; save high word, dx:ax
        mov     dx,ofilsz               ; high word of file size dx:ax
        mov     ax,ofilsz+2             ; 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
spchk5a: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: call    erpos                   ; Position cursor
        mov     ah,prstr
        mov     dx,offset erms11        ; Not enough space for file
        int     dos
        stc
spchk7: pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
spchk   endp


; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr

RSKP    PROC    NEAR
        pop     bp
        add     bp,3
        push    bp
        ret
RSKP    ENDP

code    ends
        end