NAME mssker ; File MSSKER.ASM ; Edit history: ; Last edit 7 Jan 1988 ; 7 Jan 1988 Add es: seg override in parts of gcmdl procedure. [jrd] ; 1 Jan 1988 version 2.30 ; 31 Dec 1987 Add DOS command line commands of "-f filespec" meaning replace ; name mskermit.ini with filespec as the init file, and "stay" meaning ; stay active after last command is finished. ; 21 Sept 1987 Add 'R' keyword for Receive short form. [jrd] ; 18 Aug 1987 Change CWD to CWDIR for MASM 4.5+ [jrd] ; 9 July 1987 Replace keyword CLRINP with keyword CLEAR. [jrd] ; 4 July 1987 Remove Clear and Local commands. Revise handling of Environment, ; allow very long PATH= strings, general cleanup.[jrd] ; 7 June 1987 Add error level values when returning to DOS. [jrd] ; 0 = success, 1= send failed, 2 = receive failed, 4 = REMOTE cmd failed. ; 24 March 1987 Add Disable and Enable server commands, req by Bill Catchings ; [jrd] ; 18 March 1987 Add Path not found to isfile knowledge. [jrd] ; 1 Oct 1986 Version 2.29a ; 30 August 86 Add DOS Critical Error handler replacement and redo Control ; Break handler to avoid aborting Kermit with the serial port interrupt ; running and these interrupts redirected to Kermit. [jrd] ; 14 July 86 Add entry points for Scripts. [jrd] ; [2.29] code frozen on 6 May 1986 [jrd] ;****************************** Version 2.30 ***************************** ; KERMIT, Celtic for "free" ; ; The name "Kermit" is a registered trade mark of Henson Associates, Inc., ; used by permission. ; ; Kermit-MS Program Version 2.30, 1 Jan 1988 ; Kermit-MS Program Version 2.29, 26 May 1986, plus later revisions. ; Kermit-MS Program Version 2.27, December 6,1984 ; ; Based on the Columbia University KERMIT Protocol. ; ; Copyright (C) 1982,1983,1984,1985,1986,1987,1988 ; Trustees of Columbia University ; ; Daphne Tzoar, Jeff Damens ; Columbia University Center for Computing Activities ; 612 West 115th Street ; New York, NY 10025 ; ; Joe R. Doupnik (version 2.29, 2.30) ; Dept of EE, and CASS ; Utah State University ; Logan, Utah 84322 ; ; Special thanks to Frank da Cruz, Bill Catchings, Steve Jensen, Herm Fischer, ; Vace Kundakci, and Bernie Eiben for their help and contributions. public prompt, dosnum, curdsk, fpush, isfile, sbrk, crun, errlev public takrd, takadr, taklev, filtst, drives, maxtry, imxtry public netdone include mssdef.h env equ 2CH ; environment address in psp cline equ 80H ; offset in psp of command line CSTACK SEGMENT PARA STACK 'STACK' ; Renamed from STACK dw 200 dup(0) ; Initialize stack to all zeros. CSTACK ENDS datas segment public 'datas' extrn buff:byte, comand:byte, flags:byte, pack:byte, trans:byte extrn prmptr:word, inichk:byte, ttyact:byte extrn machnam:byte, msfinal:byte, diskio:byte, decbuf:byte versio label byte verdef db cr,lf,'$' hlpmsg db 'Type ? for help',cr,lf,'$' crlf db cr,lf,'$' ermes1 db cr,lf,'?Unrecognized command$' ermes2 db cr,lf,'?Unable to initialize memory$' ermes3 db cr,lf,'?Not confirmed$' ermes4 db cr,lf,'?Unable to change directory$' erms30 db cr,lf,'Passed maximum nesting level for TAKE command$' erms31 db cr,lf,'Take-file not found$' erms34 db cr,lf,'This program requires DOS 2.0 or above$' erms35 db cr,lf,'Must specify program name$' erms37 db cr,lf,'Unable to execute program$' tmsg5 db cr,lf,'[closing log file]',cr,lf,'$' badnam db cr,lf,'?Protected or no such file(s).$' filmsg db ' File specification with optional path name $' pthmsg db ' Name of new working directory$' tophlp db cr,lf db ' Bye (to remote server) ' db ' Logout (to remote server)' db cr,lf db ' C or Connect (become a terminal) ' db ' Output text (for scripts)' db cr,lf db ' Clear (clear serial port buf) ' db ' Pause [seconds] (for scripts)' db cr,lf db ' Close (logging file) ' db ' Push (go to DOS)' db cr,lf db ' Comment (text is ignored) ' db ' Quit (leave Kermit)' db cr,lf db ' CWD (change dir &/or disk) ' db ' R or Receive (opt local filename)' db cr,lf db ' Define (a command macro) ' db ' Remote (prefix for commands)' db cr,lf db ' Delete (a file) ' db ' Run (a program)' db cr,lf db ' Directory ' db ' S or Send (local file new name)' db cr,lf db ' Disable (selected server commands)' db ' Server (become a local server)' db cr,lf db ' Do (a macro) ' db ' Set (most things)' db cr,lf db ' Echo text (show line on screen) ' db ' Show (various definitions)' db cr,lf db ' Enable (selected server commands)' db ' Space (left on current disk)' db cr,lf db ' Exit (leave Kermit) ' db ' Status (show main conditions)' db cr,lf db ' Finish (to remote server) ' db ' Stay (in Kermit after startup)' db cr,lf db ' Get (remote file opt new name)' db ' Take (do a command file)' db cr,lf db ' Hangup (drop DTR, hangs up phone)' db ' Transmit filespec [pmpt] (raw upload)' db cr,lf db ' Help (show this list) ' db ' Type (a file)' db cr,lf db ' Input [timeout] text (for scripts)' db ' Version (show Kermit',27H,'s id)' ; 27H = single quote db cr,lf db ' Log (Packet or Session to file)' db cr,lf,'$' comtab db 43 ; COMND tables mkeyw 'Bye',bye mkeyw 'C',telnet mkeyw 'Clear',scclr mkeyw 'Close',clscpt mkeyw 'Comment',rskp mkeyw 'Connect',telnet mkeyw 'CWD',cwdir mkeyw 'Define',dodef mkeyw 'Delete',delete mkeyw 'Directory',direct mkeyw 'Disable',srvdsa mkeyw 'Do',docom mkeyw 'Echo',scecho mkeyw 'Enable',srvena mkeyw 'Exit',exit mkeyw 'Finish',finish mkeyw 'Get',get mkeyw 'H',help mkeyw 'Hangup',dtrlow mkeyw 'Help',help mkeyw 'Input',scinp mkeyw 'Log',setcpt mkeyw 'Logout',logout mkeyw 'Output',scout mkeyw 'Pause',scpau mkeyw 'Push',dopush mkeyw 'Quit',exit mkeyw 'R',read mkeyw 'Receive',read mkeyw 'Remote',remote mkeyw 'Run',run mkeyw 'S',send mkeyw 'Send',send mkeyw 'Server',server mkeyw 'Set',setcom mkeyw 'Show',showcmd mkeyw 'Space',chkdsk mkeyw 'Status',status mkeyw 'Stay',rskp ; this command does nothing. mkeyw 'Take',take mkeyw 'Transmit',scxmit mkeyw 'Type',typec mkeyw 'Version',prvers shotab db 5 mkeyw 'Key',shokey mkeyw 'Macros',shomac mkeyw 'Modem',shomodem mkeyw 'Statistics',shosta mkeyw 'Translation',shorx ; Program storage. ssave dw ? ; Original SS when doing Command.com in3ad dw 0,0 ; Original break interrupt addresses ceadr dd 0 ; DOS Critical Error interrupt address curdsk db 0 ; Current disk. origd db 0 ; Original disk. orgdir db 64 dup (?) ; original directory on original disk drives db ? ; number of disk drives on system taklev db 0 ; Take levels takadr dw takstr-(size takinfo) ; Pointer into structure takstr db (size takinfo) * maxtak dup(?) psp dw ? ; segment of Program Segment Prefix imxtry db defmxtry ; Retry limit for I packet send/rcv maxtry db defmxtry ; Retry limit for data packet send/rcv ininm2 db 'MSKERMIT.INI',0 ; init file name for 2.0 filtst filest <> ; file structure for procedure isfile exearg dw ? ; segment addr of environment (filled in below) dd 0 ; ptr to cmd line (filled in below) dw 5ch,0,6ch,0 ; our def fcb's; segment filled in later delcmd db ' del ',0 ; delete command dircmd db ' dir ',0 ; directory command chkdcmd db 'chkdsk.com',0 ; space command typcmd db ' type ',0 ; type command dosnum db ? ; dos version number pthnam db 'PATH=' ; Path environment variable pthlen equ $-pthnam ; length of that string pthadr dw 0 ; offset of PATH= string slashc db ' /c ' ; slashc Must directly preceed tmpbuf tmpbuf db 65 dup (?) ; temp space for file names. cmspnam db 'COMSPEC=' ; Environment variable cmsplen equ $-cmspnam cmspbuf db '\command.com',30 dup (0) ; default name plus additional space eexit db cr,'exit',cr leexit equ $-eexit mfmsg db '?Not enough memory to run Kermit$' mf7msg db '?Attempted to allocate a corrupted memory area$' netdone dw 0 ; network closedown call pointer errlev db 0 ; DOS errorlevel to be returned datas ends ; End data segment code segment public 'code' 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, dtrlow:near extrn clscpi:near, clscpt:near, getbaud:near extrn dodef:near, setcpt:near, docom:near, shomodem:near extrn server:near, lclini:near, shokey:near, shomac:near, shosta:near extrn packlen:near, strlen:near, strcpy:near extrn strcat:near, prtasz:near, shorx:near extrn scout:near,scinp:near,scpau:near,scecho:near,scclr:near extrn scxmit:near, srvdsa:near, srvena:near assume cs:code, ds:datas, ss:cstack, es:nothing START PROC FAR mov ax,datas ; Initialize DS. mov ds,ax mov psp,es ; remember psp address mov ah,dosver int dos mov dosnum,al ; remember dos version cmp dosnum,2 ; earlier than DOS 2.0? jge start1 ; ge = no mov ah,prstr mov dx,offset erms34 ; Complain. int dos mov ax,psp ; set up exit for DOS 1 push ax ; push the segment mov ax,0 ; and the IP push ax ; make return addr of psp:0 for DOS 1 ret ; and return far to exit now start1: 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 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 si,offset orgdir ; place for directory path w/o drive code add al,'A'-1 ; make al alphabetic disk drive again mov [si],al ; put it into original path descriptor inc si mov byte ptr [si],':' ; add drive specifier too inc si mov byte ptr [si],'\' ; add root indicator as well inc si mov ah,gcd ; get current directory (path really) xor dl,dl ; use current drive int dos call getpath ; get the path from the environment call getcsp ; get comspec from environment call memini ; init our memory usage call lclini ; do local initialization call getbaud ; Get the baud rate. call dodisk ; See how many disk drives we have. call packlen ; Packet length in case do server comand. mov ah,gswitch mov al,0 ; pick up switch character int dos mov slashc+1,dl mov al,maxtry ; limit # packet retries and al,3fh ; 63 max mov maxtry,al shl al,1 ; times two. I packets get more tries. mov imxtry,al ; keep that much add al,maxtry ; try three times js start2 ; s = sign bit set, too large mov imxtry,al ; imxtry = 3 * maxtry start2: call gcmdlin ; read command line cmp taklev,0 ; in a Take file? jne start3 ; ne = yes, skip help msg mov ah,prstr mov dx,offset hlpmsg int dos start3: call rdinit ; read kermit init file ; 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 time. and flags.remflg,not dserver ; turn off server mode bit 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 short kermt5 ; Do it again. kermt2: mov dx,offset ermes1 ; Say Unrecognized Command jmp short kermt4 kermt3: mov dx,offset ermes3 ; Say Not Confirmed. kermt4: mov ah,prstr ; print the error message in dx int dos kermt5: cmp flags.cxzflg,'C' ; user Control-C abort? jne kermt7 ; ne = no, do normal operations cmp taklev,0 ; in a Take file? je kermt7 ; e = no mov bx,takadr ; structure of current Take cmp byte ptr [bx].taktyp,0ffh ; type of Take (file or macro) je kermt6 ; e = macro, do not try to close it mov bx,word ptr [bx].takhnd ; Take file handle mov ah,close2 ; DOS 2 file close int dos ; close the Take file kermt6: dec taklev ; readjust Take level sub takadr,size takinfo ; readjust Take buffer kermt7: mov flags.nmoflg,0 ; Reset filename override flag. mov flags.getflg,0 ; May as well do this one. jmp kermit ; get next command krmend: call serrst ; Just in case the port wasn't reset test flags.capflg,0FFH ; Logging active? jz krmend1 ; z = no mov dx,offset tmsg5 mov ah,prstr int dos call clscpi ; close log file nop ; this skip returns... nop nop krmend1:mov dl,origd ; Original disk drive. dec dl ; Want A == 0. mov ah,seldsk ; Reset original disk just in case. int dos mov dx,offset orgdir ; restore original directory mov ah,chdir int dos mov dx,offset in3ad ; restore Control-C interrupt vector mov al,23H ; interrupt 23H mov ah,25H ; set interrupt vector int dos ; ah, that's better. mov dx,offset ceadr ; DOS's Critical Error handler mov al,24h ; interrupt 24h mov ah,25h ; do replacement (put it back) int dos cmp netdone,0 ; does network cleanup pointer exist? je krmend2 ; e = no, so ignore it call netdone ; call the vector to shut network connection krmend2: mov ah,4cH ; terminate process mov al,errlev ; return error level int dos ret START ENDP ; change working directory cwdir proc near mov ah,cmfile mov dx,offset tmpbuf mov bx,offset pthmsg call comnd jmp r mov dx,offset tmpbuf mov ah,chdir int dos jnc cwd1 mov dx,offset ermes4 mov ah,prstr int dos jmp rskp cwd1: mov bx,dx ; change of drives, if req'd cmp byte ptr [bx+1],':' ; was a drive specified? jne cwd3 ; ne = no mov dl,[bx] ; get the drive letter and dl,5FH ; make upper case sub dl,'A' ; convert to A = 0, etc mov ah,seldsk int dos ; change disks inc dl ; count A = 1 internally mov curdsk,dl ; and store it cwd3: jmp rskp cwdir 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 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 ; Replace Int 23h and Int 24h with our own handlers. ; Revised to ask DOS for original interrupt vector contents, as suggested by ; Jack Bryans. 9 Jan 1986 jrd ; Modified again 30 August 1986 [jrd] SETINT PROC NEAR push es ; save registers push bx mov al,23H ; desired interrupt vector (^C) mov ah,35H ; Int 21H, function 35H = Get Vector. int dos ; get vector in es:bx mov in3ad,bx ; save offset address of original vector mov in3ad+2,es ; and its segment. mov al,24h ; DOS critical error, Int 24h mov ah,35h int dos mov word ptr ceadr,bx ; DOS's Critical Error handler, offset mov word ptr ceadr+2,es ; and segment address. push ds ; save ds around next DOS call. mov ax,cs ; compose full address of ^C routine. mov ds,ax ; Offset is the code segment. mov dx,offset intbrk ; and main address is intbrk. mov al,23H ; On ^C, goto intbrk. mov ah,25H ; set interrupt address from ds:dx int dos mov dx,offset dosce ; replacement Critical Error handler mov al,24h ; interrupt 24h mov ah,25h ; replace it int dos pop ds pop bx pop es ret SETINT ENDP ; TAKE commands from a file, and allow a path name TAKE PROC NEAR cmp taklev,maxtak ; Hit our limit? jl take1 ; Continue if still OK. mov ah,prstr mov dx,offset erms30 ; Complain. int dos ret take1: mov di,takadr add di,size takinfo push di mov ah,cmfile lea dx,[di].takbuf ; convenient place to parse name into mov bx,offset filmsg ; Help in case user types "?". call comnd pop di ret nop pop di ; restore frame address cmp ah,0 je take2 ; empty, complain. 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 take2 ; 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 take3 ; open ok, keep going take2: mov ah,prstr mov dx,offset erms31 int dos ret take3: inc taklev mov takadr,di mov word ptr [di].takhnd,ax ; save file handle mov byte ptr [di].taktyp,0feh ; mark as 2.0 file handle 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 ; echoing commands? je take4 ; e = no 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 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].takhnd ; file handle is stored here int dos pop bx ; restore frame address jnc takrd2 ; nc = successful read mov ax,0 ; error, say zero bytes read takrd2: mov [bx].takchl,al ; number of bytes read lea ax,[bx].takbuf mov [bx].takptr,ax pop dx pop cx pop bx ret TAKRD ENDP ; Put offset of PATH= string in pthadr. getpath proc near push bx push cx push dx mov bx,offset pthnam ; thing to find mov cx,pthlen ; length of it mov pthadr,0 ; init offset to zero call getenv ; get environment value mov pthadr,dx pop dx pop cx pop bx ret ; and return getpath endp ; copy COMSPEC= environment string into cmspbuf getcsp proc near push bx push cx push dx push es mov bx,offset cmspnam ; find COMSPEC= mov cx,cmsplen ; it's length call getenv ; get environment offset in dx mov di,offset cmspbuf ; where to store string mov si,dx ; address of COMSPEC= string mov es,psp mov bx,es:word ptr[env] ; pick up environment address mov es,bx push ds ; save ds push ds ; make ds point to environment seg push es ; make es point to datas segment pop ds pop es cld getcs1: lodsb ; get a byte from environment cmp al,' ' ; space or less? jg getcs2 ; g = no, keep copying mov al,0 ; terminate string on spaces etc getcs2: stosb ; store it in cmspbuf or al,al ; at end of string yet? jne getcs1 ; ne = no, keep copying pop ds ; recover ds pop es pop dx pop cx pop bx ret ; and return getcsp endp ; Locate string variable in Environment. ; bx/ variable to find (incl =), cx/ length of variable name, ; dx/ address to store value at. getenv proc near push ax push cx push si push di push es mov es,psp mov ax,es:word ptr[env] ; pick up environment address mov es,ax mov di,0 ; start at this offset in segment geten1: cmp es:byte ptr [di],0 ; end of environment? je geten4 ; yes, forget it push cx ; save counter push di ; and offset mov si,bx cld repe cmpsb ; search for name 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 geten4: mov dx,di ; store offset of string pop es pop di pop si pop cx pop ax 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 ax,offset ininm2 ; default name to try cmp decbuf,0 ; alternate init file given? je rdini1 ; ne = no mov ax,offset decbuf ; yes, use it rdini1: push bx call spath ; can we find it? pop di jc rdini6 ; no, forget 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 inc taklev ; bump take level add takadr,size takinfo mov di,takadr ; get current frame ptr mov word ptr [di].takhnd,ax ; save file handle mov byte ptr [di].taktyp,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 cmp flags.takflg,0 ; echo Take files? je rdini3 ; e = no mov ah,prstr mov dx,offset crlf int dos rdini3: call takrd ; Get a buffer full of data. rdini6: ret ; no init file, just return rdinit endp ; Get command line into a Take macro buffer. Allow "-f filspec" to override ; normal mskermit.ini initialization filespec, allow command "stay" to ; suppress automatic exit to DOS at end of command line execution. [jrd] gcmdlin proc near mov word ptr decbuf,0 ; storage for new init filename push es cld mov es,psp ; address psp mov ch,0 mov cl,es:byte ptr[cline] ; length of cmd line from DOS jcxz gcmdl1 ; z = empty line mov si,cline+1 ; point to actual line gcmdl0: cmp byte ptr es:[si],' ' ; skip over leading whitespace ja gcmdl2 ; a = non-whitespace inc si loop gcmdl0 ; fall through on all whitespace gcmdl1: jmp gcmdl14 ; common exit jump point gcmdl2: inc cx ; include DOS's c/r inc taklev ; bump take level add takadr,size takinfo ; address new take frame mov bx,takadr mov byte ptr [bx].taktyp,0ffh ; mark as a macro mov [bx].takchl,0 ; chars remaining in macro mov [bx].takcnt,0 ; and all chars in macro lea di,[bx].takbuf ; point at text field of take buffer mov ax,ds mov dx,es ; swap ds and es mov es,ax mov ds,dx ; ds = PSP, es = datas gcmdl3: cmp cx,0 ; anything left? jbe gcmdl10 ; be = no lodsb ; get a byte dec cx ; one less char in input string cmp al,',' ; comma? jne gcmdl4 ; no, keep going mov al,cr ; convert to cr jmp gcmdl9 ; store it gcmdl4: cmp al,'-' ; starting a flag? jne gcmdl9 ; ne = no mov ah,byte ptr[si] ; get flag letter or ah,20h ; convert to lower case cmp ah,'f' ; 'f' for init file replacement? jne gcmdl9 ; ne = no mov ah,byte ptr[si+1] ; need space or tab separator cmp ah,' ' ; separator? ja gcmdl9 ; ne = no, not a flag ; strip out and analyze flag info inc si ; point at separator dec cx gcmdl5: cmp cx,0 ; anything to read? jle gcmdl10 ; le = exhausted supply lodsb ; get filespec char from psp dec cx ; one less char in source buffer cmp al,' ' ; in whitespace? jbe gcmdl5 ; be = yes, scan it off dec si ; backup to real text inc cx ; copy filspec to buffer decbuf push di ; save current destination pointer mov di,offset decbuf ; where filespec part goes mov word ptr es:[di],0 ; plant safety terminator gcmdl6: lodsb ; get filespec char dec cx ; one less available cmp cx,0 ; any chars left? jle gcmdl7 ; le = no cmp al,' ' ; in printables? jbe gcmdl7 ; be = no, all done cmp al,',' ; comma command separator? je gcmdl7 ; e = yes, all done stosb ; store filespec char mov byte ptr es:[di],0 ; end filespec on a null jmp short gcmdl6 gcmdl7: pop di ; recover macro register dec si ; compensate for last read above inc cx gcmdl8: cmp cx,0 ; strip trailing whitespace jbe gcmdl10 ; be = nothing left lodsb dec cx cmp al,' ' ; white space? jbe gcmdl8 ; be = yes, strip it cmp al,',' ; at next command? je gcmdl10 ; e = yes, skip our own comma dec si ; back up to reread the char inc cx jmp gcmdl3 ; read more command text ; end of flag analysis gcmdl9: stosb ; deposit most recent char gcmdl10:cmp cx,0 ; anything left to read? jg gcmdl3 ; g = yes, loop ; mov ax,datas ; restore segment registers mov ds,ax mov es,ax ; return to ds=datas, es=datas lea si,[bx].takbuf ; get address of text field mov cx,di ; current end pointer, (save di) sub cx,si ; current ptr minus start offset mov [bx].takchl,cl ; count chars mov [bx].takcnt,cx cmp cx,0 jg gcmdl11 ; material at hand dec taklev ; nothing to take sub bx,size takinfo ; so reset Take info to say none mov takadr,bx jmp gcmdl14 ; and exit. ; scan for command "stay" gcmdl11:lodsb ; get a byte, cx and si are set above dec cx cmp al,' ' ; separator? jbe gcmdl12 ; be = yes, keep looking mov ah,al ; get first byte lodsb ; second byte after separator dec cx or ax,2020h ; convert to lower case cmp ax,'st' ; first two letters of stay jne gcmdl12 ; ne = no match lodsw ; next two letters (stay vs status) sub cx,2 or ax,2020h ; convert to lower case cmp ax,'ya' ; same as our pattern? jne gcmdl12 ; ne = no match ; check for separator or end of macro cmp cx,0 ; at end of macro? jle gcmdl13 ; yes, consider current match correct cmp byte ptr[si],' ' ; next char is a separator? jbe gcmdl13 ; be = yes, found correct match gcmdl12:cmp cx,0 ; done yet? ("stay" not found) jg gcmdl11 ; g = not yet, look some more mov si,offset eexit ; append command "exit" mov cx,leexit ; length of string "exit" add [bx].takchl,cl add [bx].takcnt,cx rep movsb ; copy it into the macro gcmdl13:lea ax,[bx].takbuf ; update Take info mov [bx].takptr,ax ; init buffer ptr mov [bx].takcnt+2,0 ; clear high order gcmdl14:pop es 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). Add protection of ignore hidden, subdir, volume ; label and system files. 9 Jan 86 [jrd] DELETE PROC NEAR ; revised for DOS 2.0, incl paths & ?* [jrd] mov si,offset delcmd ; del command mov di,offset tmpbuf call strcpy mov dx,offset tmpbuf call strlen ; get its length add di,cx ; point at terminator mov ah,cmtxt ; parse with cmtxt so we can have paths mov bx,di ; where to place the file specs mov dx,offset filmsg ; In case user wants help. call comnd jmp r mov byte ptr [bx],0 ; plant terminator mov dx,offset delcmd ; get length of prefix (del ) call strlen mov ax,offset tmpbuf ; command line so far add ax,cx ; bump address to filename field call isfile ; and ask if file exists & what kind it is jc delet2 ; c = no such file, complain test byte ptr filtst.dta+21,1EH; attribute bits: is file protected? jz delet3 ; z = no. go ahead. delet2: mov ah,prstr mov dx,offset badnam ; give error message int dos jmp rskp ; and ignore this command delet3: mov si,offset tmpbuf ; del cmd jmp crun ; join run cmd from there. DELETE ENDP CHKDSK PROC NEAR ; Space command (use Chkdsk.com) mov ah,cmcfm call comnd jmp r mov si,offset chkdcmd ; point to cmd jmp crun ; and go execute it nicely CHKDSK ENDP ; Get directory listing. DIRECT PROC NEAR mov si,offset dircmd ; dir command mov di,offset tmpbuf call strcpy mov dx,offset tmpbuf call strlen ; get its length add di,cx ; point at terminator mov ah,cmtxt ; parse with cmtxt so we can have paths mov bx,di ; next available byte mov dx,offset filmsg ; In case user wants help. call comnd jmp r mov byte ptr [bx],0 ; plant terminator mov si,offset tmpbuf jmp crun ; join run cmd from there. DIRECT ENDP ; the version command - print our version number prvers proc near mov ah,cmcfm call comnd jmp r mov ah,prstr mov dx,offset crlf int dos 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 jmp rskp prvers endp ; the type command - type out a file typec proc near mov si,offset typcmd ; type command mov di,offset tmpbuf call strcpy mov dx,offset tmpbuf call strlen ; get its length add di,cx ; point at terminator mov ah,cmtxt ; parse with cmtxt so we can have paths mov bx,di ; next available byte mov dx,offset filmsg ; In case user wants help. call comnd jmp r mov byte ptr [bx],0 ; plant terminator mov si,offset tmpbuf jmp crun ; join run cmd from there. typec endp ; PUSH to DOS (run another copy of Command.com or equiv) ; entry fpush (fast push...) pushes without waiting for a confirm. ; returns rskp. dopush proc near dopus1: mov ah,cmcfm call comnd jmp r fpush: mov si,offset tmpbuf ; a dummy buffer mov byte ptr [si],0 ; plant terminator jmp short crun4 ; go run it dopush endp ; Run a program from within Kermit. RUN PROC NEAR mov ah,cmtxt ; Get program name and any arguments. mov bx,offset tmpbuf ; place for user's text mov dx,offset filmsg ; In case user wants help. call comnd nop nop nop cmp ah,0 ; byte count jne run2 ; ne = have program name mov ah,prstr ; else complain mov dx,offset erms35 int dos jmp rskp run2: mov si,offset tmpbuf ; source of text jmp crun RUN ENDP ; crun - run an arbitrary program. Rewritten by [jrd] ; Enter with ordinary asciiz command in si (such as Dir *.asm). ; Append a c/r and a null terminator and then ask command.com to do it. CRUN proc near mov ah,prstr ; output crlf before executing comnd. [lba] mov dx,offset crlf ; [lba] int dos ; display it. [lba] mov di,offset tmpbuf ; where to put full command line text cmp si,di ; same place? je crun1 ; e = yes, don't copy ourself call strcpy ; si holds source text crun1: mov si,offset slashc ; DOS command begins with slashc area mov dx,offset slashc+1 ; si points to /c part of command line call strlen ; get its length into cx push bx mov bx,dx add bx,cx mov byte ptr [bx],cr ; end string with a c/r for dos. inc cx ; count the c/r mov byte ptr [bx+1],0 ; and terminate pop bx mov [si],cl ; put length of argument here crun4: mov exearg+2,si ; pointer to argument string mov exearg+4,ds ; segment of same mov es,psp ; point to psp again mov exearg+8,es ; segment of psp, use our def fcb's mov exearg+12,es ; segment of psp, ditto, for fcb 2. mov ax,es:word ptr [env] ; get environment ptr mov exearg,ax ; put into argument block mov ax,ds mov es,ax ; put es segment back mov bx,offset exearg ; es:bx points to exec parameter block mov dx,offset cmspbuf ; always use command.com mov al,0 ; load and execute... mov ah,exec mov ssave,sp ; save stack ptr int dos ; go run the program mov ax,datas mov ds,ax ; reset data segment mov es,ax ; and extra segment mov ax,cstack mov ss,ax ; and stack segment mov sp,ssave ; restore stack ptr pushf ; save flags mov ah,setdma mov dx,offset buff int dos ; restore dma address!! popf ; recover flags jc crun8 ; c = error, handle. jmp rskp ; ok, return crun8: mov ah,prstr mov dx,offset erms37 int dos jmp rskp CRUN 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 ; Control Break, Interrupt 23h replacement. ; Always return with a Continue (vs Abort) condition since Kermit will cope ; with failures. [jrd] intbrk: push ax push ds mov ax,datas ; get Kermit's data segment. 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 ; return to caller in a Continue condition. ; Kermit's DOS Critical Error Handler, Int 24h. [jrd] ; Needed to avoid aborting Kermit with the serial port interrupt active and ; the Control Break interrupt redirected. See the DOS Tech Ref Manual for ; a start on this material; it is neither complete nor entirely accurate. ; The stack is the Kermit's stack, the data segment is unknown, interrupts ; are off, and the code segment is Kermit's. Note: some implementations of ; MS DOS may leave us in DOS's stack. Called by a DOS Int 21h function. dosce: test ah,80h ; block device (disk drive)? jnz dosce1 ; nz = no; serial device, memory, etc. mov al,3 ; tell DOS to Fail the Int 21h call iret ; return to DOS dosce1: add sp,6 ; pop IP, CS, Flags regs, from DOS's Int 24h. pop ax ; restore original callers regs existing pop bx ; just before doing Int 21h call. pop cx pop dx pop si pop di pop bp pop ds pop es mov al,0ffh ; signal failure (usually) the DOS 1.x way. push ax ; Kermit's IP, CS, and Flags are on the stack push bp ; all ready for an iret, but first a word ... mov bp,sp mov ax,ss:[bp+8] ; get Kermit's flags word or ax,1 ; set the carry bit, signal failure DOS 2+ way mov ss:[bp+8],ax ; store new flags back in the stack. pop bp ; this avoids seeing the Interrupt flag bit pop ax iret ; return to user, simulate return from Int 21h ; 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. SPATH proc near mov bx,ax ; convenient place to keep this call isfile ; does it exist as it is? mov ax,bx ; if so, just return original name jc spath0 ; c = nope, prepend path elements ret spath0: push es ; save es around work mov si,ax mov dl,0 ; no '\' seen yet cld spath1: lodsb cmp al,2fh ; contains fwd slash path characters? je spath1a cmp al,5ch ; or backslash? jne spath2 ; no, keep going spath1a: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, keep looking jmp spath9 ; embedded path, fail spath3: mov si,pthadr ; offset of PATH= string in environment mov es,psp mov ax,es:word ptr[env] ; pick up environment segment mov es,ax spath4: cmp byte ptr es:[si],0 ; end of PATH= string? je spath9 ; e = yes, exit loop mov di,offset tmpbuf ; place to put name spath5: mov al,byte ptr es:[si] ; get a byte from environment string inc si 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 to null for later rereading jmp short spath7 ; and break loop spath6: mov byte ptr [di],al ; else stick in dest string inc di jmp spath5 ; and continue spath7: push si ; save this ptr mov si,bx ; this is user's file name cmp byte ptr [di-1],2fh ; does path end with switch char? je spath8 ; yes, don't put one in cmp byte ptr [di-1],5ch ; how about this one? je spath8 ; yes, don't put it in. mov byte ptr [di],5ch ; else add one inc di spath8: lodsb ; get filename character mov byte ptr [di],al ; copy filename char to tmpbuf buffer inc di or al,al ; end of string? jnz spath8 ; nz = no, copy rest of name pop si ; restore postion in path string mov ax,offset tmpbuf call isfile ; is it a file? jc spath4 ; c = no, keep looking pop es ret ; return success (carry off) spath9: pop es ; restore this stc ; no file found ret spath endp ISFILE PROC NEAR ; Enter with ds:ax pointing at asciiz filename string. ; Returns carry set if the file pointed to by ax does not exist, else reset. ; Returns status byte, fstat, with DOS status and high bit set if major error. ; Does a search-for-first to permit paths and wild cards. ; Examines All kinds of files (ordinary, subdirs, vol labels, system, ; and hidden). Upgraded to All kinds on 27 Dec 1985. Revised 30 Aug 86 [jrd] ; All registers are preserved. push dx ; save regs push cx push ax mov byte ptr filtst.dta+21,0 ; clear old attribute bits mov byte ptr filtst.fname,0 ; clear any old filenames mov filtst.fstat,0 ; clear status byte mov cx,3fH ; look at all kinds of files mov dx,offset filtst.dta ; own own temporary dta mov ah,setdma ; set to new dta int dos pop dx ; get ax (filename string ptr) push dx ; save it again mov ah,first2 ; search for first int dos pushf ; save flags mov dx,offset buff ; reset dma mov ah,setdma int dos popf ; recover flags jnc isfil1 ; nc = file found mov filtst.fstat,al ; record DOS status cmp al,2 ; just "File Not Found"? je isfil2 ; e = yes cmp al,3 ; "Path not found"? je isfil2 ; e = yes cmp al,18 ; "No more files"? je isfil2 ; e = yes or filtst.fstat,80h ; set high bit for more serious error jmp isfil2 isfil1: cmp byte ptr filtst.fname,0 ; did DOS fill in a name? jne isfil3 ; nz = yes isfil2: stc ; else set carry flag bit isfil3: pop ax pop cx pop dx ret ; DOS sets carry if file not found. ISFILE ENDP ; initialize memory usage by returning to DOS anything past the end of kermit memini proc near push es mov es,psp ; address psp segment again mov bx,offset msfinal + 15 ; end of pgm + roundup mov cl,4 shr bx,cl ; compute # of paragraphs in last seg mov ax,datas ; last segment sub ax,psp ; minus beginning add bx,ax ; # of paragraphs occupied mov ah,setblk int dos jc memin1 pop es ret memin1: pop es mov dx,offset ermes2 mov ah,prstr int dos ; complain jmp krmend ; and just exit... memini endp ; Allocate memory. Passed a memory size in ax, allocates that many ; bytes (actually rounds up to a paragraph) and returns its SEGMENT in ax. ; The memory is NOT initialized. Written by [jrd] to allow memory to ; be allocated anywhere in the 1MB address space. sbrk proc near ; K & R, please forgive us. mov bx,ax ; bytes wanted add bx,15 ; round up mov cl,4 shr bx,cl ; convert to # of paragraphs mov ah,alloc ; DOS memory allocator int dos jc sbrkx ; c = fatal ret ; and return segment in ax sbrkx: mov dx,offset mfmsg ; assume not enough memory (ax = 8). cmp ax,7 ; corrupted memory (ax = 7)? jne sbrkx1 ; ne = no. mov dx,offset mf7msg ; corrupted memory found sbrkx1: mov ah,prstr int dos jmp krmend ; exit Kermit now. sbrk 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