|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T m
Length: 105870 (0x19d8e) Types: TextFile Names: »mssfil.asm«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦71044c191⟧ »EurOpenD3/misc/kermit.ms-2.32.tar.Z« └─⟦31f2d420d⟧ └─⟦this⟧ »mssfil.asm«
NAME mssfil ; File MSSFIL.ASM ; Edit history: ; Last edit 21 Nov 1988 ; 21 Nov 1988 Version 2.32 ; 26 Oct 1988 Modify printer output code to replace Control-Z with space if ; SET EOF NOCTRL-Z is active, otherwise to tolerate a write error if the ; unwritten character is a Control-Z. Done in outbuf. Similar controls are ; in prtscr. Reason: DOS terminates i/o operation at the Control-Z for ; destinations of CON and PRN. ; 19 August Assign system file handles 1 and 4 when receiving to screen and ; printer in gofile, to satisfy disk size test in mssrcv. [jrd] ; 1 July 1988 Version 2.31 ; 21 May 1988 Correct add bug in pktsiz, from Dan Norstedt ; 7 May 1988 Round up file transfer display values ; 29 March 1988 Add attribute Deny none (40h) to readonly file opens, ; thanks to Paul Fox of AT&T. ; 25 Feb 1988 Speedup decoder, simplify getfil, add transaction details. ; 26 Jan 1988 Modify gofi1c to clear diskio.string after prtfn call. ; Modify prtfn to reference diskio.string (vs buf data) to see paths.[jrd] ; 1 Jan 1988 version 2.30 public bufpnt, buff, chrcnt, init, ofilsz, lnout public gofil, outbuf, ptchr, gtchr, gtnfil, getfil, filbuf public encode, decode, nulref, nulr, decbuf, errpack, rptq public origr, rptct, rptval, clrfln, cxmsg, biterr, intmsg public rtpos, erpos, rppos, stpos, nppos, rprpos, nrtpos, sppos public kbpos, perpos, frpos, prtasz, prtscr, prtfn, fmtdsp public diskio, locfil, strlen, strcat, strcpy, fparse, pktsize public shosta, begtim, endtim, fsta ; statistics procedures include mssdef.h getdate equ 2ah ; DOS get current date rptmin equ 3 ; At least 3 of same char in a row ; equates for screen positioning scrser equ 0209H ; place for server state display line scrfln equ 0316H ; Place for file name scrkb equ 0416H ; Place for percent transferred scrper equ 0516H ; Place for Kbytes transferred scrst equ 0616H ; Place for status scrnp equ 0816H ; Place for number of packets scrsz equ 0916h ; packet size scrnrt equ 0A16H ; Place for number of retries screrr equ 0B16H ; Place for error msgs. scrhi equ 0C16H ; Err when 8th bit is on scrfr equ 0C16H ; Rename file scrint equ 0C16H ; Acknowledge interrupt scrsp equ 0D00H ; Place for send packet scrrp equ 0F00H ; Place for receive packet scrrpr equ 1700H ; Prompt when Kermit ends (does cr/lf) datas segment public 'datas' extrn data:byte, flags:byte, trans:byte, pack:byte, hierr:byte extrn filtst:byte, tloghnd:word, dosnum:byte outlin db 10 dup (' ') verdef ; version ident db cr,lf,lf db cr,lf,' File name:' db cr,lf,' KBytes transferred:' db cr,lf db cr,lf db cr,lf db cr,lf,' Number of packets:' db cr,lf,' Packet length:' db cr,lf,' Number of retries:' db cr,lf,' Last error:' db cr,lf,' Last message:' db cr,lf,'$' ermes4 db '?Unable to make unique name$' ermes9 db '?Printer not ready$' erms10 db '?Unable to store all data$' erms11 db '?Disk full$' erms12 db '?Unable to create file $' erms13 db '?Error writing file$' infms1 db 'Server mode: type Control-C to exit',cr,lf,'$' infms5 db 'Renaming file to $' infms7 db 'File interrupt',cr,lf,'$' infms8 db 'File group interrupt',cr,lf,'$' infms9 db 'User ',5eh,' interrupt',cr,lf,'$' hibit db 'File contains 8-bit data',cr,lf,'$' asmsg db ' as $' crlf db cr,lf,'$' printer db 'PRN',0 ; DOS special chars allowed in filenames spchar2 db '$', 26h, 23h, 40h, 21h, 25h, 27H, '(', ')', '-', 7bh, 7dh db 5fh, 5eh, 7eh, 60h spc2len equ $-spchar2 rptval db 0 ; Repeated character rptct db 1 ; Number of times it's repeated rptq db drpt ; Repeat prefix origr db drpt ; Original repeat prefix chrcnt dw 0 ; Number of chars in the file buffer outpnt dw 0 ; Position in packet bufpnt dw 0 ; Position in file buffer ofilsz dw 0 ; Double word original file size (in bytes.) dw 0 tfilsz dw 0 ; Bytes transferred dw 0 oldper dw 0 ; old percentage oldkbt dw 0 ; old KB transferred wrpmsg db 0 ; non-zero if we wrote percent message fmtdsp db 0 ; non-zero if formatted display in use ; Statistics data storage area fsta statinfo <> ; for last operation values ssta statinfo <> ; for session values sflag dw 0 ; flag for send (1) or receive (0) ; 80h = begtim started lastmsg db cr,lf,cr,lf,' Totals for the last transfer ($' sessmsg db cr,lf,cr,lf,' Totals since Kermit was started ($' pinmsg db ' sec)' db cr,lf,' Serial port characters received $' poutmsg db cr,lf,' Serial port characters sent $' finmsg db cr,lf,' File characters received $' foutmsg db cr,lf,' File characters sent $' pkimsg db cr,lf,' Packets received $' pkomsg db cr,lf,' Packets sent $' nakimsg db cr,lf,' NAKs received $' nakomsg db cr,lf,' NAKs sent $' baudmsg db cr,lf,' Effective baud rate $' sndmsg db 'Sent ',0 rcvmsg db 'Recv ',0 date db '00:00:00 00 Jan 1980',0 datelen equ $-date-1 atmsg db cr,lf,' at ' atlen equ $-atmsg fasmsg db ' as ' faslen equ $-fasmsg fsucmsg db ', completed, bytes: ',0 fbadmsg db ', failed, bytes: ',0 fintmsg db ', interrupted, bytes: ',0 months db 'JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP' DB 'OCT','NOV','DEC' tens dd 1,10,100,1000,10000,100000,1000000,10000000,100000000 dd 1000000000 tenslen equ ($-tens) / 4 ; number of double words in array tens ; end statistics data area onek dw 1024 onehun dw 100 sixty dw 60 ten dw 10 denom dw 0 permsg db cr,' Percent transferred:$' cxzhlp db '^X cancels file, ^Z cancels batch' db ', ^E quits protocol' db ', ^C quits' db ', Return retries' db '$' erword db cr,lf,'Error $' rtword db cr,lf,'Retry $' cxzser db cr,lf,'Type Control X to cancel file, Control Z to cancel batch,' db cr,lf,' Control E to quit protocol, Control C to quit ' db 'abruptly,',cr,lf,' or Return to retry',cr,lf,'$' templp db 65 dup (?) ; temp for local path part templf db 14 dup (?) ; temp for local filename part temprp db 65 dup (?) ; temp for remote path part temprf db 14 dup (?) ; temp for remote filename part locfil db 65 dup (?) ; local filename for receive and get rdbuf db 65 dup (?) ; temp work buffer diskio filest <> ; ditto, for ordinary file transfers buff db buffsz dup (?) ; Use as our Disk Transfer Area filbuf equ this byte ; make filbuf and decbuf the same address decbuf db maxpack+10 dup (?) ; For decoding incoming data (+guard) unum dw ? ; unique filename generation number havdot db ? ; dot-found status in verify prepksz dw 0 ; previous packet size toscreen db 'Screen',0 ; for transaction logging datas ends code segment public 'code' extrn spack:near, cmblnk:near, locate:near, decout:near, comnd:near extrn putmod:near, poscur:near, clearl:near, isfile:near assume cs:code, ds:datas ; Position cursor for an error message ERPOS PROC NEAR test flags.remflg,dquiet ; quiet screen? jnz erpx ; nz = yes push dx ; save any preexisting message pointer test flags.remflg,dserial ; serial mode display? jnz erpo1 ; nz = yes test flags.remflg,dserver ; are we a server jnz erp0 ; nz = yes, and display is regular cmp flags.xflg,1 ; Packet header seen? jne erp0 ; No, do as normal erpo1: mov dx,offset crlf mov ah,prstr int dos mov dx,offset erword ; put out word Error int dos pop dx ; restore old pointer ret erp0: mov dx,screrr call poscur pop dx ; restore old pointer erpx: ret ERPOS ENDP ; Position cursor for number of retries message RTPOS PROC NEAR test flags.remflg,dquiet ; quiet display mode? jnz rtpx ; nz = yes test flags.remflg,dserver ; in server mode? jnz rtp0 ; nz = yes cmp flags.xflg,1 ; Packet header seen? jne rtp0 ; No, do as normal ret rtp0: test flags.remflg,dserial ; serial mode display? jnz rtp1 ; nz = yes mov dx,scrnrt call poscur jmp clearl rtp1: mov dx,offset rtword ; display word Retry mov ah,prstr int dos rtpx: ret RTPOS ENDP ; Reassure user that we acknowledge his ^X/^Z INTMSG PROC NEAR cmp fmtdsp,0 ; non-formatted screen? je int5 ; e = yes test flags.remflg,dserver ; server mode? jnz int4 ; nz = yes cmp flags.xflg,0 ; Writing to screen? jne int1 ; Yes. Don't do anything int4: test flags.remflg,dquiet ; quiet screen? jnz int1 ; yes, supress msg test flags.remflg,dserial ; serial mode display? jz int2 ; ne = no int5: mov dx,offset crlf ; yes. output initial cr/lf mov ah,prstr int dos jmp int3 ; display the message int2: mov dx,scrint call poscur call clearl int3: mov dx,offset infms7 ; File interrupted? cmp flags.cxzflg,'X' ; Yes. je int0 mov dx,offset infms8 ; File group interrupted? cmp flags.cxzflg,'Z' je int0 cmp flags.cxzflg,'C' ; just a control-C? je int1 ; e = yes, suppress msg mov dl,flags.cxzflg mov infms9+6,dl ; store interrupt code letter mov dx,offset infms9 int0: mov ah,prstr int dos int1: ret INTMSG ENDP ; Print error message that a high bit set char was found in the file BITERR PROC NEAR test flags.remflg,dquiet ; remote mode? jnz biter1 ; nz = yes, no printing push bx cmp fmtdsp,0 ; non-formatted display? je biter3 ; e = yes test flags.remflg,dserial ; serial mode display? jz biter2 ; z = no mov ah,prstr ; display an initial cr/lf mov dx,offset crlf int dos jmp biter3 biter2: mov dx,scrhi call poscur call clearl biter3: mov ah,prstr mov dx,offset hibit int dos pop bx biter1: ret BITERR ENDP ; Clear out message about interrupted file CXMSG PROC NEAR test flags.remflg,dserver ; server mode? jnz cxm1 ; nz = yes cmp flags.xflg,0 ; Writing to screen? jne cxm0 ; Yes. Don't do anything cxm1: test flags.remflg,dquiet+dserial ; quiet or serial display? jnz cxm0 ; nz = yes mov dx,scrint call poscur call clearl cxm0: ret CXMSG ENDP ; Clear out the old filename on the screen. CLRFLN PROC NEAR test flags.remflg,dquiet ; quiet display? jnz clrflnx ; nz = yes test flags.remflg,dserial ; serial display mode? jnz clrfln1 ; nz = yes, use just cr/lf mov dx,scrfln call poscur call clearl ; Clear to end of line ret clrfln1:push ax ; for serial display, does cr/lf mov ah,prstr mov dx,offset crlf int dos pop ax clrflnx:ret CLRFLN ENDP PKTSIZE PROC NEAR ; display packet size cmp fmtdsp,0 ; formatted display? je pktsiz3 ; e = no, no display push ax push dx mov ax,pack.datlen ; packet size (data part) add al,trans.chklen ; plus checksum adc ah,0 add ax,3 ; plus LEN, SEQ, TYPE cmp ax,94 ; larger than Regular? jbe pktsiz1 ; be = no add ax,3 ; add Long Packet len and chksum pktsiz1:cmp ax,prepksz ; same as previous packet? je pktsiz2 ; e = yes, skip display mov prepksz,ax ; remember new value push ax mov dx,scrsz ; position cursor call poscur call clearl ; clear to end of line pop ax call decout ; show packet length pktsiz2:pop dx pop ax pktsiz3:ret PKTSIZE ENDP ; some random screen positioning functions kbpos: mov dx,scrkb ; KBytes transferred test flags.remflg,dquiet+dserial ; quiet or serial display mode? jz setup2 ; z = no ret ; else ignore postioning request perpos: mov dx,scrper ; Percent transferred test flags.remflg,dquiet+dserial ; quiet or serial display mode? jz setup2 ; z = no ret ; else ignore postioning request frpos: mov dx,scrfr ; Say renamed file jmp setup2 stpos: mov dx,scrst ; Print status of file transfer jmp setup2 nppos: mov dx,scrnp ; Number of packets sent test flags.remflg,dquiet+dserial ; quiet or serial display mode? jz setup1 ; z = no ret rprpos: mov dx,scrrpr ; Reprompt position call setup1 ; position cursor mov fmtdsp,0 ; turn off formatted display flag ret nrtpos: mov dx,scrnrt ; Number of retries jmp short setup2 sppos: mov dx,scrsp ; Send packet location jmp short setup1 rppos: mov dx,scrrp ; Receive packet location jmp short setup1 ; common service routines for positioning setup1: test flags.remflg,dquiet+dserial; quiet or serial display mode? jnz setupa ; nz = yes cmp fmtdsp,0 ; non-formatted display? je setupa ; e = yes jmp poscur setup2: test flags.remflg,dquiet+dserial; quiet or serial display mode? jnz setupa ; nz = yes cmp fmtdsp,0 ; non-formatted display? je setupa ; e = yes call poscur ; no jmp clearl setupa: test flags.remflg,dquiet ; quiet mode? jnz setupx ; nz = yes, do nothing push ax ; display cr/lf and return push dx mov dx,offset crlf mov ah,prstr int dos pop dx pop ax setupx: ret ; Start recording of statistics for this operation. [jrd] begtim proc near test sflag,80h ; is this a duplicate call? jz begtim1 ; z = no ret ; else just return begtim1:push ax push cx push dx xor ax,ax ; clear statistics counters for this file mov fsta.prbyte,ax ; bytes received from serial port mov fsta.prbyte+2,ax mov fsta.psbyte,ax ; bytes sent out serial port mov fsta.psbyte+2,ax mov fsta.frbyte,ax ; bytes received for this file mov fsta.frbyte+2,ax mov fsta.fsbyte,ax ; bytes sent for this file mov fsta.fsbyte+2,ax mov fsta.prpkt,ax ; packets received for this file mov fsta.prpkt+2,ax mov fsta.pspkt,ax ; packets sent for this file mov fsta.pspkt+2,ax mov fsta.nakrcnt,ax ; NAKs received for this file mov fsta.nakscnt,ax ; NAKs sent for this file mov fsta.xstatus,al ; clear status byte mov ah,getdate ; get current date, convert to ascii int dos mov date+9,'0' ; init day of month begtim2:cmp dl,10 ; day of month. Ten or more days? jl begtim3 ; l = no sub dl,10 inc date+9 ; add up tens of days jmp short begtim2 ; repeat for higher order begtim3:add dl,'0' ; ascii bias mov date+10,dl ; day units mov dl,dh ; months (1-12) dec dl ; start at zero to index table mov dh,0 mov di,dx ; months shl di,1 add di,dx ; times three chars/month mov al,months[di] ; get text string for month mov date+12,al mov ax,word ptr months[di+1] mov word ptr date+13,ax mov ax,cx ; year since 1980 mov dx,0 mov di,offset date+16 ; destination call lnout ; convert number to asciiz in buffer ; start time mov ah,gettim ; DOS time of day, convert to ascii int dos mov fsta.btime,dx ; store ss.s low word of seconds mov fsta.btime+2,cx ; store hhmm high word of seconds mov date,'0' ; init begin hours field begtim4:cmp ch,10 ; ten or more hours? jl begtim5 ; l = no sub ch,10 inc date ; add up tens of hours jmp short begtim4 ; repeat for twenties begtim5:add ch,'0' ; ascii bias mov date+1,ch ; store units of hours mov date+3,'0' ; minutes field begtim6:cmp cl,10 ; ten or more minutes? jl begtim7 ; l = no sub cl,10 inc date+3 ; add up tens of minutes jmp short begtim6 ; repeat for higher orders begtim7:add cl,'0' ; ascii bias mov date+4,cl ; store units of minutes mov date+6,'0' ; seconds field begtim8:cmp dh,10 ; ten or more seconds? jl begtim9 ; l = no sub dh,10 inc date+6 ; add up tens of seconds jmp short begtim8 ; repeat for higher orders begtim9:add dh,'0' ; ascii bias mov date+7,dh mov sflag,80h ; say begtim has been run pop dx pop cx pop ax ret begtim endp ; Take snapshot of statistics counters at end of an operation ; Enter with ax = 0 for a receive operation, ax = 1 for a send. [jrd] endtim proc near test sflag,80h ; called more than once without calling begtim? jnz endtim1 ; ne = no, so do statistics snapshot ret ; yes, do nothing endtim1:and sflag,not (1) ; assume receive operation or ax,ax ; send (ax > 0), receive (ax = 0) flag jz endtim2 ; z = receive opeation or sflag,1 ; say send operation endtim2: push ax push cx push dx mov ah,gettim ; get DOS time of day int dos mov fsta.etime,dx ; store ss. s mov fsta.etime+2,cx ; hhmm cmp cx,fsta.btime+2 ; end time less than start time? ja endtim2a ; a = above (no need to test low order word) cmp dx,fsta.btime ; be. How about low order word jae endtim2a ; ae = no wrap around of time add ch,24 ; add one day to hours field endtim2a:sub dl,byte ptr fsta.btime ; 0.01 sec difference jns endtim2b dec dh ; borrow a second add dl,100 ; make difference positive endtim2b:sub dh,byte ptr fsta.btime+1; seconds difference jns endtim2c dec cl ; borrow a minute add dh,60 ; make difference positive endtim2c:mov bh,0 mov bl,dh ; bx has seconds difference sub cl,byte ptr fsta.btime+2 ; minutes jns endtim2d dec ch ; borrow an hour add cl,60 endtim2d:mov al,cl mov ah,0 mul sixty ; minutes to seconds add bx,ax ; seconds to bx sub ch,byte ptr fsta.btime+3 ; hours difference jns endtim2e add ch,24 endtim2e:mov al,ch mov ah,0 mul sixty ; hours to minutes in ax mul sixty ; minutes to seconds in dx,ax add ax,bx ; ax = seconds adc dx,0 ; dx = high word of seconds mov fsta.etime,ax ; store elapsed time, seconds, low wd mov fsta.etime+2,dx ; high word add ssta.etime,ax ; add to session time, low word adc ssta.etime+2,dx ; add to session time, high word mov ax,fsta.prbyte ; port bytes received for this file add ssta.prbyte,ax ; port bytes received for this session mov ax,fsta.prbyte+2 ; high word adc ssta.prbyte+2,ax mov ax,fsta.psbyte ; port bytes sent for this file, low word add ssta.psbyte,ax ; port bytes sent for this session, low word mov ax,fsta.psbyte+2 ; high word adc ssta.psbyte+2,ax test sflag,1 ; completing a receive operation? jnz endtim3 ; nz = no, a send operation mov ax,tfilsz+2 ; file bytes received, low word mov fsta.frbyte,ax add ssta.frbyte,ax ; session received file bytes, low word mov ax,tfilsz ; high word mov fsta.frbyte+2,ax adc ssta.frbyte+2,ax jmp short endtim4 endtim3:mov ax,tfilsz+2 ; file bytes sent, low word mov fsta.fsbyte,ax ; file bytes sent add ssta.fsbyte,ax ; session sent file bytes, low word mov ax,tfilsz ; high word mov fsta.fsbyte+2,ax adc ssta.fsbyte+2,ax endtim4:mov ax,fsta.prpkt ; packets received for this file add ssta.prpkt,ax ; session received packets mov ax,fsta.prpkt+2 adc ssta.prpkt+2,ax mov ax,fsta.pspkt ; packets sent for this file add ssta.pspkt,ax ; session sent packets mov ax,fsta.pspkt+2 adc ssta.pspkt+2,ax mov ax,fsta.nakrcnt ; NAKs received for this file add ssta.nakrcnt,ax ; session received NAKs mov ax,fsta.nakscnt ; NAKs sent for this file add ssta.nakscnt,ax ; session sent NAKs ; do transaction logging cmp tloghnd,0 ; logging transaction? -1 = not opened jg endtim5 ; g = logging jmp endtim12 ; skip logging endtim5:push di ; kind of transaction push bx ; save these registers mov bx,tloghnd ; handle for transaction log mov dx,offset rcvmsg ; assume receive message test sflag,1 ; 1 for send, 0 for receive jz endtim6 ; z = receive mov dx,offset sndmsg ; send message endtim6:call strlen ; length of message to cx mov ah,write2 int dos ; write kind of transfer ; File names cmp diskio.string,0 ; local filename je endtim9 ; e = no filename test sflag,1 ; a send operation? jnz endtim8 ; nz = yes ; Receive mov dx,offset fsta.xname ; remote name call strlen ; length to cx jcxz endtim7 ; no name mov ah,write2 int dos mov dx,offset diskio.string ; local name call strlen ; length to cx mov si,offset fsta.xname ; compare these two names mov di,dx push ds pop es repe cmpsb ; compare je endtim9 ; e = same, so no 'as' msg mov dx,offset fasmsg ; give 'as' message mov cx,faslen ; length mov ah,write2 int dos endtim7:mov dx,offset diskio.string ; local name call strlen ; get length mov ah,write2 ; write local name int dos jmp short endtim9 endtim8:mov dx,offset diskio.string ; Send. local name call strlen mov ah,write2 int dos cmp fsta.xname,0 ; using an alias? je endtim9 ; e = no mov dx,offset fasmsg ; give 'as' message mov cx,faslen mov ah,write2 int dos mov dx,offset fsta.xname ; get alias call strlen mov ah,write2 int dos ; status of transfer endtim9:mov dx,offset atmsg ; say At mov cx,atlen ; length mov bx,tloghnd ; handle mov ah,write2 int dos mov dx,offset date ; write time and date field mov cx,datelen ; length mov ah,write2 int dos mov dx,offset fsucmsg ; assume success message cmp fsta.xstatus,0 ; 0 = completed? je endtim10 ; e = completed mov dx,offset fbadmsg ; failed message test fsta.xstatus,80h ; interrupted? jz endtim10 ; z = no mov dx,offset fintmsg ; interrupted message endtim10:call strlen ; get length to cx mov ah,write2 int dos ; file bytes transferred mov ax,tfilsz+2 ; file bytes, low word mov dx,tfilsz ; high word mov di,offset temprp ; work buffer call lnout ; transform to ascii mov [di],0a0dh ; append cr/lf add di,2 ; count them mov dx,offset temprp ; start of work buffer mov cx,di ; next free byte sub cx,dx ; compute length mov ah,write2 int dos pop bx pop di endtim12:mov tfilsz,0 ; clear file size area mov tfilsz+2,0 mov sflag,0 ; say have done ending once already mov fsta.xname,0 ; clear statistics "as" name pop dx pop cx pop ax ret endtim endp ; SHOW STATISTICS command. Displays last operation and session statistics ; 9 March 1987 [jrd] shosta proc near ; show file transfer statistics mov ah,cmcfm ; confirm with carriage return call comnd ret ; not confirmed nop nop call endtim ; update statistics, just in case push bx push cx push dx mov bx,offset fsta ; pointer to file (Last op) statistics mov cx,2 ; two sets to display shosta0:push cx ; save loop counter cmp cx,2 ; doing Last operation set? mov cx,offset lastmsg ; totals for last transfer je shosta1 ; e = yes mov bx,offset ssta ; point to Session statistics area mov cx,offset sessmsg ; totals for whole session shosta1: mov ax,[bx].etime ; elapsed time of operation mov dx,[bx].etime+2 call shoprt ; show result mov cx,offset pinmsg ; port bytes received mov ax,[bx].prbyte mov dx,[bx].prbyte+2 call shoprt ; show result mov cx,offset poutmsg ; port bytes sent mov ax,[bx].psbyte mov dx,[bx].psbyte+2 call shoprt ; show result mov cx,offset finmsg ; file bytes received mov ax,[bx].frbyte mov dx,[bx].frbyte+2 call shoprt ; show result mov cx,offset foutmsg ; file bytes sent mov ax,[bx].fsbyte mov dx,[bx].fsbyte+2 call shoprt ; show result mov cx,offset pkimsg ; packets received mov ax,[bx].prpkt mov dx,[bx].prpkt+2 call shoprt ; show result mov cx,offset pkomsg ; packets sent mov ax,[bx].pspkt mov dx,[bx].pspkt+2 call shoprt ; show result mov cx,offset nakimsg ; naks received mov ax,[bx].nakrcnt xor dx,dx call shoprt ; show result mov cx,offset nakomsg ; naks sent mov ax,[bx].nakscnt xor dx,dx call shoprt ; compute baud rate as 10 * total port bytes / elapsed time mov ax,[bx].prbyte ; port bytes received, low mov dx,[bx].prbyte+2 ; port bytes received, high add ax,[bx].psbyte ; port bytes sent, low adc dx,[bx].psbyte+2 ; high. [dx,ax] = total port bytes mov cx,[bx].etime ; low word of sec in cx cmp [bx].etime+2,0 ; is high word of sec zero (e.t. < 65536 sec)? jz shosta3 ; z = yes, ready for arithmetic push ax ; else scale values, save byte count push dx mov ax,[bx].etime ; elapsed time for file, low word mov dx,[bx].etime+2 ; high word shr ax,1 ; divide seconds by two, low word ror dx,1 ; get low bit of high word and dx,8000 ; pick out just that bit or ax,dx ; mask in that bit, new time in ax (dx = 0) mov cx,ax ; save elapsed time (double-seconds) pop dx ; get byte count again pop ax shr ax,1 ; divide byte count by two also push dx ror dx,1 ; rotate low bit to high position and dx,8000h ; get low bit of high word or ax,dx ; byte count divided by two, low word pop dx shr dx,1 ; and high word shosta3:or cx,cx ; is elapsed time (in cx) zero seconds? jnz shosta4 ; nz = no inc cx ; set time to one second (no div by 0) shosta4:div cx ; bytes div seconds, ax = quo, dx = rem push dx ; save remainder of bytes/second mul ten ; quotient times ten to dx,ax pop dx ; discard overflow, recover remainder push ax ; save partial baud rate xchg ax,dx ; remainder to ax xor dx,dx ; clear extension mul ten ; remainder times ten too (keep only overflow) pop ax ; recover main partial result add ax,dx ; add two partial results xor dx,dx ; clear extension ( < 65536 baud ) mov cx,offset baudmsg call shoprt ; show result pop cx ; recover loop counter dec cx jcxz shostax ; cx = 0 means we are done jmp shosta0 ; do next set of statistics (session stuff) shostax: pop dx pop cx pop bx jmp rskp shosta endp ; Print show statistics line. Enter with CX=offset of initial message, ; dx,ax with long value shoprt proc near push ax push dx mov dx,cx ; setput initial print mov ah,prstr ; display title line (dx is ptr) int dos pop dx pop ax push di mov di,offset temprp ; work space for output call lnout ; show long integer pop di mov dx,offset temprp call prtasz ; print asciiz string ret shoprt endp ; LNOUT - Table driven unsigned long integer (32 bit) display ; Register dx holds high order word and ax holds low order word of unsigned ; long integer to be stored in decimal. Storage area is given by DS:[DI] ; DI is incremented for each storage, null terminated ; Table TENS holds set of double word values of ten raised to powers 0 to 9 ; TENSLEN holds the number of these double words ; All registers preserved. 8 March 1987 [jrd] lnout proc near push ax push bx push cx push dx push si xor si,si ; flag to say start printing (no leading 0's) mov cx,tenslen ; number of table entries lnout1: push cx ; save loop counter mov bx,cx ; index into tens double word table dec bx ; index starts at zero add bx,bx add bx,bx ; bx times four (double words to bytes) xor cx,cx ; cx is now a counter of subtractions lnout2: cmp dx,word ptr tens[bx+2] ; pattern 10**(bx/4), high order part jb lnout4 ; b = present number is less than pattern ja lnout3 ; a = present number is larger than pattern cmp ax,word ptr tens[bx] ; high words match, how about lows jb lnout4 ; b = present number is smaller than pattern lnout3: sub ax,word ptr tens[bx] ; subtract low order words sbb dx,word ptr tens[bx+2] ; subtract high order words, w/borrow inc cl ; count number of subtractions inc si ; flag to indicate printing needed jmp lnout2 ; try again to deduct present test pattern lnout4: or bx,bx ; doing least significant digit? jz lnout5 ; z = yes, always print this one or si,si ; should we print? jz lnout6 ; z = no, not yet lnout5: add cl,'0' ; get number of subtractions mov [di],cx ; store it (ch is still zero), asciiz inc di lnout6: pop cx ; recover loop counter loop lnout1 pop si pop dx pop cx pop bx pop ax ret lnout endp ; Initialize buffers and clear line INIT PROC NEAR mov hierr,0 ; clear high-bit-seen flag test flags.remflg,dquiet ; quiet display mode? jnz init3 ; nz = yes test flags.remflg,dserial ; serial mode display? jnz init2 ; nz = yes call cmblnk mov dx,offset cxzhlp call putmod ; write mode line mov fmtdsp,1 ; say doing formatted display test flags.remflg,dserver ; server mode? jz init1 ; z = no mov dx,scrser ; move cursor to top of screen call poscur mov ah,prstr mov dx,offset infms1 ; say now in server mode int dos init1: call locate mov ah,prstr ; Put statistics headers on the screen mov dx,offset outlin int dos mov wrpmsg,0 ; haven't printed the messsage yet mov prepksz,0 ; set previous packet size to zero ret init2: mov ah,prstr ; print string mov dx,offset cxzser ; status line as a text string int dos init3: mov wrpmsg,1 ; suppress display of percentage msg mov fmtdsp,0 ; say doing unformatted display ret INIT ENDP ; Output the chars in a packet ; Called with AX = size of the data, BX = address of source FILEIO PROC NEAR ptchr: mov cx,ax mov ax,offset outbuf ; routine to call when buffer gets full mov chrcnt,maxpack ; size of buffer Data mov bufpnt,offset decbuf ; decoded data placed here pending output mov bx,offset data ; source of data jmp short decode ; CX = Size of data, BX = Address of data, AX = Routine to call to ; dump data decode: push si push di push es push dx push ax mov ax,ds mov es,ax pop ax mov si,bx ; Source of data mov bx,ax ; Coroutine to call mov di,bufpnt ; Destination of data mov dh,0 ; assume no quote char cmp trans.ebquot,'N' ; no quoting? je decod1 ; yes, keep going cmp trans.ebquot,'Y' ; or not doing it? je decod1 ; yes, keep going mov dh,trans.ebquot ; otherwise use quote char decod1: mov rptct,0 ; Reset repeat count cmp cx,0 ; any more chars in source? jg decod2 ; g = yes jmp decod6 ; Else, we're through decod2: cld ; forward direction lodsb ; Pick up a char dec cx ; count number left cmp rptq,0 ; Doing repeat quoting? je dcod21 ; Nope, skip this part cmp al,rptq ; Did we pick up the repeat quote char? jne dcod21 ; No, continue processing it lodsb ; Get the size dec cx ; Modify buffer count sub al,20H ; Was made printable mov rptct,al ; Remember how many repetitions lodsb ; Get the char to repeat dec cx ; Modify buffer count dcod21: mov ah,0 ; Assume no 8-bit quote char cmp al,dh ; This the 8-bit quot char? jne decod3 ; ne = no lodsb ; Get the real character dec cx ; Decrement # chars in packet mov ah,80H ; Turn on 8-bit quot char flag decod3: cmp al,trans.squote ; Is it the quote char? jne decod4 ; ne = no, proceed lodsb ; Get the quoted character dec cx ; Decrement # of chars in packet or ah,al ; save parity (combine with prefix) and ah,80h ; only parity and al,7FH ; Turn off the parity bit cmp al,trans.squote ; Is it the quote char? je decod4 ; If so just go write it out cmp al,dh ; This the 8-bit quot char? je decod4 ; If so, just go write it out cmp al,rptq ; Is is the repeat quote character? je decod4 ; If so, just write it out cmp al,3fh ; char less than '?' ? jl decod4 ; l = yes; leave it intact cmp al,5fh ; char greater than '_' ? jg decod4 ; g = yes; leave it alone add al,40H ; Make it a control char again and al,7FH ; Modulo 128 decod4: or al,ah ; or in parity dcod41: stosb ; store the character dec rptct ; Repeat counter dec chrcnt ; Decrement number of chars in dta cmp chrcnt,0 ; space left in output buffer? jg dcod42 ; g = yes push ax ; Save the char push cx ; flush output buffer push dx push bx call bx ; Output it if full jmp decod5 ; Error return if disk is full nop pop bx pop dx pop cx mov di,bufpnt pop ax ; recover repeated char dcod42: cmp rptct,0 ; Write out char again? jg dcod41 ; g = yes jmp decod1 ; No, get next char decod5: pop bx pop dx ; dx is pushed twice (really) pop cx pop dx pop es pop di pop si ret decod6: mov bufpnt,di ; store address for next output char push cx push dx push bx call bx ; flush output buffer before final ret jmp decod5 ; Error return if disk is full nop pop bx pop dx pop cx pop dx pop es pop di pop si jmp rskp ; Return successfully if done ; output the buffer, reset bufpnt and chrcnt outbuf: mov cx,maxpack ; get full size of buffer sub cx,chrcnt ; minus space remaining = # to write jc outbf0 ; c = bad buffer pointers jnz outbu6 jmp outb11 ; cx = 0 means nothing to do outbu6: cmp flags.xflg,1 ; Writing to screen? jne outbu0 jmp outbf2 ; Yes, handle specially outbu0: mov cx,maxpack ; get full size of buffer sub cx,chrcnt ; minus space remaining = # to write jc outbf0 ; c = bad buffer pointers jnz outbu1 jmp outb11 ; cx = 0 means nothing to do outbu1: push bx mov dx,offset decbuf ; address of buffer cmp flags.destflg,1 ; disk destination? je outbu5 ; e = yes cmp flags.eofcz,0 ; end on Control-Z? jne outbu5 ; ne = yes, let DOS do it push cx ; else map Control-Z to space push di push es push ds pop es ; datas to es mov di,dx ; scan buffer es:di, cx chars worth mov al,ctlz ; look for Control-Z cld outbu3: repne scasb jne outbu4 ; ne = found no Control-Z's mov byte ptr [di-1],' ' ; replace Control-Z with space jcxz outbu4 ; z = examined all chars jmp short outbu3 ; until examined everything outbu4: pop es pop di pop cx outbu5: mov bx,diskio.handle ; file handle mov ah,write2 ; DOS 2.0 write int dos ; Write the record pop bx jc outbf0 ; c set means disk writing error cmp ax,cx ; did we write all the bytes? je outbf1 ; e = yes call erpos ; no mov dx,offset erms11 ; Disk full error cmp flags.destflg,1 ; writing to disk? je outbu0a ; e = yes push bx mov bx,offset decbuf add bx,ax ; look at break character cmp byte ptr [bx],ctlz ; ended on Control-Z? pop bx je outbf1 ; e = yes, say no error mov dx,offset ermes9 ; Printer not ready message outbu0a:mov ah,prstr ; Tell about it int dos jmp abfil ; Fix things up before aborting outbf0: call erpos mov ah,prstr ; Tell about it mov dx,offset erms13 ; Disk writing error cmp flags.destflg,0 ; writing to printer? jne outbf0a ; ne = no mov dx,offset ermes9 ; Printer not ready message outbf0a:int dos jmp abfil ; Fix things up before aborting outbf1: add tfilsz+2,cx ; count received chars adc tfilsz,0 test flags.remflg,dserial ; serial mode display? jnz outb11 ; nz = yes, skip kbyte and % displays call kbpr ; Print the kilobytes received call perpr ; Print the percent outb11: mov bufpnt,offset decbuf ; Addr for beginning mov chrcnt,maxpack ; size of empty buffer jmp rskp outbf2: ; writing to screen mov cx,maxpack ; size of full buffer sub cx,chrcnt ; minus # of unused in buffer jle outb11 ; none to print, don't try add tfilsz+2,cx ; count received chars adc tfilsz,0 mov di,offset decbuf ; Where they are call prtscr ; Output buffer to screen jmp outb11 ; Reset counter & pointer ; Tidy up before aborting. Retidied by [jrd] ABFIL PROC NEAR cmp flags.xflg,1 ; Writing to screen? je abfil1 ; Yes don't delete "file" mov bx,diskio.handle ; get file handle cmp bx,4 ; writing to DOS set of files? jbe abfil1 ; be = yes, never close them mov ah,close2 ; DOS 2.0 file close int dos cmp flags.abfflg,1 ; Delete what got across or keep it? jne abfil1 ; Nope, keep it push dx mov dx,offset diskio.string ; the full file name mov ah,del2 ; DOS 2.0 file delete int dos pop dx abfil1: mov bx,offset erms10 ; Text of message to send call errpack ; Send an error packet ret ABFIL ENDP ; General routine for sending an error packet. Register BX should ; point to the text of the message being sent in the packet ERRPACK PROC NEAR push di mov di,offset data ; Where to put the message mov al,0 errp1: mov ah,[bx] cmp ah,'$' ; At end of message? je errp2 inc al ; Remember number of chars in msg mov [di],ah inc bx inc di jmp errp1 errp2: pop di mov ah,0 mov pack.datlen,ax mov ah,'E' ; And send an error packet call spack nop ; Return if succeed or fail nop nop ret ERRPACK ENDP ; Get the chars from the file gtchr: cmp flags.filflg,0 ; Is there anything in the DMA? je gtchr0 ; e = yes, proceed mov ah,rptq mov origr,ah ; Save repeat prefix here mov rptct,1 ; Number of times char is repeated mov rptval,0 ; Value of repeated char call inbuf jmp gtchr1 ; No more chars, go return EOF nop ; Make three bytes long gtchr0: mov bx,offset inbuf jmp encode gtchr1: mov ax,0ffffh ret ; encode - writes data portion of kermit packet into filbuf ; expects BX to contain the address of a routine to refill the buffer, ; chrcnt to be the # of chars in the buffer, trans.maxdat to contain ; the maximum size of the data packet, bufpnt to contain a pointer to ; the source of the characters ; Returns: AX/ the number of characters actually written to the buffer encode: mov cx,trans.maxdat ; Maximum packet size push ds pop es ; make es:di point to datas segment mov di,offset filbuf ; Where to put the data mov si,bufpnt ; pointer into source buffer mov dl,trans.rquote ; send quote char mov dh,0 ; assume no 8-bit quoting cmp trans.ebquot,'N' ; not doing 8-bit quoting je encod1 cmp trans.ebquot,'Y' ; or can but won't? je encod1 mov dh,0ffh ; remember we have to do it encod1: cmp cx,0 ; any space left in output buffer? jg encod2 ; g = yes sub di,offset filbuf mov ax,di mov bufpnt,si ; update pointer into DMA jmp rskp encod2: cmp chrcnt,0 ; Any data in buffer? jg encod3 ; yes, skip over buffer refill call bx ; Get another buffer full jmp encod8 mov si,bufpnt ; update position in DMA cmp chrcnt,0 ; no characters returned? jne encod3 ; Got some, keep going jmp encod8 ; none, assume eof encod3: dec chrcnt ; Decrement input count cld ; forward direction lodsb cmp flags.eofcz,0 ; Is a control-z an end of file? je encd30 ; No, don't have to look for one cmp al,'Z'-40H ; Is this a control-Z? jne encd30 ; No, skip eof-processing mov flags.eoflag,0FFH ; Yes, set eof flag mov flags.filflg,0FFH ; No more input in buffer mov chrcnt,0 ; Ditto jmp encod8 ; Go set character count and return encd30: cmp rptq,0 ; Are we doing repeat prefixing? je encd3x ; Nope, skip next part cmp chrcnt,0 ; Are we on the last character? jle encd31 ; Yes, so there's no next character cmp rptct,94 ; Max number that we can put in a byte je encd31 ; Then that's it mov ah,[si] ; Get the next character cmp al,ah ; Is current char == next char? jne encd31 inc rptct ; Number of times char appears mov rptval,al ; Remember the character jmp encod1 ; Keep checking for more encd31: cmp rptct,1 ; Were previous characters repeats? je encd3x ; No, so just add this char cmp rptct,rptmin ; Are we within bounds for repeat prefixing? jge encd32 ; Yes, use repeat prefixing mov al,rptct mov ah,0 sub si,ax ; Not enough characters to warrant it mov rptval,0 ; Clear out this value mov al,rptq mov origr,al ; Save original repeat prefix mov rptq,0 ; Pretend we're not doing prefixing mov al,rptct mov ah,0 add chrcnt,ax ; Adjust input buffer pointer jmp encod1 ; Reprocess those characters encd32: push ax ; Do repeat prefixing - save data mov al,rptq ; Add repeat prefix char stosb dec cx ; Account for it in buffer size mov al,rptct ; Get the repeat count add al,20H ; Make it printable stosb ; Add to buffer dec cx pop ax ; Get back the actual character mov rptct,1 ; Reset repeat count mov rptval,0 ; And this encd3x: cmp dh,0 ; are we doing 8-bit quoting? je encod4 ; e = no, forget this test al,80h ; parity on? je encod4 ; no, don't bother with this and al,7fh ; turn off parity mov ah,trans.ebquot ; get quote char mov [di],ah ; put in packet inc di dec cx ; decrement # of chars left encod4: mov ah,al ; save character and ah,80h ; only parity and al,7fh ; turn off parity in character cmp al,' ' ; Compare to a space jl encod5 ; If less then its a control char cmp al,del ; Is the char a delete? je encod5 ; e = yes, go quote it cmp al,dl ; Is it the quote char? je encod6 ; e = yes, go add it cmp dh,0 ; are we doing 8-bit quoting? je encd41 ; e = no, don't translate it cmp al,trans.ebquot ; Is it the 8-bit quote char? je encod6 ; e = yes, just output with quote encd41: cmp origr,0 ; Doing repeat prefixing? je encod7 ; e = no, don't check for quote char cmp al,origr ; Is this the repeat quote character je encod6 ; e = yes, then quote it jmp short encod7 ; else don't quote it encod5: xor al,40h ; control char, uncontrollify encod6: mov [di],dl ; insert control quote char inc di dec cx encod7: or al,ah ; put parity back stosb dec cx ; Decrement output buffer counter cmp rptct,1 ; One occurence of this char? jne encd7x mov al,origr mov rptq,al ; Restore repeat quote char jmp encod1 ; Yes, so loop around for some more encd7x: dec rptct ; Add another entry of this char jmp encod1 ; With quoting and all encod8: sub di,offset filbuf or di,di ; buffer empty? je encod9 ; e = yes mov ax,di ; report size encoded jmp rskp ; return success encod9: mov ax,-1 ; Get a minus one ret ; return failure inbuf: cmp flags.eoflag,0 ; Have we reached the end? jz inbuf0 ret ; Return if set inbuf0: push si push dx push bx push cx mov bx,offset buff ; Set the r/w buffer pointer mov bufpnt,bx mov bx,diskio.handle ; get file handle mov cx,buffsz ; record size mov dx,bufpnt ; buffer address mov ah,readf2 ; DOS 2.0 read a record int dos jc inbuf1 ; c = error, ie file not open or ax,ax ; any bytes read? jne inbuf2 ; ne = yes (the number read) inbuf1: mov flags.eoflag,0FFH ; Set End-of-file mov flags.filflg,0ffh ; Buffer empty mov chrcnt,0 ; zero bytes left in buffer pop cx pop bx pop dx pop si ret inbuf2: add tfilsz+2,ax ; total the # bytes transferred so far adc tfilsz,0 ; it's a double word mov chrcnt,ax ; Number of chars read from file mov flags.filflg,0 ; Buffer not empty test flags.remflg,dserial ; serial display mode? jnz inbuf3 ; nz = yes, skip kbyte and % display push ax call kbpr ; Print the kilobytes sent call perpr ; Print the percent sent pop ax inbuf3: pop cx pop bx pop dx pop si jmp rskp nulref: mov chrcnt,0 ; No data to return jmp rskp nulr: ret ; dummy buffer emptier ; Print the number of Kilobytes transferred kbpr: test flags.remflg,dquiet ; quiet display mode? jnz kbpr1 ; nz = yes, no printing push bx mov ax,tfilsz+2 ; low order word mov bx,tfilsz ; high order word add ax,512 ; round up, add half the denominator adc bx,0 mov al,ah ; divide double word by 1024, in steps mov ah,bl shr ax,1 shr ax,1 ror bh,1 ror bh,1 and bh,not (3fh) or ah,bh ; ax has the result pop bx cmp ax,oldkbt ; is it the same? je kbpr1 ; yes, skip printing mov oldkbt,ax ; save new # of kb push ax call kbpos ; Postion the cursor pop ax call decout ; Print number of KBytes transferred kbpr1: ret ; Print the percent transferred perpr: test flags.remflg,dquiet ; quiet display mode? jz perpr1 ; z = no. allow printing ret ; skip printing in remote mode perpr1: cmp ofilsz,0 ; high word of original file size > 0 ? jne perpr3 ; ne = yes, use big file code cmp ofilsz+2,0 ; anything here at all? jne perpr2 ; ne = yes, use small file code ret ; otherwise, quit now perpr2: push dx ; case for files < 64 Kb mov ax,ofilsz+2 ; original size (low word) mov denom,ax mov dx,tfilsz ;transferred size times 256 in [dx,ax] mov ax,tfilsz+2 mov dh,dl ; whole value multiplied by 256 mov dl,ah mov ah,al mov al,0 mov cx,denom ; round up, add half the denominator shr cx,1 add ax,cx adc dx,0 div denom ; (256*xfer)/orig. ax = quo, dx = rem mul onehun ; multiply quotient above by 100 mov al,ah ; divide result (ax) by 256 mov ah,0 ; percentage is in ax jmp perpr4 ; finish in common code perpr3: push dx ; case for file size > 64 KB mov ax,ofilsz+2 ; original file size low order word shr ax,1 ; divide by 2 mov al,ah ; divide again by 256 for total of 512 mov ah,0 ; clear ah mov dx,ofilsz ; high order word xchg dh,dl ; do shl dx,cl=7 ror dx,1 ; old low bit of dh to high bit of dh and dl,80h ; clear lower bits. divided by two or ax,dx ; paste together the two parts into ax mov denom,ax ; denom = original size divided by 512 mov dx,tfilsz ; high order word of transferred size mov ax,tfilsz+2 ; low order word mov cx,denom ; round up, add half the denominator shr cx,1 add ax,cx adc dx,0 div denom ; xfer/(512*orig). ax=quot, dx=rem mul onehun ; times 100 for 512*percentage, in ax mov al,ah ; divide ax by 512 mov ah,0 shr ax,1 ; final percentage, in ax perpr4: pop dx cmp ax,oldper ; same as it was before? je perpr7 ; yes, don't bother printing mov oldper,ax ; remember this for next time cmp wrpmsg,0 ; did we write the percentage message? jne perpr5 ; ne = yes, skip this part push ax call perpos ; position cursor mov dx,offset permsg mov ah,prstr int dos ; write out message pop ax mov wrpmsg,1 ; init flag so we don't do it again perpr5: push ax call perpos ; Position the cursor pop ax cmp ax,onehun ; > 100% ? jle perpr6 ; no, accept it mov ax,onehun ; else just use 100 perpr6: call decout mov dl,25h ; Load a percent sign mov ah,conout ; Print the character int dos perpr7: ret ; GETFIL, called only by send code getfil: mov flags.filflg,0ffh ; Say nothing is in the buffer mov flags.eoflag,0 ; Not the end of file mov dx,offset diskio.dta ; data transfer address mov ah,setdma ; set disk transfer address int dos ; do it mov cx,0 ; attributes: find only normal files mov dx,offset diskio.string ; filename string (may have wild cards) mov ah,first2 ; DOS 2.0 search for first int dos ; get file's characteristics pushf ; save c flag mov ah,setdma ; reset dta address mov dx,offset buff ; restore dta int dos popf ; restore status of search for first jnc getfi1 ; nc = ok so far ret ; else take error exit getfi1: mov dx,offset diskio.string ; original file spec (may be wild) mov di,offset templp ; place for path part mov si,offset templf ; place for filename part call fparse ; split them mov si,offset diskio.fname ; current filename from DOS call strcat ; local path + diskio.fname mov si,di ; make it a source mov di,offset diskio.string ; new destination call strcpy ; new string = old path + DOS's filename mov ah,open2 ; DOS 2.0 file open mov al,0 ; open readonly cmp dosnum,2 ; above DOS 2? jna getfi1a ; na = no, so no shared access mov al,0+40h ; open readonly, deny none getfi1a:mov dx,offset diskio.string ; filename string int dos jnc getfi2 ; nc = opened the file ret ; else take error return getfi2: mov diskio.handle,ax ; save file handle mov ax,diskio.sizehi ; get file size (high order word) mov ofilsz,ax ; new form mov ax,diskio.sizelo ; low order word mov ofilsz+2,ax ; new form mov ax,0 mov tfilsz,ax ; Set bytes sent to zero mov tfilsz+2,ax mov ax,-1 ; get a minus one mov oldkbt,ax mov oldper,ax cmp ofilsz,0 ; Null file? jne getfl0 ; Nope cmp ofilsz+2,0 ; Null file? jne getfl0 ; Nope mov flags.eoflag,0FFH ; yes. Set EOF getfl0: jmp rskp ; GTNFIL called by send code to get next file. Rewritten by [jrd] gtnfil: cmp flags.cxzflg,'Z' ; Did we have a ^Z? jne gtn1 ; ne = no, else done sending files ret ; take failure exit gtn1: mov flags.filflg,0ffh ; Nothing in the DMA mov flags.eoflag,0 ; Not the end of file mov dx,offset diskio.dta ; point at dta mov ah,setdma ; set the dta address int dos mov ah,next2 ; DOS 2.0 search for next int dos pushf ; save carry flag mov ah,setdma ; restore dta mov dx,offset buff int dos popf ; recover carry flag jc gtn4 ; carry set means no more files found call endtim ; get tod of end of file transfer mov di,offset templp ; place for path part mov si,offset templf ; place for filename part mov dx,offset diskio.string ; current full filename call fparse ; split them mov si,offset diskio.fname ; new filename part from DOS call strcat ; rejoin path and new filename mov si,di ; new source mov di,offset diskio.string ; place for whole new name call strcpy ; copy new string mov dx,offset diskio.string ; address of new string mov ah,open2 ; DOS 2.0 file open mov al,0 ; open readonly cmp dosnum,2 ; above DOS 2? jna gtn3 ; na = no, so no shared access mov al,0+40h ; open readonly, deny none gtn3: int dos jc gtn4 ; c = could not open the file mov diskio.handle,ax ; save file handle call begtim ; start statistics counter mov ax,diskio.sizehi ; get file size (high order word) mov ofilsz,ax ; save as original file size mov ax,diskio.sizelo ; low order word mov ofilsz+2,ax mov tfilsz,0 ; Set bytes sent to zero mov tfilsz+2,0 mov oldkbt,-1 mov oldper,-1 mov ax,1 ; tell statistics this was a send operation cmp ofilsz,0 ; Null file? jne gtn2 ; Nope cmp ofilsz+2,0 ; Null file? jne gtn2 ; Nope mov flags.eoflag,0FFH ; Set EOF gtn2: jmp rskp ; set success condition gtn4: ret ; set failure condition ; Get the file name from the data portion of the F packet or from locally ; specified override filename (in locfil) ; prints the filename, handles any manipulation of the filename ; necessary, including changing the name to prevent collisions ; Called by READ (receive a file, at rfil32) gofil: push si push di mov si,offset data ; filename in packet cmp flags.xflg,0 ; receiving to screen je gofil0a ; e = no mov diskio.handle,1 ; screen is stdout, handle 1 cmp data,0 ; filename given? jne gofil0a ; ne = yes mov si,offset toscreen ; then use this dummy name gofil0a:mov di,offset diskio.string ; place where prtfn prints name call strcpy ; copy pkt filename to diskio.string mov di,offset fsta.xname ; statistics filespec save area call strcpy ; record external name pop di pop si cmp flags.xflg,0 ; Receiving to screen? (X versus F) je gofil1 ; e = no jmp gofi20 ; Yes. so skip this stuff gofil0: cmp flags.destflg,2 ; file destination = screen? jne gofil1 ; ne = no jmp gofi20 ; yes gofil1: test flags.remflg,dquiet ; quiet display mode? jnz gofi1c ; nz = yes, don't display filename call prtfn ; display the packet's filename gofi1c: mov byte ptr diskio.string,0 ; clear final filename cmp flags.destflg,0 ; writing to printer? jne gofi1a ; ne = no, go on mov ax,offset printer ; this is filename now mov diskio.handle,4 ; system printer is handle 4 jmp gofi16 ; and do it directly gofi1a: mov flags.nmoflg,0 ; assume no override name cmp byte ptr locfil,0 ; overriding name from other side? jne gofi1e ; ne = yes jmp gofil4 ; e = No. get the other end's filename gofi1e: mov flags.nmoflg,0ffh ; say using an override name mov ax,offset locfil ; get local override filename cmp word ptr locfil+1,003ah ; colon+null?(primative drive spec A:) je gofil3 ; e = yes, skip screwy DOS response (No Path) cmp word ptr locfil,'..' ; parent directory? jne gofi1g ; ne = noo cmp word ptr locfil+1,002eh ; dot dot + null? je gofi1b ; e = yes, process as directory gofi1g: cmp word ptr locfil,002eh ; dot + null (parent dir)? je gofi1b ; e = yes, process as directory call isfile ; does it exist? jnc gofi1f ; nc = file exists test filtst.fstat,80h ; serious error? jz gofil3 ; z = no, just no such file jmp gofi18a ; else quit here gofi1f: test byte ptr filtst.dta+21,10H ; subdirectory name? jnz gofi1b ; nz = yes cmp byte ptr locfil+2,5ch ; could it be a root directory like b:\? jne gofi1d ; ne = no. (DOS is not helpful with roots) cmp byte ptr locfil+3,0 ; and is it terminated in a null? je gofi1b ; e = yes, so it is a root spec gofi1d: test byte ptr filtst.dta+21,0fh ; r/o, hidden, system, vol label? jz gofil3 ; z = no jmp gofi18a ; yes. Complain and don't transfer file gofi1b: mov dx,offset locfil ; locfil is a subdirectory name call strlen ; get its length w/o terminator jcxz gofil2 ; zero length dec cx ; examine last char push bx ; save bx mov bx,cx add bx,dx cmp byte ptr [bx],5ch ; ends in backslash? je gofil2 ; e = yes cmp byte ptr [bx],2fh ; maybe forward slash? je gofil2 ; e = yes mov byte ptr [bx + 1],5ch ; no slash yet. use backslash mov byte ptr [bx + 2],0 ; plant new terminator gofil2: pop bx gofil3: mov di,offset templp ; local path mov si,offset templf ; local filename mov dx,offset locfil ; local string call fparse ; split local string mov di,offset temprp ; remote path mov si,offset temprf ; remote file mov dx,offset data ; remote string push bx ; guard against long filenames mov bx,offset data mov byte ptr [bx+64],0 ; force filename to be <= 64 text chars pop bx call fparse ; split remote string mov si,offset templp ; copy local path to mov di,offset data ; final filename call strcpy ; do the copy gofi3a: mov si,offset templf ; assume using local file name cmp byte ptr templf,0 ; local file name given? jne gofi3b ; ne = yes mov si,offset temprf ; else use remote file name gofi3b: call strcat ; do the append ; now offset data holds the new filename ; gofil4: mov ax,offset data ; assume we're writing to disk push bx ; guard against long filenames mov bx,offset data mov byte ptr [bx+64],0 ; force filename to be <= 64 text char pop bx ; recheck legality of filename in 'data' gofil5: mov di,offset temprp ; remote path mov si,offset temprf ; remote file mov dx,offset data ; remote string call strlen ; get original size push cx ; remember it call fparse ; further massage filename push si ; put pieces back together call verfil ; verify each char in temprf string mov si,di ; get path part first mov di,dx ; set destination call strcpy ; copy in path part pop si ; recover (new) filename cmp byte ptr [si],'.' ; does filename part start with a dot? jne gofil5a ; ne = no push di ; save regs push si mov di,offset rdbuf ; a work area mov byte ptr [di],'X' ; start name with letter X inc di call strcpy ; copy rest of filename mov di,si mov si,offset rdbuf ; copy new name back to original location call strcpy pop si ; restore regs pop di gofil5a:call strcat ; append it call strlen ; see if we chopped out something pop si ; get original length (from push cx above) cmp cx,si ; same size? je gofil9 ; e = yes mov flags.nmoflg,0ffh ; say that we have a replacement name ; filename is now in 'data', all converted gofil9: test flags.remflg,dquiet ; quiet display mode? jnz gofi10 ; nz = yes, don't print it cmp flags.nmoflg,0 ; using local override name? je gofi10 ; e = no mov ah,prstr mov dx,offset asmsg ; print " as " int dos mov dx,offset data ; plus the local filename call prtasz ; print asciiz string gofi10: mov ax,offset data ; point to name cmp flags.flwflg,0 ; Is file warning on? je gofi16 ; e = no, just proceed call isfile ; does it exist? mov ax,offset data ; reload ptr in case jc gofi16 ; carry set = no, just proceed mov ah,open2 ; could it be a device name? mov al,0 ; open readonly cmp dosnum,2 ; above DOS 2? jna gofi10a ; na = no, so no shared access mov al,0+40h ; open for reading, deny none gofi10a:mov dx,offset data ; the filename int dos jc gofi11 ; c = cannot open so just proceed mov bx,ax ; file handle mov ah,ioctl mov al,0 ; get info int dos mov ah,close2 ; close it int dos mov ax,offset data ; point to filename again test dl,80h ; ISDEV bit set? jz gofi11 ; z = no, not a device jmp gofi16 ; device, use name as given gofi11: call unique ; generate unique name jc gofi14 ; could not generate a unique name test flags.remflg,dquiet ; quiet display mode? jnz gofi13 ; nz = yes, skip printing push ax ; save unique name again call frpos ; Position cursor. mov ah,prstr ; Inform the user we are renaming the file mov dx,offset infms5 int dos pop ax ; get name back into ax again push ax ; save around these calls mov dx,ax ; print current filename call prtasz ; display filename pop ax ; pointer to name, again gofi13: jmp gofi16 ; and go handle file gofi14: mov dx,offset ermes4 test flags.remflg,dquiet ; quiet display mode? jnz gofi15 ; nz = yes, no printing call erpos ; Position cursor mov ah,prstr ; Tell the user we can't rename it int dos gofi15: mov bx,dx ; Tell host can't rename call errpack ; Send error packet before abort ret gofi16: mov si,ax ; pointer to (maybe new) name mov di,offset diskio.string ; filename, used in open mov dx,di ; for isfile and open below call strcpy ; copy name to diskio.string mov ax,0 mov ofilsz,ax ; original file size is unknown mov ofilsz+2,ax ; double word mov tfilsz,ax ; Set bytes received to zero mov tfilsz+2,ax mov ax,-1 ; get a minus one mov oldkbt,ax mov oldper,ax mov diskio.handle,ax ; clear handle of previous usage mov ax,dx ; filename for isfile call isfile ; check for read-only/system/vol-label/dir jc gofi16a ; c = file does not exist test byte ptr filtst.dta+21,1fh ; the no-no file attributes jz gofi16b ; z = ok jmp gofi18 ; nz = shouldn't write over one of these gofi16a:test filtst.fstat,80h ; access problem? jnz gofi18 ; nz = yes, quit here mov diskio.handle,-1 ; clear handle of previous usage mov ah,creat2 ; DOS 2.0 create file mov cx,0 ; attributes bits int dos jc gofi16b ; c = did not work, try regular open mov diskio.handle,ax ; save file handle here call begtim ; start file loggging jmp rskp gofi16b:test byte ptr filtst.dta+21,1bh ; r/o, hidden, volume label? jnz gofi18 ; we won't touch these mov ah,open2 ; open existing file (usually a device) mov al,1+1 ; open for writing int dos jc gofi18 ; carry set means can't open mov diskio.handle,ax ; file handle call begtim ; start file loggging jmp rskp gofi18a:mov si,ax ; pointer to local override name mov di,offset diskio.string ; filename, used in open call strcpy ; copy name to diskio.string ; fall through to gofi18 gofi18: test flags.remflg,dquiet ; quiet display mode? jnz gofi19 ; nz = yes, don't try printing call erpos ; Position cursor mov ah,prstr ; tell the user mov dx,offset erms12 int dos mov dx,offset diskio.string ; print offending name call prtasz ; display filename gofi19: mov dx,offset erms12 ; reset error message for packet mov bx,dx call errpack ; Send an error packet ret gofi20: cmp pack.datlen,0 ; Any data in "X" packet? je gofi21 ; Nothing to print. mov ah,prstr mov dx,offset crlf int dos int dos ; Print another crlf mov di,offset data ; Where data is mov cx,pack.datlen ; How much data we have call prtscr ; Print it on the screen mov ah,prstr mov dx,offset crlf int dos gofi21: jmp rskp ; And done FILEIO ENDP ; Given incoming filename in 'data'. Verify that each char is legal ; (if not change it to an "X"), force max of three chars after a period (dot) ; Source is at ds:si (si is changed here). [jrd] VERFIL PROC NEAR push es ; verify each char in 'data' push cx mov ax,ds mov es,ax mov havdot,0 ; say no dot found in name yet cld verfi1: lodsb ; get a byte of name from si and al,7fH ; strip any eight bit cmp al,0 ; end of name? je verfi5 ; e = yes cmp al,'.' ; a dot? jne verfi2 ; ne = no cmp havdot,0 ; have one dot already? jne verfi3 ; ne = yes, change to X mov byte ptr [si+3],0 ; forceably end filename after 3 char ext mov havdot,1 ; say have a dot now jmp verfi4 ; continue verfi2: cmp al,3ah ; colon? je verfi4 cmp al,5ch ; backslash path separator? je verfi4 cmp al,2fh ; or forward slash? je verfi4 cmp al,'0' jb verfi3 ; See if it's a legal char < '0' cmp al,'9' jbe verfi4 ; It's between 0-9 so it's OK cmp al,'A' jb verfi3 ; Check for a legal punctuation char cmp al,'Z' jbe verfi4 ; It's A-Z so it's OK cmp al,'a' jb verfi3 ; Check for a legal punctuation char cmp al,'z' ja verfi3 and al,5FH ; It's a-z, capitalize jmp verfi4 ; continue with no change verfi3: push di ; special char. Is it on the list? mov di,offset spchar2 ; list of acceptable special chars mov cx,spc2len cld repne scasb ; Search string for input char pop di je verfi4 ; e = in table, return it mov al,'X' ; else illegal, replace with "X" mov flags.nmoflg,0FFH ; say we have a replacement filename verfi4: mov [si-1],al ; update name jmp verfi1 ; loop thru rest of name verfi5: mov byte ptr[si-1],0 ; make sure it's null terminated pop cx pop es ret VERFIL ENDP ; find a unique filename... Upgraded by [jrd] ; Enter with a pointer to a (null-terminated) filename in ax ; Return with same pointer but with a new name (or old if failure) ; Success = carry clear; failure = carry set ; The idea is to pad out the main name part (8 chars) with ascii zeros and ; then change the last chars successively to a 1, 2, etc. until ; a unique name is found. All registers are preserved ; Add patch to make empty main name fields start with letter X, not digit 0 ; 23 March 1986 [jrd] unique proc near push bx push cx push dx push si push di push es push ax ; save address of source string mov dx,ds ; make es use ds segment mov es,dx mov dx,ax ; point at original filename string mov di,offset templp ; place for path mov si,offset templf ; place for filename call fparse ; separate path (di) and filename (si) mov dx,di ; point at path part call strlen ; put length in cx mov si,ax ; point to original string add si,cx ; point to filename part mov di,offset templf ; destination is temporary location mov cx,0 ; a counter cld ; set direction to be forward uniq1: lodsb ; get a byte cmp al,'.' ; have a dot? je uniq2 ; e = yes cmp al,0 ; maybe null at end? jne uniq3 ; ne = no. continue loop uniq2: cmp cl,8 ; have we copied any chars before dot? jge uniq3 ; ge = all 8 mov byte ptr [di],'0' ; avoid clobbers; pad with 0's cmp cl,0 ; first char of filename? jne uniq2a ; ne = no mov byte ptr [di],'X' ; start name with letter X, not 0 uniq2a: inc di ; and count the output chars inc cl ; and this counter too jmp uniq2 ; continue until filled 8 slots uniq3: inc cl ; cl = # char in destination stosb ; store the char cmp al,0 ; null at end? jne uniq1 ; ne = no, continue copying mov di,offset templf add di,7 ; address of last name char mov byte ptr [di],'1' ; put '1' in last name char mov unum,1 ; start with this generation digit uniq4: mov di,offset rdbuf ; build a temporary full filename mov si,offset templp ; path part call strcpy ; copy that much mov si,offset templf ; get rebuilt filename part call strcat ; paste that to the end mov ax,offset rdbuf ; point to full name call isfile ; does it exist? jc uniq6 ; c = no, succeed now inc unum ; move to next generation mov di,offset templf ; position for characters add di,7 ; point to last name char mov cx,7 ; max # of digits to play with mov bx,10 ; divisor (16 bits) mov ax,unum ; low order part of generation # uniq5: mov dx,0 ; high order part of generation # div bx ; compute digit (unum / 10) add dl,'0' ; make remainder part printable mov byte ptr [di],dl ; put into right place cmp ax,0 ; any more to do? (quotient nonzero) jz uniq4 ; z = no, try this name dec di ; else decrement char position loop uniq5 ; and keep making a number stc ; failure: set carry, keep old name jmp short uniq7 ; and exit uniq6: pop di ; address of original filename push ax ; save for exit clean up mov si,offset rdbuf call strcpy ; copy new filename over old clc ; success: clear carry flag uniq7: pop ax pop es pop di pop si pop dx pop cx pop bx ret unique endp ; [jrd] ; strlen -- computes the length, excluding the terminator, of an asciiz ; string. Input: dx = offset of the string ; Output: cx = the byte count ; Normal 'ret' return. All registers except cx are preserved ; STRLEN PROC NEAR push di push es push ax mov ax,ds ; use proper segment address mov es,ax mov di,dx mov cx,0ffffh ; large byte count cld ; set direction to be forward mov al,0 ; item sought is a null repne scasb ; search for it add cx,2 ; add for -1 and auto dec in scasb neg cx ; convert to count, excluding terminator pop ax pop es pop di ret STRLEN ENDP ; [jrd] ; strcat -- concatenates asciiz string 2 to the end of asciiz string 1 ; offset of string 1 is expected to be in ds:di. input & output ; offset of string 2 is expected to be in ds:si. input only (unchanged) ; Preserves all registers. No error returns, returns normally via ret ; STRCAT PROC NEAR push di ; save work registers push si push es push dx push cx push ax mov ax,ds ; get data segment value mov es,ax ; set es to ds for implied es:di usage mov dx,di call strlen ; get length (w/o terminator) of dest string add di,cx ; address of first terminator mov dx,si ; start offset of source string call strlen ; find its length too (in cx) inc cx ; include its terminator in the count rep movsb ; copy source string to end of output string pop ax pop cx pop dx pop es pop si pop di ret STRCAT ENDP ; [jrd] ; strcpy -- copies asciiz string pointed to by ds:si into area pointed to by ; ds:di. Returns via ret. All registers are preserved ; STRCPY PROC NEAR mov byte ptr [di],0 ; clear destination string call strcat ; let strcat do the real work ret STRCPY ENDP ; [jrd] ; fparse -- separate the drive:path part from the filename.ext part of an ; asciiz string. Characters separating parts are \ or / or : ; Inputs: asciiz input full filename string offset in ds:dx ; asciiz path offset in ds:di ; asciiz filename offset in ds:si ; Outputs: the above strings in the indicated spots ; Strategy is simple. Reverse scan input string until one of the ; three separators is encountered and then cleave at that point ; Simple filename construction restrictions added 30 Dec 1985; ; to wit: mainname limited to 8 chars or less, ; extension field limited to 3 chars or less and is found by searching ; for first occurence of a dot in the filename field. Thus the whole ; filename part is restricted to 12 (8+dot+3) chars plus a null ; All registers are preserved. Return is always via ret ; (Microsoft should have written this for DOS 2.x et seq.) FPARSE PROC NEAR push cx ; local counter push ax ; local work area push es ; implied segment register for di push di ; offset of path part of output push si ; offset of file name part of output mov ax,ds ; get data segment value mov es,ax ; set es to ds for implied es:di usage mov byte ptr [si],0 ; clear outputs mov byte ptr [di],0 push si ; save original file name address mov si,dx ; get original string address call strcpy ; copy string to original di call strlen ; find length (w/o terminator), in cx mov si,di ; address of string start add si,cx dec si ; si = address of last non-null char jcxz fpars5 ; if null skip the path scan ; now find last path char, if any ; start at the end of input string std ; set direction to be backward fpars4: lodsb ; get a byte (dec's si afterward) cmp al,5ch ; is it a backslash ('\')? je fpars6 ; e = yes cmp al,2fh ; or forward slash ('/')? je fpars6 ; e = yes cmp al,3ah ; or even the drive terminator colon? je fpars6 ; e = yes loop fpars4 ; else keep looking until cx == 0 ; si is at beginning of file name fpars5: dec si ; dec for inc below fpars6: inc si inc si ; si now points at first filename char ; cx holds number of path chars ; get original file name address (si) pop di ; and make it place to copy filename cld ; reset direction to be forward mov ax,si ; ax holds filename address for awhile push dx mov dx,si ; strlen wants string pointer in dx call strlen ; get length of filename part into cx pop dx jcxz fpar7a ; any chars to look at? z = no fpars7: cmp byte ptr [si],'.' ; look for a dot in filename je fpars8 ; e = found one inc si ; look at next filename char loop fpars7 ; keep looking until cx = zero fpar7a: mov si,ax ; no dot. recover starting address mov byte ptr [si+8],0 ; forcably truncate mainname to 8 char call strcpy ; copy this part to filename field jmp fparsx ; and exit fpars8: mov byte ptr [si+4],0 ; plant terminator after dot + 3 ext chars mov cx,si sub cx,ax ; cx now = number of chars in mainname field cmp cx,9 ; more than 8? jb fpars9 ; b = no, we're safe mov cx,8 ; limit ourselves to 8 chars in mainname fpars9: push si ; remember address of dot and extension mov si,ax ; point to start of input filename rep movsb ; copy cx chars from si to di (output) mov byte ptr [di],0 ; plant terminator where dot goes pop si ; source = dot and extension address call strcat ; append the dot & ext to the filename field fparsx: mov si,ax ; recover start of filename in input string mov byte ptr [si],0 ; terminate path field pop si pop di pop es pop ax pop cx ret FPARSE ENDP ; Print filename in offset data. Shortened by [jrd] PRTFN PROC NEAR push ax ; saves for messy clrfln routine push bx push cx push dx push di call clrfln ; Position cursor & blank out the line mov dx,offset diskio.string call strlen ; compute length of asciiz string in cx mov di,dx ; where prtscr wants its string call prtscr pop di pop dx pop cx pop bx pop ax ret PRTFN ENDP ; Print string to screen from offset ds:di for # bytes given in cx, ; regardless of $'s. All registers are preserved. [jrd] PRTSCR PROC NEAR jcxz prtscr4 ; cx = zero means nothing to show push ax push bx push dx mov dx,di ; source ptr for DOS cmp flags.eofcz,0 ; end on Control-Z? jne prtscr3 ; ne = yes, let DOS do it push cx ; else map Control-Z to space push di push es push ds pop es ; datas to es mov al,ctlz ; look for Control-Z cld ; scan buffer es:di, cx chars worth prtscr1:repne scasb jne prtscr2 ; ne = found no Control-Z's mov byte ptr [di-1],' ' ; replace Control-Z with space jcxz prtscr2 ; z = examined all chars jmp short prtscr1 ; until examined everything prtscr2:pop es pop di pop cx prtscr3:mov bx,1 ; stdout file handle mov ah,write2 int dos pop dx pop bx pop ax prtscr4:ret PRTSCR ENDP ; Print to screen asciiz string given in ds:dx. Everything preserved. [jrd] PRTASZ PROC NEAR push cx push di call strlen ; get length of asciiz string mov di,dx ; where prtscr looks call prtscr ; print counted string pop di pop cx ret PRTASZ ENDP ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr RSKP PROC NEAR pop bp add bp,3 push bp ret RSKP ENDP code ends end