Dos Command Strings Strings.asm

 page 66,132
;=======================================================================
; STRINGS.COM - a set of string handling functions for batch files.
;
; Syntax:
; STRINGS [/H][/M][/Pc ][var = ] function arg1 [, arg2] [, arg3]
;
; /? - Print help message
; /M - Use master environment block
; /Q - Suppress output to console
; /Pc - Use c as parse character instead of ','
; /Bxx - Use xx as for base (base 10 default)
; /I - Install as TSR
; /U - Uninstall 
;
; Revision History:
;
; Version 2.0 New version with many more commands.
; Version 2.1 Fixed READ bug
; Fixed Parse bug
; Added check for 386Max mem arenas
; Changed env search default
;
; Version 2.2 Fixed 2FCheck bug
; Fixed ASK bug
; Fixed FILENAME bug
; Fixed Install check
;
; Version 2.3 Added sharing flags to file operations
; Added check for internal cmd hook 
; Fixed read across boundry bug
;
; Version 2.4 Fixed print time at 12 noon bug.
; Added xlate from graphics = to char = on cmdline
;
; Version 2.5 Mod file open code to handle CD-ROMs
;
;=======================================================================
;
;Equates
;
RES_STACK	equ	offset end_of_resident+512
TRANS_STACK	equ	offset end_of_code+512
VAR_SIZE equ 128 ;Max size of variables
DATABUFF_SIZE equ 2048 ;Size of file data buffer
MAX_PARAMS	equ	10			;Max number of parameters
DEF_PARSE_CHAR	equ	","			;Default parse character
;------------------------------------------------------------------------
;Code segment
;------------------------------------------------------------------------
	 code segment
	 assume cs:code
	 org 2ch
local_environment dw ? ;Word containing the seg
	 ; of the prog's env. blk.
	 org 80h
command_tail db ? ;Offset of the cmd tail.
	 org 100h
entry: jmp initialize
program db 13,10,"STRINGS "
version		db	"2.5 "
copyright db "Copyright 1991-1993, 1995 "
		db	"Douglas Boling",10,13
 db "First published in PC Magazine,"
		db	" December 29, 1992",13,10,0,"$",1Ah
		even				;Start data on word bndry
dos_version dw 0
ems_version	dw	-1			
xms_version	dw	-1
databuff_ptr dw 0 ;Ptr to file data buffer
saved_ss	dw	0			;SS:SP saved on during
saved_sp	dw	0			; multiplex execution
saved_ssint	dw	0			;SS:SP saved on during
saved_spint	dw	0			; interrupt function.
int2fh		dd	-1			;Old mux int vector
xms_serv	dd	-1			;Entry pt for ext mem drvr
masterenv_seg 	dw 0 ;Segment of master env
localenv_seg 	dw 0 ;Segment of local env
num_params	dw	0			;Number of parameters passed
dest_var_val dw 0
dest_var_name dw 0 ;Buffer pointers to parsed
cmd_value dw 0 ; command line variables.
var1_value dw 0 ; These pointers must be
var2_value dw 0 ; kept in this order.
var3_value dw 0 
var4_value dw 0
var5_value dw 0 
var6_value dw 0
var7_value dw 0
var8_value dw 0
var9_value dw 0
var10_value dw 0
get_cmd		db	0			;Claim command flag
multiplex_id	db	0dbh			;Mux id 
number_base	dw	10			;Base for num conversion
parse_char db DEF_PARSE_CHAR ;Char used to parse command
quiet_flag	db	0			;Flag to suppress output
console_out db 1 ;Flag for con out.
use_mastenv db 0 ;Flag to use master env
help_flag	db	0			;Forces help message
install_flag	db	0			;Install as TSR
remove_flag	db	0			;Remove
installed	db	0			;Installed as TSR flag
fill_char db " " ;Char used to fill zeros
equalsub_char db 0cdh
strings_namelen	dw	7			;Length of strings name
strings_name	db	"STRINGS",0,0		;Name of program
shell_var db "COMSPEC",0,0		;Extra zero needed, don't del
shell_name db "COMMAND",0,0		;Cmd shell file name
shell_namlen db 7			;Length of shell filename
ems_header	db	'EMMXXXX0'		;Expanded mem drvr header
;
; Table of command line switches and the corrsponding jump table
;
cmd_switches	db	"h?mqpbiu"		;Letters of valid commands.
cmd_switch_end	=	$
cmd_jmp_tbl	dw	offset switch_help 	;This jump table is used to
		dw	offset switch_help 	; call the routines that
		dw	offset switch_master ; process the command line
		dw	offset switch_quiet	; arguments
		dw	offset switch_pchar 
		dw	offset switch_base
		dw	offset switch_install
		dw	offset switch_remove
;
; Table of commands
;
command_table db "LEFT",0 ;String functions
	 db "RIGHT",0
	 db "MID",0
	 db "LENGTH",0
	 db "FIND",0
	 db "FINDC",0
	 db "LOWER",0
	 db "UPPER",0
	 db "CHAR",0
	 db "VAL",0
	 db "FILEDRIVE",0
	 db "FILEDIR",0
	 db "FILENAME",0
	 db "FILEEXT",0
	 db "PARSE",0
	 db "ADDCOMMAS",0
	 db "REPEAT",0
	 db "READ",0 ;File functions
	 db "WRITE",0
	 db "FILESIZE",0
	 db "LINESIZE",0
	 db "TRUENAME",0
		db	"FILEDATE",0		
		db	"FILETIME",0		
	 db "VER",0 ;System functions
	 db "ASK",0
		db	"INWIN",0
		db	"2FCHECK",0
	 db "ENVFREE",0
	 db "ENVSIZE",0
	 db "MASTERVAR",0
	 db "LOCALVAR",0
		db	"TRUEVER",0		
		db	"FILES",0		
		db	"LASTDRIVE",0
		db	"CODEPAGE",0
		db	"COUNTRY",0
		db	"BIOSDATE",0
		db	"GETKEY",0
		db	"LOCALENV",0
		db	"MASTERENV",0
	 db "ADD",0 ;Math functions
	 db "SUB",0
	 db "MUL",0
	 db "DIV",0
	 db "AND",0
	 db "OR",0
	 db "XOR",0
	 db "NOT",0
	 db "CONVERT",0
		db	"PEEK",0		;Progamming functions
		db	"POKE",0
		db	"IN",0
		db	"OUT",0
		db	"INTERRUPT",0
		db	"SCAN",0
		db	"DAY",0			;Time/Date functions
		db	"MONTH",0
		db	"DATE",0
		db	"TIME",0
		db	"MEMTOTAL",0		;Memory status functions
		db	"MEMFREE",0
		db	"XMSTOTAL",0
		db	"XMSFREE",0
		db	"XMSVER",0
		db	"EMSTOTAL",0
		db	"EMSFREE",0
		db	"EMSVER",0
		db	"UMBLARGE",0
		db	"STRINGSVER",0		;Support functions
		db	"INSTALLED",0
		db	"HELP",0
	 db 0 ;End of list flag
;
; Jump table for commands
;
jump_table dw offset left_str
	 dw offset right_str
	 dw offset mid_str
	 dw offset length_str
	 dw offset find_str
	 dw offset findc_str
	 dw offset lower_str
	 dw offset upper_str
	 dw offset char_str
	 dw offset val_str
	 dw offset filedrive_str
	 dw offset filedir_str
	 dw offset filename_str
	 dw offset fileext_str
	 dw offset parse_str	;New
	 dw offset commas_str	;New
	 dw offset repeat_str	;New
	 dw offset readrec_file
	 dw offset writerec_file
	 dw offset filesize_file
	 dw offset numrec_file
	 dw offset truename_file
	 dw offset filedate_file	;New
	 dw offset filetime_file	;New
	
	 dw offset ver_sys
	 dw offset ask2_sys
		dw	offset inwin_sys
		dw	offset int2fcheck_sys
	 dw offset envfree_sys
	 dw offset envsize_sys
	 dw offset mastervar_sys
	 dw offset localvar_sys	 
	 dw offset truever_sys	;New
	 dw offset files_sys	;New
	 dw offset lastdrive_sys	;New
	 dw offset codepage_sys	;New
	 dw offset country_sys	;New
 	dw offset biosdate_sys	;New
 	dw offset getkey_sys	;New
 	dw offset localenv_sys	;New
 	dw offset masterenv_sys	;New
	 dw offset add_num
	 dw offset sub_num
	 dw offset mul_num
	 dw offset div_num
	 dw offset and_num		;New
	 dw offset or_num		;New
	 dw offset xor_num		;New
	 dw offset not_num		;New
	 dw offset convert_num	;New
	 dw offset peek_prog	;New
	 dw offset poke_prog	;New
	 dw offset in_prog		;New
	 dw offset out_prog		;New
	 dw offset interrupt_prog	;New
	 dw offset scan_prog	;New
	 dw offset day_time		;New
	 dw offset month_time	;New
	 dw offset date_time	;New
	 dw offset time_time	;New
	 dw offset totalmem_mem	;New
	 dw offset freemem_mem	;New
	 dw offset totalext_mem	;New
	 dw offset freeext_mem	;New
	 dw offset extver_mem	;New
	 dw offset totalems_mem	;New
	 dw offset freeems_mem	;New
	 dw offset emsver_mem	;New
	 dw offset freeumb_mem	;New
	 dw offset ver_strings	;New
	 dw offset inst_strings	;New
	 dw offset help_strings	;New
jump_table_end = $
int2fname_tbl 	db "PRINT",0 ;Alias table for multiplex
	 db "ASSIGN",0		; interrupt install check
		db	"DRIVER",0
	 db "SHARE",0
	 db "NET",0
	 db "NLS",0
	 db "ANSI",0
		db	"DOSBOX",0
	 db "HIMEM",0
	 db "DOSKEY",0
	 db "DISPLAY",0
	 db "GRAPHTBL",0
	 db "APPEND",0
	 db 0 ;End of list flag
int2falias_tbl	db	1			;ID numbers of DOS programs
		db	6			; that use the multiplex
		db	8			; interrupt.
		db	10h
		db	11h
		db	14h
		db	1Ah
		db	40h
		db	43h
		db	48h
		db	0ADh
		db	0B0h
		db	0B7h
day_list	db	"Sunday",0
		db	"Monday",0
		db	"Tuesday",0
		db	"Wednesday",0
		db	"Thursday",0
		db	"Friday",0
		db	"Saturday",0,0
month_list	db	"January",0
		db	"February",0
		db	"March",0
		db	"April",0
		db	"May",0
		db	"June",0
		db	"July",0
		db	"August",0
		db	"September",0
		db	"October",0
		db	"November",0
		db	"December",0,0
infomsg2	db	"Removed",0		
errmsg0 db "Need DOS " 
errmsg0ver	db	"2.0"
		db	" or greater",0
errmsg1 db "Usage: STRINGS [/?][/M][/Q][/Pc][/Bn][/I][/U]"
		db	" [env var =]"
	 db " FUNCTION [Params]",13,10,10
	 db " /M - Use master environment",13,10
	 db " /Q - Suppress output to screen",13,10
	 db " /Pc - Use char c instead of ',' as the"
		db	" parse character",13,10
	 db " /Bn - Use n as the number base",13,10
	 db " /I - Installs as resident code."
	 db " DOS 3.3 or later required",13,10
	 db " /U - Uninstalls if resident",13,10
		db 10,"For a list of commands type STRINGS /?",0
errmsg2 db "Not enought memory",0
errmsg3 db "Unknown command line switch",0
errmsg6 db "Illegal filename",0
errmsg7 db "Line numbers must be greater than 0",0
errmsg8 db "Line not found",0
errmsg10 db "Multiply overflow error",0
errmsg11 db "Divide by zero error",0
errmsg12 db "Addition overflow",0
errmsg13 db "Subtraction underflow",0
errmsg14 db "Number too large",0
errmsg15 db "Out of environment space",0
errmsg16 db "Can't find environment",0
errmsg17	db	"No Expanded memory",0
errmsg18	db	"Required parameter missing",0
errmsg19	db	"No Extended memory",0
errmsg20	db	"String not found",0
errmsg21	db	"Not Installed",0
errmsg22	db	"Can",39,"t remove",0
errmsg23	db	"Already installed",0
errmsg24	db	"Base must be within 2-16",0
endmsg db 13,10,0
doserr_tbl	dw	offset	doserr_00
	 	dw	offset	doserr_00
		dw	offset	doserr_02
		dw	offset	doserr_03
		dw	offset	doserr_04
		dw	offset	doserr_05
		dw	offset	doserr_00
		dw	offset	doserr_07
		dw	offset	doserr_00
		dw	offset	doserr_00
		dw	offset	doserr_10
		dw	offset	doserr_00
		dw	offset	doserr_12
		dw	offset	doserr_13
		dw	offset	doserr_00
		dw	offset	doserr_15
		dw	offset	doserr_00
		dw	offset	doserr_17
		dw	offset	doserr_18
		dw	offset	doserr_19
		dw	offset	doserr_20
		dw	offset	doserr_21
		dw	offset	doserr_22
		dw	offset	doserr_23
		dw	offset	doserr_00
		dw	offset	doserr_25
		dw	offset	doserr_26
		dw	offset	doserr_27
		dw	offset	doserr_00
		dw	offset	doserr_29
		dw	offset	doserr_30
		dw	offset	doserr_31
		dw	offset	doserr_32
		dw	offset	doserr_33
		dw	offset	doserr_34
doserr_tblend	=	$
doserr_00	db	"DOS Error",0
doserr_02	db	"File not found",0
doserr_03	db	"Path not found",0
doserr_04	db	"Too many open files",0
doserr_05	db	"Access denied",0
doserr_07	db	"Memory Corrupted",0
doserr_10	db	"Bad Environment block",0
doserr_12	db	"File Access Invalid",0
doserr_13	db	"Data Invalid",0
doserr_15	db	"Not a proper Disk",0
doserr_17	db	"Not same device",0
doserr_18	db	"No more files",0
doserr_19	db	"Disk Write Protected",0
doserr_20	db	"Unknown unit",0
doserr_21	db	"Drive not ready",0
doserr_22	db	"Unknown command",0
doserr_23	db	"CRC Data Error",0
doserr_25	db	"Disk Seek error",0
doserr_26	db	"Not a DOS disk",0
doserr_27	db	"Sector not found",0
doserr_29	db	"File Write fault",0
doserr_30	db	"File Read fault",0
doserr_31	db	"General failure",0
doserr_32	db	"File sharing violation",0
doserr_33	db	"File lock violation",0
doserr_34	db	"Illegal Disk change",0
;============================================================================
; Multiplex Interrupt handler
;============================================================================
muxint		proc	far
		assume	cs:code,ds:nothing,es:nothing
		cmp 	ax,0ae00h		;Chk for installed cmd
		je	mux_chkcmd
		cmp 	ax,0ae01h		;Chk for installed cmd
		je	mux_execute
		cmp	ah,cs:multiplex_id
		je	mux_instchk
mux_jmp:
 		jmp	cs:[int2fh]		;Jump to old interrupt
mux_instchk:
		mov	ax,-1
		push	cs
		pop	es
		iret
;
; This routine checks the cmd line for a valid Strings command.
;
mux_chkcmd:
		cld
		push	cx
		push	di
		push	si
		push	es
		
		lodsb				;Load cmd length 
		mov	cl,al
		xor	ch,ch
		mov	di,cs			;ES:DI points to my cmd
		mov	es,di			; line buff
		mov	di,offset strings_name
		repe	cmpsb
		jne	mux_chk1
		cmp	byte ptr es:[di],0
		jne	mux_chk1
		mov	al,-1			;Claim command
		mov	di,81h			;Copy to my cmd buff
		mov	si,bx			;Point to cmd line
		inc	si
		mov	cl,ds:[si]		;Get length of cmd line
		inc	si			;Load cmd line length
		inc	cx
		cmp	cl,7fh			;Only allow 127 chars
		ja	mux_chk1
		rep	movsb
		jmp	short mux_chkexit
mux_chk1:
		xor	al,al			;Don't want command
mux_chkexit:
		pop	es
		pop	si
		pop	di
		pop	cx
		or	al,al			;See if we like cmd
		je	mux_jmp			;No, pass on.
		mov	cs:get_cmd,1		;Set accept cmd flag
		jmp	short mux_iret		;Yes, return.
;
; This routine executes the command line.
;
mux_execute:
		dec	cs:get_cmd		;See if Strings claimed
		jnz	mux_jmp			; the command.
		push	ax
		mov	cs:saved_ss,ss
		mov	cs:saved_sp,sp
		mov	ax,cs
		cli
		mov	ss,ax
		mov	sp,RES_STACK
		sti
		push	bx
		push	cx
		push	dx
		push	di
		push	si
		push	bp
		push	ds
		push	es
		mov	ax,cs
		mov	ds,ax
		mov	es,ax
		assume	ds:code,es:code
		mov	si,offset 81h		;Remove name of program
		mov	di,si			; from the command 
		xor	bl,bl			; line. 1st, find the
		call	scan4char		; name, the move the
		add	si,strings_namelen	; ptr past it.
		xor	cx,cx
mux_x1:
		lodsb
		stosb
		inc	cx
		cmp	al,13
		jne	mux_x1
		mov	ds:[80h],cl
		call	main			;Showtime!
		cmp	remove_flag,0		;See if we should 
		je	mux_xexit		; remove the program
		call	remove
		jnc	mux_xexit
		call	print_strcr		;Print error message
mux_xexit:
		pop	es
		pop	ds
		pop	bp
		pop	si
		pop	di
		pop	dx
		pop	cx
		pop	bx
		cli
		mov	ss,cs:saved_ss
		mov	sp,cs:saved_sp
		cmp	cs:remove_flag,0	;If remove, discard
		je	mux_xexit1		; mem segment at the
		push	es			; last moment.
		push	cs
		pop	es			;Get code segment
		mov	ah,49h			;Free mem
		int	21h
		pop	es
mux_xexit1:
		pop	ax
		mov	byte ptr ds:[si],0	;Tell cmd.com not to ex
mux_iret:
		iret
muxint		endp
;----------------------------------------------------------------------------
; Start of code.						 	
;----------------------------------------------------------------------------
main		proc near
	 assume cs:code,ds:code,es:code
	 cld ;Set string operations 'up.'
	 call parse_cmdline ;Parse command line
	 jc main_error
	 mov si,dest_var_name ;Point to dest env var name
	 call caps_string
	 mov si,cmd_value ;Point to command buffer
	 call caps_string
		cmp	help_flag,0		;If help flag set, call
		je	main_1			; help function to 
		call	help_strings		; interpet command.
		jmp	short main_6
main_1:
		inc	cx
	 mov di,offset command_table	;Search cmd table 
	 call findstr
	 mov si,offset errmsg1
	 jc main_error
	 shl bx,1 ;Compute offset of routine to
	 call [bx+offset jump_table]	; call, then call routine
	 jc main_error
	 mov si,dest_var_val
	 cmp console_out,0 ;See how to return result
	 je main_3
	 cmp	byte ptr [si],0		;If no result, exit
	 je	main_6
 	call print_strcr ;Print result to screen
	 jmp short main_6
main_3:
	 mov di,dest_var_name
	 xchg di,si
	 call setenv ;Set environemnt variable.
	 jc main_7
main_6:
	 xor al,al ;Return code = 0
main_exit:
		ret				;Return
;
;Display error message.
;
main_7:
	 mov si,offset errmsg15 ;Out of environment space
main_error:
	 push cs
	 pop ds
	 assume ds:code
		push	si
		mov	si,offset program	;Print copyright msg
		call	print_strcr
		pop	si
	 call print_strcr ;print string
main_9:	
	 mov al,01 ;Terminate with RC = 1
	 jmp short main_exit
main		endp
;=============================================================================
; String Functions
;=============================================================================
;-----------------------------------------------------------------------------
; RIGHT STR returns the right n characters of the source string
;-----------------------------------------------------------------------------
right_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var2_value ;Convert 2nd parameter to hex
	 call asc2hex
	 call truncnum ;Truncate number to string len
	 push ax
	 mov di,var1_value ;Scan to end of string
	 call find_end
	 pop ax
right_str_2:
	 sub di,ax
	 dec di
	 cmp di,var1_value
	 ja right_str_3
	 mov di,var1_value
right_str_3:
	 mov si,dest_var_val
	 xchg di,si
	 call copy_string ;Copy string to dest buffer
	 clc
	 ret
right_str endp
;-----------------------------------------------------------------------------
; LEFT STR Returns the left n characters of the source string
;-----------------------------------------------------------------------------
left_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var2_value ;Convert 2nd parameter to hex
	 call asc2hex
	 call truncnum ;Truncate number to string len
	 mov si,var1_value
	 mov bx,ax
	 mov byte ptr [si+bx],0
	 mov di,dest_var_val
	 call copy_string ;Copy string to dest buffer
	 clc
	 ret
left_str endp
;-----------------------------------------------------------------------------
; MID STR Returns a string of n characters starting m characters from the
; left of the source string
;-----------------------------------------------------------------------------
mid_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var2_value ;Convert 2nd parameter to hex
	 call asc2hex
	 dec ax
	 call truncnum ;Truncate num
	 mov cx,ax ;Copy second parameter
	 mov si,var3_value ;Convert 3rd param to hex
	 cmp byte ptr [si],0 ;See if no parameter
	 je mid_str_0
	 call asc2hex ;If no number, return max
	 jnc mid_str_1 ; value to insure remainder
mid_str_0:
	 mov ax,VAR_SIZE ; of string returned.
mid_str_1:
	 call truncnum ;Truncate num
	 push ax ;Save length of substring
	 xor ax,ax
	 cmp al,1 ;Clear zero flag
	 mov di,var1_value ;Scan to new start of string
	 jcxz mid_str_11
	 repne scasb
mid_str_11:
	 pop cx ;Pop length of substring
	 mov si,di ;Copy ptr to start of substr
	 je mid_str_2 ;If end of str found, end
	 repne scasb ;Scan until end of substring
	 mov byte ptr [di],0
mid_str_2:
	 mov di,dest_var_val
	 call copy_string ;Copy string to dest buffer
	 clc
	 ret
mid_str endp
;-----------------------------------------------------------------------------
; LENGTH STR Computes the length of the source string
;-----------------------------------------------------------------------------
length_str proc near
	 assume cs:code,ds:code,es:code
	 mov di,var1_value ;Find_end also returns the
	 call find_end ; length of the string in
	 mov ax,cx ; CX.
	 xor dx,dx
	 mov di,dest_var_val ;Convert value to ASCII
	 call hex2asc
	 clc
	 ret
length_str endp
;-----------------------------------------------------------------------------
; UPPER STR Convert the source string to upper case
;-----------------------------------------------------------------------------
upper_str proc near
	 assume cs:code,ds:code,es:code
	 mov di,dest_var_val
	 mov si,var1_value
	 push di
	 call copy_string ;Copy string to dest buffer
	 pop si
	 call caps_string ;Convert to upper case.
	 clc
	 ret
upper_str endp
;-----------------------------------------------------------------------------
; LOWER STR Convert the source string to lower case
;-----------------------------------------------------------------------------
lower_str proc near
	 assume cs:code,ds:code,es:code
	 mov di,dest_var_val
	 mov si,var1_value
	 push di
	 call copy_string ;Copy string to dest buffer
	 pop si
	 call lc_string ;Convert to lower case.
	 clc
	 ret
lower_str endp
;-----------------------------------------------------------------------------
; CHAR STR Convert the source number to a ASCII character
; Revised in ver 2.0 to handle up to 10 numbers
;-----------------------------------------------------------------------------
char_str proc near
	 assume cs:code,ds:code,es:code
		push	bp
	 mov di,dest_var_val		;Get ptr to output buff
		mov	bp,offset var1_value	;Get ptr to var array
		mov	cx,num_params		;Get number of parameters
		or	cx,cx
		jne	charstr_1		
		mov	cx,1
charstr_1:
	 mov si,[bp]			;Get ptr to variable
		inc	bp			;Point BP to next var
		inc	bp
	 call asc2hex			;Convert ASCII num to
		stosb				; hex num and store.
		loop	charstr_1
		xor al,al ;Write number directly to
	 stosb ; dest string. Include
	 clc ; zero for termination.
		pop	bp
	 ret
char_str endp
;-----------------------------------------------------------------------------
; VAL STR Convert the source character to its HEX equivalent
; Revised in ver 2.0 to handle more than one character
;-----------------------------------------------------------------------------
val_str proc near
	 assume cs:code,ds:code,es:code
	 mov di,dest_var_val		;Get ptr to output buff
		mov	si,var1_value		;Get ptr to char string
valstr_1:
		lodsb				;Get character
		or	al,al
		je	valstr_2
		xor	ah,ah
		xor	dx,dx
	 call hex2asc			;Convert character to
		mov	byte ptr [di-1],' '	; ascii num and store.
		jmp	short valstr_1
		dec	di
valstr_2:
		xor al,al ;Write number directly to
	 stosb ; dest string. Include
	 clc ; zero for termination.
		ret
val_str endp
;-----------------------------------------------------------------------------
; FILEDRIVE STR Return only the directory from a filename string
;-----------------------------------------------------------------------------
filedrive_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var1_value ;Fully qualify filename
	 mov di,dest_var_val ;Point string to dest buff
	 call parse_filename
		mov	byte ptr [di+2],0	;Terminate after drive spec
		clc
	 ret
filedrive_str endp
;-----------------------------------------------------------------------------
; FILEDIR STR Return only the directory from a filename string
;-----------------------------------------------------------------------------
filedir_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var1_value ;Fully qualify filename
	 mov di,databuff_ptr ;Use data buff as temp buff
	 call parse_filename
		mov	si,dest_var_val
		xchg	si,di
		add	si,2			;Skip past drive stuff
	 mov bx,di
		mov	dx,bx
		inc	dx
filedir_1:
	 lodsb
		stosb
	 cmp al,'\' ;Mark start of filename or
	 jne filedir_2 ; directory.
	 mov bx,di
filedir_2:
	 or al,al			;See if at end of string
	 jne filedir_1
filedir_3:
		cmp	bx,dx			;If root dir, don't delete
		je	filedir_4		; lone \.
		dec	bx
filedir_4:
		mov	byte ptr [bx],0		;Terminate string at end of
		clc				; directory
	 ret
filedir_str endp
;-----------------------------------------------------------------------------
; FILENAME STR Return only the filename from a filename string
;-----------------------------------------------------------------------------
filename_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var1_value ;Fully qualify filename
	 mov di,databuff_ptr ;Use data buff as temp buff
		call	get_filename
	 mov di,dest_var_val		;Pt to dest buff
	 rep movsb
		xor	al,al			;Terminate filename and
		stosb				; clear error flag
	 ret
filename_str endp
;-----------------------------------------------------------------------------
; FILEEXT STR Return only the filename extension from a filename string
;-----------------------------------------------------------------------------
fileext_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var1_value ;Fully qualify filename
	 mov di,databuff_ptr ;Use 2nd buff as temp buff
	 call parse_filename
	 mov si,di
	 xor bx,bx
fileext_1:
	 lodsb
	 cmp al,'.' ;Mark start of filename or
	 jne fileext_2 ; directory.
	 mov bx,si
fileext_2:
	 or al,al
	 jne fileext_1
	 or bx,bx
	 je fileext_3
	 mov cx,si
	 sub cx,bx ;Compute length
	 mov di,dest_var_val
	 mov si,bx
	 rep movsb
		xor	al,al			;Terminate string
		stosb
fileext_3:
	 clc
	 ret
fileext_str endp
;-----------------------------------------------------------------------------
; FIND STR finds a string within another string.
; Exit: AL - Return code if string not found
; CF - Set if string not found
;-----------------------------------------------------------------------------
find_str proc near
	 mov si,var1_value ;To ignore case, capitalize
	 call caps_string ; both strings, then call
	 mov si,var2_value ; the findc function.
	 call caps_string
	 call findc_str
	 ret
find_str endp
;-----------------------------------------------------------------------------
; FINDC STR finds a string within another string, respects case.
; Exit: AL - Return code if string not found
; CF - Set if string not found
;-----------------------------------------------------------------------------
findc_str proc near
	 mov di,var1_value ;Get ptr to 1st string
	 push di
	 call find_end ;Compute length
	 pop si
	 push cx ;Save length
	 mov di,var2_value
	 mov dx,di
	 call find_end
	 mov bx,cx ;Save length of search string
	 pop cx ;Restore length of trg string
	 sub cx,bx ;Sub length of search string.
	 jb find_str_not_found
		inc	cx
find_str_1:
	 push cx
	 mov cx,bx ;Restore search str length
	 mov di,dx ;Restore ptr to search str
	 push si
	 repe cmpsb ;Compare command
	 pop si
	 pop cx
	 je find_str_found
	 inc si ;Inc target string ptr
	 loop find_str_1
find_str_not_found:
	 xor ax,ax ;Set bad return code
	 jmp short find_str_2
find_str_found:
	 mov ax,si ;Copy offset
	 sub ax,var1_value ;Sub starting offset
	 inc ax
find_str_2:
	 xor dx,dx
	 mov di,dest_var_val ;Convert value to ASCII
	 call hex2asc
	 clc
find_str_exit:
	 ret
findc_str endp
;-----------------------------------------------------------------------------
; PARSE STR Returns the nth token in a string
;-----------------------------------------------------------------------------
parse_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var2_value ;Convert 2nd param to hex
	 call asc2hex
		mov	di,100h
		mov	cx,ax			;Save count
		sub	cx,1
		jb	parse_str_3
		mov	bx,var3_value
		mov	dl,[bx]			;Get parse char
		or	dl,dl
		jne	parse_str_0
		mov	dl,' '
parse_str_0:
		mov	si,var1_value		;Get ptr to string
		or	cx,cx			;Check count for 0
		je	parse_str_2
parse_str_1:
 		mov	bl,4			;Scan for char in DL
		call	scan4char
		jc	parse_str_exit
		inc	si
		loop	parse_str_1
parse_str_2:
		mov	di,si
 		mov	bl,4			;Scan for char in DL
		call	scan4char
		mov	byte ptr [si],0		;Term string
parse_str_3:
	 mov si,dest_var_val ;Get ptr to output buff
		xchg	si,di
		call	copy_string
parse_str_exit:
		clc
		ret
parse_str endp
;-----------------------------------------------------------------------------
; COMMAS STR Returns the nth token in a string
;-----------------------------------------------------------------------------
commas_str proc near
	 assume cs:code,ds:code,es:code
		mov	di,var1_value
		call	find_end
		mov	ax,cx
		mov	cl,3
		div	cl
		mov	cl,ah			;Copy remainder
		xor	ch,ch
		mov	ah,al			;Save quotient
		mov	al,','
		mov	si,var1_value
	 mov di,dest_var_val ;Get ptr to output buff
		jcxz	commas_1
		rep	movsb
		jmp	commas_2
commas_1:
		mov	cl,ah
		jcxz	commas_str_exit
		jmp	short commas_4
		
commas_2:
		mov	cl,ah			;Get number of commas
		jcxz	commas_str_exit
commas_3:
		stosb				;Insert comma
commas_4:
		movsw				;Copy 3 digits
		movsb
		loop	commas_3
commas_str_exit:
		xor	al,al
		stosb
		clc
		ret
commas_str endp
;-----------------------------------------------------------------------------
; REPEAT STR Returns a string with n number of a character
;-----------------------------------------------------------------------------
repeat_str proc near
	 assume cs:code,ds:code,es:code
	 mov si,var2_value ;Get character to repeat
		mov	al,[si]
		push	ax
	 mov si,var1_value ;Convert 2nd param to hex
	 call asc2hex
		mov	cx,ax
		pop	ax			;Get back repeat char
		or	dx,dx
		jne	repeat_error		;See if repeat number too
		cmp	cx,VAR_SIZE		; big.
		jae	repeat_error
		mov	di,dest_var_val
		rep	stosb
		xor	al,al
		stosb
		clc
repeat_exit:
		ret
repeat_error:
		mov si,offset errmsg14		;Number too large
		stc
		jmp	short repeat_exit
repeat_str endp
;=============================================================================
; File Functions
;=============================================================================
;-----------------------------------------------------------------------------
; READ REC returns record n from a file.
;-----------------------------------------------------------------------------
readrec_file proc near
	 assume cs:code,ds:code,es:code
		mov	al,40h			;Read only, deny none
	 call filename_open
	 jc readrec_error
	 mov si,var2_value ;Convert 2nd param to record
	 call asc2hex ; number.
	 mov si,offset errmsg8 ;Record not found.
		sub	ax,1
		sbb	dx,0
		jc	readrec_error1
	 call findrec_file ;Find record.
	 mov ax,si ;Copy end of file flag.
	 mov si,offset errmsg8 ;Record not found.
	 jc readrec_error1 ;Error if record not found.
	 mov si,dest_var_val
	 xchg di,si
	 jcxz readrec_2
readrec_1:
	 lodsb ;Copy record to destination
	 cmp al,13 ; buffer.
	 je readrec_3
	 stosb
	 loop readrec_1
readrec_2:
	 or ah,ah ;Check end of file flag
	 jne readrec_3
	 mov dx,databuff_ptr ;If at end of data buffer.
	 mov cx,VAR_SIZE ; read enough to complete
	 call read_file ; record.
	 jc readrec_error
	 mov cx,ax ;Copy number of bytes read.
	 mov si,dx			;Point to start of buffer
	 jmp short readrec_1
readrec_3:
	 xor al,al ;Append terminating zero
	 stosb
	 call close_file ;Close file
	 jc readrec_error
readrec_exit:
	 ret
readrec_error:
		call	parse_doserr
	 jmp	short readrec_exit
readrec_error1:
	 call close_file
	 stc
	 jmp short readrec_exit
readrec_file endp
;-----------------------------------------------------------------------------
; WRITE REC appends a string to the end of a file.
;-----------------------------------------------------------------------------
writerec_file proc near
	 assume cs:code,ds:code,es:code
		mov	al,22h			;Read/Write, deny write 
	 call filename_open		;Parse name. open file 
	 jnc writerec_0 ; no exist, create file.
	 call create_file
	 jc writerec_error
	 mov si,1
	 jmp short writerec_1
writerec_0:
	 xor ax,ax ;Move file ptr to end of file
	 cwd
	 mov cx,2 ;Move pointer from end.
	 call move_fileptr ;Move file pointer
	 jc writerec_error
	 mov si,1
	 or dx,dx
	 jne writerec_01
	 or ax,ax
	 je writerec_1
writerec_01:
	 mov ax,-1 ;Move file ptr to last byte
	 cwd
	 mov cx,2 ;Move pointer from end.
	 call move_fileptr ;Move file pointer
	 jc writerec_error
	 dec si ;Clear EOF marker flag
	
	 mov dx,dest_var_val ;Read last char to check for
	 mov cx,1 ; EOF marker
	 call read_file
	 jc writerec_error
	 mov di,dx
	 cmp byte ptr [di],1Ah
	 jne writerec_1
	 mov ax,-1 ;See if last byte 1A EOF mark
	 cwd
	 mov cx,2 ;Move pointer from end.
	 call move_fileptr ;Move file pointer
	 jc writerec_error
	 inc si ;Set EOF marker flag
writerec_1:
	 mov di,var2_value ;Get length of string
	 mov dx,di
	 call find_end
	 dec di ;Backup before zero
	 mov ax,0a0dh
	 stosw
	 inc cx
	 inc cx
	 or si,si
	 je writerec_2
	 mov al,1ah ;Append EOF marker
	 stosb
	 inc cx
writerec_2:
	 call write_file
	 jc writerec_error
	 call close_file ;Close file
	 jc writerec_error
writerec_exit:
	 mov di,dest_var_val ;Clear dest value.
	 mov byte ptr [di],0
	 ret
writerec_error:
		call	parse_doserr		;Get msg for DOS error
	 jmp short writerec_exit
writerec_error1:
	 call close_file
	 stc
	 jmp short writerec_exit
writerec_file endp
;-----------------------------------------------------------------------------
; NUMREC FILE returns the number of records in a file.
;-----------------------------------------------------------------------------
numrec_file proc near
	 assume cs:code,ds:code,es:code
	 call filename_open		;Parse name. open file 
	 jc numrec_error
	 mov ax,-1 ;Try to find large rec num
	 mov dx,ax
	 call findrec_file ;Find record.
	 jnc numrec_error1 ;Error if record found.
	 not ax ;Compliment line count. 
	 not dx
		sub	ax,1
		sbb	dx,0
		
	 mov di,dest_var_val ;Convert rec number to ASCII
	 call hex2asc
	 call close_file ;Close file
	 jc numrec_error
numrec_exit:
	 ret
numrec_error:
		call	parse_doserr		;Get msg for DOS error
	 jmp short numrec_exit
numrec_error1:
	 call close_file
	 jc numrec_error
	 stc
	 jmp short numrec_exit
numrec_file endp
;-----------------------------------------------------------------------------
; FIND REC returns an offset to the Nth record of a file.
; Entry: DX,AX - Record to find
; BX - Source File handle
; Exit: DX,AX - Records remaing if end of file
; CF - Set if record not found.
; DI - Points to record.
; CX - Number of bytes to end of data buffer
; SI - Points to error message if CF set.
;-----------------------------------------------------------------------------
findrec_numl equ word ptr [bp-2]
findrec_numh equ word ptr [bp-4]
findrec_eof equ byte ptr [bp-5]
findrec_file proc near
	 assume cs:code,ds:code,es:code
	 push bp
	 mov bp,sp
	 sub sp,6
	 mov si,offset errmsg8 ;Record not found
	 mov findrec_eof,0 ;Clear end of file flag.
	 mov findrec_numl,ax ;Save record number.
	 mov findrec_numh,dx
findrec_1:
	 mov cx,databuff_ptr
	 xchg cx,dx
	 mov cx,DATABUFF_SIZE ;Get size of data buffer
	 call read_file ;Read data from file.
	 jc findrec_error
	 cmp ax,cx ;See if buffer filled. If
	 je findrec_2 ; not, end of file.
	 mov findrec_eof,1 ;Set end of file flag.
findrec_2:
	 mov cx,ax ;Copy num bytes read.
	 mov di,dx ;Copy buffer ptr
	 mov al,13 ;Scan for CR
		mov	dx,findrec_numl		;Check for end
		or 	dx,findrec_numh
		je	findrec_5
		jmp	short findrec_4
findrec_3:
	 mov dx,cx ;Save position in buffer
	 sub findrec_numl,1 ;Decriment record count
	 sbb findrec_numh,0
	 jne findrec_4 ;See if record count = 0
	 cmp findrec_numl,0
	 je findrec_5
findrec_4:
	 repne scasb
	 je findrec_3
	 cmp findrec_eof,1 ;If end of buffer, see if
	 jne findrec_1 ; end of file. Yes = exit
	 stc
	 jmp short findrec_exit
findrec_5:
	 cmp byte ptr [di],0ah ;discard Line feed
	 jne findrec_6
	 inc di
	 dec cx
findrec_6:
	 clc
findrec_exit:
	 mov ah,findrec_eof
	 mov al,0
	 mov si,ax ;Save end of file flag
	 mov ax,findrec_numl ;Get record number.
	 mov dx,findrec_numh
	 mov sp,bp
	 pop bp
	 ret
findrec_error:
		call	parse_doserr		;Get msg for DOS error
findrec_error1:
	 stc
	 jmp short findrec_exit
findrec_file endp
;-----------------------------------------------------------------------------
; FILE SIZE returns the size of a file
;-----------------------------------------------------------------------------
filesize_file proc near
	 assume cs:code,ds:code,es:code
		mov	al,40h			;Read only, deny none.
	 call filename_open		;Parse name. open file 
	 mov di,dest_var_val ;Point DI to result buffer.
	 jc filesize_error
	 xor ax,ax ;Zero offset.
	 xor dx,dx
	 mov cl,2 ;Move pointer from end.
	 call move_fileptr ;Move file pointer
	 jc filesize_error
	 call hex2asc ;Convert size to ASCII
	 call close_file ;Close file
	 jc filesize_error
filesize_exit:
	 ret
filesize_error:
		call	parse_doserr		;Get msg for DOS error
		jmp	short filesize_exit		
filesize_file endp
;-----------------------------------------------------------------------------
; TRUENAME FILE returns the fully qualified name of a file.
;-----------------------------------------------------------------------------
truename_file proc near
	 assume cs:code,ds:code,es:code
	 mov si,var1_value ;Fully qualify filename
	 mov di,dest_var_val ;Use dest buff as temp buff
	 call parse_filename
		jnc	truename_1
		mov	si,offset errmsg6	;Illegal filename msg
truename_1:
	 ret
truename_file endp
;-----------------------------------------------------------------------------
; FILE DATE returns the date of a file
;-----------------------------------------------------------------------------
filedate_file proc near
	 assume cs:code,ds:code,es:code
		mov	al,40h			;Read only, deny none.
	 call filename_open
	 jc filedate_error
		mov	ax,5700h		;Get file date/time
		int	21h
		jc	filedate_error
		push	bx			;Save file handle
		mov	ax,dx			;DX: yyyy yyym mmmd dddd
		shl	dx,1
		shl	dx,1
		shl	dx,1
		mov	dl,al			;DH:Months, DL:days 
		and	dx,0f1fh		;Clear off unneeded bits
		shr	ah,1			;Align years
		mov	cx,1980			;Set starting year
		add	cl,ah			;This fails in year 2048
	 mov di,dest_var_val
		call 	print_date		;Write date
		pop	bx			;Restore file handle
		call close_file ;Close file
	 jc filedate_error
filedate_exit:
	 ret
filedate_error:
		call	parse_doserr		;Get msg for DOS error
		jmp	short filedate_exit		
filedate_file endp
;-----------------------------------------------------------------------------
; FILE TIME returns the time of a file
;-----------------------------------------------------------------------------
filetime_file proc near
	 assume cs:code,ds:code,es:code
		mov	al,40h			;Read only, deny none.
	 call filename_open		;Parse name. open file 
	 jc filetime_error
		mov	ax,5700h		;Get file date/time
		int	21h
		jc	filetime_error
		push	bx			;Save file handle
		mov	dh,cl			;CX: hhhh hmmm mmms ssss
		shl	dh,1			;Mul seconds by 2
		and	dh,3fh			;Mask off other bits
		rol	cx,1			;Roll hours to low byte
		rol	cx,1			; but stop at 3 to
		rol	cx,1			; copy now aligned 
		mov	al,ch			; minute bits.
		rol	cx,1
		rol	cx,1
		mov	ch,al			;Get back minutes
		xchg	ch,cl
		and	cx,1f3fh		;Mask off sec bits
	 mov di,dest_var_val
		call	print_time
		pop	bx			;Restore file handle
		call close_file ;Close file
	 jc filetime_error
filetime_exit:
	 ret
filetime_error:
		call	parse_doserr		;Get msg for DOS error
		jmp	short filetime_exit		
filetime_file endp
;=============================================================================
; System Functions
;=============================================================================
;-----------------------------------------------------------------------------
; VER SYS returns the DOS version number
;-----------------------------------------------------------------------------
ver_sys proc near
	 assume cs:code,ds:code,es:code
	 mov ax,dos_version		;Get DOS verison
	 mov di,dest_var_val
	 call printver		;Print version
	 clc
	 ret
ver_sys endp
;-----------------------------------------------------------------------------
; ASK2 SYS prints a string then returns the user response.
;-----------------------------------------------------------------------------
ask2_sys proc near
	 assume cs:code,ds:code,es:code
		mov	al,quiet_flag		;Save state of quiet flag
		push	ax
		mov	quiet_flag,0		;Clear quiet flag 
		xor	bx,bx			;Clear password flag
		mov	cx,VAR_SIZE - 1
		cmp	num_params,1
		jb ask2_3
		je ask2_2
	 mov si,var3_value		;Chk for password flag
		call	asc2hex
		cmp	ax,2
		ja	ask2_1
		mov	bx,ax			;Copy password flag 
ask2_1:
		mov	si,var2_value		;Get max num of chars
		call	asc2hex
		mov	cx,ax
		cmp	cx,VAR_SIZE - 1
		mov	si,offset errmsg14	;Number too large
		ja	ask2_error
ask2_2:
	 mov si,var1_value
	 call print_str ;Print prompt string
ask2_3:
		mov	di,dest_var_val
		call	read_console
	 mov si,offset endmsg
	 call print_str ;Insert a CR-LF
	 clc
ask2_exit:
		pop	ax
		mov	quiet_flag,al
	 ret
ask2_error:
		stc
		jmp	short ask2_exit
ask2_sys endp
;-----------------------------------------------------------------------------
; ENVFREE SYS returns the number of bytes free in the environment.
;-----------------------------------------------------------------------------
envfree_sys proc near
	 assume cs:code,ds:code,es:code
	 push es
	 mov ax,masterenv_seg
		cmp	use_mastenv,0
		jne	envfree_0
		mov	ax,localenv_seg
envfree_0:
	 push ax
	 dec ax
	 mov es,ax
	 mov dx,es:[3] ;Get size of env segment
	 mov cl,4 ;Convert paragraphs to bytes
	 shl dx,cl
	 pop es
	 mov cx,dx
	 xor ax,ax
	 xor di,di
envfree_1:
	 repne scasb ;Loop through the environment
	 cmp byte ptr es:[di],al ; until the end of the
	 loopne envfree_1 ; env strings is found.
	 jcxz envfree_2
	 mov ax,dx
	 sub ax,di
		dec	ax			;Sub byte for extra zero
envfree_2:
	 xor dx,dx
	 pop es
	 mov di,dest_var_val
	 call hex2asc
		clc
envfree_exit:
	 ret
envfree_sys endp
;-----------------------------------------------------------------------------
; ENVSIZE SYS returns the size of the environment.
;-----------------------------------------------------------------------------
envsize_sys proc near
	 assume cs:code,ds:code,es:code
	 push es
	 mov ax,masterenv_seg
		cmp	use_mastenv,0
		jne	envsize_1
		mov	ax,localenv_seg
envsize_1:
	 push ax
	 dec ax
	 mov es,ax
	 mov ax,es:[3] ;Get size of env segment
	 mov cl,4 ;Convert paragraphs to bytes
	 shl ax,cl
	 pop es
	 xor dx,dx
	 pop es
	 mov di,dest_var_val
	 call hex2asc
	 clc
	 ret
envsize_sys endp
;-----------------------------------------------------------------------------
; MASTERVAR SYS returns the value from a variable in the master environment.
;-----------------------------------------------------------------------------
mastervar_sys proc near
	 assume cs:code,ds:code,es:code
	 push ds
	 push es
	 mov ax,masterenv_seg
	 mov di,var1_value		;Point to env var name
		call	getenvvar		;Get ptr to env var value
		jc	mastervar_exit		;CF = 1, var not found.
	 mov di,cs:dest_var_val	;Copy var value to dest string
	 call copy_string
	 clc
mastervar_exit:
	 pop ds
	 pop es
	 ret
mastervar_sys endp
;-----------------------------------------------------------------------------
; LOCALVAR SYS returns the value from a variable in the local environment.
;-----------------------------------------------------------------------------
localvar_sys	proc near
	 assume cs:code,ds:code,es:code
	 push ds
	 push es
		mov	ax,localenv_seg
	 mov di,var1_value		;Point to env var name
		call	getenvvar		;Get ptr to env var value
		jc	localvar_exit		;CF = 1, var not found.
	 mov di,cs:dest_var_val	;Copy var value to dest string
	 call copy_string
	 clc
localvar_exit:
	 pop ds
	 pop es
	 ret
localvar_sys	endp
;-----------------------------------------------------------------------
; INWIN SYS returns 1 if Windows currently running.
;-----------------------------------------------------------------------
inwin_sys	proc near
	 assume cs:code,ds:code,es:code
		mov	ax,1600h		;See if enhanced mode Win
		int	2fh
		or 	al,al
		jne	inwin_1
		mov	ax,4680h		;See if std or real Win
		int	2fh
		or 	al,al
		mov	al,0
		jne	inwin_2
inwin_1:
		mov	al,1			;Indicate Windows active
inwin_2:
		xor	ah,ah
		xor	dx,dx
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
	 clc
	 ret
inwin_sys	endp
;-----------------------------------------------------------------------
; INT2FCHECK Calls Interupt 2F (Multiplex) to determine if a program is 
; loaded.
;-----------------------------------------------------------------------
int2fcheck_sys	proc near
	 assume cs:code,ds:code,es:code
		xor	ax,ax
		cmp	dos_version,300h	;Don't call 2F if DOS 2.x
		jb	int2fcheck_3
	 mov si,var1_value ;Point to 1st parameter
	 call caps_string
	 mov di,offset int2fname_tbl	;See if an alias is used in
	 call findstr			; place of a number.
	 jc int2fcheck_1
	 mov al,[bx+offset int2falias_tbl]
	 jmp short int2fcheck_2
int2fcheck_1:
	 mov si,var1_value ;Convert 1st parameter to hex
	 call asc2hex
	 mov si,offset errmsg14 ;Number too large
		or	dx,dx
		jne	int2fcheck_error	;Make sure the number is 
		or	ah,ah			; less than 256
		jne	int2fcheck_error
int2fcheck_2:
		xor	ah,ah
		xchg	ah,al			;Device number in AH 0 in AL
		cmp	ah,13h			;Don't call for device 13
		je	int2fcheck_3
		push	si
		push	bp
		pushf
		mov	saved_spint,sp
		int	2fh
		cli
		mov	bx,cs			;Assume nothing after blind
		mov	ds,bx			; Int 2F call. Restore stack
		mov	es,bx			; and other registers.
		mov	ss,bx
		mov	sp,saved_spint
		sti
		popf
		pop	bp
		pop	si
int2fcheck_3:
		xor	ah,ah
		xor	dx,dx
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
	 clc
int2fcheck_exit:
	 ret
int2fcheck_error:
		stc
		jmp	short int2fcheck_exit
int2fcheck_sys	endp
;-----------------------------------------------------------------------
; FILES SYS Returns the number of files that can be opened
;-----------------------------------------------------------------------
files_sys	proc near
	 assume cs:code,ds:code,es:code
		push	es
		mov	ah,52h			;Get list of lists
		int	21h
		les	bx,es:[bx+4]		;Get ptr to Sys File Table
		xor	ax,ax			;Clear count
files_1:
		mov	cx,es			;If ptr -1, no more 
		cmp	bx,-1			; entries.
		je	files_3
files_2:
		add	ax,es:[bx+4]		;Add num of files in tbl
		les	bx,es:[bx]		;Get ptr to next file tbl
		jmp	short files_1
files_3:
		pop	es
		xor	dx,dx
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
	 clc
	 ret
files_sys	endp
;-----------------------------------------------------------------------
; LASTDRIVE SYS Returns the last drive letter allowed by system
;-----------------------------------------------------------------------
lastdrive_sys	proc near
		push	es
		mov	ah,52h			;Get list of lists
		int	21h
		mov	si,10h			;Assume 2.x offset
		cmp	dos_version,300h
		jb	lastdrive_1
		mov	si,1bh			;Assume 3.0 offset
		cmp	dos_version,310h
		jb	lastdrive_1
		mov	si,1bh			;Assume 3.1,2,3 offset
		cmp	dos_version,400h
		jb	lastdrive_1
		mov	si,21h			;Assume 4.x, 5.x offset
lastdrive_1:
		mov	al,es:[bx+si]		;Get lastdrive value
		add	ax,'@'			;Convert to letter
		pop	es
	 mov di,dest_var_val
		mov	ah,':'
		stosw
		xor	al,al			;Append terminating 0
		stosb
	 clc
	 ret
lastdrive_sys	endp
;------------------------------------------------------------------------
; TRUEVER SYS returns the real DOS version number. DOS 5 or later
;------------------------------------------------------------------------
truever_sys proc near
	 assume cs:code,ds:code,es:code
		mov	ax,dos_version		;Get std DOS version
		cmp	ax,500h
		jb	truever_1
		mov	ax,3306h		;Get real DOS version
		int	21h
		mov	ax,bx
		xchg	al,ah
truever_1:
	 mov di,dest_var_val
	 call printver		;Print version
	 clc
truever_exit:
	 ret
truever_sys endp
;------------------------------------------------------------------------
; CODEPAGE SYS returns the active code page
;------------------------------------------------------------------------
codepage_sys proc near
	 assume cs:code,ds:code,es:code
		mov	ax,31eh
		cmp	ax,dos_version		;See if DOS 3.3 
		ja	codepage_error
		mov	ax,6601h		;Get global code page
		int	21h
		mov	ax,bx 		;Get code page
		xor	dx,dx
	 mov di,dest_var_val
	 call hex2asc			;Print code page
	 clc
codepage_exit:
	 ret
codepage_error:
		call	seterr0msg		;Error, not DOS 3.3
		jmp	short codepage_exit
codepage_sys endp
;------------------------------------------------------------------------
; COUNTRY SYS returns the active code page
;------------------------------------------------------------------------
country_tbl	db	0,2,7,9,11,13,15,16,17,22
country_sys proc near
	 assume cs:code,ds:code,es:code
		mov	ax,3800h		;Get country info
		mov	dx,databuff_ptr		;Point to data buffer
		int	21h
		xor	ah,ah
		cmp	al,-1
		jne	countrysys_1
		mov	ax,bx 		;Get country code
countrysys_1:
		cmp	num_params,0
		je	countrysys_4
		mov	si,var1_value
		call	asc2hex
		mov	si,databuff_ptr
		cmp	ax,9
		ja	countrysys_error
countrysys_2:
		mov	cl,al
		mov	bx,offset country_tbl
		xlat	
		add	si,ax
		mov	ax,[si]			;Get word
		or	cl,cl
		je	countrysys_4
		xor	ah,ah			;Other than 0, byte
		
		cmp	cl,5			;1,2,3,4,9 are
		jbe	countrysys_3		; ASCIIZ strings
		cmp	cl,9
		je	countrysys_3
		jmp	short countrysys_4
countrysys_3:
	 mov di,dest_var_val		;Copy string
		call	copy_string
		jmp	short countrysys_exit
countrysys_4:
		xor	dx,dx
	 mov di,dest_var_val
	 call hex2asc			;Print code page
countrysys_exit:
	 clc
countrysys_exit1:
	 ret
countrysys_error:
		stc
		mov	si,offset errmsg14	;Number too large
		jmp	short countrysys_exit1
country_sys endp
;------------------------------------------------------------------------
; BIOSDATE SYS returns the date of the ROM BIOS
;------------------------------------------------------------------------
biosdate_sys proc near
	 assume cs:code,ds:code,es:code
		push	ds
		mov	si,0f000h		;ROM segment
		mov	ds,si
		mov	si,0fff5h		;Offset of date
		mov	di,cs:dest_var_val
		mov	cx,4			;Copy 8 bytes
		rep	movsw
		pop	ds
		xor	al,al			;Add terminating 0
		stosb
	 clc
	 ret
biosdate_sys endp
;------------------------------------------------------------------------
; GETKEY SYS Waits for a key and returns the ASCII code and scan code 
;------------------------------------------------------------------------
getkey_sys proc near
	 assume cs:code,ds:code,es:code
		call	getkey
	 mov di,dest_var_val
		push	ax
		xor	ah,ah
		xor	dx,dx
	 call hex2asc
		mov	byte ptr [di-1],' '	;Replace term 0 with space
		pop	ax
		xor	al,al
		xchg	al,ah
	 call hex2asc
	 clc
	 ret
getkey_sys endp
;------------------------------------------------------------------------
; LOCALENV SYS Returns the segment of the local env
;------------------------------------------------------------------------
localenv_sys proc near
	 assume cs:code,ds:code,es:code
		mov	ax,localenv_seg
		xor	dx,dx
	 mov di,dest_var_val
	 call hex2asc
	 clc
	 ret
localenv_sys endp
;------------------------------------------------------------------------
; MASTERENV SYS Returns the segment of the master env
;------------------------------------------------------------------------
masterenv_sys proc near
	 assume cs:code,ds:code,es:code
		mov	ax,masterenv_seg
		xor	dx,dx
	 mov di,dest_var_val
	 call hex2asc
	 clc
	 ret
masterenv_sys endp
;========================================================================
; Number Functions
;========================================================================
;------------------------------------------------------------------------
; ADD NUM returns the sum of a series of numbers
;------------------------------------------------------------------------
add_num proc near
	 assume cs:code,ds:code,es:code
		mov	bx,offset addnum_callback
		call	process_nums		;Process vars
	 mov si,offset errmsg12 ;Overflow message
	 ret
addnum_callback:
		add	si,ax			;Add number to running
		adc	di,dx			; sum.
		ret
add_num endp
;------------------------------------------------------------------------
; SUB NUM returns the difference of two numbers
;------------------------------------------------------------------------
sub_num proc near
	 assume cs:code,ds:code,es:code
		mov	bx,offset subnum_callback
		call	process_nums		;Process vars
	 mov si,offset errmsg13 ;Underflow message
	 ret
subnum_callback:
		sub	si,ax			;Add number to running
		sbb	di,dx			; sum.
	 ret
sub_num endp
;------------------------------------------------------------------------
; MUL NUM returns the product of two numbers
;------------------------------------------------------------------------
mul_num proc near
	 assume cs:code,ds:code,es:code
		mov	bx,offset mulnum_callback
		call	process_nums		;Process vars
	 mov si,offset errmsg13 ;Overflow message
mulnum_exit:
	 ret
mul_num_error:
	 stc
	 mov si,offset errmsg10 ;Multiply overflow
	 stc
	 jmp short mulnum_exit
mulnum_callback:
		push	bx
		push	cx
		push	dx
		push	bp
	 mov cx,dx ;Copy numbers
	 mov bx,ax
	 mul cx ;32 bit multiply.
	 jc mulcb_exit ;Param 1 in DI,SI
	 mov bp,ax ;Param 2 in CX,BX
	 mov ax,di
	 mul cx ; DI SI
	 or ax,dx ; CX BX
	 jnz mulcb_exit ; ---------------------
	 mov ax,di ; (BX * SI)
	 mul bx ; (BX * DI)
	 jc mulcb_exit ; (CX * SI)
	 add bp,ax ; + (CX * DI)
	 mov ax,si ; ---------------------
	 mul bx ; DX AX
	 add dx,bp
		mov	si,ax
		mov	di,dx
mulcb_exit:
		pop	bp
		pop	dx
		pop	cx
		pop	bx
		ret
mul_num endp
;------------------------------------------------------------------------
; DIV NUM returns the quotient of two numbers
;------------------------------------------------------------------------
div_num proc near
	 assume cs:code,ds:code,es:code
	 call conv2num ;Convert first two parms to
	 jc div_num_exit ; 32 bit numbers.
	 push cx
	 or cx,bx ;Prevent divide by zero
	 pop cx
	 jz div_num_error
div_num_1:
	 or cx,cx ;Divide both numbers by 2
	 je div_num_2 ; until high word of
	 shr dx,1 ; divisor (CX) is zero.
	 rcr ax,1
	 shr cx,1
	 rcr bx,1
	 jmp short div_num_1
div_num_2:
	 push ax ;Save low word
	 mov ax,dx
	 xor dx,dx
	 div bx ;Divide high word
	 mov cx,ax ;Save high quotent
	 pop ax
	 div bx ;Divide low word
	 mov dx,cx
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
div_num_exit:
	 ret
div_num_error:
	 mov si,offset errmsg11
	 stc
	 jmp short div_num_exit
div_num endp
;------------------------------------------------------------------------
; AND NUM returns the logical AND of two numbers
;------------------------------------------------------------------------
and_num proc near
	 assume cs:code,ds:code,es:code
		mov	bx,offset andnum_callback
		call	process_nums		;Process vars
		mov	si,offset errmsg14	;Number too large
	 ret
andnum_callback:
		and	si,ax			;AND number 
		and	di,dx			; 
		ret
and_num endp
;------------------------------------------------------------------------
; OR NUM returns the logical OR of two numbers
;------------------------------------------------------------------------
or_num proc near
	 assume cs:code,ds:code,es:code
		mov	bx,offset ornum_callback
		call	process_nums		;Process vars
		mov	si,offset errmsg14	;Number too large
	 ret
ornum_callback:
		or 	si,ax			;OR number 
		or 	di,dx			; 
		ret
or_num endp
;------------------------------------------------------------------------
; XOR NUM returns the logical exclusive OR of two numbers
;------------------------------------------------------------------------
xor_num proc near
	 assume cs:code,ds:code,es:code
	 call conv2num ;Convert first two parms to
	 jc xor_num_exit ; 32 bit numbers. Carry
	 xor ax,bx ; set, overflow.
	 xor dx,cx
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
xor_num_exit:
	 ret
xor_num endp
 
;------------------------------------------------------------------------
; NOT NUM returns the inverse of a number
;------------------------------------------------------------------------
not_num proc near
	 assume cs:code,ds:code,es:code
	 mov si,var1_value ;Convert 1st parameter to hex
	 call asc2hex
	 mov si,offset errmsg14 ;Number too large
	 jc not_num_exit ;If error, exit
	 not ax
	 not dx
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
not_num_exit:
	 ret
not_num endp
;------------------------------------------------------------------------
; CONVERT NUM converts a number to the base specified by 2nd param
;------------------------------------------------------------------------
convert_num proc near
	 assume cs:code,ds:code,es:code
	 call conv2num ;Convert first two parms
		or	cx,cx
		jne	convertnum_error
		cmp	bx,16
		ja	convertnum_error
		cmp 	bl,1
		jbe	convertnum_error
		mov	number_base,bx
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
convertnum_exit:
	 ret
convertnum_error:
		mov	si,offset errmsg14	;Number too large
		stc
		jmp	short convertnum_exit
convert_num endp
;========================================================================
; Programming Functions
;========================================================================
;------------------------------------------------------------------------
; PEEK PROG Returns byte(s) from memory
;------------------------------------------------------------------------
peek_prog	proc near
	 assume cs:code,ds:code,es:code
		push	bp
	 call conv2num ;Convert first two parms to
	 jc peek_prog_exit ; 32 bit numbers. 
		mov	si,offset errmsg14
		or	dx,cx			;Check to see that neither
		jne	peek_prog_err1		; number > 64K
		mov	cx,1			;Assume 1 byte peek
		mov	bp,0			;Assume byte size peek
	 mov di,dest_var_val		;Pt to output buff
		mov	si,ax			;Save seg value
		cmp	num_params,3		;If 3rd param, it is the
		jb	peek_prog_1		; number of bytes to
		push	ax			; return
		push	bx
		push	si
		mov	si,var3_value
		call	asc2hex
		mov	cx,ax
		pop	si
		pop	bx
		pop	ax
		cmp	num_params,4		;If 4th param, it signals
		jb	peek_prog_1		; word size peek
		mov	bp,1
		shr	cx,1
peek_prog_1:
		and	cx,003fh		;Allow only 64 bytes out
		jne	peek_prog_2
		mov	cx,1
peek_prog_2:
		push	es
		mov	es,si
		mov	ax,es:[bx]
		pop	es
		or 	bp,bp			;If word size, print
		je	peek_prog_3		; high byte first.
		push	ax
		xor	al,al
		xchg	ah,al
		xor	dx,dx
		call	lead_zero
	 call hex2asc ;Convert result to ASCII
		inc	bx
		dec	di
		pop	ax
peek_prog_3:
		xor	ah,ah
		xor	dx,dx
		call	lead_zero
	 call hex2asc ;Convert result to ASCII
		inc	bx
		mov	byte ptr [di-1],' '	;Replace term 0 with space
		loop	peek_prog_1
		mov	byte ptr [di-1],0	;Restore term 0
peek_prog_exit:
		pop	bp
	 ret
peek_prog_err1:
		stc
		jmp	short peek_prog_exit
peek_prog	endp
;------------------------------------------------------------------------
; POKE PROG Writes a string of bytes to memory
;------------------------------------------------------------------------
poke_prog	proc near
	 assume cs:code,ds:code,es:code
	 call conv2num ;Convert first two parms to
	 jc poke_prog_exit ; 32 bit numbers. 
		mov	si,offset errmsg14	;Number too large
		or	dx,cx			;Check to see that neither
		jne	poke_prog_error		; number > 64K
	 mov si,offset errmsg18	;Not enough parameters
		mov	cx,num_params
		sub	cx,2
		jbe	poke_prog_error
	 mov si,offset var3_value
		push	es
		mov	es,ax			;Load segment
		mov	di,bx			;Load offset
		cli		
poke_prog_1:
		push	si
		mov	si,[si]			;Get ptr to next var	
	 call 	asc2hex
		pop	si
	 jnc	poke_prog_3
poke_prog_2:
		sti
		pop	es
		mov	si,offset errmsg14	;Number too large
		stc	
		jmp	poke_prog_exit
poke_prog_3:
		or	dx,dx			;Check to see if poke
		jne	poke_prog_2		; val > 256
		or	ah,ah
		jne	poke_prog_2
		stosb
		inc	si			;Move ptr to next var
		inc	si
		loop	poke_prog_1
		pop	es
	 mov di,dest_var_val		;Zero return value
		mov	byte ptr [di],0
		clc
poke_prog_exit:
	 ret
poke_prog_error:
		stc
		jmp	short poke_prog_exit
poke_prog	endp
;------------------------------------------------------------------------
; IN PROG Returns a byte from an IO port.
;------------------------------------------------------------------------
in_prog		proc near
	 assume cs:code,ds:code,es:code
	 mov si,var1_value ;Convert 1st param to hex
	 call asc2hex
	 mov si,offset errmsg14 ;Number too large
	 jc in_prog_exit ;If error, exit
		or	dx,dx			;Make sure number not too
		jne	in_prog_error		; big.
		mov	dx,ax
		in	al,dx
		xor	ah,ah
		xor	dx,dx
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
in_prog_exit:
	 ret
in_prog_error:
		stc
		jmp	short in_prog_exit
in_prog		endp
;------------------------------------------------------------------------
; OUT PROG Outputs a byte to an IO port.
;------------------------------------------------------------------------
out_prog	proc near
	 assume cs:code,ds:code,es:code
	 call conv2num ;Convert first two parms to
	 jc out_prog_exit ; 32 bit numbers. 
		mov	si,offset errmsg14	;Number too large
		or	dx,dx			;Check to see that neither
		jne	out_prog_error		; number > 64K
		or	cx,cx
		jne	poke_prog_error
		or	bh,bh
		jne	poke_prog_error
		
		mov	dl,bl
		xchg	ax,dx
		out	dx,al
	 mov di,dest_var_val		;Zero output
		mov	byte ptr [di],0
out_prog_exit:
	 ret
out_prog_error:
		stc
		jmp	short out_prog_exit
out_prog	endp
;------------------------------------------------------------------------
; INTERRUPT PROG Performs an system interrupt
; NOTE: This routine contains self modifying code!!!
;------------------------------------------------------------------------
interrupt_prog	proc near
	 assume cs:code,ds:code,es:code
		push	bp
	 mov si,var1_value ;Convert 1st param to hex
	 call asc2hex
	 jnc intprog_0
intprog_error:
	 mov si,offset errmsg14 ;Number too large
		stc
		jmp	short intprog_error
intprog_0:
		or	dl,dh			;Make sure number not too
		or	dl,ah			; big.
		jne	intprog_error
		mov	si,offset intprog_opcode
		mov	[si+1],al		;Set interrupt number
		mov bp,offset var1_value ;Get ptr to array
		mov	cx,9
intprog_1:
		inc	bp
		inc	bp
		mov	si,[bp]			;Get ptr to next param
		call	asc2hex			;Convert to hex
		or	dx,dx			;Limit test
		jne	intprog_error
		mov	[bp],ax			;Save hex value
		loop	intprog_1		
		std				;Set direction down!
		mov	si,bp			;copy ptr to array
		lodsw
		mov	es,ax
		assume	es:nothing
		lodsw
		push	ax			;Save DS for later
		lodsw
		mov	bp,ax
		lodsw
		push	ax			;Save SI for later
		lodsw
		mov	di,ax
		lodsw
		mov	dx,ax
		lodsw
		mov	cx,ax
		lodsw
		mov	bx,ax
		lodsw
		pop	si
		pop	ds
intprog_2:
		cld				;For neatness
		assume	ds:nothing
		mov	cs:saved_ssint,ss
		mov	cs:saved_spint,sp		
intprog_opcode:
		int	2fh			;Perform interrupt
		cli
		mov	ss,cs:saved_ssint	;Restore stack
		mov	sp,cs:saved_spint
		sti
		pushf
		cld				;Set dir flag UP
		push	es
		push	di
		push	cs
		pop	es
		assume	es:code
		mov	di,offset var1_value
		stosw				;Save AX
		mov	ax,bx
		stosw
		mov	ax,cx
		stosw
		mov	ax,dx
		stosw
		pop	ax			;Get returned DI
		stosw
		mov	ax,si
		stosw
		mov	ax,bp
		stosw
		mov	ax,ds
		stosw
		pop	ax			;Get returned ES
		stosw
		pop	ax			;Get returned flags		
		stosw
		push	cs			;Reset DS = CS
		pop	ds
		assume	ds:code
		mov	si,offset var1_value
	 mov di,dest_var_val		;Zero output
		mov	cx,10
intprog_3:
		lodsw				;Get reg value
		xor	dx,dx
		call	hex2asc			;Convert number
		mov	byte ptr [di-1],' '
		loop	intprog_3
		mov	byte ptr [di-1],0	;Restore last 0
		clc
intprog_exit:
		pop	bp
		ret
interrupt_prog	endp
;------------------------------------------------------------------------
; SCAN PROG Returns byte(s) from memory
;------------------------------------------------------------------------
scan_prog	proc near
	 assume cs:code,ds:code,es:code
		push	bp
		mov	bp,offset var3_value	;Get ptr to scan bytes
		mov	di,databuff_ptr		;Use file data buff
		mov	cx,num_params
		sub	cx,2
		jbe	scan_prog_error
		mov	bx,cx			;Save byte count
scan_prog_0:
	 mov si,[bp]			;Get ptr to variable
		inc	bp			;Point BP to next var
		inc	bp
	 call asc2hex			;Convert ASCII num to
		stosb				; hex num and store.
		or	dx,dx
		jne	scan_prog_err1
		or	ah,ah
		jne	scan_prog_err1
		loop	scan_prog_0
		xor al,al ;Term string
	 stosb
		mov	bp,bx			;Save scan str length
	 call conv2num ;Convert first two parms to
	 jc scan_prog_exit ; 32 bit numbers. 
		mov	si,offset errmsg14
		or	dx,cx			;Check to see that neither
		jne	scan_prog_err1		; number > 64K
		push	es
		mov	es,ax			;Get segment
		mov	dx,databuff_ptr		;DX pts to scan string
		mov	cx,bx			;Scan remaining segment
		neg	cx
scan_prog_2:
		push	cx
		mov	di,bx			;Get mem offset 
		mov	si,dx			;Get scan str offset
		mov	cx,bp			;Get scan str length
		rep	cmpsb
		pop	cx
		je	scan_prog_3
		inc	bx
		loop	scan_prog_2
		pop	es
		mov	si,offset errmsg20	;String not found
		jmp	short scan_prog_error
scan_prog_3:
		pop	es
		xor	dx,dx
		mov	di,dest_var_val
		call	hex2asc
		mov	byte ptr [di-1],' '	;Replace 0 with space
		mov	ax,bx
		call	hex2asc
		clc
scan_prog_exit:
		pop	bp
	 ret
scan_prog_err1:
		mov	si,offset errmsg14	;Number too large
scan_prog_error:
		stc
		jmp	short scan_prog_exit
scan_prog	endp
;========================================================================
; Time and Date Functions
;========================================================================
;------------------------------------------------------------------------
; DAY TIME Returns the name of the current day
;------------------------------------------------------------------------
day_time	proc near
	 assume cs:code,ds:code,es:code
		cmp	num_params,1
		jae	day_time_1
		mov	ah,2ah			;Get system day
		int	21h
		xor	ah,ah
		jmp	short day_time_2
day_time_1:
	 mov si,var1_value ;Convert 1st parameter to hex
	 call asc2hex
	 mov si,offset errmsg14 ;Number too large
	 jc day_time_exit ;If error, exit
		or	dx,dx
		jne	day_time_error
		or	ax,ax
		je	day_time_error
		cmp	ax,7
		ja	day_time_error
		dec	ax
day_time_2:
		mov	bx,ax
		mov	di,offset day_list
		call	get_string
		
	 mov si,cs:dest_var_val	
		xchg	si,di
	 call copy_string
	 clc
day_time_exit:
		ret
day_time_error:
		stc
		jmp	short day_time_exit
day_time	endp
;------------------------------------------------------------------------
; MONTH TIME Returns the name of the current month
;------------------------------------------------------------------------
month_time	proc near
	 assume cs:code,ds:code,es:code
		cmp	num_params,1
		jae	month_time_1
		mov	ah,2ah			;Get system date
		int	21h
		mov	bl,dh
		xor	bh,bh
		jmp	short month_time_2
month_time_1:
	 mov si,var1_value ;Convert 1st parameter to hex
	 call asc2hex
	 mov si,offset errmsg14 ;Number too large
	 jc month_time_exit ;If error, exit
		or	dx,dx
		jne	month_time_error
		or	ax,ax
		je	month_time_error
		cmp	ax,12
		ja	month_time_error
		mov	bx,ax
month_time_2:
		dec	bx
		mov	di,offset month_list
		call	get_string
		
	 mov si,cs:dest_var_val	
		xchg	si,di
	 call copy_string
	 clc
month_time_exit:
		ret
month_time_error:
		stc
		jmp	short month_time_exit
month_time	endp
;-----------------------------------------------------------------------
; DATE TIME Returns the current date. The date is returned either 
; as Month xx, 199x or as mm-dd-yyyy depending on params.
;-----------------------------------------------------------------------
date_time	proc near
	 assume cs:code,ds:code,es:code
		xor	ax,ax			;Clear style flag
		cmp	num_params,1
		jb	date_time_1
	 mov si,var1_value ;Convert 1st parameter to hex
	 call asc2hex
		jc	date_time_error
	 mov si,offset errmsg14 ;Number too large
		or	dx,dx
		jne	date_time_error
		cmp	al,1
		ja	date_time_error
date_time_1:
		push	ax
		mov	ah,2ah			;Get system date
		int	21h
		xor	ah,ah
		pop	bx			;Get back style param
		cmp	bl,1
		je	date_time_2
		push	cx 			;Save year
		push	dx			;Save day of the month
		mov	bl,dh			;Copy month
		dec	bx
		mov	di,offset month_list
		call	get_string
		
	 mov si,dest_var_val	
		xchg	si,di
	 call copy_string
		mov	byte ptr [di-1],' '	;Change term 0 to space
		pop	ax			;Get day
		xor	ah,ah
		xor	dx,dx
	 call hex2asc ;Convert day to ASCII
		mov	word ptr [di-1],' ,'	;Change term 0 to , space
		inc	di
		pop	ax			;Get year
		xor	dx,dx
	 call hex2asc ;Convert year to ASCII
		jmp	short date_time_exit
date_time_2:
	 mov di,dest_var_val
		call	print_date		;Print in mm-dd-yyyy fmt
date_time_exit:
	 clc
date_time_exit1:
		ret
date_time_error:
		stc
		jmp	short date_time_exit1
date_time	endp
;-----------------------------------------------------------------------
; TIME TIME Returns the current time. 
;-----------------------------------------------------------------------
time_time	proc near
	 assume cs:code,ds:code,es:code
	 	mov	ah,2ch			;Get system date
		int	21h
	 mov di,dest_var_val	
		call	print_time		;Print in hh:mm:ss fmt
time_time_exit:
	 clc
time_time_exit1:
		ret
time_time_error:
		stc
		jmp	short time_time_exit1
time_time	endp
;=======================================================================
; Memory functions
;=======================================================================
;-----------------------------------------------------------------------
; TOTALMEM MEM Returns the size of the largest blk of free conv mem
;-----------------------------------------------------------------------
totalmem_mem	proc near
	 assume cs:code,ds:code,es:code
		int	12h			;Get conv mem size
totalmem_2:
		mov	cx,1024			;Convert Kbytes
		mul	cx			; to bytes
	 mov di,dest_var_val	
		call	hex2asc
	 clc
		ret
totalmem_mem	endp
;-----------------------------------------------------------------------
; FREEMEM MEM Returns the size of the largest blk of free conv mem
;-----------------------------------------------------------------------
freemem_mem	proc near
	 assume cs:code,ds:code,es:code
		cmp	installed,0
		je	freemem_1
		mov	ah,48h
		mov	bx,-1			;Ask for all mem
		int	21h
		mov	ax,bx
		jmp	short freemem_2
freemem_1:
		mov	ax,ds:[2]		;Get end of mem ptr
		mov	bx,cs
		sub	ax,bx
freemem_2:
		mov	cx,16			;Convert paragraphs
		mul	cx			; to bytes
	 mov di,dest_var_val	
		call	hex2asc
	 clc
		ret
freemem_mem	endp
;-----------------------------------------------------------------------
; FREEEXT MEM Returns the size of extended memory
;-----------------------------------------------------------------------
freeext_mem	proc near
	 assume cs:code,ds:code,es:code
		cmp	xms_version,0		;See if mem driver
		je	freeext_1
		mov	ah,8
		call	ds:[xms_serv]		;Call driver
		jmp	short freeext_2
freeext_1:
		mov	ah,88h			;Get ext mem size
		int	15h			; BIOS call
		jnc	freeext_2
		xor	ax,ax
freeext_2:
		mov	cx,1024			;Convert 1K to bytes
		mul	cx			
	 mov di,dest_var_val	
		call	hex2asc
	 clc
freeext_exit:
		ret
freeext_mem	endp
;-----------------------------------------------------------------------
; TOTALEXT MEM Returns the size of extended memory
;-----------------------------------------------------------------------
totalext_mem	proc near
	 assume cs:code,ds:code,es:code
		cmp	xms_version,0		;See if mem driver
		je	totalext_1
		mov	ah,8
		call	ds:[xms_serv]		;Call driver
		mov	ax,dx
		jmp	short totalext_2
totalext_1:
		mov	ah,88h			;Get ext mem size
		int	15h			; BIOS call
		jnc	totalext_2
		xor	ax,ax
totalext_2:
		mov	cx,1024			;Convert 1K to bytes
		mul	cx			
	 mov di,dest_var_val	
		call	hex2asc
	 clc
totalext_exit:
		ret
totalext_mem	endp
;-----------------------------------------------------------------------
; EXTVER MEM Returns the version of the extended memory driver
; Check version again, since some driver change version reporting
; depending on the system environment.
;-----------------------------------------------------------------------
extver_mem	proc near
	 assume cs:code,ds:code,es:code
		mov	ax,xms_version		;See if mem driver
		or	ax,ax
		je	extver_1
		
		xor	ax,ax
		call	ds:[xms_serv]		;Get version number
		mov	bx,ax			;Version num returned
		shr	al,1			; as BCD. Convert
		shr	al,1			; to std DOS format
		shr	al,1			; of maj in AH and
		shr	al,1			; minor in AL
		mov	ah,10
		mul	ah
		and	bl,0fh
		add	al,bl
		mov	ah,bh
extver_1:
	 mov di,dest_var_val	
		call	printver
	 clc
extver_exit:
		ret
extver_mem	endp
;-----------------------------------------------------------------------
; TOTALEMS MEM Returns the size of Expanded memory
;-----------------------------------------------------------------------
totalems_mem	proc near
	 assume cs:code,ds:code,es:code
		xor	ax,ax
		or	ax,ems_version
		je	totalems_1
		mov	ah,42h			;Get EMS Mem amounts
		int	67h			;Call driver
		mov	ax,dx
		mov	cx,16384		;Convert 16K pages 
		mul	cx			; to bytes
totalems_1:
	 mov di,dest_var_val	
		call	hex2asc
	 clc
totalems_exit:
		ret
totalems_mem	endp
;-----------------------------------------------------------------------
; FREEEMS MEM Returns the amount of free Expanded memory
;-----------------------------------------------------------------------
freeems_mem	proc near
	 assume cs:code,ds:code,es:code
		xor	ax,ax
		or	ax,ems_version
		je	freeems_1
		mov	ah,42h			;Get EMS Mem amounts
		int	67h			;Call driver
		mov	ax,bx
		mov	cx,16384		;Convert 16K pages 
		mul	cx			; to bytes
freeems_1:
	 mov di,dest_var_val	
		call	hex2asc
	 clc
freeems_exit:
		ret
freeems_mem	endp
;-----------------------------------------------------------------------
; EMSVER MEM Returns the version of the extended memory driver
; Check version again, since some driver change version reporting
; depending on the system environment.
;-----------------------------------------------------------------------
emsver_mem	proc near
	 assume cs:code,ds:code,es:code
		mov	ax,ems_version		;See if mem driver
		or	ax,ax
		je	emsver_1
		mov	ah,46h			;Get version
		int	67h
		or	ah,ah
		je	emsver_0
		xor	ax,ax
emsver_0:
		mov	bl,al			;Convert ver number
		shl	ax,1
		shl	ax,1
		shl	ax,1
		shl	ax,1
		mov	al,bl
		and	ax,0f0fh
emsver_1:
	 mov di,dest_var_val	
		call	printver
	 clc
emsver_exit:
		ret
emsver_mem	endp
;-----------------------------------------------------------------------
; FREEUMB MEM Returns the size of the largest free upper memory block 
;-----------------------------------------------------------------------
freeumb_mem	proc near
	 assume cs:code,ds:code,es:code
		cmp	dos_version,500h
		jae	freeumb_0
		cmp	xms_version,0
		mov	cx,0
		je	freeumb_3
		mov	dx,-1
		mov	ah,10h			;Request umb from drvr
		call	ds:[xms_serv]
		mov	cx,dx			;Save largest available
		jmp	short freeumb_3
freeumb_0:
		mov	ax,5800h		;Get allocation strat
		int	21h
		push	ax			;Save strategy
		mov	ax,5802h		;Get UMB link state
		int	21h
		xor	ah,ah
		push	ax			;Save link state
		mov	ax,5803h		;Link UMBs
		mov	bx,1
		int	21h
		jnc	freeumb_1
		call	check4xms		;See for ext mem drvr
		mov	cx,0
		jc	freeumb_2
		mov	dx,-1
		mov	ah,10h			;Request umb from drvr
		call	ds:[xms_serv]
		mov	cx,dx			;Save largest available
		jmp	short freeumb_2
freeumb_1:
		mov	ax,5801h		;Set mem alloc strat
		mov	bx,41h			;Best fit high only
		int	21h
		mov	ah,48h			;Alloc mem
		mov	bx,-1
		int	21h
		mov	cx,bx			;Save largest block
freeumb_2:
		pop	bx			;Get UMB link state
		mov	ax,5803h
		int	21h
		pop	bx			;Get mem alloc strat
		mov	ax,5801h
		int	21h
freeumb_3:
		mov	ax,cx
		mov	cx,16
		mul	cx
	 mov di,dest_var_val	
		call	hex2asc
	 clc
freeumb_exit:			
		ret
freeumb_error:
		call	seterr0msg		;Error, not DOS 5.0
		jmp	short freeumb_exit
freeumb_mem	endp
;=======================================================================
; Program Support Functions
;=======================================================================
;-----------------------------------------------------------------------
; HELP STRINGS Help function for the program ###
;-----------------------------------------------------------------------
help_tag1	db	"Function: ",0
help_tag2	db	" - Returns ",0
help_tag3	db	13,10,"Syntax: STRINGS [dest var =] ",0
help_tag4	db	" ",0
help_tag5	db	"This is a list of the available commands"
help_tag7	db	".",13,10,0
help_tag6	db	13,10,"For help on a specific command type: "
		db	"STRINGS HELP Command",0
help_error	db	"Command Help not available once installed",0
help_strings	proc near
	 assume cs:code,ds:code,es:code
		push	bp
		mov	si,offset program	;Print copyright msg
		call	print_strcr
	 mov si,cmd_value ;Point to command buffer
		cmp	help_flag,0
		jne	help_1
		mov	si,var1_value
help_1:
		mov	bp,si
	 call caps_string		;Search cmd table for
		inc	cx			; function. If not
	 mov di,offset command_table	; found, print general
	 call findstr			; help message.
	 jc help_3
		cmp	installed,0		;If installed, cmd help
		je	help_2			; not loaded.
		mov	si,offset help_error
 		jmp	short help_7
help_2:
		mov	si,offset help_tag1	;Print lead in.
		call	print_str
		mov	si,bp
		call	print_str		;Print function name
		mov	si,offset help_tag2	;Print sep
		call	print_str
	 shl bx,1			;Convert index into 
	 shl bx,1			; offset into help tbl
		mov	si,[bx+offset help_tbl]	;Print description
		call	print_str
		mov	si,offset help_tag7	;Print usage for fun
		call	print_str
		mov	si,offset help_tag3	;Print usage for fun
		call	print_str
	 mov si,bp			;Get ptr to command
		call	print_str		;Print function name
		mov	si,offset help_tag4	;Print sep
		call	print_str
		mov	si,[bx + offset help_tbl + 2]
		call	print_strcr		;Print syntax
		jmp	short help_exit
help_3:
		mov	si,offset help_tag5	;Print global help msg
		call	print_strcr
		mov	si,offset command_table	;Print every cmd in 
		xor	bl,bl			; the command table.
help_4:
		mov	di,dest_var_val
		mov	cx,6
help_5:
		push	cx
		call	copy_string		;Copy command
		dec	di
		mov	al,' '			;Print a cmd every
		neg	cx			; 15 columns. Fill
		add	cx,13			; in the space with
		rep	stosb			; blanks.
		pop	cx
		mov	bl,[si]			;See if end of list
		or	bl,bl
		je	help_6
		loop	help_5
help_6:
		xor	al,al
		stosb
		push	si
		mov	si,dest_var_val
		call	print_strcr
		pop	si
		or	bl,bl
		jne	help_4
		cmp	installed,0		;If installed, don't tell
		jne	help_exit		; user about cmd help.
		mov	si,offset help_tag6	;Print global help msg
help_7:
		call	print_strcr
help_exit:
		mov	di,dest_var_val
		xor	al,al
		stosb
		clc
		pop	bp
		ret
help_strings	endp
;-----------------------------------------------------------------------
; STRINGSVER Returns the Strings version
;-----------------------------------------------------------------------
ver_strings	proc	near
		mov	si,offset version
		lodsb
		mov	ah,[si+1]
		mov	di,dest_var_val
		stosw
		mov	ax,0030h		;Add ASCII 0 and term
		stosw
		clc		
		ret
ver_strings	endp
;-----------------------------------------------------------------------
; STRINGSINST Checks to see if Strings installed as TSR
;-----------------------------------------------------------------------
inst_strings	proc	near
		xor	ax,ax
		mov	dx,ax
		mov	al,installed
		mov	di,dest_var_val
		call	hex2asc
		clc		
		ret
inst_strings	endp
;=======================================================================
; Support Procedures
;=======================================================================
;-----------------------------------------------------------------------
;Read Console Gets input from the user
; Entry: CX - Max number of characters to read
; BL - Set to prevent echo of input
; DI - Ptr to output buffer
;-----------------------------------------------------------------------
readcon_keys	dw	27,4b00h,8,0
READCON_KEYCNT	equ	4
readcon_jmptbl	dw	offset readcon_echo
 		dw	offset readcon_bs
		dw	offset readcon_bs
		dw	offset readcon_esc
readcon_scur equ word ptr [bp-2]
readcon_sptr equ word ptr [bp-4]
readcon_cpos equ word ptr [bp-6]
readcon_scnt equ word ptr [bp-8]
readcon_pswf equ word ptr [bp-10]
		
read_console	proc	near
		push	bp
		mov	bp,sp
		sub	sp,10
		push	cx
		mov	ah,0fh			;Get display mode/page
		int	10h
		mov	ah,3			;Get init cursor pos
		int	10h
		pop	cx
		mov	readcon_scur,dx		;Save init cursor pos
		mov	readcon_cpos,0		;Position inside str
		mov	readcon_sptr,di		;Ptr to output buff 
		mov	readcon_scnt,cx		;Num chars to read 
		mov	readcon_pswf,bx		;Password flag
readcon_0:
		call	prog_idle		;Indicate idle
		mov	ah,6
		mov	dl,-1			;Get keyboard input
		int	21h
		jz	readcon_0		;No key, wait
		xor	ah,ah
		or	al,al			;If extended key get
		jne	readcon_1		; another.
		mov	ah,6
		mov	dl,-1			;Get extended key
		int	21h			; code.
		xor	ah,ah
		xchg	ah,al
readcon_1:
		cmp	al,13			;See if CR
		je	readcon_exit
		jcxz	readcon_3
		push	cx
		push	di
		mov	di,offset readcon_keys
		mov	cx,READCON_KEYCNT
		repne	scasw
		mov	si,cx
		pop	di
		pop	cx
		je	readcon_2
		dec	cx
		stosb				;Write char to buff		
		xor	si,si
		cmp	readcon_pswf,0
		je	readcon_2
		mov	al,'*'
		cmp	readcon_pswf,1
		je	readcon_2
		mov	al,' '
readcon_2:
		shl	si,1
		call	[si+offset readcon_jmptbl]
		jmp	short readcon_0
readcon_3:
		mov	al,7
		call	readcon_echo		;Beep the speaker
		jmp	short readcon_0
readcon_exit:
		xor	al,al			;Terminate string
		stosb
		clc
		mov	sp,bp
		pop	bp
		ret
;
; Process backspace
;
readcon_bs	proc	near
		cmp	cx,readcon_scnt		;If at start of buff,
		je	readcon_bsexit		; ignore.
		push	ax
		call	readcon_lcur		;Backup cursor
		mov	al,' '
		call	readcon_echo		;Print space
		pop	ax	
		call	readcon_lcur
		dec	di
		inc	cx
readcon_bsexit:
		stc
		ret
readcon_bs	endp
;
; Process left cursor
;
readcon_lcur	proc	near
		or	cx,cx
		je	readcon_lcurexit
		mov	al,8			;Backspace char
		call	readcon_echo
readcon_lcurexit:
		stc
		ret
readcon_lcur	endp		
;
; Process escape 
;
readcon_esc	proc	near
		cmp	cx,readcon_scnt
		je	readcon_escexit
		mov	ah,3			;Get cursor pos
		int	10h
		mov	cx,readcon_scur		;Get init cur pos
		sub	dl,cl			;Compute difference
		sub	dh,ch
		push	es
		mov	ax,40h
		mov	es,ax
		mov	al,es:[4Ah]
		pop	es
		mov	ah,dh
		mul	ah
		xor	dh,dh
		add	ax,dx
		push	ax
		mov	dx,readcon_scur		;Get initial pos
		call	setcursor
		pop	cx
		jcxz	readcon_esc2
readcon_esc1:
		mov	al,' '
		call	readcon_echo
		loop	readcon_esc1
readcon_esc2:
		mov	dx,readcon_scur		;Get initial pos
		call	setcursor
		mov	di,readcon_sptr
		mov	cx,readcon_scnt
readcon_escexit:
		
		ret
readcon_esc	endp		
;
; Echo character in AL to screen.
;
readcon_echo	proc	near
		push	ax
		push	dx
		mov	dl,al			;Echo character
		mov	ah,2
		int	21h
		pop	dx
		pop	ax
		ret
readcon_echo	endp
read_console	endp
;-----------------------------------------------------------------------
; SETCURSOR Sets the position of the cursor
; Entry: DX - New cursor position
; BH - Video page
;-----------------------------------------------------------------------
setcursor	proc	near
		mov	ah,2
		int	10h
setcursor	endp
;-----------------------------------------------------------------------
; PRINTVER Prints the version number in AX to buff 
; Entry: AX - Version number. AH = Major ver, AL = Minor ver
; DI - Pointer to output buffer
;-----------------------------------------------------------------------
printver	proc	near
	 push ax
	 mov al,100
	 xchg al,ah ;Copy major ver number
	 mul ah
	 pop bx
	 xor bh,bh
	 add ax,bx
	 xor dx,dx
	 call hex2asc
		ret
printver	endp
;-----------------------------------------------------------------------
; PRINT DATE Prints a date in mm-dd-yyyy format
; Entry: DH - Month 1 - 12
; DL - Day 1 - 31
; CX - Year
; DI - Ptr to output buffer
;-----------------------------------------------------------------------
print_date	proc	near
		push	cx 			;Save year
		push	dx			;Save day of the month
		mov	al,dh			;Copy month
		xor	ah,ah
		xor	dx,dx
		call	lead_zero		;Add leading zero
	 call hex2asc ;Convert month to ASCII
		mov	byte ptr [di-1],'-'	;Change term 0 to -
		pop	ax			;Get day
		xor	ah,ah			
		call	lead_zero		;Add leading 0
	 call hex2asc ;Convert to ASCII
		mov	byte ptr [di-1],'-'	;Change term 0 to -
		pop	ax
		xor	dx,dx
	 call hex2asc ;Convert year to ASCII
		ret
print_date	endp
;-----------------------------------------------------------------------
; PRINT TIME Prints the time in hh:mm:ss AM/PM format
; Entry: CH - Hour
; CL - Minutes
; DH - Seconds
; DI - Ptr to output buffer
;-----------------------------------------------------------------------
print_time	proc	near
		mov	bx,"ma"			;Assume AM
		cmp	ch,12
		jbe	print_time_1
		sub	ch,12
		mov	bl,'p'
print_time_1:
		push	bx			;Save AM/PM flag
		push	dx 			;Save seconds
		push	cx			;Save minutes
		mov	al,ch			;Copy hours
		xor	ah,ah
		xor	dx,dx
		call	lead_zero		;Add leading zero
	 call hex2asc ;Convert month to ASCII
		mov	byte ptr [di-1],':'	;Change term 0 to :
		pop	ax			;Get minutes
		xor	ah,ah
		xor	dx,dx
		call	lead_zero		;Add leading 0
	 call hex2asc ;Convert to ASCII
		mov	byte ptr [di-1],':'	;Change term 0 to :
		pop	ax			;Get seconds
		mov	al,ah
		xor	ah,ah
		xor	dx,dx
		call	lead_zero		;Add leading 0
	 call hex2asc ;Convert seconds
		mov	byte ptr [di-1],' '	;Change term 0 to space
		pop	ax			;Get AM/PM tag
		stosw
		xor	al,al			;Term string
		stosb
		clc
		ret
print_time	endp
;-----------------------------------------------------------------------
; Lead Zero - Adds a leading zero if number less than 10
; Entry: DI - Ptr to buffer
; AL - Number to check
;-----------------------------------------------------------------------
lead_zero	proc	near
		push	bx
		mov	bx,number_base
		cmp	al,bl
		jae	lead_zero_exit
		mov	byte ptr es:[di],'0'
		inc	di
lead_zero_exit:
		pop	bx
		ret
lead_zero	endp
;-----------------------------------------------------------------------------
; GETSTRING Returns a pointer to a string in a list from depending
; on an input index value. 
; Entry: ES:DI - Pointer to start of list
; BX - Index into list
; Exit: DI - Pointer to string
; CF - Set if index too big
;-----------------------------------------------------------------------------
get_string proc near
	 assume cs:code,ds:code
		or	bx,bx			;CF = 0
		je	getstring_exit
		call	find_endl
		dec	bx
		cmp	byte ptr es:[di],0
		jne	get_string
		stc
getstring_exit:
		ret
get_string endp
;-----------------------------------------------------------------------
; PROCESS NUMS Converts each parameter to a number then calls back
; to a routine for specific processing.
; Entry: BX - Ptr to callback procedure
;
; Exit: CF - Set if number too large
;
; Callback: 
; Called with: DX AX - Number from parameter
; SI DI - Running sum/product/logical number
; Return: CF - Set if processing should terminate; 
;-----------------------------------------------------------------------
process_nums	proc	near
		push	bp
	 mov si,var1_value		;Get ptr to variable
	 call asc2hex
		mov	si,ax			;Init vars
		mov	di,dx
		mov	bp,offset var2_value	;Get ptr to var array
		mov	cx,num_params
		dec	cx
		jbe 	procnum_exit
procnum_1:
		push	si
	 mov si,[bp]			;Get ptr to variable
	 call asc2hex
		pop	si
		jc	procnum_exit
		call	bx			;Call callback function
		jc	procnum_exit
		inc	bp			;Point BP to next var
		inc	bp
		loop	procnum_1
		mov	ax,si			;Copy number
		mov	dx,di	 
	 mov di,dest_var_val
	 call hex2asc ;Convert result to ASCII
		clc
procnum_exit:
		pop	bp
	 ret
process_nums endp
;-----------------------------------------------------------------------
; CONV2NUM converts the first two parameters to hex numbers.
; Exit: DX AX - Number from 1st parameter
; CX BX - Number from 2nd parameter
; CF - Set if either number too large
; SI - Set to error message if CF set
;-----------------------------------------------------------------------
conv2num proc near
	 mov si,var2_value ;Convert 2nd param to hex
	 call asc2hex
	 jc conv2num_error
	 mov bx,ax ;Copy second parameter
	 mov cx,dx
	 mov si,var1_value ;Convert 1st param to hex
	 call asc2hex
	 jc conv2num_error
conv2num_exit:
	 ret
conv2num_error:
	 mov si,offset errmsg14 ;Number too large
	 jmp short conv2num_exit
conv2num endp
;-----------------------------------------------------------------------
; PARSE CMDLINE Parse the cmd line into seperate strings for each param
; Exit: CF - Set if error.
; SI - Points to error message if CF set.
;-----------------------------------------------------------------------
parse_cmdline proc near
		xor	cx,cx
		mov	num_params,cx		;Init flags with zeros
		mov	quiet_flag,cl
		mov	use_mastenv,cl
		mov	help_flag,cl
		mov	number_base,10
		mov	console_out,1		
		mov	parse_char,DEF_PARSE_CHAR
		mov	cl,MAX_PARAMS + 2	;Init ptrs to zero string
		mov	di,offset dest_var_name
		mov	ax,offset entry		;Point to zero byte
		rep	stosw
	 mov si,offset command_tail
	 cmp byte ptr [si],0 ;See if cmdline = 0
	 jne parse_cmdline_0 ;If zero, report error
parse_error:
		cmp	help_flag,0
		je	parse_error1
		jmp	parse_cmdline_exit
parse_error1:
	 mov si,offset errmsg1 ;Syntax error message
		mov	al,install_flag
		or	al,remove_flag
		je	parse_error2
		mov	si,100h
parse_error2:
		stc
		jmp	parse_cmdline_exit1
parse_cmdline_0:
		;
		; Added in 2.4 to untranslate graphic equals to =.
		;
	 xor cx,cx
		mov	cl,[si]
		inc	si
		mov	di,si
		mov	al,equalsub_char
parse_cmdline_01:
		repne	scasb
		jne	parse_cmdline_1
		mov	byte ptr [di-1],'='
		or	cx,cx
		jnz	parse_cmdline_01
parse_cmdline_1:
	 xor bx,bx
	 call scan4char ;Find 1st char
	 jc parse_error
	
	 cmp al,'/' ;See if cmdline switch
	 jne parse_cmdline_2
		call	parse_switch		;Parse cmd line switch
		jnc	parse_cmdline_1
		jmp	parse_cmdline_exit1
parse_cmdline_2:
		mov	dest_var_name,si
		mov	cmd_value,si
		mov	bl,3			;Scan for space or =
		call	scan4char
		mov	byte ptr [si],0		;Term 1st string
		jc	parse_cmdline_exit
		cmp	al,'='			;If = found, first word
		je	parse_cmdline_3		; was dest env var
		xor	bl,bl
		call	scan4char
		jc	parse_cmdline_exit
		cmp	al,'='
		jne	parse_cmdline_4
parse_cmdline_3:
		inc	si			;Move past =
		xor	bx,bx	 		;Find next char
		call	scan4char
		jc	parse_error
		mov	cmd_value,si		;Save ptr to cmd
		mov	console_out,0		;No screen output
		mov	bl,1
		call	scan4char		;Find end of command
		mov	byte ptr [si],0		;Term cmd name
		jc	parse_cmdline_exit
		xor	bl,bl
		call	scan4char		;Find 1st param
		jc	parse_cmdline_exit
parse_cmdline_4:
	 mov bp,offset var1_value
		mov	cx,MAX_PARAMS
parse_cmdline_5:
		mov	[bp],si			;Save ptr to param
		add	bp,2
		inc	num_params		
		mov	bl,4			;Scan until parse char
		mov	dl,parse_char
		call	scan4char
		mov	byte ptr [si],0		;Term param
		jc	parse_cmdline_exit
		cmp	[si+1],dl
		jne	parse_cmdline_6
		inc	si
		inc	si
		jmp	short parse_cmdline_7
parse_cmdline_6:
		xor	bl,bl			;Scan until 1st char of
		call	scan4char		; next param
parse_cmdline_7:
		loop	parse_cmdline_5
parse_cmdline_exit:
	 clc
parse_cmdline_exit1:
	 ret
parse_cmdline endp
;-----------------------------------------------------------------------
; PARSE SWITCH Parse command line switches
; Entry: SI - Pointer to / chararacter
; Exit: CF - Set if error
; SI - If error, points to error message string
;-----------------------------------------------------------------------
parse_switch	proc near
		assume	cs:code,ds:code
	 inc si ;Skip past '/'.
	 lodsb ;Get cmdline switch
		mov	di,offset cmd_switches	
		mov	cx,offset cmd_switch_end - offset cmd_switches
		mov	bx,offset cmd_switch_end - offset cmd_switches - 1
		or	al,20h			;Make switch lower case
		repne	scasb
		mov	dx,offset errmsg3	;Command not found msg
		stc
		jne	switch_exit
		sub	bx,cx			;Compute offset into list
		shl	bx,1			;Convert to word offset
		clc				;Assume pass
		call	cs:[bx+offset cmd_jmp_tbl] ;Call command routine.
switch_exit:
		ret
switch_master:
	 mov use_mastenv,1 ;Set use master env flag
		ret
switch_quiet:
	 mov quiet_flag,1 ;Set to suppress output
		ret
switch_pchar:
	 lodsb
	 mov parse_char,al ;Set new parse character
		ret
switch_base:
		call	asc2hex			;Base at 10 right now
		dec	si
		or	dx,dx			;Check for proper range
		jne	switch_base1
		cmp	ax,1
		jbe	switch_base1
		cmp	ax,16
		ja	switch_base1
		mov number_base,ax ;Set new base
		clc
		ret
switch_base1:
		mov	si,offset errmsg24	;Base outside range
		stc
		ret
switch_help:
		mov	help_flag,1		;Set help flag
		ret
switch_install:
		cmp	installed,0
		je	switch_install1
		mov	si,offset errmsg23
		stc
		jmp	short switch_install2
switch_install1:
	 mov install_flag,1 ;Set to install as TSR
		clc
switch_install2:
		ret
switch_remove:
		cmp	installed,0
		jne	switch_remove1
		mov	si,offset errmsg21
		stc
		jmp	short switch_remove2
switch_remove1:
	 mov remove_flag,1 ;Set to remove as TSR
		clc
switch_remove2:
		ret
parse_switch	endp
;-----------------------------------------------------------------------
; GETCOMSPEC returns the name of the command processor from the env
;-----------------------------------------------------------------------
getcomspec proc near
	 push ds
		mov	ax,ds:[2ch]		;Get prog environment segment
	 mov di,offset shell_var	;Point to COMSPEC var name
		call	getenvvar		;Get ptr to env var value
		jc	getcomspec_exit		;CF = 1, var not found.
	 mov di,cs:dest_var_val	;Copy var value to dest string
		push	di
	 call copy_string
		pop	si
		pop	ds
	 mov di,databuff_ptr ;Use 2nd buff as temp buff
	 call parse_filename
	 mov si,di
	 mov bx,di
getcomspec_1:
	 lodsb
	 cmp al,'\' ;Mark start of filename or
	 jne getcomspec_2 ; directory.
	 mov bx,si
getcomspec_2:
	 or al,al
	 je getcomspec_3
	 cmp al,'.'
	 jne getcomspec_1
	 dec si
getcomspec_3:
	 mov cx,si
	 sub cx,bx ;Compute length
		cmp	cx,8
		jb	getcomspec_4
		mov	cx,8
getcomspec_4:
		mov	shell_namlen,cl		;Save length of comspec name
	 mov di,offset shell_name
	 mov si,bx
	 rep movsb
		xor	al,al
		stosb
getcomspec_exit:
		xor	ax,ax
		mov	di,dest_var_val		;ReZero the buffer. This is
		mov	cx,VAR_SIZE		; what caused the FILENAME
		rep	stosb			; and FILEEXT bugs in 1.1
		clc
	 ret
getcomspec endp
;-----------------------------------------------------------------------------
; FILENAME STR Return only the filename from a filename string
; Entry: SI - Partial filename
; DI - Working buffer
; Exit: SI - Points to file name
; CX - Length of filename
;-----------------------------------------------------------------------------
get_filename proc near
	 assume cs:code,ds:code,es:code
	 call parse_filename
	 mov si,di
	 mov bx,di
getfname_1:
	 lodsb
	 cmp al,'\' ;Mark start of filename or
	 jne getfname_2 ; directory.
	 mov bx,si
getfname_2:
	 cmp al,'.'			;Mark end of filename or
	 jne getfname_3 		; dir name.
	 mov	di,si
getfname_3:
	 or al,al
	 jne getfname_1
		cmp	di,bx
		ja 	getfname_4
		mov	di,si
getfname_4:
		dec	di
		mov	byte ptr ds:[di],0	;Terminate 
	 mov cx,di
	 sub cx,bx ;Compute length
	 mov si,bx
	 ret
get_filename endp
;-----------------------------------------------------------------------
; FILENAME OPEN Does std parsing of name and opens file
; Entry: AL - File open flags
;-----------------------------------------------------------------------
filename_open	proc near
	 assume cs:code,ds:code,es:code
		push	ax
	 mov si,var1_value ;Fully qualify filename
	 mov di,dest_var_val ;Use dest buff as temp buff
	 call parse_filename
	 mov dx,di ;Copy filename pointer
		pop	ax
		push	ax
	 call open_file
		jnc	filename_open_exit
	 mov di,dest_var_val ;Use dest buff as temp buff
	 call parse_filename1
	 mov dx,di ;Copy filename pointer
		pop	ax
		push	ax
	 call open_file
filename_open_exit:
		pop	ax
		ret
		ret
filename_open	endp
;-----------------------------------------------------------------------
; PARSE FILENAME creates a proper pathname for a filename
; Entry: SI - Pointer to ASCIIZ filename
; DI - Pointer to buffer to hold resulting pathname
;-----------------------------------------------------------------------
parse_filename proc near
	 assume cs:code,ds:code,es:code
	 push di
	 push si
	 cmp dos_version,300h ;See if DOS 3.x or greater.
	 jb parse_fname_0 ;If not, parse the old way.
	 mov ah,60h ;DOS Resolve Path
	 int 21h
	 jmp short parse_fname_7
parse_filename1:
	 push di			;2nd Entry pt w/o using
	 push si			; int cmd.
parse_fname_0:
	 cmp byte ptr [si+1],":" ;See if disk specified
	 je parse_fname_1 ;Yes, skip disk assignment
	 mov ah,19h ;Get default disk
	 int 21h
	 inc al
	 mov dl,al ;Save default disk number
	 add al,40h ;Make ASCII
	 mov ah,":"
	 jmp short parse_fname_2
parse_fname_1:
	 lodsw ;Get disk specified
	 and al,0DFh ;Convert to caps
	 mov dl,al
	 sub dl,40h ;Convert to hex
parse_fname_2:
	 stosw ;Load disk specification
;Look for directory specification.
	 mov bx,di ;save start of path
	 mov al,"\"
	 cmp byte ptr [si],al ;See if starting from root
	 je parse_fname_3 ;Yes, skip append of path
	 stosb ;Start at root
	 push si ;Save current pointer
	 mov si,di ;Point to dest buffer
	 mov ah,47h ;Get default path
	 int 21h
	 pop si
	 cmp byte ptr [di],0 ;See if NULL path
	 je parse_fname_3
	 call find_end ;Scan to end of path string
	 dec di ;move back before zero
	 mov al,"\" ;Append path string with
	 stosb ; a \. CX = length of path
parse_fname_3:
	 add cx,2 ;Append filename to path.
	 mov ax,VAR_SIZE ;Compute space remaining in
	 sub ax,cx ; the destination buffer.
	 xchg cx,ax
	 xor ah,ah ;Clear last char holder
parse_fname_4:
	 lodsb ;Get filename character. If
	 or al,al ; end of string, exit.
	 jz parse_fname_6 ;Else, write char.
	 stosb
	 cmp ax,".." ;If last two chars are ..,
	 jne parse_fname_5 ; scan backwards to delete
	 std ; last directory.
	 sub di,4 ;First, backup past '\..'
	 mov al,"\" ;Look for directory sep
	 push cx
	 mov cx,di ;Compute length of path
	 sub cx,bx
	 repne scasb ;Now, past last directory
	 pop cx
	 cld ;Scan forwards again
	 inc di ;Move back past \
parse_fname_5:
	 mov ah,al ;Save last character read.
	 loop parse_fname_4
parse_fname_6:
	 xor al,al ;Terminate string with 0
	 stosb
parse_fname_7:
	 pop si
	 pop di
	 ret
parse_filename endp
;-----------------------------------------------------------------------------
; CREATE FILE Creates a new file.
; Entry: DX - Pointer to ASCIIZ filename.
; Exit: BX - File handle
; CF - Set if error
;-----------------------------------------------------------------------------
create_file proc near
	 push cx
	 mov ah,3ch ;Create file
	 xor cx,cx ;Normal attributes
	 int 21h
	 mov bx,ax ;Copy file handle
	 pop cx
	 ret
create_file endp
;-----------------------------------------------------------------------------
; OPEN FILE Opens a file.
; Entry: AL - Access flags
; DX - Pointer to ASCIIZ filename.
; Exit: BX - File handle
; CF - Set if error
;-----------------------------------------------------------------------------
open_file proc near
	 mov ah,3dh ;Open file
	 int 21h
	 mov bx,ax ;Copy file handle
	 ret
open_file endp
;-----------------------------------------------------------------------------
; CLOSE FILE Closes a file.
; Entry: BX - File handle
; Exit: CF - Set if error
;-----------------------------------------------------------------------------
close_file proc near
	 mov ah,3eh ;Close file
	 int 21h
	 ret
close_file endp
;-----------------------------------------------------------------------------
; READ FILE Reads data from a file
; Entry: BX - File handle
; CX - Number of bytes to read
; DX - Pointer to data buffer
; Exit: CF - Set if error
; AX - bytes read.
;-----------------------------------------------------------------------------
read_file proc near
	 mov ah,3fh ;Read file data
	 int 21h
	 ret
read_file endp
;-----------------------------------------------------------------------------
; WRITE FILE Writes data to a file
; Entry: BX - File handle
; CX - Number of bytes to write
; DX - Pointer to data buffer
; Exit: CF - Set if error
;-----------------------------------------------------------------------------
write_file proc near
	 mov ah,40h ;Write file data
	 int 21h
	 ret
write_file endp
;-----------------------------------------------------------------------------
; MOVE FILEPTR Moves the file read pointer of a file.
; Entry: AX,DX - Offset of file pointer
; BX - File handle
; CL - Move type, 0 = from start, 1 = cur pos, 2 = from end.
; Exit: CF - Set if error
;-----------------------------------------------------------------------------
move_fileptr proc near
	 xchg cx,dx ;Copy most sig word
	 xchg dx,ax ;Copy least sig word
	 mov ah,42h ;Move file pointer
	 int 21h
	 ret
move_fileptr endp
;-----------------------------------------------------------------------------
; PARSE DOSERR Points SI to the proper DOS error string
; Entry: AL - DOS error number
; Exit: SI - Pointer to ASCIIZ string
;-----------------------------------------------------------------------------
parse_doserr proc near
		xor	ah,ah
		cmp	al,34
		jbe	parse_doserr_1
		xor	al,al
parse_doserr_1:
		shl	ax,1
		mov	si,offset doserr_tbl
		add	si,ax
		mov	si,[si]
		stc				;Set error flag
	 ret
parse_doserr endp
;-----------------------------------------------------------------------------
; TRUNCNUM truncates a number to the max length of a string
; Entry: AX - Number to truncate
; Exit: AX - Truncated number
;-----------------------------------------------------------------------------
truncnum proc near
	 cmp ax,VAR_SIZE ;VAR_SIZE = max string length
	 jb trunc_1
	 mov ax,VAR_SIZE
trunc_1:
	 ret
truncnum endp
;-----------------------------------------------------------------------------
; FINDSTR determines if a string is in a list.
; Entry: DS:SI - Pointer to ASCII string to find.
; ES:DI - Pointer to list of ASCIIZ strings.
; CX - Size of string
; Exit: DI - Pointer to entry in list
; CF - Clear if string found
; BX - If CF clear, index into list
;-----------------------------------------------------------------------------
findstr proc near
	 push cx
	 push dx
	 xor dx,dx
	 or dx,cx ;Save length of string
	 je finds_3
	 xor bx,bx ;Zero index counter
finds_1:
	 push di
	 push si
	 push cx
	 repe cmpsb ;Compare command
	 pop cx
	 pop si
	 pop di
	 clc
	 je findstr_exit
	 inc bx ;Inc string count
	 push cx
	 call find_endl ;Find end of string.
	 pop cx
	 jne finds_3
	 cmp byte ptr es:[di],0 ;See if second zero. If so
	 jne finds_1 ; end of list.
finds_3:
	 stc ;Indicate string not found
findstr_exit:
	 pop dx
	 pop cx
	 ret
findstr endp
;-----------------------------------------------------------------------------
; FIND END scans to the end of an ASCIIZ string.
; Entry: ES:DI - Pointer to ASCII string
; Exit: ES:DI - Pointers to character after string.
; CX - Length of string
; ZF - Clear if end not found in MAX length of characters
;-----------------------------------------------------------------------------
find_end proc near
	 push ax
	 mov cx,VAR_SIZE
	 xor al,al
	 repne scasb
	 pushf
	 mov ax,VAR_SIZE
	 sub ax,cx
	 xchg ax,cx
	 dec cx
	 popf
	 pop ax
	 ret
find_end endp
;-----------------------------------------------------------------------------
; FIND ENDL scans to the end of an ASCIIZ string. String can be up to 32K
; Entry: ES:DI - Pointer to ASCII string
; Exit: ES:DI - Pointers to character after string.
; CX - Length of string
; ZF - Clear if end not found in MAX length of characters
;-----------------------------------------------------------------------------
find_endl proc near
	 push ax
	 mov cx,8000h
	 xor al,al
	 repne scasb
	 pushf
	 mov ax,8000h
	 sub ax,cx
	 xchg ax,cx
	 dec cx
	 popf
	 pop ax
	 ret
find_endl endp
;-----------------------------------------------------------------------------
; CAPS STRING capitalizes ASCIIZ string
; Entry: SI - Pointer to ASCII string to capitalize
; Exit: CX - Length of string
;-----------------------------------------------------------------------------
caps_string proc near
	 assume ds:code,es:code
	 push bx
	 push dx
	 mov bx,"za" ;Set filter limits
	 mov dx,0df00h ;Set character filter
	 call filter_string
	 pop dx
	 pop bx
	 ret
caps_string endp
;-----------------------------------------------------------------------------
; LC STRING makes an ASCIIZ string lower case
; Entry: SI - Pointer to ASCII
; Exit: CX - Length of string
;-----------------------------------------------------------------------------
lc_string proc near
	 assume ds:code,es:code
	 push bx
	 push dx
	 mov bx,"ZA" ;Set filter limits
	 mov dx,0ff20h ;Set character filter
	 call filter_string
	 pop dx
	 pop bx
	 ret
lc_string endp
;-----------------------------------------------------------------------------
; FILTER STRING filters an ASCIIZ string
; Entry: DS:SI - Pointer to ASCII string
; BL - Lower limit of char range
; BH - Upper limit of char range
; DL - OR filter
; DH - AND filter
; Exit: CX - Length of string
;-----------------------------------------------------------------------------
filter_string proc near
	 assume ds:code,es:code
	 push si
	 push di
	 push es
	 mov di,si
	 push ds
	 pop es
	 xor cx,cx ;Clear byte counter.
filter_1:
	 lodsb ;Get character
	 or al,al ;Allow any non-space character
	 je filter_exit
	 cmp al,bl ;If between lower and upper
	 jb filter_2 ; char limit it.
	 cmp al,bh
	 ja filter_2
	 or al,dl ;Apply OR filter
	 and al,dh ;Apply AND filter
filter_2:
	 stosb ;Save character
	 inc cx ;Inc byte counter
	 jmp short filter_1
filter_exit:
	 pop es
	 pop di
	 pop si
	 ret
filter_string endp
;-----------------------------------------------------------------------------
; COPY STRING copies an ASCIIZ string
; Entry: DS:SI - Pointer to source ASCIIZ string
; ES:DI - Pointer to destination buffer
; Exit: CX - Length of string
;-----------------------------------------------------------------------------
copy_string proc near
	 assume ds:code,es:code
	 xor cx,cx
copy_string_1:
	 lodsb ;Move character
	 stosb
	 or al,al ;See if end of string
	 je copy_string_exit ;If so, exit
	 inc cx ;Inc count
	 jmp short copy_string_1
copy_string_exit:
	 ret
copy_string endp
;-----------------------------------------------------------------------------
; PRINT STRCR prints an ASCIIZ string then appends a CR LF to the end
; Entry: SI - pointer to ASCIIZ string.
;-----------------------------------------------------------------------------
print_strcr proc near
	 assume ds:nothing,es:nothing
	 call print_str
	 mov si,offset endmsg
	 call print_str
	 ret
print_strcr endp
;-----------------------------------------------------------------------------
; PRINT STR prints an ASCIIZ string to the std output device
; Entry: SI - Pointer to ASCIIZ string.
;-----------------------------------------------------------------------------
print_str proc near
		cmp	cs:quiet_flag,0
		jne	print_str_exit
	 lodsb ;Get character
	 or al,al ;See if end of string
	 je print_str_exit
	 mov ah,2 ;DOS print character
	 mov dl,al
	 int 21h ;Call DOS
	 jmp short print_str
print_str_exit:
	 ret
print_str endp
;-----------------------------------------------------------------------------
; HEX2ASC converts number in DX AX to ASCII
; Entry: DX AX - Number
; DI - Destination buffer
; CF - Clear
; Exit: Di - Points to byte past terminating 0
;-----------------------------------------------------------------------------
hex2asc proc near
	 assume ds:nothing,es:nothing
	 push ax
	 push bx
	 push cx
	 push dx
	 push si
	 mov si,cs:number_base ;Load number base
	 mov bx,ax
	 mov cx,dx
	 mov dx,-1 ;Load end of number flag
	 push dx
hex_loop1:
	 xchg ax,cx ;Get high word in AX
	 xor dx,dx ;Clear high word
	 div si ;Divide by base (10)
	 xchg cx,ax ;Save result of high divide
	 xchg ax,bx ;Get low word, leave remainder
	 div si ; in DX.
	 xchg bx,ax ;Save result of low divide
	 add dl,30h ;Convert to ascii
		cmp	dl,'9'
		jbe	hex_1
		add	dl,7
hex_1:
	 push dx ;Save digit on stack
	 or bx,bx
	 jne hex_loop1 ;See if number = 0. If not,
	 or cx,cx ; continue divide loop.
	 jne hex_loop1
	 mov bl,"0" ;Set leading zero flag
hex_loop2:
	 pop dx ;Get digit off stack
	 cmp dx,-1 ;See if end flag
	 je hex_2
	 or bl,dl ;Don't print leading zeros.
	 cmp bl,"0" ;The first non zero will
	 je hex_loop2 ; change bl to non-zero.
	 mov al,dl ;Write to buffer
	 stosb
	 jmp short hex_loop2
hex_2:
	 cmp bl,"0" ;If number zero, write last
	 jne hex_exit ; zero.
	 mov al,bl
	 stosb
hex_exit:
	 xor al,al ;Termainate with zero
	 stosb
	 pop si
	 pop dx
	 pop cx
	 pop bx
	 pop ax
	 ret
hex2asc endp
;------------------------------------------------------------------------
; ASC2HEX converts an ASCII number to hex
; Entry: SI - Pointer to ASCIIZ string
; Exit: DX,AX - Number
; CF - Set if overflow
;------------------------------------------------------------------------
asc2hex proc near
	 assume ds:nothing,es:nothing
	 push bx
	 push cx
		push	di
	 xor cx,cx ;Zero result
	 xor di,di
	 xor bx,bx			;Keep BH clear
asc_loop1:
	 mov bl,[si] ;Get next digit
	 inc si
		cmp	bl,"9"
		jbe	asc_1
		and	bl,0dfh			;Make upper case
		sub	bl,7
asc_1:
	 sub bl,"0" ;Convert digit from ASCII to
	 jb asc_exit ; hex. If digit illegal
		cmp	bx,cs:number_base	; char, exit.
	 jae asc_exit
asc_2:
	 xchg ax,di ;Shift result in DI CX by
	 mul cs:number_base ; the base.
	 jc asc_exit1
	 xchg di,ax
	 xchg ax,cx
	 mul cs:number_base 
	 xchg cx,ax
		add	di,dx
		jc	asc_exit1
	 add cx,bx ;Add new number to result.
	 adc di,0
	 jnc short asc_loop1
asc_exit1:
	 mov ax,cx ;Copy result
	 mov dx,di
		pop	di
	 pop cx
	 pop bx
	 ret
asc_exit:
	 clc
	 jmp short asc_exit1
asc2hex endp
;-----------------------------------------------------------------------
; SCAN4CHAR scans a string to find the first character.
; Entry: SI - pointer to ASCII string
; BL - 0 = find next char,
; 1 = find next space,
; 2 = find end of line,
; 3 = find next space or =.
; 4 = find character in DL.
; Exit: AL - matching character
; SI - pointer to matching character
; CF - set if carriage return or EOF found
;-----------------------------------------------------------------------
scan4char proc near
	 assume ds:nothing,es:nothing
scan4loop:
	 lodsb
	 cmp al,13 ;Check for CR
	 jne scan4_1
scan4_eol:
	 stc
	 jmp short scan4_exit1
scan4_1:
	 cmp bl,4
	 je scan4_dl
	 cmp bl,3
	 je scan4_equal
	 cmp bl,1 ;Check if searching for 
	 je scan4_space ; space, char, or EOL.
	 ja scan4loop
	 cmp al," " ;Check for space or other
	 jbe scan4loop ; 'white' characters.
	 jmp short scan4_exit
scan4_dl:
	 cmp al,dl ;Check for parse character
	 je scan4_exit
		or	al,al
		je	scan4_eol
	 jmp short scan4loop
scan4_equal:
	 cmp al,"=" ;Check for exit
	 je scan4_exit
scan4_space:
	 cmp al," " ;Check for characters.
	 ja scan4loop
scan4_exit:
	 clc
scan4_exit1:
	 dec si ;Back up before char
	 ret
scan4char endp
;-----------------------------------------------------------------------
; GETKEY Waits for a keypress and returns the key.
;-----------------------------------------------------------------------
getkey		proc	near
		call	prog_idle		;yield
		mov	ah,1			;Check key status
		int	16h
		jz	getkey			;No key, loop
		cld				;Reset dir flag due to yield
		mov	ah,2			;Get shift status
		int	16h
		push	ax
		mov	ax,12ffh		;Get extended shift status
		int	16h
		pop	bx
		cmp	bl,al			;See if same
		jne	getkey_1		;No, assume old keyboard
 mov ah,11h ;Get extended key status
 xor al,al ;Clear zero flag
 int 16h
 jz getkey_1
		mov	ax,1000h		;Extended keyboard read
		int 16h
		jmp short getkey_2
getkey_1:
		xor	ax,ax			;Get key from buffer
		int	16h
getkey_2:
		ret
getkey		endp
;-----------------------------------------------------------------------
;PROG IDLE Indicates to the system that we are idle
;-----------------------------------------------------------------------
prog_idle	proc	near
		push	ax
		int	28h			;Call Idle interrupt
		cmp	dos_version,300h
		jb	prog_idleexit
		mov	ax,1680h		;Mux DOS idle
		int	2fh
prog_idleexit:
		pop	ax
		ret
prog_idle	endp
;-----------------------------------------------------------------------
; GETENVVAR returns a pointer to the value of an environment variable. 
; Entry: AX - Segment of environment
; DS:DI - Pointer to ASCIIZ string containing the name of env var. 
; Exit: DS:SI - If CF = 0, Pointer to value of environment variable 
; DI - If CF = 0, Pointer to start of var name in environment
; CF - Set if variable not found
;-----------------------------------------------------------------------
getenvvar	proc near
		push	ax
		push	es
		push	ax			;Save env segment
	 push di			;Append = sign to the end of
	 call find_end		; the variable.
	 mov word ptr es:[di-1],003dh 
	 pop si
	 call caps_string
		pop	es			;Use find string routine to
	 xor di,di ; find the variable name.
	 call findstr
		jnc getenvvar_1
	 stc
	 jmp short getenvvar_exit
getenvvar_1:
	 push es			;DS:SI = ES:DI
	 pop ds 
	 xchg si,di
getenvvar_2:
	 lodsb ;Find end of var name.
	 cmp al,'='
	 jne getenvvar_2
	 clc
getenvvar_exit:
		pop	es
		pop	ax
	 ret
getenvvar	endp
;-----------------------------------------------------------------------------
; SETENV Sets environment variables.
;
; Entry: DS:SI - pointer to env var name
; ES:DI - pointer to env var value
;-----------------------------------------------------------------------------
setenv proc near
	 push bp
	 push ds
	 push es
	 push di			;Save ptr to var value
	 push es
	 call find_end ;Get length of var value
	 mov dx,cx ;Copy length
	 mov di,si
	 push ds ;Add length of var name plus
	 pop es ; room for the equals sign.
	 call find_end
	 mov word ptr es:[di-1],003dh ;Append = sign
	 inc cx ;Add byte for =
	 add dx,cx
	 inc dx ;Add byte for terminating 0
	 mov ax,masterenv_seg
		cmp	use_mastenv,0
		jne	setenv_0
		mov	ax,localenv_seg
setenv_0:
		push ax
	 dec ax
	 mov es,ax
	 mov bp,es:[3] ;Get size of env segment
	 push cx
	 mov cl,4 ;Convert paragraphs to bytes
	 shl bp,cl
	 pop cx
	 pop es
	 xor di,di ;Use find string routine to
	 call findstr ; find the variable name.
	 push si
	 push ds
	 jc setenv_2 ;Var not found, skip erase
	 push es
	 pop ds
	 mov si,di ;Erase current var value by
	 call find_endl ; copying the next env var
	 xchg si,di ; over the current one.
setenv_1:
	 cmp byte ptr [si],0
	 je setenv_2
setenv_11:
	 lodsb
	 stosb
	 or al,al
	 jne setenv_11
	 jmp short setenv_1
setenv_2:
	 pop ax ;Get ptr to var name
	 pop cx
	 pop ds ;Get ptr to var value
	 pop si
		cmp	byte ptr ds:[si],0	;See if NULL variable, If so,
		je	setenv_31		; don't add to env block
	 mov bx,di ;Get offset of end of env
	 add bx,dx ;Add length of value
	 inc bx ;Add length of terminating byte
	 cmp bp,bx
	 ja setenv_3
	 push ax			;Save ptr to var name
		push	cx
		mov	dx,es
		dec	dx
		mov	es,dx		
		push	es:[1]			;Save current owner segment
		inc	dx
		mov	es,dx
	 add bx,15 ;If no room in environemnt,
	 mov cl,4 ; see if env segment can be
	 shr bx,cl ; resized to make room for
	 mov ah,4ah ; new variable.
	 int 21h ;Reallocate memory
		pop	bx			;Get old owner
		pop	cx
	 pop ax
	 jc setenv_5
		push	es
		dec	dx
		mov	es,dx
		mov	es:[1],bx		;Restore old owner 
		pop	es
setenv_3:
	 push si
	 push ds
	 mov si,cx
	 mov ds,ax
	 call copy_string ;Copy var name to env
	 dec di ;Back up over last zero
	 pop ds
	 pop si
	 mov bl,"=" ;Since env vars can't have
	 mov bh,bl ; the char '=', filter
	 mov dl,-1 ; string to change = to
	 mov dh,equalsub_char ; graphic char that looks
	 call filter_string ; similar.
	 call copy_string ;Copy var value to env
setenv_31:
	 xor al,al
	 stosb ;Add 2 zeros to end env.
setenv_4:
	 clc ;Set pass flag
setenv_5:
	 pop es
	 pop ds
	 pop bp
	 ret
setenv endp
;-----------------------------------------------------------------------
; SETERR0MSG Sets the Bad version error message to the ver in AX
; Entry: AX - Required DOS version
; Exit: SI - Pointer to bad DOS ver message
; CF - Set 
;-----------------------------------------------------------------------
seterr0msg	proc	near
		mov	di,offset errmsg0ver	;Print version number
		call	printver
		mov	ax,[di-3]		;Insert decimal pt
		mov	byte ptr [di-3],'.'	; so it looks nice
		mov	ah,' '
		mov	[di-2],ax
		mov	si,offset errmsg0	;Point to start of msg
		stc				;Set error flag
		ret
seterr0msg	endp
;-----------------------------------------------------------------------------
; REMOVE uninstalls the installed program from memory.
;-----------------------------------------------------------------------------
remove		proc	near
		assume	cs:code,ds:code,es:code
		push	es
		mov	ax,352fh ;Get MUX vector
		int	21h
		mov	cx,cs			;Get CS
		mov	ax,es			;Check to make sure MUX
		cmp	ax,cx			; vector not modified.
		jne	remove_error
		push	ds
		mov	ax,252fh		;Set interrupt
		lds	dx,[int2fh]		;Get old int 2F vector
		int	21h
		pop	ds
		mov	es,ds:[2ch]		;Get env segment
		mov	ah,49h			;Free mem
		int	21h
		mov	si,offset infomsg2	;Removed message
		call	print_strcr
remove_exit:
		pop	es
		ret
remove_error:
		mov	remove_flag,0
 	mov	si,offset errmsg22	;Can't remove error msg
		stc
		jmp	short remove_exit
remove		endp
		even
end_of_resident	=	$
;=======================================================================
;Start of nonresident data
;=======================================================================
;-----------------------------------------------------------------------
; FINAL INSTALL Last part of install process. Must be less that
; the resident stack size. (512 bytes)
;-----------------------------------------------------------------------
final_install	proc	near
		mov	sp,di			;Set stack to res stack
		rep	stosb			;Clear buffer
		mov	databuff_ptr,di
	 add di,DATABUFF_SIZE+15
	 mov cl,4
	 shr di,cl
		mov	dx,di
	 mov ax,3100h		;TSR
	 int 21h
		mov	ax,4c01h		;This should never happen
		int	21h
final_install	endp
;-----------------------------------------------------------------------
; Lookup table for help messages. Each command has two help strings. 
; 1. The descripion of the function. The word 'Returns' is 
; automatically added to the start of the string.
; 2. A syntax message that describes the arguements for the function.
;
; This table MUST be in the same order as the command table at the
; start of the program.
;-----------------------------------------------------------------------
help_tbl dw offset left_help
 dw offset left_syntax
	 dw offset right_help
	 dw offset left_syntax
	 dw offset mid_help
	 dw offset mid_syntax
	 dw offset length_help
	 dw offset length_syntax
		dw offset find_help	
		dw offset find_syntax	
		dw offset findc_help	
		dw offset findc_syntax	
		dw offset lower_help 	
		dw offset length_syntax	
		dw offset upper_help 	
		dw offset length_syntax	
		dw offset char_help 	
		dw offset char_syntax 	
		dw offset val_help 	
		dw offset add_syntax 	
		dw offset filedrive_help	
		dw offset filename_syntax
		dw offset filedir_help	
		dw offset filename_syntax	
		dw offset filename_help	
		dw offset filename_syntax	
		dw offset fileext_help	
		dw offset filename_syntax	
	 dw offset parse_help
	 dw offset parse_syntax
	 dw offset commas_help
	 dw offset not_syntax
	 dw offset repeat_help
	 dw offset repeat_syntax
		dw offset read_help 	
		dw offset read_syntax 	
		dw offset write_help 	
		dw offset write_syntax	
		dw offset filesize_help	
		dw offset filename_syntax	
		dw offset linesize_help	
		dw offset filename_syntax	
		dw offset truename_help	
		dw offset filename_syntax	
	 dw offset filedate_help
	 dw offset filename_syntax
	 dw offset filetime_help 
	 dw offset filename_syntax
		dw offset ver_help	
		dw offset no_syntax	
		dw offset ask_help	
		dw offset ask_syntax	
		dw offset inwin_help	
		dw offset no_syntax	
		dw offset twoFcheck_help	
		dw offset twoFcheck_syntax
		dw offset envfree_help	
		dw offset no_syntax	
		dw offset envsize_help	
		dw offset no_syntax	
		dw offset mastervar_help	
		dw offset mastervar_syntax
		dw offset localvar_help	
		dw offset mastervar_syntax	
	 dw offset truever_help
	 dw offset no_syntax
	 dw offset files_help 
	 dw offset no_syntax 
	 dw offset lastdrive_help
	 dw offset no_syntax
	 dw offset codepage_help 
	 dw offset no_syntax 
	 dw offset country_help 
	 dw offset no_syntax 
 	dw offset biosdate_help 
 	dw offset no_syntax 
 	dw offset getkey_help 
 	dw offset no_syntax 
 	dw offset locenv_help 
 	dw offset no_syntax
 	dw offset masenv_help
 	dw offset no_syntax
		dw offset add_help	
		dw offset add_syntax	
		dw offset sub_help	
		dw offset add_syntax	
		dw offset mul_help	
		dw offset add_syntax	
		dw offset div_help	
		dw offset div_syntax			
	 dw offset and_help
	 dw offset add_syntax
	 dw offset or_help
	 dw offset add_syntax
	 dw offset xor_help
	 dw offset div_syntax
	 dw offset not_help
	 dw offset not_syntax
	 dw offset convert_help
	 dw offset convert_syntax
	 dw offset peek_help
	 dw offset peek_syntax
	 dw offset poke_help
	 dw offset poke_syntax
	 dw offset in_help
	 dw offset in_syntax
	 dw offset out_help
	 dw offset out_syntax
	 dw offset interrupt_help
	 dw offset interrupt_syntax
	 dw offset scan_help
	 dw offset scan_syntax
	 dw offset day_help
	 dw offset day_syntax
	 dw offset month_help
	 dw offset month_syntax
	 dw offset date_help
	 dw offset date_syntax
	 dw offset time_help
	 dw offset no_syntax
	 dw offset totalmem_help
	 dw offset no_syntax
	 dw offset freemem_help
	 dw offset no_syntax
	 dw offset totalxms_help
	 dw offset no_syntax
	 dw offset freexms_help
	 dw offset no_syntax
	 dw offset xmsver_help
	 dw offset no_syntax
	 dw offset totalems_help
	 dw offset no_syntax
	 dw offset freeems_help
	 dw offset no_syntax
	 dw offset emsver_help 
	 dw offset no_syntax 
	 dw offset freeumb_help
	 dw offset no_syntax
	 dw offset strver_help
	 dw offset no_syntax
	 dw offset strinst_help
	 dw offset no_syntax
	 dw offset help_help
	 dw offset help_syntax
;-----------------------------------------------------------------------
;Help text
;-----------------------------------------------------------------------
left_help	db "left n characters",0
left_syntax	db "String, Number of chars",0
right_help	db "right n characters",0
mid_help	db "middle n chars",0
mid_syntax	db "String, Start char, Length",0
length_help	db "String length",0
length_syntax	db "String",0
find_help	db "the position of Findstring in String",0
find_syntax	db "String, Findstring",0
findc_help	db "the position of Findstring in String. Case sen",0
findc_syntax	db "String, Findstring",0
lower_help 	db "string all lowercase",0
upper_help 	db "string all uppercase",0
char_help 	db "ASCII number of character",0
char_syntax 	db "Char[Char][char][char][char][char][char][char]",0
val_help 	db "ASCII char for a number",0
filedrive_help	db "the drive of a filename",0
filedir_help	db "the directory of a filename",0
filename_help	db "the file name",0
filename_syntax	db "Filename",0
fileext_help	db "the file extension",0
parse_help	db	"the Nth token from a string",0
parse_syntax	db	"String, Token number, Token seperator char",0
commas_help	db	"a number parsed with commas every 3 digits",0
repeat_help	db	"a string of n number of characters",0
repeat_syntax	db	"Number of chars, Char to repeat",0
read_help 	db "a line from a file",0
read_syntax 	db "Filename, line number",0
write_help 	db "nothing. Appends a string to the end of a file",0
write_syntax	db "Filename, String",0
filesize_help	db "the size of a file",0
linesize_help	db "the number of lines",0
truename_help	db "the complete filename",0
filedate_help	db	"the date of a file",0
filetime_help 	db	"the time of a file",0
ask_help	db "a response from a user",0
ask_syntax	db "[Prompt string][, Max chars][, 1=* echo 2=No echo]",0
ver_help	db "the DOS version number",0
inwin_help	db "1 if Windows running",0
twoFcheck_help	db "status of programs hooked to the Multiplex int",0
envfree_help	db "the bytes free in the environment",0
envsize_help	db "the size of the environment",0
mastervar_help	db "a variable from the Master env ",0
mastervar_syntax db	"Variable Name",0
localvar_help	db "a variable from the Local env ",0
truever_help	db	"the true DOS verison. Requires DOS 5.0 or later",0
files_help 	db	"the total number of files that can be open",0
lastdrive_help	db	"the letter of the last possible drive",0
ask2_help	db	"a response from the user",0
ask2_syntax	db	"[Prompt [,max number of chars [,no echo flag]]",0
codepage_help	db	"the active code page. Requires DOS 3.3 or later",0
country_help 	db	"the country code for the system",0
biosdate_help 	db	"the date for the ROM BIOS",0
getkey_help 	db	"the scan code and ASCII value of the next"
		db	" key pressed",0
locenv_help 	db	"the segment of the active environment",0
masenv_help 	db	"the segment of the master environment",0
add_help	db "the sum of the parameters",0
add_syntax	db "Num1, Num2 [,Num3][,Num4][,Num5][,Num6]"
		db	13,10,9,9,9,9," [,Num7][,Num8][,Num9][,Num10]",0
sub_help	db "the difference of two numbers",0
mul_help	db "the product of the parameters",0
div_help	db "the quotient of two numbers",0
div_syntax	db "Number, Number",0
and_help	db "the logical AND of the parameters",0
or_help		db "the logical OR of the parameters",0
xor_help	db "the exclusive OR of two numbers",0
not_help	db "the logical NOT of a number",0
not_syntax	db "Number",0
convert_help	db	"a number with the base specified",0
convert_syntax	db	"Number, New Base",0
peek_help	db	"a series of bytes from memory",0
peek_syntax	db	"Segment, Offset [, Number of bytes [,Word flag]]",0
poke_help	db	"nothing. Writes up to 8 bytes to memory",0
poke_syntax	db	"Segment, Offset , Byte1[,Byte2][,Byte3][,Byte4]"
		db	"[,Byte5][,Byte6][,Byte7][,Byte8]",0
in_help		db	"a byte from an I/O port",0
in_syntax	db	"Port number",0
out_help	db	"nothing. Writes a byte to an I/O port",0
out_syntax	db	"Port number, Output byte",0
interrupt_help	db	"registers from an interrupt. Dangerous!",0
interrupt_syntax db	"Interrupt number, AX, BX, CX, DX,",13,10
		db	9,9,9,9,9," DI, SI, BP, DS, ES",0
scan_help	db	"the offset of a series of bytes in memory",0
scan_syntax	db	"Segment to search, Starting Offset,",13,10
		db	9,9,9,9," Byte1 [,Byte2] [,Byte3] [Byte4]",13,10
		db	9,9,9,9," [,Byte5] [,Byte6] [,Byte7] [,Byte8]",0
day_help	db	"the name of the current day of the week,",13,10
		db	" or corresponding to the index value",0
day_syntax	db	"[Index (1=Sunday, 2=Monday 3=Tuesday...]",0
month_help	db	"the name of the current month or the month",13,10
		db	"corresponding to the index value",0
month_syntax	db	"[Index (1=January, 2=February...]",0
date_help	db	"the current date in Mmmmmmmm dd, yyyy format",0
date_syntax	db	"[If parm added, date ret in mm-dd-yyyy fmt]",0
time_help	db	"the current time",0
totalmem_help	db	"the amount of conventional memory",0
freemem_help	db	"the largest block of free conventional memory",0
totalxms_help	db	"the amount of extended memory",0
freexms_help	db	"the amount of free extended memory",0
xmsver_help	db	"the version of the extended memory driver",0
totalems_help	db	"the amount of expanded memory",0
freeems_help	db	"the amount of free expanded memory",0
emsver_help 	db	"the version of the expanded memory driver",0
freeumb_help	db	"the largest block of free Upper Memory",0
strver_help	db	"the version of Strings",0
strinst_help	db	"a non-zero number if Strings installed as TSR",0
help_help	db	"help text for the specified Strings command",0
help_syntax	db	"[Strings Command]"
no_syntax	db	0
twoFcheck_syntax db	"Number or Alias",13,10,10
	 db " Interrupt 2F, the multiplex interrupt,"
	 db " is used by many programs to",13,10
	 db "signal that they are installed. 2FCHECK"
		db	" calls interrupt 2F with a device",13,10
	 	db "number between 0 and 255. 2FCHECK returns a"
		db	" 0 if no program responds to",13,10
		db	"this device ID. If a program does respond,"
	 db " a non-zero number is returned.",13,10
		db	" To prevent users from remembering a series of"
		db	" device IDs, one of the",13,10
		db	"following aliases can be used in place of the"
		db	" device number.",13,10,10
	 	db " PRINT - PRINT resident code "
	 db " ASSIGN - ASSIGN resident code",13,10
		db	" DRIVER - DRIVER.SYS device driver "
	 db " SHARE - SHARE resident code",13,10
	 db " NET - Network redirector code "
	 db " NLS - NLSFUNC resident code",13,10
	 db " ANSI - ANSI.SYS device driver "
		db	" DOSBOX - OS/2 or Win DOS box",13,10
	 db " HIMEM - HIMEM.SYS memory manager "
	 db " DOSKEY - DOSKEY resident code",13,10
	 db " GRAPHTBL - GRAFTABL resident code "
	 db " APPEND - APPEND resident code",13,10
	 db " DISPLAY - DISPLAY.SYS",0
infomsg1	db	"Installed",0
errmsg25	db	"No multiplex IDs available",0		
;-----------------------------------------------------------------------
;INITIALIZE - Start of non resident code.
;-----------------------------------------------------------------------
initialize proc near
	 assume cs:code,ds:code,es:code
	 cld ;Set string ops 'up.'
		mov	word ptr [entry],0
	 mov ah,30h ;Get DOS version, run only
	 int 21h ; if 2.0 or greater.
	 xchg al,ah ;Swap major, minor numbers
	 mov dos_version,ax
	 cmp ah,2
	 jae init_1
		mov	si,offset program
		call	print_strcr
	 mov si,offset errmsg0 ;Bad DOS version
		mov	al,1
jmpinit_error:
	 jmp init_error
init_1:
		call	check4xms		;Chk extended mem mgr
		mov	xms_version,ax
		call	check4ems		;Chk expanded mem mgr
		mov	ems_version,ax
		call	chk_installed
		jc	jmpinit_error
		mov	bx,cs
		cmp	ax,bx
		je	init_11
		mov	installed,1
init_11:
	 mov di,TRANS_STACK		;Set up data buffers
	 mov sp,di ;Set stack
		mov	dest_var_val,di
		xor	ax,ax
		mov 	cx,VAR_SIZE
		rep	stosb
		mov	databuff_ptr,di
	 add di,DATABUFF_SIZE+15
	 mov cl,4
	 shr di,cl
		mov	bx,di
	 mov ah,4ah ;Reduce memory allocation
	 int 21h
		jnc	init_21
		mov	si,offset errmsg2
		mov	al,-1			;Set out of memory code
		jmp	short init_exit
init_21:
		push	es
		mov	ax,352eh		;Get cmd proc hook. If
		int	21h			; our normal search
		mov	ax,es:[2ch]		; fails, use this method.
		mov	bx,ax
		dec	ax
		mov	es,ax
		cmp	byte ptr es:[0],'M'
		je	init_30
		cmp	byte ptr es:[0],'Z'
		je	init_30
		mov	bx,ds:[2ch]		;If 2E fails use our env.
init_30:
		pop	es
		mov	localenv_seg,bx
		mov	masterenv_seg,bx
		call	getcomspec		;Get the name of the shell 
		call	getname			;Get the name of the prog
	 call findenv ;Use parent's env by def
	 jc	init_4
		mov	localenv_seg,ax
init_4:
	 call findmaster ;Find master env
	 jc init_5
	 mov masterenv_seg,ax
init_5:
	 call main			;Program, do your stuff
		cmp	install_flag,0		;See if we should install
		je	init_exit
		call	install			;If we return, error
init_error:		
		push	ax
		call	print_strcr		;Print error
		pop	ax
init_exit:
	 mov ah,4Ch ;Terminate
	 int 21h
initialize	endp
;-----------------------------------------------------------------------
; INSTALL Installs Strings as a TSR
;-----------------------------------------------------------------------
install		proc near
		assume	cs:code,ds:code,es:code
		mov	ax,31eh
		cmp	ax,dos_version		;See if DOS 3.3 
		jbe	install_1
		call	seterr0msg		;Error, not DOS 3.3
		ret
install_1:
		mov	ax,352fh		;Get interrupt 2F (MUX)
		int	21h ; vector.
		mov	word ptr [int2fh],bx
		mov	word ptr [int2fh+2],es
		push	cs
		pop	es
		mov	ax,252fh ;Point int 2F to internal
		mov	dx,offset muxint ; routine.
		int	21h
		mov	si,offset infomsg1
		call	print_strcr
		mov	installed,1		;Set installed flag
	 mov di,RES_STACK		;Set up data buffers
		mov	dest_var_val,di
		xor	ax,ax
		mov 	cx,VAR_SIZE
		jmp	final_install
install		endp
;-----------------------------------------------------------------------
;See if a copy is already resident in memory. If > DOS 3.0, use int 2Fh.
;-----------------------------------------------------------------------
chk_installed	proc	near
		push	es
		mov	byte ptr [entry+2],0	;Initialize fingerprint
		cmp	dos_version,300h	;See if DOS 3.0 or later
		jb	find_copy1		;No, search the old way.
		mov	cx,16			;Try 16 different IDs.
find_copy:
		xor	ax,ax
		mov	es,ax
		mov	ah,multiplex_id		;Load ID. Use Int 2Fh to
		int	2fh			; reach installed code so
		or	al,al			; that we are compatible
		jne	find_copy0		; with 386 memory managers.
		push	cs
		pop	es ;If AL not changed, Strings 
		jmp	short find_copy3	; not installed.
find_copy0:
		push	cx
		call	cmpheader		;See if really Strings by
		pop	cx ; comparing file headers.
		je	find_copy3
		inc	multiplex_id		;ID used by another program.
		loop	find_copy		; Change and try again.
		mov	dx,offset errmsg25	;All IDs taken, print error
find_copy_fail:
		stc
		jmp	short find_copy_exit
;
;For DOS 2.x find the installed code the old fashioned way by scanning
;the memory control blocks.
;
find_copy1:
		xor	bx,bx			;zero BX for start
		mov	ax,cs			;keep CS value in AX
find_copy2:
		inc	bx			;increment search segment value
		mov	es,bx
		assume	es:nothing
		cmp	ax,bx			;not installed if current
		je	find_copy3 		; segment is found.
		call	cmpheader
		jne	find_copy2		;loop back if not found
find_copy3:
		mov	ax,es			;Save seg of installed code
		clc
find_copy_exit:
		pop	es
		ret
chk_installed	endp		
;-----------------------------------------------------------------------------
; CMPHEADER compares the first 16 bytes of this file with the segment
; pointed to by ES.
; Entry: DS - code segment
; ES - pointer to segment to compare
; Exit: ZF - 0 = segments match.
;-----------------------------------------------------------------------------
cmpheader	proc	near
		mov	si,offset entry+2	;Search this segment for ASCII
		mov	di,si			; fingerprint.
		mov	cx,16
		repe	cmpsb
		ret
cmpheader	endp
;-----------------------------------------------------------------------
; GETNAME Copies the name of the program to the name buffer
;-----------------------------------------------------------------------
getname		proc near
	 assume cs:code,ds:code,es:code
		push	es
		mov	ax,300h
		cmp	dos_version,300h	;See if DOS 3.0 or later
		jb	getname_exit
		mov	ax,ds:[2ch]		;Get env segment
		push	ax
		dec	ax
		mov	es,ax
		assume	es:nothing
		mov	cx,es:[3]		;Get env seg size
		shl	cx,1
		shl	cx,1
		shl	cx,1
		shl	cx,1
		pop	es			;Get back env seg
		xor	ax,ax
		mov	di,ax
getname_1:
		repnz	scasb			;Scan env for 00
		jne	getname_exit
		cmp	byte ptr es:[di],0
		jne	getname_1
		add	di,3			;Move past cnt byte
		mov	si,databuff_ptr		;Use file buffer
		xchg	di,si
		assume	ds:nothing
		push	ds			;Copy the filename
		push	es			; into a local
		pop	ds			; buffer to avoid
		pop	es			; segment probs
		call	copy_string		; with called procs.
		mov	ax,cs
		mov	ds,ax
		mov	es,ax
		assume	ds:code,es:code
		mov	si,databuff_ptr
		mov	di,si
		add	di,100h
		call	get_filename
		mov	strings_namelen,cx	;Save length
		mov	di,offset strings_name
		call	copy_string
getname_exit:
		pop	es
	 ret
getname		endp
;-----------------------------------------------------------------------
; CHKARENA Verifys a memory arena header.
; Entry: AX - Memory block to verify
; Exit: BX - Owner of memory block
; CX - Size of memory block
; CF - Clear if valid arena header
; ZF - Clear if last memory block
;-----------------------------------------------------------------------
chkarena	proc near
	 assume cs:code,ds:code,es:code,ss:code
		push	ax
	 push es
		dec	ax
		mov	es,ax
		mov	cx,es:[3]		;Get size of block
		mov	bx,es:[1]		;Get owner of block
		cmp	bx,0fff0h		;Check for illegal owner,
		jae	chkarena_error		; stupid 386Max fix.
		cmp	byte ptr es:[0],"M"	;See if proper signature
		je	chkarena_exit		;ZF set, CF clear if match
		cmp	byte ptr es:[0],"Z"	;See if last blk sig
		jne	chkarena_error
		or	ax,ax			;Clear ZF, Clear CF
chkarena_exit:
		pop	es
		pop	ax
		ret
chkarena_error:
		stc
		jmp	short chkarena_exit
chkarena	endp
;-----------------------------------------------------------------------
; FINDCMDPSP Finds the PSP of the command processor
; Exit: AX - Segment of command processor PSP 
;-----------------------------------------------------------------------
findcmdpsp	proc near
	 assume cs:code,ds:code,es:code,ss:code
		push	dx
	 push es
findcmdpsp_1:
	 mov ax,es:[16h] ;Get parent's PSP
		call	chkarena		;See if valid memory block
		jc	findcmdpsp_error
		mov	es,ax
		cmp	ax,es:[16h]		;See if PSP is own parent
		jne	findcmdpsp_1		;No, keep looking
	 clc
findcmdpsp_exit:
	 pop es
		pop	dx
	 ret
findcmdpsp_error:
		stc
		jmp	short findcmdpsp_exit
findcmdpsp	endp
;-----------------------------------------------------------------------
; FINDPSPENV Finds the environment block for a COMMAND.COM PSP
; Entry: AX - Segment of PSP
; Exit: AX - Segment of environment
;-----------------------------------------------------------------------
findpspenv proc near
	 assume cs:code,ds:code,es:code,ss:code
		push	bx
	 push es
		mov	dx,ax			;Save PSP segment
		mov	es,ax
		mov	ax,es:[2ch]		;Get ptr to environment
		call	chkarena		;See if valid memory block
		jc	findpspenv_1
		cmp	dx,bx			;See if owned by cmd.com
		je	findpspenv_3
findpspenv_1:
		mov	ax,dx			;Get original PSP
		call	chkarena		;Compute size of segment
		jc	findpspenv_error
findpspenv_2:
	 add ax,cx ;Add size of memory block
		inc	ax
		call	chkarena
		jc	findpspenv_error
		jnz	findpspenv_error	;Env never last env block
		cmp	bx,dx			;See if owned by CMD.COM
		jne	findpspenv_2		;Yes, exit
findpspenv_3:
	 clc
findpspenv_exit:
	 pop es
		pop	bx
	 ret
findpspenv_error:
		stc
		jmp	short findpspenv_exit
findpspenv endp
;-----------------------------------------------------------------------
; FINDENV Finds the parent's environment block.
; Exit: AX - Segment of local command processor environment.
;-----------------------------------------------------------------------
findenv proc near
	 assume cs:code,ds:code,es:code,ss:code
		call	findcmdpsp		;Get PSP of command.com
	 jc findenv_error
		call	findpspenv		;Find environment for PSP
	 jc findenv_error
findenv_exit:
	 ret
findenv_error:
		mov	si,offset errmsg16	;Environment not found
		jmp	short findenv_exit
findenv endp
;-----------------------------------------------------------------------
; FINDMASTER Finds the master environment block.
; Exit: AX - Segment of master environment block.
;-----------------------------------------------------------------------
findmaster proc near
	 assume cs:code,ds:code,es:code,ss:code
	 push di
	 push es
	 mov ah,52h ;Get address of first MCB
	 int 21h
	 mov ax,es:[bx-2] ;point ES to MCB
findmaster_1:
		inc	ax
		call	chkarena		;See if valid mem block
		jc	findmaster_error
	 jnz	findmaster_error
		cmp	ax,bx			;See if PSP block
		je	findmaster_2
		add	ax,cx			;No, add size to move 2 next
	 jmp short findmaster_1
findmaster_2:
	 cmp dos_version,0a00h ;If OS/2, use DOS 3.3 method.
	 jae findmaster_3
	 cmp dos_version,0400h ;If DOS 4.00 or greater,
	 jb findmaster_3 ; COMMAND.COM may not be the
		push	ax
		dec	ax
		mov	es,ax ; first program loaded. Look
		pop	ax
		push	si
	 mov si,offset shell_name ; at the name of the program
	 mov di,8 ; stored in the last 8 bytes
		xor	cx,cx			; of the memory control
	 mov cl,shell_namlen ; block. If the name of the
	 repe cmpsb ; command processor isn't 
		pop	si
	 jne findmaster_1 ; found, keep looking.
		cmp	shell_namlen,8		;If name shorter than 8 chars
		je	findmaster_3		; check for trailing zero.
		cmp	byte ptr es:[di],0
		jne	findmaster_1
findmaster_3:
		call	findpspenv		;Find environment for PSP
findmaster_exit:
		pop	es
		pop	di
	 ret
findmaster_error:
		mov	si,offset errmsg16	;Environment not found
		stc
		jmp	short findmaster_exit
findmaster endp
;-----------------------------------------------------------------------
; CHECK4XMS Checks to see if an extended memory driver is loaded
; Exit: CF - Clear if ext driver found
; AX - EXT Version if CF clear
; SI - Points to error message if CF set
;-----------------------------------------------------------------------
check4xms	proc	near
		mov	ax,4300h		;Extended mem drvr chk
		int	2fh
		or	al,al			;See if loaded
		je	check4xms_error
		push	es		
		mov	ax,4310h		;Get driver entry pt
		int	2fh
 		mov	word ptr [xms_serv],bx
		mov	word ptr [xms_serv+2],es
 		pop	es
		xor	ax,ax
		call	ds:[xms_serv]		;Get version number
		mov	bx,ax			;Version num returned
		shr	al,1			; as BCD. Convert
		shr	al,1			; to std DOS format
		shr	al,1			; of maj in AH and
		shr	al,1			; minor in AL
		mov	ah,10
		mul	ah
		and	bl,0fh
		add	al,bl
		mov	ah,bh
		clc
check4xms_exit:
		ret
check4xms_error:
		stc
		xor	ax,ax
		mov	si,offset errmsg19	;ext mem not available
		jmp	short check4xms_exit
check4xms	endp
;-----------------------------------------------------------------------
; CHECK4EMS Checks to see if an expanded memory driver is loaded
; Exit: CF - Clear if EMS driver found
; AX - EMS Version if CF clear
; SI - Points to error message if CF set
;-----------------------------------------------------------------------
check4ems	proc	near
		push	es
		push	di
		mov	ax,3567h		;Get interrupt 67 vector
		int	21h
		mov	di,0ah
		mov	si,offset ems_header
		mov	cx,8
		repe	cmpsb
		pop	di
		pop	es
		jne	check4ems_error
		mov	ah,40h			;Get status
		int	67h
		or	ah,ah
		jne	check4ems_error
		mov	ah,46h			;Get version
		int	67h
		or	ah,ah
		jne	check4ems_error
		mov	bl,al			;Convert ver number
		shl	ax,1
		shl	ax,1
		shl	ax,1
		shl	ax,1
		mov	al,bl
		and	ax,0f0fh
		clc
check4ems_exit:
		ret
check4ems_error:
		stc
		xor	ax,ax
		mov	si,offset errmsg17	;EMS not available
		jmp	short check4ems_exit
check4ems	endp
		even ;Set stack on word boundry
end_of_code = $
code ends
end entry

4

file: /Techref/dos/command/strings/STRINGS.ASM, 167KB, , updated: 1995年3月27日 12:10, local time: 2025年9月10日 13:25,
40.74.122.252:LOG IN

©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://techref.massmind.org/techref/dos/command/strings/STRINGS.ASM"> dos command strings STRINGS</A>

Did you find what you needed?

Welcome to massmind.org!

Welcome to techref.massmind.org!

.

AltStyle によって変換されたページ (->オリジナル) /