home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
mskermit.tar.gz
/
mskermit.tar
/
mssker.asm
< prev
next >
Wrap
Assembly Source File
|
1998-05-28
|
109KB
|
3,558 lines
NAME mssker
; File MSSKER.ASM
include mssdef.h
; Edit history
; 18 Jan 1995 version 3.14
; Last edit
; 18 Jan 1995
;****************************** Version 3.14 *****************************
; KERMIT, Celtic for "free"
;
; The name "Kermit" is a registered trade mark of Henson Associates, Inc.,
; used by permission.
;
; MS-DOS Kermit Program Version 3.15 beta, Sept 97
; MS-DOS Kermit Program Version 3.14, 18 Jan 95
; MS-DOS Kermit Program Version 3.13, 8 July 93
; MS-DOS Kermit Program Version 3.12, Feb 1992
; MS-DOS Kermit Program Version 3.11, 6 Sept 1991
; MS-DOS Kermit Program Version 3.10, 2 March 1991
; MS-DOS Kermit Program Version 3.02, development for 3.10, 1990-91
; MS-DOS Kermit Program Version 3.01, 20 March 1990
; MS-DOS Kermit 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
; Kermit-MS Program Version 2.26, July 27, 1984
; PC-Kermit Program Version 1.20, November 4, 1983
; PC-Kermit Program Version 1.0, 1982
;
; Based on the Columbia University KERMIT Protocol.
;
; Copyright (C) 1982, 1997, Trustees of Columbia University in the
; City of New York. The MS-DOS Kermit software may not be, in whole
; or in part, licensed or sold for profit as a software product itself,
; nor may it be included in or distributed with commercial products
; or otherwise distributed by commercial concerns to their clients
; or customers without written permission of the Office of Kermit
; Development and Distribution, Columbia University. This copyright
; notice must not be removed, altered, or obscured.
;
; Original Authors (versions 1.0 through 2.28):
; Daphne Tzoar, Jeff Damens
; Columbia University Center for Computing Activities
; 612 West 115th Street
; New York, NY 10025
;
; Present author (version 2.29, 2.30, 2.31, 2.32, 3.00, 3.01, 3.02,
; 3.10, 3.11, 3.12, 3.13, 3.14):
; Joe R. Doupnik
; Dept of EE, and CASS
; Utah State University
; Logan, UT 84322, USA
; E-Mail: JRD@CC.USU.EDU (Internet), JRD@USU (BITNET)
;
; Special thanks to Christine Gianone, Frank da Cruz, Bill Catchings,
; Bernie Eiben, Vace Kundakci, Terry Kennedy, Jack Bryans, 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, tdfmt
public lclsusp, lclrest, lclexit, cwdir, kstatus, verident, cdsr
public spath, patched, getenv, psp, dosctty, patchid, retcmd
public tcptos, emsrbhandle, emsgshandle, apctrap, cboff, cbrestore
public startup, cmdfile, inidir, tmpbuf, malloc, dostemp
public breakcmd, xms, xmsrhandle, xmsghandle, xmsep
env equ 2CH ; environment address in psp
cline equ 80H ; offset in psp of command line
braceop equ 7bh ; opening curly brace
bracecl equ 7dh ; closing curly brace
_STACK SEGMENT ; our stack
dw 1500 dup (0) ; for TCP code
dw 200 dup(0) ; for main Kermit code
msfinal label word ; top of stack
_STACK ENDS
data segment
extrn buff:byte, comand:byte, flags:byte, trans:byte, prmptr:word
extrn machnam:byte, decbuf:byte, rstate:byte, sstate:byte
extrn mcctab:byte, rdbuf:byte, takeerror:byte, macroerror:byte
extrn dos_bottom:byte, domath_ptr:word, domath_cnt:word
extrn domath_msg:word, oldifelse:byte, retbuf:byte
verident label byte
verdef
patchena db '$patch level'
patchid db ' 0 $'
copyright db cr,lf
db 'Copyright (C) Trustees of Columbia University 1982, 1997.'
db cr,lf,'$'
copyright2 db cr,lf,lf
db ' Copyright (C) 1982, 1997, Trustees of Columbia University in the'
db cr,lf
db ' City of New York. The MS-DOS Kermit software may not be, in whole'
db cr,lf
db ' or in part, licensed or sold for profit as a software product itself,'
db cr,lf
db ' nor may it be included in or distributed with commercial products'
db cr,lf
db ' or otherwise distributed by commercial concerns to their clients'
db cr,lf
db ' or customers without written permission of the Office of Kermit'
db cr,lf
db ' Development and Distribution, Columbia University. This copyright'
db cr,lf
db ' notice must not be removed, altered, or obscured.'
db cr,lf,'$'
hlpmsg db cr,lf,'Type ? or HELP for help',cr,lf,'$'
crlf db cr,lf,'$'
patpmt db 0
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.'
db ' Version number mismatch.',cr,lf,'$'
ermes7 db cr,lf,'Patch file was not found',cr,lf,'$'
ermes8 db cr,lf,'Fatal error in patch file! 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 command interpreter $'
erms38 db cr,lf,' Not an 8250 UART at this COM port$'
erms39 db cr,lf,' UART tests ok$'
badnam db cr,lf,'?No such file(s)$'
ifdef no_graphics
nographics db ' No graphics$'
endif ; no_graphics
ifdef no_network
nonet db ' No network$'
else
ifdef no_tcp
notcp db ' No tcp/ip$'
endif ; no_tcp
endif ; no_network
ifdef no_terminal
noterm db ' No terminal$'
endif ; no_terminal
msgif db cr,lf,' IF extensions are ',0
msggraph db cr,lf,' Graphics is ',0
msgtcpip db cr,lf,' TCP/IP is ',0
msgnetwork db cr,lf,' Network is ',0
msgterm db cr,lf,' Terminal emulation is ',0
msgnot db 'not ',0
msgavail db 'available$',0
xms xmsreq <> ; XMS request block
data ends
data1 segment
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$'
pathlp db ' optional path for file mskermit.pch$'
stophlp db ' Status value to be returned msg, nothing if no new value$'
setenvhlp db ' name=string phrase to be put into DOS master environment$'
tophlp db cr,lf
db ' Ask, Askq (read keybd to variable) '
db ' Pause [secs], MPause/Msleep [millisec]'
db cr,lf
db ' APC text send App Prog Cmd to host'
db ' Pop, End (exit current macro/Take file)'
db cr,lf
db ' Bye (logout remote server) '
db ' Push (go to DOS, keep Kermit)'
db cr,lf
db ' C or Connect (become a terminal) '
db ' Quit (leave Kermit)'
db cr,lf
db ' Check (graphics, tcp/ip, networks) '
db ' R or Receive (opt local filename)'
db cr,lf
db ' Clear (Input, comms recv buffer) '
db ' Read (line from a file to variable)'
db cr,lf
db ' Close (logging and script file) '
db ' Reget (get rest of a partial file)'
db cr,lf
db ' CLS (clear screen at command level)'
db ' Reinput (script Input, reread buffer)'
db cr,lf
db ' CWD or CD (change dir &/or disk) '
db ' Remote (prefix for commands)'
db cr,lf
db ' Decrement/Increment variable number'
db ' Replay (file through term emulator)'
db cr,lf
db ' Define/Assign (a command macro) '
db ' Reset (clock)'
db cr,lf
db ' Delete (a file) '
db ' Retrieve (get files, delete source) '
db cr,lf
db ' Dial (phone number) '
db ' Return text (from macro to \v(return))'
db cr,lf
db ' Directory (filepsec) '
db ' Run (a program)'
db cr,lf
db ' Disable (selected server commands)'
db ' S, Send, Resend, Psend local new-name'
db cr,lf
db ' Echo text (show line on screen) '
db ' Server [timeout] (become a server)'
db cr,lf
db ' Else (follows IF statment) '
db ' Set (most things)'
db cr,lf
db ' Enable (selected server commands)'
db ' Setenv name=string to DOS environment'
db cr,lf
db ' EXIT (leave Kermit) '
db ' Show (most things)'
db cr,lf
db ' Finish (to remote server) '
db ' Sleep time (wait, no comms echos)'
db cr,lf
db ' For var start stop step {commands} '
db ' Space (free on current disk)'
db cr,lf
db ' Get (remote file opt new name)'
db ' Stop (exit all Take files & macros)'
db cr,lf
db ' Getc (read 1 byte from kbd to var)'
db ' Switch index {:label, cmds,...}'
db cr,lf
db ' GetOK (get Yes, OK, No response)'
db ' Take (commands from a file)'
db cr,lf
db ' Goto (label, Take file or Macro)'
db ' Telnet host port NEW or RESUME'
db cr,lf
db ' Hangup (drop DTR, hang up phone) '
db ' Test COM1 ... COM4 (check for UART)'
db cr,lf
db ' If [not] <condition> <command> '
db ' Transmit filespec [prompt] (raw upload)'
db cr,lf
db ' I or Input [timeout] text (scripts)'
db ' Type (a file)'
db cr,lf
db ' INTRO introduction to Kermit '
db ' Version (show version and copyright)'
db cr,lf
db ' Log (Packet, Session, Transaction) '
db ' Wait [timeout] on modem \cd \cts \dsr'
db cr,lf
db ' Mail (file to host Mailer) '
db ' While <condition> {commands}'
db cr,lf
db ' Minput (Input with many patterns)'
db ' Write FILE or log file text'
db cr,lf
db ' Move (send files, delete source)'
db ' Undefine (macro or array element)'
db cr,lf
db ' Open Read/Write/Append file '
db ' Xecho string (without leading cr/lf)'
db cr,lf
db ' Output text (to comms channel)'
db ' Xif <condition> {cmds} ELSE {cmds}'
db '$'
qckhlp db cr,lf
db 'MS-DOS Kermit 3.15, 15 Sept 1997, Copyright (C) 1982, 1995,'
db cr,lf
db 'Trustees of Columbia University in the City of New York.'
db cr,lf,lf
db 'Important commands (type the command, then press the'
db ' Enter key):'
db cr,lf,lf
db ' INTRO - For an introduction to MS-DOS Kermit.'
db cr,lf
db ' VERSION - For version and copyright information.'
db cr,lf
db ' EXIT - To leave MS-DOS Kermit.'
db cr,lf,lf
db 'Press the question-mark (?) key for context-sensitive'
db ' help'
db cr,lf
db ' at any point within a command.'
db cr,lf,lf
db 'DOCUMENTATION:'
db cr,lf
db ' "Using MS-DOS Kermit" by Christine M. Gianone,'
db cr,lf
db ' Digital Press / Butterworth-Heinemann, 1992, ISBN'
db ' 1-55558-082-3.'
db cr,lf
db ' Please purchase this manual - it shows you how to use'
db ' the software,'
db cr,lf
db ' it answers your questions, and its sales support the'
db ' Kermit effort.'
db cr,lf
db ' To order, call +1 212 854-3703 or +1 800 366-2665.'
db cr,lf,lf
db 'And see these files on your Kermit diskette for additional'
db ' information:'
db cr,lf
db ' KERMIT.UPD - New features and updates.'
db cr,lf
db ' KERMIT.BWR - Hints and tips, troubleshooting information,'
db ' etc.'
db cr,lf
db ' KERMIT.HLP - Concise descriptions of each command.'
db cr,lf,'$'
ifndef no_terminal
intrhlp 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 <the Enter key>.'
db ' Example: SET SPEED 2400<Enter>',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<Enter> or even SET SPE<Esc> 24<Esc>'
db '<Enter>',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 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-'
; labels where Connect mode escape printable goes (twice)
intrhlp1 db ' C (Control-'
intrhlp2 db ' '
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-C, transfer'
db ' files with',cr,lf
db ' SEND x.txt, GET b.txt, BYE.'
db cr,lf,lf
db ' MS-DOS Kermit commands, a functional summary:'
db cr,lf
db cr,lf,' Local file management: '
db 'Kermit program management:'
db cr,lf,' DIR (list files) '
db ' EXIT (from Kermit, return to DOS)'
db cr,lf,' CD (change directory) '
db ' QUIT (same as EXIT)'
db cr,lf,' DELETE (delete files) '
db ' TAKE (execute Kermit commands from file)'
db cr,lf,' RUN (a DOS command) '
db ' CLS (clear screen)'
db cr,lf,' TYPE (display a file) '
db ' PUSH (enter DOS, EXIT returns to Kermit)'
db cr,lf,' SPACE (show disk space) '
db ' Ctrl-C (interrupt a command)'
db cr,lf
db cr,lf,' Communication settings: '
db 'Terminal emulation:'
db cr,lf,' SET PORT, SET SPEED '
db ' CONNECT (begin terminal emulation)'
db cr,lf,' SET PARITY '
db ' HANGUP (close connection)'
db cr,lf,' SET FLOW-CONTROL '
db ' Alt-X (return to MS-Kermit> prompt)'
db cr,lf,' SET LOCAL-ECHO '
db ' SET KEY (key mapping)'
db cr,lf,' SET ? to see others '
db ' SET TERMINAL TYPE, BYTESIZE, other parameters'
db cr,lf,' SHOW COMMUNICATIONS, MODEM '
db ' SHOW TERMINAL, SHOW KEY'
db cr,lf
db cr,lf,' File transfer settings: '
db cr,lf,' SET FILE CHARACTER-SET name '
db ' SET TRANSFER CHARACTER-SET'
db cr,lf,' SET FILE TYPE TEXT, BINARY '
db ' SET SEND or RECEIVE parameters'
db cr,lf,' SET FILE ? to see others '
db ' SET WINDOWS (sliding windows)'
db cr,lf,' SHOW FILE '
db ' SHOW PROTOCOL, SHOW STATISTICS'
db cr,lf,lf
db cr,lf,' Kermit file transfer: '
db 'ASCII file transfer:'
db cr,lf,' SEND files (to RECEIVE) '
db ' LOG SESSION, CLOSE SESSION (download)'
db cr,lf,' RECEIVE (from SEND) '
db ' TRANSMIT (upload)'
db cr,lf,' MAIL files (to RECEIVE) '
db ' SET TRANSMIT parameters'
db cr,lf
db cr,lf,' Using a Kermit server: '
db 'Being a kermit server:'
db cr,lf,' GET files (from server) '
db ' SET SERVER TIMEOUT or LOGIN'
db cr,lf,' SEND or MAIL (to server) '
db ' ENABLE or DISABLE features'
db cr,lf,' REMOTE command (to server) '
db ' SERVER'
db cr,lf,' FINISH, LOGOUT, BYE '
db ' SHOW SERVER'
db cr,lf
db cr,lf,' Script programming commands: '
db cr,lf,' INPUT, REINPUT secs text '
db ' :label, GOTO label'
db cr,lf,' OUTPUT text '
db ' IF [ NOT ] condition command'
db cr,lf,' DECREMENT or INCREMENT variable number'
db cr,lf,' ASK or ASKQ variable prompt '
db ' OPEN READ (or WRITE or APPEND) file'
db cr,lf,' DEFINE variable or macro '
db ' READ variable-name'
db cr,lf,' ASSIGN variable or macro '
db ' WRITE file-designator text'
db cr,lf,' [ DO ] macro arguments '
db ' CLOSE READ or WRITE file or logfile'
db cr,lf,' ECHO text '
db ' END or POP from macro or file'
db cr,lf,' PAUSE time '
db ' STOP all macros and command files'
db cr,lf,' SLEEP time no comms sampling'
db ' WRITE file-designator text'
db cr,lf,' WAIT time modem-signals '
db ' SHOW VARIABLES, SHOW SCRIPTS, SHOW MACROS'
db cr,lf
db ' Use "?" within comands for help on what fits that word.$'
endif ;; ifndef no_terminal
kpath db 64 dup (0) ; Kermit's paths to Kermit files
data1 ends
data segment
comtab db 106 - 1 ; COMND tables
mkeyw 'APC',scapc
mkeyw 'Asg',assign ; synonym
mkeyw 'Ask',ask
mkeyw 'Askq',askq
mkeyw '_assign',hide_assign ; hidden, expand destination name
mkeyw 'Assign',assign
mkeyw 'Break',breakcmd
mkeyw 'Bye',bye
mkeyw 'C',telnet
mkeyw 'CD',cwdir
mkeyw 'Clear',scclr
mkeyw 'Close',clscpt
mkeyw 'Check',check
mkeyw 'Comment',comnt
mkeyw 'Connect',telnet
mkeyw 'Continue',continue
mkeyw 'CLS',cls
mkeyw 'CWD',cwdir
mkeyw 'Declare',declare
mkeyw '_define',hide_define ; hidden, expand destination name
mkeyw 'Define',dodef
mkeyw 'Dec',decvar ; decrement vs declare resolver
mkeyw 'Decrement',decvar
mkeyw 'Delete',delete
mkeyw 'Dial',dial
mkeyw 'Directory',direct
mkeyw 'Disable',srvdsa
mkeyw 'Do',docom
mkeyw 'Echo',scecho
mkeyw 'Else',elsecmd
mkeyw 'Enable',srvena
mkeyw 'End',popcmd
mkeyw 'Exit',exit
mkeyw 'Finish',finish
mkeyw '_forinc',_forinc ; hidden, FOR statement incrementer
mkeyw 'For',forcmd
mkeyw 'Forward',sforward ; hidden "Forward" goto
mkeyw 'Get',get
mkeyw 'G',get ; hidden synomym for Get
mkeyw 'Ge',get ; ditto
mkeyw 'Getc',getc
mkeyw 'Getok',getok
mkeyw 'goto',sgoto
mkeyw 'H',help
mkeyw 'Hangup',dtrlow
mkeyw 'Help',help
mkeyw 'If',ifcmd
mkeyw 'I',scinp
mkeyw 'Increment',incvar
mkeyw 'Input',scinp
mkeyw 'INTRO',intro
mkeyw 'Local',localmac
mkeyw 'Log',setcpt
mkeyw 'Mail',mail
mkeyw 'Minput',scminput
mkeyw 'Move',move
mkeyw 'Mpause',scmpause
mkeyw 'Msleep',scmpause
mkeyw 'Open',vfopen
mkeyw 'O',scout ; hidden synomym for OUTPUT
mkeyw 'Output',scout
mkeyw 'Pause',scpau
mkeyw 'Pop',popcmd
mkeyw 'Psend',psend
mkeyw 'Push',dopush
mkeyw 'Quit',exit
mkeyw 'R',read
mkeyw 'Read',vfread
mkeyw 'Receive',read
mkeyw 'Reget',reget
mkeyw 'Reinput',screinp
mkeyw 'Remote',remote
mkeyw 'Replay',replay
mkeyw 'Resend',resend
mkeyw 'Reset',reset
mkeyw 'Retrieve',retrieve
mkeyw 'Return',retcmd
mkeyw 'Run',run
mkeyw 'S',send
mkeyw 'Send',send
mkeyw 'Server',server
mkeyw 'Set',setcom
mkeyw 'Setenv',setenv
mkeyw 'Show',showcmd
mkeyw 'Sleep',scsleep
mkeyw 'Space',chkdsk
mkeyw 'Statistics',shosta
mkeyw 'Stay',stay
mkeyw 'Stop',takeqit
mkeyw 'Switch',switch
mkeyw 'Take',take
mkeyw 'Test',testcom
mkeyw 'Transmit',scxmit
mkeyw 'xmit',scxmit ; hidden synonym
mkeyw 'Type',typec
mkeyw 'Undefine',undefine
mkeyw 'Version',prvers
mkeyw 'Wait',scwait
mkeyw 'While',whilecmd
mkeyw 'Write',write
mkeyw 'Xecho',xecho
mkeyw 'XIF',xifcmd
mkeyw ':',comnt ; script labels, do not react
mkeyw 'Patch',patch
mkeyw 'Nopush',pushproc ; must be hidden
ifdef no_network
shotab db 19 - 2 ; SHOW keyword
else
ifndef no_tcp
shotab db 19 ; SHOW keyword
else
shotab db 19 - 1 ; SHOW keyword
endif ; no_tcp
endif ; no_network
mkeyw 'array',sharray
mkeyw 'Communications',shcom
mkeyw 'Control-prefixing',cntlsho
mkeyw 'File',shfile
mkeyw 'Key',shokey
mkeyw 'Logging',shlog
mkeyw 'Macros',shomac
mkeyw 'Memory',shmem
mkeyw 'Modem',shomodem
ifndef no_network
mkeyw 'Network',shownet
endif ; no_network
mkeyw 'Protocol',shpro
mkeyw 'Scripts',shscpt
mkeyw 'Server',shserv
ifndef no_tcp
mkeyw 'Sessions',sesdisp ; TCP/IP
endif ; no_tcp
mkeyw 'Statistics',shosta
mkeyw 'Status',status
mkeyw 'Terminal',shterm
mkeyw 'Translation',shorx
mkeyw 'Variables',shovar
; Kermit initing from Environment
nulprmpt db 0,0,0 ; null prompt
initab db 8 ; Environment phrase dispatch table
mkeyw 'INPUT-buffer-length',setinpbuf ; Script INPUT buffer length
mkeyw 'Rollback',setrollb ; number of Terminal rollback screens
mkeyw 'Width',setwidth ; columns in rollback buffer, def=80
mkeyw 'COM1',com1port
mkeyw 'COM2',com2port
mkeyw 'COM3',com3port
mkeyw 'COM4',com4port
mkeyw 'Path',mkkpath
featab db 5 ; Compiled-in feature list for CHECK cmd
mkeyw 'if',5
mkeyw 'graphics',1
mkeyw 'networks',3
mkeyw 'tcp',2
mkeyw 'terminals',4
chktab db 4 ; table of comm ports for TEST
mkeyw 'COM1',1
mkeyw 'COM2',2
mkeyw 'COM3',3
mkeyw 'COM4',4
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
tcptos dw 0 ; top of stack for TCP code
ssave dd 0 ; Original SS:SP 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
emsrbhandle dw -1 ; EMS rollback handle, -1 means invalid
emsgshandle dw -1 ; EMS graphics handle, -1 means invalid
xmsrhandle dw 0 ; XMS rollback buffer handle, 0 = invalid
xmsghandle dw 0 ; XMS graphics memory buffer handle
xmsep dd 0 ; XMS manager entry point, 0 = invalid
dosnum dw 0 ; dos version number, major=low, minor=high
dosctty db 0 ; !=0 if DOS attempts using our comms line
curdsk db 0 ; Current disk
origd db 0 ; Original disk
orgdir db 64 dup (0) ; original directory on original disk
startup db 64 dup (0) ; our startup directory
cmdfile db 64 dup (0) ; path and file of last TAKE
inidir db 64 dup (0) ; mskermit.ini directory (ends on \)
taklev db 0 ; Take levels
takadr dw takstr-(size takinfo) ; Pointer into structure
takstr db (size takinfo) * maxtak dup(0)
cmdlinetake db 0 ; non-zero if have DOS command line cmds
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
ifdef nls_portuguese
ptchnam db 'MSRP315.PCH',0 ; Portuguese
else
ifdef no_terminal
ifdef no_network
ptchnam db 'MSRL315.PCH',0 ; MSK Lite
else
ptchnam db 'MSRN315.PCH',0 ; MSK medium-lite
endif
else
ifdef no_network
ptchnam db 'MSRM315.PCH',0 ; MSK medium
else
ptchnam db 'MSR315.PCH',0 ; main patch file name (Version dependent)
endif
endif
endif
ptchnam2 db 'MSKERMIT.PCH',0 ; alternate 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
dostempname db 'TEMP=' ; DOS TEMP= enviroment string
dostempnlen equ $ - dostempname ; string length
dostemp db ' > ',60 dup (0) ; " > " contents of TEMP= "\$kermit$.tmp"
tmpname db '$kermit$.tmp',0 ; path must start on dostemp+3
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
onexit db 8,0,'ON_EXIT',CR ; <length>on_exit macro name
onexlen equ $-onexit-2 ; length of name
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
moremsg db '... more, press a key to continue ...$'
errlev db 0 ; DOS errorlevel to be returned
kstatus dw 0 ; command execution status (0 = success)
thsep db 0 ; thousands separator
tdfmt db 0 ; date/time format code
totpar dw 0
apctrap db 0 ; disable command if done via APC
pttemp db 0 ; Patch temp variable
temp dw 0
tempptr dw 0
seekptr dw 0,0 ; pointer for lseek in takeread
nopush_flag db 0 ; nz = stops push/run, keep hidden
segstr db 'ABCDEFG' ; segment "names" for patcher
lsegstr equ $-segstr
even
segtab dw code ; segment values for patcher
dw code1
dw code2
dw data
dw data1
dw _TEXT
dw dgroup
data ends
code1 segment
extrn fparse:far, iseof:far, strlen:far, strcpy:far, prtscr:far
extrn strcat:far, prtasz:far, domath:far, decout:far, poplevel:far
assume cs:code1
code1 ends
code segment
extrn reget:near, mail:near, shovar:near, scapc: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, cmblnk:near, getc: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 shserv:near, initibm:near, forcmd:near, _forinc:near
extrn shorx:near, lnout:near, lnouts:near, scminput: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, takclos:far, ask:near, askq:near
extrn assign:near, sgoto:near, screinp:near, ifcmd:near, write:near
extrn setinpbuf:near, shmem:near, replay:near, xifcmd:near
extrn com1port:near, com2port:near, com3port:near
extrn com4port:near, popcmd:near, mprompt:near, locate:near
extrn vfopen:near, vfread:near, decvar:near, incvar:near
extrn setwidth:near, scmpause:near, whilecmd:near, reset:near
extrn getok:near, cntlsho:near, shownet:near, ctlu:near
extrn resend:near, psend:near, tstport:near, scsleep:near
extrn takopen_file:far, takopen_macro:far, sforward:near
extrn hide_assign:near, hide_define:near, dial:near, declare:near
extrn sharray:near, localmac:near, switch:near, move:near
extrn retrieve:near, undefine:near, xecho:near
ifndef no_tcp
extrn sesdisp:near
endif ; no_tcp
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: call memini ; initialize our memory usage
mov ah,setdma ; set disk transfer address
mov dx,offset buff
int dos
call far ptr 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, t/date code
mov ah,gswitch
xor al,al ; pick up switch character
int dos
mov slashc+1,dl
and maxtry,3fh ; limit # packet retries
mov bx,4 ; PRN handle for DOS
mov ah,ioctl
mov al,0 ; get info to
int dos
or dl,20h ; turn on binary mode
xor dh,dh
mov ah,ioctl
mov al,1 ; set info
int dos
call getcsp ; get comspec from environment
call getssp ; get shellspec from environment
call getdostemp ; get DOS TEMP= string
mov dx,offset dostemp
call strlen ; length so far
cmp cx,3 ; just ' > '?
je start1c ; e = yes, no TEMP= in environment
mov bx,dx ; string so far
add bx,cx ; last byte + 1
cmp byte ptr [bx-1],'\' ; ends on slash?
je start1c ; e = yes
mov word ptr [bx],'\'+0 ; append slash and null terminator
start1c:mov di,dx ; destination of dostemp
mov si,offset tmpname ; redirection filename
call strcat ; append
call getargv ; get directory where we started
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 "<digit>=" 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 krmend5 ; quit immediately
start2: mov word ptr comand.cmrprs,offset krmend ; offset of reparse addr
mov ax,cs ; our current code segment
mov word ptr comand.cmrprs+2,ax ; segment of reparse address
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
ifdef no_graphics
mov dx,offset nographics
int dos
endif
ifdef no_network
mov dx,offset nonet
int dos
else
ifdef no_tcp
mov dx,offset notcp
int dos
endif
endif
ifdef no_terminal
mov dx,offset noterm
int dos
endif
ifdef no_graphics + no_tcp + no_network
mov dx,offset crlf
int dos
endif
mov dx,offset hlpmsg
int dos
start3: mov patchena,' ' ; let patch level show
call serrst ; reset serial port (if active)
call initibm ; define IBM macro
call rdinit ; read kermit init file
push es
mov di,ds
mov es,di
mov di,offset inidir ; remember path to mskermit.ini
mov si,offset cmdfile ; last Take file path+name
call strcpy ; copy whole string
mov dx,di
call strlen ; length of complete path+filename
add di,cx ; last byte +1
mov al,'\' ; look for this separator
std
repne scasb
cld
mov byte ptr [di+2],0 ; terminate after separator
pop es
; This is the main KERMIT loop. It prompts for and gets the users commands
kermit: mov ax,ds
mov es,ax ; convenient safety measure
mov dx,prmptr ; get prompt string address
call mprompt ; set master reparse address to here
cmp flags.cxzflg,'C' ; did someone want out?
jne kermt4 ; ne = no
kermt2: cmp taklev,0 ; are we in a Take file?
je kermt4 ; e = no, ignore the signal
call takclos ; close take file, release buffer
jmp short kermt2 ; close any other take files
kermt4: mov flags.cxzflg,0 ; reset each time
and flags.remflg,not dserver ; turn off server mode bit
cmp dosctty,0 ; is DOS using our comms line?
je kermt1 ; e = no
and flags.remflg,not(dquiet+dregular+dserial)
or flags.remflg,dquiet ; set display to quiet mode
call serrst ; close port so CTTY can run
kermt1: 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
push bx
mov bx,takadr
mov al,taklev
mov [bx].takinvoke,al ; remember Take level of this cmd
pop bx
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 short kermt5 ; close any other take files
kermt7: cmp flags.extflg,0 ; exit flag set?
jne krmend ; ne = yes, exit
mov bx,takadr
mov al,[bx].takinvoke ; take level at start of command parse
cmp al,taklev ; same?
jne kermt10 ; ne = no, already closed
mov al,[bx].taktyp ; kind, file or macro
cmp al,take_file ; type of Take, file?
jne kermt8 ; ne = no (macro)
cmp takeerror,0 ; is Take Error off?
jne kermt9 ; ne = no, close Take file
kermt8: cmp al,take_macro ; regular macro?
jne kermt10 ; ne = no, leaves internal macro
cmp macroerror,0 ; is Macro Error off?
je kermt10 ; e = yes, error is not fatal
kermt9: call takclos ; close Take file or Macro
kermt10:jmp kermit ; e = no, get next command
krmend: mov flags.cxzflg,0 ; reset each time
mov flags.extflg,0
call far ptr exmacro ; find on_exit macro
jc krmend2 ; c = not found
; perform ON_EXIT macro
krmend1:cmp taklev,0 ; finished with macros?
je krmend2 ; e = yes
mov dx,prmptr ; get prompt string address
call mprompt ; set master reparse address to here
cmp taklev,0 ; still in on_exit?
je krmend2 ; e = no, exit to DOS
mov flags.cxzflg,0 ; reset each time
and flags.remflg,not dserver ; turn off server mode bit
mov dx,offset comtab ; keyword table
xor bx,bx ; no help
mov ah,cmkey
mov comand.impdo,1 ; allow implied "DO macro"
mov comand.cmcr,1 ; allow bare CR's
call comnd
jc krmend2 ; c = failure
mov comand.impdo,0 ; only on initial keyword, not here
call bx ; call the routine returned in BX
jnc krmend1 ; nc = success, keep doing commands
; end of ON_EXIT macro processing
krmend2:cmp taklev,0 ; in a Take file?
je krmend3 ; e = no
call takclos ; close take file, release buffer
jmp short krmend2 ; close any other take files
krmend3:mov bx,lclexit ; addr of sys dependent exit routine
or bx,bx ; sys dependent routines want service?
jz krmend4 ; z = no
call bx ; call it
jnc krmend4 ; nc = close
jmp kermit ; c = do not close
krmend4:call serrst ; just in case the port wasn't reset
call clscpi ; close log files
call far ptr emsclose ; close and return EMS memory
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
push ds ; save ds around these DOS calls
mov ax,cs ; compose full address of ^C routine
mov ds,ax ; segment is the code segment
mov dx,offset in3ad ; restore Control-C interrupt vector
mov al,23H ; interrupt 23H
mov ah,setintv ; 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,setintv ; do replacement (put it back)
int dos
pop ds
call cbrestore ; restore state of Control-Break Chk
krmend5: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: cmp taklev,0 ; in a Take file?
je exit2 ; e = no
call takclos ; close take file, release buffer
jmp short exit1 ; close any other take files
exit2: clc
ret
EXIT ENDP
; Resume For/While at the foot of the interation loop
CONTINUE proc near
mov temp,1 ; marker for continue
jmp short break1
CONTINUE endp
; Abandon current For/While statement
BREAKCMD proc near
mov temp,0 ; marker for break
break1: mov al,taklev ; Take level
or al,al ; in take/macro?
jz breakx ; z = no
mov bx,takadr
break2: test [bx].takattr,take_while ; is this a for/while/switch macro?
jnz break3 ; nz = yes
sub bx,size takinfo ; work backward
dec al
jnz break2 ; nz = have some
stc ; carry set to say for/while not
ret ; found
break3: cmp temp,0 ; Break?
jne break4 ; ne = no, Continue
push ax ; save found take level
call takclos ; close for/while macro
pop ax
mov bx,takadr ; new take level
cmp taklev,al ; did all at that level and above?
jae break3 ; ae = no
breakx: clc
ret
; Continue
break4: inc al ; look above the for/while macro
add bx,size takinfo
cmp al,taklev ; above the current macros?
ja breakx ; a = yes, quit
mov [bx].takcnt,0 ; exhaust the macro to end reading
jmp short break4
BREAKCMD endp
; Permit ELSE keyword right after failed IF statement
ELSECMD proc near
cmp oldifelse,0 ; ELSE permitted after failed IF?
je elsecmd1 ; e = no
mov oldifelse,0
clc ; let cmd parser read rest as cmd
ret
elsecmd1:
mov ah,cmline ; discard the line quietly
mov comand.cmblen,cmdblen ; set line capacity (length of rdbuf)
mov bx,offset rdbuf
xor dx,dx ; no help
call comnd
clc
ret
ELSECMD endp
; RETURN string string is placed in buffer retbuf
RETCMD proc near
mov ah,cmline
mov bx,offset retbuf+2 ; returned string
mov word ptr rdbuf,0
xor dx,dx
call comnd
jnc retcmd1
ret
retcmd1:mov word ptr retbuf,ax ; <count word> <returned string>
call poplevel ; do the pop
ret
RETCMD endp
; NOPUSH
PUSHPROC PROC NEAR
mov ah,cmeol
call comnd
jc pushp1 ; c = failure
mov nopush_flag,1 ; set nopush condition
pushp1: ret
PUSHPROC ENDP
code ends
code1 segment
assume cs:code1
exmacro proc far ; perform on_exit() macro
push bx
push cx
push si
mov bx,offset mcctab ; table of macro names
mov cl,[bx] ; number of names in table
xor ch,ch
jcxz exmacx ; z = empty table, do nothing
inc bx ; point to length of first name
exmac2: mov ax,[bx] ; length of this name
cmp ax,onexlen ; length same as desired keyword?
jne exmac3 ; ne = no, search again
mov si,bx
add si,2 ; point at first char of name
push cx ; save name counter
push di ; save reg
mov cx,onexlen ; length of name
mov di,offset onexit+2 ; point at desired macro name text
push es ; save reg
push ds
pop es ; make es use data segment
cld
repe cmpsb ; match strings
pop es ; need current si below
pop di
pop cx ; recover saved regs
jne exmac3 ; ne = no match
mov onexit+2,0 ; change name to be invisible
mov byte ptr [bx+2],0 ; change macro table name too
jmp short exmac4 ; e = matched
exmac3: add bx,ax ; step to next name, add name length
add bx,4 ; + count and def word ptr
loop exmac2 ; try next name
exmacx: pop si ; no macro, fail
pop cx
pop bx
stc ; say failure
ret
exmac4: call takopen_macro ; open a macro
jc exmacx ; c = failed
mov bx,takadr ; point to current macro structure
mov ax,ds ; text is in our data seg
mov [bx].takbuf,ax ; seg of definition string struc
mov [bx].takptr,offset onexit+2 ; where to read next command char
mov [bx].takcnt,onexlen ; number of chars in definition
mov [bx].takargc,0 ; store macro argument count
pop si
pop cx
pop bx
clc ; say success
ret
exmacro endp
; Close and return EMS memory, uses emsrbhandle and emsgshandle
emsclose proc far
mov ah,45h ; release handle and memory
mov dx,emsrbhandle ; handle
or dx,dx ; is handle valid (not -1)?
jl emsclose1 ; l = no
int 67h ; ems interrupt
emsclose1:mov emsrbhandle,-1
mov ah,45h ; release handle and memory
mov dx,emsgshandle ; handle
or dx,dx ; is handle valid (not -1)?
jl emsclose2 ; l = no
int 67h ; ems interrupt
emsclose2:mov emsgshandle,-1
cmp xmsrhandle,0 ; XMS rollback handle, valid?
je emsclose3 ; e = no
mov dx,xmsrhandle
mov ah,0ah ; XMS free block
call dword ptr xmsep ; XMS manager entry point
mov xmsrhandle,0
emsclose3:cmp xmsghandle,0 ; XMS graphics handle, valid?
je emsclose4 ; e = no
mov dx,xmsghandle
mov ah,0ah ; XMS free block
call dword ptr xmsep ; XMS manager entry point
mov xmsghandle,0
emsclose4:ret
emsclose endp
code1 ends
code segment
assume cs:code
; TAKE commands from a file, and allow a path name
TAKE PROC NEAR
mov kstatus,kssuc ; 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 bx,offset tmpbuf ; work buffer
mov tmpbuf,0
mov dx,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: push dx
int dos
pop dx
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 cmdfile,0 ; clear latest cmd file info
mov kstatus,kstake ; status, Take failed
clc ; we've done all error displays
ret
; TAKE4: enter with ax=filename ptr
TAKE4: push dx ; save filename string
push ax ; save pointer
call takopen_file ; open take file
pop ax
pop dx
jc take6 ; c = failure
call save_cmdfile ; save path+name of file from dx
push bx
mov bx,takadr ; get current frame ptr
mov [bx].takhnd,ax ; save 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
; TAKE-QUIT (STOP) Exit all Take files immediately but gracefully
TAKEQIT PROC NEAR
xor ax,ax
mov errlev,al ; return value in ERRORLEVEL
mov kstatus,ax ; and in STATUS
mov ah,cmword ; get optional error value
mov bx,offset rdbuf
mov dx,offset stophlp ; help on numerical argument
mov comand.cmcr,1 ; bare c/r's allowed
call comnd
mov comand.cmcr,0 ; restore normal state
jc takqit3 ; c = failure
mov ah,cmline ; get optional error msg
mov bx,offset rdbuf+100
mov dx,offset stophlp ; help on numerical argument
mov comand.cmcr,1 ; bare c/r's allowed
mov comand.cmdonum,1 ; \number conversion allowed
call comnd
mov comand.cmcr,0 ; restore normal state
jc takqit3 ; c = failure
push ax ; save string count
mov ah,cmeol ; confirm
call comnd
pop ax
jc takqit3
mov domath_ptr,offset rdbuf ; string
mov domath_cnt,ax ; string length
call domath ; convert to number in dx:ax
cmp domath_cnt,0
jne takqit2 ; ne = did not convert whole word
mov errlev,al ; return value in ERRORLEVEL
mov kstatus,ax ; and in STATUS
mov si,offset rdbuf+100
takqit4:lodsb ; read a msg char
or al,al ; null terminator?
jz takqit2 ; z = empty string
cmp al,' ' ; leading white space?
je takqit4 ; be = leading white space
dec si ; backup to non-white char
mov dx,offset crlf
mov ah,prstr
int dos
mov dx,si ; message pointer
call prtasz
takqit2:xor ch,ch
mov cl,taklev ; number of Take levels active
jcxz takqit3 ; z = none
cmp cmdlinetake,cl ; have DOS level command line?
jae takqit3 ; ae = yes, don't close it here
call takclos ; close current Take file
jmp short takqit2 ; repeat until all are closed
takqit3:clc ; success
ret
TAKEQIT ENDP
code ends
code1 segment
assume cs:code1
TAKRD PROC FAR
push ax
push bx
push cx
push dx
push di
push es
push temp
mov bx,takadr
cmp [bx].taktyp,take_file ; get type of take (file?)
je takrd0 ; e = take file, not macro
jmp takrd30
takrd0: xor ax,ax
mov [bx].takcnt,ax ; number of bytes to be read
mov [bx].takptr,ax ; offset of first new character
mov temp,ax ; prime the disk reader
mov tempptr,offset tmpbuf + 1
takrd1: mov ax,[bx].takbuf ; segment of Take buffer
mov es,ax
mov cx,tbufsiz ; # of bytes to examine
xor dx,dx ; dl = 0 for store data (vs comments)
xor di,di ; offset in buffer where data starts
takrd2: call takrworker ; fill take buffer, return a byte
jc takrd30 ; c = failure
add word ptr [bx].takseek,1
adc word ptr [bx].takseek+2,0 ; seek distance, bytes
mov bx,takadr
cmp al,TAB ; TAB?
jne takrd2a ; ne = no
mov al,' ' ; convert to space
takrd2a:cmp al,LF ; line terminator?
je takrd2 ; e = yes, ignore it
cmp al,CR ; internal line terminator?
je takrd5 ; e = yes, always write in buffer
or dl,dl ; store data (vs discard comments)?
jnz takrd2 ; nz = no, discard, read comments
cmp al,';' ; start of comment indicator?
jne takrd5 ; ne = no
cmp [bx].takcnt,0 ; bytes examined in buffer, so far
je takrd4 ; e = nothing, so no escape either
mov ah,byte ptr es:[di-1] ; preceeding char
cmp ah,'\' ; escaped?
jne takrd3 ; ne = no
dec di ; overwrite '\' with ';'
dec [bx].takcnt
dec cx
jmp short takrd5
takrd3: cmp ah,' ' ; whitespace precedessor?
je takrd4 ; e = yes, ';' starts a comment
cmp ah,TAB ; this kind too?
jne takrd5 ; ne = no, not a comment
takrd4: mov dl,1 ; say start discarding comments
jmp short takrd2 ; read more comments
takrd5: cmp al,' ' ; space
jne takrd6 ; ne = no
cmp [bx].takcnt,0 ; anything in line buffer yet?
je takrd7 ; e = no, omit leading space
takrd6: inc [bx].takcnt ; bytes accepted into buffer so far
stosb ; store byte
cmp al,CR ; ending on CR?
jne takrd7 ; ne = no
or dl,dl ; processing comment?
jnz takrd30 ; nz = yes, CR ends comment line
cmp [bx].takcnt,1 ; more than just CR?
jbe takrd30 ; be = no
cmp byte ptr es:[di-2],'-' ; hyphenated line?
jne takrd30 ; ne = no
sub [bx].takcnt,2 ; remove '-' and CR from buffer
sub di,2 ; back over both
add cx,2 ; add back capacity
xor dl,dl ; end of comment
takrd7: loop takrd2
takrd30:mov cx,[bx].takcnt ; trim trailing spaces. line count
cmp cx,1
jbe takrd34 ; be = empty line or eof
mov di,cx
dec di ; count of 1 is only es:[0]
cmp byte ptr es:[di-1],' ' ; ended on text?
ja takrd34 ; a = yes, do not trim
xor di,di ; es:di(0) is buffer
dec cx ; back over final CR
jcxz takrd34 ; z = only CR remained
add di,cx
dec di ; look at last byte - 1
mov al,' ' ; scan for
std
repe scasb
cld
jne takrd32 ; ne = does not over decrement
dec di
takrd32:inc di
inc di
mov byte ptr es:[di],CR ; final CR goes here
inc di
mov [bx].takcnt,di ; new count
takrd34:pop temp
pop es
pop di
pop dx
pop cx
pop bx
pop ax
ret
TAKRD ENDP
; Read one byte from disk file into al.
; Return carry set if failure
takrworker proc near
push bx
push cx
push dx
push es
cmp temp,0 ; bytes to be read from tmpbuf
jne takrwork1 ; ne = have things in the buffer
mov bx,takadr
mov dx,word ptr [bx].takseek
mov cx,word ptr [bx].takseek+2
mov bx,[bx].takhnd ; bx = file handle
mov ah,lseek ; seek
mov al,0 ; from start of file
int dos
mov temp,0 ; bytes remaining unread
mov cx,length tmpbuf - 1 ; # of bytes to read
mov dx,offset tmpbuf + 1 ; ds:dx = buffer, skip count word
mov tempptr,dx ; where to read from tmpbuf next time
mov ah,readf2 ; read file
int dos
jc takrwork2 ; c = error, preserve ax
mov temp,ax ; returned byte count
or ax,ax
jz takrwork2 ; z = nothing left
takrwork1:
mov bx,tempptr ; where to read a byte
mov al,[bx] ; read the byte
inc tempptr ; where to read next time
dec temp ; say another byte read
clc
jmp short takrwork3 ; succeed
takrwork2:stc ; fail
takrwork3:pop es
pop dx
pop cx
pop bx
ret
takrworker endp
code1 ends
code segment
assume cs:code
; 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 spath ; is it around?
jc rdini2 ; c = no
call take2 ; let Take do error msgs
clc ; force success
ret
rdini1: call spath ; is it around?
jc rdini2 ; c = 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, MSRxxx.PCH or MSKERMIT.PCH has the following format:
; 301 \Xxxxx Text to display upon successful patch load
; ;301 for V3.01. 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
;
; The 1st xx is the original value of the 1st byte @seg:offset for comparison.
; A 00 value says don't compare. Subsequent xx's are replacement bytes.
; CS & DS lines may be intermixed. AS & BS segments may be used when some
; external module sets words aseg & bseg to a seg-base.
; This mechanism expects file msscmd.obj to be linked first.
PATCH proc
mov bx,offset rdbuf ; optional path prefix
mov rdbuf,0
mov dx,offset pathlp
mov ah,cmword
call comnd
jc patch1
mov ah,cmeol
call comnd
jc patch1
xor ax,ax
xchg al,patched ; clear and test patched
test al,al
jz patch1 ; 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
jc patch2 ; c = NG
patch1: ret
patch2: mov dx,offset ermes8 ; Fatal error
mov ah,prstr
int dos
jmp krmend ; force exit
ptchr: mov dx,offset rdbuf ; optional path
call strlen ; get length, if any
jcxz ptchr1 ; z = nothing
mov si,dx
add si,cx
cmp byte ptr [si-1],'\' ; ends with path specifier?
je ptchr1 ; e = yes
cmp byte ptr [si-1],':' ; or a drive specifier?
je ptchr1 ; e = yes
mov word ptr [si],'\'+0 ; add '\' + null
ptchr1: mov di,offset rdbuf+66 ; path goes here
mov si,offset rdbuf+140 ; filename goes here, discard
mov byte ptr [di],0 ; clear
call fparse ; split optional path
mov si,offset ptchnam ; add name of patch file
call strcat
mov ax,di ; setup filename pointer for rdini1
call rdini1 ; let rdini try to find it & do take stuff
jnc ptch1 ; nc = file msrxxx.pch was found
mov di,offset rdbuf+66 ; path goes here
mov si,offset rdbuf+140 ; filename goes here, discard
mov byte ptr [di],0 ; clear
mov dx,offset rdbuf ; source string again
call fparse ; split optional path
mov si,offset ptchnam2 ; try alternate name
mov byte ptr [di],0 ; insert terminator
call strcat
mov ax,di ; setup filename pointer for rdini1
call rdini1 ; let rdini try to find it & do take stuff
jnc ptch1 ; nc = file msrxxx.pch was found
mov dx,offset ermes7 ; say file not found
mov ah,prstr
int dos
clc
ret
ptch1: mov al,taklev ; remember initial take level
mov pttemp,al ; when it changes it is EOF & done
mov comand.cmkeep,1 ; keep Take open after eof
mov comand.cmcr,1 ; bare cr's ok, to prevent prserr @EOF
call ptchrl ; read 1st line's 1st 'word'
jc ptch2 ; c = trouble
jz ptch3 ; z = EOF
mov si,offset tmpbuf+1
mov domath_cnt,4 ; length of field
mov domath_ptr,si
mov domath_msg,1 ; don't complain
call domath ; convert number to binary
cmp domath_cnt,0
jne ptch2 ; ne = bad number, or none
cmp ax,version ; does it match this version?
je ptch4 ; e = yes
ptch2: mov al,pttemp ; if take level has changed,
xor al,taklev ; we're already out of patch file
jnz ptch3 ; nz = change in take level
call takclos ; close patch file
ptch3: mov dx,offset ermes6
mov ah,prstr ; issue warning msg
int dos
clc
ret
ptch4: mov dx,offset tmpbuf+1
call ptchrw ; read 2nd "word", 1st line
jc ptch2 ; c = NG
mov si,offset tmpbuf+1
mov domath_cnt,6 ; arbitrary length
mov domath_ptr,si
call domath ; convert 2nd "magic number"
cmp domath_cnt,0
jne ptch2 ; ne = bad number, or none
cmp ax,totpar ; is it the total paragraphs memini computed?
jne ptch2 ; ne = no
mov bx,offset buff ; place to stash 1st lines patch/version msg
xor dx,dx ; help
mov ah,cmline ; read it
call comnd
ptch5: call ptchrl ; read CS:xxxx or DS:xxxx
jc ptch6
jz ptch7 ; z = EOF
cmp ax,7 ; were 7 chars read?
jne ptch6 ; ne = no
mov si,offset tmpbuf+1
and word ptr[si],not 2020h ; convert to upper case
cld
lodsb ; get the seg char
cmp word ptr[si],':S' ; S:, actually
je ptch8 ; e = ok
ptch6: stc ; error exit
ret
ptch7: test flags.remflg,dquiet ; quiet display?
jnz ptch7a ; nz = yes, skip msg
mov ah,prstr
mov dx,offset verident ; display version header
int dos
ptch7a: clc
ret
ptch8: push ds
pop es
mov di,offset segstr
mov cx,lsegstr ; search for seg char in segstr
repne scasb
jne ptch6 ; ne = not found
sub di,offset segstr+1 ; distance spanned
shl di,1 ; make a word index
mov bx,segtab[di] ; bx = seg-base
or bx,bx ; seg-base = 0, disabled for patching
jz ptch6 ; z = 0, no patching
mov word ptr[si],'X\' ; put '\X' in front for hex
mov domath_cnt,16 ; arbitrary length
mov domath_ptr,si
call domath ; convert number to binary
cmp domath_cnt,0
jne ptch6 ; ne = bad number, or none
push bx ; save seg being patched
push ax ; save location being patched
mov tmpbuf+64,0 ; clear replacement byte count
ptch9: mov dx,offset tmpbuf+4
call ptchrw ; read replacement byte follwing '\X'
jnc ptch11 ; nc = OK
ptch10: pop ax ; clean stack & error return
pop bx
stc
ret
ptch11: or ax,ax ; EOL?
jnz ptch13 ; nz = no
mov si,offset tmpbuf+64
cld
lodsb ; replacement byte count
cmp al,2 ; gotta be at least 2
jb ptch10 ; b = too few
xor ch,ch
mov cl,al ; replacement count
pop di ; patch location
pop es ; patch segment
lodsb ; al = comparison byte
or al,al ; key value to ignore comparison?
jz ptch12 ; z = 0, yes
cmp byte ptr es:[di],al ; do read check on memory image
jne ptch6 ; ne = no match, fail now
ptch12: dec cx ; adjust for comparison byte
rep movsb ; make patch
jmp ptch5 ; loop to read next line
ptch13: cmp al,2 ; 2 chars req'd for replacement byte
jne ptch10 ; ne = bad
mov domath_ptr,offset tmpbuf+2; convert it
mov domath_cnt,16 ; arbitrary length
call domath ; convert number to binary
cmp domath_cnt,0
jne ptch10 ; ne = 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 short ptch9 ; loop for next byte
ptchrl: mov dx,offset patpmt ; read 1st word, next line to tmpbuf+1
call prompt
mov dx,offset tmpbuf+1
call ptchrw
jc ptchrb ; c = NG
push dx
mov dl,pttemp ; old Take level
xor dl,taklev ; current Take level, changed
pop dx
jz ptchra ; z = no, not EOF
xor ax,ax ; set z flag for EOF
ret
ptchra: or ax,ax ; empty or comment line?
jz ptchrl ; z = empty or comment, ignore
ptchrb: ret
ptchrw: push dx
mov dl,pttemp ; old Take level
xor dl,taklev ; current Take level, changed?
pop dx
jz ptchrwa ; z = no, not EOF
xor ax,ax ; set z flag for EOF
ret
ptchrwa:mov ah,cmword
mov comand.cmper,1 ; prohibit substitution variable expansion
xor bx,bx ; 'help' ptr
xchg bx,dx ; order for comnd
call comnd ; line length is in ax
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 cmdlinetake,0 ; flag for DOS command line Take
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_macro ; open take as macro
mov bx,takadr
mov ax,150 ; space needed: DOS line + ",stay"
call malloc ; hope it works
mov [bx].takbuf,ax ; memory segment
or [bx].takattr,take_malloc ; remember to dispose via takclos
mov es,ax ; segment of buffer
mov di,2 ; skip count word
push psp
pop DS ; DS = PSP
xor dx,dx ; clear brace count
gcmdl3: or cx,cx ; 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
or dx,dx ; inside braces?
jnz gcmdl9 ; nz = yes, retain embedded commas
mov al,cr ; convert to cr
jmp short gcmdl9 ; store it
gcmdl4: call bracechk ; check for curly braces
jc gcmdl9 ; c = found and counted brace
or dx,dx ; outside braces?
jnz gcmdl9 ; nz = no, ignore flag
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: or cx,cx ; 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
or cx,cx ; 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: or cx,cx ; 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:or cx,cx ; 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,2 ; skip count word
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:word ptr [0],cx ; store count word
xor dx,dx ; brace count
or cx,cx
jg gcmdl11 ; g = material at hand
call takclos ; empty take file
jmp short gcmdl14 ; finish up
; scan for command "stay"
gcmdl11:mov ax,es:[si] ; get 2 bytes, cx and si are set above
inc si ; increment by only one char
dec cx
call bracechk ; check for braces
jc gcmdl12 ; c = brace found
cmp al,' ' ; separator?
jbe gcmdl12 ; be = yes, keep looking
cmp al,',' ; comma separator?
je gcmdl12 ; e = yes
or dx,dx ; within braces?
jnz gcmdl12 ; nz = yes, skip STAY search
or ax,2020h ; convert to lower case
cmp ax,'ts' ; first two letters of stay
jne gcmdl12 ; ne = no match
mov ax,es:[si+1] ; next two letters (stay vs status)
or ax,2020h ; convert to lower case
cmp ax,'ya' ; same as our pattern?
jne gcmdl12 ; ne = no match
add si,3 ; char after "stay"
sub cx,3
; check for separator or end of macro
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
or cx,cx ; at end of macro?
jle gcmdl13 ; yes, consider current match correct
gcmdl12:or cx,cx ; done yet? ("stay" not found)
jg gcmdl11 ; g = not yet, look some more
mov cmdlinetake,1 ; remember doing DOS cmd line Take
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,2 ; init buffer ptr
mov cx,[bx].takcnt ; count of bytes in buffer
mov es:[0],cx ; count of bytes in Take buffer
gcmdl14:pop es
ret
gcmdlin endp
; Curly brace checker. Examine (and preserve) char in AL. Count up/down
; braces in dx because DS is unknown here
bracechk proc near
cmp al,braceop ; opening brace?
jne bracech1 ; ne = no
inc dx ; count up braces
stc ; say brace seen
ret
bracech1:cmp al,bracecl ; closing brace
jne bracech3 ; ne = no
sub dx,1 ; count down with sign
jns bracech2 ; ns = no underflow
xor dx,dx ; don't go below zero
bracech2:stc ; say brace detected
ret
bracech3:clc ; say brace not found
ret
bracechk 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
push di
mov di,ax
push di
push ax
mov dx,di ; get string length
call strlen
cld
push es
mov ax,ds
mov es,ax
mov al,'\' ; look for path separator
repne scasb ; scan es:di for separator
pop es
pop ax
pop di
je spath14 ; e = found, use as-is
cmp byte ptr [di+1],':' ; drive already given?
jne spath10 ; ne = no
spath14:pop di
clc
ret ; path stuff is already in ax
spath10:push bx
mov bx,[di]
and bx,not 2020h ; to upper
cmp bx,'UN' ; look for DOS 5 NUL
jne spath11 ; ne = mismatch
mov bx,[di+2]
and bl,not 20h
cmp bx,'L'+0
spath11:pop bx
jne spath12 ; ne = mismatch
pop di
clc
ret
spath12:push ax
mov di,offset decbuf+64 ; where results will be returned
mov ah,gcurdsk ; get current disk
int dos
inc al ; make 1 == A (not zero)
add al,'A'-1 ; make al alphabetic disk drive again
mov [di],al ; put it into original path descriptor
inc di
mov word ptr [di],'\:' ; add drive specifier too
add di,2
push si
mov si,di
mov ah,gcd ; get current directory (path really)
xor dl,dl ; use current drive
int dos
pop si
push dx
mov dx,di ; find end of string
call strlen
pop dx
add di,cx ; step after path
pop ax
push si
mov dx,ax ; this is user's file name
mov si,offset decbuf+200 ; filename goes here, temp
push di ; preserve di from above
mov di,offset decbuf+220 ; far away, path, temp
call fparse ; get filename to ds:si
pop di
cmp byte ptr [di-1],2fh ; does path end with switch char?
je spath13 ; yes, don't put one in
cmp byte ptr [di-1],5ch ; how about this one?
je spath13 ; yes, don't put it in
mov byte ptr [di],5ch ; else add one
inc di
spath13:lodsb ; get filename character
mov byte ptr [di],al ; copy filename char to output buffer
inc di
or al,al ; end of string?
jnz spath13 ; nz = no, copy rest of name
pop si ; restore postion in path string
pop di
mov ax,offset decbuf+64 ; return results in ax
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 ; ne = 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: call skpath ; search kermit's path
jnc spath8a ; nc = located file
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 short 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
spath8a: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
; Search Kermit's path for file. Return carry clear if found, else carry set.
; Worker for spath above.
skpath proc near
mov si,seg kpath ; Kermit's path string
mov es,si
mov si,offset kpath
mov di,offset decbuf+64 ; place to put name
skpath1:mov al,es:[si] ; get a byte from string
inc si
cmp al,';' ; end of this part?
je skpath3 ; yes, break loop
or al,al ; maybe end of string?
jnz skpath2 ; nz = no, keep going
dec si ; back up to null for later rereading
jmp short skpath3 ; and break loop
skpath2:mov byte ptr [di],al ; else stick in dest string
inc di
jmp short skpath1 ; and continue
skpath3:cld
mov si,bx ; this is user's file name
cmp byte ptr [di-1],2fh ; does path end with switch char?
je skpath4 ; yes, don't put one in
cmp byte ptr [di-1],5ch ; how about this one?
je skpath4 ; yes, don't put it in
mov byte ptr [di],5ch ; else add one
inc di
skpath4:lodsb ; get filename character
mov byte ptr [di],al ; copy filename char to output buffer
inc di
or al,al ; end of string?
jnz skpath4 ; nz = no, copy rest of name
mov ax,offset decbuf+64
push bx
call isfile ; is it a file?
pop bx
jnc skpath5 ; nc = yes
ret
skpath5:test byte ptr filtst.dta+21,10H ; subdirectory name?
jnz skpath6 ; nz = yes
clc ; report file found (AX as offset)
ret
skpath6:stc ; report failure
ret
skpath endp
; Put Kermit's Enviroment Path indicator into string kpath
mkkpath proc near
mov bx,offset rdbuf
mov word ptr rdbuf,0
xor dx,dx
mov comand.cmblen,64 ; 64 bytes max
mov ah,cmword ; get a word, with semicolons
call comnd
jnc mkkpath1 ; nc = success
ret
mkkpath1:mov cx,ax ; get string length
push si
push di
push es
mov si,offset rdbuf ; source
mov di,seg kpath ; storage spot
mov es,di
mov di,offset kpath
cld
rep movsb ; copy
mov word ptr es:[di],0 ; terminate
pop es
pop di
pop si
ret
mkkpath 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
; getdostemp: copy TEMP= environment string into dostemp
getcsp proc near
mov bx,offset cmspnam ; find COMSPEC=
mov cx,cmsplen ; its 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 ; its length
mov di,offset shellbuf ; where to store string
jmp short getccom ; do common worker
getdostemp:mov bx,offset dostempname ; fine TEMP=
mov cx,dostempnlen ; its length
mov di,offset dostemp+3 ; where to store string
getccom:push es
call getenv ; get environment offset into dx
jc getcs3 ; c = not found
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
getcs3: 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_macro ; open Take buffer in macro space
jnc getpar2 ; nc = success
getpar1a:mov flags.extflg,1 ; say exit now
pop es ; clean stack
pop dx
pop ds
jmp getparx ; exit with fatal error
getpar2:mov ax,tbufsiz ; take buffer size (bytes)
call malloc ; get memory, seg returned in AX
jc getpar1a ; c = failed
mov bx,takadr ; bx = Take data structure
mov [bx].takbuf,ax ; segment of memory
or [bx].takattr,take_malloc ; remember to dispose via takclos
mov es,ax ; ES = segment of buffer
mov di,2 ; skip count word 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:[0],cx ; store count byte
mov [bx].takptr,2 ; 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?
jle getpar9 ; le = 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
getenv5: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 short geten1 ; loop thru rest of environment
geten2: add di,cx ; skip to definition
geten6: mov si,bx ; name
add si,cx ; length
cmp byte ptr [si-1],'=' ; caller wanted '=' as last char?
je geten7 ; e = yes, and we found it
mov al,es:[di] ; get next char
cmp al,'=' ; at the equals sign?
je geten7 ; e = yes
inc di ; point at next char
cmp al,' ' ; white space?
jbe geten6 ; be = yes, skip over this
dec di ; backup
jmp short getenv5 ; not a match, keep looking
geten7: 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
; SETENV
; Put "NAME=string" to the DOS master environment, uses undocumented Int 2Eh
setenv proc near
mov bx,offset decbuf+1 ; borrowed buffer
mov word ptr [bx],'ES'
mov word ptr [bx+2],' T' ; preload "SET "
add bx,4 ; where rest of text goes
mov dx,offset setenvhlp ; help
mov comand.cmblen,126 - 4 ; max buffer length
mov ah,cmline ; get line of text
call comnd
jnc setenv1 ; nc = success
ret ; failure
setenv1:mov bx,offset decbuf+1+4 ; look after "SET "
add bx,ax ; plus length of user string
mov word ptr [bx],CR ; terminate in CR NUL
sub bx,offset decbuf ; compute length
mov decbuf,bl ; <count byte><text>CR
mov ah,getintv ; does Int 2eh exist?
mov al,2eh
int dos
jnc setenv2 ; nc = call succeeded
ret
setenv2:cmp byte ptr es:[bx],0cfh ; is an IRET?
jne setenv3 ; no, assume vector exists
stc
ret
setenv3:push si
push di
push bp
cli
mov ax,ss ; save ss:sp
mov word ptr ssave+2,ax
mov word ptr ssave,sp
sti
cld
mov si,offset decbuf ; string pointer to ds:si
int 2eh ; Command.com, do contents of ds:si
mov bx,data ; restore segment registers
mov ds,bx ; reset data segment
mov es,bx ; and extra segment
cli
mov bx,word ptr ssave+2
mov ss,bx ; and stack segment
mov sp,word ptr ssave ; restore stack ptr
sti
pop bp
pop di
pop si
clc
ret
setenv endp
; Store drive:path\ of where Kermit was started into string startup
getargv proc near
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
mov cx,0ffffh ; bogus length
getargv1:xor al,al ; 0 = marker to look for
repne scasb ; search for it
cmp es:byte ptr [di],0 ; end of environment (double null)?
jne getargv1 ; ne = no
inc di ; skip single null
cmp es:word ptr [di],1 ; marker for argv[0]?
jne getargv5 ; ne = no
add di,2 ; skip word
mov si,offset startup ; startup string
mov cx,63 ; max length of wanted string
getargv2:mov al,es:[di] ; get char
mov [si],al ; store in startup string
inc si
inc di
or al,al ; null terminator?
loopnz getargv2 ; nz = no
; trim off filename part
mov cx,si
sub cx,offset startup
dec si
getargv3:cmp byte ptr [si],':' ; back to drive terminator?
jne getargv3a ; ne = no
mov byte ptr [si+1],'\' ; make drive:\ syntax for root
inc si ; move over separators
jmp short getargv4 ; done
getargv3a:cmp byte ptr [si],'\' ; or path separator?
je getargv4 ; e = yes
dec si ; backup
loop getargv3
getargv4:mov byte ptr [si+1],0 ; terminate string
getargv5:pop es
pop di
pop si
ret
getargv 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
mov al,tmpbuf ; get time/date format code
mov tdfmt,al ; save it
ret
gettsep endp
STAY PROC NEAR
clc
ret
STAY ENDP
; CHECK <build features>, returns success if feature is present
CHECK proc near
mov kstatus,kssuc ; global status
mov ah,cmkey
mov dx,offset featab ; feature table
xor bx,bx ; table is help
call comnd
jnc check1 ; nc = success
mov kstatus,ksgen ; general failure
ret
check1: push bx
mov ah,cmeol
call comnd
pop bx
jnc check1a
mov kstatus,ksgen ; general failure
ret
check1a:mov di,offset tmpbuf
cmp bx,1 ; graphics?
jne check2 ; ne = no
mov si,offset msggraph
call strcpy
ifdef no_graphics
mov kstatus,ksgen
mov si,offset msgnot
call strcat
endif
jmp short check6
check2: cmp bx,2 ; tcp/ip?
jne check3 ; ne = no
mov si,offset msgtcpip
call strcpy
ifdef no_tcp
mov kstatus,ksgen
mov si,offset msgnot
call strcat
endif
jmp short check6
check3: cmp bx,3 ; networks?
jne check4 ; ne = no
mov si,offset msgnetwork
call strcpy
ifdef no_network
mov kstatus,ksgen
mov si,offset msgnot
call strcat
endif
jmp short check6
check4: cmp bx,4 ; terminals?
jne check5 ; ne = no
mov si,offset msgterm ; terminals
call strcpy
ifdef no_terminal
mov kstatus,ksgen
mov si,offset msgnot
call strcat
endif
jmp short check6
check5: mov si,offset msgif ; IF statements
call strcpy
check6: mov si,offset msgavail
call strcat
cmp taklev,0 ; at top level?
jne checkx ; ne = no, be quiet
mov dx,offset tmpbuf ; show msg
mov ah,prstr
int dos
checkx: ret ; return status
CHECK endp
TESTCOM proc near
mov kstatus,kssuc ; global status
mov ah,cmkey
mov dx,offset chktab ; check comm port table
xor bx,bx ; table is help
call comnd
jnc testc1 ; nc = success
mov kstatus,ksgen ; general failure
ret
testc1: mov al,flags.comflg ; save current comms port flag
push ax
mov flags.comflg,bl ; port number, 1..4
call tstport ; see if real UART, carry set if not
pop ax
mov flags.comflg,al ; restore flag
mov dx,offset erms39 ; say UART
jnc testc2 ; nc = real UART
mov dx,offset erms38 ; say non-UART
mov kstatus,ksgen ; set failure state for non-UART
testc2: cmp taklev,0 ; in a Take file or macro?
je testc3 ; e = no, display
cmp flags.takflg,0 ; Take echo off?
je testc4 ; e = yes, do not display
testc3: mov ah,prstr
int dos
testc4: ret
TESTCOM endp
CLS proc near ; Clear command level screen
mov ah,cmeol ; get a confirmation
call comnd
jc cls1 ; c = failure
call cmblnk ; blank the screen
call locate ; put cursor at home position
cls1: ret
CLS endp
COMNT PROC NEAR ; COMMENT command
mov ah,cmline
mov bx,offset tmpbuf
xor dx,dx ; help
call comnd
jc comnt1
mov ah,cmeol
call comnd
comnt1: ret
COMNT ENDP
; change working directory
cwdir proc near
mov kstatus,kssuc ; global status
mov ah,cmword
mov bx,offset tmpbuf
mov dx,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 si,offset tmpbuf ; cdsr wants drive/path ptr in si
call cdsr ; common CD sub-routine
jnc cwd2 ; nc = success
mov kstatus,ksgen ; global status for unsuccess
cwd2: cmp taklev,0 ; in a Take file or macro?
je cwd3 ; e = no, do echo
cmp flags.takflg,0 ; ok to echo?
je cwd4 ; e = no
cwd3: push dx
mov dx,offset crlf ; msgs from cdsr don't include this
mov ah,prstr ; so let's do it now
int dos
pop dx
call prtasz ; output current drive/path or err msg
cwd4: clc
ret
cwdir 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,kssuc ; 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
push ax
mov ah,cmeol
call comnd
pop ax
jc delet0
cmp apctrap,0 ; disable from APC
jne delet0 ; ne = yes
or ax,ax ; anything given?
jnz delet1 ; nz = yes
mov ah,prstr
mov dx,offset ermes1 ; say need something
int dos
clc ; say success
delet0: mov kstatus,ksgen ; global status
ret
delet1: mov si,offset tmpbuf ; source
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,ksgen ; 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 <optional drive letter>
CHKDSK PROC NEAR ; Space command
mov kstatus,kssuc ; global status
mov bx,offset tmpbuf ; buffer
mov tmpbuf,0 ; init to null
mov dx,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,ksgen ; 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
; Get directory listing
DIRECT PROC NEAR
mov kstatus,kssuc ; 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
direct0:mov kstatus,ksgen ; global status
ret ; failure
direct1:mov ah,cmeol
call comnd
jc direct0
mov word ptr [bx],0 ; plant terminator
mov si,offset tmpbuf
push si
add si,temp ; user's text after ' dir '
mov cl,curdsk ; current drive number ('A'=1)
add cl,'A'-1 ; make a letter
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
mov kstatus,ksgen ; global status
stc
ret
direct3:jmp crun ; join run cmd from there
DIRECT ENDP
; This is the 'HELP' command. It gives a list of the commands
; And INTRO command
INTRO proc near
mov kstatus,kssuc ; global status
mov ah,cmeol
call comnd ; get a confirm
jnc intro1 ; nc = success
ret ; failure
intro1:
ifdef no_terminal
ret
else
push es
mov ax,seg intrhlp ; all Intro text is in data1
mov es,ax
mov al,trans.escchr ; Connect mode escape char
cmp al,' ' ; printable now?
jae intro2 ; ae = yes
add al,40h ; make control visible
intro2: mov si,offset intrhlp1
mov es:[si],al
mov si,offset intrhlp2
mov es:[si],al
mov si,offset intrhlp ; Intro text in seg data1
xor bx,bx ; line counter
cld
jmp short help2 ; common tail
endif ; ifdef no_terminal
INTRO endp
HELP PROC NEAR
mov kstatus,kssuc ; global status
mov ah,cmeol
call comnd ; get a confirm
jnc help1 ; nc = success
ret ; failure
help1: mov si,offset qckhlp ; help text in seg data1
xor bx,bx ; line counter
push es
mov ax,seg qckhlp ; all help text is in data1
mov es,ax
cld
HELP2: mov al,es:[si] ; read a help msg byte
inc si
cmp al,'$' ; end of message?
je help3 ; e = yes, stop
mov ah,conout
mov dl,al
int dos ; display byte
cmp dl,LF ; line break?
jne help2 ; ne = no
inc bl ; count line
cmp bl,dos_bottom ; (24) time for a more msg?
jbe help2 ; be = not yet
xor bl,bl ; reset line count
call iseof ; are we at EOF, such as from disk?
jc help2 ; c = yes, ignore more msg
push es
push si
mov ah,prstr
mov dx,offset moremsg ; "... more..." msg
int dos
pop si
pop es
mov ah,coninq ; read the char from file, not device
int dos
cmp al,3 ; a ^C?
je short help3 ; e = yes, stop the display
cmp al,'?' ; query?
je helpquery ; e = yes
push bx ; save line counter
push es ; and read pointer
push si
call ctlu ; clear display's line, reuse it
pop si
pop es
pop bx
jmp short help2 ; continue
help3: pop es
clc
ret
helpquery:pop es
mov ah,prstr ; show help summary screen
mov dx,offset crlf ; a few blank lines
int dos
int dos
int dos
push ds
mov dx,seg tophlp
mov ds,dx
mov dx,offset tophlp ; show usual cryptic help
int dos
pop ds
clc
ret
HELP ENDP
; the version command - print our version number
prvers proc near
mov kstatus,kssuc ; 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
mov ah,prstr
mov dx,offset copyright2 ; full copyright notice
int dos
clc
prvers1:ret
prvers endp
; SHOW command dispatcher
showcmd proc near
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,kssuc ; 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, allows | more
mov bx,di ; next available byte
mov dx,offset filmsg ; In case user wants help
call comnd
jc typec1 ; c = failure
push ax
mov ah,cmeol
call comnd
pop ax
jc typec1
or ax,ax ; any text given?
jnz typec2 ; nz = yes
mov ah,prstr
mov dx,offset ermes1 ; say need more info
int dos
mov kstatus,ksgen ; global status
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,ksgen ; 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: cmp nopush_flag,0 ; pushing allowed?
jne fpush1 ; ne = no
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
fpush1: stc ; fail
ret
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: cmp apctrap,0 ; disable from APC
jne run3 ; ne = yes, fail
cmp nopush_flag,0 ; pushing disabled?
jne run3 ; ne = yes, fail
or ax,ax ; 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
run3: stc ; failure exit
ret
RUN ENDP
; crun - run an arbitrary program.
; 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
mov ax,ss ; save ss:sp
mov word ptr ssave+2,ax
mov word ptr ssave,sp
xor al,al ; 0 = load and execute (DX has name)
mov ah,exec
int dos ; go run command.com
mov bx,data ; restore segment registers
mov ds,bx ; reset data segment
mov es,bx ; and extra segment
cli
mov bx,word ptr ssave+2
mov ss,bx ; and stack segment
mov sp,word ptr ssave ; restore stack ptr
sti
jnc crun9 ; nc = no error
mov ah,prstr ; failure, complain
mov dx,offset erms37
int dos
mov dx,offset cmspbuf ; path\name of command.com
call prtasz ; asciiz
mov kstatus,ksgen ; global status
crun9: mov ah,setdma ; restore dma buffer pointer
mov dx,offset buff
int dos ; restore dma address!!
call cboff ; turn off DOS BREAK check
cmp lclrest,0 ; sys dependent routine to call?
je crun10 ; e = none
mov bx,lclrest ; get routine's address
call bx ; call sys dependent restore routine
crun10: clc
ret
CRUN ENDP
code ends
code1 segment
assume cs:code1
; Write path and filename of current Take file to buffer cmdfile.
; Enter with current working filename offset in register dx.
save_cmdfile proc far
push ax
push dx
push es
mov di,ds
mov es,di
mov di,offset cmdfile ; destination
mov si,dx ; source path
mov ax,[si]
cmp ah,':' ; drive specified?
jne save_c1 ; ne = no
and al,not 20h ; upper case the drive letter
add si,2 ; next byte to read
jmp short save_c2
save_c1:mov al,curdsk ; current drive letter
add al,'A'-1 ; number back to letter
mov ah,':' ; and letter
save_c2:cld
stosw ; write to output
xor al,al
mov [di],al ; terminate
cmp byte ptr [si],'\' ; rooted path?
je save_c3 ; e = yes
mov al,'\' ; get root indicator
stosb ; write to output
mov dl,cmdfile ; get upper case drive letter
sub dl,'A'-1 ; A = 1
push si
mov si,di ; gcd writes to si
mov ah,gcd ; get current directory (path really)
int dos
mov dx,si
call strlen
add di,cx
pop si
jcxz save_c3 ; z = no directory, have \ already
mov al,'\' ; path separator
stosb
mov byte ptr [di],0 ; terminator
save_c3:call strcpy ; copy rest of string
pop es
pop dx
pop ax
ret
save_cmdfile 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 FAR
push es ; save registers
mov al,23H ; desired interrupt vector (^C)
mov ah,getintv ; 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,getintv
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 calls
mov ax,seg intbrk ; 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,setintv ; set interrupt address from ds:dx
int dos
mov dx,offset dosce ; replacement Critical Error handler
mov al,24h ; interrupt 24h
mov ah,setintv ; 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
; 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
; Set DOS' Control-Break Check to off
cboff proc far
push ax
push dx
mov ax,3301h ; set Control-Break Chk state
xor dl,dl ; set state to off
int dos
pop dx
pop ax
ret
cboff endp
; Restore DOS's Control-Break Check to startup value
cbrestore proc far
push ax
push dx
mov ax,3301h ; set Control-Break Chk state
mov dl,orgcbrk ; restore state to startup value
int dos
pop dx
pop ax
ret
cbrestore 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 FAR
mov kstatus,kssuc ; global status
xor cx,cx ; 0 for default drive, if none
cmp byte ptr[si],ch ; any drive/path?
je cdsr4 ; e = no, 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
mov kstatus,ksgen ; global status
ret ; carry is set
cdsr2: push cx ; save drive letter part
mov dx,si
cdsr2a: call strlen
mov bx,cx ; look at last char+1
dec bx ; last byte
cmp cx,1 ; if any bytes
jbe cdsr2b ; be = enough for 1 char
cmp byte ptr [si+bx],'\' ; ends on backslash?
jne cdsr2b ; ne = no
cmp byte ptr [si+bx-1],':' ; ends as ":\"?
je cdsr2b ; e = yes, leave intact for roots
mov byte ptr [si+bx],0 ; trim trailing backslash
jmp short cdsr2a ; keep trimming
cdsr2b: pop cx ; restore drive letter
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
mov dx,si ; where chdir wants it
cmp byte ptr [si+1],':' ; drive specified?
jne cdsr3 ; ne = no, just path
add dx,2 ; skip "drive:"
cmp byte ptr [si+2],0 ; any path?
je cdsr4 ; e = no
cdsr3: mov ah,chdir
int dos
jnc cdsr4 ; nc = success
mov dx,offset ermes4 ; ret carry set, dx --> err msg
ret
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
; 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 FAR
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
; 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
ISFILE PROC FAR
push bx
push ax
mov bx,ax
mov ax,[bx]
or al,al ; is string empty?
jnz isfil5 ; nz = no
pop ax
pop bx
stc ; return failure
ret
isfil5: and ax,not 2020h ; to upper
cmp ax,'UN' ; look for DOS 5 NUL
jne isfil4 ; ne = mismatch
mov ax,[bx+2]
and al,not 20h
cmp ax,'L'+0
jne isfil4 ; ne = mismatch
pop ax
pop bx
clc ; say success
ret
isfil4: pop ax
pop bx
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
; 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
malloc proc far
push bx
push cx
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 mallocx ; c = fatal
cmp cx,bx ; paragraphs wanted vs delivered
jae mallocx ; ae = enough
push es
mov es,ax
mov ah,freemem ; free the memory
int dos
pop es
stc ; fail
mallocx:pop cx
pop bx
ret ; and return segment in ax
malloc endp
code1 ends
code segment
assume cs:code
; initialize memory usage by returning to DOS anything past the end of kermit
memini proc near
push es
mov bx,_TEXT
sub bx,psp
mov totpar,bx ; save for patcher's magic number
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 ah,setblk
int dos
pop es
jnc memin1
mov dx,offset ermes2
mov ah,prstr
int dos ; complain
jmp krmend ; exit Kermit now
memin1: pop dx ; save return address here
mov ax,_STACK ; move SS down to DGROUP, adj SP
sub ax,DGROUP ; paragraphs to move
mov cl,4 ; convert to bytes
shl ax,cl
mov bx,SP ; current SP offset
add bx,ax ; new SP, same memory cell
mov ax,bx
sub ax,400 ; top 400 bytes = Kermit
mov tcptos,ax ; report this as TCP's top of stack
mov ax,DGROUP ; new SS
cli
mov SS,ax
mov SP,bx ; whew!
sti
push dx ; push return address
clc
ret
memini endp
sbrk proc near ; K & R, please forgive us
call malloc ; allocate memory
jc sbrkx ; c = failed
ret ; success
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