NAME mssker ; File MSSKER.ASM include mssdef.h ; Edit history: ; Last edit 16 Jan 1990 ; 16 June Add SHELL= support for PUSH as suggested by Mike Brown, Purdue ; University Computing Center. ;****************************** Version 3.00 ***************************** ; KERMIT, Celtic for "free" ; ; The name "Kermit" is a registered trade mark of Henson Associates, Inc., ; used by permission. ; ; Kermit-MS Program Version 3.00, 16 Jan 1990 ; Kermit-MS Program Version 2.32, 11 Dec 1988 ; Kermit-MS Program Version 2.31, 1 July 1988 ; 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, 1989, 1990 ; 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, 2.31, 2.32, 3.00) ; Dept of EE, and CASS ; Utah State University ; Logan, Utah 84322 ; jrd@cc.usu.Bitnet ; ; Special thanks to Frank da Cruz, Bill Catchings, Steve Jensen, Herm Fischer, ; Vace Kundakci, Bernie Eiben and many many others for their help and ; contributions. public dosnum, curdsk, fpush, isfile, sbrk, crun, errlev public takrd, takadr, taklev, filtst, maxtry, dskspace, thsep public lclsusp, lclrest, lclexit, cwdir, kstatus, verident, cdsr public spath, patched env equ 2CH ; environment address in psp cline equ 80H ; offset in psp of command line STACK SEGMENT PARA STACK 'STACK' ; our stack dw 200 dup(0) ; initialize stack to all zeros msfinal label word ; top of stack STACK ENDS data segment public 'data' extrn buff:byte, comand:byte, flags:byte, trans:byte, prmptr:word extrn machnam:byte, decbuf:byte, rstate:byte, sstate:byte verident label byte verdef db '$' copyright db cr,lf db 'Copyright (C) Trustees of Columbia University 1982, 1990.' db cr,lf,'$' hlpmsg db cr,lf,'Type ? or HELP for help',cr,lf,'$' crlf db cr,lf,'$' ermes1 db cr,lf,'?More parameters are needed$' ermes2 db cr,lf,'?Unable to initialize memory$' ermes3 db cr,lf,'?Command canceled$' ermes4 db '?Unable to change directory',0 ; asciiz ermes5 db cr,lf,'?Unable to complete initialization process$' ermes6 db cr,lf,'Ignoring patch file MSKERMIT.PCH. ' db 'Version number mismatch.',cr,lf,'$' ermes7 db cr,lf,'Patch file MSKERMIT.PCH was not found',cr,lf,'$' ermes8 db cr,lf,'Fatal error in MSKERMIT.PCH! Please remove PATCH ' db 'command.',cr,lf,'$' erms30 db cr,lf,'?Passed maximum nesting level for TAKE command$' erms31 db cr,lf,'?Cannot find Take-file: $' erms34 db cr,lf,'This program requires DOS 2.0 or above$' erms37 db cr,lf,'?Unable to execute program$' badnam db cr,lf,'?No such file(s)$' filmsg db ' Filename$' dskmsg db ' disk drive letter or Return$' pthmsg db ' Name of new working directory and/or disk$' runmsg db ' program name and command line$' tophlp db cr,lf db ' Ask, Askq (read keybd to variable) ' db ' Output text (for scripts)' db cr,lf db ' Bye (logout remote server) ' db ' Pause [seconds] (for scripts)' db cr,lf db ' C or Connect (become a terminal) ' db ' Pop (exit current Take file or macro)' db cr,lf db ' Clear (clear serial port buffer)' db ' Push (go to DOS, keep Kermit)' db cr,lf db ' Close (logging file) ' db ' Quit (leave Kermit)' db cr,lf db ' CWD or CD (change dir &/or disk) ' db ' R or Receive (opt local filename)' db cr,lf db ' Define/Assign (a command macro) ' db ' Reinput (script Input, reread buffer)' db cr,lf db ' Delete (a file) ' db ' Remote (prefix for commands)' db cr,lf db ' Directory ' db ' Replay (file through term emulator)' db cr,lf db ' Disable (selected server commands)' db ' Run (a program)' db cr,lf db ' Do (a macro) ' db ' S or Send local file new name' db cr,lf db ' Echo text (show line on screen) ' db ' Server [timeout] (become a server)' db cr,lf db ' Enable (selected server commands)' db ' Set (most things)' db cr,lf db ' EXIT (leave Kermit) ' db ' Show (most things)' db cr,lf db ' Finish (to remote server) ' db ' Space (free on current disk)' db cr,lf db ' Get (remote file opt new name)' db ' Status (show main conditions)' db cr,lf db ' Goto (label, Take file or Macro)' db ' Stop (exit all Take files & macros)' db cr,lf db ' Hangup (drop DTR, hang up phone) ' db ' Take (do a command file)' db cr,lf db ' If [not] ' db ' Transmit filespec [prompt] (raw upload)' db cr,lf db ' I or Input [timeout] text (scripts)' db ' Type (a file)' db cr,lf db ' Log (Packet, Session, Transaction) ' db ' Wait [timeout] on modem \cd \cts \dsr' db cr,lf db ' Logout (remote server) ' db ' Write ' db cr,lf db ' Mail (file to host Mailer)' db cr,lf db ' Type HELP for an Introduction, use "?" within commands for' db ' specific help.$' ;;; db ' Load filespec (file translate tbl) ' ;;; db ' Version (show Kermit''s id)' qckhlp db cr,lf db ' Introduction to MS-DOS Kermit',cr,lf db 'o An MS-Kermit command is a line of words separated by spaces and' db ' ending with',cr,lf,' a carriage return .' db ' Example: SET SPEED 2400',cr,lf db 'o Most words can be abbreviated and can be completed by pressing' db ' the Esc key.',cr,lf db ' Example: SET SPE 24 or even SET SPE 24' db '',cr,lf db 'o Help (detailed, specific): press the "?" key where a word would' db ' appear.',cr,lf db 'o Edit lines using the Backspace key to delete characters,' db ' Control-W to delete',cr,lf db ' words, and Control-U to delete the line. Control-C cancels the' db ' command.',cr,lf db 'o Frequently used MS-Kermit commands:',cr,lf db ' EXIT Leave the Kermit program. QUIT does the same' db ' thing.',cr,lf db ' SET PORT, PARITY, SPEED, TERMINAL and many other' db ' parameters.',cr,lf db ' SHOW or STATUS Display groups of important parameters.' db ' SHOW ? for categories.',cr,lf,lf db ' CONNECT Establish a terminal connection to a remote' db ' system or a modem.',cr,lf db ' Control-$' qckhlp1 db ' C (Control-$' qckhlp2 db ' followed by "C") Return to MS-Kermit> prompt.',cr,lf,lf db ' SEND filename Send the file(s) to Kermit on the other' db ' computer).',cr,lf db ' RECEIVE Receive file(s), SEND them from Kermit on the' db ' other computer.',cr,lf db ' GET filename Ask the remote Kermit server to send the file(s)' db ' to us.',cr,lf db ' FINISH Shut down remote Kermit but stay logged into' db ' remote system.',cr,lf db ' BYE FINISH and logout of remote system and exit' db ' local Kermit.',cr,lf db 'o Common startup sequence: SET SPEED 9600, CONNECT, login, start' db ' remote Kermit,',cr,lf db ' put it into Server mode, escape back with Control-$' qckhlp3 db ' C, transfer' db ' files with',cr,lf db ' SEND x.txt, GET b.txt, BYE. Read more about it in "Using MS-DOS' db ' Kermit".' db cr,lf db 'Press the "?" key for a summary of Kermit commands, or space for' db ' prompt.$' comtab db 59 ; COMND tables mkeyw 'Ask',ask mkeyw 'Askq',askq mkeyw 'Assign',assign mkeyw 'Bye',bye mkeyw 'C',telnet mkeyw 'CD',cwdir mkeyw 'Clear',scclr mkeyw 'Close',clscpt mkeyw 'Comment',comnt 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 'goto',goto mkeyw 'H',help mkeyw 'Hangup',dtrlow mkeyw 'Help',help mkeyw 'If',ifcmd mkeyw 'I',scinp mkeyw 'Input',scinp ;;; mkeyw 'Load',load mkeyw 'Log',setcpt mkeyw 'Logout',logout mkeyw 'Mail',mail mkeyw 'Output',scout mkeyw 'Pause',scpau mkeyw 'Pop',takclos mkeyw 'Push',dopush mkeyw 'Quit',exit mkeyw 'R',read mkeyw 'Receive',read mkeyw 'Reinput',screinp mkeyw 'Remote',remote mkeyw 'Replay',replay 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',stay mkeyw 'Stop',takeqit mkeyw 'Take',take mkeyw 'Transmit',scxmit mkeyw 'Type',typec mkeyw 'Version',prvers mkeyw 'Wait',scwait mkeyw 'Write',write mkeyw ':',comnt ; script labels, do not react mkeyw 'Patch',patch shotab db 13 ; SHOW keyword mkeyw 'Communications',shcom mkeyw 'File',shfile mkeyw 'Key',shokey mkeyw 'Logging',shlog mkeyw 'Macros',shomac mkeyw 'Memory',shmem mkeyw 'Modem',shomodem mkeyw 'Protocol',shpro mkeyw 'Scripts',shscpt mkeyw 'Server',shserv mkeyw 'Statistics',shosta mkeyw 'Terminal',shterm mkeyw 'Translation',shorx ; Kermit initing from Environment nulprmpt db 0 ; null prompt initab db 2 ; Environment phrase dispatch table mkeyw 'INPUT-buffer-length',setinpbuf ; Script INPUT buffer length mkeyw 'Rollback',setrollb ; number of Terminal rollback screens patched db 1 ; 1 = enable patching; 0 = disable or done even lclsusp dw 0 ; address of routine to call when going to DOS lclrest dw 0 ; address of routine to call when returning lclexit dw 0 ; address of routine to call when exiting ssave dw 0 ; Original SS when doing Command.com in3ad dw 0,0 ; Original break interrupt addresses ceadr dd 0 ; DOS Critical Error interrupt address orgcbrk db 0 ; original Control-Break Check state psp dw 0 ; segment of Program Segment Prefix exearg dw 0 ; 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 dosnum dw 0 ; dos version number, major=low, minor=high curdsk db 0 ; Current disk origd db 0 ; Original disk orgdir db 64 dup (0) ; original directory on original disk taklev db 0 ; Take levels takadr dw takstr-(size takinfo) ; Pointer into structure takstr db (size takinfo) * maxtak dup(?) filtst filest <> ; file structure for procedure isfile maxtry db defmxtry ; Retry limit for data packet send/rcv ininm2 db 'MSKERMIT.INI',0 ; init file name for 2.0 ptchnam db 'MSKERMIT.PCH',0 ; patch file name delcmd db ' del ',0 ; delete command dircmd db ' dir ',0 ; directory command typcmd db ' type ',0 ; type command kerenv db 'KERMIT=',0,0 ; Kermit= environment variable, + 2 nulls 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 128 dup (0) ; temp space for file names and comments cmspnam db 'COMSPEC=' ; Environment variable cmsplen equ $-cmspnam cmspbuf db '\command.com',30 dup (0) ; default name plus additional space shellnam db 'SHELL=' ; Environment variable shellen equ $-shellnam shellbuf db 40 dup (0) ; buffer for name 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$' spcmsg db ' bytes available on drive ' spcmsg1 db ' :',cr,lf,0 spcmsg2 db cr,lf,' Drive ' spcmsg3 db ' : is not ready',0 errlev db 0 ; DOS errorlevel to be returned kstatus dw 0 ; command execution status (0 = success) thsep db 0 ; thousands separator totpar dw 0 temp dw 0 data ends code segment public 'code' extrn logout:near, mail:near, load:near extrn bye:near, telnet:near, finish:near, comnd:near, prompt:near extrn read:near, remote:near, send:near, status:near, get:near extrn serrst:near, setcom:near, dtrlow:near extrn clscpi:near, clscpt:near, scpini:near, setrollb:near extrn dodef:near, setcpt:near, docom:near, shomodem:near extrn server:near, lclini:near, shokey:near, shomac:near, shosta:near extrn strlen:near, strcpy:near, shserv:near, initibm:near extrn strcat:near, prtasz:near, shorx:near, lnout:near, lnouts:near extrn scout:near,scinp:near,scpau:near,scecho:near,scclr:near extrn scxmit:near, scwait:near, srvdsa:near, srvena:near extrn shcom:near, shlog:near, shpro:near, shterm:near, shscpt:near extrn shfile:near, takopen:near, takclos:near, ask:near, askq:near extrn assign:near, goto:near, screinp:near, ifcmd:near, write:near extrn setinpbuf:near, shmem:near, replay:near, atoi:near extrn katoi:near assume cs:code, ds:data, ss:stack, es:nothing START PROC FAR mov ax,data ; initialize DS mov ds,ax mov psp,es ; remember psp address mov ah,dosver ; get DOS version number (word) int dos xchg ah,al ; major version to ah mov dosnum,ax ; remember dos version cmp ax,200h ; earlier than DOS 2.0? jge start1 ; ge = no mov ah,prstr mov dx,offset erms34 ; complain int dos push psp ; set up exit for DOS 1 xor ax,ax ; and the IP push ax ; make return addr of psp:0 for DOS 1 ret ; and return far to exit now start1: mov ah,setdma ; set disk transfer address mov dx,offset buff int dos call setint ; ^C, DOS critical error interrupts mov ah,gcurdsk ; get current disk int dos inc al ; make 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 gettsep ; get thousands separator mov ah,gswitch xor al,al ; pick up switch character int dos mov slashc+1,dl and maxtry,3fh ; limit # packet retries call getcsp ; get comspec from environment call getssp ; get shellspec from environment call memini ; initialize our memory usage call getparm ; read "KERMIT=" Environment line jc start1b ; c = fatal error xor cl,cl ; counter, starts at 0 start1a:mov bx,offset kerenv+6 ; append "=" to "KERMIT" mov [bx],cl ; binary digit inc cl add byte ptr [bx],'0' ; to ascii mov byte ptr [bx+1],'=' ; append equals sign call getparm ; read "KERMITn=" Environment line jc start1b ; c = fatal error cmp cl,9 ; done all digits? jbe start1a ; be = no ; call scpini ; initialize script routines jc start1b ; c = fatal error call lclini ; do local initialization cmp flags.extflg,0 ; exit now? je start2 ; e = no start1b:mov ah,prstr ; announce our premature exit mov dx,offset ermes5 ; can't complete initialization int dos jmp krmend ; quit immediately start2: mov comand.cmrprs,offset krmend ; address to go to on reparse mov comand.cmostp,sp ; save for reparse too 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 machnam ; display machine name int dos mov dx,offset verident ; display version header int dos mov dx,offset copyright ; display copyright notice int dos mov dx,offset hlpmsg int dos start3: mov flags.chrset,437 ; find default global char set cmp dosnum,0300h+30 ; DOS version 3.30 or higher? jb start4 ; b = no, no Code Pages mov ax,6601h ; get global Code Page int dos ; bx=active Code Page, dx=boot CP mov flags.chrset,bx ; setup default SET FILE CHAR SET start4: call serrst ; reset serial port (if active) call initibm ; define IBM macro 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 ; convenient safety measure cmp taklev,0 ; keep port open between script cmds jne kermt1 ; ne = in Take or Macro call serrst ; reset serial port for CTTY DOS use kermt1: mov dx,prmptr ; get prompt call prompt ; prompt user, set reparse address mov flags.cxzflg,0 ; reset each time and flags.remflg,not dserver ; turn off server mode bit mov dx,offset comtab mov bx,offset tophlp cmp flags.extflg,0 ; exit flag set? jne krmend ; ne = yes, jump to KRMEND mov comand.cmcr,1 ; allow bare CR's mov ah,cmkey mov comand.impdo,1 ; allow implied "DO macro" call comnd jc kermt3 ; c = failure mov comand.impdo,0 ; only on initial keyword, not here mov comand.cmcr,0 ; no more bare CR's call bx ; call the routine returned in BX jc kermt3 ; c = failure cmp flags.extflg,0 ; exit flag set? jne krmend ; ne = yes, jump to KRMEND jmp short kermt5 ; do idle loop cleanup kermt3: cmp flags.cxzflg,'C' ; got here via Control-C? jne kermt7 ; ne = no cmp flags.extflg,0 ; exit flag set? jne kermt5 ; ne = yes, skip msg, do cleanup mov dx,offset ermes3 ; say command not executed 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 call takclos ; close take file, release buffer jmp kermt5 ; close any other take files kermt7: cmp flags.extflg,0 ; exit flag set? jne krmend ; ne = yes, Exit now jmp kermit ; get next command krmend: call serrst ; just in case the port wasn't reset test flags.capflg,0FFH ; Logging active? jz krmend2 ; z = no call clscpi ; close log files krmend2:cmp lclexit,0 ; sys dependent routines want service? je krmend3 ; e = no mov bx,lclexit ; addr of sys dependent exit routine call bx ; call it krmend3: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 call cbrestore ; restore state of Control-Break Chk mov ah,4cH ; terminate process mov al,errlev ; return error level int dos ret START ENDP ; This is the 'EXIT' command. It leaves KERMIT and returns to DOS EXIT PROC NEAR mov ah,cmeol call comnd ; get a confirm jc exit1 ; c = failure mov flags.extflg,1 ; set the exit-Kermit flag exit1: ret EXIT ENDP ; TAKE commands from a file, and allow a path name TAKE PROC NEAR mov kstatus,0 ; global status, success cmp taklev,maxtak ; at the limit? jl take1 ; l = no mov ah,prstr mov dx,offset erms30 ; complain int dos stc ; failure ret take1: mov dx,offset tmpbuf ; work buffer mov tmpbuf,0 mov bx,offset filmsg ; Help in case user types "?" mov ah,cmword ; get file name call comnd jc take1a ; c = failure mov ah,cmeol call comnd jc take1a ; c = failure mov ax,offset tmpbuf ; point to name again cmp tmpbuf,0 ; empty filespec? jne take2 ; ne = no mov ah,prstr mov dx,offset ermes1 ; say more parameters needed int dos stc take1a: ret ; TAKE2: enter with ax=filename ptr TAKE2: call spath ; is it around? jc take3 ; no, go complain mov dx,ax ; point to name from spath mov ah,open2 ; open file xor al,al ; 0 = open for reading cmp dosnum,300h ; at or above DOS 3? jb take2a ; b = no, so no shared access or al,40h ; open for reading, deny none take2a: int dos jnc take4 ; nc = opened ok, keep going mov ax,dx ; recover filename pointer take3: push ax mov ah,prstr mov dx,offset erms31 int dos pop ax mov dx,ax ; asciiz file name call prtasz ; display it mov kstatus,8 ; global status clc ; we've done all error displays ret ; TAKE4: enter with ax=filename ptr TAKE4: call takopen ; open take buffer in macro space jc take6 ; c = failure push bx mov bx,takadr ; get current frame ptr mov [bx].takhnd,ax ; save file handle mov [bx].taktyp,0feh ; mark as 2.0 file handle pop bx cmp flags.takflg,0 ; echoing Take files? je take5 ; e = no mov ah,prstr mov dx,offset crlf int dos take5: call takrd ; get a buffer full of data clc ; success take6: ret TAKE ENDP TAKRD PROC NEAR push ax push bx push cx push dx mov bx,takadr cmp [bx].taktyp,0feh ; get type of take (file?) jne takrd1 ; ne = no, do not read from disk mov cx,tbufsiz-1 ; # of bytes to read mov ax,[bx].takbuf ; segment of Take buffer push bx ; save frame address mov bx,[bx].takhnd ; file handle is stored here push ds ; save ds mov ds,ax ; set ds to Take buffer segment mov dx,1 ; ds:dx = buffer, skip count byte mov ah,readf2 ; read file int dos pop ds pop bx ; restore frame address jnc takrd2 ; nc = successful read takrd1: xor ax,ax ; error, say zero bytes read takrd2: mov [bx].takcnt,ax ; number of bytes read mov [bx].takptr,1 ; offset of first new character pop dx pop cx pop bx pop ax ret TAKRD ENDP ; TAKE-QUIT (STOP) Exit all Take files immediately but gracefully TAKEQIT PROC NEAR mov ah,cmeol call comnd jnc takqit1 ; nc = success ret takqit1:xor ch,ch mov cl,taklev ; number of Take levels active jcxz takqit2 ; z = none call takclos ; close current Take file jmp short takqit1 ; repeat until all are closed takqit2:clc ; success ret TAKEQIT ENDP ; put mskermit.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 call take2 ; let Take do error msgs clc ; force success ret rdini1: call spath ; is it around? jc rdini2 ; no, ignore file mov dx,ax ; point to name from spath mov ah,open2 ; open file xor al,al ; 0 = open for reading cmp dosnum,300h ; at or above DOS 3? jb rdini1a ; b = no, so no shared access or al,40h ; open for reading, deny none rdini1a:int dos jc rdini2 ; c = no ini file found, ignore call take4 ; use TAKE command to complete work clc ; ignore errors rdini2: ret rdinit endp ; Patcher. Patch file, MSKERMIT.PCH has the following format: ; 300 \Xxxxx ; optional comment. 300 for V3.00. For xxxx, see below ; ; optional comment lines may appear anywhere after the 1st ; ; xxxx in 1st line total paragraphs in memory image. Use \X if hex. ; DS:xxxx xx xx ; optional comment. DS (or CS) are case insensitive ; CS:xxxx xx xx xx ; locations must be 4 hex chars, contents must be 2 ; CS & DS lines may be intermixed. ; This mechanism expects file msscmd.obj to be linked first. PATCH proc mov ah,cmeol ; confirm please call comnd jc ptch2 ; c = no confirm xor ax,ax xchg al,patched ; clear and test patched test al,al jnz ptch2 ; z = disabled or done xchg ah,flags.takflg ; clear take flag -- don't echo patches mov byte ptr temp,ah; but save it call ptchr mov al,byte ptr temp; restore take flag mov flags.takflg,al jnc ptch2 ; nc = OK mov dx,offset ermes8; Fatal error mov ah,prstr int dos jmp krmend ; force exit ptchr: mov ax,offset ptchnam ; point to name of patch file call rdini1 ; let rdini try to find it & do take stuff jnc ptch15 ; nc = file mskermit.pch was found mov dx,offset ermes7 ; say file not found mov ah,prstr int dos clc ret ptch15: mov al,taklev ; remember initial take level mov tmpbuf,al ; when it changes, it's EOF & we're done mov comand.cmcr,1 ; bare cr's ok, to prevent prserr @EOF mov dx,offset tmpbuf+1 ; where to read call ptchrl ; read 1st 'word' jc ptch0 ; c = trubble mov si,offset tmpbuf+1 call atoi ; convert version string to integer jc ptch0 ; c = bad number, or none cmp ax,version ; does it match this version? je ptch3 ; e = yes ptch0: mov al,tmpbuf ; if take level has changed, xor al,taklev ; we're already out of patch file jnz ptch1 ; nz = change in take level call takclos ; close patch file ptch1: mov dx,offset ermes6 mov ah,prstr ; issue warning msg int dos clc ptch2: ret ptch3: mov dx,offset tmpbuf+1 call ptchrw ; read 2nd "word", 1st line jc ptch0 ; c = NG mov si,offset tmpbuf+1 call katoi ; convert 2nd "magic number" jc ptch0 ; c = NG cmp ax,totpar ; is it the total paragraphs memini computed? jne ptch0 ; ne = no ptch4: mov dx,offset tmpbuf+1 call ptchrl ; read CS:xxxx or DS:xxxx jc ptch2 mov si,offset tmpbuf cld lodsb ; initial take level xor al,taklev ; if unequal, it's EOF (clears carry) jnz ptch2 ; nz = EOF, we're done test ah,ah ; empty (possibly ;comment) line? jz ptch4 ; z = yes; ignore it mov tmpbuf+64,al ; clear replacement byte count cmp ah,7 ; were 7 chars read? jne ptch5 ; ne = no and word ptr[si],not 2020h ; convert to upper case lodsb ; get the 'C' or 'D' cmp word ptr[si],':S' ; S:, actually je ptch6 ; e = ok ptch5: stc ; error exit ret ptch6: mov bx,cs ; assume CS cmp al,'C' je ptch7 ; e = good guess cmp al,'D' jne ptch5 ; ne = neither CS or DS mov bx,ds ; modify assumption ptch7: mov word ptr[si],'X\' ; put '\X' in front of hex for katoi call katoi ; convert location jc ptch2 ; c= NG push bx ; save seg being patched push ax ; save location being patched ptch8: mov dx,offset tmpbuf+4 call ptchrw ; read replacement byte follwing '\X' jnc ptch10 ; nc = OK ptch9: pop ax ; clean stack & error return pop ax stc ret ptch10: test ah,ah ; EOL? jnz ptch11 ; nz = no mov si,offset tmpbuf+64 cld lodsb ; replacement byte count cmp ah,al ; were there any? je ptch9 ; e = no mov cx,ax ; replacement count pop di ; patch location pop es ; patch segment rep movsb ; make patch jmp ptch4 ; loop to read next line ptch11: cmp ah,2 ; 2 chars req'd for replacement byte jne ptch9 ; ne = bad mov si,offset tmpbuf+2 ; convert it call katoi jc ptch9 ; c = bad number or none mov si,offset tmpbuf+64 ; --> replacement byte counted string inc byte ptr[si] ; bump count mov bl,[si] xor bh,bh mov byte ptr[si+bx],al ; stash replacement byte jmp ptch8 ; loop for next byte ptchrl: mov bp,dx mov dx,offset crlf call prompt mov dx,bp ptchrw: mov ah,cmword mov comand.cmper,1 ; prohibit substitution variable expansion xor bx,bx ; 'help' ptr call comnd ret PATCH 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 xor ch,ch 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 call takopen ; open take buffer in macro space mov bx,takadr mov byte ptr [bx].taktyp,0ffh ; mark as a macro mov [bx].takcnt,0 ; length of text mov es,[bx].takbuf ; segment of buffer, from takopen mov di,1 ; skip count byte push psp pop ds ; DS = PSP gcmdl3: cmp cx,0 ; anything left? jle gcmdl10 ; le = no lodsb ; get a byte from PSP's command line dec cx ; one less char in input string cmp al,',' ; comma? jne gcmdl4 ; no, keep going mov al,cr ; convert to cr jmp short gcmdl9 ; store it gcmdl4: cmp al,'-' ; starting a flag? jne gcmdl9 ; ne = no mov ah,[si] ; get flag letter or ah,20h ; convert to lower case cmp ah,'f' ; 'f' for init file replacement? jne gcmdl9 ; ne = no inc si ; accept flag letter 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 es ; save current destination pointer push di ; which is in es:di (Take buffer) mov di,data ; set es:di to regular decbuf mov es,di lea di,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 al,' ' ; in printables? jbe gcmdl7 ; be = no, all done cmp al,',' ; comma command separator? je gcmdl7 ; e = yes, all done stosb ; store filespec char cmp cx,0 ; any chars left? jg short gcmdl6 ; g = yes gcmdl7: mov byte ptr es:[di],0 ; end filespec on a null pop di ; recover destination pointer es:di pop es gcmdl8: cmp cx,0 ; strip trailing whitespace jle gcmdl10 ; le = 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,data ; restore segment registers mov ds,ax mov si,[bx].takbuf ; get segment of Take buffer mov es,si mov si,1 ; skip count byte mov cx,di ; current end pointer, (save di) sub cx,si ; current ptr minus start offset mov [bx].takcnt,cx ; chars in buffer so far mov es:byte ptr [0],cl ; store count byte cmp cx,0 jg gcmdl11 ; material at hand call takclos ; empty take file jmp gcmdl14 ; finish up ; scan for command "stay" gcmdl11:mov al,es:[si] ; get a byte, cx and si are set above inc si dec cx cmp al,' ' ; separator? jbe gcmdl12 ; be = yes, keep looking cmp al,',' ; comma separator? je gcmdl12 ; e = yes mov ah,al ; get first byte mov al,es:[si] ; second byte after separator inc si dec cx or ax,2020h ; convert to lower case cmp ax,'st' ; first two letters of stay jne gcmdl12 ; ne = no match mov ax,es:[si] ; next two letters (stay vs status) add si,2 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 es:[si],' ' ; next char is a separator? jbe gcmdl13 ; be = yes, found correct match cmp byte ptr es:[si],',' ; or comma separator? je gcmdl13 ; e = yes 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].takcnt,cx rep movsb ; copy it into the Take buffer gcmdl13:mov [bx].takptr,1 ; init buffer ptr mov cx,[bx].takcnt ; count of bytes in buffer mov es:byte ptr [0],cl ; count of bytes in Take buffer gcmdl14:pop es ret gcmdlin endp ; Enter with ax pointing to file name. Searches path for given file, ; returns with ax pointing to whole name, or carry set if file can't be found. SPATH proc near call isfile ; does it exist as it is? jc spath0 ; c = no, prepend path elements test byte ptr filtst.dta+21,10H ; subdirectory name? jnz spath0 ; nz = yes, not desired file clc ret spath0: push es ; save es around work push bx push si push di mov bx,ax ; save filename pointer in bx mov si,ax xor dl,dl ; 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 short spath9 ; embedded path, fail spath3: mov si,pthadr ; offset of PATH= string in environment mov es,psp mov di,es:word ptr[env] ; pick up environment segment mov es,di spath4: cmp byte ptr es:[si],0 ; end of PATH= string? je spath9 ; e = yes, exit loop mov di,offset decbuf+64 ; 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 or al,al ; maybe end of string? jnz spath6 ; nz = 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 output 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 decbuf+64 call isfile ; is it a file? jc spath4 ; c = no, keep looking test byte ptr filtst.dta+21,10H ; subdirectory name? jnz spath4 ; nz = yes pop di pop si pop bx pop es clc ret ; return success (carry clear) spath9: mov ax,bx ; restore original filename pointer pop di ; restore regs pop si pop bx pop es stc ; no file found ret spath 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 getpath endp ; getcsp: copy COMSPEC= environment string into cmspbuf ; getssp: copy SHELL= environment string into shellbuf getcsp proc near mov bx,offset cmspnam ; find COMSPEC= mov cx,cmsplen ; it's length mov di,offset cmspbuf ; where to store string jmp short getccom ; do common worker getssp: mov bx,offset shellnam ; find SHELL= mov cx,shellen ; it's length mov di,offset shellbuf ; where to store string getccom:push es call getenv ; get environment offset into dx 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 data segment pop ds pop es cld getcs1: lodsb ; get a byte from environment cmp al,' ' ; space or less? jg getcs2 ; g = no, keep copying xor al,al ; terminate string on spaces etc getcs2: stosb ; store it in destination or al,al ; at end of string yet? jnz getcs1 ; nz = no, keep copying pop ds ; recover ds pop es ret getcsp endp ; Get Kermit parameters from the Environment. Parameters are commands like ; regular commands except they do not appear in the SET main table and are ; separated from one another by semicolons. They appear after KERMIT=. ; Do not allow Take/Macros to be created by any of these commands. ; On fatal error exits with carry set and flags.extflg = 1 getparm proc near push ax push bx push cx push dx push si push di push es mov es,psp ; segment of our PSP mov ax,es:word ptr[env] ; pick up environment address mov es,ax mov bx,offset kerenv ; Environment word to find, asciiz mov dx,bx call strlen ; length of its string to cx call getenv ; return dx = offset in environment jnc getpar1 ; nc = success jmp getpar9 ; c = not found getpar1:PUSH DS ; save regular DS push dx ; push Environment offset, then seg push es call takopen ; open Take buffer in macro space jnc getpar2 ; nc = success mov flags.extflg,1 ; say exit now pop es ; clean stack pop dx pop ds jmp getparx ; exit with fatal error getpar2:mov bx,takadr ; bx = Take data structure mov byte ptr [bx].taktyp,0ffh ; mark as a macro mov [bx].takcnt,0 ; length of text mov es,[bx].takbuf ; ES = segment of buffer, from takopen mov di,1 ; skip count byte field for es:di pop DS ; pop Environment segment (was in ES) pop si ; and seg, DS:SI is now Environment xor cx,cx ; line length counter getpar3:lodsb ; read an Environment character or al,al ; null (EOL)? jz getpar5 ; z = yes, stop here cmp al,';' ; semicolon separator? jne getpar4 ; ne = no mov al,CR ; replace semicolon with carriage ret getpar4:stosb ; store char in Take buffer inc cx ; count line length jmp short getpar3 ; get more text, until a null getpar5:mov al,CR ; terminate line, regardless stosb inc cx ; count terminator POP DS ; restore regular DS mov [bx].takcnt,cx ; chars in Take/macro buffer mov es:byte ptr [0],cl ; store count byte mov [bx].takptr,1 ; init buffer read ptr to first char jcxz getpar8 ; z = nothing left, exit ; parse each item as a command getpar6:mov comand.cmquiet,1 ; no screen display mov dx,offset nulprmpt ; set null prompt call prompt cmp flags.extflg,0 ; exit flag set? jne getpar8 ; ne = yes, exit this routine now mov dx,offset initab ; table of initialization routines xor bx,bx ; no explict help text mov comand.cmcr,1 ; allow bare CR's mov comand.impdo,0 ; do not search Macro table mov ah,cmkey ; match a keyword call comnd jc getpar7 ; c = failure mov comand.cmcr,0 ; no more bare CR's call bx ; call the routine returned in BX ; ignore failures (carry bit set) getpar7:cmp taklev,0 ; finished Take file? je getpar8 ; e = yes cmp flags.extflg,0 ; exit flag set? je getpar6 ; e = no, finish all commands getpar8:call takclos ; close our take file, if open getpar9:mov flags.extflg,0 ; do not leave this flag set clc ; clear for success getparx:mov comand.cmquiet,0 ; regular screen echoing pop es pop di pop si pop dx pop cx pop bx pop ax ret getparm endp ; Locate string variable in Environment ; bx = variable to find (usually including =), cx = length of variable name. ; Returns dx = offset within Environment of char following the name and ; carry clear, else carry set and dx unchanged. 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 xor di,di ; start at offset 0 in segment geten1: cmp es:byte ptr [di],0 ; end of environment? je geten3 ; 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 xor al,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 dx,di ; store offset of string clc ; carry clear for success jmp short geten4 geten3: stc ; carry set for failure geten4: pop es pop di pop si pop cx pop ax ret getenv endp ; Get thousands separator from DOS Country Information gettsep proc near mov ah,38h ; Get Country Information mov dx,offset tmpbuf ; temp buffer int dos mov bx,7 ; assume DOS 3+ position in buffer cmp byte ptr dosnum+1,3 ; DOS 3 or above? jae gettse1 ; ae = yes mov bx,4 ; for DOS 2.1 cmp dosnum,210h ; earlier than version 2.1? jae gettse1 ; ae = no mov al,',' ; use comma for old DOS's gettse1:mov al,tmpbuf[bx] ; get thousands separator char mov thsep,al ; save it ret gettsep endp STAY PROC NEAR clc ret STAY ENDP COMNT PROC NEAR ; COMMENT command mov ah,cmline mov bx,offset tmpbuf xor dx,dx call comnd ret COMNT ENDP ; change working directory cwdir proc near mov kstatus,0 ; global status mov ah,cmword mov dx,offset tmpbuf mov bx,offset pthmsg mov word ptr tmpbuf,0 call comnd ; get drive/dir spec, if any mov ah,cmeol call comnd jnc cwd1 ret ; c = failure cwd1: mov dx,offset crlf ; msgs from cdsr don't include this mov ah,prstr ; so let's do it now int dos mov si,offset tmpbuf ; cdsr wants drive/path ptr in si call cdsr ; common CD sub-routine jnc cwd2 ; nc = success mov kstatus,8 ; global status for unsuccess cwd2: call prtasz ; output current drive/path or err msg clc ret cwdir endp ; CDSR processes both CD & REM CD. Entered with si --> drive/path, it returns ; dx --> ASCIIZ current drive/path, w/carry clear, if successful, or error msg ; w/carry set, if not. CDSR PROC xor cx,cx ; 0 for default drive, if none cmp byte ptr[si],ch ; any drive/path? je cdsr4 ; e = no, just format current drive/path cmp byte ptr[si+1],':' ; is drive specified? jne cdsr1 ; ne = no mov cl,[si] ; drive letter cmp byte ptr[si+2],ch ; any path? jne cdsr1 ; ne = yes mov word ptr[si+2],'.' ; append dot+null as path to kludge DOS cdsr1: call dskspace ; test for drive, spec'd by cl, ready jnc cdsr2 ; nc = ready mov spcmsg3,cl ; insert drive letter ret'd by dskspace mov dx,offset spcmsg2+2 ; in err msg. dx --> msg w/o cr,lf ret ; carry is set cdsr2: mov dx,si ; where chdir wants it mov ah,chdir int dos jnc cdsr3 ; nc = success mov dx,offset ermes4 ; ret carry set, dx --> err msg ret cdsr3: mov dl,cl ; uc drive letter ret'd by dskspace sub dl,'A' ; A = 0 for seldsk mov ah,seldsk int dos inc dl ; A = 1 for curdsk mov curdsk,dl cdsr4: push si ; use caller's buffer for cur dr/path mov ax,':@' ; al = 'A' - 1, ah = ':' add al,curdsk ; al = drive letter mov [si],ax ; stash drive: inc si inc si mov byte ptr[si],'\' ; add \ inc si mov ah,gcd ; gcd fills in path as ASCIIZ xor dl,dl ; use current drive int dos pop dx ; return caller's buffer pointer in dx clc ret CDSR ENDP ; Erase specified file(s). Add protection of ignore hidden, subdir, volume ; label and system files. 9 Jan 86 [jrd] DELETE PROC NEAR ; includes paths and "?*" wildcards mov kstatus,0 ; global status 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 temp,di ; remember starting spot mov ah,cmline ; get a line mov bx,di ; where to place the file spec mov dx,offset filmsg ; help message call comnd jc delet0 ; c = failure or ah,ah ; anything given? jnz delet1 ; nz = yes mov ah,prstr mov dx,offset ermes1 ; say need something int dos clc ; say success delet0: ret delet1: mov di,temp ; start of filespec xor cl,cl ; disk drive letter cmp byte ptr [di+1],':' ; drive specified? jne delet2 ; ne = no mov cl,[di] ; get drive letter delet2: call dskspace ; compute space, get letter into CL jnc delet3 ; nc = success mov spcmsg3,cl ; put drive letter in msg mov dx,offset spcmsg2 ; error message call prtasz mov kstatus,8 ; global status clc ret ; and ignore this command delet3: mov si,offset tmpbuf ; del cmd jmp crun ; join run cmd from there DELETE ENDP ; Space CHKDSK PROC NEAR ; Space command mov kstatus,0 ; global status mov dx,offset tmpbuf ; buffer mov tmpbuf,0 ; init to null mov bx,offset dskmsg ; help message mov ah,cmword ; get optional drive letter call comnd ; ignore errors mov ah,cmeol call comnd jnc chkdsk1 ; nc = success ret ; failure chkdsk1:mov cl,tmpbuf ; set drive letter call dskspace ; compute space, get letter into CL jnc chkdsk2 ; nc = success and cl,5fh ; to upper case mov spcmsg3,cl ; insert drive letter mov dx,offset spcmsg2 ; say drive not ready call prtasz mov kstatus,8 ; global status clc ret chkdsk2:mov spcmsg1,cl ; insert drive letter mov di,offset tmpbuf ; work space for lnout mov word ptr[di],0a0dh ; cr/lf mov word ptr[di+2],' ' ; add two spaces add di,4 call lnouts ; use thousands separator mov si,offset spcmsg call strcat ; add text to end of message mov dx,offset tmpbuf call prtasz ; print asciiz string clc ret CHKDSK ENDP ; Compute disk free space (bytes) into long word dx:ax. ; Enter with disk LETTER in CL (use null if current disk). ; Returns uppercase drive letter in CL. ; Returns carry set if drive access error. Changes AX, DX. DSKSPACE PROC NEAR mov dl,cl ; desired disk letter, or null or dl,dl ; use current disk? jnz dskspa1 ; nz = no mov ah,gcurdsk ; get current disk int dos add al,'A' ; make 0 ==> A mov dl,al dskspa1:and dl,5fh ; convert to upper case mov cl,dl ; return upper case drive letter in CL sub dl,'A'-1 ; 'A' is 1, etc push bx push cx mov ah,36h ; get disk free space, bx=sect/cluster int dos ; dx:ax=sectors, cx=bytes/sector cmp ax,0ffffh ; error response? jne dskspa2 ; ne = no pop cx pop bx stc ; return error ret dskspa2:mul bx ; sectors/cluster * clusters = sectors mov bx,dx ; save high word of sectors (> 64K) mul cx ; bytes = sectors * bytes/sector push ax ; save low word of bytes mov ax,bx ; recall sectors high word mov bx,dx ; save current bytes high word mul cx ; high word sectors * bytes/sector add ax,bx ; new high bytes + old high bytes mov dx,ax ; store high word in dx pop ax ; space is in dx:ax as a long word pop cx pop bx clc ret DSKSPACE ENDP ; Get directory listing DIRECT PROC NEAR mov kstatus,0 ; global status 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 temp,cx ; remember length mov ah,cmline ; parse with cmline to allow switches mov bx,di ; next available byte mov dx,offset filmsg ; help message call comnd jnc direct1 ; nc = success ret ; failure direct1:mov word ptr [bx],0 ; plant terminator mov cl,curdsk ; current drive number ('A'=1) add cl,'A'-1 ; make a letter mov si,offset tmpbuf push si add si,temp ; user's text after ' dir ' cmp byte ptr [si+1],':' ; drive specified? jne direct2 ; ne = no, use current drive mov cl,[si] ; get drive letter from buffer direct2:call dskspace ; check for drive ready pop si jnc direct3 ; nc = drive ready mov spcmsg3,cl ; insert letter mov dx,offset spcmsg2 ; say drive is not ready call prtasz stc ret direct3:jmp crun ; join run cmd from there DIRECT ENDP ; This is the 'HELP' command. It gives a list of the commands HELP PROC NEAR mov kstatus,0 ; global status mov ah,cmeol call comnd ; get a confirm jnc help1 ; nc = success ret ; failure help1: mov ah,prstr ; show Quick help summary screen mov dx,offset qckhlp int dos mov ah,conout mov dl,trans.escchr ; get Kermit escape character add dl,40h ; convert to printable push dx ; save it for repeats below int dos mov ah,prstr mov dx,offset qckhlp1 ; more help text int dos mov ah,conout pop dx push dx int dos mov ah,prstr mov dx,offset qckhlp2 ; more help text int dos pop dx ; recover current escape char mov ah,conout int dos mov ah,prstr mov dx,offset qckhlp3 ; end of help message int dos mov ah,coninq ; get a keystroke, quietly int dos cmp al,'?' ; query mark? jne helpx ; ne = no, skip second screen mov ah,prstr ; show help summary screen mov dx,offset crlf ; a few blank lines int dos int dos int dos mov dx,offset tophlp ; show usual cryptic help int dos helpx: clc ret HELP ENDP ; the version command - print our version number prvers proc near mov kstatus,0 ; global status mov ah,cmeol call comnd jc prvers1 ; c = failure mov ah,prstr mov dx,offset crlf int dos mov ah,prstr mov dx,offset machnam ; display machine name int dos mov ah,prstr ; display the version header mov dx,offset verident int dos clc prvers1:ret prvers endp ; the show command showcmd proc near mov kstatus,0 ; global status mov ah,cmkey mov dx,offset shotab xor bx,bx ; no canned help call comnd jc showc1 ; c = failure jmp bx ; execute the handler showc1: ret ; failure showcmd endp ; the type command - type out a file typec proc near mov kstatus,0 ; global status 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 temp,di ; save place for later mov ah,cmline ; parse with cmline so we can have paths mov bx,di ; next available byte mov dx,offset filmsg ; In case user wants help call comnd jc typec1 ; c = failure or ah,ah ; any text given? jnz typec2 ; nz = yes mov ah,prstr mov dx,offset ermes1 ; say need more info int dos clc typec1: ret typec2: mov byte ptr [bx],0 ; plant terminator mov si,temp ; start of filespec xor cl,cl ; say local drive cmp byte ptr [si+1],':' ; drive given? jne typec3 ; ne = no mov cl,[si] ; get drive letter typec3: call dskspace ; check for drive ready jnc typec4 mov spcmsg3,cl ; put drive letter in msg mov dx,offset spcmsg2 ; error message call prtasz mov kstatus,8 ; global status clc ret ; and ignore this command typec4: mov si,offset tmpbuf jmp short 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 dopush proc near mov ah,cmeol call comnd jnc fpush ; nc = success ret ; failure fpush: mov si,offset tmpbuf ; a dummy buffer mov byte ptr [si],0 ; plant terminator mov dx,offset cmspbuf ; always use command.com cmp shellbuf,0 ; SHELL= present? je crun4 ; e = no, use COMSPEC= name mov dx,offset shellbuf ; use SHELL= name jmp short crun4 ; go run it dopush endp ; Run a program from within Kermit RUN PROC NEAR mov ah,cmline ; get program name and any arguments mov bx,offset tmpbuf ; place for user's text mov dx,offset runmsg ; In case user wants help call comnd jnc run1 ; nc = success ret ; failure run1: or ah,ah ; byte count jnz run2 ; nz = have program name mov ah,prstr ; else complain mov dx,offset ermes1 ; need more info int dos clc ret run2: mov si,offset tmpbuf ; source of text jmp short 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 ; Set errlev with DOS errorlevel from subprocess. CRUN proc near mov ah,prstr ; output crlf before executing comnd mov dx,offset crlf ; [lba] int dos 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 mov dx,offset cmspbuf ; always use command.com crun4: mov exearg+2,si ; pointer to argument string mov exearg+4,ds ; segment of same cmp lclsusp,0 ; sys dependent routine to call je crun5 ; e = none mov bx,lclsusp ; address to call push dx ; preserve name in dx call bx ; call sys dependent suspend routine pop dx crun5: push dx ; preserve name in dx call serrst ; reset serial port (if active) call cbrestore ; restore state of Control-Break Chk pop dx 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 xor al,al ; 0 = load and execute (DX has name) mov ah,exec mov ssave,sp ; save stack ptr int dos ; go run the program mov ax,data ; restore segment registers mov ds,ax ; reset data segment mov es,ax ; and extra segment mov ax,stack 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!! cmp flags.chrset,1 ; user-defined table in use? je crun6 ; e = yes mov flags.chrset,437 ; find default global char set cmp dosnum,0300h+30 ; DOS version 3.30 or higher? jb crun6 ; b = no, no Code Pages mov ax,6601h ; get global Code Page int dos ; bx=active Code Page, dx=boot CP mov flags.chrset,bx ; setup default SET FILE CHAR SET crun6: call cboff ; turn off DOS BREAK check popf ; recover flags jc crun8 ; c = error, handle cmp lclrest,0 ; sys dependent routine to call je crun9 ; e = none mov bx,lclrest ; get routine's address call bx ; call sys dependent restore routine crun9: mov ah,4dh ; get subprocess return status int dos mov errlev,al ; DOS errorlevel clc ret crun8: mov ah,prstr mov dx,offset erms37 int dos mov kstatus,8 ; global status clc ret CRUN 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 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 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 ; segment is the code segment mov dx,offset intbrk ; and offset 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 mov ax,3300h ; get state of Control-Break Check int dos mov orgcbrk,dl ; save state here pop es ret SETINT 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,data ; get Kermit's data segment mov ds,ax mov flags.cxzflg,'C' ; say we saw a ^C mov rstate,'E' mov sstate,'E' pop ds pop ax iret ; return to caller in a Continue condition ; Set DOS' Control-Break Check to off cboff proc near mov ax,3301h ; set Control-Break Chk state xor dl,dl ; set state to off int dos ret cboff endp ; Restore DOS's Control-Break Check to startup value cbrestore proc near push dx mov ax,3301h ; set Control-Break Chk state mov dl,orgcbrk ; restore state to startup value int dos pop dx ret cbrestore endp ; 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 bp ; Kermit's IP, CS, and Flags are on the stack mov bp,sp ; all ready for an iret, but first a word .. or word ptr[bp+8],1 ; set carry bit, signals failure DOS 2+ way pop bp ; this avoids seeing the Interrupt flag bit iret ; return to user, simulate return from Int 21h 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 short isfil2 isfil1: cmp byte ptr filtst.fname,0 ; did DOS fill in a name? je isfil2 ; z = no clc jmp short isfil3 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,stack ; last segment sub ax,psp ; minus beginning add bx,ax ; # of paragraphs occupied mov totpar,bx ; save for patcher's magic number 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 ; exit Kermit now 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 cx,bx ; remember quantity wanted mov ah,alloc ; DOS memory allocator int dos jc sbrkx ; c = fatal cmp cx,bx ; paragraphs wanted vs delivered jb sbrkx ; b = not enough, fatal error 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 code ends end start