        NAME    ccsfil
; Fle CCSFIL.ASM
;CHINESE
ifdef   MSDOS
        include mssfil.dat
else
        include ccsfil.dat
endif

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
        mcmsg   erword ,cerword         ; put out word Error
        int     dos
        pop     dx                      ; restore old pointer
        ret
erp0:   
;       mov     dx,screrr
        mcscr   screrr,cscrerr

        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
        mcscr   scrnrt,cscrnrt

        call    poscur
        jmp     clearl
rtp1:   mcmsg   rtword,crtword         ; 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
        mcscr   scrint,cscrint

        call    poscur
        call    clearl
int3:   mcmsg   infms7,cinfms7          ; File interrupted?
        cmp     flags.cxzflg,'X'        ; Yes.
        je      int0
        mcmsg   infms8,cinfms8          ; 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
        mcmsg   infms9, cinfms9
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
        mcscr   scrhi,cscrhi

        call    poscur
        call    clearl
biter3: mov     ah,prstr
        mcmsg   hibit,chibit
        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
        mcscr   scrint,cscrint
        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
        mcscr   scrfln,cscrfln

        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
        mcscr   scrsz,cscrsz
        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
        mcscr   scrkb,cscrkb

        test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
        jnz     kbpos1
        jmp      setup2                  ; z = no
kbpos1: ret                             ; else ignore postioning request
perpos: 
;       mov     dx,scrper               ; Percent transferred
        mcscr   scrper,cscrper

        test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
        jnz     perpos1
        jmp      setup2                  ; z = no
perpos1: ret                             ; else ignore postioning request
frpos:  
;       mov     dx,scrfr                ; Say renamed file
        mcscr   scrfr,cscrfr

        jmp     setup2
stpos:  
;       mov     dx,scrst                ; Print status of file transfer
        mcscr   scrst,cscrst

        jmp     setup2
nppos: 
;       mov     dx,scrnp                ; Number of packets sent
        mcscr   scrnp,cscrnp

        test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
        jz      setup1                  ; z = no
        ret
rprpos: 
;       mov     dx,scrrpr               ; Reprompt position
        mcscr   scrrpr,cscrrpr

        call    setup1                  ; position cursor
        mov     fmtdsp,0                ; turn off formatted display flag
        ret
nrtpos: 
;       mov     dx,scrnrt               ; Number of retries
        mcscr   scrnrt,cscrnrt

        jmp     short setup2
sppos:  
;       mov     dx,scrsp                ; Send packet location
        mcscr   scrsp,cscrsp

        jmp     short setup1
rppos:  
;       mov     dx,scrrp                ; Receive packet location
        mcscr   scrrp,cscrrp

        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
        mcmsg   fasmsg,cfasmsg          ; 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
        mcmsg   fasmsg,cfasmsg

        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
        mcmsg   fsucmsg ,cfsucmsg       ; assume success message
        cmp     fsta.xstatus,0          ; 0 = completed?
        je      endtim10                ; e = completed
        mcmsg   fbadmsg,cfbadmsg         ; failed message
        test    fsta.xstatus,80h        ; interrupted?
        jz      endtim10                ; z = no
        mcmsg   fintmsg,  cfintmsg       ; 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?
;--Pay attention to the next instructon ' je ... '(eflag), Sept.14,1990[zqf]
;       mov     cx,offset lastmsg       ; totals for last transfer
        pushf
        mcmsgc   lastmsg,clastmsg       
        popf
        je      shosta1                 ; e = yes
;-----------
        mov     bx,offset ssta          ; point to Session statistics area
;       mov     cx,offset sessmsg       ; totals for whole session
        mcmsgc   sessmsg,csessmsg       
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
        mcmsgc   pinmsg,cpinmsg        
        mov     ax,[bx].prbyte
        mov     dx,[bx].prbyte+2
        call    shoprt                  ; show result
;        mov     cx,offset poutmsg       ; port bytes sent
        mcmsgc  poutmsg,cpoutmsg

        mov     ax,[bx].psbyte
        mov     dx,[bx].psbyte+2
        call    shoprt                  ; show result
;        mov     cx,offset finmsg        ; file bytes received
        mcmsgc  finmsg,cfinmsg

        mov     ax,[bx].frbyte
        mov     dx,[bx].frbyte+2
        call    shoprt                  ; show result
;       mov     cx,offset foutmsg       ; file bytes sent
        mcmsgc  foutmsg,cfoutmsg

        mov     ax,[bx].fsbyte
        mov     dx,[bx].fsbyte+2
        call    shoprt                  ; show result
;       mov     cx,offset pkimsg        ; packets received
        mcmsgc  pkimsg,cpkimsg

        mov     ax,[bx].prpkt
        mov     dx,[bx].prpkt+2
        call    shoprt                  ; show result
;       mov     cx,offset pkomsg        ; packets sent
        mcmsgc  pkomsg,cpkomsg
 
        mov     ax,[bx].pspkt
        mov     dx,[bx].pspkt+2
        call    shoprt                  ; show result
;        mov     cx,offset nakimsg       ; naks received
        mcmsgc  nakimsg,cnakimsg

        mov     ax,[bx].nakrcnt
        xor     dx,dx
        call    shoprt                  ; show result
;        mov     cx,offset nakomsg       ; naks sent
        mcmsgc  nakomsg,cnakomsg

        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
        mcmsgc  baudmsg,cbaudmsg

        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
        mcmsg   cxzhlp,ccxzhlp
        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
        mcscr   scrser,cscrser

        call    poscur
        mov     ah,prstr
        mcmsg   infms1,cinfms1        ; say now in server mode
        int     dos
init1:  call    locate
        mov     ah,prstr                ; Put statistics headers on the screen
        mcmsg   outlin, coutlin
        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
        mcmsg   cxzser,ccxzser          ; 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
        jnc     outbu7
        jmp     outbf0                  ; c = bad buffer pointers
outbu7: 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
        mcmsg   erms11,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
        mcmsg   ermes9,cermes9          ; 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
        mcmsg   erms13,cerms13          ; Disk writing error
        cmp     flags.destflg,0         ; writing to printer?
        jne     outbf0a                 ; ne = no
;        mov     dx,offset ermes9        ; Printer not ready message
        mcmsg   ermes9,cermes9

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: mcmsg   erms10, cerms10         ; 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
        mcmsg   permsg,cpermsg
        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 "
        mcmsg   asmsg,casmsg

        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?
        jne     gofi100
        jmp     gofi16                  ; e = no, just proceed
gofi100: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
        mcmsg   infms5,cinfms5
        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:  mcmsg   ermes4, cermes4
        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
        mcmsg   erms12,cerms12
        int     dos
        mov     dx,offset diskio.string ; print offending name
        call    prtasz                  ; display filename
gofi19: 
;mov     dx,offset erms12        ; reset error message for packet
        mcmsg   erms12,cerms12
        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
