Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83; site ut-ngp.UUCP Path: utzoo!linus!philabs!cmcl2!seismo!ut-sally!ut-ngp!knutson From: knutson@ut-ngp.UUCP (Jim Knutson) Newsgroups: net.sources Subject: MS-DOS Kermit sources (Part 2 of 7) Message-ID: <1005@ut-ngp.UUCP> Date: Fri, 5-Oct-84 12:04:03 EDT Article-I.D.: ut-ngp.1005 Posted: Fri Oct 5 12:04:03 1984 Date-Received: Sun, 7-Oct-84 03:55:02 EDT Organization: U.Texas Computation Center, Austin, Texas Lines: 3142 : Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting mskerm.asm' sed 's/^X//' <<'//go.sysin dd *' >mskerm.asm public prompt, dosnum, curdsk, swchar include msdefs.h ;******************** Version 2.26 ********************************** ; KERMIT, Celtic for "free" ; ; The name "Kermit" is a registered trade mark of Henson Associates, Inc., ; used by permission. ; ; Kermit-MS Program Version 2.26, July 26, 1984 ; ; Based on the Columbia University KERMIT Protocol. ; ; Copyright (C) 1982,1983,1984 Trustees of Columbia University ; ; Daphne Tzoar, Jeff Damens ; Columbia University Center for Computing Activities ; 612 West 115th Street ; New York, NY 10025 ; ; Special thanks to Frank da Cruz, Bill Catchings, Steve Jensen, Herm Fischer, ; Vace Kundakci, and Bernie Eiben for their help and contributions. makseg equ 26H deffcb equ 5cH setblk equ 4AH exec equ 4BH env equ 2CH ; environment address in psp terma equ 10 ; termination address in psp cline equ 80H ; offset in psp of command line namsiz equ 20 ; Bytes for file name and size. maxnam equ 10 chmod equ 43H ; chmod call (used to test for file existence) STACK SEGMENT PARA STACK 'STACK' DW 100 DUP(0) ; Initialize stack to all zeros. STK EQU THIS WORD STACK ENDS datas segment public 'datas' extrn buff:byte, comand:byte, flags:byte, pack:byte, trans:byte extrn fcb:byte, cpfcb:byte, prmptr:word, inichk:byte extrn machnam:byte public takadr,taklev versio label byte verdef db cr,lf db 'Type ? for help',cr,lf db '$' tmp db ?,'$' crlf db cr,lf,'$' ermes1 db cr,lf,'?Unrecognized command$' ermes3 db cr,lf,'?Not confirmed$' erms30 db cr,lf,'Passed maximum nesting level for TAKE command$' erms31 db cr,lf,'Take file not found$' erms32 db cr,lf,'File(s) not found$' erms33 db cr,lf,'CHKDSK program not found on current disk$' erms34 db cr,lf,'This command works only for DOS 2.0 and above$' erms35 db cr,lf,'Must specify program name$' erms36 db cr,lf,'Could not free memory$' erms37 db cr,lf,'Unable to execute program$' infms1 db 'Really erase *.*? $' infms8 db cr,lf,'File(s) erased$' tmsg5 db cr,lf,'[closing log file]',cr,lf,'$' ; [jd] filhlp1 db ' Command file specification $' filhlp2 db ' File specification (possibly wild) $' filhlp3 db ' File spec (possibly wild) or confirm with carriage return$' filmsg db ' File specification with optional path name $' filwmsg db ' File specification (possibly wild) with optional path name $' chkfil db 0,'CHKDSK COM' chkflen equ $-chkfil tophlp db cr,lf db 'BYE',tab,tab db 'CLOSE',tab,tab db 'CONNECT',tab,tab db 'DEFINE',tab,tab db cr,lf db 'DELETE',tab,tab db 'DIRECTORY',tab db 'DO',tab,tab db 'EXIT',tab,tab db cr,lf db 'FINISH',tab,tab db 'GET',tab,tab db 'HELP',tab,tab db 'LOCAL',tab,tab db cr,lf db 'LOG',tab,tab db 'LOGOUT',tab,tab db 'PUSH',tab,tab db 'QUIT',tab,tab db cr,lf db 'RECEIVE',tab,tab db 'REMOTE',tab,tab db 'RUN',tab,tab db 'SEND',tab,tab db cr,lf db 'SERVER',tab,tab db 'SET',tab,tab db 'SHOW',tab,tab db 'SPACE',tab,tab db cr,lf db 'STATUS',tab,tab db 'TAKE' db '$' lochlp db cr,lf,'DELETE file' db cr,lf,'DIRECTORY [filespec]' db cr,lf,'SPACE remaining on current disk' db cr,lf,'RUN program' db cr,lf,'PUSH to command interpreter' db '$' ; COMND tables yestab db 2 mkeyw 'NO',0 mkeyw 'YES',1 comtab db 27 mkeyw 'BYE',bye mkeyw 'C',telnet mkeyw 'CLOSE',clscpt mkeyw 'CONNECT',telnet mkeyw 'DEFINE',dodef mkeyw 'DELETE',delete mkeyw 'DIRECTORY',direct mkeyw 'DO',docom mkeyw 'EXIT',exit mkeyw 'FINISH',finish mkeyw 'GET',get mkeyw 'HELP',help mkeyw 'LOCAL',lclcmd mkeyw 'LOG',setcpt mkeyw 'LOGOUT',logout mkeyw 'PUSH',dopush mkeyw 'QUIT',exit mkeyw 'RECEIVE',read mkeyw 'REMOTE',remote mkeyw 'RUN',run mkeyw 'SEND',send mkeyw 'SERVER',server mkeyw 'SET',setcom mkeyw 'SHOW',showcmd mkeyw 'SPACE',chkdsk mkeyw 'STATUS',status mkeyw 'TAKE',take loctab db 5 mkeyw 'DELETE',delete mkeyw 'DIRECTORY',direct mkeyw 'PUSH',dopush mkeyw 'RUN',run mkeyw 'SPACE',chkdsk shotab db 2 mkeyw 'KEY',shokey mkeyw 'MACRO',shomac ; Program storage. oldstk dw ? ; Storage for system stack. oldsts dw ? ; System stack segment. ssave dw ? ; Original SS when doing CHKDSK. siz dw ? ; Memory size. in3ad dd 0 ; Original break interrupt addresses. [25] curdsk db 0 ; Current disk. origd db 0 ; Original disk. fildat db 0 ; Manipulate file data/time creation. db 0 taklev db 0 ; Take levels. [25t] takadr dw takstr-(size takinfo) ; Pointer into structure. [25t] temp dw 0 temp1 dw ? ; Temporary storage. temp2 dw ? temp3 dw ? temp4 dw ? psp dw ? divst dw 0 takstr db (size takinfo) * maxtak dup(?) ininam db 0,'MSKERMITINI' ; init file name, on default disk, 12 chars ininm2 db 'MSKERMIT.INI',0 ; init file name for 2.0 nambuf db maxnam * namsiz dup (?) cmdnam db namsiz dup (?) exefcb db fcbsiz dup (?) exefcb2 db fcbsiz dup (?) exearg dw ? ; segment addr of environment (filled in below) dd 0 ; ptr to cmd line (filled in below) dd exefcb ; default fcb dd exefcb2 ; second default fcb dircmd db ' /c dir ' dirclen equ $-dircmd dirnam db 50h dup (?) chkdcmd db 'chkdsk.com' chkdlen equ $-chkdcmd dosnum db ? ; dos version number pthnam db 'PATH=' pthlen equ $-pthnam pthbuf db 100 dup (?) ; buffer for path definition. defpth db '\', 70 dup (?) ; buffer for default path cmspnam db 'COMSPEC=' cmsplen equ $-cmspnam cmspbuf db '\command.com',0 ; default name db 30 dup (?) ; some additional space tfile db 100 dup (?) ; temp space for file names. eexit db cr,'exit',cr leexit equ $-eexit swchar db '\' ; default switch character. datas ends ; End data segment code segment public public takrd start proc far extrn cmblnk:near, locate:near, logout:near extrn bye:near, telnet:near, finish:near, comnd:near extrn read:near, remote:near, send:near, status:near, get:near extrn dodisk:near, serrst:near, setcom:near extrn clscpi:near, clscpt:near, getbaud:near extrn dodef:near, setcpt:near, docom:near extrn server:near, lclini:near, shokey:near, shomac:near assume cs:code,ds:datas,ss:stack,es:nothing push ds ; Save system data area. sub ax,ax ; Get a zero. push ax ; Put zero return addr on stack. mov ax,datas ; Initialize DS. mov ds,ax sub ax,ax mov oldstk,sp ; Save old stack pointer. mov ax,es:[2] ; In program segment prefix mov siz,ax ; Pick up memory size mov psp,es mov ah,prstr mov dx,offset machnam ; print machine name int dos mov ah,prstr ; Print the version header. mov dx,offset versio int dos mov ah,setdma ; Set disk transfer address. mov dx,offset buff int dos call getbaud ; Get the baud rate. [25] call dodisk ; See how many disk drives we have. [21a] call setint mov ah,gcurdsk ; Get current disk. int dos inc al ; We want 1 == A (not zero). mov curdsk,al mov origd,al ; Remember original disk we started on. mov ah,dosver int dos mov dosnum,al ; remember dos version cmp al,0 je start1 ; 1.1, keep going mov es,psp mov ax,es:[env] ; pick up environment address push ax call getpath ; get the path from the environment pop ax ; get environment back call getcsp ; get comspec from environment as well start1: call lclini ; do local initialization call gcmdlin ; read command line call rdinit ; read kermit init file call packlen ; Packet length in case do server comand. ; This is the main KERMIT loop. It prompts for and gets the users commands. kermit: mov ax,ds mov es,ax ; make sure this addresses data segment mov dx,prmptr ; get prompt call prompt ; Prompt the user. mov pack.state,0 ; Clear the state. mov flags.cxzflg,0 ; Reset each itme. mov ah,inichk ; Original or set checksum length. mov trans.chklen,ah ; Reset just in case. mov dx,offset comtab mov bx,offset tophlp mov comand.cmcr,1 ; Allow bare CR's. mov ah,cmkey call comnd jmp kermt2 mov comand.cmcr,0 ; Not anymore. call bx ; Call the routine returned. jmp kermt3 cmp flags.extflg,0 ; Check if the exit flag is set. jne krmend ; If so jump to KRMEND. jmp kermit ; Do it again. kermt2: mov dx,offset ermes1 ; Give an error. jmp short kermt4 kermt3: mov dx,offset ermes3 ; Give an error. kermt4: cmp flags.cxzflg,'C' ; some sort of abort? je kermit ; yes, don't print error message. mov ah,prstr int dos mov flags.droflg,0 ; Reset drive override flag. mov flags.nmoflg,0 ; Reset filename override flag. mov flags.getflg,0 ; May as well do this one. mov flags.cmrflg,0 ; This one too. jmp kermit krmend: call serrst ; Just in case the port wasn't reset. [21c] mov dl,origd ; Original disk drive. dec dl ; Want A == 0. mov ah,seldsk ; Reset original disk just in case. int dos mov sp,oldstk ret START ENDP ; This is the 'exit' command. It leaves KERMIT and returns to DOS. EXIT PROC NEAR mov ah,cmcfm call comnd ; Get a confirm. jmp r test flags.capflg,0FFH ; capturing? jz exit1 ; no, keep going mov dx,offset tmsg5 mov ah,prstr int dos call clscpi nop ; this skip returns... nop nop exit1: mov flags.extflg,1 ; Set the exit flag. jmp rskp ; Then return to system. EXIT ENDP ; This is the 'help' command. It gives a list of the commands. HELP PROC NEAR mov ah,cmcfm call comnd ; Get a confirm. jmp r mov ah,prstr ; Print a string to the console. mov dx,offset tophlp ; The address of the help message. int dos jmp rskp HELP ENDP lclcmd proc near mov ah,cmkey mov dx,offset loctab mov bx,offset lochlp call comnd jmp r call bx nop nop nop jmp rskp lclcmd endp ; Don't ignore ^C when in debug mode. SETINT PROC NEAR push ds ; Don't forget this. [25] mov ax,ds mov es,ax ; So can access our data area. mov ax,0 mov ds,ax ; Access low core. mov ax,ds:[23H * 4] ; Address for interrupt 23H. mov cx,ds:[23H * 4 +2] ; CS value for it. mov word ptr es:in3ad,ax ; Remember original values. mov word ptr es:in3ad+2,cx mov ax,cs mov ds,ax ; Access code are. mov dx,offset intbrk mov al,23H ; On ^C, goto above address. mov ah,25H int dos pop ds ret SETINT ENDP ; take commands from a file, but allow a path name PTAKE PROC NEAR cmp taklev,maxtak ; Hit our limit? jl ptake1 ; Continue if still OK. mov ah,prstr mov dx,offset erms30 ; Complain. int dos ret ptake1: mov di,takadr add di,size takinfo push di mov ah,cmtxt lea bx,[di].takbuf ; convenient place to parse name into mov dx,offset filmsg ; Help in case user types "?". call comnd pop di ret nop pop di ; restore frame address push di ; keep it on stack. lea si,[di].takbuf ; get buffer back mov bl,ah ; length of thing parsed mov bh,0 mov byte ptr [bx+si],0 ; make it asciz mov ax,si ; point to name again call spath ; is it around? pop di ; need this back jc ptake2 ; no, go complain mov dx,ax ; point to name from spath mov ah,open2 ; 2.0 open call mov al,0 ; open for reading int dos jnc ptake3 ; open ok, keep going ptake2: mov ah,prstr mov dx,offset erms31 int dos ret ptake3: inc taklev mov takadr,di mov word ptr [di].takfcb+1,ax ; save file descriptor mov byte ptr [di].takfcb,0feh ; mark as 2.0 file descriptor mov bx,ax ; need descriptor here mov ah,lseek mov al,2 mov cx,0 mov dx,cx ; seek 0 bytes from end int dos mov [di].takcnt,ax mov [di].takcnt+2,dx ; store length mov ah,lseek mov al,0 mov cx,0 mov dx,cx ; now seek back to beginning int dos cmp flags.takflg,0 je ptake4 mov ah,prstr mov dx,offset crlf int dos ptake4: call takrd ; Get a buffer full of data. jmp rskp PTAKE ENDP ; TAKE commands from a file. [25t] TAKE PROC NEAR cmp dosnum,0 je take1 jmp ptake ; use this for 2.0 take1: cmp taklev,maxtak ; Hit our limit? jl take2 ; Continue if still OK. mov ah,prstr mov dx,offset erms30 ; Complain. int dos ret take2: mov bx,takadr add bx,size takinfo push bx lea dx,[bx].takfcb mov comand.cmcr,0 ; Filename must be specified. mov ah,cmifi mov bx,offset filhlp1 call comnd pop bx ret ; Make sure this is three bytes long. nop pop bx mov byte ptr [bx].takfcb+32,0 ; have to clear current record in fcb mov ah,openf lea dx,[bx].takfcb int dos cmp al,0FFH ; File not found? jne take3 mov ah,prstr mov dx,offset erms31 int dos take3: inc taklev mov takadr,bx mov ax,word ptr [bx+16].takfcb mov [bx].takcnt,ax mov ax,word ptr [bx+18].takfcb mov [bx].takcnt+2,ax ; copy size into takinfo cmp flags.takflg,0 je take4 mov ah,prstr mov dx,offset crlf int dos take4: call takrd ; Get a buffer full of data. jmp rskp TAKE ENDP TAKRD PROC NEAR push bx push cx push dx mov bx,takadr cmp byte ptr [bx].takfcb,0feh ; is it a 2.0 file handle? jne takrd1 ; no, handle differently push bx ; save frame address lea dx,[bx].takbuf ; buffer to read into mov cx,dmasiz ; # of bytes to read mov ah,readf2 ; 2.0 read call mov bx,word ptr [bx].takfcb+1 ; file handle is stored here int dos pop bx ; restore frame address jmp takrd2 ; rejoin common exit takrd1: mov ah,setdma lea dx,[bx].takbuf int dos mov ah,readf lea dx,[bx].takfcb int dos mov ah,setdma lea dx,buff int dos takrd2: mov [bx].takchl,dmasiz lea ax,[bx].takbuf mov [bx].takptr,ax pop dx pop cx pop bx ret TAKRD ENDP ; copy the path into pthbuf ; enter with ax/ environment segment address ; works in 2.0 only. getpath proc near push es mov bx,ds mov es,bx ; address data segment mov bx,offset pthnam ; thing to find mov cx,pthlen ; length of it mov dx,offset pthbuf ; place to put it mov byte ptr pthbuf,0 ; initialize to null... call getenv ; get environment value pop es ret ; and return getpath endp ; copy the comspec into cmspbuf ; enter with ax/ environment segment address ; works in 2.0 only. getcsp proc near push es mov bx,ds mov es,bx ; address data segment mov bx,offset cmspnam ; thing to find mov cx,cmsplen ; length of it mov dx,offset cmspbuf ; place to put it call getenv ; get environment value pop es ret ; and return getcsp endp ; find a path variable. Enter with ax/ environment segment, ; bx/ variable to find (incl =), cx/ length of variable name, ; dx/ address to store value at. ; The buffer given in dx is unchanged if the variable isn't found getenv proc near push ds push es mov es,ax ; address segment mov di,0 ; offset in segment geten1: cmp es:byte ptr [di],0 ; end? je geten4 ; yes, forget it push cx ; save counter push di ; and offset mov si,bx repe cmpsb ; is it the one? pop di pop cx ; restore these je geten2 ; found it, break loop push cx ; preserve again mov cx,0ffffh ; bogus length mov al,0 ; marker to look for repne scasb ; search for it pop cx ; restore length jmp geten1 ; loop thru rest of environment geten2: add di,cx ; skip to definition mov si,di ; this is source mov di,dx ; destination as given mov ax,ds mov bx,es mov ds,bx mov es,ax ; exchange segment regs for copy geten3: lodsb ; get a byte stosb ; drop it off cmp al,0 ; end of string jne geten3 ; no, go on geten4: pop es pop ds ; restore registers ret ; and return getenv endp ; put kermit.ini onto take stack if it exists. Just like ; the take command, except it doesn't read a filename. rdinit proc near ; read kermit init file... mov al,dosnum ; get dos version or al,al jne rdini4 ; post 2.0, use file handle instead... mov bx,takadr add bx,size takinfo ; bump take ptr, point to current take frame lea di,[bx].takfcb ; destination is fcb mov ax,ds mov es,ax ; destination segment = source segment mov si,offset ininam ; name of init file mov cx,12 ; 8 char name + 3 char ext + 1 char drive... rep movsb ; copy it in mov byte ptr [bx].takfcb+32,0 ; have to clear current record in fcb mov ah,openf lea dx,[bx].takfcb int dos cmp al,0FFH ; File not found? jne rdini1 ; no, keep going ret ; else just return, no init file rdini1: inc taklev ; bump take level mov takadr,bx ; save current take frame ptr mov ax,word ptr [bx+16].takfcb mov [bx].takcnt,ax mov ax,word ptr [bx+18].takfcb mov [bx].takcnt+2,ax ; copy size into takinfo rdini2: cmp flags.takflg,0 je rdini3 mov ah,prstr mov dx,offset crlf int dos rdini3: call takrd ; Get a buffer full of data. ret rdini4: mov ax,offset ininm2 ; name to try push bx call spath ; can we find it? pop di jc rdini6 ; no, forget it, go use it mov dx,ax ; point to name mov ah,open2 ; 2.0 open function mov al,0 ; for reading... int dos jc rdini6 ; can't open, forget it rdini5: inc taklev ; bump take level add takadr,size takinfo mov di,takadr ; get current frame ptr mov word ptr [di].takfcb+1,ax ; save file handle mov byte ptr [di].takfcb,0feh ; mark as a handle mov bx,ax ; move file ptr mov ah,lseek mov al,2 mov cx,0 mov dx,0 ; seek to end of file int dos mov [di].takcnt,ax ; copy file size mov [di].takcnt+2,dx ; into structure mov al,0 mov ah,lseek mov cx,0 mov dx,0 int dos ; seek back to beginning jmp rdini2 ; go rejoin common exit rdini6: ret ; no init file, just return rdinit endp ; get command line into a macro buffer. gcmdlin proc near push ds push es cld mov es,psp ; address psp mov ch,0 mov cl,es:[cline] ; length of cmd line mov di,cline+1 ; point to actual line mov al,' ' jcxz gcmdl3 ; no command line, forget it. repe scasb ; skip over spaces je gcmdl3 ; all spaces, forget it mov si,di ; this is first non-space dec si ; pre-incremented... inc cx inc taklev ; bump take level add takadr,size takinfo ; address new take frame mov bx,takadr mov byte ptr [bx].takfcb,0ffh ; mark as a macro push cx ; save length push ds ; and segment lea di,[bx].takbuf ; into take buffer mov ax,ds mov ds,psp mov es,ax ; switch segments for copy gcmdl1: lodsb ; get a byte cmp al,',' ; comma? jne gcmdl2 ; no, keep going mov al,cr ; convert to cr gcmdl2: stosb ; deposit it loop gcmdl1 ; copy whole cmd pop ds ; restore segment mov si,offset eexit ; something to tack onto end mov cx,leexit ; length of it rep movsb ; copy it in pop cx ; restore len add cx,leexit ; count wnat we added lea ax,[bx].takbuf mov [bx].takptr,ax ; init buffer ptr mov [bx].takchl,cl ; chars remaining mov [bx].takcnt,cx ; and all chars mov [bx].takcnt+2,0 ; clear high order gcmdl3: pop es pop ds ret gcmdlin endp ; This routine prints the prompt and specifies the reparse address. PROMPT PROC NEAR mov comand.cmprmp,dx ; save the prompt pop bx ; Get the return address. mov comand.cmrprs,bx ; Save as addr to go to on reparse. mov comand.cmostp,sp ; Save for later restoral. push bx ; Put it on the stack again. mov bx,offset comand.cmdbuf mov comand.cmcptr,bx ; Initialize the command pointer. mov comand.cmdptr,bx mov ah,0 mov comand.cmaflg,ah ; Zero the flags. mov comand.cmccnt,ah mov comand.cmsflg,0FFH cmp flags.takflg,0 ; look at take flag jne promp1 ; supposed to echo, skip this check... cmp taklev,0 ; inside a take file? je promp1 ; no, keep going ret ; yes, return promp1: mov ah,prstr mov dx,offset crlf int dos mov ah,prstr ; Print the prompt. mov dx,comand.cmprmp int dos ret PROMPT ENDP ; Erase specified file(s). DELETE PROC NEAR mov comand.cmcr,0 ; Filename must be specified. mov ah,cmifi ; Parse an input filespec. mov dx,offset fcb mov bx,offset filhlp2 ; Text of help message. call comnd jmp r ; Bad filename. mov ah,cmcfm ; Parse a confirm. call comnd jmp r cld mov di,offset fcb+1 mov al,'?' mov cx,11 ; # of chars in a name repe scasb ; are they all ?'s? jne del1 ; no, skip message mov dx,offset infms1 call prompt mov ah,cmkey mov dx,offset yestab mov bx,0 call comnd jmp r push bx mov ah,cmcfm call comnd pop bx ret nop pop bx cmp bx,0 jne del1 jmp rskp del1: mov dx,offset fcb mov ah,sfirst ; See if any files match this specification. int dos cmp al,0FFH ; No matches? jne del2 mov ah,prstr mov dx,offset erms32 int dos jmp rskp del2: mov dx,offset fcb mov ah,delf ; Erase the file(s). int dos mov dx,offset infms8 mov ah,prstr ; Say we did so. int dos jmp rskp DELETE ENDP CHKDSK PROC NEAR mov ah,cmcfm call comnd jmp r cmp dosnum,0 je chkds1 ; yes, have to do it the hard way mov si,offset chkdcmd ; point to cmd mov cx,chkdlen ; and length jmp crun ; and go execute it nicely chkds1: push es mov ax,ds mov es,ax mov di,offset fcb mov si,offset chkfil mov cx,chkflen rep movsb mov dx,offset stk + 15 ; End of stack plus roundoff. mov cl,4 shr dx,cl ; Divide to get segment. add dx,seg stack ; Get past the stack. mov es,dx ; remember where segment is. mov ah,makseg ; Create new PSP. int dos mov ax,siz ; Update machine size. mov es:2,ax mov es: byte ptr [deffcb],0 ; Blank default fcb. mov di,deffcb+1 mov al,' ' ; Blank out fcb. mov cx,fcbsiz rep stosb mov word ptr es:[terma],offset term ; Termination address. mov es:[terma+2],cs mov ah,openf mov dx,offset fcb int dos inc al jnz chkok mov dx,offset erms33 mov ah,prstr int dos jmp chkend chkok: mov byte ptr fcb+32,0 ; set current record field mov di,100h ; offset to copy into lp: mov dx,offset fcb mov ah,readf int dos push ax ; save status mov si,offset buff mov cx,dmasiz/2 ; Word size of DMA rep movsw ; copy into new segment... pop ax cmp al,1 ; End of file je dun cmp al,3 ; Done here too jne lp dun: mov ssave,sp ; Save stack pointer. mov ax,es mov word ptr cs:[doit+2],ax ; Set segment for CHKDSK. mov ds,ax mov ss,ax mov ax,0 jmp cs: dword ptr [doit] ; Call CHKDSK. term: mov ax,seg datas ; Reset data area. mov ds,ax mov sp,ssave mov ax,seg stack mov ss,ax mov ah,setdma mov dx,offset buff int dos ; restore dma address!! chkend: pop es jmp rskp doit dd 100h CHKDSK ENDP ; Get directory listing. DIRECT PROC NEAR mov ah,dosver ; See what level of DOS we're at. int dos cmp al,0 ; Level 2.0 or above? jne dir4 ; Yes - get directory the easy way. mov comand.cmcr,1 ; Allow plain CR (so DIR == DIR *.*). mov ah,cmifi ; Get input file spec. mov dx,offset fcb ; Give the address for the FCB. mov bx,offset filhlp3 call comnd jmp r mov ah,cmcfm ; Parse a confirm. call comnd jmp r mov comand.cmcr,0 ; Reset this. push es mov ax,ds mov es,ax mov temp1,0FFH mov di,offset nambuf dir0: call getfn ; Get a matching file name. cmp al,0FFH ; Retcode -- are we done? je dir1 ; Yes, just leave. call dumpit ; Print it or dump to buffer. jmp dir0 dir1: pop es jmp rskp dir4: mov si,offset cmspbuf ; command processor mov di,offset dirnam dir5: lodsb ; get a byte or al,al jz dir6 ; stop on the null stosb ; otherwise copy it in jmp dir5 ; and keep going dir6: mov si,offset dircmd ; add directory command to it mov cx,dirclen rep movsb mov ah,cmtxt ; parse with cmtxt so we can have paths... mov bx,di ; next available byte mov dx,offset filwmsg ; In case user wants help. call comnd jmp r mov cl,ah mov ch,0 ; length of name sub di,offset dirnam ; compute # of bytes used add cx,di mov si,offset dirnam ; dir cmd jmp crun ; join run cmd from there. DIRECT ENDP getfn: cmp temp1,0FFH jne gtfn1 mov ah,sfirst ; Any matches? mov dx,offset fcb int dos cmp al,0FFH ; Means no matches. je gtfn5 call savfcb mov temp1,0 jmp gtfn4 gtfn1: cmp flags.wldflg,0FFH ; Wilcard seen? je gtfn2 ; Yes, get next file. mov al,0FFH ; No, set retcode. ret gtfn2: call rstfcb mov ah,snext mov dx,offset fcb int dos cmp al,0 ; Any more matches? je gtfn3 ; Yes keep going. mov al,0FFH ; OK return code. ret gtfn3: call savfcb gtfn4: push di mov si,offset buff ; Data is here. mov di,offset fcb ; Copy to here. mov cx,37 repne movsb pop di call nicnam ; Make name nice for printing. mov al,0 ret gtfn5: mov ah,prstr ; Don't print if a server. mov dx,offset erms32 ; Say no matches. int dos mov al,0FFH ; Failure return code. ret savfcb: push di mov si,offset fcb ; Data is here. mov di,offset cpfcb ; Copy to here. mov cx,37 repne movsb pop di ret rstfcb: push di mov si,offset cpfcb ; Data is here. mov di,offset fcb ; Copy to here. mov cx,37 repne movsb pop di ret nicnam: mov al,CR ; Add CRLF before print names stosb mov al,LF stosb mov cx,8 mov si,offset fcb+1 repne movsb ; Get the file name. mov al,' ' stosb mov cx,3 repne movsb mov al,tab stosb mov al,' ' stosb mov ah,openf mov dx,offset fcb int dos mov bx,offset fcb+18 ; Get hi order word of file size. mov ax,[bx] mov dx,ax mov bx,offset fcb+16 ; Get lo order word. mov ax,[bx] call nout2x ; Get it in decimal. mov al,tab stosb mov al,' ' stosb mov ah,0 mov si,offset fcb+20 lodsb mov fildat+1,al lodsb mov fildat,al ; Date field of fcb. mov cl,5 shr fildat+1,cl and fildat,1 mov cl,3 shl fildat,cl mov al,fildat or al,fildat+1 ; Get the month field. cmp al,9 jg nic0 push ax mov al,' ' stosb pop ax nic0: call nout2 ; Make it decimal. mov al,'-' stosb mov si,offset fcb+20 ; Get date field. lodsb and al,1FH cmp al,10 ; Only one digit? jge nic0x push ax mov al,'0' ; Make it two digits. stosb pop ax nic0x: call nout2 ; Make it decimal. mov al,'-' stosb mov si,offset fcb+21 ; Get the year field. lodsb shr al,1 add al,80 cmp al,100 ; At the year 2000 or above? js nic0y ; No, just go on. sub al,100 ; Go back to two digits. nic0y: cmp al,10 ; Only one digit? jge nic0z push ax mov al,'0' ; Make it two digits. stosb pop ax nic0z: call nout2 ; Make it decimal. mov al,tab stosb mov si,offset fcb+23 ; Get time field of fcb. lodsb mov cl,3 ; Get the hour field. shr al,cl mov tmp,'a' ; For AM. cmp al,12 ; Before noon? jl nic1 mov tmp,'p' ; It's PM. je nic1 ; Don't change "12" to "0". sub al,12 ; Use a 12 hr. clock. nic1: cmp al,0 ; Just after midnight? jne nic1x add al,12 ; Make it "12" instead of "0". nic1x: cmp al,10 ; Pad with a space? jge nic2 push ax mov al,' ' stosb pop ax nic2: call nout2 ; Make it decimal. mov al,':' ; Separate hours and minutes. stosb mov si,offset fcb+23 ; Get the minutes field. lodsb and al,07 mov cl,3 shl al,cl mov ah,al mov si,offset fcb+22 lodsb mov cl,5 shr al,cl or al,ah mov ah,0 cmp al,10 ; Would there be a leading zero. jge nic3 push ax mov al,'0' stosb pop ax nic3: call nout2 ; Make it decimal. mov al,tmp ; Add 'a' (AM) or 'p' (PM). stosb mov ah,closf mov dx,offset fcb int dos ret ; For now, just print it. dumpit: mov al,'$' stosb mov ah,prstr mov dx,offset nambuf int dos mov di,offset nambuf ret ; push to an inferior command parser dopush proc near cmp dosnum,0 ; < 2.0 ? jne dopus1 ; no, go on mov dx,offset erms34 mov ah,prstr int dos jmp rskp dopus1: mov ah,cmcfm call comnd jmp r mov si,offset cmspbuf ; name of parser push si ; save beginning sub cx,cx ; initial length dopus2: lodsb inc cx ; count this or al,al ; at end? jnz dopus2 ; no, keep going pop si ; restore cmd dec cx ; this is incremented one over jmp short crun ; go run it dopush endp ; crun - run an arbitrary program. Enter with si/address of whole ; cmd, cx/length of cmd. CRUN proc near push cx ; save length of cmd mov ax,ds mov es,ax ; address dest segment mov di,offset nambuf rep movsb ; copy command so we can mess with it pop cx mov si,offset nambuf ; point to command jmp short run3 ; and join run code CRUN ENDP RUN PROC NEAR cmp dosnum,0 jne run1 mov ah,prstr mov dx,offset erms34 ; Complain. int dos jmp rskp run1: mov ah,cmtxt ; Get program name. mov bx,offset nambuf ; Convenient buffer. mov dx,offset filmsg ; In case user wants help. call comnd nop nop nop cmp ah,0 jne run2 mov ah,prstr mov dx,offset erms35 int dos jmp rskp run2: mov cl,ah mov ch,0 mov si,offset nambuf ; alternate entry if cmd is already known. Source cmd ptr in si ; is trashed. run3: mov bx,cx mov byte ptr [si+bx],cr ; end string with a cr for dos. mov di,offset cmdnam mov ax,ds mov es,ax run4: lodsb cmp al,' ' jne run5 dec si ; back up over space jmp short run6 ; and exit loop run5: stosb loop run4 run6: mov byte ptr [di],0 ; terminate string dec si ; point back a byte into argument mov [si],cl ; put length of argument here mov exearg+2,si ; pointer to argument string mov exearg+4,ds ; segment of same inc si ; pass length over mov al,1 ; scan leading separators mov di,offset exefcb ; parse into this fcb mov ah,prsfcb int dos ; go parse the fcb mov al,1 ; scan leading separators mov di,offset exefcb2 ; second fcb to fill mov ah,prsfcb int dos ; parse the fcb mov es,psp ; point to psp again mov ax,es:[env] ; get environment ptr mov exearg,ax ; put into argument block mov bx,offset stk + 15 ; end of pgm mov cl,4 shr bx,cl ; compute # of paragraphs in last segment mov ax,seg stack ; end of kermit sub ax,psp ; minus beginning... add bx,ax ; # of paragraphs occupied mov ah,setblk int dos jc run7 ; nope... mov ax,ds mov es,ax ; put es segment back mov ax,offset cmdnam ; point to cmd name again call spath ; look for it jc run8 ; not found, go complain mov dx,ax ; point to command name mov al,0 ; load and execute... mov ah,exec mov bx,offset exearg ; and to arguments mov ssave,sp ; save stack ptr int dos ; go run the program mov ax,seg datas mov ds,ax ; reset data segment mov ax,seg stack mov ss,ax ; and stack segment mov sp,ssave ; restore stack ptr mov ah,setdma mov dx,offset buff pushf ; save flags int dos ; restore dma address!! popf ; recover flags jc run8 ; error, handle. jmp rskp ; ok, return run7: mov ah,prstr mov dx,offset erms36 int dos jmp rskp run8: mov ah,prstr mov dx,offset erms37 int dos jmp rskp RUN ENDP ; the show command showcmd proc near mov ah,cmkey mov dx,offset shotab xor bx,bx ; no canned help call comnd jmp r call bx ; call the handler jmp r jmp rskp ; and return showcmd endp intbrk: cmp flags.debug,1 ; Debug mode? je intb1 ; Yes, then don't ignore the ^C. push ax push ds mov ax,seg datas mov ds,ax mov flags.cxzflg,'C' ; Say we saw a ^C. mov pack.state,'A' ; Set the state to abort. pop ds pop ax iret intb1: jmp in3ad ; Original break interrupt address. ; Set the maximum data packet size. [21b] PACKLEN PROC NEAR mov ah,trans.spsiz ; Maximum send packet size. sub ah,4 ; Size minus control info. sub ah,trans.chklen ; And minus checksum chars. sub ah,2 ; Leave room at end: 2 for possible #X. cmp trans.ebquot,'N' ; Doing 8-bit quoting? je pack0 ; Nope so we've got our size. cmp trans.ebquot,'Y' je pack0 ; Not doing it in this case either. sub ah,1 ; Another 1 for 8th-bit quoting. pack0: mov trans.maxdat,ah ; Save max length for data field. ret PACKLEN ENDP NOUT2 PROC NEAR push ax push dx mov temp,10 ; Divide quotient by 10. cwd ; Convert word to doubleword. div temp ; AX <-- Quo, DX <-- Rem. cmp ax,0 ; Are we done? jz nout0 ; Yes. call nout2 ; If not, then recurse. nout0: add dl,'0' ; Make it printable. mov temp,ax mov al,dl stosb mov ax,temp pop dx pop ax ret ; We're done. [21c] NOUT2 ENDP NOUT2X PROC NEAR push ax push dx push cx mov temp,10 ; Divide quotient by 10. div temp ; AX <-- Quo, DX <-- Rem. mov cx,dx ; Remember the remainder. cmp ax,0 ; Are we done? jz nout0x ; Yes. mov dx,0 call nout2 ; If not, then recurse. nout0x: add cl,'0' ; Make it printable. mov temp,ax mov al,cl stosb mov ax,temp pop cx pop dx pop ax ret ; We're done. [21c] NOUT2X ENDP SPATH proc near ; enter with ax/ ptr to file name. Searches path for given file, ; returns with ax/ ptr to whole name, or carry on if file isn't ; to be found. push es mov bx,ds mov es,bx ; address data segment mov bx,ax ; convenient place to keep this mov si,ax mov di,offset tfile ; place to copy to mov dl,0 ; no '\' seen yet mov ah,swchar ; get switch character spath1: lodsb stosb cmp al,ah ; contain path characters? jne spath2 ; no, keep going mov dl,1 ; remember we've seen them spath2: or al,al jnz spath1 ; copy name in or dl,dl ; look at flag jz spath3 ; no path embedded, keep going pop es mov ax,offset tfile ; else... call isfile mov ax,offset tfile ; point to right thing... ret ; let isfile decide and return ; no path, keep going spath3: mov si,offset pthbuf ; path definition cmp byte ptr [si],0 ; empty path? jne spath4 ; no, keep going mov ah,gcd ; get current dir mov dl,0 ; for default drive mov si,offset defpth+1 ; place to put it int dos mov si,offset defpth ; point to the path spath4: cmp byte ptr [si],0 ; null, exit loop je spath9 mov di,offset tfile ; place to put name spath5: lodsb ; get a byte cmp al,';' ; end of this part? je spath7 ; yes, break loop cmp al,0 ; maybe end of string? jne spath6 ; no, keep going dec si ; back up over it jmp short spath7 ; and break loop spath6: stosb ; else stick in dest string jmp spath5 ; and continue spath7: push si ; save this ptr mov si,bx ; this is user's file name mov al,swchar ; get switch character. cmp byte ptr [di-1],al ; does it end with switch char? je spath8 ; yes, don't put one in stosb ; else add one spath8: lodsb stosb or al,al jnz spath8 ; copy rest of name pop si ; restore pos in path def mov ax,offset tfile call isfile ; is it a file? jc spath4 ; no, keep looking mov ax,offset tfile pop es ret ; return success (carry off) spath9: pop es ; restore this mov ax,bx ; not found yet, get original path call isfile ; does it exist? ret ; return whatever isfile says. spath endp isfile proc near ; returns carry off if the file pointed to by ax exists mov dx,ax ; copy ptr mov al,0 ; don't change anything mov ah,chmod int dos ret ; dos sets carry isfile 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 ; Jumping here is the same as a ret. R PROC NEAR ret R ENDP code ends ; End of code section. end start //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mskerm.asm /bin/echo -n ' '; /bin/ls -ld mskerm.asm fi /bin/echo 'Extracting mskermit.ini' sed 's/^X//' <<'//go.sysin dd *' >mskermit.ini ; Make shift-comma send a left angle bracket set key scan 556 < ; Shift-period sends a right angle bracket set key scan 558 > ; Accent grave is where ESC is supposed to be set key scan 96 \33 ; Put accent grave on the ESC function key set key scan 27 ` //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mskermit.ini /bin/echo -n ' '; /bin/ls -ld mskermit.ini fi /bin/echo 'Extracting mskermit.msg' sed 's/^X//' <<'//go.sysin dd *' >mskermit.msg This issue of the Info-Kermit Digest is devoted to the long-heralded (and overdue) announcement of version 2 of Kermit for MS-DOS systems (Kermit is Columbia University's file transfer protocol for use over telecommunication lines, and it runs on a wide variety of systems). We announced our intention to provide this new release back in January, and have been working on it ever since. The previous release was 1.20, 28 November 1983. The new version is called "Kermit-MS" rather than "Kermit-86" and the version number is 2.26. It is available for several systems: System DOS Versions ------ ------------ IBM PC, PPC, and XT 1.1, 2.0 & above DEC Rainbow 100 and 100+ 2.05 & above HP-150 2.0 Wang PC 2.01 Others (Generic DOS) 1.1, 2.0 & above Versions for the IBM PCjr and Heath/Zenith 100 are soon to be added (version 1.20 already run on these machines). If your MS-DOS system is not on this list, you are invited to add support for it by supplying the appropriate system- and device-dependent modules (described below). The IBM version has been tested on IBM PCs with the old and new motherboards and ROMs, as well as on the XT and Portable PC, on hard disks, floppy disks, and RAM disks, and on color and monochrome monitors. It has NOT been tested on the Compaq, Columbia, or other "PC compatible" product; there is some chance that it might not work on the compatibles even when the previous release (1.20) did, because of greater dependence on the display hardware. Version 2 of MS-DOS Kermit has been tested successfully up to 9600 baud on the IBM, DEC, HP, and Wang micros, in communication with full duplex systems like the DEC-20 and VAX, and half duplex systems like IBM mainframes. Kermit-MS requires about 80K RAM, and certain functions like PUSH and RUN will need additional memory. Thus, for DOS plus Kermit, you will need a machine with at least 128K. Version 1.20 can run on a 64K machine. Version 1.20 will remain available indefinitely because it has proven quite stable, runs on a variety of PC-compatible systems, and is considerably smaller than version 2. Here is a summary of the changes: * Program organization: The program has been broken up into separate source files, assembled separately, and linked together. The modules are: System/Device Independent: MSKERM.ASM - Main program MSSEND.ASM - File sender MSRECV.ASM - File receiver MSSERV.ASM - Server operation MSFILE.ASM - File i/o MSCMD.ASM - Command parser MSTERM.ASM - CONNECT command MSCOMM.ASM - Communications port buffering & flow control MSSET.ASM - SET, SHOW, and STATUS commands MSDEFS.H - Data structure definitions and equates System/Device Dependent: MSXxxx.ASM - System-dependent code for system xxx MSYxxx.ASM - System-dependent screen and keyboard code MSZxxx.ASM - Modem control (modem-dependent) MSXSYS.DOC - Description of system-dependent modules The modular organization allows easier modification of the program, quicker transfer of modified portions from system to system. The modules are designed to be well-defined and self-contained, such that they can be easily replaced. For instance, someone who prefers windows and mice to typing commands could replace the command parsing module without having to worry about the effect on the other modules. * Kermit Protocol Improvements: Kermit-MS now supports: X. 8th-bit prefixing for passing binary data through 7-bit communication links X. 12-bit checksums and 16-bit CRCs as alternate block check types X. Compression of repeated bytes X. Server operation X. Advanced commands for servers, including: REMOTE DELETE REMOTE DIRECTORY REMOTE HELP REMOTE HOST REMOTE SPACE REMOTE TYPE These advanced protocol features can be used in conjunction with other advanced Kermit implementations, including itself, as well as the current Kermits for the DECsystem-10, DECSYSTEM-20, VAX/VMS, PDP-11 (RSX, RSTS, RT), DEC Pro-350, and others, and soon to include IBM VM/CMS and UNIX. * Local command execution: The following new commands provide access to DOS functions from within the Kermit-MS program: DELETE DIRECTORY SET DEFAULT DISK PUSH (to DOS) SET DESTINATION (device - disk or printer) SPACE (runs CHKDSK) RUN (a program) * Command parsing: The command parser has been improved in many areas. For instance, "?" now works much better than before (though still not perfectly). ESC now provides completion not only in keywords, but also in filenames. CTRL-W deletes the previous "word" on the command line. CTRL-C always returns to the Kermit-MS> prompt. There is a command macro facility; DEFINE lets you build macros by combining Kermit-MS commands, DO executes them, SHOW displays them. DOS command line arguments are accepted, and may be strung together separated by commas, e.g. "kermit set baud 9600, set timer on, connect" Kermit-MS now reads an initialization file, MSKERMIT.INI, and can process (nested) command files with a TAKE command. * Terminal emulation: On IBM micros, the speed of Heath-19 terminal emulation has been improved by using direct screen memory access. Functions like insert and delete character now execute very rapidly. Heath-19 emulation functions, such as reverse index, missing from earlier releases are now supplied. H19 emulation may be disabled to allow the use of other console drivers, like ANSI.SYS, in conjunction with Kermit-MS. On systems with 25 lines, the 25th line is an inverse video mode line, displaying current settings, which may be kept or turned off. On the IBM and DEC systems, there are pop-up help and status screens, and the screen is saved and restored between remote/local context switches. The terminal session can be logged to disk to provide unguarded capture of remote files or session typescripts. On the IBM, DEC, and HP systems, the screen can be rolled back several pages, on a per-line or per-screen basis. On most of the systems, print-screen (screen dump) and CTRL-print-screen (toggle printing on/off) work as they do in DOS. On the IBM and DEC systems, a key redefinition facility is available to allow the layout of the keyboard to be altered to suit individual tastes, to set up keypads or function keys for specific applications, or to construct "keystroke macros". On IBM micros, the ALT key can be set up for use as a META key for use with EMACS-like editors. All versions of Kermit-MS except the generic DOS version are capable of transmitting the BREAK signal. The functions that are missing from the Wang and/or HP micros -- key redefinition, pop-up menus, screen rollback, screen print -- were omitted due to lack of information about how to get at the scan codes, screen memory, printer interrupts, etc, and may be added at a later time. Meanwhile, anyone out there who has the information and feels inclined to add missing features is invited to do so. * Communication options: The port characteristics are left alone when Kermit-MS starts (in the previous release, Kermit-MS always set the baud rate). The program allows settings for speed, duplex, flow control, handshake, and parity on a per-port basis, to allow convenient switching between ports. * File Transfer: You can now supply new names for files in SEND and GET commands. A timeout facility has been added to allow automatic recovery from deadlocks when communicating with systems (like IBM mainframes) that can't time out. The file transfer display has been reformatted, and includes more useful information, including a percentage for outbound files. The various counts are updated more reliably. Several options are available for interrupting file transfer, including ^X (cancel current file), ^Z (cancel entire batch), ^E (user-generated "error"), ^C (return immediately to command level), CR (simulate a timeout). The options are displayed during file transfer. There is a new end-of-file option to allow selection of DOS-style (believe the DOS byte count) or CP/M-style (file ends at first CTRL-Z) EOF detection. * Remote operation: Kermit-MS may be run from the back port in either interactive or server mode. This allows micro-to-micro file transfer without requiring an operator on both ends. * New Bootstrapping Procedure: The Kermit .EXE files for the various systems are now encoded using a printable 4-for-3 encoding, with compression of repeated 0 bytes. The result tends to be smaller than the original .EXE file. A new set of bootstrapping programs has been provided: MSMKBOO.C Encode. Can be used on any binary file. Written in C. MSBOOT.FOR Send the encoded file from the mainframe. Fortran. MSPCTRAN.BAS Decode the encoded file on the micro. MS Basic. MSPCBOOT.BAS Receive on the micro, decode on the fly. MS Basic. * Documentation: There's an entirely new manual, available now as a separate document, soon to be incorporated into the Kermit User Guide. It describes operation of the program in detail, along with the new bootstrapping procedure. * How To Get It: Kermit is available for a wide variety of systems -- micros, minis, and mainframes. It is distributed by Columbia University via network or on magnetic tape. For further information about Kermit, send network mail to INFO-KERMIT-REQUEST@COLUMBIA-20, or write to the Kermit Distribution address below. To be added to the Info-Kermit network mailing list, mail to INFO-KERMIT-REQUEST@COLUMBIA-20. The new MS-DOS Kermit files are available from COLUMBIA-20 via anonymous FTP after 6pm daily (ARPANET), though KERMSRV at CUVMA on BITNET (BITNET users should type "SMSG RSCS MSG CUVMA KERMSRV HELP" for information about the Columbia Kermit file server), and on all the Columbia DEC-20 systems in the KERMIT area. The file names all begin with "MS" (on BITNET, omit the "KER:" prefix). The executable programs have the suffix .EXE and are in 8-bit binary format. The corresponding 7-bit ASCII encoded files have the suffix .BOO. The system-specific programs are available in both .EXE and .BOO formats. KER:MSIBMPC -- IBM PC, XT KER:MSIBMJR -- IBM PCjr (not yet availble) KER:MSRB100 -- DEC Rainbow 100, 100+ KER:MSHP150 -- Hewlett-Packard 150 KER:MSHZ100 -- Heath/Zenith 100 (not yet available) KER:MSWANG -- Wang PC KER:MSGENER -- Generic DOS KER:MS*.ASM, KER:MS*.H are the assembler source files. KER:MSBUILD.HLP tells how to build the program. KER:MSKERMIT.DOC is the new MS-DOS section for the Kermit User Guide. KER:MSKERMIT.MSS is the Scribe source for the .DOC file. Those without network access may write to the following address for details of how to order a complete Kermit distribution on 9-track magnetic tape: KERMIT Distribution Columbia University Center for Computing Activities 612 West 115th Street New York, NY 10025 Version 2 of MS-DOS Kermit will be submitted to PC-SIG so that it can be ordered on IBM PC floppy disks. Inquiries should be directed to PC Software Interest Group 1556 Halford Avenue, Suite #130 Santa Clara, CA 95051 Phone 408-730-9291 Be sure to wait until they have version 2, because they are presently distributing version 1 on their disks numbers 41 and 42. It may take some time for them to update their distribution. * Credit: The bulk of the work was done by Daphne Tzoar and Jeff Damens of the Columbia University Center for Computing Activities. Many ideas (and "existence proofs") were contributed by Herm Fischer of Litton Data Systems -- key redefinitions, remote and server operation, etc, but those who have been using Herm's modified 1.20 will find that some of the features he added have been done differently in this release. 8th-bit quoting was originally added by Leslie Spira and her staff at The Source Telecomputing to allow Kermit to transfer binary files over Telenet. The new bootstrapping procedure and the new file transfer display were done by Bill Catchings of Columbia. Filename completion came from Kimmo Laaksonen at the Helsinki University of Technology. Some corporate support and encouragement was provided by Digital Equipment Corporation, Wang Laboratories, and IBM. * Disclaimer: Although we have been using the new version on several different kinds of systems for a good while and have done extensive testing, some bugs may have slipped through. Please hang on to your old release (1.20), and don't hesitate to report any problems to Info-Kermit@COLUMBIA-20. Suggestions and contributions are also welcome. //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mskermit.msg /bin/echo -n ' '; /bin/ls -ld mskermit.msg fi /bin/echo 'Extracting msmeta.ini' sed 's/^X//' <<'//go.sysin dd *' >msmeta.ini ; TAKE'ing this file will make the ALT key function like a META key ; on the IBM PC. set key scan 2064 \321 set key scan 2065 \327 set key scan 2066 \305 set key scan 2067 \322 set key scan 2068 \324 set key scan 2069 \331 set key scan 2070 \325 set key scan 2071 \311 set key scan 2072 \317 set key scan 2073 \320 set key scan 2078 \301 set key scan 2079 \323 set key scan 2080 \304 set key scan 2081 \306 set key scan 2082 \307 set key scan 2083 \310 set key scan 2084 \312 set key scan 2086 \313 set key scan 2087 \314 set key scan 2168 \261 set key scan 2169 \262 set key scan 2170 \263 set key scan 2171 \264 set key scan 2172 \265 set key scan 2173 \266 set key scan 2174 \267 set key scan 2175 \270 set key scan 2176 \271 set key scan 2177 \272 set key scan 2092 \332 set key scan 2093 \330 set key scan 2094 \303 set key scan 2095 \326 set key scan 2096 \302 set key scan 2097 \316 set key scan 2098 \317//go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msmeta.ini /bin/echo -n ' '; /bin/ls -ld msmeta.ini fi X/bin/echo 'Extracting msrbemacs.ini' sed 's/^X//' <<'//go.sysin dd *' >msrbemacs.ini ; EMACS function key setup for Kermit-MS/Rainbow ; ; C-@ (set mark) on SELECT set key select \00 ; ; M-h (select region) on CTRL-SELECT set key scan 1313 \33h ; ; C-U 12 C-X C-I (rigidly indent region 12 spaces) on TAB set key scan 9 \25\61\62\30\11 ; C-X C-I (rigidly indent region) on SHIFT-TAB set key scan 521 \30\11 ; ; C-S (forward search) on FIND set key find \23 ; ; C-R (reverse search) on CTRL-FIND set key scan 1307 \22 ; ; M-D (delete word) on REMOVE set key remove \33d ; ; M-K (delete sentence) on CTRL-REMOVE set key scan 1311 \33k ; ; C-P (up line) on uparrow set key scan 295 \20 ; ; M-[ (up paragraph) on CTRL-uparrow set key scan 1319 \33[ ; ; C-X [ (up page) on SHIFT-uparrow set key scan 807 \30[ ; ; M-< (top of file) on CTRL-SHIFT-uparrow set key scan 1831 \33< ; ; C-B (back character) on leftarrow set key scan 301 \02 ; ; C-A (beginning of line) on CTRL-leftarrow set key scan 1325 \01 ; ; M-A (back sentence) on SHIFT-leftarrow set key scan 813 \33a ; ; C-N (next line) on downarrow set key scan 297 \16 ; M-] (down paragraph) on CTRL-downarrow set key scan 1321 \33] ; ; C-X ] (down page) on SHIFT-downarrow set key scan 809 \30] ; ; M-> (end of file) on CTRL-SHIFT-downarrow set key scan 1833 \30> ; ; C-F (forward character) on rightarrow set key scan 299 \06 ; ; C-E (end of line) on CTRL-rightarrow set key scan 1323 \05 ; ; M-E (end of sentence) on SHIFT-rightarrow set key scan 811 \33e ; ; C-X E (do keyboard macro) on DO set key scan 257 \30e ; ; C-U C-X E (do keyboard macro 4x) on CTRL-DO set key scan 1281 \25\30e ; ; C-U 8 C-X E (do keyboard macro 8x) on SHIFT-DO set key scan 769 \25\70\30e ; ; C-U C-U C-X E (do keyboard macro 16x) on CTRL-SHIFT-DO set key scan 1793 \25\25\30e ; ; ^_ on HELP set key scan 256 \37 ; ; C-X C-Z on EXIT set key scan 271 \30\32 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msrbemacs.ini /bin/echo -n ' '; /bin/ls -ld msrbemacs.ini fi X/bin/echo 'Extracting msrecv.asm' sed 's/^X//' <<'//go.sysin dd *' >msrecv.asm public read12, read2, rin21, rfile3, read, updrtr, nak, rrinit include msdefs.h datas segment public 'datas' extrn fcb:byte, data:byte, bufpnt:word, chrcnt:word, curchk:byte extrn comand:byte, flags:byte, pack:byte, trans:byte ermes7 db '?Unable to receive initiate$' ermes8 db '?Unable to receive file name$' ermes9 db '?Unable to receive end of file$' erms10 db '?Unable to receive data$' infms1 db cr,' Receiving: In progress$' infms3 db 'Completed$' infms4 db 'Failed$' infms6 db 'Interrupted$' remmsg1 db 'Kermit-MS: Invalid filename' filhlp2 db ' Confirm with carriage return or specify name ' db ' to use for incoming file $' ender db bell,bell,'$' crlf db cr,lf,'$' temp dw 0 datas ends code segment public extrn gofil:near, outbuf:near, fixfcb:near, comnd:near extrn spack:near, rpack:near, serini:near, serrst:near extrn spar:near, rpar:near, init:near, init1:near, cxmsg:near extrn error:near, ptchr:near, erpos:near, rtpos:near extrn stpos:near, rprpos:near, nppos:near, nout:near extrn dodec:near, doenc:near, errpack:near extrn send11:near, clrmod:near assume cs:code, ds:datas ; Update retry count and fall through to send a NAK. nak0: call updrtr ; Update retry count. nak: mov ax,pack.pktnum ; Get the packet number we're waiting for. mov pack.argblk,ax mov pack.argbk1,0 mov cx,0 ; No data, but this may change. call doenc ; So call encode. mov ah,'N' ; NAK that packet. call spack jmp abort nop ; So 'jmp rskp' in SPACK comes here. [19a] ret ; Go around again. updrtr: cmp pack.state,'A' ; Supposed to abort? je upd0 ; Yes, don't bother with retry count. inc pack.numrtr ; Increment the number of retries. cmp flags.xflg,1 ; Writing to screen? je upd0 call rtpos ; Position cursor. mov ax,pack.numrtr call nout ; Write the number of retries. upd0: ret ; Abort ABORT PROC NEAR mov pack.state,'A' ; Otherwise abort. ret ABORT ENDP ; init variables for read... rrinit proc near mov pack.numpkt,0 ; Set the number of packets to zero. mov pack.numrtr,0 ; Set the number of retries to zero. mov pack.pktnum,0 ; Set the packet number to zero. mov pack.numtry,0 ; Set the number of tries to zero. ret rrinit endp ; RECEIVE command -- Some code moved to the GET routine. [21a] READ PROC NEAR mov comand.cmrflg,1 ; Say we're receiving a file. [21a start] mov comand.cmcr,1 ; Allow bare CR after RECEIVE. mov flags.droflg,0 ; Override default drive flag. mov flags.nmoflg,0 ; Override file name from other host? mov dx,offset fcb ; Put filename here. mov bx,offset filhlp2 ; Text of help message. mov ah,cmifi ; Read in the filename. call comnd jmp r mov comand.cmrflg,0 ; Reset flag. mov comand.cmcr,0 mov flags.wldflg,0 ; Just in case mov ah,cmcfm ; Get a confirm. call comnd jmp r read1: cmp flags.remflg,0 ; remote mode? jne read12 ; yes, no printing call init read12: mov flags.cxzflg,0 ; Reset ^X/^Z flag. [20c] call rrinit ; init variables for read call serini ; Initialize serial port. [14] cmp flags.remflg,0 ; in remote mode? jne read12a ; yes, no printing call init1 ; Clear the line and initialize the buffers. call stpos mov ah,prstr ; Be informative. mov dx,offset infms1 int dos call rtpos ; Position cursor. mov ax,pack.numrtr call nout ; Write the number of retries. read12a:mov pack.state,'R' ; Set the state to receive initiate. read2: cmp flags.xflg,1 ; Are we receiving to the screen. [21c] je read21 ; Skip the screen stuff. [21c] cmp flags.remflg,0 ; maybe remote mode? jne read21 ; yup, skip the screen stuff call nppos ; Position cursor for number of packets msg. mov ax,pack.numpkt call nout ; Write the number of packets. read21: mov ah,pack.state ; Get the state. [21c] cmp ah,'D' ; Are we in the data send state? jne read3 call rdata jmp read2 read3: cmp ah,'F' ; Are we in the file receive state? jne read4 call rfile ; Call receive file. jmp read2 read4: cmp ah,'R' ; Are we in the receive initiate state? jne read5 call rinit jmp read2 read5: cmp ah,'C' ; Are we in the receive complete state? jne read6 call serrst ; Reset serial port. [14] cmp flags.xflg,0 ; Did we write to the screen? [21c] je read51 ; No so print status. [21c] mov flags.xflg,0 ; Reset it. [21c] jmp rskp ; Yes, so just return. [21c] read51: cmp flags.remflg,0 ; remote mode? jne read51a ; yes, keep going call stpos ; Position cursor. [21c] mov ah,prstr mov dx,offset infms3 ; Plus a little cuteness. cmp flags.cxzflg,0 ; Completed or interrupted? [20c] je read13 ; Ended normally. [20c] mov dx,offset infms6 ; Say was interrupted. [20c] read13: int dos cmp flags.belflg,0 ; Bell desired? [17a] je readnb ; No. [17a] mov dx,offset ender ; Ring them bells. [4] int dos ; [4] readnb: call clrmod ; clear 25th line call rprpos ; Put prompt here. read51a:jmp rskp read6: call serrst ; Reset serial port. [14] cmp flags.xflg,0 ; Did we write out to screen? [21c] je read61 ; No so print status. [21c] mov flags.xflg,0 ; Reset it. [21c] jmp rskp ; Print onto screen. [21c] read61: cmp flags.remflg,0 ; remote mode? jne read7a ; yes, no printing. call stpos ; Position cursor. [21c] mov ah,prstr mov dx,offset infms4 ; Plus a little cuteness. int dos cmp flags.belflg,0 ; Bell desired? [17a] je read7 ; No. [17a] mov dx,offset ender ; Ring them bells. [4] int dos ; [4] read7: call clrmod ; clear mode line call rprpos ; Put prompt here. read7a: jmp rskp READ ENDP ; Receive routines ; Receive init RINIT PROC NEAR mov ah,pack.numtry ; Get the number of tries. cmp ah,imxtry ; Have we reached the maximum number of tries? jl rinit2 call erpos ; Position cursor. mov dx,offset ermes7 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rinit2: inc ah ; Increment it. mov pack.numtry,ah ; Save the updated number of tries. mov ax,pack.argbk2 ; get packet type if here from get cmp flags.getflg,1 ; Have we already read in the packet? [21a] je rin21a ; Yes, so don't call RPACK. [21a] mov ah,trans.chklen mov curchk,ah ; Save checksum length we want to use. mov trans.chklen,1 ; Use 1 char for init packet. call rpack ; Get a packet. jmp rin22 ; Trashed packet: nak, retry. push ax mov ah,curchk mov trans.chklen,ah ; Reset to desired value. pop ax rin21a: cmp ah,'S' ; Is it a send initiate packet? jne rinit3 ; If not see if its an error. rin21: mov flags.getflg,0 ; Reset flag. [21a] mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov ax,pack.argblk ; Returned packet number. (Synchronize them.) inc ax ; Increment it. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. mov bx,pack.numpkt inc bx ; Increment the number of packets. mov pack.numpkt,bx mov ax,pack.argbk1 ; Get the number of arguments received. mov bx,offset data ; Get a pointer to the data. call spar ; Get the data into the proper variables. mov bx,offset data ; Get a pointer to our data block. call rpar ; Set up the receive parameters. xchg ah,al mov ah,0 mov pack.argbk1,ax ; Store the returned number of arguments. mov ah,trans.chklen ; Checksum length we'll use. mov curchk,ah ; Save it. mov trans.chklen,1 ; Use 1 char for init packet. mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov ah,curchk ; Checksum length we'll use. mov trans.chklen,ah ; Reset to desired value. mov ah,'F' ; Set the state to file send. mov pack.state,ah ret rin22: mov ah,curchk mov trans.chklen,ah ; Reset to desired value. jmp nak0 ; Try again. rinit3: cmp ah,'E' ; Is it an error packet? jne rinit4 call error rinit4: jmp abort RINIT ENDP ; Receive file RFILE PROC NEAR cmp pack.numtry,maxtry ; Have we reached the maximum number of tries? jl rfile1 call erpos ; Position cursor. mov dx,offset ermes8 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rfile1: inc pack.numtry ; Save the updated number of tries. call rpack ; Get a packet. jmp nak0 ; Trashed packet: nak, retry. cmp ah,'S' ; Is it a send initiate packet? je rfil10 call dodec ; Decode all incoming packets. jmp rfile2 ; No, try next type. rfil10: cmp pack.oldtry,imxtry ; Have we reached the maximum number of tries? jl rfil12 ; If not proceed. call erpos ; Position cursor. mov dx,offset ermes7 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rfil12: inc pack.oldtry ; Save the updated number of tries. mov ax,pack.pktnum ; Get the present packet number. cmp ax,0 ; Had we wrapped around? [18 start] jne rfilx mov ax,64 rfilx: dec ax ; Decrement. [18 end -- new label] cmp ax,pack.argblk ; Is the packet's number one less than now? je rfil13 jmp nak0 ; No, NAK and try again. rfil13: call updrtr ; Update retry count. mov pack.numtry,0 ; Reset the number of tries. mov bx,offset data ; Get a pointer to our data block. call rpar ; Set up the parameter information. xchg ah,al mov ah,0 mov pack.argbk1,ax ; Save the number of arguments. mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rfile2: cmp ah,'Z' ; Is it an EOF packet? jne rfile3 ; No, try next type. cmp pack.oldtry,maxtry ; Have we reached the maximum number of tries? jl rfil21 ; If not proceed. call erpos ; Position cursor. mov dx,offset ermes9 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rfil21: inc pack.oldtry ; Increment it. mov ax,pack.pktnum ; Get the present packet number. cmp ax,0 ; Had we wrapped around? [18 start] jne rfily mov ax,64 rfily: dec ax ; Decrement. [18 end -- new label] cmp ax,pack.argblk ; Is the packet's number one less than now? je rfil24 jmp nak0 ; No, NAK and try again. rfil24: call updrtr ; Update retry count. mov pack.numtry,0 mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rfile3: cmp ah,'F' ; Start of file? je rfil31 ; Yes. [21c] cmp ah,'X' ; Text header packet? [21c] jne rfile4 ; Neither one. rfil31: mov ax,pack.argblk ; Get the packet number. [21c] cmp ax,pack.pktnum ; Is it the right packet number? je rfil32 jmp nak ; No, NAK it and try again. rfil32: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. call gofil ; Get a file to write to. jmp abort call init1 ; Initialize all the buffers. mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'D' ; Set the state to data receive. ret rfile4: cmp ah,'B' ; End of transmission. jne rfile5 mov ax,pack.pktnum cmp ax,pack.argblk ; Do we match? je rfil41 jmp nak ; No, NAK it and try again. rfil41: mov pack.argbk1,0 ; No data. (Packet number already in argblk). mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'C' ; Set the state to complete. ret rfile5: cmp ah,'E' ; Is it an error packet. jne rfile6 call error rfile6: jmp abort RFILE ENDP ; Receive data RDATA PROC NEAR cmp pack.numtry,maxtry ; Get the number of tries. jl rdata1 call erpos ; Position cursor. mov dx,offset erms10 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rdata1: inc pack.numtry ; Save the updated number of tries. call rpack ; Get a packet. jmp nak0 ; Trashed packet: nak, retry. cmp ah,'D' ; Is it a data packet? je rdat11 call dodec ; Decode data. jmp rdata2 ; No, try next type. rdat11: mov ax,pack.pktnum ; Get the present packet number. cmp ax,pack.argblk ; Is the packet's number correct? jz rdat14 cmp pack.oldtry,maxtry ; Have we reached the maximum number of tries? jl rdat12 ; If not proceed. call erpos ; Position cursor. mov dx,offset erms10 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rdat12: inc pack.oldtry ; Save the updated number of tries. mov ax,pack.pktnum cmp ax,0 ; Had we wrapped around? [18 start] jne rdatx mov ax,64 rdatx: dec ax ; [14] [18 end -- new label] cmp ax,pack.argblk ; Is the packet's number one less than now? je rdat13 jmp nak0 ; No, NAK it and try again. rdat13: call updrtr ; Update retry count. mov pack.numtry,0 ; Reset number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdat14: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov ax,pack.argbk1 ; Get the length of the data. cmp flags.cxzflg,0 ; Has the user typed a ^X or ^Z? [20c] je rdt14x ; No, write out the data. cmp flags.abfflg,1 ; Discard incomplete files? je rdat15 ; If yes don't write data out to file. [20c] rdt14x: mov bx,offset data ; Where the data is. [25] call ptchr jmp abort ; Unable to write out chars; abort. rdat15: mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (Packet number still in argblk.) mov cx,0 cmp flags.cxzflg,0 ; Interrupt file transfer? [20c] je rdat16 ; Nope. [20c] mov bx,offset data ; Send data in ACK in case remote... [20c] mov ah,flags.cxzflg ; ... knows about ^X/^Z. [20c] mov [bx],ah ; Put data into the packet. [20c] mov pack.argbk1,1 ; Set data size to 1. [20c] mov cx,1 rdat16: call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdata2: cmp ah,'F' ; Start of file? je rdat20 ; Yup. [21c] cmp ah,'X' ; Text header packet? [21c] jne rdata3 ; No, try next type. rdat20: cmp pack.oldtry,maxtry ; Reached the max number of tries? [21c] jl rdat21 ; If not proceed. call erpos ; Position cursor. mov dx,offset ermes8 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rdat21: inc pack.oldtry ; Save the updated number of tries. mov ax,pack.pktnum cmp ax,0 ; Had we wrapped around? [18 start] jne rdaty mov ax,64 rdaty: dec ax ; [14 Omitted accidentally - D.T.] [18 end] cmp ax,pack.argblk ; Is the packet's number one less than now? je rdat22 jmp nak0 ; No, NAK it and try again. rdat22: call updrtr ; Update retry count. mov pack.numtry,0 ; Reset number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdata3: cmp ah,'Z' ; Is it a EOF packet? je rdat3x ; [13] jmp rdata4 ; Try and see if its an error. [13] rdat3x: mov ax,pack.pktnum ; Get the present packet number. [13] cmp ax,pack.argblk ; Is the packet's number correct? je rdat32 jmp nak0 ; No, NAK it and try again. rdat32: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt cmp flags.cxzflg,0 ; Do we want to discard the file? [20c] jne rdt32x ; Yes. [20c] cmp pack.argbk1,1 ; One piece of data? [20c] jne rdat33 ; Nope - finish writing out file? [20c] mov bx,offset data ; Get data area. [20c] mov ah,[bx] ; Get the data. [20c] cmp ah,'D' ; "D" for discard? [20c] jne rdat33 ; Nope - write out file. [20c] rdt32x: cmp flags.abfflg,0 ; Keep incomplete files? je rdat33 ; Yes, go write it out. mov ah,closf ; First, close the file. mov dx,offset fcb ; Give the file parameters. [20c] int dos ; Kill it, ignore errors. [20c] mov ah,delf ; Delete the file if opened. [20c] int dos cmp flags.cxzflg,'X' ; Kill one file or all? [20c] jne rdat36 ; No so leave flag alone. [20c] call cxmsg ; Clear msg about interrupt. [20c] mov flags.cxzflg,0 ; Reset - ^X only kills one file. [20c] jmp rdat36 rdat33: mov bx,bufpnt ; Get the dma pointer. mov ax,80H sub ax,chrcnt ; Get the number of chars left in the DMA. cmp flags.eofcz,0 ; should we write a ^Z? jz rdat35 ; no, keep going cmp flags.xflg,0 ; writing to a file? jne rdat35 ; no, skip ^Z cmp ax,80H ; [13 start] jne rdat34 call outbuf ; Write out buffer if no room for ^Z. jmp abort mov ax,0 ; [13 end] inc chrcnt ; Increment size by one (not two). [21b] rdat34: mov cl,'Z'-100O ; Put in a ^Z for EOF. mov [bx],cl ; Add it. [21c] inc ax dec chrcnt rdat35: mov cx,chrcnt mov temp,cx call outbuf ; Output the last buffer. jmp abort ; Give up if the disk is full. mov ax,temp ; Prepare for the function call. call fixfcb mov ah,closf ; Close up the file. mov dx,offset fcb int dos rdat36: cmp flags.destflg,1 ; Writing to disk? je rdat37 ; Yes, skip next part. cmp flags.xflg,1 ; Writing to screen? je rdat37 ; Yes, skip this part. mov dl,ff ; Send a form feed. mov ah,lstout ; Write out to first printer. int dos rdat37: mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'F' ret rdata4: cmp ah,'E' ; Is it an error packet. jne rdata5 call error rdata5: jmp abort RDATA 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 R PROC NEAR ret R ENDP code ends end //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msmeta.ini /bin/echo -n ' '; /bin/ls -ld msmeta.ini fi /bin/echo 'Extracting msrecv.asm' sed 's/^X//' <<'//go.sysin dd *' >msrecv.asm public read12, read2, rin21, rfile3, read, updrtr, nak, rrinit include msdefs.h datas segment public 'datas' extrn fcb:byte, data:byte, bufpnt:word, chrcnt:word, curchk:byte extrn comand:byte, flags:byte, pack:byte, trans:byte ermes7 db '?Unable to receive initiate$' ermes8 db '?Unable to receive file name$' ermes9 db '?Unable to receive end of file$' erms10 db '?Unable to receive data$' infms1 db cr,' Receiving: In progress$' infms3 db 'Completed$' infms4 db 'Failed$' infms6 db 'Interrupted$' remmsg1 db 'Kermit-MS: Invalid filename' filhlp2 db ' Confirm with carriage return or specify name ' db ' to use for incoming file $' ender db bell,bell,'$' crlf db cr,lf,'$' temp dw 0 datas ends code segment public extrn gofil:near, outbuf:near, fixfcb:near, comnd:near extrn spack:near, rpack:near, serini:near, serrst:near extrn spar:near, rpar:near, init:near, init1:near, cxmsg:near extrn error:near, ptchr:near, erpos:near, rtpos:near extrn stpos:near, rprpos:near, nppos:near, nout:near extrn dodec:near, doenc:near, errpack:near extrn send11:near, clrmod:near assume cs:code, ds:datas ; Update retry count and fall through to send a NAK. nak0: call updrtr ; Update retry count. nak: mov ax,pack.pktnum ; Get the packet number we're waiting for. mov pack.argblk,ax mov pack.argbk1,0 mov cx,0 ; No data, but this may change. call doenc ; So call encode. mov ah,'N' ; NAK that packet. call spack jmp abort nop ; So 'jmp rskp' in SPACK comes here. [19a] ret ; Go around again. updrtr: cmp pack.state,'A' ; Supposed to abort? je upd0 ; Yes, don't bother with retry count. inc pack.numrtr ; Increment the number of retries. cmp flags.xflg,1 ; Writing to screen? je upd0 call rtpos ; Position cursor. mov ax,pack.numrtr call nout ; Write the number of retries. upd0: ret ; Abort ABORT PROC NEAR mov pack.state,'A' ; Otherwise abort. ret ABORT ENDP ; init variables for read... rrinit proc near mov pack.numpkt,0 ; Set the number of packets to zero. mov pack.numrtr,0 ; Set the number of retries to zero. mov pack.pktnum,0 ; Set the packet number to zero. mov pack.numtry,0 ; Set the number of tries to zero. ret rrinit endp ; RECEIVE command -- Some code moved to the GET routine. [21a] READ PROC NEAR mov comand.cmrflg,1 ; Say we're receiving a file. [21a start] mov comand.cmcr,1 ; Allow bare CR after RECEIVE. mov flags.droflg,0 ; Override default drive flag. mov flags.nmoflg,0 ; Override file name from other host? mov dx,offset fcb ; Put filename here. mov bx,offset filhlp2 ; Text of help message. mov ah,cmifi ; Read in the filename. call comnd jmp r mov comand.cmrflg,0 ; Reset flag. mov comand.cmcr,0 mov flags.wldflg,0 ; Just in case mov ah,cmcfm ; Get a confirm. call comnd jmp r read1: cmp flags.remflg,0 ; remote mode? jne read12 ; yes, no printing call init read12: mov flags.cxzflg,0 ; Reset ^X/^Z flag. [20c] call rrinit ; init variables for read call serini ; Initialize serial port. [14] cmp flags.remflg,0 ; in remote mode? jne read12a ; yes, no printing call init1 ; Clear the line and initialize the buffers. call stpos mov ah,prstr ; Be informative. mov dx,offset infms1 int dos call rtpos ; Position cursor. mov ax,pack.numrtr call nout ; Write the number of retries. read12a:mov pack.state,'R' ; Set the state to receive initiate. read2: cmp flags.xflg,1 ; Are we receiving to the screen. [21c] je read21 ; Skip the screen stuff. [21c] cmp flags.remflg,0 ; maybe remote mode? jne read21 ; yup, skip the screen stuff call nppos ; Position cursor for number of packets msg. mov ax,pack.numpkt call nout ; Write the number of packets. read21: mov ah,pack.state ; Get the state. [21c] cmp ah,'D' ; Are we in the data send state? jne read3 call rdata jmp read2 read3: cmp ah,'F' ; Are we in the file receive state? jne read4 call rfile ; Call receive file. jmp read2 read4: cmp ah,'R' ; Are we in the receive initiate state? jne read5 call rinit jmp read2 read5: cmp ah,'C' ; Are we in the receive complete state? jne read6 call serrst ; Reset serial port. [14] cmp flags.xflg,0 ; Did we write to the screen? [21c] je read51 ; No so print status. [21c] mov flags.xflg,0 ; Reset it. [21c] jmp rskp ; Yes, so just return. [21c] read51: cmp flags.remflg,0 ; remote mode? jne read51a ; yes, keep going call stpos ; Position cursor. [21c] mov ah,prstr mov dx,offset infms3 ; Plus a little cuteness. cmp flags.cxzflg,0 ; Completed or interrupted? [20c] je read13 ; Ended normally. [20c] mov dx,offset infms6 ; Say was interrupted. [20c] read13: int dos cmp flags.belflg,0 ; Bell desired? [17a] je readnb ; No. [17a] mov dx,offset ender ; Ring them bells. [4] int dos ; [4] readnb: call clrmod ; clear 25th line call rprpos ; Put prompt here. read51a:jmp rskp read6: call serrst ; Reset serial port. [14] cmp flags.xflg,0 ; Did we write out to screen? [21c] je read61 ; No so print status. [21c] mov flags.xflg,0 ; Reset it. [21c] jmp rskp ; Print onto screen. [21c] read61: cmp flags.remflg,0 ; remote mode? jne read7a ; yes, no printing. call stpos ; Position cursor. [21c] mov ah,prstr mov dx,offset infms4 ; Plus a little cuteness. int dos cmp flags.belflg,0 ; Bell desired? [17a] je read7 ; No. [17a] mov dx,offset ender ; Ring them bells. [4] int dos ; [4] read7: call clrmod ; clear mode line call rprpos ; Put prompt here. read7a: jmp rskp READ ENDP ; Receive routines ; Receive init RINIT PROC NEAR mov ah,pack.numtry ; Get the number of tries. cmp ah,imxtry ; Have we reached the maximum number of tries? jl rinit2 call erpos ; Position cursor. mov dx,offset ermes7 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rinit2: inc ah ; Increment it. mov pack.numtry,ah ; Save the updated number of tries. mov ax,pack.argbk2 ; get packet type if here from get cmp flags.getflg,1 ; Have we already read in the packet? [21a] je rin21a ; Yes, so don't call RPACK. [21a] mov ah,trans.chklen mov curchk,ah ; Save checksum length we want to use. mov trans.chklen,1 ; Use 1 char for init packet. call rpack ; Get a packet. jmp rin22 ; Trashed packet: nak, retry. push ax mov ah,curchk mov trans.chklen,ah ; Reset to desired value. pop ax rin21a: cmp ah,'S' ; Is it a send initiate packet? jne rinit3 ; If not see if its an error. rin21: mov flags.getflg,0 ; Reset flag. [21a] mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov ax,pack.argblk ; Returned packet number. (Synchronize them.) inc ax ; Increment it. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. mov bx,pack.numpkt inc bx ; Increment the number of packets. mov pack.numpkt,bx mov ax,pack.argbk1 ; Get the number of arguments received. mov bx,offset data ; Get a pointer to the data. call spar ; Get the data into the proper variables. mov bx,offset data ; Get a pointer to our data block. call rpar ; Set up the receive parameters. xchg ah,al mov ah,0 mov pack.argbk1,ax ; Store the returned number of arguments. mov ah,trans.chklen ; Checksum length we'll use. mov curchk,ah ; Save it. mov trans.chklen,1 ; Use 1 char for init packet. mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov ah,curchk ; Checksum length we'll use. mov trans.chklen,ah ; Reset to desired value. mov ah,'F' ; Set the state to file send. mov pack.state,ah ret rin22: mov ah,curchk mov trans.chklen,ah ; Reset to desired value. jmp nak0 ; Try again. rinit3: cmp ah,'E' ; Is it an error packet? jne rinit4 call error rinit4: jmp abort RINIT ENDP ; Receive file RFILE PROC NEAR cmp pack.numtry,maxtry ; Have we reached the maximum number of tries? jl rfile1 call erpos ; Position cursor. mov dx,offset ermes8 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rfile1: inc pack.numtry ; Save the updated number of tries. call rpack ; Get a packet. jmp nak0 ; Trashed packet: nak, retry. cmp ah,'S' ; Is it a send initiate packet? je rfil10 call dodec ; Decode all incoming packets. jmp rfile2 ; No, try next type. rfil10: cmp pack.oldtry,imxtry ; Have we reached the maximum number of tries? jl rfil12 ; If not proceed. call erpos ; Position cursor. mov dx,offset ermes7 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rfil12: inc pack.oldtry ; Save the updated number of tries. mov ax,pack.pktnum ; Get the present packet number. cmp ax,0 ; Had we wrapped around? [18 start] jne rfilx mov ax,64 rfilx: dec ax ; Decrement. [18 end -- new label] cmp ax,pack.argblk ; Is the packet's number one less than now? je rfil13 jmp nak0 ; No, NAK and try again. rfil13: call updrtr ; Update retry count. mov pack.numtry,0 ; Reset the number of tries. mov bx,offset data ; Get a pointer to our data block. call rpar ; Set up the parameter information. xchg ah,al mov ah,0 mov pack.argbk1,ax ; Save the number of arguments. mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rfile2: cmp ah,'Z' ; Is it an EOF packet? jne rfile3 ; No, try next type. cmp pack.oldtry,maxtry ; Have we reached the maximum number of tries? jl rfil21 ; If not proceed. call erpos ; Position cursor. mov dx,offset ermes9 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rfil21: inc pack.oldtry ; Increment it. mov ax,pack.pktnum ; Get the present packet number. cmp ax,0 ; Had we wrapped around? [18 start] jne rfily mov ax,64 rfily: dec ax ; Decrement. [18 end -- new label] cmp ax,pack.argblk ; Is the packet's number one less than now? je rfil24 jmp nak0 ; No, NAK and try again. rfil24: call updrtr ; Update retry count. mov pack.numtry,0 mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rfile3: cmp ah,'F' ; Start of file? je rfil31 ; Yes. [21c] cmp ah,'X' ; Text header packet? [21c] jne rfile4 ; Neither one. rfil31: mov ax,pack.argblk ; Get the packet number. [21c] cmp ax,pack.pktnum ; Is it the right packet number? je rfil32 jmp nak ; No, NAK it and try again. rfil32: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. call gofil ; Get a file to write to. jmp abort call init1 ; Initialize all the buffers. mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'D' ; Set the state to data receive. ret rfile4: cmp ah,'B' ; End of transmission. jne rfile5 mov ax,pack.pktnum cmp ax,pack.argblk ; Do we match? je rfil41 jmp nak ; No, NAK it and try again. rfil41: mov pack.argbk1,0 ; No data. (Packet number already in argblk). mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'C' ; Set the state to complete. ret rfile5: cmp ah,'E' ; Is it an error packet. jne rfile6 call error rfile6: jmp abort RFILE ENDP ; Receive data RDATA PROC NEAR cmp pack.numtry,maxtry ; Get the number of tries. jl rdata1 call erpos ; Position cursor. mov dx,offset erms10 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rdata1: inc pack.numtry ; Save the updated number of tries. call rpack ; Get a packet. jmp nak0 ; Trashed packet: nak, retry. cmp ah,'D' ; Is it a data packet? je rdat11 call dodec ; Decode data. jmp rdata2 ; No, try next type. rdat11: mov ax,pack.pktnum ; Get the present packet number. cmp ax,pack.argblk ; Is the packet's number correct? jz rdat14 cmp pack.oldtry,maxtry ; Have we reached the maximum number of tries? jl rdat12 ; If not proceed. call erpos ; Position cursor. mov dx,offset erms10 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rdat12: inc pack.oldtry ; Save the updated number of tries. mov ax,pack.pktnum cmp ax,0 ; Had we wrapped around? [18 start] jne rdatx mov ax,64 rdatx: dec ax ; [14] [18 end -- new label] cmp ax,pack.argblk ; Is the packet's number one less than now? je rdat13 jmp nak0 ; No, NAK it and try again. rdat13: call updrtr ; Update retry count. mov pack.numtry,0 ; Reset number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdat14: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov ax,pack.argbk1 ; Get the length of the data. cmp flags.cxzflg,0 ; Has the user typed a ^X or ^Z? [20c] je rdt14x ; No, write out the data. cmp flags.abfflg,1 ; Discard incomplete files? je rdat15 ; If yes don't write data out to file. [20c] rdt14x: mov bx,offset data ; Where the data is. [25] call ptchr jmp abort ; Unable to write out chars; abort. rdat15: mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (Packet number still in argblk.) mov cx,0 cmp flags.cxzflg,0 ; Interrupt file transfer? [20c] je rdat16 ; Nope. [20c] mov bx,offset data ; Send data in ACK in case remote... [20c] mov ah,flags.cxzflg ; ... knows about ^X/^Z. [20c] mov [bx],ah ; Put data into the packet. [20c] mov pack.argbk1,1 ; Set data size to 1. [20c] mov cx,1 rdat16: call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdata2: cmp ah,'F' ; Start of file? je rdat20 ; Yup. [21c] cmp ah,'X' ; Text header packet? [21c] jne rdata3 ; No, try next type. rdat20: cmp pack.oldtry,maxtry ; Reached the max number of tries? [21c] jl rdat21 ; If not proceed. call erpos ; Position cursor. mov dx,offset ermes8 mov ah,prstr int dos ; Print an error message. mov bx,dx call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. rdat21: inc pack.oldtry ; Save the updated number of tries. mov ax,pack.pktnum cmp ax,0 ; Had we wrapped around? [18 start] jne rdaty mov ax,64 rdaty: dec ax ; [14 Omitted accidentally - D.T.] [18 end] cmp ax,pack.argblk ; Is the packet's number one less than now? je rdat22 jmp nak0 ; No, NAK it and try again. rdat22: call updrtr ; Update retry count. mov pack.numtry,0 ; Reset number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort ret rdata3: cmp ah,'Z' ; Is it a EOF packet? je rdat3x ; [13] jmp rdata4 ; Try and see if its an error. [13] rdat3x: mov ax,pack.pktnum ; Get the present packet number. [13] cmp ax,pack.argblk ; Is the packet's number correct? je rdat32 jmp nak0 ; No, NAK it and try again. rdat32: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt cmp flags.cxzflg,0 ; Do we want to discard the file? [20c] jne rdt32x ; Yes. [20c] cmp pack.argbk1,1 ; One piece of data? [20c] jne rdat33 ; Nope - finish writing out file? [20c] mov bx,offset data ; Get data area. [20c] mov ah,[bx] ; Get the data. [20c] cmp ah,'D' ; "D" for discard? [20c] jne rdat33 ; Nope - write out file. [20c] rdt32x: cmp flags.abfflg,0 ; Keep incomplete files? je rdat33 ; Yes, go write it out. mov ah,closf ; First, close the file. mov dx,offset fcb ; Give the file parameters. [20c] int dos ; Kill it, ignore errors. [20c] mov ah,delf ; Delete the file if opened. [20c] int dos cmp flags.cxzflg,'X' ; Kill one file or all? [20c] jne rdat36 ; No so leave flag alone. [20c] call cxmsg ; Clear msg about interrupt. [20c] mov flags.cxzflg,0 ; Reset - ^X only kills one file. [20c] jmp rdat36 rdat33: mov bx,bufpnt ; Get the dma pointer. mov ax,80H sub ax,chrcnt ; Get the number of chars left in the DMA. cmp flags.eofcz,0 ; should we write a ^Z? jz rdat35 ; no, keep going cmp flags.xflg,0 ; writing to a file? jne rdat35 ; no, skip ^Z cmp ax,80H ; [13 start] jne rdat34 call outbuf ; Write out buffer if no room for ^Z. jmp abort mov ax,0 ; [13 end] inc chrcnt ; Increment size by one (not two). [21b] rdat34: mov cl,'Z'-100O ; Put in a ^Z for EOF. mov [bx],cl ; Add it. [21c] inc ax dec chrcnt rdat35: mov cx,chrcnt mov temp,cx call outbuf ; Output the last buffer. jmp abort ; Give up if the disk is full. mov ax,temp ; Prepare for the function call. call fixfcb mov ah,closf ; Close up the file. mov dx,offset fcb int dos rdat36: cmp flags.destflg,1 ; Writing to disk? je rdat37 ; Yes, skip next part. cmp flags.xflg,1 ; Writing to screen? je rdat37 ; Yes, skip this part. mov dl,ff ; Send a form feed. mov ah,lstout ; Write out to first printer. int dos rdat37: mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov pack.argbk1,0 ; No data. (The packet number is in argblk.) mov cx,0 call doenc mov ah,'Y' ; Acknowledge packet. call spack ; Send the packet. jmp abort mov pack.state,'F' ret rdata4: cmp ah,'E' ; Is it an error packet. jne rdata5 call error rdata5: jmp abort RDATA 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 R PROC NEAR ret R ENDP code ends end //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msrecv.asm /bin/echo -n ' '; /bin/ls -ld msrecv.asm fi