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