	page	,160
;								      ;AC000;
;		    IBM Confidential				      ;AC000;
;		    Copyright (C) IBM Corporation 1981-1992	      ;AC000;
;		    All Rights Reserved.			      ;AC000;
;								      ;AC000;

title	bios system initialization

;
; Multiple configuration block support	Created 16-Mar-1992 by JeffPar
;
; Summary:
;
;   The procedure "organize" crunches the in-memory copy of config.sys
;   into lines delimited by CR/LF (sometimes no CR, but *always* an LF)
;   with the leading "keyword=" replaced by single character codes (eg, B
;   for BUFFERS, D for DEVICE, Z for any unrecognized keyword);  see comtab
;   and/or config.inc for the full list.
;
;   [blockname] and INCLUDE are the major syntactical additions for multi-
;   configuration support.  blockname is either MENU, which contains one
;   or more MENUITEM lines, an optional MENUDEFAULT (which includes optional
;   time-out), or any user-defined keyword, such as NETWORK, CD-ROM, etc.
;   INCLUDE allows the current block to name another block for inclusion
;   during the processing phase of CONFIG.SYS.	An INCLUDE is only honored
;   once, precluding nasty infinite-loop scenarios.  If blocks are present
;   without a MENU block, then only lines inside COMMON blocks are processed.
;
; Example:
;
;   [menu]
;   menuitem=misc,Miscellaneous
;   menuitem=network,Network Configuration
;   menudefault=network,15
;
;   [network]
;   include misc
;   device=foo
;
;   [misc]
;   device=bar
;   include alternate
;
;   [alternate]
;   device=tar
;
;
;   When the menu is displayed
;
;    1. Miscellaneous
;    2. Network Configuration
;
;   #2 is highlighted as the default option, and will be automatically
;   selected after 15 seconds.	It will invoke the following lines in the
;   following order:
;
;	DEVICE=BAR
;	DEVICE=TAR
;	DEVICE=FOO
;

key_clean	equ	3fh	; F5
key_ctrl_clean	equ	62h	; ctrl-F5
key_inter	equ	42h	; F8
key_ctrl_inter	equ	65h	; ctrl-F8
MULTI_CONFIG equ 1

	.xlist
	include version.inc		; set version build flags
	include biosseg.inc		; establish bios segment structure
	.list

lf	equ	10
cr	equ	13
tab	equ	9

; the following depends on the positions of the various letters in switchlist

switchnum	equ 11111000b		; which switches require number

	.xlist
	include bpb.inc
	include syscall.inc
	include doscntry.inc
	include devsym.inc
	include ioctl.inc
	include devmark.inc
	include config.inc
ifdef	MULTI_CONFIG
	include rombios.inc
endif
	.list

stacksw equ	true			; include switchable hardware stacks

	if	ibmjapver
noexec	equ	true
	else
noexec	equ	false
	endif


Bios_Data segment
	extrn eot:byte			; New in MS 410 driver
	extrn ec35_flag: byte
Bios_Data ends

sysinitseg segment public

assume	cs:sysinitseg,ds:nothing,es:nothing,ss:nothing

ifdef	MULTI_CONFIG

	MAX_MULTI_CONFIG equ 9		; max # of multi-config menu items supported

	extrn	initial_time:word
	extrn	config_envlen:word
	extrn	config_wrkseg:word
	extrn	config_cmd:byte
	extrn	config_multi:byte
	extrn	OnOff:byte		; New in MS 410 driver
	extrn	$PauseMsg:byte		; New in MS 410 driver
	extrn	$CleanMsg:byte		; New in MS 410 driver
	extrn	$InterMsg:byte		; New in MS 410 driver
	extrn	$MenuHeader:byte
	extrn	$MenuPrmpt:byte
	extrn	$StatusLine:byte
	extrn	$InterPrmpt:byte
	extrn	$YES:byte
	extrn	$NO:byte
	extrn	$TimeOut:byte
	extrn	$AutoPrmpt:byte
	extrn	swit_n:byte, swit_f:byte; New in MS 410 driver

	public	newcmd,tmplate,commnd2,commnd3,commnd4 ; Changed in 410
endif

IFDEF	CONFIGPROC

	extrn	badopm:byte,badcom:byte,badblock:byte
	extrn	badsiz_pre:byte,
	extrn	error_line:near

	extrn	dosinfo:dword
	extrn	memory_size:word,fcbs:byte,keep:byte
	extrn	default_drive:byte,confbot:word
	extrn	buffers:word,zero:byte,sepchr:byte
	extrn	files:byte
	extrn	count:word,chrptr:word
	extrn	bufptr:byte,prmblk:byte
	extrn	ldoff:word,area:word,packet:byte,unitcount:byte,
	extrn	break_addr:dword,bpb_addr:dword,drivenumber:byte
	extrn	com_level:byte, cmmt:byte, cmmt1:byte, cmmt2:byte
	extrn	cmd_indicator:byte
	extrn	multdeviceflag:byte
	extrn	org_count:word

	extrn	getchr:near
	extrn	DevEntry:dword

	insert_blank	db	0	; M051: indicates that blank has been
					; M051: inserted
ENDIF ; CONFIGPROC

	extrn	memlo:word,memhi:word,alloclim:word
	extrn	devmark_addr:word,badmem:byte,crlfm:byte
	extrn	setdevmarkflag:byte,badld_pre:byte

	extrn	donotshownum:byte
	extrn	pararound:near
	extrn	stall:near


	public	int24,open_dev,mem_err,prndev,auxdev,commnd,condev
	public	commnd,command_line
	public	badfil,round,print

IFDEF	CONFIGPROC

	public	organize,newline,calldev,badload
	public	config,getnum,prnerr
	public	delim
	public	setparms, parseline, diddleback
	public	setdoscountryinfo,set_country_path,move_asciiz
	public	cntry_drv,cntry_root,cntry_path
	public	pathstring

;
;----------------------------------------------------------------------------
;
; procedure : setparms
;
; the following set of routines is used to parse the drivparm = command in
; the config.sys file to change the default drive parameters.
;
;----------------------------------------------------------------------------
;
setparms	proc	near

	push	ds
	push	ax
	push	bx
	push	cx
	push	dx

	push	cs
	pop	ds
	assume	ds:sysinitseg

	xor	bx,bx
	mov	bl,byte ptr drive
	inc	bl			; get it correct for ioctl
					;  call (1=a,2=b...)
	mov	dx,offset deviceparameters
	mov	ah,ioctl
	mov	al,generic_ioctl
	mov	ch,rawio
	mov	cl,set_device_parameters
	int	21h

	mov	ax,Bios_Data		; get Bios_Data segment
	mov	ds,ax			; set Bios_Data segment
	assume	ds:Bios_Data

	test	cs:word ptr switches, flagec35	; MAR ; seems to require CS
						      ; segment override?!
						      ; compiler bug!?
	jz	not_ec35

	mov	cl,cs:byte ptr drive	   ; which drive was this for?
					   ; MAR ; seems to require CS
						 ; segment override?!
						 ; compiler bug!?

	mov	al,1			; assume drive 0
	shl	al,cl			; set proper bit depending on drive
	or	ds:ec35_flag,al 	; set the bit in the permanent flags

not_ec35:

;	Now adjust the BIOS's EOT variable if our new drive has more
;	sectors per track than any old ones.

	mov	al,cs:byte ptr deviceparameters.DP_BPB.BPB_SECTORSPERTRACK
	cmp	al,eot
	jbe	eot_ok
	mov	eot,al
eot_ok:

	pop	dx			; fix up all the registers
	pop	cx
	pop	bx
	pop	ax
	pop	ds
	assume	ds:nothing
	ret

setparms	endp

;
;----------------------------------------------------------------------------
;
; procedure : diddleback
;
; replace default values for further drivparm commands
;
;----------------------------------------------------------------------------
;

diddleback	proc	near

	push	ds
	push	cs
	pop	ds
	assume	ds:sysinitseg
	mov	word ptr deviceparameters.dp_cylinders,80
	mov	byte ptr deviceparameters.dp_devicetype, dev_3inch720kb
	mov	word ptr deviceparameters.dp_deviceattributes,0
	mov	word ptr switches,0	    ; zero all switches
	pop	ds
	assume	ds:nothing
	ret

diddleback	endp

;
;----------------------------------------------------------------------------
;
; procedure : parseline
;
; entry point is parseline. al contains the first character in command line.
;
;----------------------------------------------------------------------------
;

parseline	proc	near
					; don't get character first time
	push	ds

	push	cs
	pop	ds
	assume	ds:sysinitseg

nextswtch:
	cmp	al,cr			; carriage return?
	jz	done_line
	cmp	al,lf			; linefeed?
	jz	put_back		; put it back and done

; anything less or equal to a space is ignored.

	cmp	al,' '                  ; space?
	jbe	get_next		; skip over space
	cmp	al,'/'
	jz	getparm
	stc				; mark error invalid-character-in-input
	jmp	short exitpl

getparm:
	call	check_switch
	mov	word ptr switches,bx	; save switches read so far
	jc	swterr

get_next:
	call	getchr
	jc	done_line
	jmp	nextswtch

swterr:
	jmp	short exitpl		; exit if error

done_line:
	test	word ptr switches,flagdrive  ; see if drive specified
	jnz	okay
	stc				; mark error no-drive-specified
	jmp	short exitpl

okay:
	mov	ax,word ptr switches
	and	ax,0003h	    ; get flag bits for changeline and non-rem
	mov	word ptr deviceparameters.dp_deviceattributes,ax
	mov	word ptr deviceparameters.dp_tracktableentries, 0
	clc			    ; everything is fine
	call	setdeviceparameters
exitpl:
	pop	ds
	ret

put_back:
	inc	count			; one more char to scan
	dec	chrptr			; back up over linefeed
	jmp	short done_line

parseline	endp

;
;----------------------------------------------------------------------------
;
; procedure : check_switch
;
; processes a switch in the input. it ensures that the switch is valid, and
; gets the number, if any required, following the switch. the switch and the
; number *must* be separated by a colon. carry is set if there is any kind of
; error.
;
;----------------------------------------------------------------------------
;

check_switch	proc	near

	call	getchr
	jc	err_check
	and	al,0dfh 		; convert it to upper case
	cmp	al,'A'
	jb	err_check
	cmp	al,'Z'
	ja	err_check

	push	es

	push	cs
	pop	es

	mov	cl,byte ptr switchlist	; get number of valid switches
	mov	ch,0
	mov	di,1+offset switchlist	; point to string of valid switches
	repne	scasb

	pop	es
	jnz	err_check

	mov	ax,1
	shl	ax,cl			; set bit to indicate switch
	mov	bx,word ptr switches	; get switches so far
	or	bx,ax			; save this with other switches
	mov	cx,ax
	test	ax, switchnum		; test against switches that require number to follow
	jz	done_swtch

	call	getchr
	jc	err_swtch

	cmp	al,':'
	jnz	err_swtch

	call	getchr
	push	bx			; preserve switches
	mov	byte ptr cs:sepchr,' '  ; allow space separators
	call	getnum
	mov	byte ptr cs:sepchr,0
	pop	bx			; restore switches

; because getnum does not consider carriage-return or line-feed as ok, we do
; not check for carry set here. if there is an error, it will be detected
; further on (hopefully).

	call	process_num

done_swtch:
	clc
	ret

err_swtch:
	xor	bx,cx			; remove this switch from the records
err_check:
	stc
	ret

check_switch	endp

;
;----------------------------------------------------------------------------
;
; procedure : process_num
;
; this routine takes the switch just input, and the number following (if any),
; and sets the value in the appropriate variable. if the number input is zero
; then it does nothing - it assumes the default value that is present in the
; variable at the beginning. zero is ok for form factor and drive, however.
;
;----------------------------------------------------------------------------
;

process_num	proc	near
	test	word ptr switches,cx	; if this switch has been done before,
	jnz	done_ret		; ignore this one.
	test	cx,flagdrive
	jz	try_f
	mov	byte ptr drive,al
	jmp	short done_ret

try_f:
	test	cx,flagff
	jz	try_t

; ensure that we do not get bogus form factors that are not supported

	mov	byte ptr deviceparameters.dp_devicetype,al
	jmp	short done_ret

try_t:
	or	ax,ax
	jz	done_ret		; if number entered was 0, assume default value
	test	cx,flagcyln
	jz	try_s

	mov	word ptr deviceparameters.dp_cylinders,ax
	jmp	short done_ret

try_s:
	test	cx,flagseclim
	jz	try_h
	mov	word ptr slim,ax
	jmp	short done_ret

; must be for number of heads

try_h:
	mov	word ptr hlim,ax

done_ret:
	clc
	ret

process_num	endp

;	M047 -- Begin modifications (too numerous to mark specifically)
;
;----------------------------------------------------------------------------
;
; procedure : setdeviceparameters
;
; setdeviceparameters sets up the recommended bpb in each bds in the
; system based on the form factor. it is assumed that the bpbs for the
; various form factors are present in the bpbtable. for hard files,
; the recommended bpb is the same as the bpb on the drive.
; no attempt is made to preserve registers since we are going to jump to
; sysinit straight after this routine.
;
;	if we return carry, the DRIVPARM will be aborted, but presently
;	  we always return no carry
;
;
;	note:  there is a routine by the same name in msdioctl.asm
;
;----------------------------------------------------------------------------
;

setdeviceparameters	proc	near

	push	es

	push	cs
	pop	es
	assume	es:sysinitseg

	xor	bx,bx
	mov	bl,byte ptr deviceparameters.dp_devicetype
	cmp	bl,dev_5inch
	jnz	got_80

	mov	word ptr deviceparameters.dp_cylinders,40	; 48 tpi=40 cyl

got_80:
	shl	bx,1			; get index into bpb table
	mov	si,bpbtable[bx] 	; get address of bpb

	mov	di,offset deviceparameters.dp_bpb ; es:di -> bpb
	mov	cx,size A_BPB
	cld
	repe	movsb

	pop	es
	assume es:nothing

	test	word ptr switches,flagseclim
	jz	see_heads

	mov	ax,word ptr slim
	mov	word ptr deviceparameters.dp_bpb.bpb_sectorspertrack,ax

see_heads:
	test	word ptr switches,flagheads
	jz	heads_not_altered

	mov	ax,word ptr hlim
	mov	word ptr deviceparameters.dp_bpb.bpb_heads,ax

heads_not_altered:


; set up correct media descriptor byte and sectors/cluster
;   sectors/cluster is always 2 except for any one sided disk or 1.44M

	mov	byte ptr deviceparameters.dp_bpb.bpb_sectorspercluster,2
	mov	bl,0f0h 		; get default mediabyte

;	preload the mediadescriptor from the bpb into bh for convenient access

	mov	bh,byte ptr deviceparameters.dp_bpb.bpb_mediadescriptor

	cmp	word ptr deviceparameters.dp_bpb.bpb_heads,2	; >2 heads?
	ja	got_correct_mediad	; just use default if heads>2

	jnz	only_one_head		; one head, do one head stuff

;	two head drives will use the mediadescriptor from the bpb

	mov	bl,bh			; get mediadescriptor from bpb

;	two sided drives have two special cases to look for.  One is
;	   a 320K diskette (40 tracks, 8 secs per track).  It uses
;	   a mediaid of 0fch.  The other is 1.44M, which uses only
;	   one sector/cluster.

;	any drive with 18secs/trk, 2 heads, 80 tracks, will be assumed
;	   to be a 1.44M and use only 1 sector per cluster.  Any other
;	   type of 2 headed drive is all set.

	cmp	deviceparameters.dp_bpb.bpb_sectorspertrack,18
	jnz	not_144m
	cmp	deviceparameters.dp_cylinders,80
	jnz	not_144m

;	We've got cyl=80, heads=2, secpertrack=18.  Set cluster size to 1.

	jmp	short got_one_secperclus_drive


;	check for 320K

not_144m:
	cmp	deviceparameters.dp_cylinders,40
	jnz	got_correct_mediad
	cmp	deviceparameters.dp_bpb.bpb_sectorspertrack,8
	jnz	got_correct_mediad

	mov	bl,0fch
	jmp	short got_correct_mediad


only_one_head:

;	if we don't have a 360K drive, then just go use 0f0h as media descr.

	cmp	deviceparameters.dp_devicetype,dev_5inch
	jnz	got_one_secperclus_drive

;	single sided 360K drive uses either 0fch or 0feh, depending on
;	  whether sectorspertrack is 8 or 9.  For our purposes, anything
;	  besides 8 will be considered 0fch

	mov	bl,0fch 		; single sided 9 sector media id
	cmp	word ptr deviceparameters.dp_bpb.bpb_sectorspertrack,8
	jnz	got_one_secperclus_drive ; okay if anything besides 8

	mov	bl,0feh 		; 160K mediaid

;	we've either got a one sided drive, or a 1.44M drive
;	  either case we'll use 1 sector per cluster instead of 2

got_one_secperclus_drive:
	mov	byte ptr deviceparameters.dp_bpb.bpb_sectorspercluster,1

got_correct_mediad:
	mov	byte ptr deviceparameters.dp_bpb.bpb_mediadescriptor,bl


;	 Calculate the correct number of Total Sectors on medium
;
	mov	ax,deviceparameters.dp_cylinders
	mul	word ptr deviceparameters.dp_bpb.bpb_heads
	mul	word ptr deviceparameters.dp_bpb.bpb_sectorspertrack
	mov	word ptr deviceparameters.dp_bpb.bpb_totalsectors,ax
	clc				; we currently return no errors

	ret

setdeviceparameters	endp

;	M047 -- end rewritten routine

;
;----------------------------------------------------------------------------
;
; procedure : organize
;
;----------------------------------------------------------------------------
;
		assume ds:nothing, es:nothing
organize	proc	near

	mov	cx,[count]
	jcxz	nochar1

ifndef	MULTI_CONFIG
;
;   In MULTI_CONFIG, we map to upper case on a line-by-line basis,
;   because we the case of values in SET commands preserved
;
	call	mapcase
endif
	xor	si,si
	mov	di,si
	xor	ax,ax
	mov	com_level, 0

org1:
	call	skip_comment
	jz	end_commd_line		; found a comment string and skipped.
	call	get2			; not a comment string. then get a char.
	cmp	al, lf
	je	end_commd_line		; starts with a blank line.
	cmp	al, ' '
	jbe	org1			; skip leading control characters
	jmp	short findit

end_commd_line:
	stosb				; store line feed char in buffer for the linecount.
	mov	com_level, 0		; reset the command level.
	jmp	org1

nochar1:
	stc
	ret

findit:
	push	cx
	push	si
	push	di
	mov	bp,si
	dec	bp
	mov	si,offset comtab	; prepare to search command table
	mov	ch,0
findcom:
	mov	di,bp
	mov	cl,[si]
	inc	si
	jcxz	nocom

ifdef	MULTI_CONFIG
;
;   Simplify future parsing by collapsing ";" onto "REM", and at the same
;   time skip the upcoming delimiter test (since ";" need not be followed by
;   anything in particular)
;
	cmp	byte ptr es:[di],CONFIG_SEMICOLON
	je	semicolon
loopcom:
	mov	al,es:[di]
	inc	di
	and	al,not 20h		; force upper case
	inc	si			; compare to byte @es:di
	cmp	al,ds:[si-1]
	loope	loopcom
else
	repe	cmpsb
endif
	lahf
	add	si,cx			; bump to next position without affecting flags
	sahf
	lodsb				; get indicator letter
	jnz	findcom
	cmp	byte ptr es:[di], cr	; the next char might be cr,lf
	je	gotcom0 		; such as in "rem",cr,lf case.
	cmp	byte ptr es:[di], lf
	je	gotcom0

ifdef	MULTI_CONFIG
;
;   Skip the delimiter test for the BEGIN identifier (it doesn't have one).
;
	cmp	al,CONFIG_BEGIN
	je	gotcom0
endif
	push	ax
	mov	al, byte ptr es:[di]	; now the next char. should be a delim.

ifdef	MULTI_CONFIG
;
;   If keyword is *immediately* followed by a question mark (?), then
;   set the high bit of the ASCII command code (CONFIG_OPTION_QUERY) that is
;   stored in the CONFIG.SYS memory image.
;
	cmp	al,'?'                  ; explicit interactive command?
	jne	no_query		; no
	pop	ax			; yes, so retrieve the original code
	or	al,CONFIG_OPTION_QUERY	; and set the QUERY bit
	jmp	short gotcom0		;
semicolon:
	mov	al,CONFIG_REM
	jmp	short gotcom0
no_query:
endif  ;MULTI_CONFIG

	call	delim
no_delim:
	pop	ax
	jnz	findcom
gotcom0:
	pop	di
	pop	si
	pop	cx
	jmp	short gotcom

nocom:
	pop	di
	pop	si
	pop	cx
	mov	al,CONFIG_UNKNOWN
	stosb				; save indicator char.
skipline:
	call	get2
	cmp	al, lf			; skip this bad command line
	jne	skipline
	jmp	end_commd_line		; handle next command line

gotcom:
	stosb				; save indicator char in buffer

ifdef	MULTI_CONFIG
;
;   Don't pollute "cmd_indicator" with the CONFIG_OPTION_QUERY bit though;
;   it screws up the direct comparisons below.
;
	and	al,NOT CONFIG_OPTION_QUERY
endif
	mov	cmd_indicator, al	; save it for the future use.

ifdef	MULTI_CONFIG
;
;   There is no whitespace/delimiter between the "begin block" character
;   ([) and the name of block (eg, [menu]), therefore skip this delimiter
;   skipping code
;
	cmp	al,CONFIG_BEGIN
	je	org31
	cmp	al,CONFIG_SUBMENU
	je	no_mapcase
	cmp	al,CONFIG_MENUITEM
	je	no_mapcase
	cmp	al,CONFIG_MENUDEFAULT
	je	no_mapcase
	cmp	al,CONFIG_INCLUDE
	je	no_mapcase
	call	mapcase 		; map case of rest of line to UPPER
no_mapcase:
endif

org2:	call	get2			; skip the command name until delimiter
	cmp	al, lf
	je	org21
	cmp	al, cr
	je	org21
	cmp	al, '/'                 ; T-RICHJ: Added to allow DEVHIGH/L:...
	je	org21			; T-RICHJ: to be parsed properly.

	call	delim
	jnz	org2
	jmp	short org3

org21:					;if cr or lf then
	dec	si			; undo si, cx register
	inc	cx			;  and continue

org3:	cmp	cmd_indicator, CONFIG_COMMENT
	je	get_cmt_token
	cmp	cmd_indicator, CONFIG_DEVICE
	je	org_file
	cmp	cmd_indicator, CONFIG_INSTALL
	je	org_file
	cmp	cmd_indicator, CONFIG_INSTALLHIGH
	je	org_file
	cmp	cmd_indicator, CONFIG_SHELL
	je	org_file
	cmp	cmd_indicator, CONFIG_SWITCHES
	je	org_switch
org31:
	jmp	org4

org_switch:
	call	skip_comment
	jz	end_commd_line_brdg

	call	get2
	call	org_delim
	jz	org_switch

	stosb
	jmp	org5

org_file:			; get the filename and put 0 at end,
	call	skip_comment
	jz	org_put_zero

	call	get2		; not a comment
	call	delim
	jz	org_file	; skip the possible delimeters

	stosb			; copy the first non delim char found in buffer

org_copy_file:
	call	skip_comment	; comment char in the filename?
	jz	org_put_zero	; then stop copying filename at that point

	call	get2
	cmp	al, '/'         ; a switch char? (device=filename/xxx)
	je	end_file_slash	; this will be the special case.

	stosb			; save the char. in buffer
	call	delim
	jz	end_copy_file

	cmp	al, ' '
	ja	org_copy_file	; keep copying
	jmp	short end_copy_file ; otherwise, assume end of the filename.

get_cmt_token:			; get the token. just max. 2 char.
	call	get2
	cmp	al, ' '         ; skip white spaces or "=" char.
	je	get_cmt_token	; (we are allowing the other special
	cmp	al, tab 	;  charaters can used for comment id.
	je	get_cmt_token	;  character.)
	cmp	al, '='         ; = is special in this case.
	je	get_cmt_token
	cmp	al, cr
	je	get_cmt_end	; cannot accept the carridge return
	cmp	al, lf
	je	get_cmt_end

	mov	cmmt1, al	; store it
	mov	cmmt, 1 	; 1 char. so far.
	call	get2
	cmp	al, ' '
	je	get_cmt_end
	cmp	al, tab
	je	get_cmt_end
	cmp	al, cr
	je	get_cmt_end
	cmp	al, lf
	je	end_commd_line_brdg

	mov	cmmt2, al
	inc	cmmt

get_cmt_end:
	call	get2
	cmp	al, lf
	jne	get_cmt_end		; skip it.

end_commd_line_brdg: jmp end_commd_line ; else jmp to end_commd_line

org_put_zero:				; make the filename in front of
	mov	byte ptr es:[di], 0	;  the comment string to be an asciiz.
	inc	di
	jmp	end_commd_line		;  (maybe null if device=/*)

end_file_slash: 			; al = "/" option char.
	mov	byte ptr es:[di],0	; make a filename an asciiz
	inc	di			; and
	stosb				; store "/" after that.
	jmp	short org5		; continue with the rest of the line

end_copy_file:
	mov	byte ptr es:[di-1], 0	; make it an asciiz and handle the next char.
	cmp	al, lf
	je	end_commd_line_brdg
	jmp	short org5

org4:					; org4 skips all delimiters after the command name except for '/'
	call	skip_comment
	jz	end_commd_line_brdg

	call	get2
	call	org_delim		; skip delimiters except '/' (mrw 4/88)
	jz	org4
	jmp	short org51

org5:					; rest of the line
	call	skip_comment		; comment?
	jz	end_commd_line_brdg
	call	get2			; not a comment.

org51:
	stosb				; copy the character
	cmp	al, '"'                 ; a quote ?
	je	at_quote
	cmp	al, ' '
	ja	org5
;;					  ; M051 - Start		 d1340
;;									 d1340
;;	  cmp	  cmd_indicator, CONFIG_DEVICEHIGH; Q: is this devicehighd1340
;;	  jne	  not_dh		  ; N:				 d1340
;;	  cmp	  al, lf		  ; Q: is this line feed	 d1340
;;	 je	 org_dhlf		 ; Y: stuff a blank before the lfd1340
;;	  cmp	  al, cr		  ; Q: is this a cr		 d1340
;;	  jne	  org5			  ; N:				 d1340
;;	  mov	  byte ptr es:[di-1], ' ' ; overwrite cr with blank      d1340
;;	  stosb 			  ; put cr after blank		 d1340
;;	  inc	  [insert_blank]	  ; indicate that blank has been d1340
;;					  ; inserted			 d1340
;;	  jmp	  org5							 d1340
;;not_dh:				  ; M051 - End			 d1340

	cmp	al, lf			  ; line feed?
	je	org1_brdg		  ; handles the next command line.
	jmp	org5			  ; handles next char in this line.

;org_dhlf:				   ; M051 - Start		d1340
;;	  cmp	  [insert_blank], 1	  ; Q:has a blank already been inserted
;;	  je	  org1_brdg		  ; Y:				d1340
;;	  mov	  byte ptr es:[di-1], ' ' ; overwrite lf with blank     d1340
;;	  stosb 			  ; put lf after blank		d1340
					  ; M051 - End			d1340

org1_brdg:
;;	  mov	  [insert_blank], 0	  ; M051: clear blank indicator ford1340
					  ; M051: devicehigh		 d1340
	jmp	org1

at_quote:
	cmp	com_level, 0
	je	up_level
	mov	com_level, 0		; reset it.
	jmp	org5

up_level:
	inc	com_level		; set it.
	jmp	org5

organize	endp

;
;----------------------------------------------------------------------------
;
; procedure : get2
;
;----------------------------------------------------------------------------
;
get2	proc	near
	jcxz	noget
	lods	byte ptr es:[si]
	dec	cx
	ret
noget:
	pop	cx
	mov	count,di
	mov	org_count, di
	xor	si,si
	mov	chrptr,si
ifndef	MULTI_CONFIG
	ret
else
;
;   This was the rather kludgy way out of procedure "organize", but instead
;   of returning to doconf, we now want to check config.sys BEGIN/END blocks
;   and the new boot menu stuff...
;
	mov	cx,di
	jmp	menu_check
endif
get2	endp


;
;----------------------------------------------------------------------------
;
; procedure : skip_comment
;
;skip the commented string until lf, if current es:si-> a comment string.
;in) es:si-> sting
;	 cx -> length.
;out) zero flag not set if not found a comment string.
;	  zero flag set if found a comment string and skipped it. al will contain
;	  the line feed charater at this moment when return.
;	  ax register destroyed.
;	  if found, si, cx register adjusted accordingly.
;
;----------------------------------------------------------------------------
;
skip_comment	proc	near

	jcxz	noget		; get out of the organize routine.
	cmp	com_level, 0	; only check it if parameter level is 0.
	jne	no_commt	;  (not inside quotations)

	cmp	cmmt, 1
	jb	no_commt

	mov	al, es:[si]
	cmp	cmmt1, al
	jne	no_commt

	cmp	cmmt, 2
	jne	skip_cmmt

	mov	al, es:[si+1]
	cmp	cmmt2, al
	jne	no_commt

skip_cmmt:
	jcxz	noget		; get out of organize routine.
	mov	al, es:[si]
	inc	si
	dec	cx
	cmp	al, lf		; line feed?
	jne	skip_cmmt

no_commt:
	ret

skip_comment	endp


ifdef	MULTI_CONFIG

;
;----------------------------------------------------------------------------
;
;   kbd_read: wait for keystroke
;
;   INPUT
;	DS == CS == sysinitseg
;
;   OUTPUT
;	Carry SET to clean boot, CLEAR otherwise
;
;   OTHER REGS USED
;	All
;
;   HISTORY
;	Created 16-Nov-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	kbd_read

kbd_read proc	near
	assume	ds:sysinitseg

	test	byte ptr [bDisableUI],2
	jnz	kbd_nodelay

	push	ds		; the bios timer tick count is incremented
	sub	ax,ax		; 18.2 times per second;
	mov	ds,ax		; make sure 37 have passed since we saved time
	assume	ds:nothing

kbd_loop:
	mov	ah,1		;
	int	16h		; peek the keyboard
	jnz	kbd_loopdone	; something's there, get out
	mov	ah,2		; peek the shift states
	int	16h		;
	test	al,03h		; either right or left shift key bits set?
	jnz	kbd_loopdone	; yes
	mov	ax,ds:[046Ch]	;
	sub	ax,initial_time ; get difference
	cmp	ax,37		; reached limit?
	jb	kbd_loop	; not yet
kbd_loopdone:
	pop	ds		; delay complete!
	assume	ds:sysinitseg

kbd_nodelay:
	sub	bx,bx		; assume clean boot
	mov	ah,2		; peek the shift states
	int	16h		;
	test	al,03h		; either right or left shift key bits set?
	jz	kbd_notshift	; no
	inc	bx		; yes
	inc	bx		;
	or    byte ptr [bQueryOpt],4  ;MARMAR (MS62)
kbd_notshift:			;
	mov	ah,1		; peek the keyboard
	int	16h		;
	jz	kbd_test	; no key present
	or	al,al		; is it a function key?
	jnz	kbd_test	; no
	cmp	ah,key_ctrl_clean ; ctrl-F5?
	je	accept_f5
	cmp	ah,key_clean	; F5 function key?
	jne	kbd_notf5	; no
accept_f5:
	mov	dx,offset $CleanMsg
	call	print		;
	or	[bQueryOpt],4	; no more interaction
	jmp	short kbd_eat	; yes, clean boot selected
kbd_notf5:
	cmp	ah,key_ctrl_inter ; ctrl-f8?
	je	accept_f8
	cmp	ah,key_inter	; f8?
	jne	kbd_exit	; no
accept_f8:
	mov	dx,offset $InterMsg
	call	print		;
	mov	bl,1		; yes, interactive-boot option enabled
	mov	[bQueryOpt],bl	; change default setting
kbd_eat:			;
	mov	ah,0		;
	int	16h		; eat the key we assumed was a signal
	mov	byte ptr [secElapsed],-1
	or	bx,bx		;
	jz	kbd_clean	;
kbd_test:			;
	cmp	bl,2		;
	jb	kbd_exit	;
kbd_clean:			;
	call	disable_autoexec; yes, tell COMMAND to skip autoexec.bat
	stc			; set carry to indicate abort
	ret			;
kbd_exit:			;
	clc			; clear carry to indicate success
	ret			;
kbd_read endp

;
;----------------------------------------------------------------------------
;
;   set_numlock: set numlock LED
;
;   INPUT
;	ES:SI -> numlock setting (ie, "ON" or "OFF")
;
;   OUTPUT
;	None
;
;   OTHER REGS USED
;	None
;
;   HISTORY
;		     Created 16-Nov-1992 by JeffPar
;  BDD1    04-06-93  Check for network server mode added.
;
;----------------------------------------------------------------------------
;
	public	set_numlock

set_numlock proc near
	push	ax
	push	ds
	in	al,64h		      ; BDD1
	test	al,10h		      ; Check for password server mode
	jz	set_done	      ; Yes it is, skip numlock setting
	sub	ax,ax
	mov	ds,ax
	mov	ax,es:[si]	; get 1st 2 bytes of value (ON or OF)
	cmp	ax,word ptr cs:[OnOff+2]; should we turn it off?
	jne	not_off 	; no
	and	byte ptr ds:[0417h],not 20h
	jmp	short set_done
not_off:
	cmp	ax,word ptr cs:[OnOff]	; should we turn it on?
	stc
	jne	set_done	; no
	or	byte ptr ds:[0417h],20h
set_done:
	pop	ds
	pop	ax
	ret
set_numlock endp

;
;----------------------------------------------------------------------------
;
;   menu_check:  check for presence of menu (and other) configuration blocks
;
;   INPUT
;	CX == "organized" config.sys memory image length
;    ES:SI -> "organized" config.sys memory image
;	DS == CS == sysinitseg
;
;   OUTPUT
;	Same as above;	the idea is that menu_check simply transforms
;	a block-structured config.sys image into a conventional image,
;	based on the user's block selection and any other boot-time options
;	the user may have employed...
;
;   OTHER REGS USED
;	All
;
;   NOTES
;	[count] and [org_count] are set to the new config.sys image length
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	menu_check

menu_check  proc near
;
;   Search for SWITCHES, determine if /N or /F are present;  if so, then
;   disable clean/interactive boot options
;
	push	cx		;
	push	si		;
	sub	bx,bx		; remains ZERO until first block
swchk_loop:			;
	call	get_char	; get first char of current line
	jc	swchk_end	; hit eof
	cmp	al,CONFIG_BEGIN ;
	jne	swchk_next1	;
	inc	bx		; remember that we've seen a block
	jmp	short swchk_nextline
swchk_next1:			;
	cmp	al,CONFIG_NUMLOCK
	jne	swchk_next2	;
	or	bx,bx		; only do NUMLOCK commands that exist
	jnz	swchk_nextline	; before the first block
	call	set_numlock	; REM it out so we don't act on it later, too
	mov	byte ptr es:[si-1],CONFIG_REM
	jmp	short swchk_nextline
swchk_next2:			;
	cmp	al,CONFIG_SWITCHES
	jne	swchk_nextline	; this line ain't it
swchk_scan:			;
	call	get_char	; look for /N or /F
swchk_scan1:			;
	cmp	al,LF		; end of line?
	je	swchk_nextline	; yes
	cmp	al,'/'          ; switch-char?
	jne	swchk_scan	; no
	call	get_char	;
	and	al,not 20h	; convert to upper case
	cmp	al,byte ptr swit_n+1
	jne	swchk_scan2	; no
	or	byte ptr [bDisableUI],1
	jmp	swchk_scan	; continue looking for switches of interest
swchk_scan2:			;
	cmp	al,byte ptr swit_f+1
	jne	swchk_scan1	; no
	or	byte ptr [bDisableUI],2
	jmp	swchk_scan	; continue looking for switches of interest
swchk_nextline: 		;
	call	skip_opt_line	;
	jmp	swchk_loop	;
swchk_end:			;
	pop	si		;
	pop	cx		;
;
;   Do the keyboard tests for clean/interactive boot now, but only if
;   the DisableUI flag is still clear
;
	test	byte ptr [bDisableUI],1
	jnz	menu_search
;
;   Wait for 2 seconds first, UNLESS the /F bit was set in bDisableUI, or
;   there is anything at all in the keyboard buffer
;
	call	kbd_read
	jnc	menu_search
	jmp	menu_abort
;
;   Search for MENU block;  it is allowed to be anywhere in config.sys
;
menu_search:
	sub	bx,bx		; if no MENU, default to zero for no_selection
	mov	di,offset szMenu;
	call	find_block	; find the MENU block
	jnc	menu_found	;
	mov	byte ptr [szBoot],0
	jmp	no_selection	; not found
;
;   Process the requested menu color(s)
;
menu_color:
	push	cx		;
	push	dx		;
	mov	dx,0007h	; default color setting
	call	get_number	; get first number
	and	bl,00Fh 	; first # is foreground color (for low nibble)
	mov	ch,bl		; save it in CH
	and	dl,0F0h 	;
	or	dl,bl		;
	call	delim		; did we hit a delimiter
	jne	check_color	; no, all done
	call	get_number	; get next number
	and	bl,00Fh 	; second # is background color (for high nibble)
	mov	dh,bl		; save it in DH
	and	dl,00Fh 	;
	mov	cl,4		;
	shl	bl,cl		;
	or	dl,bl		;
check_color:			;
	cmp	ch,dh		; are foreground/background the same?
	jne	set_color	; no
	xor	dl,08h		; yes, so modify the fgnd intensity
set_color:
	mov	[bMenuColor],dl ;
	pop	dx		;
	pop	cx		;
	jmp	menu_nextitem	;
;
;   Back to our regularly scheduled program (the COLOR and other goop
;   above is there simply to alleviate short jump problems)
;
menu_found:
	mov	byte ptr [bDefBlock],1
	mov	word ptr [offDefBlock],0
	mov	byte ptr [secTimeOut],-1
	and	byte ptr [bQueryOpt],not 2

	call	skip_opt_line	; skip to next line
	sub	dx,dx		; initialize total block count (0 => none yet)
;
;   Process the menu block now
;
menu_process:
	call	get_char	; get first char of current line
	jc	to_menu_getdefault  ; could happen if menu block at end (rare)
	and	al,NOT CONFIG_OPTION_QUERY
	cmp	al,CONFIG_BEGIN ; BEGIN implies END
	je	to_menu_getdefault
	cmp	al,CONFIG_SUBMENU
	je	menu_item	; go process sub-menu
	cmp	al,CONFIG_MENUITEM
	je	menu_item	; go process menu item
	cmp	al,CONFIG_MENUDEFAULT
	je	menu_default	; go process menu default
	cmp	al,CONFIG_MENUCOLOR
	je	menu_color	; go process menu color
	cmp	al,CONFIG_NUMLOCK
	je	menu_numlock	;
	cmp	al,CONFIG_REM	; allow remarks in menu block
	je	menu_nextitem	;
	call	any_delim	; allow blank lines and such
	je	menu_nextitem	;
	stc			;
	call	print_error	; non-MENU command!
	jmp	short menu_nextitem
menu_numlock:
	call	set_numlock
	jmp	short menu_nextitem
to_menu_getdefault:
	jmp	short menu_getdefault
;
;   Save the offset of the default block name, we'll need it later
;
menu_default:
	mov	[offDefBlock],si; save address of default block name
	cmp	byte ptr [secElapsed],0
	jne	timeout_skip	; secElapsed is only zero for the FIRST menu,
	call	skip_token	; and for subsequent menus IF nothing was typed;
	jc	menu_nextitem	; secElapsed becomes -1 forever as soon as
	call	skip_delim	; something is typed
	jc	menu_nextitem	;
	mov	si,bx		;
	call	get_number	; get number (of seconds for timeout)
	cmp	bl,90		; limit it to a reasonable number
	jb	timeout_ok	; (besides, 99 is the largest # my simple
	mov	bl,90		;  display function can handle)
timeout_ok:			;
	mov	[secTimeOut],bl ;
timeout_skip:
	jmp	short menu_nextitem
;
;   Verify that this is a valid menu item by searching for the named block
;
menu_item:
	cmp	dl,MAX_MULTI_CONFIG ; have we reached the max # of items yet?
	jae	menu_nextitem	;
	mov	di,si		; DS:DI -> block name to search for
	call	srch_block	;
	je	menu_itemfound	;
	stc			;
	call	print_error	; print error and pause
	jmp	short menu_nextitem ; if not found, ignore this menu item
;
;   srch_block, having succeeded, returns DI -> past the token that it
;   just matched, which in this case should be a descriptive string;  ES:SI
;   and CX are unmodified
;
menu_itemfound:
	inc	dx		; otherwise, increment total block count
	mov	bx,dx		; and use it to index the arrays of offsets
	mov	abBlockType[bx],al
	add	bx,bx		; of recorded block names and descriptions
;
;   There should be a description immediately following the block name on
;   MENUITEM line;  failing that, we'll just use the block name as the
;   description...
;
	mov	aoffBlockName[bx],si
	mov	aoffBlockDesc[bx],si
	mov	di,bx		; skip_delim modifies BX, so stash it in DI
	call	skip_token	;
	jc	menu_nextitem	; hit eol/eof
	call	skip_delim	;
	jc	menu_nextitem	; hit eol/eof
	xchg	bx,di		;
	mov	aoffBlockDesc[bx],di

menu_nextitem:
	call	skip_opt_line	;
	jmp	menu_process	; go back for more lines
;
;   Display menu items now, after determining which one is default
;
menu_getdefault:
	or	dl,dl		; where there any valid blocks at all?
	jnz	menu_valid	; yes
	sub	bx,bx		; no, so force autoselect of 0
	jmp	menu_autoselect ; (meaning: process common blocks only)
menu_valid:
	sub	bx,bx		;
	mov	[bMaxBlock],dl	; first, record how many blocks we found
	mov	di,[offDefBlock];
	or	di,di		; does a default block exist?
	jz	menu_nodefault	; no
	inc	bx		; yes, walk name table, looking for default
menu_chkdefault:
	push	bx		;
	add	bx,bx		;
	mov	si,aoffBlockName[bx]
	mov	cx,128		; arbitrary maximum length of a name
	push	ds		;
	push	es		;
	pop	ds		;
	call	comp_names	; is this block the same as the default?
	pop	ds		;
	pop	bx		;
	je	menu_setdefault ; yes
	inc	bx		;
	cmp	bl,[bMaxBlock]	; all done searching?
	jbe	menu_chkdefault ; not yet
menu_nodefault:
	mov	bl,1		; if no default, force default to #1
menu_setdefault:
	mov	[bDefBlock],bl	; yes, this will be the initial current block
;
;   If the timeout was explicitly set to 0 (or technically, anything that
;   failed to resolve to a number, like "NONE" or "EAT POTATOES"), then we're
;   supposed to skip menu display and run with the specified default block;
;   however, if the user hit Enter prior to boot, thereby requesting fully
;   INTERACTIVE boot, then we shall display the menu block anyway (though still
;   with no timeout)
;
	cmp	[secTimeOut],0	; is timeout zero? (ie, assume default)
	jne	menu_display	; no
	test	byte ptr [bQueryOpt],1	; yes, but was INTERACTIVE requested?
	jnz	menu_display	; yes, so *don't* assume default after all
	jmp	not_topmenu	;
;
;   Reset the mode, so that we know screen is clean and cursor is home
;
menu_display:
	mov	ah,0Fh		; get current video mode
	int	10h		;
	mov	ah,00h		; just re-select that mode
	int	10h		;
	push	es		;
	mov	ax,40h		; reach down into the ROM BIOS data area
	mov	es,ax		; and save the current (default) video page
	mov	ax,es:[004Eh]	; start address and page #, in case the
	mov	[wCRTStart],ax	; undocumented QUIET option was enabled
	mov	al,es:[0062h]	;
	mov	[bCRTPage],al	;
	mov	ax,word ptr [bMenuPage] ; select new page for menu
	int	10h		;
	mov	ax,0600h	; clear entire screen
	mov	bh,[bMenuColor] ; using this color
	sub	cx,cx		; upper left row/col
	mov	dl,byte ptr es:[CRT_Cols]
	dec	dl		;
	mov	dh,es:[CRT_Rows];
	or	dh,dh		; # of rows valid?
	jnz	menu_clear	; hopefully
	mov	dh,[bLastRow]	; no, use a default
menu_clear:
	int	10h		; clear the screen using the req. attribute
	pop	es		;
	mov	[bLastRow],dh	; save DH
	mov	dx,offset $MenuHeader
	call	print		; cursor now on row 3 (numbered from 0)

	test	byte ptr [bDisableUI],1
	jnz	menu_nostatus	;
	mov	bh,[bMenuPage]	;
	mov	dh,[bLastRow]	; restore DH
	mov	dl,0		; print the status line on row DH, col 0,
	mov	ah,02h		; now that we can trash the cursor position
	int	10h		;
	mov	dx,offset $StatusLine
	call	print		;
	mov	ah,3		; get cursor position
	int	10h		;
	sub	dl,2		;
	mov	[bLastCol],dl	; save column where status char will go

menu_nostatus:
	mov	bx,1		; now prepare to display all the menu items
menu_disploop:
	call	print_item	; print item #BL
	inc	bx		; why "inc bx"?  because it's a 1-byte opcode
	cmp	bl,[bMaxBlock]	; all done?
	jbe	menu_disploop	; not yet
;
;   Set cursor position to just below the menu items
;
	mov	dl,0		; select column
	mov	dh,bl		;
	add	dh,4		; select row below menu
	mov	bh,[bMenuPage]	;
	mov	ah,02h		; set cursor position beneath the block list
	int	10h		;

	mov	dx,offset $MenuPrmpt
	call	print		;
	call	select_item	; make a selection, return # in BX
	mov	dx,offset crlfm ;
	call	print		;
	push	word ptr [bDisableUI]
	or	byte ptr [bDisableUI],1
	call	show_status	; clear the status line now
	pop	word ptr [bDisableUI]
;
;   Now begins the "re-organization" process...
;
menu_autoselect:
	cmp	bx,-1		; clean boot requested?
	jne	normal_boot	; no
	call	disable_autoexec; basically, add a /D to the command.com line
menu_abort:
	sub	cx,cx		; then immediately exit with 0 config.sys image
	jmp	menu_exit	;

normal_boot:
	cmp	bx,-2		; back to top-level menu?
	jne	not_topmenu	; no
	mov	cx,[count]	; yes, start all over
	sub	si,si		;
	jmp	menu_search

not_topmenu:
	cmp	abBlockType[bx],CONFIG_SUBMENU
	jne	not_submenu	;
	add	bx,bx		;
	mov	di,aoffBlockName[bx]
	call	srch_block	; THIS CANNOT FAIL!
	mov	si,di		;
	mov	cx,bx		; ES:SI and CX are ready for another round
	jmp	menu_found

not_submenu:
	add	bx,bx		; get BX -> name of selected block
	mov	bx,aoffBlockName[bx]
;
;   BX should now either be ZERO (meaning no block has been selected) or
;   the offset relative to ES of the block name to be processed (along with
;   all the "common" lines of course)
;
no_selection:
	mov	[offDefBlock],bx; save selection
	mov	cx,[count]	; reset ES:SI and CX for reprocessing
	sub	si,si		;
	push	ds		;
	mov	ds,[config_wrkseg]; this is where we'll store new config.sys image
	assume	ds:nothing	;
	sub	di,di		;
;
;   ES:SI-> config.sys, DS:DI -> new config.sys workspace
;
;   Work our way through the config.sys image again, this time copying
;   all lines that are (A) "common" lines outside any block or (B) lines
;   within the requested block.  Lines inside INCLUDEd blocks are transparently
;   copied by copy_block in a recursive fashion;  the amount of recursion is
;   limited by the fact INCLUDE statements are REMed by copy_block as they are
;   processed and by the number of unique INCLUDE stmts in config.sys...
;
;   BUGBUG 20-Mar-1992 JeffPar:  If we can figure out the lower bound of the
;   stack we're running on, then we should check it inside copy_block
;
copyblock_loop:
	push	bx		; save selected block name
	call	copy_block	; process (named or common) block
	pop	bx		;
	jc	move_config	; hit eof
;
;   copy_block can only return for two reasons:  it hit eof or a new block
;
copyblock_begin:
	push	ax		;
	push	cx		;
	push	si		;
	push	di		; always do "common" blocks
	mov	di,offset szCommon
	push	ds		;
	push	cs		;
	pop	ds		;
	call	comp_names	;
	pop	ds		;
	pop	di		;
	pop	si		;
	pop	cx		;
	pop	ax		;
	je	copyblock_check ;
	or	bx,bx		; is there a block name to check?
	jz	copyblock_skip	; no
	push	di		;
	mov	di,bx		; check block against given block name
	push	ds		;
	push	es		;
	pop	ds		;
	call	comp_names	; is this the block we really want to do?
	pop	ds		;
	pop	di		;
copyblock_check:
	jc	move_config	; hit eof
	jne	copyblock_skip	;
	call	skip_opt_line	;
	jmp	copyblock_loop	;

copyblock_skip: 		;
	call	skip_opt_line	; this ain't the block we wanted, so skip it
	call	get_char	;
	jc	move_config	; hit eof
	and	al,NOT CONFIG_OPTION_QUERY
	cmp	al,CONFIG_BEGIN ;
	je	copyblock_begin ;
	jmp	copyblock_skip	; anything else is just skipped
;
;   To create as little risk to the rest of SysInit as little as possible,
;   and to free the workspace at "config_wrkseg" for creating an environment,
;   copy the new config.sys image to "confbot"
;
move_config:
	mov	cx,di		; now copy workspace at DS:DI to "confbot"
	push	cx		;
;
;   But first, copy the CONFIG=<configuration><0> string to the workspace,
;   since the configuration name only currently exists in the "confbot" area
;
	mov	cx,offset szMenu-offset szBoot-1
	mov	si,offset szBoot; first copy the CONFIG= part
	inc	di		; skip a byte, in case absolutely nothing
				; was copied to the workspace, because we always
				; zero the first byte of the workspace (below)
copy_boot:			;
	lods	byte ptr cs:[si];
	mov	[di],al 	;
	inc	di		;
	loop	copy_boot	;

	push	es		; then copy the configuration name
	mov	cx,128-7	; put an upper limit on the name, to be safe
	mov	si,cs:[offDefBlock]; ES:SI -> default block name
	or	si,si		; valid?
	jnz	l1		; yes
	push	cs		;
	pop	es		;
	mov	si,offset szCommon
l1:	mov	al,es:[si]	;
	call	any_delim	;
	je	l2		;
	mov	[di],al 	;
	inc	si		;
	inc	di		;
	loop	l1		;
l2:	mov	byte ptr [di],LF; terminate the configuration string
	pop	es		;
;
;   Now we can copy "config_wrkseg" (DS) to "confbot" (ES)
;
	sub	di,di		;
	mov	[config_envlen],di
	sub	si,si		;
	pop	cx		; recover the size of "config_wrkseg"

	push	cx		;
	rep	movsb		; moved!
	pop	cx		;
	mov	ax,ds		;
	pop	ds		;
	assume	ds:sysinitseg	;
;
;   Now that the config_wrkseg is available once again, we shall
;   use it to create an environment.  The first thing to go in will be
;   the "CONFIG=configuration" thing.  It is also important to zero
;   the first byte of the workspace, so that copy_envvar knows the buffer
;   is empty.
;
	push	es		;
	mov	es,ax		;
	inc	si		; ES:SI -> "CONFIG=configuration"
	mov	byte ptr es:[0],0;empty the environment block
	call	copy_envvar	; copy envvar at ES:SI to "config_wrkseg"
	pop	es
;
;   Before returning, restore the default video page setting but do NOT
;   do it using INT 10h's Set Active Page function, because if the menu was
;   displayed on a different page, then it's because we don't want to see
;   all the device driver/TSR goop (which goes to the default page)
;
menu_done:
	cmp	byte ptr [bMenuPage],0
	je	menu_exit	;
	push	es		;
	mov	ax,40h		;
	mov	es,ax		;
	mov	ax,[wCRTStart]	;
	mov	es:[004Eh],ax	;
	mov	al,[bCRTPage]	;
	mov	es:[0062h],al	;
	pop	es		;
menu_exit:
	mov	[count],cx	; set new counts
	mov	[org_count],cx	;
;MARMAR (MS62)	sub	si,si		; always return ES:SI pointing to config.sys
	ret
menu_check  endp

;
;----------------------------------------------------------------------------
;
;   copy_envvar:  copy the envvar at ES:SI to "config_wrkseg"
;
;   INPUT
;    ES:SI -> environment variable (in the form "var=string<cr/lf>")
;
;   OUTPUT
;	config_envlen (ie, where to put next envvar) updated appropriately
;	carry set if error (eg, missing =); clear otherwise
;
;   OTHER REGS USED
;	None
;
;   NOTES
;	None
;
;   HISTORY
;	Created 29-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	copy_envvar
	assume	ds:sysinitseg,es:nothing

copy_envvar proc near
	push	cx		;
	push	si		;
	push	ds		;
	push	es		;
	push	es		;
	mov	es,[config_wrkseg]; ES:DI to point to next available byte
	pop	ds		  ; DS:SI to point to envvar
	assume	ds:nothing	;
;
;   Have to calculate the length of the variable name (and if we hit
;   the end of the line before we hit '=', then it's curtains for this
;   config.sys line)
;
;   The check for NULL is important because copy_envvar is also used to copy
;   the initial CONFIG= setting, which will have been zapped by a NULL if no
;   menu block existed (in order to prevent the creation of an environment)
;
	sub	cx,cx		;
copy_varlen:			;
	lodsb			;
	or	al,al		; NULL?
	stc			;
	jz	copy_envexit	; yes, abort
	cmp	al,CR		;
	stc			;
	je	copy_envexit	;
	cmp	al,LF		;
	stc			;
	je	copy_envexit	;
	inc	cx		;
	cmp	al,'='          ;
	jne	copy_varlen	;
	mov	al,0		;
	mov	ah,[si] 	; save char after '='
	sub	si,cx		; back up to given varname
	dec	cx		; CX == # of bytes in varname
	sub	di,di		; start looking for DS:SI at ES:0
copy_varsrch:
	cmp	byte ptr es:[di],al
	je	copy_envprep	; search failed, just copy var
	mov	bx,di		; ES:BX -> start of this varname
	push	cx		;
	push	si		;
	repe	cmpsb		;
	pop	si		;
	pop	cx		;
	jne	copy_varnext	; no match, skip to next varname
	cmp	byte ptr es:[di],'='
	jne	copy_varnext	; no match, there's more characters
;
;   Previous occurrence of variable has been found;  determine the
;   entire length and then destroy it
;
	mov	cx,-1		;
	repne	scasb		; guaranteed to get null (since we put it there)
	push	si		;
	mov	si,di		;
	mov	di,bx		;
	mov	cx,[config_envlen]
	sub	cx,si		; destroy variable now
	rep movs byte ptr es:[di],byte ptr es:[si]
	pop	si		;
copy_envprep:
	cmp	ah,CR		; if there is nothing after the '='
	je	copy_envdel	; then just exit with variable deleted
	cmp	ah,LF		;
	je	copy_envdel	;
	jmp	short copy_envloop

copy_varnext:			;
	push	cx		;
	mov	cx,-1		;
	repne	scasb		;
	pop	cx		;
	jmp	copy_varsrch	;

copy_envloop:			;
	lodsb			;
	cmp	al,CR		;
	je	copy_envdone	;
	cmp	al,LF		;
	je	copy_envdone	;
	stosb			;
	jmp	copy_envloop	;

copy_envdone:			;
	sub	al,al		; do SUB to clear carry as well
	stosb			; always null-terminate these puppies
copy_envdel:			;
	mov	es:[di],al	; and stick another null to terminate the env.
	mov	[config_envlen],di

copy_envexit:			;
	pop	es		;
	pop	ds		;
	assume	ds:sysinitseg	;
	pop	si		;
	pop	cx		;
	ret			;
copy_envvar endp

;
;----------------------------------------------------------------------------
;
;   copy_block:  copy the current block to the new config.sys workspace
;
;   INPUT
;	CX == remaining bytes in "organized" config.sys memory image
;    ES:SI -> remaining bytes in "organized" config.sys memory image
;    DS:DI -> new config.sys workspace (equal in size to the original
;	      config.sys image) where the current block is to be copied
;
;   OUTPUT
;	Same as above
;	AL also equals the last character read from the organized image
;
;   OTHER REGS USED
;	All
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	copy_block

copy_block  proc near
	call	get_char	; check for include
	jc	copy_done	;
	and	al,NOT CONFIG_OPTION_QUERY
	cmp	al,CONFIG_BEGIN ; another BEGIN implies END as well
	je	copy_done	;
;
;   BOOTCON relies on these things being in-memory (sigh)
;
;	cmp	al,CONFIG_REM	; don't bother copying this
;	je	copy_nextline	;
;	cmp	al,CONFIG_COMMENT;don't bother copying this either
;	je	copy_nextline	;
;
;   This is being left out for old reporting of bad command lines;
;   alternatively, we could report the error here and save the rest of
;   sysinit from ever having to deal with bogus CONFIG.SYS stuff...
;
;	cmp	al,CONFIG_UNKNOWN;don't bother copying this either too
;	je	copy_nextline	;
				;
	cmp	al,CONFIG_INCLUDE
	mov	al,ah		; AL == the original line code
	jne	copy_line	; not an "include" line
;
;   We have hit an "INCLUDE" line;  first, REM out the line so that we
;   never try to include the block again (no infinite include loops please),
;   then search for the named block and call copy_block again.
;
	mov	byte ptr es:[si-1],CONFIG_REM
	push	di		;

	mov	di,offset szMenu
	call	comp_names_safe ; don't allow INCLUDE MENU
	je	copy_skip	;

	mov	di,offset szCommon
	call	comp_names_safe ; don't allow INCLUDE COMMON
	je	copy_skip	;

	mov	di,si		; try to find the block
	call	srch_block	;
	mov	dx,di		;
	pop	di		;
	jne	copy_error	; no such block
	push	cx		;
	mov	cx,bx		;
	push	si		;
	dec	dx		;
	mov	si,dx		;
	call	skip_line	; skip the rest of the "block name" line
	call	copy_block	; and copy in the rest of that block
	pop	si		;
	pop	cx		;
	sub	al,al		; force skip_opt_line to skip...
	jmp	short copy_nextline

copy_skip:
	pop	di
copy_error:
	clc			;
	call	print_error	; note that carry is clear, no pause
	jmp	short copy_nextline
;
;   Copy the line at ES:SI to the current location at DS:DI
;
copy_line:
	mov	[di],al 	;
	inc	di		;
	cmp	al,' '          ; is this is a "real" line with a "real" code?
	jb	copy_nextline	; no
	cmp	cs:[config_multi],0
	je	copy_loop	; not a multi-config config.sys, don't embed #s
	call	get_linenum	; BX == line # of line @ES:SI
	mov	[di],bx 	; stash it immediately following the line code
	inc	di		;
	inc	di		;
	jmp	short copy_next ;
copy_loop:			;
	call	get_char	;
	jc	copy_done	; end of file
	mov	[di],al 	;
	inc	di		;
copy_next:
	cmp	al,LF		; done with line?
	jne	copy_loop	; nope

copy_nextline:
	call	skip_opt_line	;
	jmp	copy_block	;

copy_done:
	ret
copy_block  endp

;
;----------------------------------------------------------------------------
;
;   get_linenum:  return line # (in BX) of current line (@ES:SI)
;
;   INPUT
;    ES:SI -> some line in the config.sys memory image
;
;   OUTPUT
;	BX == line # (relative to 1)
;
;   OTHER REGS USED
;	DX
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	get_linenum

get_linenum proc near
	push	ax		;
	sub	bx,bx		; BX == line # (to be returned)
	push	cx		;
	mov	dx,si		; DX == the offset we're looking for
	push	si		;
	mov	cx,cs:[count]	;
	sub	si,si		; prepare to scan entire file
get_linenum_loop:		;
	call	skip_line	;
	jc	get_linenum_done;
	inc	bx		;
	cmp	si,dx		; have we exceeded the desired offset yet?
	jb	get_linenum_loop; no
get_linenum_done:		;
	pop	si		;
	pop	cx		;
	pop	ax		;
	ret			;
get_linenum endp

;
;----------------------------------------------------------------------------
;
;   srch_block:  searches entire config.sys for block name @ES:DI
;
;   INPUT
;	ES -> config.sys image
;    ES:DI -> block name to find
;
;   OUTPUT
;	ZF flag set, if found
;    ES:DI -> just past the name in the block heading, if found
;	BX == # bytes remaining from that point, if found
;
;   OTHER REGS USED
;	None
;
;   NOTES
;	This differs from "find_block" in that it searches the ENTIRE
;	config.sys image, not merely the remaining portion, and that it
;	takes a pointer to block name that is *elsewhere* in the image
;	(ie, ES) as opposed to some string constant in our own segment (DS).
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	srch_block

srch_block  proc near		; returns BX -> named block in CONFIG.SYS
	push	ax		;
	push	cx		;
	mov	cx,cs:[count]	;
	push	si		;
	sub	si,si		;
	push	ds		;
	push	es		;
	pop	ds		;
	call	find_block	;
	mov	di,si		;
	mov	bx,cx		;
	pop	ds		;
	pop	si		;
	pop	cx		;
	pop	ax		;
	ret			;
srch_block  endp

;
;----------------------------------------------------------------------------
;
;   find_block:  searches rest of config.sys for block name @DS:DI
;
;   INPUT
;    DS:DI -> block name to find
;    ES:SI -> remainder of config.sys image
;	CX == remaining size of config.sys image
;
;   OUTPUT
;	ZF flag set, if found (also, CF set if EOF)
;    ES:SI -> where the search stopped (at end of block name or EOF)
;	CX == # bytes remaining from that point
;
;   OTHER REGS USED
;	AX
;
;   NOTES
;	This differs from "srch_block" in that it searches only the
;	remaining portion of the config.sys image and leaves SI and CX
;	pointing to where the search left off, and that it takes a pointer
;	to search string in our own segment (DS:DI instead of ES:DI).
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	find_block

find_block  proc near
	call	get_char	; get line code
	jc	find_exit	; end of file
	and	al,NOT CONFIG_OPTION_QUERY
	cmp	al,CONFIG_BEGIN ; beginning of a block?
	je	check_line	; no
	cmp	al,CONFIG_INCLUDE
	jne	next_line	;
	or	cs:[config_multi],1
	jmp	short next_line ;
check_line:
	or	cs:[config_multi],1
	call	comp_names	; compare block names
	jbe	find_exit	; end of file, or names matched
next_line:
	call	skip_opt_line	; no, so skip to next line
	jmp	find_block	;
find_exit:
	ret
find_block  endp

;
;----------------------------------------------------------------------------
;
;   comp_names:  compares keyword @DS:DI to position in config.sys @ES:SI
;
;   INPUT
;    DS:DI -> keyword to compare
;    ES:SI -> position in config.sys
;	CX == remaining bytes in config.sys
;
;   OUTPUT
;	ZF flag set, if match (also, CF set if EOF)
;    ES:SI -> where the comparison stopped (at end of block name or EOF)
;	CX == # bytes remaining from that point
;
;   OTHER REGS USED
;	AX
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	comp_names

comp_names  proc near
	push	di		;
comp_loop:			;
	call	get_char	;
	jc	comp_exit	;
	call	any_delim	; is next character a delimiter?
	mov	ah,[di] 	; (get next character we're supposed to match)
	je	comp_almost	; yes, it *could* be a match
	inc	di		;
	and	ax,not 2020h	; BUGBUG -- assumes both names are alphanumeric -JTP
	cmp	al,ah		; match?
	je	comp_loop	; yes, keep looking at the characters
	clc			; prevent erroneous eof indication: clear carry
comp_exit:			;
	pop	di		;
	ret			;
comp_almost:			;
	xchg	al,ah		; we don't know for sure if it's a match
	call	any_delim	; until we verify that the second string has
	xchg	al,ah		; been exhausted also...
	jmp	comp_exit	; if we are, this call to any_delim will tell...
comp_names  endp

	public	comp_names_safe

comp_names_safe proc near
	push	ax
	push	cx
	push	si
	push	ds
	push	cs
	pop	ds
	call	comp_names
	pop	ds
	pop	si
	pop	cx
	pop	ax
	ret
comp_names_safe endp


;
;----------------------------------------------------------------------------
;
;   print_item:  display menu item #BL
;
;   INPUT
;	BL == menu item # to display
;
;   OUTPUT
;	Menu item displayed, with appropriate highlighting if BL == bDefBlock
;
;   OTHER REGS USED
;	None
;
;   NOTES
;	This function saves/restores the current cursor position, so you
;	needn't worry about it.
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	print_item

print_item  proc near		; prints menu item #BL (1 to N)
	push	ax		;
	push	bx		;
	push	cx		;
	push	dx		;
	push	si		;
	mov	ah,03h		; get cursor position
	mov	bh,[bMenuPage]	; always page zero
	int	10h		; DH/DL = row/column
	push	dx		; save it
	mov	ah,02h		; set cursor position
	mov	dh,bl		;
	add	dh,3		;
	mov	dl,5		;
	int	10h		; set cursor position for correct row/col
	mov	al,bl		;
	add	al,'0'          ; convert menu item # to ASCII digit
	mov	ah,[bMenuColor] ; normal attribute
	cmp	bl,[bDefBlock]	; are we printing the current block?
	jne	print_other	; no
	or	ah,70h		; yes, set bgnd color to white
	mov	ch,ah		;
	mov	cl,4		;
	rol	ch,cl		;
	cmp	ch,ah		; are fgnd/bgnd the same?
	jne	print_other	; no
	xor	ah,08h		; yes, so modify the fgnd intensity
print_other:			;
	mov	bh,0		;
	add	bx,bx		;
	mov	di,aoffBlockDesc[bx]
	mov	bl,ah		; put the attribute in the correct register now
	mov	bh,[bMenuPage]	; get correct video page #
	mov	ah,09h		; write char/attr
	mov	cx,1		;
	int	10h		;
	inc	dl		; increment column
	mov	ah,02h		;
	int	10h		;
	mov	ax,0900h+'.'    ;
	int	10h		; display '.'
	inc	dl		; increment column
	mov	ah,02h		;
	int	10h		;
	mov	ax,0900h+' '    ;
	int	10h		; display ' '
	inc	dl		; increment column
	mov	ah,02h		;
	int	10h		;
	push	es		;
print_loop:			;
	mov	al,es:[di]	; get a character of the description
	inc	di		;
	cmp	al,TAB		; substitute spaces for tabs
	jne	print_nontab	;
	mov	al,' '          ;
print_nontab:			;
	cmp	al,' '          ;
	jb	print_done	; stop at the 1st character < space
	cmp	al,'$'          ;
	je	print_done	; also stop on $
	mov	ah,09h		; display function #
	int	10h		;
	inc	dl		; increment column
	cmp	dl,78		; far enough?
	jae	print_done	; yes
	mov	ah,02h		;
	int	10h		;
	jmp	print_loop	;
print_done:			;
	pop	es		;
	pop	dx		;
	mov	ah,02h		;
	int	10h		; restore previous row/col
	pop	si		;
	pop	dx		;
	pop	cx		;
	pop	bx		;
	pop	ax		;
	ret			;
print_item  endp

;
;----------------------------------------------------------------------------
;
;   select_item:  wait for user to select menu item, with time-out
;
;   INPUT
;	None
;
;   OUTPUT
;	BX == menu item # (1-N), or -1 for clean boot
;	Selected menu item highlighted
;	Cursor positioned beneath menu, ready for tty-style output now
;
;   OTHER REGS USED
;	None
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	select_item

select_item proc near		; returns digit value in BX (trashes AX/CX/DX)
	mov	bl,[bDefBlock]	; BL will be the default block #
	mov	al,bl		;
	call	disp_num	;
	call	show_status	; display current interactive status
	cmp	byte ptr [secTimeOut],-1
	je	input_key	; no time-out, just go to input
	mov	ah,Get_Time	;
	int	21h		;
	mov	bh,dh		; BH = initial # of seconds
check_time:
	mov	al,[secTimeOut] ;
	sub	al,[secElapsed] ;
	jae	show_time	;
	or	byte ptr [bQueryOpt],2	; disable all further prompting
	mov	byte ptr [secElapsed],0
	jmp	select_done	; time's up!
show_time:
	push	bx		;
	mov	bl,al		; save # in BL
	mov	bh,[bMenuPage]	;
	mov	ah,03h		; get cursor position
	int	10h		;
	push	dx		;
	add	dl,8		; move cursor to the right
	mov	ah,02h		; set cursor position
	int	10h		;
	mov	dx,offset $TimeOut
	call	print		; print the "Time remaining: " prompt
	mov	al,bl		; recover # from BL
	cbw			; this works because AL is always <= 90
	mov	cl,10		;
	div	cl		; AL = tens digit, AH = ones digit
	mov	cl,ah		;
	add	al,'0'          ;
	mov	ah,0Eh		;
	int	10h		; write TTY tens digit
	mov	al,cl		;
	add	al,'0'          ;
	mov	ah,0Eh		;
	int	10h		; write TTY ones digit
	pop	dx
	mov	ah,02h		; set cursor position back to where it was
	int	10h		;
	pop	bx		;
input_key:
	mov	ah,Raw_Con_IO	;
	mov	dl,0FFh 	; input request
	int	21h		;
	jnz	got_key 	;
	cmp	byte ptr [secTimeOut],-1; is there a time-out?
	je	input_key	; no, just go back to input
	mov	ah,Get_Time	;
	int	21h		; DH = seconds
	mov	ah,dh		;
	sub	dh,bh		; should generally be zero or one
	mov	bh,ah		;
	jnc	got_time	;
	mov	dh,1		; it wrapped back to zero, so assume one
got_time:
	or	dh,dh		; any change?
	jz	input_key	; no
	add	[secElapsed],dh ;
	jmp	check_time	;
got_key:
	push	ax		;
	mov	ax,-1		; zap both secTimeOut and secElapsed
	xchg	word ptr [secTimeOut],ax
	cmp	al,-1		; was time-out already disabled?
	je	timeout_disabled; yes
	push	bx		; let's disable # seconds display
	mov	ax,0A20h	; write multiple spaces
	mov	bx,word ptr [bMenuColor]
	mov	cx,80		; 80 of them, to be safe
	int	10h		; to completely obliterate # seconds display
	pop	bx		;
timeout_disabled:
	pop	ax		;
	or	al,al		; extended key pressed?
	jnz	normal_key	; no
	int	21h		; get the next part of the key then
	jz	input_key	; hmmm, what happened to the second part?

	cmp	al,48h		; up arrow?
	jne	not_up		; no
	cmp	bl,1		; are we as up as up can get?
	jbe	input_key	; yes, ignore it
	dec	[bDefBlock]	;
	call	print_item	; re-print the current item
	dec	bl		; and then print the new current item
	jmp	short print1
not_up:
	cmp	al,50h		; down arrow?
	jne	not_down	; no
	cmp	bl,[bMaxBlock]	; are we as down as down can get?
	jae	to_input_key	; yes, ignore it
	inc	[bDefBlock]	;
	call	print_item	; re-print the current item
	inc	bx		; and then print the new current item
print1: mov	al,bl		;
print2: call	print_item	;
	call	disp_num	;
to_input_key:
	jmp	input_key	;
not_down:
	test	byte ptr [bDisableUI],1
	jnz	to_input_key	; don't allow F8 or F5
	cmp	al,42h		; F8 function key?
	jne	not_f8		; no
	xor	byte ptr [bQueryOpt],1
	call	show_status	;
	jmp	input_key	;
not_f8:
	cmp	al,3Fh		; F5 function key?
	jne	to_input_key	; no
	or	byte ptr [bQueryOpt],4	; no more queries  ;MARMAR MS62
	mov	bx,-1		; special return code (-1) indicating clean boot
	mov	al,' '          ; don't want to display anything really;
	jmp	short disp_input; just want to display the cr/lf sequence...

normal_key:
	cmp	al,0Dh		; Enter?
	je	select_done	; yes
	cmp	al,08h		; backspace?
	jne	not_backspace	; no
	mov	bx,-2		; yes, special return code
	ret			;
not_backspace:
	sub	al,'0'          ; is greater than '0'?
	jbe	to_input_key	; no
	cmp	al,[bMaxBlock]	; is less than or equal to the maximum digit?
	ja	to_input_key	; no
	mov	[bDefBlock],al	;
	call	print_item	; redisplay the current selection
	mov	bl,al		; set new selection
	jmp	print2

select_done:
	mov	bh,0		; return a full 16-bit value (for indexing)
	mov	al,bl		;
	add	al,'0'          ; convert it into a digit, then display it
	if2
	.errnz	disp_input-$	; fall into disp_input
	endif
select_item endp

;
;----------------------------------------------------------------------------
;
;   disp_input:  display a single character + cr/lf
;
;   INPUT
;	AL == character to display
;
;   OUTPUT
;	None
;
;   OTHER REGS USED
;	None
;
;   NOTES
;	This function is used not only for the menu input selection but
;	also for the interactive line prompting (the y/n/a thing).
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	disp_input

disp_input  proc near
	push	ax
	cmp	al,' '
	jae	disp_ok
	mov	al,' '
disp_ok:
	mov	dl,al
	mov	ah,Std_Con_Output
	int	21h
	mov	dx,offset crlfm
	call	print
	pop	ax
	ret
disp_input  endp

disp_num    proc near
	push	bx
	add	al,'0'
	mov	ah,0Ah
	mov	bx,word ptr [bMenuColor]
	mov	cx,1
	int	10h
	pop	bx
	ret
disp_num    endp

;
;----------------------------------------------------------------------------
;
;   show_status:  display current interactive mode setting (on/off/none)
;
;   INPUT
;	None
;
;   OUTPUT
;	None
;
;   OTHER REGS USED
;	None
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	show_status

show_status proc near
	push	bx		; BL = video page #
	mov	bx,word ptr [bMenuColor]
	mov	ah,03h		; get cursor position
	int	10h		;
	push	dx		; save it
	mov	ah,02h		; set cursor position
	mov	dx,word ptr [bLastCol]	 ; set correct row/col
	test	byte ptr [bDisableUI],1
	jz	show_onoff	; just show on/off
	mov	dl,0		;
	int	10h		;
	mov	ax,0A20h	; write multiple spaces
	mov	cx,80		; 80 of them, to be exact
	int	10h		; to obliterate the status line
	jmp	short show_done ;
show_onoff:			;
	int	10h		;
	mov	al,$NO		; assume OFF
	cmp	byte ptr [bQueryOpt],1	; is interactive mode on?
	jne	show_noton	; no
	mov	al,$YES 	; yes
show_noton:			;
	mov	ah,0Eh		; write TTY
	int	10h		;
show_done:			;
	pop	dx		;
	mov	ah,02h		;
	int	10h		; restore original cursor position
	pop	bx		;
	ret			;
show_status endp

;
;----------------------------------------------------------------------------
;
;   skip_token:  advances ES:SI/CX past the current token
;
;   INPUT
;    ES:SI -> position in config.sys
;	CX == remaining bytes in config.sys
;
;   OUTPUT
;	CF set if EOL/EOF hit
;	AL == 1st char of delimiter
;    ES:SI -> just past the delimiter
;	CX == # bytes remaining from that point
;
;   OTHER REGS USED
;	AX
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	skip_token

skip_token  proc near
	call	get_char
	jc	skip_token_done
	call	any_delim
	jne	skip_token
skip_check_eol:
	cmp	al,CR
	je	skip_token_eol
	cmp	al,LF
	je	skip_token_eol
	clc
	jmp	short skip_token_done
skip_token_eol:
	stc
skip_token_done:
	ret
skip_token  endp

;
;----------------------------------------------------------------------------
;
;   skip_delim:  advances ES:SI/CX past the current delimiter
;
;   INPUT
;    ES:SI -> position in config.sys
;	CX == remaining bytes in config.sys
;
;   OUTPUT
;	CF set if EOF hit
;	AL == 1st char of token
;    ES:SI -> just past the token
;	CX == # bytes remaining from that point
;    ES:BX -> new token (since ES:SI is already pointing 1 byte past token)
;
;   OTHER REGS USED
;	AX
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	skip_delim

skip_delim  proc near		; returns carry set if eol/eof
	call	get_char	;
	lea	bx,[si-1]	; also returns BX -> next token
	jc	skip_token_done ;
	call	delim		;
	je	skip_delim	;
	jmp	skip_check_eol	;
skip_delim  endp

;
;----------------------------------------------------------------------------
;
;   skip_opt_line:  same as skip_line provided AL != LF
;
;   INPUT
;	AL == last character read
;    ES:SI -> position in config.sys
;	CX == remaining bytes in config.sys
;
;   OUTPUT
;	CF set if EOF hit
;	AL == 1st char of new line
;    ES:SI -> just past 1st char of new line
;	CX == # bytes remaining from that point
;
;   OTHER REGS USED
;	AX
;
;   NOTES
;	In other words, the purpose here is to skip to the next line,
;	unless ES:SI is already sitting at the front of the next line (which
;	it would be if the last character fetched -- AL -- was a linefeed)
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	skip_opt_line

skip_opt_line	proc near
	cmp	al,LF
	je	skip_line_done
	if2
	.errnz	skip_line-$	; fall into skip_line
	endif
skip_opt_line	endp

;
;----------------------------------------------------------------------------
;
;   skip_line:	skip to the next line
;
;   INPUT
;    ES:SI -> position in config.sys
;	CX == remaining bytes in config.sys
;
;   OUTPUT
;	CF set if EOF hit
;    ES:SI -> just past 1st char of new line
;	CX == # bytes remaining from that point
;
;   OTHER REGS USED
;	AX
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	skip_line

skip_line   proc near
	call	get_char
	jc	skip_line_done
	cmp	al,LF
	jne	skip_line
skip_line_done:
	ret
skip_line   endp

;
;----------------------------------------------------------------------------
;
;   get_number:  return binary equivalent of numeric string
;
;   INPUT
;    ES:SI -> position in config.sys
;	CX == remaining bytes in config.sys
;
;   OUTPUT
;	AL == non-digit encountered
;	BX == binary #
;    ES:SI -> just past 1st non-digit
;	CX == # bytes remaining from that point
;
;   OTHER REGS USED
;	AX
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	get_number

get_number  proc near
	sub	bx,bx		; BX = result
num_loop:
	call	get_char	;
	jc	num_done	;
	cmp	al,'0'          ; convert to value
	jb	num_done	; no more number
	cmp	al,'9'          ;
	ja	num_done	;
	push	ax		;
	mov	ax,10		;
	push	dx		;
	mul	bx		;
	pop	dx		;
	mov	bx,ax		;
	pop	ax		;
	sub	al,'0'          ;
	cbw			;
	add	bx,ax		;
	jmp	num_loop	;
num_done:
	ret
get_number  endp

;
;----------------------------------------------------------------------------
;
;   get_char:  return next character, advance ES:SI, and decrement CX
;
;   INPUT
;    ES:SI -> position in config.sys
;	CX == remaining bytes in config.sys
;
;   OUTPUT
;	AL == next character
;    ES:SI -> just past next character
;	CX == # bytes remaining from that point
;
;   OTHER REGS USED
;	AX
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	get_char

get_char    proc near
	sub	cx,1		; use SUB to set carry,zero
	jb	get_fail	; out of data
	lods	byte ptr es:[si];
	mov	ah,al		;
	ret			;
get_fail:			; restore CX to zero
	mov	cx,0		; leave carry set, zero not set
nearby_ret:
	ret
get_char    endp

;
;----------------------------------------------------------------------------
;
;   query_user:  ask user whether to execute current config.sys command
;
;   INPUT
;	AL == current command code
;    ES:SI -> current command line in config.sys
;    config_cmd == current command code, but with QUERY bit intact
;		   (00h used to generate "Process AUTOEXEC.BAT" prompt)
;
;   OUTPUT
;	CF set if command should be ignored (it is also REM'ed out)
;
;   OTHER REGS USED
;	BX, CX, DX, DI
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	query_user

query_user  proc near
	test	byte ptr [bQueryOpt],4	; answer no to everything?
	jz	@F			;
	jmp	skip_all		;
@@:	test	byte ptr [bQueryOpt],2	; answer yes to everything?
	jnz	nearby_ret		; yes (and return carry clear!)
	push	ax			;
	mov	al,[config_cmd] 	;
	test	byte ptr [bQueryOpt],1	; query every command?
	jnz	query_all		; yes
	test	al,CONFIG_OPTION_QUERY	;
	jnz	query_all		;
	jmp	do_cmd			;
query_all:
;
;   Search for the command code (AL) in "comtab", and then print
;   out the corresponding keyword, followed by the rest of the actual
;   line pointed to by ES:SI
;
	push	si			; save pointer to rest of CONFIG.SYS line
	mov	dx,offset $AutoPrmpt	;
	and	al,NOT CONFIG_OPTION_QUERY
	jz	generic_prompt		; config_cmd must have been 0

	mov	dh,al			; save config_cmd in DH
	sub	bx,bx			;
	mov	di,offset comtab	;
find_match:				;
	mov	bl,[di] 		; get size of current keyword
	or	bl,bl			;
	jz	line_print		; end of table
	inc	di			;
	cmp	al,[di+bx]		; match?
	je	cmd_match		; yes
	lea	di,[di+bx+1]		; otherwise, skip this command code
	jmp	find_match		; loop
cmd_match:				;
	mov	cl,[di-1]		;
	mov	ch,0			;
	mov	ah,Std_Con_Output
cmd_print:				;
	mov	al,[di] 		;
	inc	di			;
	mov	dl,al			;
	int	21h			;
	loop	cmd_print		;
	mov	dl,'='                  ;
	cmp	dh,CONFIG_SET		; for SET commands, don't display a '='
	jne	cmd_notset		;
	mov	dl,' '                  ;
cmd_notset:
	int	21h			; '=' looks funny on SET commands
line_print:				;
	lods	byte ptr es:[si]	;
	or	al,al			;
	jnz	non_null		;
	mov	al,' '                  ;
non_null:				;
	cmp	al,' '                  ; control code?
	jb	prompt_user		; yes, assume end of line
	jne	non_space		;
	cmp	byte ptr es:[si],' '    ;
	jb	prompt_user		;
non_space:				;
	mov	dl,al			;
	mov	ah,Std_Con_Output	;
	int	21h			;
	jmp	line_print		;
prompt_user:				;
	mov	dx,offset $InterPrmpt	;

generic_prompt:
	call	print			;
input_loop:				;
	mov	ah,0			; read a key
	int	16h			;
	or	al,al			; is it a function key?
	jnz	not_func		; no
	cmp	ah,3Fh			; F5 function key?
	jne	input_loop		; no
	mov	al,$NO			;
	or	byte ptr [bQueryOpt],4	; no more queries
	jmp	short legal_char	;
not_func:
     ifndef RUS
	and	al,not 20h		; converting to upper case
     endif
	cmp	al,$NO			; verify character is legal
	je	legal_char		;
	cmp	al,$YES 		;
	je	legal_char		;
	cmp	[config_cmd],0		;
	je	input_loop		; don't allow Esc on this query
	cmp	al,1Bh			; Esc?
	jne	input_loop		;
	or	byte ptr [bQueryOpt],2	; no more interactive boot prompts
	mov	al,$YES
legal_char:				;
	call	disp_input		;
	pop	si			; restore pointer to rest of CONFIG.SYS line

	cmp	al,$NO			; process line?
	je	skip_cmd		; no
do_cmd:
	pop	ax			;
	clc				; just do the command
	ret

skip_cmd:
	pop	ax			;
skip_all:
	mov	ah,CONFIG_REM		; fake out the rest of sysinit's processing
	stc
	ret
query_user  endp

;
;----------------------------------------------------------------------------
;
;   print_error:  displays multi-config error conditions
;
;   INPUT
;    Carry set to pause, clear to not
;    ES:SI -> current command line in config.sys
;
;   OUTPUT
;	None
;
;   OTHER REGS USED
;	None
;
;   NOTES
;	None
;
;   HISTORY
;	Created 16-Mar-1992 by JeffPar
;
;----------------------------------------------------------------------------
;
	public	print_error
	extrn	error_line:near
	extrn	linecount:word

print_error proc near
	push	ax
	push	bx
	push	cx
	push	dx
	push	ds
	push	cs
	pop	ds
	pushf
	call	get_linenum
	mov	[linecount],bx
	call	error_line
	popf
	jnc	pe_ret
	mov	dx,offset $PauseMsg
	call	print
	mov	ax,0C07h		; flush input buffer, then wait for key
	int	21h			; wait for a key
	or	al,al			; extended key?
	jnz	@F			; no
	mov	ah,07h			; yes
	int	21h			; eat it too
@@:	mov	dx,offset crlfm
	call	print
pe_ret: pop	ds
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
print_error endp


;
;   This function is very simple:  it merely prepends a "/D" to the
;   command-line for the shell;  this (undocumented) switch disables
;   AUTOEXEC.BAT processing and the date/time prompt that is usually
;   displayed when there's no AUTOEXEC.BAT.
;
;   Entry: DS == CS == Bios_Init
;
;   Exit:  none
;
;   Uses:  AX, BX, CX, DX
;
	public	disable_autoexec

disable_autoexec proc near

	test	byte ptr [bQueryOpt],4	;
	jz	disable_exit		; clean boot wasn't selected

	test	byte ptr [bShellSwitches],1
	jnz	disable_exit		; autoexec already disabled

	or	byte ptr [bShellSwitches],1

	or	word ptr [bQueryOpt],0102h
	mov	dx,' D'                 ; prepare to stuff a /D

stuff_switch:
	mov	al,[command_line-1]	; get default switchchar
	or	al,al			; anything there?
	jz	disable_exit		; no, disable_autoexec already called
	mov	bl,[command_line]	;
	mov	bh,0			; BX == command-line length
	mov	cx,bx			;
	add	bl,3			;
	cmp	bl,126			;
	ja	disable_exit		;
	mov	[command_line],bl	; update length
	add	bx,offset command_line+1; make sure we move the NULL too
	inc	cx			; (just for consistency sake)
disable_loop:				;
	mov	ah,[bx-3]		;
	mov	[bx],ah 		;
	dec	bx			;
	loop	disable_loop		;
	mov	[bx-2],al		;
	mov	word ptr [bx-1],dx	; switch is stuffed into place now
disable_exit:				;
	ret				;
disable_autoexec endp

;
;   This function is also very simple:	it prepends a "/Y" to the
;   command-line for the shell;  this (undocumented) switch enables
;   AUTOEXEC.BAT prompting.  It is called at the end of menu processing
;   (ie, menu_exit), but does nothing unless bQueryOpt == 1.
;
;   Entry: DS == CS == Bios_Init
;
;   Exit:  none
;
;   Uses:  AX, BX, CX, DX
;
	public	prompt_autoexec

prompt_autoexec proc near

	cmp	[bQueryOpt],1		; interactive mode enabled?
	jne	disable_exit		; no

	test	byte ptr [bShellSwitches],02h
	jnz	disable_exit		; autoexec already disabled

	or	byte ptr [bShellSwitches],02h

	mov	dx,' Y'
	jmp	stuff_switch		; stuff the switch

prompt_autoexec endp

	assume	ds:nothing

endif  ;MULTI_CONFIG

;
;----------------------------------------------------------------------------
;
; procedure : delim
;
;----------------------------------------------------------------------------
;
ifdef	MULTI_CONFIG
any_delim proc	near
	cmp	al,CR
	je	delim_ret
	cmp	al,LF
	je	delim_ret
	cmp	al,'['
	je	delim_ret
	cmp	al,']'
	je	delim_ret
	if2
	.errnz	delim-$
	endif
any_delim endp
endif  ;MULTI_CONFIG

delim	proc	near
	cmp	al,'/'          ; ibm will assume "/" as an delimeter.
	jz	delim_ret

	cmp	al, 0		; special case for sysinit!!!
	jz	delim_ret

org_delim:			; used by organize routine except for getting
	cmp	al,' '          ;the filename.
	jz	delim_ret
	cmp	al,TAB
	jz	delim_ret
	cmp	al,'='
	jz	delim_ret
	cmp	al,','
	jz	delim_ret
	cmp	al,';'
ifdef	MULTI_CONFIG
;
;   Make sure there's no chance of a false EOF indication
;
	clc
endif
delim_ret:
	ret
delim	endp

;
;----------------------------------------------------------------------------
;
; procedure : newline
;
;  newline returns with first character of next line
;
;----------------------------------------------------------------------------
;

newline proc	near

	call	getchr			;skip non-control characters
	jc	nl_ret
	cmp	al,lf			;look for line feed
	jnz	newline
	call	getchr
nl_ret:
	ret

newline endp

;
;----------------------------------------------------------------------------
;
; procedure : mapcase
;
;----------------------------------------------------------------------------
;
mapcase proc	near
	push	cx
	push	si
	push	ds

	push	es
	pop	ds

ifdef	MULTI_CONFIG
	mov	bl,al			; same cmd code this line
else
	xor	si,si
endif

convloop:
	lodsb

	ifdef	DBCS
	call	testkanj
	jz	normconv		; if this is not lead byte

	mov	ah,al
	lodsb				; get tail byte
	cmp	ax,DB_SPACE
	jnz	@f			; if this is not dbcs space
	mov	word ptr [si-2],'  '    ; set 2 single space
@@:

	dec	cx
	jcxz	convdone		; just ignore 1/2 kanji error
	jmp	short noconv

;fall through, know al is not in 'a'-'z' range

normconv:
	endif

	cmp	al,'a'
	jb	noconv
	cmp	al,'z'
	ja	noconv
	sub	al,20h
	mov	[si-1],al
noconv:
ifdef	MULTI_CONFIG
;
;   When MULTI_CONFIG enabled, "mapcase" is used to map everything to
;   upper-case a line at a time, after we've been able to figure out whether
;   the line is a SET command or not (since we don't want to upper-case
;   anything after the "=" in a SET)
;
	cmp	bl,CONFIG_SET		; preserve case for part of the line?
	jne	check_eol		; no, just check for end-of-line
	cmp	al,'='                  ; separator between SET var and value?
	je	convdone		; yes
check_eol:
	cmp	al,cr
	je	convdone
	cmp	al,lf
	je	convdone
endif
	loop	convloop

convdone:
	pop	ds
	pop	si
	pop	cx
	ret

	ifdef	DBCS

	public	testkanj
testkanj:
	push	si
	push	ds

	push	ax
	mov	ax,6300h		; get dos dbcs vector
	int	21h
	pop	ax

bdbcs_do:

	cmp	ds:word ptr [si],0	; end of lead byte info?
	jz	bdbcs_notfound		; jump if so
	cmp	al,ds:[si]		; less than first byte character?
	jb	bdbcs_next		; jump if not
	cmp	al,ds:[si+1]		; grater than first byte character?
	ja	bdbcs_next

bdbcs_found:

	push	ax
	xor	ax,ax
	inc	ax			; reset zero flag
	pop	ax

bdbcs_exit:

	pop	ds
	pop	si
	ret

bdbcs_notfound:

	push	ax
	xor	ax,ax			; set zero flag
	pop	ax
	jmp	short bdbcs_exit

bdbcs_next:

	add	si,2			; points next lead byte table
	jmp	short bdbcs_do

	endif  ; DBCS

mapcase endp

ENDIF ; CONFIGPROC

;
;----------------------------------------------------------------------------
;
; procedure : round
;
; round the values in memlo and memhi to paragraph boundary.
; perform bounds check.
;
;----------------------------------------------------------------------------
;

round	proc	near

	push	ax
	mov	ax,[memlo]

	call	pararound		; para round up

	add	[memhi],ax
	mov	[memlo],0
	mov	ax,memhi		; ax = new memhi
	cmp	ax,[alloclim]		; if new memhi >= alloclim, error
	jae	mem_err
	test	cs:[setdevmarkflag], for_devmark
	jz	skip_set_devmarksize
	push	es
	push	si
	mov	si, cs:[devmark_addr]
	mov	es, si
	sub	ax, si
	dec	ax
	mov	es:[devmark_size], ax	; paragraph
	and	cs:[setdevmarkflag], not_for_devmark
	pop	si
	pop	es
skip_set_devmarksize:
	pop	ax
	clc				;clear carry
	ret

mem_err:
	mov	dx,offset badmem
	push	cs
	pop	ds
	call	print
	jmp	stall

round	endp

IFDEF	CONFIGPROC
;
;----------------------------------------------------------------------------
;
; procedure : calldev
;
;----------------------------------------------------------------------------
;
calldev proc	near

	mov	ds,word ptr cs:[DevEntry+2]
	add	bx,word ptr cs:[DevEntry]	;do a little relocation
	mov	ax,ds:[bx]

	push	word ptr cs:[DevEntry]
	mov	word ptr cs:[DevEntry],ax
	mov	bx,offset packet
	call	[DevEntry]
	pop	word ptr cs:[DevEntry]
	ret

calldev endp

;
;----------------------------------------------------------------------------
;
; procedure : todigit
;
;----------------------------------------------------------------------------
;
todigit proc	near
	sub	al,'0'
	jb	notdig
	cmp	al,9
	ja	notdig
	clc
	ret
notdig:
	stc
	ret
todigit endp

;
;----------------------------------------------------------------------------
;
; procedure : getnum
;
; getnum parses a decimal number.
; returns it in ax, sets zero flag if ax = 0 (may be considered an
; error), if number is bad carry is set, zero is set, ax=0.
;
;----------------------------------------------------------------------------
;

getnum	proc	near

	push	bx
	xor	bx,bx			; running count is zero

b2:
	call	todigit 		; do we have a digit
	jc	badnum			; no, bomb

	xchg	ax,bx			; put total in ax
	push	bx			; save digit
	mov	bx,10			; base of arithmetic
	mul	bx			; shift by one decimal di...
	pop	bx			; get back digit
	add	al,bl			; get total
	adc	ah,0			; make that 16 bits
	jc	badnum			; too big a number

	xchg	ax,bx			; stash total

	call	getchr			;get next digit
	jc	b1			; no more characters
	cmp	al, ' '                 ; space?
	jz	b15			; then end of digits
	cmp	al, ','                 ; ',' is a seperator!!!
	jz	b15			; then end of digits.
	cmp	al, tab 		; tab
	jz	b15
	cmp	al,sepchr		; allow 0 or special separators
	jz	b15
	cmp	al,'/'                  ; see if another switch follows
	nop				; cas - remnant of old bad code
	nop
	jz	b15
	cmp	al,lf			; line-feed?
	jz	b15
	cmp	al,cr			; carriage return?
	jz	b15
	or	al,al			; end of line separator?
	jnz	b2			; no, try as a valid char...

b15:
	inc	count			; one more character to s...
	dec	chrptr			; back up over separator
b1:
	mov	ax,bx			; get proper count
	or	ax,ax			; clears carry, sets zero accordingly
	pop	bx
	ret
badnum:
	mov	sepchr,0
	xor	ax,ax		; set zero flag, and ax = 0
	pop	bx
	stc			; and carry set
	ret

getnum	endp

;*****************************************************************

setdoscountryinfo	proc	near

;input: es:di -> pointer to dos_country_cdpg_info
;	ds:0  -> buffer.
;	si = 0
;	ax = country id
;	dx = code page id. (if 0, then use ccsyscodepage as a default.)
;	bx = file handle
;	this routine can handle maxium 438 country_data entries.
;
;output: dos_country_cdpg_info set.
;	 carry set if any file read failure or wrong information in the file.
;	 carry set and cx = -1 if cannot find the matching country_id, codepage
;	 _id in the file.

	push	di
	push	ax
	push	dx

	xor	cx,cx
	xor	dx,dx
	mov	ax,512			;read 512 bytes
	call	readincontrolbuffer	;read the file header
	jc	setdosdata_fail

	push	es
	push	si

	push	cs
	pop	es

	mov	di,offset country_file_signature
	mov	cx,8			;length of the signature
	repz	cmpsb

	pop	si
	pop	es
	jnz	setdosdata_fail 	;signature mismatch

	add	si,18			;si -> county info type
	cmp	byte ptr ds:[si],1	;only accept type 1 (currently only 1 header type)
	jne	setdosdata_fail 	;cannot proceed. error return

	inc	si			;si -> file offset
	mov	dx,word ptr ds:[si]	;get the info file offset.
	mov	cx,word ptr ds:[si+2]
	mov	ax,6144 		;read 6144 bytes.
	call	readincontrolbuffer	;read info
	jc	setdosdata_fail

	mov	cx, word ptr ds:[si]	;get the # of country, codepage combination entries
	cmp	cx, 438 		;cannot handle more than 438 entries.
					;
	ja	setdosdata_fail

	inc	si
	inc	si			;si -> entry information packet
	pop	dx			;restore code page id
	pop	ax			;restore country id
	pop	di

setdoscntry_find:			;search for desired country_id,codepage_id.
	cmp	ax, word ptr ds:[si+2]	;compare country_id
	jne	setdoscntry_next

	cmp	dx, 0			;no user specified code page ?
	je	setdoscntry_any_codepage;then no need to match code page id.
	cmp	dx, word ptr ds:[si+4]	;compare code page id
	je	setdoscntry_got_it

setdoscntry_next:
	add	si, word ptr ds:[si]	;next entry
	inc	si
	inc	si			;take a word for size of entry itself
	loop	setdoscntry_find

	mov	cx, -1			;signals that bad country id entered.
setdoscntry_fail:
	stc
	ret

setdosdata_fail:
	pop	si
	pop	cx
	pop	di
	jmp	short setdoscntry_fail

setdoscntry_any_codepage:		;use the code_page_id of the country_id found.
	mov	dx, word ptr ds:[si+4]

setdoscntry_got_it:			;found the matching entry
	mov	cs:cntrycodepage_id, dx ;save code page id for this country.
	mov	dx, word ptr ds:[si+10] ;get the file offset of country data
	mov	cx, word ptr ds:[si+12]
	mov	ax, 512 		;read 512 bytes
	call	readincontrolbuffer
	jc	setdoscntry_fail

	mov	cx, word ptr ds:[si]	;get the number of entries to handle.
	inc	si
	inc	si			;si -> first entry

setdoscntry_data:
	push	di			;es:di -> dos_country_cdpg_info
	push	cx			;save # of entry left
	push	si			;si -> current entry in control buffer

	mov	al, byte ptr ds:[si+2]	;get data entry id
	call	getcountrydestination	;get the address of destination in es:di
	jc	setdoscntry_data_next	;no matching data entry id in dos

	mov	dx, word ptr ds:[si+4]	;get offset of data
	mov	cx, word ptr ds:[si+6]
	mov	ax,4200h
	stc
	int	21h			;move pointer
	jc	setdosdata_fail

	mov	dx,512			;start of data buffer
	mov	cx,20			;read 20 bytes only. we only need to
	mov	ah,3fh			;look at the length of the data in the file.
	stc
	int	21h			;read the country.sys data
	jc	setdosdata_fail 	;read failure

	cmp	ax,cx
	jne	setdosdata_fail

	mov	dx,word ptr ds:[si+4]	;get offset of data again.
	mov	cx,word ptr ds:[si+6]
	mov	ax,4200h
	stc
	int	21h			;move pointer back again
	jc	setdosdata_fail

	push	si
	mov	si,(512+8)		;get length of the data from the file
	mov	cx,word ptr ds:[si]
	pop	si
	mov	dx,512			;start of data buffer
	add	cx,10			;signature + a word for the length itself
	mov	ah,3fh			;read the data from the file.
	stc
	int	21h
	jc	setdosdata_fail

	cmp	ax, cx
	jne	setdosdata_fail

	mov	al,byte ptr ds:[si+2]	;save data id for future use.
	mov	si,(512+8)		;si-> data buffer + id tag field
	mov	cx,word ptr ds:[si]	;get the length of the file
	inc	cx			;take care of a word for lenght of tab
	inc	cx			;itself.
	cmp	cx,(2048 - 512 - 8)	;fit into the buffer?
	ja	setdosdata_fail

	if	bugfix
	call	setdbcs_before_copy
	endif

	cmp	al, setcountryinfo	;is the data for setcountryinfo table?
	jne	setdoscntry_mov 	;no, don't worry

	push	word ptr es:[di+ccmono_ptr-cccountryinfolen]	;cannot destroy ccmono_ptr address. save them.
	push	word ptr es:[di+ccmono_ptr-cccountryinfolen+2]	;at this time di -> cccountryinfolen
	push	di			;save di

	push	ax
	mov	ax,cs:cntrycodepage_id	;do not use the code page info in country_info
	mov	ds:[si+4], ax		;use the saved one for this !!!!
	pop	ax

setdoscntry_mov:
	rep	movsb			;copy the table into dos
	cmp	al, setcountryinfo	;was the ccmono_ptr saved?
	jne	setdoscntry_data_next

	pop	di			;restore di
	pop	word ptr es:[di+ccmono_ptr-cccountryinfolen+2]	 ;restore
	pop	word ptr es:[di+ccmono_ptr-cccountryinfolen]

setdoscntry_data_next:
	pop	si			;restore control buffer pointer
	pop	cx			;restore # of entries left
	pop	di			;restore pointer to dso_country_cdpg
	add	si, word ptr ds:[si]	;try to get the next entry
	inc	si
	inc	si			;take a word of entry length itself
	dec	cx
	cmp	cx,0
	je	setdoscntry_ok
	jmp	setdoscntry_data

setdoscntry_ok:
	ret
setdoscountryinfo	endp

	if	bugfix
setdbcs_before_copy	proc	near

	cmp	al,setdbcs		; dbcs vector set?
	jnz	@f			; jump if not
	cmp	word ptr es:[di], 0	; zero byte data block?
	jz	@f			; jump if so

	push	di
	push	ax
	push	cx
	mov	cx,es:[di]		; load block length
	add	di,2			; points actual data
	xor	al,al			; fill bytes
	rep	stosb			; clear data block
	pop	cx
	pop	ax
	pop	di
@@:
	ret
setdbcs_before_copy	endp
	endif

getcountrydestination	proc	near

;get the destination address in the dos country info table.
;input: al - data id
;	es:di -> dos_country_cdpg_info
;on return:
;	es:di -> destination address of the matching data id
;	carry set if no matching data id found in dos.

	push	cx
	add	di,ccnumber_of_entries	;skip the reserved area, syscodepage etc.
	mov	cx,word ptr es:[di]	;get the number of entries
	inc	di
	inc	di			;si -> the first start entry id

getcntrydest:
	cmp	byte ptr es:[di],al
	je	getcntrydest_ok
	cmp	byte ptr es:[di],setcountryinfo ;was it setcountryinfo entry?
	je	getcntrydest_1

	add	di,5			;next data id
	jmp	short getcntrydest_loop

getcntrydest_1:
	add	di,new_country_size + 3 ;next data id
getcntrydest_loop:
	loop	getcntrydest
	stc
	jmp	short getcntrydest_exit

getcntrydest_ok:
	cmp	al,setcountryinfo	;select country info?
	jne	getcntrydest_ok1

	inc	di			;now di -> cccountryinfolen
	jmp	short getcntrydest_exit

getcntrydest_ok1:
	les	di,dword ptr es:[di+1]	;get the destination in es:di

getcntrydest_exit:
	pop	cx
	ret
getcountrydestination	endp


readincontrolbuffer	proc	near

;move file pointer to cx:dx
;read ax bytes into the control buffer. (should be less than 2 kb)
;si will be set to 0 hence ds:si points to the control buffer.
;entry:  cx,dx offset from the start of the file where the read/write pointer
;	 be moved.
;	 ax - # of bytes to read
;	 bx - file handle
;	 ds - buffer seg.
;return: the control data information is read into ds:0 - ds:0200.
;	 cx,dx value destroyed.
;	 carry set if error in reading file.

	push	ax			;# of bytes to read
	mov	ax, 4200h
	stc
	int	21h			;move pointer
	pop	cx			;# of bytes to read
	jc	ricb_exit

	xor	dx,dx			;ds:dx -> control buffer
	xor	si,si
	mov	ah,3fh			;read into the buffer
	stc
	int	21h			;should be less than 1024 bytes.

ricb_exit:
	ret
readincontrolbuffer	endp


set_country_path	proc	near

;in:  ds - sysinitseg, es - confbot, si -> start of the asciiz path string
;     dosinfo_ext, cntry_drv, cntry_root, cntry_path
;     assumes current directory is the root directory.
;out: ds:di -> full path (cntry_drv).
;     set the cntry_drv string from the country=,,path command.
;     ds, es, si value saved.

	push	si

	push	ds			;switch ds, es
	push	es
	pop	ds
	pop	es			;now ds -> confbot, es -> sysinitseg

	call	chk_drive_letter	;current ds:[si] is a drive letter?
	jc	scp_default_drv 	;no, use current default drive.

	mov	al, byte ptr ds:[si]
	inc	si
	inc	si			;si -> next char after ":"
	jmp	short scp_setdrv

scp_default_drv:
	mov	ah, 19h
	int	21h
	add	al, "A"                 ;convert it to a character.

scp_setdrv:
	mov	cs:cntry_drv, al	;set the drive letter.
	mov	di, offset cntry_path
	mov	al, byte ptr ds:[si]
	cmp	al, "\"
	je	scp_root_dir

	cmp	al,"/"                  ;let's accept "/" as an directory delim
	je	scp_root_dir

	jmp	short scp_path

scp_root_dir:
	dec	di			;di -> cntry_root
scp_path:
	call	move_asciiz		;copy it

	mov	di, offset cntry_drv
scpath_exit:

	push	ds			;switch ds, es
	push	es
	pop	ds
	pop	es			;ds, es value restored

	pop	si
	ret
set_country_path	endp


chk_drive_letter	proc	near
;check if ds:[si] is a drive letter followed by ":".
;assume that every alpha charater is already converted to upper case.
;carry set if not.

	push	ax
	cmp	byte ptr ds:[si], "A"
	jb	cdletter_no
	cmp	byte ptr ds:[si], "Z"
	ja	cdletter_no
	cmp	byte ptr ds:[si+1], ":"
	jne	cdletter_no

	jmp	short cdletter_exit

cdletter_no:
	stc

cdletter_exit:
	pop	ax
	ret
chk_drive_letter	endp


move_asciiz	proc	near
;in: ds:si -> source es:di -> target
;out: copy the string until 0.
;assumes there exists a 0.

masciiz_loop:
	movsb
	cmp	byte ptr ds:[si-1],0	;was it 0?
	jne	masciiz_loop
	ret
move_asciiz	endp

ENDIF ; CONFIGPROC

;
;	ds:dx points to string to output (asciz)
;
;	prints <badld_pre> <string> <badld_post>

badfil:
	push	cs
	pop	es

	mov	si,dx
badload:
	mov	dx,offset badld_pre	;want to print config error
	mov	bx, offset crlfm

prnerr:
	push	cs
	pop	ds
	call	print

prn1:
	mov	dl,es:[si]
	or	dl,dl
	jz	prn2
	mov	ah,std_con_output
	int	21h
	inc	si
	jmp	prn1

prn2:
	mov	dx,bx
	call	print

IFDEF	CONFIGPROC
	cmp	donotshownum,1		; suppress line number when handling command.com
	je	prnexit
	call	error_line
ENDIF ; CONFIGPROC

prnexit:
	ret

print:
	mov	ah,std_con_string_output
	int	21h
	ret


	if	noexec

; load non exe file called [ds:dx] at memory location es:bx

ldfil:
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	ds
	push	bx

	xor	ax,ax			;open the file
	mov	ah,open
	stc				;in case of int 24
	int	21h
	pop	dx			;clean stack in case jump
	jc	ldret

	push	dx
	mov	bx,ax			;handle in bx
	xor	cx,cx
	xor	dx,dx
	mov	ax,(lseek shl 8) or 2
	stc				;in case of int 24
	int	21h			; get file size in dx:ax
	jc	ldclsp

	or	dx,dx
	jnz	lderrp			; file >64k
	pop	dx

	push	dx
	mov	cx,es			; cx:dx is xaddr
	add	dx,ax			; add file size to xaddr
	jnc	dosize
	add	cx,1000h		; ripple carry
dosize:
	mov	ax,dx
	call	pararound
	mov	dx,ax

	add	cx,dx
	cmp	cx,[alloclim]
	jb	okld
	jmp	mem_err

okld:
	xor	cx,cx
	xor	dx,dx
	mov	ax,lseek shl 8		;reset pointer to beginning of file
	stc				;in case of int 24
	int	21h
	jc	ldclsp

	pop	dx

	push	es			;read the file in
	pop	ds			;trans addr is ds:dx

	mov	cx,0ff00h		; .com files arn't any bigger than
					; 64k-100h
	mov	ah,read
	stc				;in case of int 24
	int	21h
	jc	ldcls

	mov	si,dx			;check for exe file
	cmp	word ptr [si],"ZM"
	clc				; assume ok
	jnz	ldcls			; only know how to do .com files

	stc
	jmp	short ldcls

lderrp:
	stc
ldclsp:
	pop	dx			;clean stack
ldcls:
	pushf
	mov	ah,close		;close the file
	stc
	int	21h
	popf

ldret:	pop	ds
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
	endif

;
;  open device pointed to by dx, al has access code
;   if unable to open do a device open null device instead
;
open_dev:
	call	open_file
	jnc	open_dev3

open_dev1:
	mov	dx,offset nuldev
	call	open_file
of_ret:
	ret

open_dev3:
	mov	bx,ax			; handle from open to bx
	xor	ax,ax			; get device info
	mov	ah,ioctl
	int	21h
	test	dl,10000000b
	jnz	of_ret

	mov	ah,close
	int	21h
	jmp	open_dev1

open_file:
	mov	ah,open
	stc
	int	21h
	ret

; test int24. return back to dos with the fake user response of "fail"

int24:
	mov	al, 3			; fail the system call
	iret				; return back to dos.

include copyrigh.inc			; copyright statement


nuldev	db	"NUL",0
condev	db	"CON",0
auxdev	db	"AUX",0
prndev	db	"PRN",0

IFDEF	CONFIGPROC
config	db	"\DOS.CFG",0

cntry_drv   db	  "A:"
cntry_root  db	  "\"
cntry_path  db	  "COUNTRY.SYS",0
	    db	  52 dup (0)

country_file_signature db 0ffh,'COUNTRY'

cntrycodepage_id dw ?

ENDIF ; CONFIGPROC

ifdef	MULTI_CONFIG
newcmd	db	0			; non-zero if different shell selected
tmplate db	64
endif

ifdef	ROMEXEC
	db	7
commnd	db	"COMMAND",0
	db	56 dup (0)
else
	db	12
commnd	db	"\COMMAND.COM",0
	db	51 dup (0)
endif

ifdef	MULTI_CONFIG
commnd2 db	"\COMMAND.COM",0	; alternate commands to exec,
	db	2,"/P",0		; followed by their respective alternate
commnd3 db	"\MSDOS\COMMAND.COM",0	; command lines
	db	11,"A:\MSDOS /P",0      ;(the drive letter are dynamically replaced)
commnd4 db	"\DOS\COMMAND.COM",0	;
	db	9,"A:\DOS /P",0 	;

	db	0			; default switchchar (referenced as command_line-1)
endif

command_line	db  2,"/P"              ; default command.com args
	db	125 dup (0)

IFDEF	CONFIGPROC

pathstring db	64 dup (0)

ifdef	MULTI_CONFIG
;
;   Beware of byte pairs accessed as words (see all "KEEP AFTER" notes below)
;
	public	bQueryOpt
	public	bDisableUI
	public	bShellSwitches
bShellSwitches	db	0		; 0=none, 1=/D, 2=/Y (KEEP AFTER bQueryOpt)
bMenuColor	db	07h ;1Fh	; default fgnd/bgnd color
bMenuPage	db	0		; menu video page (KEEP AFTER bMenuColor)
		db	5		; video page function # (KEEP AFTER bMenuPage)
bLastCol	db	?		; ending column on status line
bLastRow	db	24		; row # of status line (KEEP AFTER bLastCol)
bDisableUI	db	0		; 1=disable clean/interactive
					; 2=disable default 2-second delay
bCRTPage	db	?		; value saved from BIOS data area
wCRTStart	dw	?		; value saved from BIOS data area
bQueryOpt	db	0		; 0=off, 1=prompt all, 2=prompt none, 4=skip all
bDefBlock	db	1		; default block #
bMaxBlock	db	?		; maxmimum block #
offDefBlock	dw	0		; offset of name of default block (if any)
secTimeOut	db	-1		; # of seconds for timeout (-1 == indefinite)
secElapsed	db	0		; # of seconds elapsed so far (KEEP AFTER secTimeOut)
abBlockType	db  MAX_MULTI_CONFIG+1 dup(?)	; array of block types
aoffBlockName	dw  MAX_MULTI_CONFIG+1 dup(?)	; array of offsets of block names
aoffBlockDesc	dw  MAX_MULTI_CONFIG+1 dup(?)	; array of offsets of block descriptions

szBoot		db	"CONFIG=",0
szMenu		db	"MENU",0
szCommon	db	"COMMON",0

endif  ;MULTI_CONFIG

comtab	label	byte

;	     cmd len	command 	cmd code
;	     -------	------- 	--------
;
ifdef MULTI_CONFIG
	db	1,	"[",		CONFIG_BEGIN
endif
	db	5,	"BREAK",	CONFIG_BREAK
	db	7,	"BUFFERS",	CONFIG_BUFFERS
	db	7,	"COMMENT",	CONFIG_COMMENT
	db	7,	"COUNTRY",	CONFIG_COUNTRY
	db	6,	"DEVICE",       CONFIG_DEVICE
	db	10,	"DEVICEHIGH",	CONFIG_DEVICEHIGH
	db	3,	"DOS",		CONFIG_DOS
	db	8,	"DRIVPARM",	CONFIG_DRIVPARM
	db	4,	"FCBS", 	CONFIG_FCBS
	db	5,	"FILES",	CONFIG_FILES
ifdef MULTI_CONFIG
	db	7,	"INCLUDE",	CONFIG_INCLUDE
endif
	db	7,	"INSTALL",	CONFIG_INSTALL
	db	11,	"INSTALLHIGH",	CONFIG_INSTALLHIGH
	db	9,	"LASTDRIVE",	CONFIG_LASTDRIVE
ifdef MULTI_CONFIG
	db	7,	"SUBMENU",	CONFIG_SUBMENU
	db	9,	"MENUCOLOR",	CONFIG_MENUCOLOR
	db	11,	"MENUDEFAULT",	CONFIG_MENUDEFAULT
	db	8,	"MENUITEM",	CONFIG_MENUITEM
endif
	db	10,	"MULTITRACK",	CONFIG_MULTITRACK
ifdef MULTI_CONFIG
	db	7,	"NUMLOCK",	CONFIG_NUMLOCK
endif
	db	3,	"REM",		CONFIG_REM
ifdef MULTI_CONFIG
	db	3,	"SET",		CONFIG_SET
endif
	db	5,	"SHELL",	CONFIG_SHELL
if    STACKSW
	db	6,	"STACKS",       CONFIG_STACKS
endif
	db	8,	"SWITCHES",	CONFIG_SWITCHES

; VCB - Add DOSDATA command
	db	7,	"DOSDATA",	CONFIG_DOSDATA
; end DOSDATA

	db	0

ENDIF  ;CONFIGPROC

public deviceparameters
deviceparameters a_deviceparameters <0,dev_3inch720kb,0,80>

IFDEF	CONFIGPROC

hlim	    dw	    2
slim	    dw	    9

public drive
drive	db	?

public switches
switches    dw	0


; the following are the recommended bpbs for the media that we know of so
; far.

; 48 tpi diskettes

bpb48t	dw	512
	db	2
	dw	1
	db	2
	dw	112
	dw	2*9*40
	db	0fdh
	dw	2
	dw	9
	dw	2
	dd	0
	dd	0

; 96tpi diskettes

bpb96t	dw	512
	db	1
	dw	1
	db	2
	dw	224
	dw	2*15*80
	db	0f9h
	dw	7
	dw	15
	dw	2
	dd	0
	dd	0

; 3 1/2 inch diskette bpb

bpb35	dw	512
	db	2
	dw	1
	db	2
	dw	70h
	dw	2*9*80
	db	0f9h
	dw	3
	dw	9
	dw	2
	dd	0
	dd	0

bpb35h	dw	0200h
	db	01h
	dw	0001h
	db	02h
	dw	0e0h
	dw	0b40h
	db	0f0h
	dw	0009h
	dw	0012h
	dw	0002h
	dd	0
	dd	0

;
; m037 - BEGIN
;
bpb288	dw	0200h
	db	02h
	dw	0001h
	db	02h
	dw	240
	dw	2*36*80
	db	0f0h
	dw	0009h
	dw	36
	dw	0002h
	dd	0
	dd	0
;
; m037 - END
;
bpbtable    dw	    bpb48t		; 48tpi drives
	    dw	    bpb96t		; 96tpi drives
	    dw	    bpb35		; 3.5" drives
; the following are not supported, so default to 3.5" media layout
	    dw	    bpb35		; not used - 8" drives
	    dw	    bpb35		; not used - 8" drives
	    dw	    bpb35		; not used - hard files
	    dw	    bpb35		; not used - tape drives
	    dw	    bpb35h		; 3-1/2" 1.44mb drive
	    dw	    bpb35		; ERIMO 			m037
	    dw	    bpb288		; 2.88 MB diskette drives	m037

switchlist  db	8,"FHSTDICN"         ; preserve the positions of n and c.

; the following depend on the positions of the various letters in switchlist

;switchnum	equ 11111000b		; which switches require number

flagec35	equ 00000100b		; electrically compatible 3.5 inch disk drive
flagdrive	equ 00001000b
flagcyln	equ 00010000b
flagseclim	equ 00100000b
flagheads	equ 01000000b
flagff		equ 10000000b
ENDIF ; CONFIGPROC
sysinitseg	ends
	end
