;    File              : $GENERCFG.A86$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;
;    $Log$
;
;    GENERCFG.A86 1.51 93/12/02 00:15:06
;    When auto-sizing DEVICEHIGH requirements use EXE header info if present
;    GENERCFG.A86 1.49 93/11/29 14:10:29
;    Add NUMLOCK=ON/OFF support
;    GENERCFG.A86 1.48 93/11/28 15:32:22
;    Support HIBUFFERS in UMB's
;    GENERCFG.A86 1.47 93/11/22 15:45:11
;    Ignore [COMMON] statements
;    GENERCFG.A86 1.46 93/11/18 16:20:16
;    Add primitive multi-master checking
;    GENERCFG.A86 1.45 93/11/17 20:07:17
;    change history defaults
;    GENERCFG.A86 1.44 93/11/16 14:10:21
;    F5 has precedence over ?
;    GENERCFG.A86 1.42 93/11/08 17:37:06
;    Move INSTALLHIGH to before INSTALL, so it's now recognised
;    GENERCFG.A86 1.41 93/11/04 16:28:08
;    Cosmetic change - the preload name field now strips off "C:\" properly
;    GENERCFG.A86 1.40 93/11/03 22:52:14
;    Replace chardev test with one for zero units
;    GENERCFG.A86 1.39 93/11/03 18:21:16
;    disable CHARDEV test until it gets fixed
;    GENERCFG.A86 1.38 93/11/03 17:11:05
;    Preloaded compression drivers maybe loaded from C: when booting
;    from a diskette.    
;    GENERCFG.A86 1.37 93/10/29 19:42:51
;    DOS=UMB turns HIDOS on
;    GENERCFG.A86 1.36 93/09/28 19:54:52
;    Support "DEVICE?=" syntax, and "SWITCHES=/N"
;    GENERCFG.A86 1.35 93/09/02 22:35:16 
;    Add header to system allocations
;    GENERCFG.A86 1.32 93/08/02 14:46:21
;    hide preload drives from func_device
;    support INSTALLHIGH
;    GENERCFG.A86 1.30 93/07/29 14:47:15
;    Change SETVER method
;    GENERCFG.A86 1.28 93/07/20 22:32:07
;    default upper memory link = FFFF
;    ENDLOG


	include config.equ
	include	i:msdos.equ
	include i:char.def
	include	i:reqhdr.equ
	include	i:driver.equ
	include	i:fdos.equ
	include	i:f52data.def		; Function 52 DOS Data Area
	include	i:doshndl.def		; DOS Handle Structure Definition
	include i:country.def

TRUE	   	equ	0FFFFh		; value of TRUE
FALSE	   	equ	0		; value of FALSE

F5KEY		equ	3F00h		; keys returned by BIOS
F8KEY		equ	4200h		;  in boot_options

CGROUP		group	INITCODE, INITDATA, INITENV

INITCODE	CSEG	PARA 'INITCODE'

	extrn	whitespace:near
	extrn	build_cmd_tail:near
	extrn	device_init:near
	extrn	init_static_request:near
	extrn	block_device:near
	extrn	device_insert:near
if not ADDDRV
	extrn	nls_hook:near
	extrn	nls_unhook:near
endif

CONFIG_ERRLVL	equ	400h		; special 'CONFIG' error level

; config_process returns CF set if an error occurred during command file
; processing for ADDDRV.EXE

	Public config_process
config_process:		; Process CONFIG.SYS

	mov	save_sp,sp		; save SP for unstructured GOSUB's

if ADDDRV
	mov	error_flag,0
else
	mov	ax,offset envstart	; env buffer is para aligned
	mov	cl,4
	shr	ax,cl			; convert offset to paras
	mov	cx,ds
	add	ax,cx			; segment of env variables
	les	bx,drdos_ptr
	mov	es:DRDOS_ENVSEG[bx],ax	; tell COMMAND.COM where we are
	push ds ! pop es
endif

	mov	ah,MS_DRV_GET		; check whether we are loading from
	int	DOS_INT			;   drive C:
	cmp	al,2
	 je	preload_security
	push	ds
	mov	ah,32h			; get DOS parameter block
	mov	dl,3			; ensure drive C: is valid (no critical
	int	DOS_INT			;  errors)
	pop	ds
	cmp	al,0ffh			; skip if the drive is not valid
	 je	preload_stacker
	mov	alt_drive,1		; drive C: should be used also
	jmp	preload_stacker

preload_security:
	call	preload_device		; preload the SECURITY device driver

preload_stacker:
	mov	preload_file,offset stacker_file+2
	call	preload_device		; preload STACKER from default drive
	 jnc	preload_done

	mov	preload_file,offset dblspace_file+2
	call	preload_device		; STACKER failed, try DBLSPACE from default
	 jnc	preload_done

	cmp	alt_drive,0		; STACKER and DBLSPACE failed on A:,
	 je	preload_done		;   should we look on drive C:?

	mov	preload_file,offset stacker_file
	call	preload_device		; preload STACKER from drive C:
	 jnc	preload_done

	mov	preload_file,offset dblspace_file
	call	preload_device		; STACKER failed, try DBLSPACE from C:

preload_done:
	mov	ax,(MS_X_OPEN*256)+80h	; Open the configuration file
	mov	dx,offset cfg_file	; Try Opening the file DCONFIG.SYS
	int	DOS_INT			; if this fails then open CONFIG.SYS
	 jnc	cfg_20			; Found DCONFIG.SYS

if ADDDRV
	mov	si,dx
	mov	dx,offset err_no_command_file
	xor	al,al
	call	config_error
	jmps	cfg_exit
else
	mov	si,offset cfg_file+1
	mov	di,offset cfg_file
	call	copy_asciiz		; make DCONFIG.SYS = CONFIG.SYS
	
	mov	ax,(MS_X_OPEN*256)+80h	; Open the configuration file
	int	DOS_INT			; CONFIG.SYS
	 jc	cfg_exit		; Return on error without complaint
endif

cfg_20:
	mov	bx,ax			; get the CONFIG handle
	mov	ah,MS_X_CLOSE		; Close the CONFIG file
	int	DOS_INT

cfg_nextline:				; Read the next command line
	call	readline		; If Carry then Terminal Error
	 jc	cfg_exit		;  so Exit
	mov	ax,boot_options
cfg_continue:
	push	ax			; save query options
	call	scan			; Now Scan the command List 
	pop	ax			; recover query options
	 jc	cfg_error		; for a matching command name
	call	cfg_query		; prompt to see if we want it
	 jc	cfg_nextline
	call	CFG_FUNC[di]		; Execute the request command
	jmps	cfg_nextline		; and loop till the end of the file

cfg_error:
	mov	dx,offset bad_command	; Display a Bad command error msg
	mov	al,CR			; and the CR terminated string
	call	config_error		; then continue processing
	jmps	cfg_nextline		; Carry Returned On Error

cfg_exit:
	mov	sp,save_sp		; get back the real stack
	call	preload_complete	; move transcient code to final position
if ADDDRV
	cmp	error_flag,1		;Set CF if error flag is zero
	cmc				;Set CF if error flag is 1
endif
	ret


cfg_query:
; On Entry:
;	AX = boot_options key
;	SI -> command tail
;	DI -> CFG_
; On Exit:
;	CY set if we should skip command
;	SI/DI preserved
;
	push	si
	push	di
	test	CFG_FLAGS[di],CF_NOF	; are Function Keys allowed ?
;	clc				; if not process the command
	 jz $+5 ! jmp cfg_query90
	cmp	ax,F5KEY		; F5 bypasses CONFIG processing
	stc				;  so bypass this line
	 je	cfg_query90
	test	CFG_FLAGS[di],CF_QUERY	; specific QUERY request ?
	 jnz	cfg_query10
	cmp	ax,F8KEY		; should we prompt for everything ?
	clc				; if not process the command
	 jne	cfg_query90
cfg_query10:
	push	si
	mov	si,CFG_NAME[di]		; DS:SI -> command name we matched
cfg_query20:
	lodsb				; get character
	test	al,al			; zero terminated
	 jz	cfg_query30
	xchg	ax,dx			; DL= character
	mov	ah,MS_C_WRITE
	int	DOS_INT			; output the character
	jmps	cfg_query20		; "DEVICE"
cfg_query30:
	mov	dl,'='
	mov	ah,MS_C_WRITE
	int	DOS_INT			; "DEVICE="
	pop	si			; rest of command line
cfg_query40:
	lodsb
	cmp	al,CR			; is it the end of the line ?
	 je	cfg_query50		;  no, do another character
	xchg	ax,dx			; DL= character
	mov	ah,MS_C_WRITE
	int	DOS_INT			; output the character
	jmps	cfg_query40		; "DEVICE=FILENAME.SYS"
cfg_query50:
	mov	ah,MS_C_WRITESTR	; Output msg of form " (Y/N) ? "
	mov	dx,offset confirm_msg1
	int	DOS_INT			; do " ("
	mov	ah,MS_C_WRITE
	mov	dl,yes_char
	int	DOS_INT			; do "Y"
	mov	dl,'/'
	int	DOS_INT			; do "/"
	mov	dl,no_char
	int	DOS_INT			; do "N"
	mov	ah,MS_C_WRITESTR
	mov	dx,offset confirm_msg2
	int	DOS_INT			; do ") ? "
cfg_query60:
	call	wait_for_key		; wait until a key is pressed
	mov	al,default_query_char	; if we timeout default
	 jc	cfg_query70
	mov	ah,MS_C_RAWIN
	int	DOS_INT			; read a char
	test	al,al			; is it a function key ?
	 jnz	cfg_query70
	mov	ah,MS_C_RAWIN
	int	DOS_INT			; throw away function keys
    jmps    cfg_query60  
cfg_query70:
	push	ax			; save response
	mov	ah,MS_C_WRITE
	mov	dl,al
	int	DOS_INT			; echo the char
	mov	ah,MS_C_WRITESTR
	mov	dx,offset confirm_msg3	; "DEVICE=FILENAME.SYS (Y/N) ?"
	int	DOS_INT			; now do CR/LF to tidy up
	pop	ax
	call	toupper			; make response upper case
	cmp	al,yes_char		; is it yes ?
	 je	cfg_query90
	stc				; return CY set, skip this line
cfg_query90:
	pop	di
	pop	si
	ret


call_preload_entry:		; call the preload device driver back door
	push	ds		; all DBLSPACE calls destroy DS register
	callf	preload_entry
	pop	ds
	ret

preload_complete:		; CONFIG processing complete
	push	ds
	push	es
	mov	ax,4a11h		; DBLSPACE presence check
	xor	bx,bx
	int	2fh
	or	ax,ax
	 jnz	reloc_end
	test	dx,8000h		; is relocation already complete (may
	 jz	reloc_end		;   be done by HIDEVICE statement)
	mov	bx,0ffffh		; query the size of the transcient
	mov	ax,4a11h		;   portion
	int	2fh

	mov	cx,mem_current		; pull transcient portion down to low
	mov	es,cx			;   memory
	inc	cx
	mov	es:DMD_ID,'D'		; control block type is 'D' (device
	mov	es:DMD_PSP,cx		;   driver), owner is self
	mov	es:DMD_LEN,ax
	inc	ax			; control block overhead
	add	mem_current,ax
	mov	di,8			; copy the name to the control block
	mov	si,preload_file		; device filename
preload_complete10:
	lodsb				; get a character
	cmp	al,'\'			; have we found the '\' yet ?
	 jne	preload_complete10	; no, go swallow another character
	movsw ! movsw ! movsw ! movsw	; copy the 8 bytes
	mov	ax,es
	inc	ax			; segment address of free memory
	mov	es,ax
	mov	bx,0fffeh		; move transcient portion down
	mov	ax,4a11h
	int	2fh
reloc_end:
	pop	es
	pop	ds
	ret

PreloadFixup:
;------------
; On Entry:
;	None
; On Exit:
;	None
;	All regs preserved
;
	push	es
	push	bx
	push	ax
	mov	al,preload_drv		; get number of preload drives
	les	bx,func52_ptr
	sub	es:F52_PHYDRV[bx],al
	sub	es:F52_LASTDRV[bx],al
	pop	ax
	pop	bx
	pop	es
	ret

PreloadCleanup:
;--------------	
; On Entry:
;	None
; On Exit:
;	None
;
; inform DBLSPACE about each device driver
	push	ds
	push	es
	xor	ax,ax
	xchg	al,preload_drv		; get number of preload drives
	les	bx,func52_ptr
	add	es:F52_PHYDRV[bx],al
	add	es:F52_LASTDRV[bx],al
	mov	ax,4a11h		; DBLSPACE installation check
	xor	bx,bx
	int	2fh
	or	ax,ax
	 jnz	broadcast_exit
	test	dx,8000h		; initialisation complete?
	 jz	broadcast_exit
	mov	preload_drv,ch		; save # preload drives
	mov	dx,mem_max		; top of available memory
	mov	cx,mem_current		; base of available memory
	sub	dx,cx
	mov	ah,55h			; version number
	mov	al,dev_count		; number of new units installed
    mov bx,02h          
	callf	preload_entry
broadcast_exit:
	pop	es
	pop	ds
	ret

preload_device:			; preload disk compression driver before
				;   processing (D)CONFIG.SYS file
	mov	dx,preload_file
	mov	cx,mem_current		; next available segment address
	mov	es,cx
	inc	cx
	mov	es:DMD_ID,'D'		; control block type is 'D' (device
	mov	es:DMD_PSP,cx		;   driver), owner is self
	mov	di,8			; copy the name to the control block
	mov	si,dx			; device filename
preload_device10:
	lodsb				; get a character
	cmp	al,'\'			; have we found the '\' yet ?
	 jne	preload_device10	; no, go swallow another character
	movsw ! movsw ! movsw ! movsw	; copy the 8 bytes
	mov	ax,es
	inc	ax			; segment address of free memory
	mov	dev_load_seg,ax		; destination segment for EXEC call
	mov	dev_reloc_seg,ax	; relocation factor for .EXE drivers
	push	ax
	push ds ! pop es
	mov	bx,offset dev_epb	; ES:BX control structure
	mov	ax,(MS_X_EXEC * 256)+3
	int	DOS_INT
	pop	es			; ES:0 -> preload driver
	 jnc	load_ok
load_bad:
	jmp	preload_exit
load_ok:
	cmp	es:WORD PTR .12h,2e2ch	; preload device signature
	 jne	load_bad
	mov	preload_seg,es		; back door entry to preload driver
	cmp	es:word ptr .82h,6	; is it the old DBLSPACE driver ?
	 jne	load_new_dblspace
	mov	preload_ver,6		; yes, give it the old version
load_new_dblspace:
	mov	rel_unit,0		; reset driver relative unit no.
	mov	bx,offset request_hdr	; DS:BX static INIT request header
	mov	ax,mem_max		; highest segment available to the
	mov	ds:RH0_RESIDENT,0	;   driver
	mov	ds:RH0_RESIDENT+2,ax
	mov	ax,cs
	mov	es,ax
	push	ds
	call	init_static_request	; initialise remaining fields
    mov ax,cs:preload_ver   
	callf	preload_entry
	pop	ds
	 jc	preload_exit		; INIT function fails if CARRY set
	or	ax,ax			;   or AX != 0
	 jnz	preload_exit
;;	mov	ax,ds:RH0_RESIDENT	; end of resident portion (offset)
;;	add	ax,15			; convert offset to paragraph size
;;	mov	cl,4
;;	shr	ax,cl
;;	add	ax,ds:RH0_RESIDENT+2	; end of resident portion (segment)
;JS we should check that the source and destination do not overlap here!

	mov	bx,04h			; find the size of the transient code
	call	call_preload_entry
	sub	mem_max,ax		; move the top of available memory down

	mov	es,mem_max		; ES destination for relocatable code
	dec	mem_max			; last free segment address
	mov	bx,06h			; call driver to relocate code etc.
	call	call_preload_entry
    inc ax          
	mov	mem_current,ax		; update base of free TPA

	mov	es,preload_seg
	xor	di,di			; ES:DI -> preload driver
	mov	bx,offset request_hdr
	mov	al,ds:RH0_NUNITS
	test	al,al
	 jz	preload_char_dev
	add	preload_drv,al		; remember how many preloaded drives
	call	block_device		; setup the DPBs and other block structures

preload_char_dev:
	mov	cx,mem_current
	mov	ax,preload_seg
	sub	cx,ax			; CX = length of preload driver
	dec	ax
	mov	es,ax			; ES:0 -> "DMD" for preload driver
	mov	es:DMD_LEN,cx
	inc	ax
	mov	es,ax
	xor	di,di			; ES:DI -> "device" header
	mov	ax,0FFFFh
	mov	es:[di],ax
	mov	es:2[di],ax
	call	device_insert		; insert device into chain

	mov	dx,mem_max		; top of available memory
	mov	cx,mem_current		; base of available memory
	sub	dx,cx
	mov	ax,5500h		; AH = version, AL = all internal drives
	mov	bx,02h			; mount existing container files
	call	call_preload_entry

	xor	bx,bx			; complete initialisation (hook
	call	call_preload_entry	;   interrupts etc.)

preload_exit:
	push cs ! pop es
	ret



func_hidevice:		; HIDEVICE=filename
; Look for /L:r1[,s1][;r2[,s2]] [/S] filename
; Look for SIZE=<hexnumber> filename

	mov	himem_region,0		; assume no region supplied
	mov	himem_size,0		;  and no size
	mov	di,offset region_opt	; check out /L: region option
	call	compare
	 jc	hidevice10
	call	parse_region		; get region and size
	 jnc	hidevice20
	xor	si,si			; something wrong, give an error
	mov	dx,offset bad_filename
	jmp	config_error

hidevice10:
	mov	di,offset size_opt	; check out SIZE= option
	call	compare
	 jc	hidevice20		; if no SIZE= just try to load it
	call	parse_size		;  else get supplied value
hidevice20:
	call	whitespace		; we may have a '=' lurking
hidevice30:
	lodsb				; strip off optional '='
	cmp	al,'='			;  before the filename
	 je	hidevice30
	dec	si			; it wasn't a '=' after all
	call	whitespace

	mov	ax,himem_size		; get size parameter
	cmp	ax,6000h		; should we just load low ?
	 jae	func_device
	push	si
	test	ax,ax			; have we been given a size ?
	 jne	hidevice40
	call	size_file		; get requirements from file size
hidevice40:
	call	himem_setup		; try and find some hi memory
	pop	si
	 jc	func_device		; if we can't load low
	push	si
	call	func_device		; now try and install the device
	call	himem_cleanup		; clean up our hi memory
	pop	si
	 jc	func_device		; error loading hi, try low
	ret


func_device:		; DEVICE=filename
	push	si
	mov	di,offset dev_name	; Copy the Device Filename into a 
	mov	byte ptr [di],0		; local buffer and zero terminate
	call	copy_file
	pop	si			; Restore original SI
	 jc 	device_error

	push	es
	push	si
	mov	es,mem_current		; ES points at structure
	call	BuildHeader
	pop	si
	push	si
	call	FindName		; DS:SI -> leafname
	call	SetName			; Fill in name info
	pop	si
	pop	es

	mov	ax,mem_current		; Get Current Memory Base
	inc	ax			; allow a para for a header
	mov	dev_load_seg,ax		; Setup the Load Address and the
	mov	dev_reloc_seg,ax	; relocation factor to be applied to
					; .EXE device drivers
	
	mov	ax,(MS_X_OPEN * 256)+0	; open file r/o
	mov	dx,offset dev_name
	int	DOS_INT
	 jc	device_error
	mov	bx,ax			; now find out how big the file is
	mov	ax,(MS_X_LSEEK * 256)+2
	xor	cx,cx
	xor	dx,dx			; by seeking to zero bytes from end
	int	DOS_INT
	 jc	device_error

	xchg	ax,cx			; save lo byte of length

	mov	ah,MS_X_CLOSE		; close this file
	int	DOS_INT
	 jc	device_error

	mov	ax,cx			; DX:AX file length
	or	cx,dx			; do we have a zero length file
	 jz	device_error		; if so stop now

	mov	cl,4
	shr	ax,cl			; convert file size to para
	mov	cl,12
	shl	dx,cl			; ignore > 1 MByte portion
	or	dx,ax			; dx = file size in para
	inc	dx			; one for rounding error
	
	add	dx,mem_current		; top of area needed for the load
	cmp	dx,mem_max		; will file fit (really HIDEVICE check)
	 jb	func_device10		; no, stop now
	ret

device_error:
	mov	error_level,ax		; save error code
	mov	al,0
	mov	si,offset dev_name
	mov	dx,offset bad_filename
	jmp	config_error

func_device10:
	mov	ax,(MS_X_EXEC * 256)+3	; Use the Load Overlay function to
	mov	bx,offset dev_epb	; Read in and Relocate the Device
	mov	dx,offset dev_name	; driver
	int	DOS_INT			;
	 jc	device_error

;	mov	dosVersion,ax		; set version number

	push	es
	mov	ax,mem_current		; Get Current Memory Base
	push	ax
	inc	ax			; skip the header
	xor	di,di			; Address of the first device header
	mov	es,ax			; in the device chain

if ADDDRV
	test	es:DH_ATTRIB[di],DA_CHARDEV
	 jnz	f_dev30			;Character device driver ?

; Can't install block device drivers so output error message and exit.
	pop	es
	pop	es

	xor	al,al
	mov	si,offset dev_name
	mov	dx,offset err_block_device
	jmp	config_error

f_dev30:
endif

	call	PreloadFixup		; fiddle things for preload
if DOS5
	call	ProtmanFixup		; fiddle int 12 memory for PROTMAN$
endif
	call	device_init		; initialise the device drivers
if DOS5
	call	ProtmanCleanup		; restore int 12 memory after PROTMAN$
endif
	mov	error_level,ax		; save error code
	pop	es			;  by old EMM386.SYS
	mov	ax,mem_current
	mov	es:DMD_LEN,ax		; save memory field
	mov	ax,es
	inc	ax
	sub	es:DMD_LEN,ax
	pop	es
	call	PreloadCleanup		; cleanup after ourselves
	ret


BuildHeader:
;-----------
; On Entry:
;	ES:0 -> header
; On Exit:
;	None
;
	xor	di,di
	mov	al,'D'
	stosb				; ID_FIELD
	mov	ax,es
	inc	ax
	stosw				; OWNER_FIELD
	xor	ax,ax
	stosb ! stosw ! stosw		; zero rest up to name
	ret


FindName:
;--------
; On Entry:
;	DS:SI -> pathname of file
; On Exit:
;	DS:SI -> final leaf name of file
;	CX = length of leaf name
;
	mov	cx,si			; remember start of leaf
FindName10:
	lodsb
	cmp	al,' '			; end of the name ?
	 jbe	FindName30
	call	dbcs_lead		; is it a double byte pair ?
	 jne	FindName20
	lodsb				; include the second byte
	jmps	FindName10
FindName20:
	cmp	al,'\'			; is it a seperator ?
	 je	FindName
	cmp	al,'/'
	 je	FindName
	jmps	FindName10
FindName30:
	xchg	cx,si			; SI -> start of leaf name
	sub	cx,si
	dec	cx			; CX = length
	ret


SetName:
;-------
; On Entry:
;	DS:SI -> leaf name to update
;	ES = DMD to update
; On Exit:
;	CX/SI preserved
;
	push	si
	mov	di,offset DMD_NAME	; point at the owners name field
SetName10:
	lodsb
	cmp	al,' '			; end of the name ?
	 jbe	SetName30
	call	dbcs_lead		; is it a double byte pair ?
	 jne	SetName20
	stosb				; copy 1st byte of pair
	cmp	di,(offset DMD_NAME)+DMD_NAME_LEN
	 jae	SetName30		; don't overflow if name too long
	movsb				; and the second
	jmps	SetName10

SetName20:
	stosb
	cmp	al,'.'			; discard all following '.'
	 je	SetName30
	cmp	di,(offset DMD_NAME)+DMD_NAME_LEN
	 jb	SetName10		; don't overflow if name too long
SetName30:
	dec	di
	xor	ax,ax
SetName40:
	stosb				; zero the '.'
	cmp	di,(offset DMD_NAME)+DMD_NAME_LEN
	 jb	SetName40		; zero the rest of the name
SetName50:
	pop	si
	ret

if DOS5

ROS_MEMORY	equ	.413h		; main memory on KB

protmanName	db	'PROTMAN$'

protmanAdjust	dw	0

ProtmanFixup:
;------------
; On Entry:
;	ES:DI -> device driver header
; On Exit:
;	All regs preserved
;
; fiddle int 12 memory for PROTMAN$
;
	push	cx
	push	si
	push	di
	mov	cs:protmanAdjust,0	; assume it's not protman
	mov	si,offset protmanName
	lea	di,DH_NAME[di]		; ES:DI -> device driver name
	mov	cx,8/2
	repe	cmpsw			; does the name match ?
	 jne	ProtmanFixup10
	mov	si,mem_max		; SI = top of memory in para
	push	ds
;	xor	cx,cx
	mov	ds,cx
	mov	cl,10-4
	shr	si,cl			; convert para to KBytes
	mov	cx,ds:ROS_MEMORY	; CX = existing top of memory
	sub	cx,si			; CX = amount to hide
	sub	ds:ROS_MEMORY,cx	; hide it
	pop	ds
	mov	cs:protmanAdjust,cx	; remember how much we hid
ProtmanFixup10:
	pop	di
	pop	si
	pop	cx
	ret

ProtmanCleanup:
;--------------
; On Entry:
;	None
; On Exit:
;	All regs preserved
; restore int 12 memory after PROTMAN$
;
	push	ds
	push	ax
	xor	ax,ax
	mov	ds,ax
	mov	ax,cs:protmanAdjust	; normally zero..
	add	ds:ROS_MEMORY,ax
	pop	ax
	pop	ds
	ret

endif

;
;	The syntax currently supported is "COUNTRY=NNN,[YYY],[FILENAME]" 
;	where:-
;		NNN 	 is a valid country code based on
;			 the International Dialing Code
;
;		YYY	 is the default CODEPAGE
;
;		FILENAME is the location and name of the COUNTRY.SYS
;			 file containing the extended country info.
;
;
if not ADDDRV
func_country:		; COUNTRY=nnn,[yyy],[filename]
	call	atoi			; ax = country code
	 jc	f_ctry50		; check for error
	mov	country_code,ax		; save the Country Code
	call	separator		; look for ','
	 jc	f_ctry20
	call	atoi			; Get the Code Page
	 jc	f_ctry10		; invalid or non existent code page
	mov	code_page,ax		; save default Code Page
f_ctry10:
	call	separator		; look for ','
	 jc	f_ctry20		; copy the supplied pathname
	les	bx,drdos_ptr		; Get the internal data area
	mov	di,es:DRDOS_COUNTRY_FILE[bx]
					; ES:DI -> pcmode buffer for name
	call	copy_file
	push	cs			; restore ES
	pop	es
f_ctry20:
	call	nls_hook		; install our lang support
	mov	bx, country_code	; bx = country code
	mov	ah, MS_S_COUNTRY
	mov	al, 0FFh		; 16 bit country code
	mov	dx, 0FFFFh		; set country code subfunction
	int	DOS_INT
	 jc	f_ctry40		; check for error
	mov	bx, code_page		; bx = code page
	or	bx,bx
	 jz	f_ctry30		; No Code Page Set leave as 437
	mov	ax, MS_X_SETCP		; set codepage subfunction
	int	DOS_INT			; to set Current CodePage
f_ctry30:
	jmp	nls_unhook		; remove our lang support
;	ret

f_ctry40:
	call	nls_unhook		; remove our lang support
f_ctry50:
; Bad or non-existant number in command tail. Display error message.
	xor	si,si
	mov	dx, offset bad_country
	jmp	config_error



func_shell:		; SHELL=filename
	mov	di,offset shell		; Copy the New Command Name
	call	copy_file		; into the BIOSINIT buffer
	mov al,0 ! stosb		; Terminate the Environment Correctly
	jc	shell_error

	mov	di,offset shell_cline+1	; Now copy the default command
	mov al,' ' ! stosb		; into place
	mov	cx,0			; 		
f_sh10:
	lodsb ! stosb			; Copy the next Character
	inc	cx			; Increment the count
	cmp	al,CR			; Was this the end of string Char
	jnz	f_sh10			; No so Repeat
	mov	shell_cline,cl		; Save Command Length
	ret

shell_error:
	mov	al,CR
	mov	dx,offset bad_shell
	jmp	config_error


func_lastdrive:		; LASTDRIVE=d:
	call	atoi			; are we supplying a decimal number?
	 jc	f_lastdrive10
	cmp	al,32			; is it in range ?
	 jbe	f_lastdrive20	
lastdrv_error:
	xor	si,si			; Do not display Failing String
	mov	dx,offset bad_lastdrive	; and display an error message
	jmp	config_error

f_lastdrive10:
	lodsb				; Get the Option Character 
	call	toupper			; and convert to Upper Case
	sub	al, 'A'			; al should be between 0..25
	jc	lastdrv_error
	cmp	al, 'Z'-'A'
	ja	lastdrv_error
	inc	al			; al = number of drives in system
f_lastdrive20:
	mov	last_drv,al		; remember for later
	ret



func_break:		; BREAK=ON/OFF
	call	check_onoff		; Check for ON or OFF
	mov	dl,al			; Get value found
	jnc	set_break		; and check for error

	xor	si,si			; Do not display Failing String
	mov	dx, offset bad_break	; Bad or non-existant ON/OFF string
	jmp	config_error		; in command tail. Display error message.

set_break:
	mov	ah,MS_S_BREAK		; Set the Default Break Flag
	mov	al,01			; Set Sub-Function
	int	DOS_INT			; Execute function and return
	ret



func_numlock:		; NUMLOCK=ON/OFF
	call	check_onoff		; Check for ON or OFF
	 jc	numlock20
	push	ds
	xor	bx,bx
	mov	ds,bx
	mov	bx,417h			; DS:BX -> keyboard control
	or	ds:byte ptr [bx],20h	; set numlock bit
	test	al,al			; was it numlock on ?
	 jnz	numlock10		; if not, better clear the bit
	and	ds:byte ptr [bx],not 20h
numlock10:
	pop	ds
	mov	ah,MS_C_STAT		; get console status
	int	DOS_INT			; (usually int 16 update NUMLOCK state)
numlock20:
	ret



func_hibuffers:		; HIBUFFERS=nn[,nn]
	or	buffersIn,BUFFERS_IN_HMA
;	jmp	func_buffers

func_buffers:		; BUFFERS=nn[,nn]
	call	atoi			; AX = # of buffers
	 jc	buffer_error		; check for error
	cmp	ax,MIN_NUM_BUFFS	; check if less than minimum
	 jae	func_buf1
	mov	ax,3			; force to use minimum if less
func_buf1:
	cmp	ax,MAX_NUM_BUFFS	; check if more than maximum
	 jbe	func_buf2
	mov	ax,MAX_NUM_BUFFS	; force to use maximum if more
func_buf2:
	mov	init_buf,al		; update if we want more
	call	separator		; look for ','
	 jc	func_buf4
	call	atoi			; Get read-ahead buffer size
	 jc	buffer_error
	cmp	ax,MIN_READ_AHEAD
	 jb	buffer_error
	cmp	ax,MAX_READ_AHEAD
	 ja	buffer_error
	mov	num_read_ahead_buf,al
func_buf4:
	ret

buffer_error:
	xor	si,si			; Do not display Failing String
	mov	dx, offset bad_buffers
	jmp	config_error


func_files:		; FILES=nn
	call	atoi			; AX = # of files
	 jc	files_error		; check for error
	cmp	ax,MIN_NUM_FILES	; check if less than minimum
	 jae	func_fil1
	mov	ax,MIN_NUM_FILES	; force to use minimum if less
func_fil1:
	cmp	ax,MAX_NUM_FILES	; check if more than maximum
	 jbe	func_fil2
	mov	ax,MAX_NUM_FILES	; force to use maximum if more
func_fil2:
	mov	num_files,ax		; update the number required
	ret

files_error:
	xor	si,si			; Do not display Failing String
	mov	dx, offset bad_files
	jmp	config_error



func_fcbs:		; FCBS=nn
	call	atoi			; AX = # of files
	 jc	fcbs_error		; check for error
	cmp	ax,MIN_NUM_FCBS		; check if less than minimum
	 jae	func_fcb1
	mov	ax,MIN_NUM_FCBS		; force to use minimum if less
func_fcb1:
	cmp	ax,MAX_NUM_FCBS		; check if more than maximum
	 jbe	func_fcb2
	mov	ax,MAX_NUM_FCBS		; force to use maximum if more
func_fcb2:
	mov	num_fcbs,ax		; update number of FCB's
	ret

fcbs_error:
	xor	si,si			; Do not display Failing String
	mov	dx, offset bad_fcbs
	jmp	config_error
endif					;not ADDDRV



func_common:		; [COMMON]
func_remark:		; REM Comment field
	ret

func_switches:		; SWITCHES=...
			; /N = disable F5/F8 feature
			; /W = zap wina20.386 name
			; /K = disable enhanced keyboard support
			; /F = skip statup delay

	call	whitespace		; skip all spaces
	lodsw				; get '/x'
	cmp	al,CR			; check for end-of-line
	 je	func_switches10
	cmp	ah,CR			; check for end-of-line
	 je	func_switches10
	cmp	ax,'N/'
	 jne	func_switches
	mov	boot_options,0		; disable boot options
func_switches10:
	ret

func_stacks:		; STACKS=number,size
;-----------
	call	atoi			; ax = number of stacks
	 jc	func_stacks20		; check for error
	test	ax,ax			; special case ? (disabled)
	 jz	func_stacks10
	cmp	ax,MIN_NUM_STACKS	; range check for a sensible value
	 jb	func_stacks20
	cmp	ax,MAX_NUM_STACKS
	 ja	func_stacks20
func_stacks10:
	mov	num_stacks,ax
	call	separator		; look for ','
	 jc	func_stacks20
	call	atoi			; get size of a stack frame
	 jc	func_stacks20
	cmp	ax,MIN_SIZE_STACK	; range check it
	 jb	func_stacks20
	cmp	ax,MAX_SIZE_STACK
	 ja	func_stacks20
	mov	stack_size,ax
func_stacks20:
	ret


if not ADDDRV
func_deblock:		; DEBLOCK=xxxx
;------------
	call	atohex			; read hex number into DX:AX
	 jc	func_deblock10
	test	dx,dx
	 jnz	func_deblock10
	mov	DeblockSetByUser,TRUE	; the user has supplied a setting
	push	ds
	mov	ds,bios_seg
	mov	DeblockSeg,ax		; save deblocking segment
	pop	ds
func_deblock10:
	ret

func_fastopen:		; FASTOPEN=nn
	call	atoi			; AX = # of files to cache
	 jc	fopen_error		; check for error
	test	ax,ax			; disable fast open?
	 jz	func_fopen2		; yes, allow to set to 0000
	cmp	ax,MIN_NUM_FOPEN	; check if less than minimum
	 jae	func_fopen1
	mov	ax,MIN_NUM_FOPEN	; force to use minimum if less
func_fopen1:
	cmp	ax,MAX_NUM_FOPEN	; check if more than maximum
	 jbe	func_fopen2
	mov	ax,MAX_NUM_FOPEN	; force to use maximum if more
func_fopen2:
	mov	num_fopen,ax		; update if we want more
func_fopen3:
	ret

fopen_error:
	xor	si,si			; Do not display Failing String
	mov	dx, offset bad_fopen
	jmp	config_error


func_drivparm:	; DRIVPARM = /d:nn [/c] [/f:ff] [h:hh] [/n] [/s:ss] [/t:tt]
;-------------
;	This function specifies the drive parameters for a device
;	and overrides the defaults assumed by the device driver.

	mov	drivp_drv,0FFh		; invalid drive
	call	get_switch		; get next switch
	cmp	al,'d'			; first option must be /d:dd
	 jne	drivparm_error
	call	get_number		; get numeric parameter
	mov	drivp_drv,al
	mov	drivp_chg,FALSE
	mov	drivp_prm,FALSE
	mov	drivp_trk,80		; assume 80 tracks

	mov	bl,drivp_drv		; get drive to set up
	cmp	bl,'Z'-'A'
	 ja	drivparm_error
	inc	bl
	mov	ioctl_pb,0		; return defaults
	mov	ax,440Dh		; generic IOCTL
	mov	cx,0860h		; get device parameters
	mov	dx,offset ioctl_func
	int	DOS_INT
	 jc	drivparm_error		; skip if we can't get parameters

	mov	drivp_ff,2		; assume 720K 3.5" drive
	call	set_form_factor		; set defaults for form factor

	call	get_switch		; get next switch
	cmp	al,'c'			; is it /c (change line available)
	 jne	drivparm1
	mov	drivp_chg,TRUE		; disk change line available
	call	get_switch		; get next switch
drivparm1:
	cmp	al,'f'			; form factor specification?
	 jne	drivparm2
	call	get_number		; get numeric parameter
	mov	drivp_ff,al		; set form factor
	call	set_form_factor		; set defaults for form factor
	jmps	drivparm_loop		; get more parameters

drivparm_error:
	xor	si,si
	mov	dx,offset bad_drivparm
	jmp	config_error

drivparm_loop:
	call	get_switch		; get next switch
drivparm2:
	cmp	al,'h'			; specify number of heads
	 jne	drivparm3
	call	get_number		; get numeric parameter
	cmp	ax,99
	 ja	drivparm_error
	mov	drivp_heads,ax		; set # of heads
	jmps	drivparm_loop

drivparm3:
	cmp	al,'n'
	 jne	drivparm4
	mov	drivp_prm,TRUE		; non-removable media
	jmps	drivparm_loop

drivparm4:
	cmp	al,'s'
	 jne	drivparm5
	call	get_number		; get numeric parameter
	cmp	ax,63			; range check sector per track
	 ja	drivparm_error
	mov	drivp_spt,ax		; set # of sectors/track
	jmps	drivparm_loop

drivparm5:
	cmp	al,'t'
	 jne	drivparm_error
	call	get_number		; get numeric parameter
	cmp	ax,999
	 ja	drivparm_error
	mov	drivp_trk,ax		; set # of sectors/track
	jmps	drivparm_loop

drivparm_done:				; now set drive parameters
	mov	bl,drivp_drv
	cmp	bl,'Z'-'A'
	 ja	drivparm_error

	mov	ioctl_func,0000$0100b	; normal track layout assumed,
					; set device BPB for drive
	mov	al,drivp_ff		; get form factor
	mov	ioctl_type,al

	sub	ax,ax			; assume removable, no disk change
	cmp	drivp_prm,FALSE
	 je	drivp_d1
	or	ax,1			; drive is permanent media
drivp_d1:
	cmp	drivp_chg,FALSE
	 je	drivp_d2
	or	ax,2			; drive supports disk change line
drivp_d2:
	mov	ioctl_attrib,ax		; set drive attributes

	mov	ax,drivp_trk		; set # of cylinders
	mov	ioctl_tracks,ax
	
	mov	ioctl_mtype,0		; assume standard type

	mov	ax,drivp_spt		; get sectors/track
	mov	di,offset ioctl_layout
	push	ds ! pop es		; ES:DI -> layout table
	stosw				; set # of sectors
	xchg	ax,cx			; CX = # of sectors
	mov	ax,1			; start with sector 1
drivp_d3:
	stosw				; set next sector #
	inc	ax			; move to next sector
	loop	drivp_d3		; repeat for all sectors

	mov	ax,drivp_heads
	mul	drivp_spt
	mov	dx,drivp_trk
	mul	dx			; AX/DX = # of sectors/disk
	mov	di,offset ioctl_bpb
	mov	8[di],ax		; set sectors per disk
	test	dx,dx
	 jz	drivp_d4
	mov	word ptr 8[di],0	; indicate large partition
	mov	21[di],ax		; set disk size in sectors
	mov	23[di],dx
drivp_d4:
	mov	bl,drivp_drv
	inc	bl
	mov	ax,440Dh
	mov	cx,0840h		; set drive parameters
	mov	dx,offset ioctl_func
	int	DOS_INT			; tell the BIOS, ignore errors

	ret


get_switch:				; get next command line switch
	call	whitespace		; skip all spaces & tabs
	lodsb				; get next character
	pop	dx			; get return address
	cmp	al,'/'			; did we get a switch?
	 je	get_swt9		; yes, return the character
	jmp	drivparm_done
get_swt9:
	lodsb
	or	al,('a' xor 'A')	; return upper-cased character
	jmp	dx
	

get_number:
;	entry:	SI -> next character (must be ':')

	lodsb				; get next character
	cmp	al,':'			; must be colon
	 jne	get_num_err		; return error if not
	call	atoi			; get numeric value
	 jc	get_num_err		; must be number
	ret				; AX = number

get_num_err:
	pop	dx
	jmp	drivparm_error		; reject this command


set_form_factor:
	mov	bl,drivp_ff
	cmp	bl,7
	 ja	set_form9
	mov	bh,0
	shl	bx,1
	push	si ! push es
	mov	si,ff_table[bx]		; SI -> default media BPB
	push	ds ! pop es
	mov	di,offset ioctl_bpb	; ES:DI -> local BPB
	mov	cx,21			; copy initialized portion
	rep	movsb
	pop	es ! pop si
	ret

set_form9:
	jmp	drivparm_error


;
;	This function modifies the History buffer support provided by
;	DR DOS the defaults are History OFF, 512 byte buffers,
;	Insert ON, Search OFF, Matching OFF.
;
func_history:	; HISTORY = ON|OFF[,NNNN[,ON|OFF[,ON|OFF[,ON|OFF]]]]
;------------
	mov	history_flg,0		; start with it all off

	call	check_onoff		; Check for ON|OFF Switch 
	 jc	f_hist_err
	test al,al ! jz f_hist_exit	; if OFF forget the rest
	or	history_flg,RLF_ENHANCED+RLF_INS

	call	separator		; look for ','
	 jc	f_hist_exit		; Buffer Size not Specified
	call	atoi			; Get the Buffer Size
	 jc	f_hist_err		; Invalid on no existant size
	cmp ax,128 ! jb f_hist_err	; Buffer Size to Small
	cmp ax,4096 ! ja f_hist_err	; Buffer Size to Large
	mov	history_size,ax		; Save History Buffer Size

	call	separator		; look for ','
	 jc	f_hist_exit		; Insert mode not Specified
	call	check_onoff		; Check for ON|OFF Switch 
	 jc	f_hist_err
	test	al,al ! jnz func_hist10
	and	history_flg,not RLF_INS	; Insert state OFF
func_hist10:
	call	separator		; look for ','
	 jc	f_hist_exit		; Search mode not Specified
	call	check_onoff		; Check for ON|OFF Switch 
	 jc	f_hist_err
	test	al,al ! jz func_hist20
	or	history_flg,RLF_SEARCH	; Search state ON
func_hist20:
	call	separator		; look for ','
	 jc	f_hist_exit		; Match mode not Specified
	call	check_onoff		; Check for ON|OFF Switch 
	 jc	f_hist_err
	test	al,al ! jz func_hist30
	or	history_flg,RLF_MATCH	; Match state ON
func_hist30:

f_hist_exit:
	ret

f_hist_err:
	xor	si,si			; Do not display Failing String
	mov	dx, offset bad_history	; Bad or non-existant ON/OFF string
	jmp	config_error		; in command tail. Display error message.


;
;	HIINSTALL filename [Command Line Parameters]
;
;	As INSTALL, but uses high memory if possible
;
func_hiinstall:

    mov ax,5802h       
	int	DOS_INT			;   get upper memory link
	cbw
	push	ax			; save upper memory link
	
    mov ax,5800h       
	int	DOS_INT			;   get alloc strategy
	push	ax			; save alloc strategy
	
    mov ax,5803h       
	mov	bx,1			;   set upper memory link
	int	DOS_INT			;   to 1
	
    mov ax,5801h       
	mov	bx,80h			;   set alloc strategy to lowest-upper
	int	DOS_INT			;   if available
	
	call	func_install		; try and install it
	
    mov ax,5801h       
	pop	bx			;   set alloc strategy
	int	DOS_INT			;   to original value
	
    mov ax,5803h       
	pop	bx			;   set upper memory link
	int	DOS_INT			;   to original value
	
	ret



;
;	INSTALL filename [Command Line Parameters]
;
;	INSTALL will load and execute "FILENAME" with the optional command 
;	line parameters and continue processing the DCONFIG.SYS file when
;	the application terminates.
;
; Entry
;	ds:si -> first character of CR terminated filename and option string.
; Exit
;	none
;
; WARNING -
;	This code make certain assumptions about memory layout. 
;	If memory gets fragmented then it all starts falling apart.
;
func_install:
;------------
	push ds ! push es
;
;	Shrink the previously allocated memory block to MEM_CURRENT
;	in preparation of the INSTALL EXEC function.
;
	mov	es,mem_current_base	; ES: Base Allocated Memory
	mov	bx,mem_current		; Get the currently allocated memory
	sub	bx,mem_current_base	;  and subtract mem_current_base to
	mov	ah,MS_M_FREE		;  give the number of paragraphs used
	 jz	func_i10		; if none, free it all up	
	mov	ah,MS_M_SETBLOCK	; else modify block accordingly
func_i10:
	int	DOS_INT

; Now to protect the CONFIG code in high memory
	mov	ah,MS_M_ALLOC		; we now try to find base of TPA
	mov	bx,0ffffh		; ASSUME it is the biggest bit
	int	21h			;  of free memory about
	mov	ah,MS_M_ALLOC		; give it to me please
	int	21h			; ax:0 -> exec memory
	push	bx			; we have allocated BX para's
	mov	es,ax			; ES -> exec memory
	mov	bx,init_dseg		; we want to protect BX:0 and above
	dec	bx			; allow for DMD
	sub	bx,ax			; we can spare this many paras
	mov	ah,MS_M_SETBLOCK	;  for the exec so grow the 
	int	21h			;  block accordingly
	pop	ax			; AX = total, BX = amount for install

	sub	ax,bx			; AX = amount we just freed
	dec	ax			; allow for DMD
	xchg	ax,bx			; BX = freed portion (the Init code)
	mov	ah,MS_M_ALLOC		; ASSUME an allocation of this size
	int	21h			;  will contain SYSDAT/CONFIG
	push	ax			; save seg so we can free mem later

	mov	ah, MS_M_FREE		; now free up the bit we prepared
	int	21h			;  earlier so exec has something
	push ds ! pop es		;  to work in

	mov	di,offset dev_name	; Copy the filename into a local
	call	copy_file		; buffer

					; Calculate the command line length
	mov	di,si			; by scanning the command line for
	mov	al,CR			; the terminating CR
	mov	cx,128
	repnz	scasb
	dec	di
	mov	ax,di
	sub	ax,si
	dec	si			; Point to the byte before the 
	mov	byte ptr [si],al	; command line string and update 
					; the count	

	mov	ax,offset envstart	; env buffer is para aligned
	mov	cl,4
	shr	ax,cl			; convert offset to paras
	mov	cx,ds
	add	ax,cx			; segment of env variables
	mov	exec_envseg,ax
	mov	exec_lineoff,si
	mov	exec_lineseg,ds
	mov	exec_fcb1off,0FFFEh	  ; Force PCMODE to generate
	mov	exec_fcb2off,0FFFEh	  ; correct FCB References

	mov	system_ss,ss
	mov	system_sp,sp

	mov	dx,offset dev_name	; Get ASCIIZ Command
	mov	bx,offset exec_envseg	; and Parameter Block Offset
	mov	ax,4B00h		; Load and Execute Program with Handle
	int	DOS_INT			; EXEC the application

	cli				; Swap back to the original stack
	mov	ss,cs:system_ss		; again with interrupts disabled
    mov sp,cs:system_sp
	sti

	mov	ah,MS_X_WAIT		; if all went well return the
	 jnc	func_i20		;  termination code
	mov	ah,MS_F_ERROR		; if we had an error from EXEC
	xor	bx,bx			;  get extended error
func_i20:
	int	DOS_INT			; retrieve error value
	mov	cs:error_level,ax	;  and save for testing

	pop	es			; recover the seg we protected
	mov	ah, MS_M_FREE		; so we can free that memory up
	int	21h
	
	mov	ah,MS_M_ALLOC		; try and allocate as much as possible
	mov	bx, 0FFFFh		; ASSUME this will cover CONFIG
	int	21h			; bx = No. of paras available
	mov	ah, MS_M_ALLOC		;  give me bx paras please
	int	21h			;  ax:0 -> my memory

	pop es ! pop ds
	mov	mem_current_base,ax	; save memory base
	mov	mem_current,ax		; for future allocations
	ret



func_hidos:		; HIDOS ON/OFF
	call	check_onoff		; Check for ON or OFF
	 jc	f_hidos10		; Return on error
	mov	hidos,al		; update hidos flag
f_hidos10:
	ret


func_dos:		; DOS=HIGH - relocate BIOS/BDOS/Buffer etc to FFFF
	call	separator		; Deblank Command
	mov	di,offset high_opt	; es:di -> "HIGH"
	call	compare			; do we have an "HIGH"?
	 jc	func_dos10
	push	si
	call	func_dos_high		; execute HIGH
	pop	si
	jmps	func_dos
func_dos10:
	mov	di,offset low_opt	; es:di -> "LOW"
	call	compare
	 jc	func_dos20
	push	si
	call	func_dos_low		; execute LOW
	pop	si
	jmps	func_dos
func_dos20:
	mov	di,offset umb_opt	; es:di -> "UMB"
	call	compare
	 jc	func_dos30
	push	si
	call	func_dos_umb		; execute UMB
	pop	si
	jmps	func_dos
func_dos30:
	mov	di,offset noumb_opt	; es:di -> "NOUMB"
	call	compare
	 jc	func_dos40
	push	si
	call	func_dos_noumb		; execute NOUMB
	pop	si
	jmps	func_dos
func_dos40:
	ret

func_dos_high:
;-------------
; Move DOS into the HMA and allocate buffers etc. high too.
;
	mov	dos_target_seg,0FFFFh
	mov	bios_target_seg,0FFFFh
	mov	hidos,TRUE		; update hidos flag to be ON
	or	buffersIn,BUFFERS_IN_HMA; buffers at seg FFFF too
	ret

func_dos_low:
;------------
; force all allocation to be low
;
	mov	dos_target_seg,0
	mov	bios_target_seg,0
	mov	hidos,FALSE		; system allocation from low memory
	mov	buffersIn,0		; buffers from low memory
	ret

func_dos_umb:
;------------
; allocate Upper Memory Blocks and link them to the DMD chain
;
	mov	hidos,TRUE		; update hidos flag to be ON
	call	initialise_dmd_upper	; build initial upper memory DMD
	 jc	func_dos_umb30
func_dos_umb10:
	call	alloc_xms_umb		; allocate XMS upper memory
	 jc	func_dos_umb20
	call	add_dmd_upper		; add to upper memory DMD's
	jmps	func_dos_umb10		;  go around again
func_dos_umb20:
	call	remove_last_dmd		; get rid of useless last DMD
func_dos_umb30:
	mov	ax,(MS_M_STRATEGY*256)+3
	mov	bx,1			; link in upper memory region
	int	21h
	ret

func_dos_noumb:
;--------------
; Unlink Upper Memory blocks from the DMD chain
;
	mov	ax,(MS_M_STRATEGY*256)+3
	xor	bx,bx			; unlink upper memory region
	int	21h
	ret

alloc_xms_umb:
; On Entry:
;	None
; On Exit:
;	CY set is no upper memory available
; else
;	BX = para base address
;	DX = para size
;
; Try to allocate the largest possible block of XMS memory
; so we can link it to the upper memory chain
;
	push	es
	mov	ax,4300h		; check for XMS installation
	int	2fh
	cmp	al,80h
	 jne	alloc_xms10
	mov	ax,4310h		; get address of XMS driver
	int	2fh
	mov	word ptr xms_driver,bx
	mov	word ptr xms_driver+2,es
	mov	ah,10h			; allocate upper memory block
	mov	dx,0FFFFh		; DX set to find largest block
	callf	xms_driver
	cmp	dx,3			; we need at least 3 para's
	 jb	alloc_xms10		;  before we contruct a DMD
	mov	ah,10h			; now allocate largest block
	callf	xms_driver
	cmp	ax,1			; did we succeed ?
	 je	alloc_xms20
alloc_xms10:
	stc				; return CY set indicating failure
alloc_xms20:
	pop	es
	ret

xms_driver	rd	0
	dw	0,0

initialise_dmd_upper:
; On Entry:
;	None
; On Exit:
;	CY set if chain already exists
;	(BX/DX preserved)
;
; build initial upper memory DMD
; we rely on the fact the last para in memory is unused
; (but BIOSINIT makes sure that is true)
;
	push	es
	push	bx
	push	dx
if DOS5
	les	bx,func52_ptr		; ES:BX -> list of lists
	cmp	es:F52_DMD_UPPER[bx],0FFFFh
else
	les	bx,drdos_ptr
	cmp	es:DRDOS_DMD_UPPER[bx],0
	les	bx,funv52_ptr		; ES:BX -> list of lists
endif
	stc				; assume error return required
	 jne	initialise_dmd_upper30	; bail out if chain already established
	mov	es,es:F52_DMDROOT	; ES -> 1st DMD
initialise_dmd_upper10:
	cmp	es:DMD_ID,IDZ		; end of DMD chain ?
	 je	initialise_dmd_upper20
	cmp	es:DMD_ID,IDM		; do we have any more DMD's ?
	stc
	 jne	initialise_dmd_upper30 	; woops, chain must be bad
	mov	ax,es			; better point to it
	inc	ax
	add	ax,es:DMD_LEN		; AX:0 -> next DMD
	mov	es,ax
	jmps	initialise_dmd_upper10

initialise_dmd_upper20:
	mov	ax,es
	add	ax,es:DMD_LEN		; AX:0 -> will be upper memory chain
	cmp	ax,0A000h		; if the DMD chain is already into
	cmc				;  upper memory, lets make sure we
	 jb	initialise_dmd_upper30	;  stop before we fall apart
	mov	es:DMD_ID,IDM		; no longer the last entry
	dec	es:DMD_LEN		; shorten last DMD to make room
	mov	es,ax			; point to new DMD
	mov	es:DMD_ID,IDZ		; there is only one entry in the chain
	mov	es:DMD_PSP,8		; its' owned by "system"
	xchg	ax,cx			; CX = DMD
	mov	ax,0FFFFh
	sub	ax,cx			; it's this big
	mov	es:DMD_LEN,ax
if DOS5
	les	bx,func52_ptr
	mov	es:F52_DMD_UPPER[bx],cx
else
	les	bx,drdos_ptr
	mov	es:DRDOS_DMD_UPPER[bx],cx
endif
	clc
initialise_dmd_upper30:
	pop	dx
	pop	bx
	pop	es
	ret


remove_last_dmd:
; On Entry:
;	None
; On Exit:
;	None
;
; We have build an upper memory DMD chain, but we have left an extra
; DMD around covering the ROMs at the top of memory. Remove it if
; it's not required.
;
	push	es
	les	bx,func52_ptr		; ES:BX -> list of lists
	mov	es,es:F52_DMDROOT[bx]	; ES -> 1st DMD
remove_last_dmd10:
	cmp	es:DMD_ID,IDM		; do we have any more DMD's ?
	 jne	remove_last_dmd20	; bail out if we don't
	mov	ax,es			; remember previous DMD
	mov	dx,es
	inc	dx
	add	dx,es:DMD_LEN		; DX:0 -> next DMD
	mov	es,dx
	cmp	es:DMD_ID,IDZ		; end of DMD chain ?
	 jne	remove_last_dmd10
	cmp	es:DMD_PSP,8		; is it owned by "system" ?
	 jne	remove_last_dmd20	;  if so we can ditch this entry
	mov	es,ax			; ES = next to last DMD
	mov	es:DMD_ID,IDZ		; new end of chain
	inc	es:DMD_LEN		; include last para
if DOS5
	les	bx,func52_ptr		; ES:BX -> list of lists
	cmp	dx,es:F52_DMD_UPPER[bx]
	 jne	remove_last_dmd20	; remove upper memory link if none left
	mov	es:F52_DMD_UPPER[bx],0FFFFh
else
	les	bx,drdos_ptr
	cmp	dx,es:DRDOS_DMD_UPPER[bx]
	 jne	remove_last_dmd20	; remove upper memory link if none left
	mov	es:DRDOS_DMD_UPPER[bx],0
endif
remove_last_dmd20:	
	pop	es
	ret


add_dmd_upper:
; On Entry:
;	BX = base address of DMD
;	DX = size of DMD
; On Exit:
;	None
;
; Add this block into the upper memory chain.
; To do this we find the DMD containing the block and link it into place
;
	push	es
	push	bx			; save base address
	les	bx,func52_ptr		; ES:BX -> list of lists
	mov	ax,es:F52_DMDROOT[bx]	; AX -> 1st DMD
	pop	bx			; 1st DMD is always below XMS
	cmp	ax,bx			;  memory, so bomb out if not
	 jae	add_dmd_upper40		;  as our DMD's must be corrupt
add_dmd_upper10:
	mov	es,ax
	add	ax,es:DMD_LEN		; AX:0 -> end of this block
	cmp	ax,bx			; is the next block above us ?
	 ja	add_dmd_upper20		; if not try the next block
	inc	ax			; AX:0 -> next DMD
	cmp	es:DMD_ID,IDM		; do we have any more DMD's ?
	 je	add_dmd_upper10		;  we should have......
	jmps	add_dmd_upper40		; stop, DMD's are screwed up

add_dmd_upper20:
; We have found the block we wish to insert a new free block into
	cmp	es:DMD_PSP,8		; it must be owned by "system"
	 jne	add_dmd_upper40
; Shorten existing DMD to point to new block
	mov	ax,bx			; work out how far to new DMD
	mov	cx,es
	sub	ax,cx			; it's this many para's
	dec	ax			; forget the header
	xchg	ax,es:DMD_LEN		; set new length
; now we need to work out how much is left above the new DMD
	sub	ax,dx			; subtract length of new block
	sub	ax,es:DMD_LEN		; subtract the portion below
; Create DMD covering new block
	mov	cl,IDM			; create a new entry
	xchg	cl,es:DMD_ID		; CL = existing ID (M/Z)
	mov	es,bx			; ES -> base of new DMD
	mov	es:DMD_ID,IDM		; it's a link field
	mov	es:DMD_PSP,0		; it's free
	dec	dx			; forget the header
	add	bx,dx			; last para is here
	dec	dx			; forget the next link
	mov	es:DMD_LEN,dx		; it's this long
; Build a new DMD at the top if the new block for anything above it
	mov	es,bx
	mov	es:DMD_ID,cl		; inherit the ID field
	mov	es:DMD_LEN,ax		; and it's this long
	test	ax,ax			; if zero length then
	 jz	add_dmd_upper30		;  it's free
	mov	ax,8			; else it's system
add_dmd_upper30:
	mov	es:DMD_PSP,ax		; set owner
add_dmd_upper40:
	pop	es
	ret




func_set:		; SET envar=string
	call	whitespace		; deblank the command
	mov	di,offset envstart-1	; point to our environment area
func_set5:
	inc	di
	cmp	es:word ptr [di],0	; are we at the end yet
	 jne	func_set5
	cmp	di,offset envstart	; if nothing is there yet start
	 je	func_set10		;  at the NUL, else skip the NUL
	inc	di			;  to leave a seperator
func_set10:
	lodsb				; get a character
	cmp	al,CR			; end of the line yet ?
	 je	func_set20
	cmp	di,offset envend	; have we room ?
	 jae	func_set30		; bail out if not
	stosb				; save the character
	jmps	func_set10
func_set20:
	xor	ax,ax			; terminate with NULL
	stosb
func_set30:
	ret
endif					;not ADDDRV


func_echo:		; ECHO "string"	
	call	whitespace		; Scan off all white space
	lodsb				; before the optional
	cmp	al,'='			; '=' character.
	 je	func_echo10
	dec	si			; point at char
func_echo10:
	mov	dx,offset msg_dollar	; NUL error message
	mov	al,CR			; SI -> config line anyway
	jmp	config_error		; use error reporting routine

func_yeschar:		; yeschar "string"	
	call	whitespace		; Scan off all white space
	lodsb				; before the optional
	cmp	al,'='			; '=' character.
	 je	func_yeschar10
	dec	si			; point at char
func_yeschar10:
	call	whitespace
	lodsb
	mov	yes_char,al		; update YES character
	ret

func_chain:		; CHAIN="filename" - use as new CONFIG.SYS
	mov	di,offset dev_name	; Copy the Device Filename into a 
	mov	byte ptr [di],0		; local buffer and zero terminate
	call	copy_file
	 jc	func_chain10		; ignore if any problems
	mov	ax,(MS_X_OPEN*256)+80h	; Try to open the file
	mov	dx,offset dev_name	; as a new config file
	int	DOS_INT			; if we can't ignore it
	 jc	func_chain10
	mov	bx,ax
	mov	ah,MS_X_CLOSE
	int	DOS_INT			; close the new file
	mov	si,offset dev_name
	mov	di,offset cfg_file
	call	copy_asciiz		; copy the new name
	mov	cfg_seeklo,0		; start at begining of it
	mov	cfg_seekhi,0
	mov	cfg_tail,0		; force a read
func_chain10:
	ret

func_switch:				; SWITCH=option0, option1
; GOSUB to appropriate label
	call	wait_for_key		; wait until a key is pressed
	 jc	func_switch01		; ignore if timeout
	mov	ah,MS_C_RAWIN
	int	DOS_INT			; read a char
	cmp	al,CR
	 jne	func_switch02
func_switch01:
	mov	al,default_switch_char	; use default character
func_switch02:
	cmp	al,'0'			; ignore if < '0'
	 jb	func_switch
	cmp	al,'9'			;  or > '9'
	 ja	func_switch
	sub	al,'1'			; convert from ASCII
	 jns	func_switch05
	mov	al,10			; make '0' into 10
func_switch05:
	cbw				; AX = lines to skip
	xchg	ax,cx			; make CX the loop count
	 jcxz	func_switch30
	mov	bx,si			; BX -> saved command line start
func_switch10:
	push	bx
	push	cx
	mov	di,offset dev_name	; copy and discard a label
	call	copy_file
	pop	cx
	pop	bx
	 jc	func_switch40		; ignore if any problems
	push	bx
	push	cx
	call	separator		; look for ','
	pop	cx
	pop	bx
	 jc	func_switch40		; stop at end of line
	loop	func_switch10
func_switch30:
	jmp	func_gosub		; execute a GOSUB

func_switch40:
	mov	si,bx			; retract to start of line
	jmps	func_switch		;  then back to sleep again

func_gosub:		; GOSUB="label"
;----------
	pop	ax			; get return address
	mov	bx,cfg_seeklo		; get existing offset
	mov	cx,cfg_seekhi		;  in CONFIG file
	sub	bx,cfg_tail		; work out begining of buffer
	sbb	cx,0
	add	bx,cfg_head		; add in current offset in buffer
	adc	cx,0
	push	bx			; save as position to RETURN to
	push	cx
	push	ax			; save return address again
	call	func_goto		; try to GOTO label
	 jc	func_return
	ret				; RET, with old offset on stack

func_return:		; RETURN [n]
;-----------
	pop	bx			; get return address
	cmp	sp,save_sp		; is anything on stack ?
	 jae	func_return20		; no, cannot RETURN
	pop	cfg_seekhi
	pop	cfg_seeklo		; restore position in file
	mov	cfg_tail,0		; force a read
	push	bx
	call	atoi			; returning a value ?
	pop	bx
	 jnc	func_return10		; default to 0
	xor	ax,ax
func_return10:
	mov	error_level,ax		; return result in error level
func_return20:
	push	bx			; save return address
	ret				;  and return to it

func_goto:		; GOTO="label"
;---------
	mov	di,offset dev_name	; Copy the label into a 
	mov	byte ptr [di],0		; local buffer and zero terminate
	call	copy_file
	 jc	func_goto10		; ignore if any problems

	mov	cfg_seeklo,0		; Seek to start of file
	mov	cfg_seekhi,0
	mov	cfg_tail,0		; force a re-read

func_goto5:
	call	readline		; read in a line
	 jc	func_goto10		; stop if end of file
	call	strupr			; upper case possible label
	mov	bx,offset cfg_buffer
	cmp	ds:byte ptr [bx],':'		; is it a label ?
	 jne	func_goto5		; no, try next line
	mov	si,offset dev_name
func_goto6:
	inc	bx			; next char in possible label
	lodsb				; get a character
	test	al,al			; end of label ?
	 je	func_goto10		; we have a match !
	cmp	al,ds:byte ptr [bx]	; does it match
	 jne	func_goto5		; no, try next line
	jmps	func_goto6		; yes, look at next character
func_goto10:
	ret

func_exit:
; Stop processing CONFIG.SYS
	call	readline		; read in a line
	 jnc	func_exit		;  until we can read no more..
	ret

func_cls:
; CLEAR SCREEN
; This is PC specific - sorry
	mov	ah,15			; get current
	int	10h			; screen mode
	mov	ah,0
	int	10h			; reset it to clear screen
	ret

func_cpos:
; Set cursor position
	call	atoi			; AX = row
	 jnc	func_cpos10		; check for error
	xor	ax,ax			; default to top left
	jmps	func_cpos40
func_cpos10:
	push	ax			; save row
	call	separator		; look for ','
	 jc	func_cpos20		; no col specified
	call	atoi			; get col
	 jnc	func_cpos30
func_cpos20:
	mov	ax,1			; default to left
func_cpos30:
	pop	dx
	mov	ah,dl			; AH = row, AL = col
	sub	ax,0101h		; compensate for being one based
func_cpos40:
	xchg	ax,dx			; DH = row, DL = col
	xor	bx,bx			; page zero
	mov	ah,2			; set cursor position
	int	10h			; Eeeek!! call the ROS
	ret

func_timeout:
; set TIMEOUT for keyboard input
	call	atoi			; AX = # timeout count
	 jnc	func_timeout10		; check for error
	xor	ax,ax			; bad values mean no timeout
func_timeout10:
	mov	keyb_timeout,ax		; save timeout count
	call	separator		; look for ','
	 jc	func_timeout20
	lodsb				; get default query char
	cmp al,LF ! je func_timeout20
	cmp al,CR ! je func_timeout20
	mov	default_query_char,al
	call	separator		; look for ','
	 jc	func_timeout20
	lodsb				; get default switch char
	cmp al,CR ! je func_timeout20
	cmp al,LF ! je func_timeout20
	mov	default_switch_char,al
func_timeout20:
	ret

func_error:
; ERROR='n'
	call	atoi			; AX = error count to match
	 jc	func_error10
	mov	error_level,ax		; set error level
func_error10:
	ret

func_onerror:
; ONERROR='n' optional command
;
	call	whitespace		; Scan off all white space
	xor	bx,bx			; index relationship = 1st item
	xor	dx,dx			; DX is bit to set
func_onerror10:
	or	bx,dx			; set reationship bit
	lodsb				; now process a character
	mov	dx,2
	cmp	al,'='			; if '=' set bit 1
	 je	func_onerror10
	mov	dx,4
	cmp	al,'<'			; if '<' set bit 2
	 je	func_onerror10
	mov	dx,8
	cmp	al,'>'			; if '>' set bit 3
	 je	func_onerror10
	dec	si			; point at char
	push	bx			; save relationship
	call	atoi			; AX = error count to match
	pop	bx			; recover relationship
	 jc	func_onerror20
	cmp	error_level,ax		; is it the error level we want ?
	jmp	func_onerror_tbl[bx]	; jump to handler
func_onerror20:
	ret

func_onerror_tbl:
	dw	func_onerror_eq		; . . .
	dw	func_onerror_eq		; . . =
	dw	func_onerror_lt		; . < .
	dw	func_onerror_le		; . < =
	dw	func_onerror_gt		; > . .
	dw	func_onerror_ge		; > . =
	dw	func_onerror_ne		; > < .
	dw	func_onerror_take	; > < =
	
func_onerror_eq:
	 je	func_onerror_take
	ret

func_onerror_ne:
	 jne	func_onerror_take
	ret

func_onerror_lt:
	 jb	func_onerror_take
	ret

func_onerror_le:
	 jbe	func_onerror_take
	ret

func_onerror_gt:
	 ja	func_onerror_take
	ret

func_onerror_ge:
	 jae	func_onerror_take
	ret

func_onerror_take:
	pop	ax			; discard return address
	xor	ax,ax			; boot key options = none
	jmp	cfg_continue		; and execute this command



func_query:
; ?optional command
	cmp	boot_options,F5KEY	; if F5 has been pressed then
	 je	func_query50		;  do nothing
	call	whitespace		; discard any following whitespace
	lodsb				; get a character	
	cmp	al,'?'			; is it another '?', is so swallow it
	 je	func_query		;  and go round again
	dec	si			; it wasn't a '?', forget we looked
	push	si			; save current position
	lodsb				; get next real char
	xor	cx,cx			; assume no prompt string
	cmp	al,'"'			; '?"user prompt"' - keep silent as
	 jne	func_query10		; user has supplied prompt
	xchg	ax,cx			; CL = " if user prompt
	lodsb
func_query10:
	cmp	al,cl			; is this the user prompt char ?
	 je	func_query20		;  then stop now
	xchg	ax,dx			; DL= character
	mov	ah,MS_C_WRITE
	int	DOS_INT			; output the character
	lodsb
	cmp	al,CR			; is it the end of the line ?
	 jne	func_query10		;  no, do another character
	mov	ah,MS_C_WRITESTR	; Output msg of form " (Y/N) ? "
	mov	dx,offset confirm_msg1
	int	DOS_INT			; do " ("
	mov	ah,MS_C_WRITE
	mov	dl,yes_char
	int	DOS_INT			; do "Y"
	mov	dl,'/'
	int	DOS_INT			; do "/"
	mov	dl,no_char
	int	DOS_INT			; do "N"
	mov	ah,MS_C_WRITESTR
	mov	dx,offset confirm_msg2
	int	DOS_INT			; do ") ? "
func_query20:
	 jcxz	func_query30		; if no user supplied prompt
	pop	ax			; don't discard original starting
	push	si			;  position
func_query30:
	call	wait_for_key		; wait until a key is pressed
	mov	al,default_query_char	; if we timeout default
	 jc	func_query40
	mov	ah,MS_C_RAWIN
	int	DOS_INT			; read a char
	test	al,al			; is it a function key ?
	 jnz	func_query40
	mov	ah,MS_C_RAWIN
	int	DOS_INT			; throw away function keys
    jmps    func_query30
func_query40:
	push	ax			; save response
	mov	ah,MS_C_WRITE
	mov	dl,al
	int	DOS_INT			; echo the char
	mov	ah,MS_C_WRITESTR
	mov	dx,offset confirm_msg3
	int	DOS_INT			; now do CR/LF to tidy up
	pop	ax
	call	toupper			; make response upper case
	pop	si			; recover starting position
	cmp	al,yes_char
	 jne	func_query50
	pop	ax			; Discard Return Address
	xor	ax,ax			; boot key options = none
	jmp	cfg_continue		; Execute the command
func_query50:
	ret				; Return without Executing Command



func_getkey:				; GETKEY
	call	wait_for_key		; wait until a key is pressed
	mov	ax,CONFIG_ERRLVL	; assume we have timed out
	 jc	func_getkey10		; ignore if timeout
	mov	ah,MS_C_RAWIN
	int	DOS_INT			; read a char
	xor	ah,ah			; convert to word
func_getkey10:
	mov	error_level,ax
	ret
	
	
;	CONFIG_ERROR is the global error handler for the CONFIG.SYS
;	commands. It is called with SI pointing to the CR/LF terminated string
;	that caused the error and with DX pointing to an "informative" error
;	message.
;
;	On Entry:- 	AL	Terminating Character
;			DX	Offset of Error Message
;			SI	0000 No Message to display
;				Offset of AL terminated string
;
config_error:

if ADDDRV
	mov	error_flag,1
endif

	push	ax
	mov	ah,MS_C_WRITESTR	; Print the Error Message
	int	DOS_INT			; passed in DX
	pop	ax

	mov	ah,al			; AH = terminating character
	test	si,si			; display the failing string ?
	 jz	cfg_e20			;  YES then scan for terminator
cfg_e10:
	lodsb				; get char to display
	cmp	al,ah			; have we reached the terminator ?
	 je	cfg_e20
	xchg	ax,dx			; DL = character to display
	mov	ah,MS_C_WRITE		; print a character at a time
	int	DOS_INT
	xchg	ax,dx			; terminator back in AH
	jmps	cfg_e10
cfg_e20:
;;	jmp	crlf			; Terminate with a CRLF

	Public	crlf
crlf:
	push	dx
	mov	dx,offset msg_crlf	; Print a CR LF
	mov	ah,MS_C_WRITESTR
	int	DOS_INT
	pop	dx
	ret

;
;	Scan the command table for a match with the first entry in the
;	CR/LF terminated string passed in SI
scan:
	call	whitespace		; scan off all white space
	push	bx			; save the CONFIG Handle
	mov	bx,offset cfg_table - CFG_SIZE

scan_10:
	add	bx,CFG_SIZE		; bx -> next entry in table
	mov	di,CFG_NAME[bx]		; es:di -> next entry name
	test	di,di			; end of table ?
	stc				; assume so
	 jz	scan_exit		; Yes Exit with the Carry Flag Set
	push	si			; Save the String Offset
	call	compare
	pop	ax			; Remove String Address
	 jnc	scan_20			; String Matched
	xchg	ax,si			; Restore the original String Address
	jmps	scan_10			; and test the next entry

scan_20:
	and	CFG_FLAGS[bx],not CF_QUERY
	test	CFG_FLAGS[bx],CF_LC	; should we upper case line ?
	 jnz	scan_50			;  skip if not
	xchg	ax,si
	call	strupr			; upper case the command line
	xchg	ax,si
scan_30:
	call	whitespace		; Scan off all white space before and
	lodsb				;  after the option '=' character
	cmp	al,'?'			; are we querying things ?
	 jne	scan_40
	or	CFG_FLAGS[bx],CF_QUERY	; remember the query, now go and
	jmps	scan_30			;  remove any other whitespace
scan_40:
	cmp	al,'='			; '=' character.
	 je	scan_30
	dec	si
scan_50:
	mov	di,bx			; Save the Table Entry
	xor	ax,ax			; and exit with the Carry Flag Reset
scan_exit:
	pop	bx
	ret

; Compare two strings in case insensitive manner
; On Entry:
;	ds:si -> String 1 (upper/lower case, length determined by string 2)
;	es:di -> String 2 (uppercase, null terminated)
; On Exit:
;	Carry clear:	strings are the same
;	ds:si -> character immediately following end of string 1
;	es:di -> character immediately following end on string 2
;
;	Carry set:	strings different
;	ds:si -> As on entry
;	es:di -> undefined
;
compare:
;-------
	push	bx
	push	si			; save starting position
compare10:
	mov	al,es:[di]		; al = next character 
	inc	di
	test	al,al			; end of string 2 yet ?
	 jz	compare40		; yes, strings must be equal
	call	dbcs_lead		; DBCS lead byte?
	 jnz	compare20		;  no
	mov	ah,al
	lodsb				; is 1st byte of pair the same ?
	cmp	al,ah
	 jne	compare30
	cmpsb				; is 2nd byte of pair equal ?
	 jne	compare30
	jmps	compare10
compare20:
	call	toupper			; just uppercase this byte
	xchg	ax,bx			; BL = string2 character
	lodsb				; al = next char in string 1
	call	toupper			; (can't be KANJI if it matches)
	cmp	al,bl			; check the characters are
	 je	compare10		;  identical stop the compare 
compare30:
	stc				; on a mismatch and set CY
compare40:
	pop	bx			; recover starting position
	 jnc	compare50
	mov	si,bx			; SI = original start
compare50:
	pop	bx
	ret


separator:
;---------
; On Entry:
;	DS:SI -> string
; On Exit:
;	DS:SI -> next option
;	CY set if end of line
;
; Strips off all whitespace, and the optional ','
; CY set at end of line
	call	whitespace			; deblank string and
	lodsb					;  check for ',' separator
	cmp	al,','				;  discarding if found
	 je	separator10
	cmp	al,CR				; end of the line ?
	stc					; assume so
	 je	separator10
	dec	si				; something else, leave alone
	clc					; not end of line
separator10:
	ret

separator20:
	call	whitespace			; strip of following spaces
	clc					; not end of line
	ret


strupr:
;------
; Uppercase a null terminated string.
; Entry
;	ds:si ->	null terminated string
; Exit
;	none		(string is uppercased)
; Lost
;	no registers changed


	push	si
	push	ax

spr_loop:
	mov	al, [si]		; al = next byte from string
	test	al, al			; end of string?
	jz	spr_done		;  yes - exit
	
;	cmp	al,' '			; BAP. End at first space
;	je	spr_done		; or comma or slash
;	cmp	al,','			; so that parameters
;	je	spr_done		; are not uppercased
;	cmp	al,'/'			; Took out again cos it caused
;	je	spr_done		; problems with labels (I think).

	call	dbcs_lead		; DBCS lead byte?
	jnz	spr_not_dbcs		;  no
	inc	si			;  yes - skip first and second bytes of
	inc	si			;  pair as they cannot be uppercased
	jmp	spr_loop		; loop round
spr_not_dbcs:

	call	toupper			; just uppercase this byte
	mov	[si], al		; return the result to the string
	inc	si
	jmp	spr_loop		; continue

spr_done:
	pop	ax
	pop	si
	ret


dbcs_lead:
;---------
; Return true if given byte is the first of a double byte character.
; Entry
;	al 	= byte to be tested
; Exit
;	Z Flag	= 1 - byte is a DBCS lead
;		  0 - byte is not a DBCS lead
; Lost
;	no registers changed


	push	ds
	push	si
	push	bx
	push	ax

; First get a pointer to the double byte lead table in the COUNTRY info.
	lds	si, dbcs_tbl		; ds:si -> double byte table
	inc	si
	inc	si			; skip table length

; Examine each entry in the table to see if it defines a range that includes
; the given character.
	mov	bl, al			; bl = byte to be tested
dbcs_loop:
	lodsw				; al/ah = start/end of range
	test 	ax, ax			; end of table?
	jz	dbcs_no			;  yes - exit (not in table)
	cmp	al, bl			; start <= bl?
	ja	dbcs_loop		;  no - try next range
	cmp	ah, bl			; bl <= end?
	jb	dbcs_loop		;  no - try next range

	cmp	al, al			; return with Z flag set
	jmp	dbcs_exit

dbcs_no:
	cmp	al, 1			; return with Z flag reset

dbcs_exit:
	pop	ax
	pop	bx
	pop	si
	pop	ds
	ret



toupper:
;-------
; Return the uppercase equivilant of the given character.
; The uppercase function defined in the international info block is 
; called for characters above 80h.
; Entry
;	al = character to uppercase
; Exit
;	al uppercased
; Lost
; 	no registers lost

	push	bx
	mov	bh, ah
	mov	ah, 0			; ax = character to be converted
	cmp	al, 'a'			; al < 'a'?
	jb	exit_toupper		;  yes - done (char unchanged)
	cmp	al, 'z'			; al <= 'z'?
	jbe	a_z			;  yes - do ASCII conversion
	cmp	al, 80h			; international char?
	jb	exit_toupper		;  no - done (char unchanged)

; ch >= 80h  -- call international routine
	callf	dword ptr ctry_info+CI_CASEOFF
	jmp	exit_toupper

a_z:
; 'a' <= ch <= 'z'  -- convert to uppercase ASCII equivilant
	and	al, 0DFh

exit_toupper:
	mov	ah, bh
	pop	bx
	ret




;
;	Scan the string DS:SI for ON or OFF return with the carry flag set
;	on error or AL = 1 for ON and AL = 0 for OFF.
;
check_onoff:
	call	whitespace		; Deblank Command
	push	si
	mov	di,offset cmd_on	; es:di -> "ON"
	call	compare			; do we have an "ON"?
	mov	al,01			; Assume ON found
	jnc	chk_onoff10

	pop si ! push si		; Save String Location in Case of Error
	mov	di,offset cmd_off	; es:di -> "OFF"
	call	compare			; do we have an "OFF"?
	mov	al,00
	jnc	chk_onoff10		
	pop	si			; No match so return original address
	stc				; with the CARRY falg set.
	ret

chk_onoff10:
	pop	di			; Remove Old String address
	ret				; and return to caller 

atohex:
;------
; To convert a hex number in the form of an ASCII string to a 32 bit 
; integer.
;
; On Entry:
;	DS:SI -> ASCII hex number 
;		 (the end of the number is taken as the first non-digit)
; On Exit:
;	CY clear:	
;		DX:AX = converted number
;		ds:si -> first non-digit
;
;	CY set:	
;		Either the first character was not a digit
;		or the number could not be represented in 32 bits
;		ds:si -> point at which error occured
;		ax undefined
; Lost
;	no other register

	push	bx
	push	cx
	push	di
	call	whitespace			; Deblank Line
	mov	di,si				; save string start offset
	xor	dx,dx
	xor	bx,bx				; number is formed in DX:BX

atohex10:
	lodsb					; AL = next char from string
	call	toupper				; upper case it
	cmp	al,'A'
	 jb	atohex20
	cmp	al,'F'
	 ja	atohex20
	sub	al,'A'-10
	jmps	atohex30
atohex20:
	sub	al, '0'
	 jc	atohex40			; stop if invalid character
	cmp	al, 9
	 ja	atohex40
atohex30:
	cbw					; AX = digit
	test	dh,0f0h				; will we overflow ?
	 jnz	atohex_error
	mov	cl,4
	push	bx				; save (top 4 bits)
	shl	bx,cl				; *16
	add	bx,ax				; add in new digit
	pop	ax
	rol	ax,cl				; top 4 bits to bottom 4 bits
	and	ax,000Fh			; isolate them
	shl	dx,cl
	add	dx,ax				; add in new digit
	jmp	atohex10

atohex40:
	dec	si				; forget the char we stopped on
	cmp	si, di				; was there at least one digit?
	 ja	atohex50			;  yes - exit with carry clear
atohex_error:
	stc					; set error flag
atohex50:
	xchg	ax,bx				; AX = result
	pop	di
	pop	cx
	pop	bx
	ret

atoi:
;----
; To convert a decimal number in the form of an ASCII string to a 16 bit 
; integer.
;
; Entry
;	ds:si -> ASCII decimal number 
;		 (the end of the number is taken as the first non-digit)
; Exit
;	Carry clear:	
;		ax = converted number
;		ds:si -> first non-digit
;
;	Carry set:	
;		Either the first character was not a digit
;		or the number could not be represented in 16 bits
;		ds:si -> point at which error occured
;		ax undefined
; Lost
;	no other register

	push bx ! push cx
	push dx ! push di
	call	whitespace			; Deblank Line
	mov	di, si				; save string start offset
	mov	cx, 10				; for multiply
	xor	ax, ax				; number is formed in ax

atoi_loop:
	mov	bl, [si]			; bl = next char from string
	sub	bl, '0'
	jc	atoi_done
	cmp	bl, 9
	ja	atoi_done
	mov	bh, 0				; bx = next digit

	mul	cx				; ax = 10 * ax
	jc	exit_atoi			; check for 16 bit overflow
	add	ax, bx				; ax = (10 * ax) + bx
	jc	exit_atoi
	
	inc	si				; ds:si -> next char in string
	jmp	atoi_loop

atoi_done:
	cmp	si, di				; was there at least one digit?
	jne	exit_atoi			;  yes - exit with carry clear
	stc					;  no - set error flag

exit_atoi:
	pop di ! pop dx
	pop cx ! pop bx
	ret

atol:
;----
; To convert a decimal number in the form of an ASCII string to a 32 bit 
; integer.
;
; Entry
;	ds:si -> ASCII decimal number 
;		 (the end of the number is taken as the first non-digit)
; Exit
;	CY clear:	
;		DX:AX = converted number
;		ds:si -> first non-digit
;
;	CY set:	
;		Either the first character was not a digit
;		or the number could not be represented in 32 bits
;		ds:si -> point at which error occured
;		ax undefined
; Lost
;	no other register

	push	bx
	push	cx
	push	di
	call	whitespace			; Deblank Line
	mov	di, si				; save string start offset
	xor	ax, ax				; number is formed in
	cwd					;  DX/AX
	
atol10:
	xor	bx,bx				; use CX/BX for next digit
	xor	cx,cx
	mov	bl,[si]				; BL = next char from string
	sub	bl,'0'
	 jc	atol20
	cmp	bl, 9				; validate digit
	 ja	atol20
	add	ax,ax
	adc	dx,dx				; * 2
	 jc	atol30
	add	bx,ax				; * 2 + new digit
	adc	cx,dx
	 jc	atol30
	add	ax,ax
	adc	dx,dx				; * 4
	 jc	atol30
	add	ax,ax
	adc	dx,dx				; * 8
	 jc	atol30
	add	ax,bx				; * 10 + new digit
	add	dx,cx
	 jc	atol30
	inc	si				; ds:si -> next char in string
	jmp	atol10

atol20:
	cmp	si,di				; was there at least one digit?
	 jne	atol30				;  yes - exit with carry clear
	stc					;  no - set error flag
atol30:
	pop	di
	pop	cx
	pop	bx
	ret

readline:
;--------
; On Entry:
;	None
; On Exit:
;	DS:SI -> line in buffer
;	CY set if we have a problem (eg at EOF)
;
	mov	cx,CFG_BUF_LEN-2	; Read the next command line
	mov	di,offset cfg_buffer	; into the CFG_BUFFER
	mov	si,di			; Save the Destination String
					; address
read_l10:
	call	getchar			; al = next char from file
	cmp al,CR ! jz read_l10		; end of line ?
	cmp al,LF ! jz read_l10		; end of line ?
	cmp al,EOF ! jne read_l20	; end of file ?
	stc				; indicate a problem
	ret

read_l20:
	stosb				; put next char into the buffer
	call	getchar			; al = next char from file

	cmp al,EOF ! jz read_l30	; end of file ?
	cmp al,CR ! jz read_l30		; end of line ?
	cmp al,LF ! jz read_l30		; end of line ?
	loop	read_l20		; loop while space remains

; If we fall through to this point the line is too long. Make it a comment.
	mov	di, si			; ds:di -> start of buffer
	mov 	al, ';'
	stosb				; place ';' at buffer start
	mov	cx, 1			; get another one character
	jmps	read_l20		; loop until all of this line consumed

; At this point buffer contains a line of text from CCONFIG.SYS.
; Terminate it properly
read_l30:
	mov al,CR ! stosb		; terminate line with CR
	mov al,LF ! stosb		; and a LF
	xor al,al ! stosb		; Reset the Carry Flag
	ret

getchar:
	mov	bx,cfg_head		; we are here in the buffer
	cmp	bx,cfg_tail		; are there any more characters ?
	 jae	getchar10		; no, read some in from disk
	push	ds
	mov	ds,init_dseg
	mov	al,CONFIG_BUF[bx]	; get a character from the buffer
	pop	ds
	inc	cfg_head		; inc the pointer
	ret

getchar10:
; we need to read some characters from disk into our buffer
	push cx ! push dx		; Assume something will go wrong
	mov	cfg_tail,0		;  say nothing is in the buffer

	mov	ax,(MS_X_OPEN*256)+80h	; Open the configuration file
	mov	dx,offset cfg_file
	int	DOS_INT
	 jc	getchar40		; failure, return EOF

	mov	bx,ax
	mov	ax,(MS_X_LSEEK*256)+0
	mov	dx,cfg_seeklo
	mov	cx,cfg_seekhi
	int	DOS_INT			; seek to current file position
	 jc	getchar30		; failure to seek, close and exit

	mov	ah,MS_X_READ
	mov	cx,CONFIG_BUF_SIZE
	push	ds
	mov	ds,init_dseg
	mov	dx,offset CONFIG_BUF	; lets try and fill out buffer
	int	DOS_INT
	pop	ds
	 jc	getchar30
	mov	cfg_tail,ax		
	mov	ax,(MS_X_LSEEK*256)+1
	xor	dx,dx
	xor	cx,cx
	int	DOS_INT			; get current file position
	mov	cfg_seeklo,ax		;  and save for possible
	mov	cfg_seekhi,dx		;  future re-opens
getchar30:
	mov	ah,MS_X_CLOSE		; Close the CONFIG file
	int	DOS_INT
getchar40:
	mov	bx,cfg_tail		; now lets see if we filled the buffer
	cmp	bx,CONFIG_BUF_SIZE	;  if not its EOF so mark it as such
	 je	getchar50
	push	ds
	mov	ds,init_dseg
	mov	CONFIG_BUF[bx],EOF	; add an EOF mark
	pop	ds			;  in case there isn't one already
	inc	cfg_tail
	inc	cfg_seeklo
	 jnz	getchar50
	inc	cfg_seekhi
getchar50:
	push	ds
	mov	ds,init_dseg
	mov	al,CONFIG_BUF		; return 1st char from buffer
	pop	ds
	mov	cfg_head,1		; remember we have returned char
	pop dx ! pop cx
	ret


;
; On a DEVICEHIGH we have encountered a line
; /L:r1[,s1][;r2[,s2]]... [/S]
; where r1 = load region, s1 = hex size in bytes, r2,s2 etc are further regions
; currently only r1/s1 are supported
; /S says the regions should m#be minimised

parse_region:
;On Entry:
;	DS:SI -> command line following '/L:'	
; On Exit:
;	DS:SI -> 1st non-parsed character
;	CY set on error
;
	call	atoi			; get a region to load in
	 jc	parse_region40
	mov	himem_region,ax		; remember region to try
	call	whitespace		; scan off all white space
	lodsb ! dec si			; now see is we have an optional size
	cmp	al,','			; have we a ',' character ?
	mov	ax,0			; assume minimum size not supplied
	 jne	parse_region30
	inc	si
	call	atol			; read number into DX:AX
	cmp	dx,15			; is number too big ?
	 ja	parse_region40
	mov	cx,16			; convert to para's
	div	cx
	inc	ax			; allow for round up
	inc	ax			;  and for header
	push	ax			; save size of region
parse_region10:
	mov	di,offset slashs_opt	; do we have a "/S" to minimise
	call	compare			;  the UMB's (ignore it if so)
	 jnc	parse_region20
	call	whitespace		; scan off all white space
	lodsb ! dec si			; strip off other regions
	cmp	al,';'			; another region follows ';'
	 jne	parse_region20
	inc	si
	call	atoi			; eat the region number
	 jc	parse_region20
	call	whitespace		; scan off all white space
	lodsb ! dec si
	cmp	al,','			; is a size specified ?
	 jne	parse_region10		; no, check for another region
	inc	si
	call	atol			; eat the size
	 jnc	parse_region10
parse_region20:
	pop	ax
parse_region30:
	clc				; we can proceed
	ret

parse_region40:
	mov	himem_size,0FFFFh	; 1 MByte wanted (ho, ho)
	stc				; we had problems..
	ret

; On a DEVICEHIGH we may encounter a line
; SIZE [=] s
; where s = size of region in hex bytes

parse_size:
;On Entry:
;	DS:SI -> command line following '/L:'	
; On Exit:
;	DS:SI -> 1st non-parsed character
;	CY set on error
;
	call	whitespace		; Scan off all white space
	lodsb				; before and after the optional
	cmp	al,'='			; '=' character.
	 je	parse_size
	dec	si
	call	atohex			; read hex number into DX:AX
	 jc	parse_size10
	cmp	dx,15			; is number too big ?
	 ja	parse_size20		; just load low	
	mov	cx,16			; convert to para's
	div	cx
	inc	ax			; allow for round up
	inc	ax			;  and for header
	mov	himem_size,ax		; remember size required
parse_size10:
	clc
	ret

parse_size20:
	mov	himem_size,0FFFFh	; 1 MByte wanted (ho, ho)
	stc
	ret


; A size has not been suppleied with DEVICEHIGH, so guess-timate one
; based on file size

size_file:
; On Entry:
;	DS:SI -> filename
; On Exit:
;	DS:SI preserved
;
	push	si
	mov	di,offset dev_name	; copy the device filename into a
	mov	byte ptr [di],0		;  local buffer and zero terminate
	call	copy_file
	pop	si
	mov	ax,(MS_X_OPEN * 256)+0	; open file r/o
	mov	dx,offset dev_name
	int	DOS_INT
	 jnc	size_file10
	mov	ax,0FFFFh		; can't open file, force low to prevent
	ret				;  two sets of error messages

size_file10:
	xchg	ax,bx			; handle in BX
	mov	ah,MS_X_READ
	mov	cx,EXE_LENGTH
	mov	dx,offset exeBuffer
	int	DOS_INT			; read in possible exe header
	 jc	size_file20
	cmp	ax,cx			; did we read all we wanted ?
	 jb	size_file40		; if not it can't be an EXE
	cmp	exeSignature,'ZM'	; check the signature
	 jne	size_file40		; if invalid can't be an EXE
	mov	ax,512
	mul	exeSize			; DX/AX bytes in image
	add	ax,exeFinal
	adc	dx,0
	mov	cx,16
	cmp	dx,cx			; are we too big ?
	 jae	size_file20		; yes, force low
	div	cx			; AX = para's required
	inc	ax			; one for rounding error
	 jz	size_file20
	add	ax,exeMinpara		; add on extra para's required
	 jnc	size_file30
size_file20:
	mov	ax,0FFFFh		; problems, force a load low
size_file30:
	push	ax			; save para's required
	mov	ah,MS_X_CLOSE		; close this file
	int	DOS_INT
	pop	ax			; AX para's required
	ret

size_file40:
	mov	ax,(MS_X_LSEEK * 256)+2
	xor	cx,cx			; now find out how big the file is
	xor	dx,dx			;  by seeking to zero bytes from end
	int	DOS_INT
	 jc	size_file20
	mov	cx,16
	cmp	dx,cx			; are we too big ?
	 jae	size_file20		; yes, force low
	div	cx			; AX = para's required
	inc	ax			; one for rounding error
	jmps	size_file30

himem_setup:
; On Entry:
;	AX = minimum amount of upper memory required (in para's)
; On Exit:
;	CY clear if able to satisfy request
;	CY set on error (we then load low)
;
; try and find some hi memory
; we allocate the biggest available chunk of upper memory
;
	push	es
	mov	cx,ax			; CX = para's required
	mov	ax,mem_current_base
	mov	himem_current_base,ax	; save mem_current_base
	mov	ax,mem_current
	mov	himem_current,ax	; save mem_current
	mov	ax,mem_max
	mov	himem_max,ax		; save mem_max
	mov	ah,MS_M_ALLOC
	mov	bx,0FFFFh		; give me all memory (please)
	int	21h			; bx = No. of paras available
	cmp	bx,cx			; do we have enough ?
	 jc	himem_setup40		; no, give up now

	cmp	himem_region,0		; is there a region specified ?
	 je	himem_setup20		; no, allocate largest block

; Allocate the region specified by /L:
	les	bx,func52_ptr		; ES:BX -> list of lists
	mov	ax,es:F52_DMD_UPPER[bx]	; get upper memory link
	cmp	ax,0FFFFh		; make sure there is one
	 je	himem_setup20		; shouldn't happen....
	mov	es,ax
himem_setup10:
	cmp	es:DMD_ID,'M'		; is there another block ?
	stc				; if we run out of blocks then
	 jne	himem_setup40		;  we load low
	mov	es,ax			; ES -> DMD
	mov	bx,es:DMD_LEN		; get length in para'a
	inc	ax
	add	ax,bx			; AX -> next DMD
	cmp	es:DMD_PSP,0		; is it free ?
	 jne	himem_setup10		; no, try the next
	dec	himem_region		; found the right region yet ?
	 jnz	himem_setup10
	cmp	bx,cx			; do we have enough ?
	 jc	himem_setup40		; no, go low
	mov	ax,es			; ES -> DMD header to allocate
	inc	ax
	mov	es,ax			; ES -> data in block
	mov	ah, MS_M_SETBLOCK
	int	21h			; "allocate" this block
	mov	ax,es
	 jnc	himem_setup30		; this can only fail if DMD chain
	jmps	himem_setup40		;  is corrupt...

himem_setup20:
; allocate the largest block available for DEVICEHIGH
	mov	ah, MS_M_ALLOC
	mov	bx, 0FFFFh		; give me all memory (please)
	int	21h			; bx = No. of paras available
	mov	ah, MS_M_ALLOC		; give me bx paras please
	int	21h			; ax:0 -> my memory
	 jc	himem_setup40		; woops, what happened ?
himem_setup30:
	mov	mem_current_base,ax
	mov	mem_current,ax		; save base of himem area
	mov	mem_max,ax
	add	mem_max,bx		; top of himem area
himem_setup40:
	pop	es
	ret

himem_cleanup:
; clean up our high memory - this hook should free up any difference
; between himem_current and himem_max
	mov	ax,himem_max
	mov	mem_max,ax		; restore mem_max
	mov	ax,himem_current_base
	xchg	mem_current_base,ax	; restore mem_current_base
	mov	bx,himem_current
	xchg	mem_current,bx		; restore mem_current
	push	es
	mov	es,ax			; ES -> memory block
	sub	bx,ax			; has any memory been used ?
	 jz	himem_cleanup10
	mov	ah,MS_M_SETBLOCK	; try and shrink the block
	int	DOS_INT			; to the size we used
	pop	es
;	clc				; return success
	ret

himem_cleanup10:
	mov	ah,MS_M_FREE		; free it all up
	int	DOS_INT
	pop	es
	stc				; return an error
	ret

copy_asciiz:
;-----------
	lodsb				; get a character
	stosb				; copy it
	test	al,al			; is it the terminating NUL ?
	 jnz	copy_asciiz		; do next char
	ret

wait_for_key:
;------------
; On Entry:
;	None
; On Exit:
;	CY set if no key pressed within timeout
;
	mov	cx,keyb_timeout		; get timeout value
	clc				; assume no timeout
	 jcxz	wait_for_key30
wait_for_key10:
	push	cx
	mov	ah,MS_T_GETTIME		; get current time
	int	DOS_INT			;  so we can do timeout
	mov	bx,dx			;  save secs in BH
	pop	cx
wait_for_key20:
	mov	ah,MS_C_STAT		; is a character ready ?
	int	DOS_INT			; if so process it
	test	al,al			; do we have a character ?
	 jnz	wait_for_key30
	push	cx
	mov	ah,MS_T_GETTIME		; get current time
	int	DOS_INT			;  so we can do timeout
	pop	cx
	cmp	bh,dh			; have we timed out ?
	 je	wait_for_key20
	loop	wait_for_key10		; another second gone by
	stc				; we have timed out
wait_for_key30:
	ret

;
;	COPY_FILE copies the next parameter from DS:SI into the buffer
;	at ES:DI and terminates with a NULL character. The parameter is
;	expected to be a FileName. DS:SI are returned pointing to the 
;	next parameter in the command.
;
copy_file:
	call	whitespace	; DeBlank the Command Line
	mov	cx,MAX_FILELEN	; Limit FileName Length
	push	si		; Save SI in case of error
copy_f10:
	lodsb			; Copy upto the first Space or 
	cmp	al,' '		; Control Character
	 jbe	copy_f20
	cmp	al,','		; stop at ',' too
	 je	copy_f20
	cmp	al,'/'		; Also stop scanning when a switch 
	 je	copy_f20	; character is detected
	stosb
	loop	copy_f10
	pop	si		; Restore the original SI	
	mov	ax,13		; 13 = invalid data error
	stc			; and return with an error
	ret

copy_f20:
	pop	ax		; Remove Original String address
	dec	si		; Point at the failing character 
	xor	ax,ax
	stosb			; Zero Terminate FileName
	ret


INITDATA	DSEG 'INITDATA'

if ADDDRV
	extrn	err_no_command_file:byte
	extrn	err_block_device:byte
else
	extrn	shell:byte		; Default Command Processor
	extrn	shell_cline:byte	; Default Command Line
endif
	extrn	dev_epb:byte
	extrn	dev_count:byte
	extrn	rel_unit:word

	extrn	dos_target_seg:word
	extrn	bios_target_seg:word
	extrn	mem_current_base:word	; Current Base Address
	extrn	mem_current:word	; Current Load Address
	extrn	mem_max:word		; Top of Available Memory
	extrn	mem_size:word		; Real top of Memory

	extrn	init_dseg:word		; Current Init Data Segment


include	initmsgs.def				; Include TFT Header File


;	extrn	bad_command:byte
;	extrn	bad_filename:byte

if not ADDDRV
;	extrn	bad_shell:byte
;	extrn	bad_country:byte
;	extrn	bad_lastdrive:byte
;	extrn	bad_break:byte
;	extrn	bad_buffers:byte
;	extrn	bad_files:byte
;	extrn	bad_fcbs:byte
;	extrn	bad_fopen:byte
;	extrn	bad_drivparm:byte
;	extrn	bad_history:byte
endif

;	extrn	yes_char:byte		; In BIOSMSGS.ASM
;	extrn	no_char:byte
	
	extrn	dev_load_seg:word
	extrn	dev_reloc_seg:word
	extrn	dev_epb:byte
	extrn	dev_name:byte
	extrn	dev_name:byte
	extrn	dosVersion:word
	extrn	strategy_off:word
	extrn	strategy_seg:word
	extrn	interrupt_off:word
	extrn	interrupt_seg:word
	extrn	request_hdr:byte
	extrn	next_drv:byte
	extrn	strategy_seg:word
	extrn	strategy:dword
	extrn	interrupt:dword
	extrn	func52_ptr:dword
	extrn	strategy_seg:word
	extrn	condev_off:word
	extrn	condev_seg:word
	extrn	clkdev_off:word
	extrn	clkdev_seg:word
	extrn	num_blkdev:byte
	extrn	blkdev_table:byte
	extrn	last_drv:byte
	extrn	next_drv:byte
	extrn	max_secsize:word
	extrn	max_clsize:word
	extrn	country_code:word
	extrn	code_page:word
	extrn	drdos_ptr:dword
	extrn	init_buf:byte
	extrn	num_read_ahead_buf:byte
	extrn	buffersIn:byte
	extrn	num_files:word
	extrn	num_fcbs:word
	extrn	num_fopen:word
	extrn	history_flg:byte	; In INIT code
	extrn	history_size:word	;
	extrn	num_stacks:word
	extrn	stack_size:word

if not ADDDRV
	extrn	hidos:byte
	extrn	bios_seg:word
	extrn	DeblockSetByUser:Byte
	extrn	DeblockSeg:word		; In BIOS data
endif

	extrn	dbcs_tbl:dword
	extrn	ctry_info:byte



	Public	cfg_file, cfg_file_end

preload_entry	rd	0		; preload back door entry
		dw	14h		; offset is pre-initialised to 14h
preload_seg	rw	1

preload_ver	dw	10		; version to give DBLSPACE

	Public	preload_drv
preload_drv	db	0		; number of preload drives
alt_drive	db	0		; preload checks alternative drive,
					;   (only used loading from A:)

; The preload_file is used as is to open a preload device
; It is also used to initialise a "DMD" name, and the code to do this
; currently finds the "\" and then copies the next 8 characters.
; This works with the current names - any new names may require modifications

preload_file	dw	offset security_file	; initially '\SECURITY.BIN'
security_file	db	'\SECURITY.BIN',0
stacker_file	db	'C:\STACKER.BIN',0
dblspace_file	db	'C:\DBLSPACE.BIN',0

cfg_file	db	'DCONFIG.SYS',0	; Configuration File
		rb	64		; space for bigger CHAIN'd file
cfg_file_end	rb	0

cfg_seeklo	dw	0		; offset we have reached in CONFIG file
cfg_seekhi	dw	0		; in case Richards CONFIG file > 64k

cfg_head	dw	0		; offset we are at in CONFIG_BUF
cfg_tail	dw	0		; # bytes currently in CONFIG_BUF

cfg_buffer	rb	CFG_BUF_LEN	; individual lines live here

;
;	EXEC parameter blocks for INSTALL function
;
exec_envseg	rw	1		; Environment Segment
exec_lineoff	rw	1		; Command Line Offset
exec_lineseg	rw	1		; Command Line Segment
exec_fcb1off	rw	1		; Offset of FCB 1 (5Ch)
exec_fcb1seg	rw	1		; Segment of FCB 1 (5Ch)
exec_fcb2off	rw	1		; Offset of FCB 2 (6Ch)
exec_fcb2seg	rw	1		; Segment of FCB 2 (6Ch)
		rd	2		; Initial SS:SP & CS:IP

system_sp	rw	1
system_ss	rw	1

ioctl_pb	rb	0
ioctl_func	rb	1		; special functions
ioctl_type	rb	1		; device type (form factor)
ioctl_attrib	rw	1		; device attributes
ioctl_tracks	rw	1		; # of tracks
ioctl_mtype	rb	1		; media type, usually zero
ioctl_bpb	rb	31		; default BPB for this type of disk
ioctl_layout	rw	1+64		; support 64 sectors/track max.

drivp_drv	rb	1		; drive 0-15
drivp_chg	rb	1		; change line support
drivp_prm	rb	1		; permanent media flag
drivp_ff		rb	1		; form factor
drivp_trk	dw	80
drivp_spt	equ	word ptr ioctl_bpb+13
drivp_heads	equ	word ptr ioctl_bpb+15	; # of heads

ff_table	dw	bpb360, bpb1200, bpb720		; 360/1200/720 Kb
		dw	bpb243, bpb1200			; 8" sd/dd
		dw	bpb360, bpb360			; hard disk, tape
		dw	bpb1440				; 1440 Kb

bpb360	dw	512
	db	2
	dw	1
	db	2
	dw	112
	dw	40*2*9
	db	0FDh
	dw	2
	dw	9
	dw	2

bpb1200	dw	512
	db	1
	dw	1
	db	2
	dw	224
	dw	80*2*15
	db	0F9h
	dw	7
	dw	15
	dw	2

bpb720	dw	512			; bytes per sector
	db	2			; sectors/cluster
	dw	1			; FAT address
	db	2			; # of FAT copies
	dw	112			; root directory size
	dw	80*2*9			; sectors/disk
	db	0F9h			; media byte
	dw	3			; size of single FAT copy
	dw	9			; sectors per track
	dw	2			; # of heads

bpb1440	dw	512
	db	1
	dw	1
	db	2
	dw	224
	dw	80*2*18
	db	0F9h
	dw	7
	dw	18
	dw	2

bpb243	dw	128
	db	4
	dw	1
	db	2
	dw	64
	dw	77*1*26
	db	0E5h
	dw	1
	dw	26
	dw	1

msg_crlf	db	CR, LF
msg_dollar	db	'$'

CFG_NAME	equ	word ptr .0000h		; Command Name
CFG_FUNC	equ	word ptr .0002h		; Command Subroutine
CFG_FLAGS	equ	word ptr .0004h		; Command flags
CFG_SIZE	equ	6			; Size of each Entry

CF_NOF		equ	0001h		; set if F5/F8 should be ignored
CF_LC		equ	0002h		; set if case should be preserved
CF_QUERY	equ	0004h		; set at run time eg. "DEVICE?"

cfg_table	rw	0
if not ADDDRV
	dw	cmd_country,	func_country,	0	; COUNTRY=nnn,nnn,country
	dw	cmd_shell,	func_shell,	0	; SHELL=filename
	dw	cmd_lastdrive,	func_lastdrive,	0	; LASTDRIVE=d:
	dw	cmd_break,	func_break,	0	; BREAK=ON/OFF
	dw	cmd_buffers,	func_buffers,	0	; BUFFERS=nn
	dw	cmd_hibuffers,	func_hibuffers,	0	; HIBUFFERS=nn
	dw	cmd_fcbs,	func_fcbs,	0	; FCBS=nn
	dw	cmd_files,	func_files,	0	; FILES=nn
	dw	cmd_stacks,	func_stacks,	0	; STACKS=nn
	dw	cmd_fastopen,	func_fastopen,	0	; FASTOPEN=nnn
	dw	cmd_drivparm,	func_drivparm,	0	; DRIVPARM=/d:nn ...
	dw	cmd_history,	func_history,	0	; HISTORY=ON|OFF,NNN
	dw	cmd_hiinstall,	func_hiinstall,	0	; HIINSTALL=cmdstring
	dw	cmd_installhigh, func_hiinstall, 0	; INSTALLHIGH=cmdstring
	dw	cmd_install,	func_install,	0	; INSTALL=cmdstring
	dw	cmd_hidos,	func_hidos,	0	; HIDOS=ON/OFF
	dw	cmd_dos,	func_dos,	0	; DOS=HIGH
	dw	cmd_set,	func_set,	CF_LC	; SET envar=string
	dw	cmd_switches,	func_switches,	CF_NOF	; SWITCHES=...
endif
	dw	cmd_hidevice,	func_hidevice,	0	; HIDEVICE=filename
	dw	cmd_devicehigh,	func_hidevice,	0	; DEVICEHIGH=filename
	dw	cmd_device,	func_device,	0	; DEVICE=filename
	dw	cmd_remark,	func_remark,	CF_NOF	; REM Comment
	dw	cmd_semicolon,	func_remark,	CF_NOF	; ; Comment
	dw	cmd_colon,	func_remark,	CF_NOF	; :label
	dw	cmd_chain,	func_chain,	0	; CHAIN=filename
	dw	cmd_goto,	func_goto,	0	; GOTO=label
	dw	cmd_gosub,	func_gosub,	0	; GOSUB=label
	dw	cmd_return,	func_return,	0	; RETURN (from GOSUB)
	dw	cmd_cls,	func_cls,	0	; Clear Screen
	dw	cmd_cpos,	func_cpos,	0	; Set Cursor Position
	dw	cmd_timeout,	func_timeout,	0	; set ? TIMEOUT
	dw	cmd_switch,	func_switch,	0	; SWITCH=n
	dw	cmd_onerror,	func_onerror,	CF_LC	; ONERROR='n' optional command
	dw	cmd_query,	func_query, CF_NOF+CF_LC; ?optional command
	dw	cmd_echo,	func_echo,	CF_LC	; ECHO=string
	dw	cmd_exit,	func_exit,	0	; EXIT
	dw	cmd_error,	func_error,	0	; ERROR='n'
	dw	cmd_getkey,	func_getkey,	0	; GETKEY
	dw	cmd_yeschar,	func_yeschar,	0	; YESCHAR=
	dw	cmd_deblock,	func_deblock,	0	; DEBLOCK=xxxx
	dw	cmd_numlock,	func_numlock,	0	; NUMLOCK=ON/OFF
	dw	cmd_common,	func_common,	0	; [COMMON]
 	dw	0				; end of table

if not ADDDRV
cmd_country	db	'COUNTRY',0
cmd_shell	db	'SHELL',0
cmd_lastdrive	db	'LASTDRIVE',0
cmd_break	db	'BREAK',0
cmd_buffers	db	'BUFFERS',0
cmd_hibuffers	db	'HIBUFFERS',0
cmd_fcbs	db	'FCBS',0
cmd_files	db	'FILES',0
cmd_stacks	db	'STACKS',0
cmd_fastopen	db	'FASTOPEN',0
cmd_drivparm	db	'DRIVPARM', 0
cmd_history	db	'HISTORY', 0
cmd_install	db	'INSTALL', 0
cmd_hiinstall	db	'HIINSTALL', 0
cmd_installhigh	db	'INSTALLHIGH', 0
cmd_hidos	db	'HIDOS',0
cmd_dos		db	'DOS',0
cmd_set		db	'SET',0
cmd_switches	db	'SWITCHES',0
endif
cmd_hidevice	db	'HIDEVICE',0
cmd_devicehigh	db	'DEVICEHIGH',0
cmd_device	db	'DEVICE',0
cmd_remark	db	'REM', 0
cmd_semicolon	db	';',0
cmd_colon	db	':',0
cmd_chain	db	'CHAIN',0
cmd_goto	db	'GOTO',0
cmd_gosub	db	'GOSUB',0
cmd_return	db	'RETURN',0
cmd_cls		db	'CLS',0
cmd_cpos	db	'CPOS',0
cmd_timeout	db	'TIMEOUT',0
cmd_switch	db	'SWITCH',0
cmd_onerror	db	'ONERROR',0
cmd_query	db	'?',0
cmd_echo	db	'ECHO',0
cmd_exit	db	'EXIT',0
cmd_error	db	'ERROR',0
cmd_getkey	db	'GETKEY',0
cmd_yeschar	db	'YESCHAR',0
cmd_deblock	db	'DEBLOCK',0
cmd_numlock	db	'NUMLOCK',0
cmd_common	db	'[COMMON]',0

cmd_on		db	'ON',0
cmd_off		db	'OFF',0

confirm_msg1	db	' ($'
confirm_msg2	db	') ? $'
confirm_msg3	db	CR,LF,'$'

region_opt	db	'/L:',0
slashs_opt	db	'/S',0
size_opt	db	'SIZE',0
high_opt	db	'HIGH',0
low_opt		db	'LOW',0
umb_opt		db	'UMB',0
noumb_opt	db	'NOUMB',0

himem_region	dw	0		; region to hidevice into
himem_size	dw	0		; minimum size wanted
himem_current	dw	0
himem_current_base dw	0
himem_max	dw	0

if ADDDRV
error_flag	db	0		;1 if error occurred during command
					;file processing, 0 otherwise
endif

default_query_char	db	CR
default_switch_char	db	'1'

keyb_timeout	dw	0		; default is no timeout
error_level	dw	0		; default is no error
save_sp		dw	0		; save SP here for GOSUB/RETURN's


INITENV		DSEG	PARA 'INITDATA'

envstart	rb	253		; initial env buffer
envend		dw	0		; make it double null terminated
		db	1Ah		; EOF marker env buffer
	Public	boot_options
boot_options	dw	0
; set by BIOS to either the SHIFT states, or to F5KEY or F8KEY

EXE_LENGTH	equ	001Ch

exeBuffer	rw	0
exeSignature	dw	0	; 0000 Valid EXE contains 'MZ'
exeFinal	dw	0	; 0002 Image Length MOD 512
exeSize		dw	0	; 0004 Image Length DIV 512
exeRelcnt	dw	0	; 0006 No. of Relocation Items
exeHeader	dw	0	; 0008 Header Size in paragraphs
exeMinpara	dw	0	; 000A Minimum No extra paragraphs
exeMaxpara	dw	0	; 000C Maximum No of extra paragraphs
exeSS		dw	0	; 000E Displacment of Stack Segment
exeSP		dw	0	; 0010 Initial SP
exeChecksum	dw	0	; 0012 Negative CheckSum
exeIP		dw	0	; 0014 Initial IP
exeCS		dw	0	; 0016 Code Segment displacement
exeReloff	dw	0	; 0018 Byte Offset of First REL item
exeOverlay	dw	0	; 001A Overlay Number (0 == Resident)

	end

