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

⟦ba1b24542⟧ TextFile

    Length: 105870 (0x19d8e)
    Types: TextFile
    Names: »mssfil.asm«

Derivation

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

TextFile

        NAME    mssfil
; File MSSFIL.ASM
; Edit history:
; Last edit 21 Nov 1988
; 21 Nov 1988 Version 2.32
; 26 Oct 1988 Modify printer output code to replace Control-Z with space if
;  SET EOF NOCTRL-Z is active, otherwise to tolerate a write error if the
;  unwritten character is a Control-Z. Done in outbuf. Similar controls are
;  in prtscr. Reason: DOS terminates i/o operation at the Control-Z for
;  destinations of CON and PRN.
; 19 August Assign system file handles 1 and 4 when receiving to screen and
;  printer in gofile, to satisfy disk size test in mssrcv. [jrd]
; 1 July 1988 Version 2.31
; 21 May 1988 Correct add bug in pktsiz, from Dan Norstedt
; 7 May 1988 Round up file transfer display values
; 29 March 1988 Add attribute Deny none (40h) to readonly file opens,
;  thanks to Paul Fox of AT&T.
; 25 Feb 1988 Speedup decoder, simplify getfil, add transaction details.
; 26 Jan 1988 Modify gofi1c to clear diskio.string after prtfn call.
;  Modify prtfn to reference diskio.string (vs buf data) to see paths.[jrd]
; 1 Jan 1988 version 2.30

        public  bufpnt, buff, chrcnt, init, ofilsz, lnout
        public  gofil, outbuf, ptchr, gtchr, gtnfil, getfil, filbuf
        public  encode, decode, nulref, nulr, decbuf, errpack, rptq
        public  origr, rptct, rptval, clrfln, cxmsg, biterr, intmsg
        public  rtpos, erpos, rppos, stpos, nppos, rprpos, nrtpos, sppos
        public  kbpos, perpos, frpos, prtasz, prtscr, prtfn, fmtdsp
        public  diskio, locfil, strlen, strcat, strcpy, fparse, pktsize
        public  shosta, begtim, endtim, fsta ; statistics procedures

        include mssdef.h

getdate equ     2ah             ; DOS get current date
rptmin  equ     3               ; At least 3 of same char in a row
                                ; equates for screen positioning
scrser  equ     0209H           ; place for server state display line
scrfln  equ     0316H           ; Place for file name
scrkb   equ     0416H           ; Place for percent transferred
scrper  equ     0516H           ; Place for Kbytes transferred
scrst   equ     0616H           ; Place for status
scrnp   equ     0816H           ; Place for number of packets
scrsz   equ     0916h           ; packet size
scrnrt  equ     0A16H           ; Place for number of retries
screrr  equ     0B16H           ; Place for error msgs.
scrhi   equ     0C16H           ; Err when 8th bit is on
scrfr   equ     0C16H           ; Rename file
scrint  equ     0C16H           ; Acknowledge interrupt
scrsp   equ     0D00H           ; Place for send packet
scrrp   equ     0F00H           ; Place for receive packet
scrrpr  equ     1700H           ; Prompt when Kermit ends (does cr/lf)

datas   segment public 'datas'
        extrn   data:byte, flags:byte, trans:byte, pack:byte, hierr:byte
        extrn   filtst:byte, tloghnd:word, dosnum:byte

outlin  db      10 dup (' ')
        verdef                                          ; version ident
        db      cr,lf,lf
        db      cr,lf,'           File name:'
        db      cr,lf,'  KBytes transferred:'
        db      cr,lf
        db      cr,lf
        db      cr,lf
        db      cr,lf,'   Number of packets:'
        db      cr,lf,'       Packet length:'
        db      cr,lf,'   Number of retries:'
        db      cr,lf,'          Last error:'
        db      cr,lf,'        Last message:'
        db      cr,lf,'$'

ermes4  db      '?Unable to make unique name$'
ermes9  db      '?Printer not ready$'
erms10  db      '?Unable to store all data$'
erms11  db      '?Disk full$'
erms12  db      '?Unable to create file $'
erms13  db      '?Error writing file$'
infms1  db      'Server mode: type Control-C to exit',cr,lf,'$'
infms5  db      'Renaming file to $'
infms7  db      'File interrupt',cr,lf,'$'
infms8  db      'File group interrupt',cr,lf,'$'
infms9  db      'User ',5eh,'  interrupt',cr,lf,'$'
hibit   db      'File contains 8-bit data',cr,lf,'$'
asmsg   db      '  as  $'
crlf    db      cr,lf,'$'
printer db      'PRN',0
                                ; DOS special chars allowed in filenames
spchar2 db      '$', 26h, 23h, 40h, 21h, 25h, 27H, '(', ')', '-', 7bh, 7dh
        db      5fh, 5eh, 7eh, 60h
spc2len equ     $-spchar2
rptval  db      0               ; Repeated character
rptct   db      1               ; Number of times it's repeated
rptq    db      drpt            ; Repeat prefix
origr   db      drpt            ; Original repeat prefix
chrcnt  dw      0               ; Number of chars in the file buffer
outpnt  dw      0               ; Position in packet
bufpnt  dw      0               ; Position in file buffer
ofilsz  dw      0               ; Double word original file size (in bytes.)
        dw      0
tfilsz  dw      0               ; Bytes transferred
        dw      0
oldper  dw      0               ; old percentage
oldkbt  dw      0               ; old KB transferred
wrpmsg  db      0               ; non-zero if we wrote percent message
fmtdsp  db      0               ; non-zero if formatted display in use
                                ; Statistics data storage area
fsta    statinfo <>             ; for last operation values
ssta    statinfo <>             ; for session values
sflag   dw      0               ; flag for send (1) or receive (0)
                                ;   80h = begtim started

lastmsg db      cr,lf,cr,lf,' Totals for the last transfer    ($'
sessmsg db      cr,lf,cr,lf,' Totals since Kermit was started ($'
pinmsg  db      ' sec)'
        db      cr,lf,'  Serial port characters received $'
poutmsg db      cr,lf,'  Serial port characters sent     $'
finmsg  db      cr,lf,'  File characters received        $'
foutmsg db      cr,lf,'  File characters sent            $'
pkimsg  db      cr,lf,'  Packets received                $'
pkomsg  db      cr,lf,'  Packets sent                    $'
nakimsg db      cr,lf,'  NAKs received                   $'
nakomsg db      cr,lf,'  NAKs sent                       $'
baudmsg db      cr,lf,'  Effective baud rate             $'
sndmsg  db      'Sent ',0
rcvmsg  db      'Recv ',0
date    db      '00:00:00 00 Jan 1980',0
datelen equ     $-date-1
atmsg   db      cr,lf,'  at '
atlen   equ     $-atmsg
fasmsg  db      ' as '
faslen  equ     $-fasmsg
fsucmsg db      ', completed, bytes: ',0
fbadmsg db      ', failed, bytes: ',0
fintmsg db      ', interrupted, bytes: ',0
months  db      'JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP'
        DB      'OCT','NOV','DEC'

tens    dd      1,10,100,1000,10000,100000,1000000,10000000,100000000
        dd      1000000000
tenslen equ     ($-tens) / 4    ; number of double words in array tens
                                ; end statistics data area
onek    dw      1024
onehun  dw      100
sixty   dw      60
ten     dw      10
denom   dw      0
permsg  db      cr,' Percent transferred:$'
cxzhlp  db      '^X cancels file, ^Z cancels batch'
        db      ', ^E quits protocol'
        db      ', ^C quits'
        db      ', Return retries'
        db      '$'
erword  db      cr,lf,'Error $'
rtword  db      cr,lf,'Retry $'
cxzser  db   cr,lf,'Type Control X to cancel file, Control Z to cancel batch,'
        db      cr,lf,' Control E to quit protocol, Control C to quit '
        db      'abruptly,',cr,lf,' or Return to retry',cr,lf,'$'
templp  db      65 dup (?)      ; temp for local path part
templf  db      14 dup (?)      ; temp for local filename part
temprp  db      65 dup (?)      ; temp for remote path part
temprf  db      14 dup (?)      ; temp for remote filename part
locfil  db      65 dup (?)      ; local filename for receive and get
rdbuf   db      65 dup (?)      ; temp work buffer
diskio  filest  <>              ; ditto, for ordinary file transfers

buff    db      buffsz dup (?)  ; Use as our Disk Transfer Area
filbuf  equ     this byte       ; make filbuf and decbuf the same address
decbuf  db      maxpack+10 dup (?) ; For decoding incoming data (+guard)
unum    dw      ?               ; unique filename generation number
havdot  db      ?               ; dot-found status in verify
prepksz dw      0               ; previous packet size
toscreen db     'Screen',0      ; for transaction logging
datas   ends

code    segment public 'code'
        extrn   spack:near, cmblnk:near, locate:near, decout:near, comnd:near
        extrn   putmod:near, poscur:near, clearl:near, isfile:near
        assume  cs:code, ds:datas

; Position cursor for an error message

ERPOS   PROC    NEAR
        test    flags.remflg,dquiet     ; quiet screen?
        jnz     erpx                    ; nz = yes
        push    dx              ; save any preexisting message pointer
        test    flags.remflg,dserial    ; serial mode display?
        jnz     erpo1                   ; nz = yes
        test    flags.remflg,dserver    ; are we a server
        jnz     erp0                    ; nz = yes, and display is regular
        cmp     flags.xflg,1            ; Packet header seen?
        jne     erp0                    ; No, do as normal
erpo1:  mov     dx,offset crlf
        mov     ah,prstr
        int     dos
        mov     dx,offset erword        ; put out word Error
        int     dos
        pop     dx                      ; restore old pointer
        ret
erp0:   mov     dx,screrr
        call    poscur
        pop     dx                      ; restore old pointer
erpx:   ret
ERPOS   ENDP

; Position cursor for number of retries message

RTPOS   PROC    NEAR
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     rtpx                    ; nz = yes
        test    flags.remflg,dserver    ; in server mode?
        jnz     rtp0                    ; nz = yes
        cmp     flags.xflg,1            ; Packet header seen?
        jne     rtp0                    ; No, do as normal
        ret
rtp0:   test    flags.remflg,dserial    ; serial mode display?
        jnz     rtp1                    ; nz = yes
        mov     dx,scrnrt
        call    poscur
        jmp     clearl
rtp1:   mov     dx,offset rtword        ; display word Retry
        mov     ah,prstr
        int     dos
rtpx:   ret
RTPOS   ENDP

; Reassure user that we acknowledge his ^X/^Z

INTMSG  PROC    NEAR
        cmp     fmtdsp,0                ; non-formatted screen?
        je      int5                    ; e = yes
        test    flags.remflg,dserver    ; server mode?
        jnz     int4                    ; nz = yes
        cmp     flags.xflg,0            ; Writing to screen?
        jne     int1                    ; Yes. Don't do anything
int4:   test    flags.remflg,dquiet     ; quiet screen?
        jnz     int1                    ; yes, supress msg
        test    flags.remflg,dserial    ; serial mode display?
        jz      int2                    ; ne = no
int5:   mov     dx,offset crlf          ; yes. output initial cr/lf
        mov     ah,prstr
        int     dos
        jmp     int3                    ; display the message
int2:   mov     dx,scrint
        call    poscur
        call    clearl
int3:   mov     dx,offset infms7        ; File interrupted?
        cmp     flags.cxzflg,'X'        ; Yes.
        je      int0
        mov     dx,offset infms8        ; File group interrupted?
        cmp     flags.cxzflg,'Z'
        je      int0
        cmp     flags.cxzflg,'C'        ; just a control-C?
        je      int1                    ; e = yes, suppress msg
        mov     dl,flags.cxzflg
        mov     infms9+6,dl             ; store interrupt code letter
        mov     dx,offset infms9
int0:   mov     ah,prstr
        int     dos
int1:   ret
INTMSG  ENDP

; Print error message that a high bit set char was found in the file

BITERR  PROC    NEAR
        test    flags.remflg,dquiet     ; remote mode?
        jnz     biter1                  ; nz = yes, no printing
        push    bx
        cmp     fmtdsp,0                ; non-formatted display?
        je      biter3                  ; e = yes
        test    flags.remflg,dserial    ; serial mode display?
        jz      biter2                  ; z = no
        mov     ah,prstr                ; display an initial cr/lf
        mov     dx,offset crlf
        int     dos
        jmp     biter3
biter2: mov     dx,scrhi
        call    poscur
        call    clearl
biter3: mov     ah,prstr
        mov     dx,offset hibit
        int     dos
        pop     bx
biter1: ret
BITERR  ENDP

;  Clear out message about interrupted file

CXMSG   PROC    NEAR
        test    flags.remflg,dserver    ; server mode?
        jnz     cxm1                    ; nz = yes
        cmp     flags.xflg,0            ; Writing to screen?
        jne     cxm0                    ; Yes. Don't do anything
cxm1:   test    flags.remflg,dquiet+dserial ; quiet or serial display?
        jnz     cxm0                    ; nz = yes
        mov     dx,scrint
        call    poscur
        call    clearl
cxm0:   ret
CXMSG   ENDP

;  Clear out the old filename on the screen.

CLRFLN  PROC    NEAR
        test    flags.remflg,dquiet     ; quiet display?
        jnz     clrflnx                 ; nz = yes
        test    flags.remflg,dserial    ; serial display mode?
        jnz     clrfln1                 ; nz = yes, use just cr/lf
        mov     dx,scrfln
        call    poscur
        call    clearl                  ; Clear to end of line
        ret
clrfln1:push    ax                      ; for serial display, does cr/lf
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        pop     ax
clrflnx:ret
CLRFLN  ENDP

PKTSIZE PROC    NEAR                    ; display packet size
        cmp     fmtdsp,0                ; formatted display?
        je      pktsiz3                 ; e = no, no display
        push    ax
        push    dx
        mov     ax,pack.datlen          ; packet size (data part)
        add     al,trans.chklen         ; plus checksum
        adc     ah,0
        add     ax,3                    ; plus LEN, SEQ, TYPE
        cmp     ax,94                   ; larger than Regular?
        jbe     pktsiz1                 ; be = no
        add     ax,3                    ; add Long Packet len and chksum
pktsiz1:cmp     ax,prepksz              ; same as previous packet?
        je      pktsiz2                 ; e = yes, skip display
        mov     prepksz,ax              ; remember new value
        push    ax
        mov     dx,scrsz                ; position cursor
        call    poscur
        call    clearl                  ; clear to end of line
        pop     ax
        call    decout                  ; show packet length
pktsiz2:pop     dx
        pop     ax
pktsiz3:ret
PKTSIZE ENDP

; some random screen positioning functions
kbpos:  mov     dx,scrkb                ; KBytes transferred
        test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
        jz      setup2                  ; z = no
        ret                             ; else ignore postioning request
perpos: mov     dx,scrper               ; Percent transferred
        test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
        jz      setup2                  ; z = no
        ret                             ; else ignore postioning request
frpos:  mov     dx,scrfr                ; Say renamed file
        jmp     setup2
stpos:  mov     dx,scrst                ; Print status of file transfer
        jmp     setup2
nppos:  mov     dx,scrnp                ; Number of packets sent
        test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
        jz      setup1                  ; z = no
        ret
rprpos: mov     dx,scrrpr               ; Reprompt position
        call    setup1                  ; position cursor
        mov     fmtdsp,0                ; turn off formatted display flag
        ret
nrtpos: mov     dx,scrnrt               ; Number of retries
        jmp     short setup2
sppos:  mov     dx,scrsp                ; Send packet location
        jmp     short setup1
rppos:  mov     dx,scrrp                ; Receive packet location
        jmp     short setup1
                                ; common service routines for positioning
setup1: test    flags.remflg,dquiet+dserial; quiet or serial display mode?
        jnz     setupa                  ; nz = yes
        cmp     fmtdsp,0                ; non-formatted display?
        je      setupa                  ; e = yes
        jmp     poscur
setup2: test    flags.remflg,dquiet+dserial; quiet or serial display mode?
        jnz     setupa                  ; nz = yes
        cmp     fmtdsp,0                ; non-formatted display?
        je      setupa                  ; e = yes
        call    poscur                  ; no
        jmp     clearl
setupa: test    flags.remflg,dquiet     ; quiet mode?
        jnz     setupx                  ; nz = yes, do nothing
        push    ax                      ; display cr/lf and return
        push    dx
        mov     dx,offset crlf
        mov     ah,prstr
        int     dos
        pop     dx
        pop     ax
setupx: ret

; Start recording of statistics for this operation. [jrd]
begtim  proc    near
        test    sflag,80h               ; is this a duplicate call?
        jz      begtim1                 ; z = no
        ret                             ; else just return
begtim1:push    ax
        push    cx
        push    dx
        xor     ax,ax           ; clear statistics counters for this file
        mov     fsta.prbyte,ax          ; bytes received from serial port
        mov     fsta.prbyte+2,ax
        mov     fsta.psbyte,ax          ; bytes sent out serial port
        mov     fsta.psbyte+2,ax
        mov     fsta.frbyte,ax          ; bytes received for this file
        mov     fsta.frbyte+2,ax
        mov     fsta.fsbyte,ax          ; bytes sent for this file
        mov     fsta.fsbyte+2,ax
        mov     fsta.prpkt,ax           ; packets received for this file
        mov     fsta.prpkt+2,ax
        mov     fsta.pspkt,ax           ; packets sent for this file
        mov     fsta.pspkt+2,ax
        mov     fsta.nakrcnt,ax         ; NAKs received for this file
        mov     fsta.nakscnt,ax         ; NAKs sent for this file
        mov     fsta.xstatus,al         ; clear status byte
        mov     ah,getdate              ; get current date, convert to ascii
        int     dos
        mov     date+9,'0'              ; init day of month
begtim2:cmp     dl,10                   ; day of month. Ten or more days?
        jl      begtim3                 ; l = no
        sub     dl,10
        inc     date+9                  ; add up tens of days
        jmp     short begtim2           ; repeat for higher order
begtim3:add     dl,'0'                  ; ascii bias
        mov     date+10,dl              ; day units
        mov     dl,dh                   ; months (1-12)
        dec     dl                      ; start at zero to index table
        mov     dh,0
        mov     di,dx                   ; months
        shl     di,1
        add     di,dx                   ; times three chars/month
        mov     al,months[di]           ; get text string for month
        mov     date+12,al
        mov     ax,word ptr months[di+1]
        mov     word ptr date+13,ax
        mov     ax,cx                   ; year since 1980
        mov     dx,0
        mov     di,offset date+16       ; destination
        call    lnout                   ; convert number to asciiz in buffer
                                        ; start time
        mov     ah,gettim               ; DOS time of day, convert to ascii
        int     dos
        mov     fsta.btime,dx           ; store ss.s   low word of seconds
        mov     fsta.btime+2,cx         ; store hhmm   high word of seconds
        mov     date,'0'                ; init begin hours field
begtim4:cmp     ch,10                   ; ten or more hours?
        jl      begtim5                 ; l = no
        sub     ch,10
        inc     date                    ; add up tens of hours
        jmp     short begtim4           ; repeat for twenties
begtim5:add     ch,'0'                  ; ascii bias
        mov     date+1,ch               ; store units of hours
        mov     date+3,'0'              ; minutes field
begtim6:cmp     cl,10                   ; ten or more minutes?
        jl      begtim7                 ; l = no
        sub     cl,10
        inc     date+3                  ; add up tens of minutes
        jmp     short begtim6           ; repeat for higher orders
begtim7:add     cl,'0'                  ; ascii bias
        mov     date+4,cl               ; store units of minutes
        mov     date+6,'0'              ; seconds field
begtim8:cmp     dh,10                   ; ten or more seconds?
        jl      begtim9                 ; l = no
        sub     dh,10
        inc     date+6                  ; add up tens of seconds
        jmp     short begtim8           ; repeat for higher orders
begtim9:add     dh,'0'                  ; ascii bias
        mov     date+7,dh
        mov     sflag,80h               ; say begtim has been run
        pop     dx
        pop     cx
        pop     ax
        ret
begtim  endp

; Take snapshot of statistics counters at end of an operation
; Enter with ax = 0 for a receive operation, ax = 1 for a send. [jrd]
endtim  proc    near
        test    sflag,80h       ; called more than once without calling begtim?
        jnz     endtim1                 ; ne = no, so do statistics snapshot
        ret                             ; yes, do nothing
endtim1:and     sflag,not (1)           ; assume receive operation
        or      ax,ax                   ; send (ax > 0), receive (ax = 0) flag
        jz      endtim2                 ; z = receive opeation
        or      sflag,1                 ; say send operation
endtim2:
        push    ax
        push    cx
        push    dx
        mov     ah,gettim               ; get DOS time of day
        int     dos
        mov     fsta.etime,dx           ; store ss. s
        mov     fsta.etime+2,cx         ; hhmm
        cmp     cx,fsta.btime+2         ; end time less than start time?
        ja      endtim2a        ; a = above (no need to test low order word)
        cmp     dx,fsta.btime           ; be. How about low order word
        jae     endtim2a                ; ae = no wrap around of time
        add     ch,24                   ; add one day to hours field
endtim2a:sub    dl,byte ptr fsta.btime ; 0.01 sec difference
        jns     endtim2b
        dec     dh                      ; borrow a second
        add     dl,100                  ; make difference positive
endtim2b:sub    dh,byte ptr fsta.btime+1; seconds difference
        jns     endtim2c
        dec     cl                      ; borrow a minute
        add     dh,60                   ; make difference positive
endtim2c:mov    bh,0
        mov     bl,dh                   ; bx has seconds difference
        sub     cl,byte ptr fsta.btime+2 ; minutes
        jns     endtim2d
        dec     ch                      ; borrow an hour
        add     cl,60
endtim2d:mov    al,cl
        mov     ah,0
        mul     sixty                   ; minutes to seconds
        add     bx,ax                   ; seconds to bx
        sub     ch,byte ptr fsta.btime+3 ; hours difference
        jns     endtim2e
        add     ch,24
endtim2e:mov    al,ch
        mov     ah,0
        mul     sixty                   ; hours to minutes in ax
        mul     sixty                   ; minutes to seconds in dx,ax
        add     ax,bx                   ; ax = seconds
        adc     dx,0                    ; dx = high word of seconds
        mov     fsta.etime,ax           ; store elapsed time, seconds, low wd
        mov     fsta.etime+2,dx         ; high word
        add     ssta.etime,ax           ; add to session time, low word
        adc     ssta.etime+2,dx         ; add to session time, high word

        mov     ax,fsta.prbyte          ; port bytes received for this file
        add     ssta.prbyte,ax          ; port bytes received for this session
        mov     ax,fsta.prbyte+2        ; high word
        adc     ssta.prbyte+2,ax
        mov     ax,fsta.psbyte  ; port bytes sent for this file, low word
        add     ssta.psbyte,ax  ; port bytes sent for this session, low word
        mov     ax,fsta.psbyte+2        ; high word
        adc     ssta.psbyte+2,ax

        test    sflag,1                 ; completing a receive operation?
        jnz     endtim3                 ; nz = no, a send operation
        mov     ax,tfilsz+2             ; file bytes received, low word
        mov     fsta.frbyte,ax
        add     ssta.frbyte,ax          ; session received file bytes, low word
        mov     ax,tfilsz               ; high word
        mov     fsta.frbyte+2,ax
        adc     ssta.frbyte+2,ax
        jmp     short endtim4

endtim3:mov     ax,tfilsz+2             ; file bytes sent, low word
        mov     fsta.fsbyte,ax          ; file bytes sent
        add     ssta.fsbyte,ax          ; session sent file bytes, low word
        mov     ax,tfilsz               ; high word
        mov     fsta.fsbyte+2,ax
        adc     ssta.fsbyte+2,ax

endtim4:mov     ax,fsta.prpkt           ; packets received for this file
        add     ssta.prpkt,ax           ; session received packets
        mov     ax,fsta.prpkt+2
        adc     ssta.prpkt+2,ax
        mov     ax,fsta.pspkt           ; packets sent for this file
        add     ssta.pspkt,ax           ; session sent packets
        mov     ax,fsta.pspkt+2
        adc     ssta.pspkt+2,ax
        mov     ax,fsta.nakrcnt         ; NAKs received for this file
        add     ssta.nakrcnt,ax         ; session received NAKs
        mov     ax,fsta.nakscnt         ; NAKs sent for this file
        add     ssta.nakscnt,ax         ; session sent NAKs
                                        ; do transaction logging
        cmp     tloghnd,0               ; logging transaction? -1 = not opened
        jg      endtim5                 ; g = logging
        jmp     endtim12                ; skip logging
endtim5:push    di                      ; kind of transaction
        push    bx                      ; save these registers
        mov     bx,tloghnd              ; handle for transaction log
        mov     dx,offset rcvmsg        ; assume receive message
        test    sflag,1                 ; 1 for send, 0 for receive
        jz      endtim6                 ; z = receive
        mov     dx,offset sndmsg        ; send message
endtim6:call    strlen                  ; length of message to cx
        mov     ah,write2
        int     dos                     ; write kind of transfer
                                        ; File names
        cmp     diskio.string,0         ; local filename
        je      endtim9                 ; e = no filename
        test    sflag,1                 ; a send operation?
        jnz     endtim8                 ; nz = yes
                                        ; Receive
        mov     dx,offset fsta.xname    ; remote name
        call    strlen                  ; length to cx
        jcxz    endtim7                 ; no name
        mov     ah,write2
        int     dos
        mov     dx,offset diskio.string ; local name
        call    strlen                  ; length to cx
        mov     si,offset fsta.xname    ; compare these two names
        mov     di,dx
        push    ds
        pop     es
        repe    cmpsb                   ; compare
        je      endtim9                 ; e = same, so no 'as' msg
        mov     dx,offset fasmsg        ; give 'as' message
        mov     cx,faslen               ; length
        mov     ah,write2
        int     dos
endtim7:mov     dx,offset diskio.string ; local name
        call    strlen                  ; get length
        mov     ah,write2               ; write local name
        int     dos
        jmp     short endtim9

endtim8:mov     dx,offset diskio.string ; Send. local name
        call    strlen
        mov     ah,write2
        int     dos
        cmp     fsta.xname,0            ; using an alias?
        je      endtim9                 ; e = no
        mov     dx,offset fasmsg        ; give 'as' message
        mov     cx,faslen
        mov     ah,write2
        int     dos
        mov     dx,offset fsta.xname    ; get alias
        call    strlen
        mov     ah,write2
        int     dos
                                        ; status of transfer
endtim9:mov     dx,offset atmsg         ; say At
        mov     cx,atlen                ; length
        mov     bx,tloghnd              ; handle
        mov     ah,write2
        int     dos
        mov     dx,offset date          ; write time and date field
        mov     cx,datelen              ; length
        mov     ah,write2
        int     dos
        mov     dx,offset fsucmsg       ; assume success message
        cmp     fsta.xstatus,0          ; 0 = completed?
        je      endtim10                ; e = completed
        mov     dx,offset fbadmsg       ; failed message
        test    fsta.xstatus,80h        ; interrupted?
        jz      endtim10                ; z = no
        mov     dx,offset fintmsg       ; interrupted message
endtim10:call   strlen                  ; get length to cx
        mov     ah,write2
        int     dos
                                        ; file bytes transferred
        mov     ax,tfilsz+2             ; file bytes, low word
        mov     dx,tfilsz               ; high word
        mov     di,offset temprp        ; work buffer
        call    lnout                   ; transform to ascii
        mov     [di],0a0dh              ; append cr/lf
        add     di,2                    ; count them
        mov     dx,offset temprp        ; start of work buffer
        mov     cx,di                   ; next free byte
        sub     cx,dx                   ; compute length
        mov     ah,write2
        int     dos
        pop     bx
        pop     di
endtim12:mov    tfilsz,0                ; clear file size area
        mov     tfilsz+2,0
        mov     sflag,0                 ; say have done ending once already
        mov     fsta.xname,0            ; clear statistics "as" name
        pop     dx
        pop     cx
        pop     ax
        ret
endtim  endp

; SHOW STATISTICS command. Displays last operation and session statistics
; 9 March 1987 [jrd]
shosta  proc    near                    ; show file transfer statistics
        mov     ah,cmcfm                ; confirm with carriage return
        call    comnd
         ret                            ; not confirmed
         nop
         nop
        call    endtim                  ; update statistics, just in case
        push    bx
        push    cx
        push    dx
        mov     bx,offset fsta          ; pointer to file (Last op) statistics
        mov     cx,2                    ; two sets to display
shosta0:push    cx                      ; save loop counter
        cmp     cx,2                    ; doing Last operation set?
        mov     cx,offset lastmsg       ; totals for last transfer
        je      shosta1                 ; e = yes
        mov     bx,offset ssta          ; point to Session statistics area
        mov     cx,offset sessmsg       ; totals for whole session
shosta1:
        mov     ax,[bx].etime           ; elapsed time of operation
        mov     dx,[bx].etime+2
        call    shoprt                  ; show result
        mov     cx,offset pinmsg        ; port bytes received
        mov     ax,[bx].prbyte
        mov     dx,[bx].prbyte+2
        call    shoprt                  ; show result
        mov     cx,offset poutmsg       ; port bytes sent
        mov     ax,[bx].psbyte
        mov     dx,[bx].psbyte+2
        call    shoprt                  ; show result
        mov     cx,offset finmsg        ; file bytes received
        mov     ax,[bx].frbyte
        mov     dx,[bx].frbyte+2
        call    shoprt                  ; show result
        mov     cx,offset foutmsg       ; file bytes sent
        mov     ax,[bx].fsbyte
        mov     dx,[bx].fsbyte+2
        call    shoprt                  ; show result
        mov     cx,offset pkimsg        ; packets received
        mov     ax,[bx].prpkt
        mov     dx,[bx].prpkt+2
        call    shoprt                  ; show result
        mov     cx,offset pkomsg        ; packets sent
        mov     ax,[bx].pspkt
        mov     dx,[bx].pspkt+2
        call    shoprt                  ; show result
        mov     cx,offset nakimsg       ; naks received
        mov     ax,[bx].nakrcnt
        xor     dx,dx
        call    shoprt                  ; show result
        mov     cx,offset nakomsg       ; naks sent
        mov     ax,[bx].nakscnt
        xor     dx,dx
        call    shoprt
                ; compute baud rate as  10 * total port bytes / elapsed time
        mov     ax,[bx].prbyte          ; port bytes received, low
        mov     dx,[bx].prbyte+2        ; port bytes received, high
        add     ax,[bx].psbyte          ; port bytes sent, low
        adc     dx,[bx].psbyte+2        ;  high. [dx,ax] = total port bytes

        mov     cx,[bx].etime           ; low word of sec in cx
        cmp     [bx].etime+2,0  ; is high word of sec zero (e.t. < 65536 sec)?
        jz      shosta3                 ; z = yes, ready for arithmetic
        push    ax                      ; else scale values, save byte count
        push    dx
        mov     ax,[bx].etime           ; elapsed time for file, low word
        mov     dx,[bx].etime+2         ;  high word
        shr     ax,1                    ; divide seconds by two, low word
        ror     dx,1                    ; get low bit of high word
        and     dx,8000                 ; pick out just that bit
        or      ax,dx           ; mask in that bit, new time in ax (dx = 0)
        mov     cx,ax                   ; save elapsed time (double-seconds)
        pop     dx                      ; get byte count again
        pop     ax
        shr     ax,1                    ; divide byte count by two also
        push    dx
        ror     dx,1                    ; rotate low bit to high position
        and     dx,8000h                ; get low bit of high word
        or      ax,dx                   ; byte count divided by two, low word
        pop     dx
        shr     dx,1                    ; and high word
shosta3:or      cx,cx                  ; is elapsed time (in cx) zero seconds?
        jnz     shosta4                 ; nz = no
        inc     cx                      ; set time to one second (no div by 0)
shosta4:div     cx                     ; bytes div seconds, ax = quo, dx = rem
        push    dx                      ; save remainder of bytes/second
        mul     ten                     ; quotient times ten to dx,ax
        pop     dx                      ; discard overflow, recover remainder
        push    ax                      ; save partial baud rate
        xchg    ax,dx                   ; remainder to ax
        xor     dx,dx                   ; clear extension
        mul     ten             ; remainder times ten too (keep only overflow)
        pop     ax                      ; recover main partial result
        add     ax,dx                   ; add two partial results
        xor     dx,dx                   ; clear extension ( < 65536 baud )
        mov     cx,offset baudmsg
        call    shoprt                  ; show result
        pop     cx                      ; recover loop counter
        dec     cx
        jcxz    shostax                 ; cx = 0 means we are done
        jmp     shosta0         ; do next set of statistics (session stuff)
shostax:
        pop     dx
        pop     cx
        pop     bx
        jmp     rskp
shosta  endp

; Print show statistics line. Enter with CX=offset of initial message,
; dx,ax with long value
shoprt  proc    near
        push    ax
        push    dx
        mov     dx,cx                   ; setput initial print
        mov     ah,prstr                ; display title line (dx is ptr)
        int     dos
        pop     dx
        pop     ax
        push    di
        mov     di,offset temprp        ; work space for output
        call    lnout                   ; show long integer
        pop     di
        mov     dx,offset temprp
        call    prtasz                  ; print asciiz string
        ret
shoprt  endp

; LNOUT - Table driven unsigned long integer (32 bit) display
; Register dx holds high order word and ax holds low order word of unsigned
; long integer to be stored in decimal. Storage area is given by DS:[DI]
; DI is incremented for each storage, null terminated
; Table TENS holds set of double word values of ten raised to powers 0 to 9
; TENSLEN holds the number of these double words
; All registers preserved.      8 March 1987 [jrd]
lnout   proc    near
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        xor     si,si           ; flag to say start printing (no leading 0's)
        mov     cx,tenslen      ; number of table entries
lnout1: push    cx              ; save loop counter
        mov     bx,cx           ; index into tens double word table
        dec     bx              ; index starts at zero
        add     bx,bx
        add     bx,bx           ; bx times four (double words to bytes)
        xor     cx,cx           ; cx is now a counter of subtractions

lnout2: cmp     dx,word ptr tens[bx+2]  ; pattern 10**(bx/4), high order part
        jb      lnout4          ; b = present number is less than pattern
        ja      lnout3          ; a = present number is larger than pattern
        cmp     ax,word ptr tens[bx] ; high words match, how about lows
        jb      lnout4          ; b = present number is smaller than pattern
lnout3: sub     ax,word ptr tens[bx]    ; subtract low order words
        sbb     dx,word ptr tens[bx+2]  ; subtract high order words, w/borrow
        inc     cl                      ; count number of subtractions
        inc     si              ; flag to indicate printing needed
        jmp     lnout2          ; try again to deduct present test pattern

lnout4: or      bx,bx           ; doing least significant digit?
        jz      lnout5          ; z = yes, always print this one
        or      si,si           ; should we print?
        jz      lnout6          ; z = no, not yet
lnout5: add     cl,'0'          ; get number of subtractions
        mov     [di],cx         ; store it (ch is still zero), asciiz
        inc     di
lnout6: pop     cx              ; recover loop counter
        loop    lnout1
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
lnout   endp

;       Initialize buffers and clear line

INIT    PROC    NEAR
        mov     hierr,0                 ; clear high-bit-seen flag
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     init3                   ; nz = yes
        test    flags.remflg,dserial    ; serial mode display?
        jnz     init2                   ; nz = yes
        call    cmblnk
        mov     dx,offset cxzhlp
        call    putmod                  ; write mode line
        mov     fmtdsp,1                ; say doing formatted display
        test    flags.remflg,dserver    ; server mode?
        jz      init1                   ; z = no
        mov     dx,scrser               ; move cursor to top of screen
        call    poscur
        mov     ah,prstr
        mov     dx,offset infms1        ; say now in server mode
        int     dos
init1:  call    locate
        mov     ah,prstr                ; Put statistics headers on the screen
        mov     dx,offset outlin
        int     dos
        mov     wrpmsg,0                ; haven't printed the messsage yet
        mov     prepksz,0               ; set previous packet size to zero
        ret
init2:  mov     ah,prstr                ; print string
        mov     dx,offset cxzser        ; status line as a text string
        int     dos
init3:  mov     wrpmsg,1                ; suppress display of percentage msg
        mov     fmtdsp,0                ; say doing unformatted display
        ret
INIT    ENDP


;       Output the chars in a packet

; Called with AX = size of the data, BX = address of source

FILEIO  PROC    NEAR

ptchr:  mov     cx,ax
        mov     ax,offset outbuf       ; routine to call when buffer gets full
        mov     chrcnt,maxpack          ; size of buffer Data
        mov     bufpnt,offset decbuf ; decoded data placed here pending output
        mov     bx,offset data          ; source of data
        jmp     short decode


; CX = Size of data, BX = Address of data, AX = Routine to call to
; dump data

decode: push    si
        push    di
        push    es
        push    dx
        push    ax
        mov     ax,ds
        mov     es,ax
        pop     ax
        mov     si,bx                   ; Source of data
        mov     bx,ax                   ; Coroutine to call
        mov     di,bufpnt               ; Destination of data
        mov     dh,0                    ; assume no quote char
        cmp     trans.ebquot,'N'        ; no quoting?
        je      decod1                  ; yes, keep going
        cmp     trans.ebquot,'Y'        ; or not doing it?
        je      decod1                  ; yes, keep going
        mov     dh,trans.ebquot         ; otherwise use quote char

decod1: mov     rptct,0                 ; Reset repeat count
        cmp     cx,0                    ; any more chars in source?
        jg      decod2                  ; g = yes
        jmp     decod6                  ; Else, we're through
decod2: cld                             ; forward direction
        lodsb                           ; Pick up a char
        dec     cx                      ; count number left
        cmp     rptq,0                  ; Doing repeat quoting?
        je      dcod21                  ; Nope, skip this part
        cmp     al,rptq                 ; Did we pick up the repeat quote char?
        jne     dcod21                  ; No, continue processing it
        lodsb                           ; Get the size
        dec     cx                      ; Modify buffer count
        sub     al,20H                  ; Was made printable
        mov     rptct,al                ; Remember how many repetitions
        lodsb                           ; Get the char to repeat
        dec     cx                      ; Modify buffer count
dcod21: mov     ah,0                    ; Assume no 8-bit quote char
        cmp     al,dh                   ; This the 8-bit quot char?
        jne     decod3                  ; ne = no
        lodsb                           ; Get the real character
        dec     cx                      ; Decrement # chars in packet
        mov     ah,80H                  ; Turn on 8-bit quot char flag
decod3: cmp     al,trans.squote         ; Is it the quote char?
        jne     decod4                  ; ne = no, proceed
        lodsb                           ; Get the quoted character
        dec     cx                      ; Decrement # of chars in packet
        or      ah,al                   ; save parity (combine with prefix)
        and     ah,80h                  ; only parity
        and     al,7FH                  ; Turn off the parity bit
        cmp     al,trans.squote         ; Is it the quote char?
        je      decod4                  ; If so just go write it out
        cmp     al,dh                   ; This the 8-bit quot char?
        je      decod4                  ; If so, just go write it out
        cmp     al,rptq                 ; Is is the repeat quote character?
        je      decod4                  ; If so, just write it out
        cmp     al,3fh                  ; char less than '?' ?
        jl      decod4                  ; l = yes; leave it intact
        cmp     al,5fh                  ; char greater than '_' ?
        jg      decod4                  ; g = yes; leave it alone
        add     al,40H                  ; Make it a control char again
        and     al,7FH                  ; Modulo 128
decod4: or      al,ah                   ; or in parity
dcod41: stosb                           ; store the character
        dec     rptct                   ; Repeat counter
        dec     chrcnt                  ; Decrement number of chars in dta
        cmp     chrcnt,0                ; space left in output buffer?
        jg      dcod42                  ; g = yes
        push    ax                      ; Save the char
        push    cx                      ; flush output buffer
        push    dx
        push    bx
        call    bx                      ; Output it if full
         jmp    decod5                  ;  Error return if disk is full
         nop
        pop     bx
        pop     dx
        pop     cx
        mov     di,bufpnt
        pop     ax                      ; recover repeated char
dcod42: cmp     rptct,0                 ; Write out char again?
        jg      dcod41                  ; g = yes
        jmp     decod1                  ; No, get next char
decod5: pop     bx
        pop     dx                      ; dx is pushed twice (really)
        pop     cx
        pop     dx
        pop     es
        pop     di
        pop     si
        ret
decod6: mov     bufpnt,di               ; store address for next output char
        push    cx
        push    dx
        push    bx
        call    bx                      ; flush output buffer before final ret
         jmp    decod5                  ;  Error return if disk is full
         nop
        pop     bx
        pop     dx
        pop     cx

        pop     dx
        pop     es
        pop     di
        pop     si
        jmp     rskp                    ; Return successfully if done

                                ; output the buffer, reset bufpnt and chrcnt

outbuf: mov     cx,maxpack              ; get full size of buffer
        sub     cx,chrcnt               ; minus space remaining = # to write
        jc      outbf0                  ; c = bad buffer pointers
        jnz     outbu6
        jmp     outb11                  ; cx = 0 means nothing to do
outbu6: cmp     flags.xflg,1            ; Writing to screen?
        jne     outbu0
        jmp     outbf2                  ; Yes, handle specially
outbu0:
        mov     cx,maxpack              ; get full size of buffer
        sub     cx,chrcnt               ; minus space remaining = # to write
        jc      outbf0                  ; c = bad buffer pointers
        jnz     outbu1
        jmp     outb11                  ; cx = 0 means nothing to do
outbu1: push    bx
        mov     dx,offset decbuf        ; address of buffer
        cmp     flags.destflg,1         ; disk destination?
        je      outbu5                  ; e = yes
        cmp     flags.eofcz,0           ; end on Control-Z?
        jne     outbu5                  ; ne = yes, let DOS do it
        push    cx                      ; else map Control-Z to space
        push    di
        push    es
        push    ds
        pop     es                      ; datas to es
        mov     di,dx                   ; scan buffer es:di, cx chars worth
        mov     al,ctlz                 ; look for Control-Z
        cld
outbu3: repne   scasb
        jne     outbu4                  ; ne = found no Control-Z's
        mov     byte ptr [di-1],' '     ; replace Control-Z with space
        jcxz    outbu4                  ; z = examined all chars
        jmp     short outbu3            ; until examined everything
outbu4: pop     es
        pop     di
        pop     cx
outbu5: mov     bx,diskio.handle        ; file handle
        mov     ah,write2               ; DOS 2.0 write
        int     dos                     ; Write the record
        pop     bx
        jc      outbf0                  ; c set means disk writing error
        cmp     ax,cx                   ; did we write all the bytes?
        je      outbf1                  ; e = yes
        call    erpos                   ; no
        mov     dx,offset erms11        ; Disk full error
        cmp     flags.destflg,1         ; writing to disk?
        je      outbu0a                 ; e = yes
        push    bx
        mov     bx,offset decbuf
        add     bx,ax                   ; look at break character
        cmp     byte ptr [bx],ctlz      ; ended on Control-Z?
        pop     bx
        je      outbf1                  ; e = yes, say no error
        mov     dx,offset ermes9        ; Printer not ready message
outbu0a:mov     ah,prstr                ; Tell about it
        int     dos
        jmp     abfil                   ; Fix things up before aborting

outbf0: call    erpos
        mov     ah,prstr                ; Tell about it
        mov     dx,offset erms13        ; Disk writing error
        cmp     flags.destflg,0         ; writing to printer?
        jne     outbf0a                 ; ne = no
        mov     dx,offset ermes9        ; Printer not ready message
outbf0a:int     dos
        jmp     abfil                   ; Fix things up before aborting

outbf1: add     tfilsz+2,cx             ; count received chars
        adc     tfilsz,0
        test    flags.remflg,dserial    ; serial mode display?
        jnz     outb11                  ; nz = yes, skip kbyte and % displays
        call    kbpr                    ; Print the kilobytes received
        call    perpr                   ; Print the percent
outb11: mov     bufpnt,offset decbuf    ; Addr for beginning
        mov     chrcnt,maxpack          ; size of empty buffer
        jmp     rskp

outbf2:                                 ; writing to screen
        mov     cx,maxpack              ; size of full buffer
        sub     cx,chrcnt               ; minus # of unused in buffer
        jle     outb11                  ; none to print, don't try
        add     tfilsz+2,cx             ; count received chars
        adc     tfilsz,0
        mov     di,offset decbuf        ; Where they are
        call    prtscr                  ; Output buffer to screen
        jmp     outb11                  ; Reset counter & pointer

;  Tidy up before aborting. Retidied by [jrd]
ABFIL   PROC    NEAR
        cmp     flags.xflg,1            ; Writing to screen?
        je      abfil1                  ; Yes don't delete "file"
        mov     bx,diskio.handle        ; get file handle
        cmp     bx,4                    ; writing to DOS set of files?
        jbe     abfil1                  ; be = yes, never close them
        mov     ah,close2               ; DOS 2.0 file close
        int     dos
        cmp     flags.abfflg,1          ; Delete what got across or keep it?
        jne     abfil1                  ; Nope, keep it
        push    dx
        mov     dx,offset diskio.string ; the full file name
        mov     ah,del2                 ; DOS 2.0 file delete
        int     dos
        pop     dx
abfil1: mov     bx,offset erms10        ; Text of message to send
        call    errpack                 ; Send an error packet
        ret
ABFIL   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    di
        mov     di,offset data          ; Where to put the message
        mov     al,0
errp1:  mov     ah,[bx]
        cmp     ah,'$'                  ; At end of message?
        je      errp2
        inc     al                      ; Remember number of chars in msg
        mov     [di],ah
        inc     bx
        inc     di
        jmp     errp1
errp2:  pop     di
        mov     ah,0
        mov     pack.datlen,ax
        mov     ah,'E'                  ; And send an error packet
        call    spack
         nop                            ; Return if succeed or fail
         nop
         nop
        ret
ERRPACK ENDP

;       Get the chars from the file

gtchr:  cmp     flags.filflg,0          ; Is there anything in the DMA?
        je      gtchr0                  ; e = yes, proceed
        mov     ah,rptq
        mov     origr,ah                ; Save repeat prefix here
        mov     rptct,1                 ; Number of times char is repeated
        mov     rptval,0                ; Value of repeated char
        call    inbuf
         jmp    gtchr1                  ; No more chars, go return EOF
         nop                            ; Make  three bytes long
gtchr0: mov     bx,offset inbuf
        jmp     encode
gtchr1: mov     ax,0ffffh
        ret

; encode - writes data portion of kermit packet into filbuf
; expects BX to contain the address of a routine to refill the buffer,
; chrcnt to be the # of chars in the buffer, trans.maxdat to contain
; the maximum size of the data packet, bufpnt to contain a pointer to
; the source of the characters
; Returns: AX/ the number of characters actually written to the buffer

encode: mov     cx,trans.maxdat         ; Maximum packet size
        push    ds
        pop     es                      ; make es:di point to datas segment
        mov     di,offset filbuf        ; Where to put the data
        mov     si,bufpnt               ; pointer into source buffer
        mov     dl,trans.rquote         ; send quote char
        mov     dh,0                    ; assume no 8-bit quoting
        cmp     trans.ebquot,'N'        ; not doing 8-bit quoting
        je      encod1
        cmp     trans.ebquot,'Y'        ; or can but won't?
        je      encod1
        mov     dh,0ffh                 ; remember we have to do it
encod1: cmp     cx,0                    ; any space left in output buffer?
        jg      encod2                  ; g = yes
        sub     di,offset filbuf
        mov     ax,di
        mov     bufpnt,si               ; update pointer into DMA
        jmp     rskp
encod2: cmp     chrcnt,0                ; Any data in buffer?
        jg      encod3                  ; yes, skip over buffer refill
        call    bx                      ; Get another buffer full
         jmp    encod8
        mov     si,bufpnt               ; update position in DMA
        cmp     chrcnt,0                ; no characters returned?
        jne     encod3                  ; Got some, keep going
        jmp     encod8                  ; none, assume eof
encod3: dec     chrcnt                  ; Decrement input count
        cld                             ; forward direction
        lodsb
        cmp     flags.eofcz,0           ; Is a control-z an end of file?
        je      encd30                  ; No, don't have to look for one
        cmp     al,'Z'-40H              ; Is this a control-Z?
        jne     encd30                  ; No, skip eof-processing
        mov     flags.eoflag,0FFH       ; Yes, set eof flag
        mov     flags.filflg,0FFH       ; No more input in buffer
        mov     chrcnt,0                ; Ditto
        jmp     encod8                  ; Go set character count and return
encd30: cmp     rptq,0                  ; Are we doing repeat prefixing?
        je      encd3x                  ; Nope, skip next part
        cmp     chrcnt,0                ; Are we on the last character?
        jle     encd31                  ; Yes, so there's no next character
        cmp     rptct,94                ; Max number that we can put in a byte
        je      encd31                  ; Then that's it
        mov     ah,[si]                 ; Get the next character
        cmp     al,ah                   ; Is current char == next char?
        jne     encd31
        inc     rptct                   ; Number of times char appears
        mov     rptval,al               ; Remember the character
        jmp     encod1                  ; Keep checking for more
encd31: cmp     rptct,1                 ; Were previous characters repeats?
        je      encd3x                  ; No, so just add this char
        cmp     rptct,rptmin    ; Are we within bounds for repeat prefixing?
        jge     encd32                  ; Yes, use repeat prefixing
        mov     al,rptct
        mov     ah,0
        sub     si,ax                   ; Not enough characters to warrant it
        mov     rptval,0                ; Clear out this value
        mov     al,rptq
        mov     origr,al                ; Save original repeat prefix
        mov     rptq,0                  ; Pretend we're not doing prefixing
        mov     al,rptct
        mov     ah,0
        add     chrcnt,ax               ; Adjust input buffer pointer
        jmp     encod1                  ; Reprocess those characters
encd32: push    ax                      ; Do repeat prefixing - save data
        mov     al,rptq                 ; Add repeat prefix char
        stosb
        dec     cx                      ; Account for it in buffer size
        mov     al,rptct                ; Get the repeat count
        add     al,20H                  ; Make it printable
        stosb                           ; Add to buffer
        dec     cx
        pop     ax                      ; Get back the actual character
        mov     rptct,1                 ; Reset repeat count
        mov     rptval,0                ; And this
encd3x: cmp     dh,0                    ; are we doing 8-bit quoting?
        je      encod4                  ; e = no, forget this
        test    al,80h                  ; parity on?
        je      encod4                  ; no, don't bother with this
        and     al,7fh                  ; turn off parity
        mov     ah,trans.ebquot         ; get quote char
        mov     [di],ah                 ; put in packet
        inc     di
        dec     cx                      ; decrement # of chars left
encod4: mov     ah,al                   ; save character
        and     ah,80h                  ; only parity
        and     al,7fh                  ; turn off parity in character
        cmp     al,' '                  ; Compare to a space
        jl      encod5                  ; If less then its a control char
        cmp     al,del                  ; Is the char a delete?
        je      encod5                  ; e = yes, go quote it
        cmp     al,dl                   ; Is it the quote char?
        je      encod6                  ; e = yes, go add it
        cmp     dh,0                    ; are we doing 8-bit quoting?
        je      encd41                  ; e = no, don't translate it
        cmp     al,trans.ebquot         ; Is it the 8-bit quote char?
        je      encod6                  ; e = yes, just output with quote
encd41: cmp     origr,0                 ; Doing repeat prefixing?
        je      encod7                  ; e = no, don't check for quote char
        cmp     al,origr                ; Is this the repeat quote character
        je      encod6                  ; e = yes, then quote it
        jmp     short encod7            ; else don't quote it

encod5: xor     al,40h                  ; control char, uncontrollify
encod6: mov     [di],dl                 ; insert control quote char
        inc     di
        dec     cx
encod7: or      al,ah                   ; put parity back
        stosb
        dec     cx                      ; Decrement output buffer counter
        cmp     rptct,1                 ; One occurence of this char?
        jne     encd7x
        mov     al,origr
        mov     rptq,al                 ; Restore repeat quote char
        jmp     encod1                  ; Yes, so loop around for some more
encd7x: dec     rptct                   ; Add another entry of this char
        jmp     encod1                  ; With quoting and all

encod8: sub     di,offset filbuf
        or      di,di                   ; buffer empty?
        je      encod9                  ; e = yes
        mov     ax,di                   ; report size encoded
        jmp     rskp                    ; return success
encod9: mov     ax,-1                   ; Get a minus one
        ret                             ; return failure


inbuf:  cmp     flags.eoflag,0          ; Have we reached the end?
        jz      inbuf0
        ret                             ; Return if set
inbuf0: push    si
        push    dx
        push    bx
        push    cx
        mov     bx,offset buff          ; Set the r/w buffer pointer
        mov     bufpnt,bx
        mov     bx,diskio.handle        ; get file handle
        mov     cx,buffsz               ; record size
        mov     dx,bufpnt               ; buffer address
        mov     ah,readf2               ; DOS 2.0 read a record
        int     dos
        jc      inbuf1                  ; c = error, ie file not open
        or      ax,ax                   ; any bytes read?
        jne     inbuf2                  ; ne = yes (the number read)
inbuf1: mov     flags.eoflag,0FFH       ; Set End-of-file
        mov     flags.filflg,0ffh       ; Buffer empty
        mov     chrcnt,0                ; zero bytes left in buffer
        pop     cx
        pop     bx
        pop     dx
        pop     si
        ret
inbuf2: add     tfilsz+2,ax             ; total the # bytes transferred so far
        adc     tfilsz,0                ; it's a double word
        mov     chrcnt,ax               ; Number of chars read from file
        mov     flags.filflg,0          ; Buffer not empty
        test    flags.remflg,dserial    ; serial display mode?
        jnz     inbuf3                  ; nz = yes, skip kbyte and % display
        push    ax
        call    kbpr                    ; Print the kilobytes sent
        call    perpr                   ; Print the percent sent
        pop     ax
inbuf3: pop     cx
        pop     bx
        pop     dx
        pop     si
        jmp     rskp

nulref: mov     chrcnt,0                ; No data to return
        jmp     rskp

nulr:   ret                             ; dummy buffer emptier

; Print the number of Kilobytes transferred

kbpr:   test    flags.remflg,dquiet     ; quiet display mode?
        jnz     kbpr1                   ; nz = yes, no printing
        push    bx
        mov     ax,tfilsz+2             ; low order word
        mov     bx,tfilsz               ; high order word
        add     ax,512                  ; round up, add half the denominator
        adc     bx,0
        mov     al,ah                   ; divide double word by 1024, in steps
        mov     ah,bl
        shr     ax,1
        shr     ax,1
        ror     bh,1
        ror     bh,1
        and     bh,not (3fh)
        or      ah,bh                   ; ax has the result
        pop     bx
        cmp     ax,oldkbt               ; is it the same?
        je      kbpr1                   ; yes, skip printing
        mov     oldkbt,ax               ; save new # of kb
        push    ax
        call    kbpos                   ; Postion the cursor
        pop     ax
        call    decout                  ; Print number of KBytes transferred
kbpr1:  ret


; Print the percent transferred

perpr:  test    flags.remflg,dquiet     ; quiet display mode?
        jz      perpr1                  ; z = no. allow printing
        ret                             ; skip printing in remote mode
perpr1: cmp     ofilsz,0                ; high word of original file size > 0 ?
        jne     perpr3                  ; ne = yes, use big file code
        cmp     ofilsz+2,0              ; anything here at all?
        jne     perpr2                  ; ne = yes, use small file code
        ret                             ; otherwise, quit now
perpr2: push    dx                      ; case for files < 64 Kb
        mov     ax,ofilsz+2             ; original size (low word)
        mov     denom,ax
        mov     dx,tfilsz               ;transferred size times 256 in [dx,ax]
        mov     ax,tfilsz+2
        mov     dh,dl                   ; whole value multiplied by 256
        mov     dl,ah
        mov     ah,al
        mov     al,0
        mov     cx,denom                ; round up, add half the denominator
        shr     cx,1
        add     ax,cx
        adc     dx,0
        div     denom                   ; (256*xfer)/orig. ax = quo, dx = rem
        mul     onehun                  ; multiply quotient above by 100
        mov     al,ah                   ; divide result (ax) by 256
        mov     ah,0                    ; percentage is in ax
        jmp     perpr4                  ; finish in common code
perpr3: push    dx                      ; case for file size > 64 KB
        mov     ax,ofilsz+2             ; original file size low order word
        shr     ax,1                    ; divide by 2
        mov     al,ah                   ; divide again by 256 for total of 512
        mov     ah,0                    ; clear ah
        mov     dx,ofilsz               ; high order word
        xchg    dh,dl                   ; do shl dx,cl=7
        ror     dx,1                    ; old low bit of dh to high bit of dh
        and     dl,80h                  ; clear lower bits. divided by two
        or      ax,dx                   ; paste together the two parts into ax
        mov     denom,ax                ; denom = original size divided by 512
        mov     dx,tfilsz               ; high order word of transferred size
        mov     ax,tfilsz+2             ; low order word
        mov     cx,denom                ; round up, add half the denominator
        shr     cx,1
        add     ax,cx
        adc     dx,0
        div     denom                   ; xfer/(512*orig). ax=quot, dx=rem
        mul     onehun                  ; times 100 for 512*percentage, in ax
        mov     al,ah                   ; divide ax by 512
        mov     ah,0
        shr     ax,1                    ; final percentage, in ax
perpr4: pop     dx
        cmp     ax,oldper               ; same as it was before?
        je      perpr7                  ; yes, don't bother printing
        mov     oldper,ax               ; remember this for next time
        cmp     wrpmsg,0                ; did we write the percentage message?
        jne     perpr5                  ; ne = yes, skip this part
        push    ax
        call    perpos                  ; position cursor
        mov     dx,offset permsg
        mov     ah,prstr
        int     dos                     ; write out message
        pop     ax
        mov     wrpmsg,1                ; init flag so we don't do it again
perpr5: push    ax
        call    perpos                  ; Position the cursor
        pop     ax
        cmp     ax,onehun               ; > 100% ?
        jle     perpr6                  ; no, accept it
        mov     ax,onehun               ; else just use 100
perpr6: call    decout
        mov     dl,25h                  ; Load a percent sign
        mov     ah,conout               ; Print the character
        int     dos
perpr7: ret
                                ; GETFIL, called only by send code
getfil: mov     flags.filflg,0ffh       ; Say nothing is in the buffer
        mov     flags.eoflag,0          ; Not the end of file
        mov     dx,offset diskio.dta    ; data transfer address
        mov     ah,setdma               ; set disk transfer address
        int     dos                     ; do it
        mov     cx,0                    ; attributes: find only normal files
        mov     dx,offset diskio.string ; filename string (may have wild cards)
        mov     ah,first2               ; DOS 2.0 search for first
        int     dos                     ; get file's characteristics
        pushf                           ; save c flag
        mov     ah,setdma               ; reset dta address
        mov     dx,offset buff          ; restore dta
        int     dos
        popf                            ; restore status of search for first
        jnc     getfi1                  ; nc = ok so far
        ret                             ; else take error exit
getfi1:
        mov     dx,offset diskio.string ; original file spec (may be wild)
        mov     di,offset templp        ; place for path part
        mov     si,offset templf        ; place for filename part
        call    fparse                  ; split them
        mov     si,offset diskio.fname  ; current filename from DOS
        call    strcat                  ; local path + diskio.fname
        mov     si,di                   ; make it a source
        mov     di,offset diskio.string ; new destination
        call    strcpy                ; new string = old path + DOS's filename
        mov     ah,open2                ; DOS 2.0 file open
        mov     al,0                    ; open readonly
        cmp     dosnum,2                ; above DOS 2?
        jna     getfi1a                 ; na = no, so no shared access
        mov     al,0+40h                ; open readonly, deny none
getfi1a:mov     dx,offset diskio.string ; filename string
        int     dos
        jnc     getfi2                  ; nc = opened the file
        ret                             ; else take error return
getfi2: mov     diskio.handle,ax        ; save file handle
        mov     ax,diskio.sizehi        ; get file size (high order word)
        mov     ofilsz,ax               ; new form
        mov     ax,diskio.sizelo        ; low order word
        mov     ofilsz+2,ax             ; new form
        mov     ax,0
        mov     tfilsz,ax               ; Set bytes sent to zero
        mov     tfilsz+2,ax
        mov     ax,-1                   ; get a minus one
        mov     oldkbt,ax
        mov     oldper,ax
        cmp     ofilsz,0                ; Null file?
        jne     getfl0                  ; Nope
        cmp     ofilsz+2,0              ; Null file?
        jne     getfl0                  ; Nope
        mov     flags.eoflag,0FFH       ; yes. Set EOF
getfl0: jmp     rskp

; GTNFIL called by send code to get next file. Rewritten by [jrd]

gtnfil: cmp     flags.cxzflg,'Z'        ; Did we have a ^Z?
        jne     gtn1                    ; ne = no, else done sending files
        ret                             ; take failure exit

gtn1:   mov     flags.filflg,0ffh       ; Nothing in the DMA
        mov     flags.eoflag,0          ; Not the end of file
        mov     dx,offset diskio.dta    ; point at dta
        mov     ah,setdma               ; set the dta address
        int     dos
        mov     ah,next2                ; DOS 2.0 search for next
        int     dos
        pushf                           ; save carry flag
        mov     ah,setdma               ; restore dta
        mov     dx,offset buff
        int     dos
        popf                            ; recover carry flag
        jc      gtn4                    ; carry set means no more files found
        call    endtim                  ; get tod of end of file transfer
        mov     di,offset templp        ; place for path part
        mov     si,offset templf        ; place for filename part
        mov     dx,offset diskio.string ; current full filename
        call    fparse                  ; split them
        mov     si,offset diskio.fname  ; new filename part from DOS
        call    strcat                  ; rejoin path and new filename
        mov     si,di                   ; new source
        mov     di,offset diskio.string ; place for whole new name
        call    strcpy                  ; copy new string
        mov     dx,offset diskio.string ; address of new string
        mov     ah,open2                ; DOS 2.0 file open
        mov     al,0                    ; open readonly
        cmp     dosnum,2                ; above DOS 2?
        jna     gtn3                    ; na = no, so no shared access
        mov     al,0+40h                ; open readonly, deny none
gtn3:   int     dos
        jc      gtn4                    ; c = could not open the file

        mov     diskio.handle,ax        ; save file handle
        call    begtim                  ; start statistics counter
        mov     ax,diskio.sizehi        ; get file size (high order word)
        mov     ofilsz,ax               ; save as original file size
        mov     ax,diskio.sizelo        ; low order word
        mov     ofilsz+2,ax
        mov     tfilsz,0                ; Set bytes sent to zero
        mov     tfilsz+2,0
        mov     oldkbt,-1
        mov     oldper,-1
        mov     ax,1            ; tell statistics this was a send operation
        cmp     ofilsz,0                ; Null file?
        jne     gtn2                    ; Nope
        cmp     ofilsz+2,0              ; Null file?
        jne     gtn2                    ; Nope
        mov     flags.eoflag,0FFH       ; Set EOF
gtn2:   jmp     rskp                    ; set success condition
gtn4:   ret                             ; set failure condition


; Get the file name from the data portion of the F packet or from locally
; specified override filename (in locfil)
; prints the filename, handles any manipulation of the filename
; necessary, including changing the name to prevent collisions
; Called by READ (receive a file, at rfil32)

gofil:  push    si
        push    di
        mov     si,offset data          ; filename in packet
        cmp     flags.xflg,0            ; receiving to screen
        je      gofil0a                 ; e = no
        mov     diskio.handle,1         ; screen is stdout, handle 1
        cmp     data,0                  ; filename given?
        jne     gofil0a                 ; ne = yes
        mov     si,offset toscreen      ; then use this dummy name
gofil0a:mov     di,offset diskio.string ; place where prtfn prints name
        call    strcpy                  ; copy pkt filename to diskio.string
        mov     di,offset fsta.xname    ; statistics filespec save area
        call    strcpy                  ; record external name
        pop     di
        pop     si
        cmp     flags.xflg,0            ; Receiving to screen? (X versus F)
        je      gofil1                  ; e = no
        jmp     gofi20                  ; Yes. so skip this stuff
gofil0: cmp     flags.destflg,2         ; file destination = screen?
        jne     gofil1                  ; ne = no
        jmp     gofi20                  ; yes
gofil1: test    flags.remflg,dquiet     ; quiet display mode?
        jnz     gofi1c                  ; nz = yes, don't display filename
        call    prtfn                   ; display the packet's filename
gofi1c: mov     byte ptr diskio.string,0 ; clear final filename
        cmp     flags.destflg,0         ; writing to printer?
        jne     gofi1a                  ; ne = no, go on
        mov     ax,offset printer       ; this is filename now
        mov     diskio.handle,4         ; system printer is handle 4
        jmp     gofi16                  ; and do it directly
gofi1a: mov     flags.nmoflg,0          ; assume no override name
        cmp     byte ptr locfil,0       ; overriding name from other side?
        jne     gofi1e                  ; ne = yes
        jmp     gofil4                  ; e = No. get the other end's filename
gofi1e: mov     flags.nmoflg,0ffh       ; say using an override name
        mov     ax,offset locfil        ; get local override filename
        cmp     word ptr locfil+1,003ah ; colon+null?(primative drive spec A:)
        je      gofil3          ; e = yes, skip screwy DOS response (No Path)
        cmp     word ptr locfil,'..'    ; parent directory?
        jne     gofi1g                  ; ne = noo
        cmp     word ptr locfil+1,002eh ; dot dot + null?
        je      gofi1b                  ; e = yes, process as directory
gofi1g: cmp     word ptr locfil,002eh   ; dot + null (parent dir)?
        je      gofi1b                  ; e = yes, process as directory
        call    isfile                  ; does it exist?
        jnc     gofi1f                  ; nc = file exists
        test    filtst.fstat,80h        ; serious error?
        jz      gofil3                  ; z = no, just no such file
        jmp     gofi18a                 ; else quit here
gofi1f: test    byte ptr filtst.dta+21,10H      ; subdirectory name?
        jnz     gofi1b                  ; nz = yes
        cmp     byte ptr locfil+2,5ch ; could it be a root directory like b:\?
        jne     gofi1d              ; ne = no. (DOS is not helpful with roots)
        cmp     byte ptr locfil+3,0     ; and is it terminated in a null?
        je      gofi1b                  ; e = yes, so it is a root spec
gofi1d: test    byte ptr filtst.dta+21,0fh   ; r/o, hidden, system, vol label?
        jz      gofil3                  ; z = no
        jmp     gofi18a                ; yes. Complain and don't transfer file
gofi1b: mov     dx,offset locfil        ; locfil is a subdirectory name
        call    strlen                  ; get its length w/o terminator
        jcxz    gofil2                  ; zero length
        dec     cx                      ; examine last char
        push    bx                      ; save bx
        mov     bx,cx
        add     bx,dx
        cmp     byte ptr [bx],5ch       ; ends in backslash?
        je      gofil2                  ; e = yes
        cmp     byte ptr [bx],2fh       ; maybe forward slash?
        je      gofil2                  ; e = yes
        mov     byte ptr [bx + 1],5ch   ; no slash yet. use backslash
        mov     byte ptr [bx + 2],0     ; plant new terminator
gofil2: pop     bx

gofil3: mov     di,offset templp        ; local path
        mov     si,offset templf        ; local filename
        mov     dx,offset locfil        ; local string
        call    fparse                  ; split local string
        mov     di,offset temprp        ; remote path
        mov     si,offset temprf        ; remote file
        mov     dx,offset data          ; remote string
        push    bx                      ; guard against long filenames
        mov     bx,offset data
        mov     byte ptr [bx+64],0     ; force filename to be <= 64 text chars
        pop     bx
        call    fparse                  ; split remote string
        mov     si,offset templp        ; copy local path to
        mov     di,offset data          ; final filename
        call    strcpy                  ; do the copy
gofi3a: mov     si,offset templf        ; assume using local file name
        cmp     byte ptr templf,0       ; local file name given?
        jne     gofi3b                  ; ne = yes
        mov     si,offset temprf        ; else use remote file name
gofi3b: call    strcat                  ; do the append
                                ; now offset data holds the new filename
                                        ;
gofil4: mov     ax,offset data          ; assume we're writing to disk
        push    bx                      ; guard against long filenames
        mov     bx,offset data
        mov     byte ptr [bx+64],0      ; force filename to be <= 64 text char
        pop     bx
                                ; recheck legality of filename in 'data'
gofil5: mov     di,offset temprp        ; remote path
        mov     si,offset temprf        ; remote file
        mov     dx,offset data          ; remote string
        call    strlen                  ; get original size
        push    cx                      ; remember it
        call    fparse                  ; further massage filename
        push    si                      ; put pieces back together
        call    verfil                  ; verify each char in temprf string
        mov     si,di                   ; get path part first
        mov     di,dx                   ; set destination
        call    strcpy                  ; copy in path part
        pop     si                      ; recover (new) filename
        cmp     byte ptr [si],'.'       ; does filename part start with a dot?
        jne     gofil5a                 ; ne = no
        push    di                      ; save regs
        push    si
        mov     di,offset rdbuf         ; a work area
        mov     byte ptr [di],'X'       ; start name with letter X
        inc     di
        call    strcpy                  ; copy rest of filename
        mov     di,si
        mov     si,offset rdbuf      ; copy new name back to original location
        call    strcpy
        pop     si                      ; restore regs
        pop     di
gofil5a:call    strcat                  ; append it
        call    strlen                  ; see if we chopped out something
        pop     si                  ; get original length (from push cx above)
        cmp     cx,si                   ; same size?
        je      gofil9                  ; e = yes
        mov     flags.nmoflg,0ffh       ; say that we have a replacement name
                                ; filename is now in 'data', all converted
gofil9: test    flags.remflg,dquiet     ; quiet display mode?
        jnz     gofi10                  ; nz = yes, don't print it
        cmp     flags.nmoflg,0          ; using local override name?
        je      gofi10                  ; e = no
        mov     ah,prstr
        mov     dx,offset asmsg         ; print " as "
        int     dos
        mov     dx,offset data          ; plus the local filename
        call    prtasz                  ; print asciiz string
gofi10: mov     ax,offset data          ; point to name
        cmp     flags.flwflg,0          ; Is file warning on?
        je      gofi16                  ; e = no, just proceed
        call    isfile                  ; does it exist?
        mov     ax,offset data          ; reload ptr in case
        jc      gofi16                  ; carry set = no, just proceed
        mov     ah,open2                ; could it be a device name?
        mov     al,0                    ; open readonly
        cmp     dosnum,2                ; above DOS 2?
        jna     gofi10a                 ; na = no, so no shared access
        mov     al,0+40h                ; open for reading, deny none
gofi10a:mov     dx,offset data          ; the filename
        int     dos
        jc      gofi11                  ; c = cannot open so just proceed
        mov     bx,ax                   ; file handle
        mov     ah,ioctl
        mov     al,0                    ; get info
        int     dos
        mov     ah,close2               ; close it
        int     dos
        mov     ax,offset data          ; point to filename again
        test    dl,80h                  ; ISDEV bit set?
        jz      gofi11                  ; z = no, not a device
        jmp     gofi16                  ; device, use name as given
gofi11: call    unique                  ; generate unique name
        jc      gofi14                  ; could not generate a unique name
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     gofi13                  ; nz = yes, skip printing
        push    ax                      ; save unique name again
        call    frpos                   ; Position cursor.
        mov     ah,prstr           ; Inform the user we are renaming the file
        mov     dx,offset infms5
        int     dos
        pop     ax                      ; get name back into ax again
        push    ax                      ; save around these calls
        mov     dx,ax                   ; print current filename
        call    prtasz                  ; display filename
        pop     ax                      ; pointer to name, again
gofi13: jmp     gofi16                  ; and go handle file

gofi14: mov     dx,offset ermes4
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     gofi15                  ; nz = yes, no printing
        call    erpos                   ; Position cursor
        mov     ah,prstr                ; Tell the user we can't rename it
        int     dos
gofi15: mov     bx,dx                   ; Tell host can't rename
        call    errpack                 ; Send error packet before abort
        ret

gofi16: mov     si,ax                   ; pointer to (maybe new) name
        mov     di,offset diskio.string ; filename, used in open
        mov     dx,di                   ;  for isfile and open below
        call    strcpy                  ; copy name to diskio.string
        mov     ax,0
        mov     ofilsz,ax               ; original file size is unknown
        mov     ofilsz+2,ax             ; double word
        mov     tfilsz,ax               ; Set bytes received to zero
        mov     tfilsz+2,ax
        mov     ax,-1                   ; get a minus one
        mov     oldkbt,ax
        mov     oldper,ax
        mov     diskio.handle,ax        ; clear handle of previous usage
        mov     ax,dx                   ; filename for isfile
        call    isfile          ; check for read-only/system/vol-label/dir
        jc      gofi16a                 ; c = file does not exist
        test    byte ptr filtst.dta+21,1fh      ; the no-no file attributes
        jz      gofi16b                 ; z = ok
        jmp     gofi18          ; nz = shouldn't write over one of these
gofi16a:test    filtst.fstat,80h        ; access problem?
        jnz     gofi18                  ; nz = yes, quit here
        mov     diskio.handle,-1        ; clear handle of previous usage
        mov     ah,creat2               ; DOS 2.0 create file
        mov     cx,0                    ; attributes bits
        int     dos
        jc      gofi16b                 ; c = did not work, try regular open
        mov     diskio.handle,ax        ; save file handle here
        call    begtim                  ; start file loggging
        jmp     rskp
gofi16b:test    byte ptr filtst.dta+21,1bh      ; r/o, hidden, volume label?
        jnz     gofi18                  ; we won't touch these
        mov     ah,open2               ; open existing file (usually a device)
        mov     al,1+1                  ; open for writing
        int     dos
        jc      gofi18                  ; carry set means can't open
        mov     diskio.handle,ax        ; file handle
        call    begtim                  ; start file loggging
        jmp     rskp

gofi18a:mov     si,ax                   ; pointer to local override name
        mov     di,offset diskio.string ; filename, used in open
        call    strcpy                  ; copy name to diskio.string
                                        ; fall  through to gofi18
gofi18: test    flags.remflg,dquiet     ; quiet display mode?
        jnz     gofi19                  ; nz = yes, don't try printing
        call    erpos                   ; Position cursor
        mov     ah,prstr                ; tell the user
        mov     dx,offset erms12
        int     dos
        mov     dx,offset diskio.string ; print offending name
        call    prtasz                  ; display filename
gofi19: mov     dx,offset erms12        ; reset error message for packet
        mov     bx,dx
        call    errpack                 ; Send an error packet
        ret
gofi20: cmp     pack.datlen,0           ; Any data in "X" packet?
        je      gofi21                  ; Nothing to print.
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        int     dos                     ; Print another crlf
        mov     di,offset data          ; Where data is
        mov     cx,pack.datlen          ; How much data we have
        call    prtscr                  ; Print it on the screen
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos
gofi21: jmp     rskp                    ; And done

FILEIO  ENDP

; Given incoming filename in 'data'.  Verify that each char is legal
; (if not change it to an "X"), force max of three chars after a period (dot)
; Source is at ds:si (si is changed here). [jrd]

VERFIL  PROC    NEAR
        push    es                      ; verify each char in 'data'
        push    cx
        mov     ax,ds
        mov     es,ax
        mov     havdot,0                ; say no dot found in name yet
        cld
verfi1: lodsb                           ; get a byte of name from si
        and     al,7fH                  ; strip any eight bit
        cmp     al,0                    ; end of name?
        je      verfi5                  ; e = yes
        cmp     al,'.'                  ; a dot?
        jne     verfi2                  ; ne = no
        cmp     havdot,0                ; have one dot already?
        jne     verfi3                  ; ne = yes, change to X
        mov     byte ptr [si+3],0    ; forceably end filename after 3 char ext
        mov     havdot,1                ; say have a dot now
        jmp     verfi4                  ; continue
verfi2: cmp     al,3ah                  ; colon?
        je      verfi4
        cmp     al,5ch                  ; backslash path separator?
        je      verfi4
        cmp     al,2fh                  ; or forward slash?
        je      verfi4
        cmp     al,'0'
        jb      verfi3                  ; See if it's a legal char < '0'
        cmp     al,'9'
        jbe     verfi4                  ; It's between 0-9 so it's OK
        cmp     al,'A'
        jb      verfi3                  ; Check for a legal punctuation char
        cmp     al,'Z'
        jbe     verfi4                  ; It's A-Z so it's OK
        cmp     al,'a'
        jb      verfi3                  ; Check for a legal punctuation char
        cmp     al,'z'
        ja      verfi3
        and     al,5FH                  ; It's a-z, capitalize
        jmp     verfi4                  ; continue with no change

verfi3: push    di                      ; special char. Is it on the list?
        mov     di,offset spchar2       ; list of acceptable special chars
        mov     cx,spc2len
        cld
        repne   scasb                   ; Search string for input char
        pop     di
        je      verfi4                  ; e = in table, return it
        mov     al,'X'                  ; else illegal, replace with "X"
        mov     flags.nmoflg,0FFH       ; say we have a replacement filename
verfi4: mov     [si-1],al               ; update name
        jmp     verfi1                  ; loop thru rest of name
verfi5: mov     byte ptr[si-1],0        ; make sure it's null terminated
        pop     cx
        pop     es
        ret
VERFIL  ENDP

; find a unique filename...     Upgraded by [jrd]
; Enter with a pointer to a (null-terminated) filename in ax
; Return with same pointer but with a new name (or old if failure)
; Success = carry clear; failure = carry set
; The idea is to pad out the main name part (8 chars) with ascii zeros and
; then change the last chars successively to a 1, 2, etc. until
; a unique name is found. All registers are preserved
; Add patch to make empty main name fields start with letter X, not digit 0
; 23 March 1986 [jrd]
unique  proc    near
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    es
        push    ax                      ; save address of source string
        mov     dx,ds                   ; make es use ds segment
        mov     es,dx
        mov     dx,ax                   ; point at original filename string
        mov     di,offset templp        ; place for path
        mov     si,offset templf        ; place for filename
        call    fparse                  ; separate path (di) and filename (si)
        mov     dx,di                   ; point at path part
        call    strlen                  ; put length in cx
        mov     si,ax                   ; point to original string
        add     si,cx                   ; point to filename part
        mov     di,offset templf        ; destination is temporary location
        mov     cx,0                    ; a counter
        cld                             ; set direction to be forward
uniq1:  lodsb                           ; get a byte
        cmp     al,'.'                  ; have a dot?
        je      uniq2                   ; e = yes
        cmp     al,0                    ; maybe null at end?
        jne     uniq3                   ; ne = no. continue loop

uniq2:  cmp     cl,8                    ; have we copied any chars before dot?
        jge     uniq3                   ; ge = all 8
        mov     byte ptr [di],'0'       ; avoid clobbers; pad with 0's
        cmp     cl,0                    ; first char of filename?
        jne     uniq2a                  ; ne = no
        mov     byte ptr [di],'X'       ; start name with letter X, not 0
uniq2a: inc     di                      ; and count the output chars
        inc     cl                      ; and this counter too
        jmp     uniq2                   ; continue until filled 8 slots
uniq3:  inc     cl                      ; cl = # char in destination
        stosb                           ; store the char
        cmp     al,0                    ; null at end?
        jne     uniq1                   ; ne = no, continue copying

        mov     di,offset templf
        add     di,7                    ; address of last name char
        mov     byte ptr [di],'1'       ; put '1' in last name char
        mov     unum,1                  ; start with this generation digit

uniq4:  mov     di,offset rdbuf         ; build a temporary full filename
        mov     si,offset templp        ; path part
        call    strcpy                  ; copy that much
        mov     si,offset templf        ; get rebuilt filename part
        call    strcat                  ; paste that to the end
        mov     ax,offset rdbuf         ; point to full name
        call    isfile                  ; does it exist?
        jc      uniq6                   ; c = no, succeed now

        inc     unum                    ; move to next generation
        mov     di,offset templf        ; position for characters
        add     di,7                    ; point to last name char
        mov     cx,7                    ; max # of digits to play with
        mov     bx,10                   ; divisor (16 bits)
        mov     ax,unum                 ; low order part of generation #
uniq5:  mov     dx,0                    ; high order part of generation #
        div     bx                      ; compute digit (unum / 10)
        add     dl,'0'                  ; make remainder part printable
        mov     byte ptr [di],dl        ; put into right place
        cmp     ax,0                    ; any more to do? (quotient nonzero)
        jz      uniq4                   ; z = no, try this name
        dec     di                      ; else decrement char position
        loop    uniq5                   ;   and keep making a number
        stc                             ; failure: set carry, keep old name
        jmp     short uniq7             ;   and exit

uniq6:  pop     di                      ; address of original filename
        push    ax                      ; save for exit clean up
        mov     si,offset rdbuf
        call    strcpy                  ; copy new filename over old
        clc                             ; success: clear carry flag
uniq7:  pop     ax
        pop     es
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        ret
unique  endp


;       [jrd]
; strlen -- computes the length, excluding the terminator, of an asciiz
;       string. Input: dx = offset of the string
;               Output: cx = the byte count
;       Normal 'ret' return. All registers except cx are preserved
;
STRLEN  PROC    NEAR
        push    di
        push    es
        push    ax
        mov     ax,ds                   ; use proper segment address
        mov     es,ax
        mov     di,dx
        mov     cx,0ffffh               ; large byte count
        cld                             ; set direction to be forward
        mov     al,0                    ; item sought is a null
        repne   scasb                   ; search for it
        add     cx,2                    ; add for -1 and auto dec in scasb
        neg     cx                    ; convert to count, excluding terminator
        pop     ax
        pop     es
        pop     di
        ret
STRLEN  ENDP

;       [jrd]
; strcat -- concatenates asciiz string 2 to the end of asciiz string 1
;       offset of string 1 is expected to be in ds:di. input & output
;       offset of string 2 is expected to be in ds:si. input only (unchanged)
;       Preserves all registers. No error returns, returns normally via ret
;
STRCAT  PROC    NEAR
        push    di                      ; save work registers
        push    si
        push    es
        push    dx
        push    cx
        push    ax
        mov     ax,ds                   ; get data segment value
        mov     es,ax                   ; set es to ds for implied es:di usage
        mov     dx,di
        call    strlen          ; get length (w/o terminator) of dest string
        add     di,cx                   ; address of first terminator
        mov     dx,si                   ; start offset of source string
        call    strlen                  ; find its length too (in cx)
        inc     cx                      ; include its terminator in the count
        rep     movsb           ; copy source string to end of output string
        pop     ax
        pop     cx
        pop     dx
        pop     es
        pop     si
        pop     di
        ret
STRCAT  ENDP

;       [jrd]
; strcpy -- copies asciiz string pointed to by ds:si into area pointed to by
;       ds:di. Returns via ret. All registers are preserved
;
STRCPY  PROC    NEAR
        mov     byte ptr [di],0         ; clear destination string
        call    strcat                  ; let strcat do the real work
        ret
STRCPY  ENDP

;       [jrd]
; fparse -- separate the drive:path part from the filename.ext part of an
;       asciiz string. Characters separating parts are  \ or / or :
;       Inputs: asciiz input full filename string offset in ds:dx
;               asciiz path offset in ds:di
;               asciiz filename offset in ds:si
;       Outputs: the above strings in the indicated spots
;       Strategy is simple. Reverse scan input string until one of the
;       three separators is encountered and then cleave at that point
;       Simple filename construction restrictions added 30 Dec 1985;
;       to wit: mainname limited to 8 chars or less,
;       extension field limited to 3 chars or less and is found by searching
;       for first occurence of a dot in the filename field. Thus the whole
;       filename part is restricted to 12 (8+dot+3) chars plus a null
;       All registers are preserved. Return is always via ret
;       (Microsoft should have written this for DOS 2.x et seq.)

FPARSE  PROC    NEAR
        push    cx                      ; local counter
        push    ax                      ; local work area
        push    es                      ; implied segment register for di
        push    di                      ; offset of path part of output
        push    si                      ; offset of file name part of output
        mov     ax,ds                   ; get data segment value
        mov     es,ax                   ; set es to ds for implied es:di usage
        mov     byte ptr [si],0         ; clear outputs
        mov     byte ptr [di],0

        push    si                      ; save original file name address
        mov     si,dx                   ; get original string address
        call    strcpy                  ; copy string to original di
        call    strlen                  ; find length (w/o terminator), in cx
        mov     si,di                   ; address of string start
        add     si,cx
        dec     si                      ; si = address of last non-null char
        jcxz    fpars5                  ; if null skip the path scan
                                        ; now find last path char, if any
                                        ; start at the end of input string
        std                             ; set direction to be backward
fpars4: lodsb                           ; get a byte (dec's si afterward)
        cmp     al,5ch                  ; is it a backslash ('\')?
        je      fpars6                  ; e = yes
        cmp     al,2fh                  ; or forward slash ('/')?
        je      fpars6                  ; e = yes
        cmp     al,3ah                  ; or even the drive terminator colon?
        je      fpars6                  ; e = yes
        loop    fpars4                  ; else keep looking until cx == 0
                                        ; si is at beginning of file name
fpars5: dec     si                      ; dec for inc below
fpars6: inc     si
        inc     si                      ; si now points at first filename char
                                        ; cx holds number of path chars
                                        ; get original file name address (si)
        pop     di                      ; and make it place to copy filename
        cld                             ; reset direction to be forward
        mov     ax,si                   ; ax holds filename address for awhile
        push    dx
        mov     dx,si                   ; strlen wants string pointer in dx
        call    strlen                  ; get length of filename part into cx
        pop     dx
        jcxz    fpar7a                  ; any chars to look at? z = no
fpars7: cmp     byte ptr [si],'.'       ; look for a dot in filename
        je      fpars8                  ; e = found one
        inc     si                      ; look at next filename char
        loop    fpars7                  ; keep looking until cx = zero
fpar7a: mov     si,ax                   ; no dot. recover starting address
        mov     byte ptr [si+8],0       ; forcably truncate mainname to 8 char
        call    strcpy                  ; copy this part to filename field
        jmp     fparsx                  ;  and exit
fpars8: mov     byte ptr [si+4],0   ; plant terminator after dot + 3 ext chars
        mov     cx,si
        sub     cx,ax           ; cx now = number of chars in mainname field
        cmp     cx,9                    ; more than 8?
        jb      fpars9                  ; b = no, we're safe
        mov     cx,8                 ; limit ourselves to 8 chars in mainname
fpars9: push    si                   ; remember address of dot and extension
        mov     si,ax                   ; point to start of input filename
        rep     movsb                   ; copy cx chars from si to di (output)
        mov     byte ptr [di],0         ; plant terminator where dot goes
        pop     si                      ; source = dot and extension address
        call    strcat          ; append the dot & ext to the filename field
fparsx: mov     si,ax           ; recover start of filename in input string
        mov     byte ptr [si],0         ; terminate path field
        pop     si
        pop     di
        pop     es
        pop     ax
        pop     cx
        ret
FPARSE  ENDP

; Print filename in offset data. Shortened by [jrd]
PRTFN   PROC    NEAR
        push    ax                      ; saves for messy clrfln routine
        push    bx
        push    cx
        push    dx
        push    di
        call    clrfln                  ; Position cursor & blank out the line
        mov     dx,offset diskio.string
        call    strlen                 ; compute length of asciiz string in cx
        mov     di,dx                   ; where prtscr wants its string
        call    prtscr
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
PRTFN   ENDP


; Print string to screen from offset ds:di for # bytes given in cx,
; regardless of $'s.  All registers are preserved.              [jrd]

PRTSCR  PROC    NEAR
        jcxz    prtscr4                 ; cx = zero means nothing to show
        push    ax
        push    bx
        push    dx
        mov     dx,di                   ; source ptr for DOS
        cmp     flags.eofcz,0           ; end on Control-Z?
        jne     prtscr3                 ; ne = yes, let DOS do it
        push    cx                      ; else map Control-Z to space
        push    di
        push    es
        push    ds
        pop     es                      ; datas to es
        mov     al,ctlz                 ; look for Control-Z
        cld                             ; scan buffer es:di, cx chars worth
prtscr1:repne   scasb
        jne     prtscr2                 ; ne = found no Control-Z's
        mov     byte ptr [di-1],' '     ; replace Control-Z with space
        jcxz    prtscr2                 ; z = examined all chars
        jmp     short prtscr1           ; until examined everything
prtscr2:pop     es
        pop     di
        pop     cx
prtscr3:mov     bx,1                    ; stdout file handle
        mov     ah,write2
        int     dos
        pop     dx
        pop     bx
        pop     ax
prtscr4:ret
PRTSCR  ENDP

; Print to screen asciiz string given in ds:dx. Everything preserved. [jrd]
PRTASZ  PROC    NEAR
        push    cx
        push    di
        call    strlen                  ; get length of asciiz string
        mov     di,dx                   ; where prtscr looks
        call    prtscr                  ; print counted string
        pop     di
        pop     cx
        ret
PRTASZ  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