;    File              : $CONFIG.A86$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;
;    $Log$
;
;    CONFIG.A86 1.35 93/12/01 18:30:29
;    nls_temp_area grows to 258 bytes
;    CONFIG.A86 1.34 93/11/28 15:30:27
;    Support HIBUFFERS in UMB's
;    CONFIG.A86 1.32 93/11/22 15:02:14
;    Bus Master checks do Int 21/0D before/after to discard any buffers
;    CONFIG.A86 1.30 93/11/18 18:02:26 
;    Add primitive multi-master checking
;    CONFIG.A86 1.27 93/11/04 16:34:24
;    Extra callout to STACKER to determine if a drive is valid
;    CONFIG.A86 1.26 93/11/03 17:00:19
;    Stop dblspace phantom drives from appearing (FATsize=0)
;    CONFIG.A86 1.25 93/09/14 20:12:24
;    Initialise LFLG_PHYSICAL better - allow for zero FAT's meaning phantom drive
;    CONFIG.A86 1.24 93/09/02 22:34:50
;    Add header to system allocations
;    CONFIG.A86 1.23 93/08/06 20:55:23
;    re-arrange device init order for SCREATE.SYS on a VDISK.SYS
;    CONFIG.A86 1.22 93/08/02 14:45:55
;    hide preload drives from func_device
;    CONFIG.A86 1.20 93/07/28 19:19:08
;    call to SetupHMA before AllocHMA in setup_buffers allows buffers to go high
;    on novell memory managers
;    ENDLOG

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

CGROUP		group	INITCODE, INITDATA

INITCODE	CSEG	PARA 'INITCODE'

	extrn	AllocHMA:near
	extrn	SetupHMA:near
	extrn	alloc_instseg:near	; Allocate "Segment" Instance Memory
	extrn	alloc_hiseg:near	; Allocate "Segment" High Memory
	extrn	alloc_seg:near		; Allocate "Segment" Memory
	extrn	config_process:near
	extrn	InitStacks:near
	extrn	HookInt2F:near
	extrn	UnhookInt2F:near
	extrn	Verify386:near
	
	Public config_init
config_init:				; Initialize the CONFIG data
	ret


	Public country_init
country_init:
;------------
	push	ds
	push	es
	push	di
	push	si

; Obtain the address of the DBCS table in the BDOS.
	mov	ax, 06507h		; Extended Country Info: get DBCS ptr
	mov	bx, 0FFFFh		; codepage number: -1 for global cp
	mov	cx, 00005h		; size of info. buffer
	mov	dx, 0FFFFh		; country code: -1 for current country
	mov	di, offset dbcs_buf
	push	ds
	pop	es			; es:di -> 5 byte buffer
	int	DOS_INT			; returns with buffer filled in

; Get the current country information.
	mov	dx, offset ctry_info	; ds:dx -> buffer to be filled in
	mov	ax, 03800h		; get current country info
	int	DOS_INT
	jnc	ctry_info_done		; no carry = no error

; Failed to get country info. Place dummy uppercase routine in table.
	mov	si, offset ctry_info
	mov	CI_CASEOFF[si], offset myretf
	mov	CI_CASESEG[si], cs
	mov	bx, country_code

ctry_info_done:
	mov	country_code, bx

	pop	si
	pop	di
	pop	es
	pop	ds
	ret

myretf:	retf

;
;	CONFIG is called after the BIOS INIT code has been relocated to high
;	memory, the BIOS and BDOS have been initialised. 
;
	Public config			; Process CONFIG.SYS, loading and
config:					; initialising device drivers
	call	country_init		; initialise DBCS tbl and country info
	mov	ax,max_secsize		; get maximum sector size in BIOS
	les	bx,func52_ptr
	cmp	ax,es:F52_SECSIZE[bx]	; larger than default?
	 jbe	cfg_skip		; skip if not
	mov	es:F52_SECSIZE[bx],ax	; else update sector size
cfg_skip:
	call	cpu_init		; initialise CPU type
	push ds ! pop es

	call	config_process		; Process CONFIG.SYS

	cmp	num_files,MIN_NUM_FILES	; Ensure the Minimum number of File
	jae	cfg_ex10		; have been allocated.
	mov	num_files,MIN_NUM_FILES
cfg_ex10:
	xor	ax,ax
	mov	al,init_buf		; now ensure we have allocated
	mov	num_buf,ax		; the correct number of buffers

	call	SetupDeblocking		; do our thing with deblocking

	call	config_finish		; clean up configuration
	call	setup_fopen		; allocate disk hashing
	call	setup_history
	ret

cpu_init:
; If we are on a 386 or above set CPU flag
	call	Verify386		; make sure it's a 386
	 jc	cpu_init10		; skip setting falg if not
	les	bx,func52_ptr
	mov	es:F52_CPU_TYPE[bx],1	; we have a 386 !
cpu_init10:
	stc				; it's not a 386
	ret


SetupDeblocking:
;---------------
; Some types of hard disk controller give us problems with disk access of
; mapped memory (eg. upper memory, LIM pages).
; We can force single sector deblocking in the disk driver to avoid these
; problems. On DRDOS 5/6 our default was single sector I/O above A000, but
; this gives performance problems when devices/tsr's are loaded into upper
; memory (eg. STACKER, SERVER). To avoid this we use the following strategy.
;
; Default for CONFIG is A000, and may be updated at any time by a DEBLOCK=
; statement. If this happens the user setting has priority.
;
; At the end of CONFIG, assume no user supplied setting, we do some simple
; tests for multi-master controllers. If these fail we leave the settings
; at A000. If they succeed we go ahead and change the default setting to
; deblock at FFFF. The test is to read the 1st sector into low memory, and
; again into upper memory. If we read the same thing then assume all is well.
; If we can't do this leave the deblocking set at A000.
;
; NB. We will still have problems from LIM, and from DMA crossing page
; boundaries on some memory managers (eg. DRDOS 5.0)
;
	les	bx,func52_ptr
	lea	bx,F52_DEVROOT[bx]	; ES:BX -> NUL device link
	cmp	DeblockSetByUser,FALSE
	 je	SetupDeblocking20	; the user is king
SetupDeblocking10:
	push cs ! pop es		; ES -> local data again
	ret

SetupDeblocking20:			; get next device driver
	les	bx,es:[bx]		; we want resident disk device
	cmp	bx,0FFFFh		; end of the chain ?
	 je	SetupDeblocking10
	test	es:word ptr 4[bx],8000h
	 jnz	SetupDeblocking20	; we assume one disk device at 70:xxxx
	mov	ax,es
	cmp	ax,cs:bios_seg		; scan for resident disk in IO.SYS
	 jne	SetupDeblocking20
	cmp	es:word ptr 18[bx],0EDCh
	 jne	SetupDeblocking20	; is it our disk driver ?
	lea	ax,22[bx]		; deblocking variable is here
	mov	deblockOffset,ax	; remember that for fixups
	mov	deblockSeg,es
	mov	ax,es:DH_STRATEGY[bx]	; Set up the STRATEGY Entry Point
	mov	strategy_off,ax
	mov	strategy_seg,es	

	mov	ax,es:DH_INTERRUPT[bx]	; Set up the INTERRUPT Entry Point
	mov	interrupt_off,ax
	mov	interrupt_seg,es
	mov	al,es:DH_NAME[bx]	; get # supported units
	cbw
	mov	numUnits,ax		; remember for later

	mov	ax,(MS_M_STRATEGY*256)+1; set allocation strategy
	mov	bl,42h			;  to last fit, upper only
	int	DOS_INT
	mov	bx,512/16		; we need a 512 byte buffer
	mov	ah, MS_M_ALLOC		; try to allocate on in upper memory
	int	DOS_INT
	 jc	SetupDeblocking50
	mov	UpperMemoryBuffer,ax	; use this for deblocking checks
	mov	cx,numUnits		; CX = # of drives supported
	mov	dx,'C'-'A'		; start with drive C:
	sub	cx,dx			; CX = # of potential hard disks
	 jbe	SetupDeblocking40	; skip tests if none
SetupDeblocking30:			; DX = next drive, CX = drive count
	push cx ! push dx
	call	BusMasterCheck		; check if drive DL bus master disk
	pop dx ! pop cx
	 jc	SetupDeblocking40	; is so leave deblocking alone
	inc	dx			; else move to next drive
	loop	SetupDeblocking30	; repeat for all drives
	les	bx,deblockPointer
	mov	es:word ptr [bx],0FFFFh	; safe to disable deblocking
SetupDeblocking40:
	mov	es,UpperMemoryBuffer
	mov	ah,MS_M_FREE
	int	DOS_INT			; free the upper memory buffer
SetupDeblocking50:
	mov	ax,(MS_M_STRATEGY*256)+1; set allocation strategy
	mov	bl,0			;  to first fit
	int	DOS_INT
	push cs ! pop es		; ES -> local data again
	ret


BusMasterCheck:		; determine if we have an old troublesome controller
;--------------
; On Entry:
;	DL = drive to check (zero based)
; On Exit:
;	CY set if troublesome drive
;
	call	BusMasterRemovable	; is it a removable device ?
	 jnc	BMCheck10		; yes, skip the checks
	mov	ax,mem_current		; read into low memory
	call	BusMasterRead		; read one sector from disk
	 jc	BMCheck10		; give up if we couldn't read

	mov	es,mem_current		; ensure at least the 1st word will
	mov	ax,es:.0		;  differ if the read doesn't happen
	not	ax
	mov	es,UpperMemoryBuffer
	mov	es:.0,ax

	mov	ax,UpperMemoryBuffer	; read into upper memory
	call	BusMasterRead		; read one sector from disk
	 jc	BMCheck10		; give up if we couldn't read
		
	xor	si,si
	xor	di,di
	mov	cx,512/2
	mov	es,mem_current
	mov	ds,UpperMemoryBuffer
	repe	cmpsw			; does the sector match ?
	push cs ! pop ds
	 je	BMCheck10		; yes, everything is fine
	stc				; no, better leave DEBLOCK at A000
BMCheck10:
	ret

BusMasterRemovable:
;------------------
; On Entry:
;	DL = drive
; On Exit:
;	CY set if not a removable drive
;	DL preserved
;
	push cs ! pop es
	mov	bx,offset removableMediaRequest
	mov	es:RH_UNIT[bx],dl
	callf	cs:strategy
	callf	cs:interrupt
	test	es:RH_STATUS[bx],RHS_BUSY
	 jz	BusMasterRemovable10
	stc				; busy bit set, it's a hard disk
BusMasterRemovable10:
	ret

BusMasterRead:		; Read first sector from drive BL
;-------------
; On Entry:
;	AX = segment of buffer
;	DL = 02 for C:, 03 for D:, etc.
; Exit:
;	CY clear if deblocking not required
;	DL preserved
;
	les	bx,deblockPointer
	mov	es:word ptr [bx],0FFFFh	; no deblocking during the test
	push cs ! pop es
	mov	bx,offset readRequest
	mov	es:RH_UNIT[bx],dl
	mov	es:RH4_BUFOFF[bx],0
	mov	es:RH4_BUFSEG[bx],ax
	mov	es:RH4_COUNT[bx],1
	callf	cs:strategy
	callf	cs:interrupt
	test	es:RH_STATUS[bx],RHS_ERROR
	 jz	BusMasterRead10
	stc				; error bit set, say we had a problem
BusMasterRead10:
	les	bx,deblockPointer
	mov	es:word ptr [bx],0A000h	; enable deblocking again
	ret


	Public	setup_stacks

setup_stacks:
	mov	cx,num_stacks		; we want this many stacks
	 jcxz	setup_stacks10		; skip if we don't want any
	mov	dx,stack_size		; they should be this big
	call	InitStacks		; initialise the stacks
setup_stacks10:
	ret

setup_history:
	test	history_flg,RLF_ENHANCED
	 jz	setup_hist10		; if history not enabled just exit

	push	es
	les	bx,drdos_ptr		; Get the internal data area

	mov	si,es:DRDOS_HIST1CTL[bx]; Get the address of History Control
	mov	ax,history_size		; Get Offset buffer for History data
	call	setup_hist20		; Allocate Buffer 1

	mov	si,es:DRDOS_HIST2CTL[bx]; Get the address of History Control 2
	mov	ax,history_size		; Get Offset buffer for History data
	call	setup_hist20		; Allocate Buffer 2

	mov	al,history_flg		; copy history state
	mov	es:DRDOS_HISTFLG[bx],al	;  into DRDOS data area

	pop	es
setup_hist10:
	ret

setup_hist20:
	and	ax,not 15		; round to a complete paragraph
	add	ax,16			; always be a para bigger
	mov	es:word ptr 02[si],ax	; Buffer Tail Address
	mov	cl,4
	push	ax			; save buffers size in bytes
	shr	ax,cl			; convert to para's
	mov	dl,'H'			; History buffer
	call	alloc_instseg		; Allocate Buffer
	mov	es:word ptr 00[si],ax	; Buffer Start Address
	pop	cx			; recover buffer size in bytes
	push	es
	mov	es,ax			; point ES at buffer seg
	xor	di,di			; ES:DI -> buffer
	xor	ax,ax			; zero it
	rep	stosb			; before use
	pop	es
	ret


	Public	device_init, resident_device_init

;	DEVICE_INIT will initialise the device with the Device Header at
;	ES:BX
;
;	Entry:
;		ES:DI		Address of First Device Header
;		DS:SI		Command Line Parameters
;
;	Exit:
;		AX		Top bit set on error, error code in AL
;
device_init:
;-----------
	push es ! push di
	call	build_cmd_tail		; point DS:SI to dos style cmd line
	pop di ! pop es
	call	HookInt2F		; hook the Int 2F vector
	call	resident_device_init
	call	UnhookInt2F		; get off the Int 2F vector
	test	ax,ax			; set the flags
	ret

resident_device_init:
;--------------------
	mov	rel_unit,0		; set rel unit to zero for block devices
	mov	dev_count,0
	mov	bx,offset request_hdr	; ds:bx -> command block
if TRUE
	mov	ax,mem_max		; AX:0 -> top of available memory
	mov	ds:RH0_RESIDENT,0	; pass to the device driver
	mov	ds:RH0_RESIDENT+2,ax	;  in the RESIDENT field
else
	mov	ds:RH0_RESIDENT,di	; Force the default RESIDENT field
	mov	ds:RH0_RESIDENT+2,es	;  to be the error condition
endif
dev_i10:
	push	si
	push es ! push di
	call	save_vecs		; save interrupt vectors
	mov	bx,offset request_hdr	; ds:bx -> command block
	call	dev_init		; initialise the device driver
	test	es:DH_ATTRIB[di],DA_CHARDEV
	 jnz	dev_i18			; skip if a character device
	cmp	ds:RH0_NUNITS,0		; no drives installed for disk device?
	 je	dev_i_err		; failed if no drives found
dev_i18:
	mov	ax,ds:RH0_RESIDENT	; Calculate the address of
	mov	cl,4			; last paragraph used by the
	shr	ax,cl			; device driver. If this is the
	add	ax,ds:RH0_RESIDENT+2	; device driver CS then error
	test	ds:RH0_RESIDENT,15	; allow for partial para ?
	 jz	dev_i19
	inc	ax			; round it up
dev_i19:
	cmp	ax,strategy_seg
	 jbe	dev_i_err

	cmp	ax,mem_max		; Check for Memory Overflow
	 jb	dev_i30			; if it does then we can't install
	call	restore_vecs		; so replace interrupt vectors
dev_i_err:				; device initialization failed!
	les	di,cs:func52_ptr	; ES:DI -> internal data
	mov	ax,es
	or	ax,di			; DOS data area present yet?
	pop di ! pop es			; recover the device header

	les	di,es:DH_NEXT[di]	; try next device driver
	 jz	dev_i60			; if it's resident initialisation
	mov	di,0FFFFh		;  else stop now
	jmps	dev_i60

;	The device driver initialised OK so now build/update internal
;	tables based on the device driver type.
;
;	AX = next available paragraph
;	DS:BX = request header
;	ES:DI = device driver header
;
dev_i30:				; DEV_INIT OK so update the Top of
	mov	mem_current,ax		; memory field
	test	es:DH_ATTRIB[di],DA_CHARDEV
	 jz	dev_i40

	call	char_device		; Handle Initialization of all
	jmps	dev_i50			; character devices

dev_i40:
	call	block_device		; Handle Initialization of all
;	jmps	dev_i50			; Block Devices

dev_i50:
	pop di ! pop es			; Retrieve the current device header
	push	es:DH_NEXTSEG[di]	; save next entry on the list
	push	es:DH_NEXTOFF[di]	; while we deal with existing one
	mov	es:DH_NEXTOFF[di],0FFFFh; terminate the list	
	call	device_insert		;  and insert into the list
	pop	di
	pop	es			; go round till the end
dev_i60:
	pop	si			; recover cmdline for next device
	cmp	di,0FFFFh		; was that the last device to
	 jne	dev_i10			;  initialise, no do next
	mov	bx,offset request_hdr	; ds:bx -> command block
	mov	ax,ds:RH_STATUS		; return Status Register
	and	ax,80FFh		; is there an error ?
	 js	dev_i70
	xor	ax,ax			; no, return success
dev_i70:
	ret


	public	init_static_request
init_static_request:
					; Set up request header for INIT command.
	sub	ax,ax			; get a convenient zero
	mov	ds:RH_LEN,RH0_LEN	; Init Request Length
	mov	ds:RH_UNIT,al		; relative drive always 0
	mov	ds:RH_CMD,CMD_INIT	; Init Command 
	mov	ds:RH_STATUS,ax		; Zero Status Register

	mov	ds:RH0_BPBOFF,si	; Save the command line offset and
	mov	ds:RH0_BPBSEG,ds	; Segment in the BPB Pointer

	mov	al,next_drv		; the first drive for this device
	sub	al,preload_drv		; (not including preloaded devices)
	mov	ds:RH0_DRIVE,al	 	; will be allocated as NEXT_DRV
;;;	mov	es:DH_NEXTSEG[di],0	; force seg to zero (386max 4.05)
	ret

dev_init:
;--------
; On Entry:
;	ES:DI -> device driver header
;	DS:BX -> req header
;	DS:SI -> command line
; On Exit:
;	ES:DI/DS:BX <preserved>
;
	mov	ax,es:DH_STRATEGY[di]	; Set up the STRATEGY Entry Point
	mov	strategy_off,ax
	mov	strategy_seg,es	

	mov	ax,es:DH_INTERRUPT[di]	; Set up the INTERRUPT Entry Point
	mov	interrupt_off,ax
	mov	interrupt_seg,es
	call	init_static_request

	push ds ! push es		; Save Segment registers
	push bx ! push si ! push di	; and pointers (not all preserve them)
	push ds ! pop es		; ES -> Points at the Data Segment
	mov	ds,strategy_seg		; DS == Device Drive Segment
 	mov	si,di			; DS:SI -> device driver header
 	callf	cs:strategy		; Call Device Strategy Routine
	callf	cs:interrupt		; Call Device Interrupt Routine
	pop di ! pop si ! pop bx	; recover the pointers
	pop es ! pop ds			; Restore Segment Registers
	mov	es:DH_NEXTSEG[di],es	; ignore segment - it MUST be same one
	ret

;
;	Character Device Driver Initialised OK so now build/update internal
;	tables based on the device driver type.
;
;	DS:BX		Request Header
;	ES:DI		Device Driver Header
;
char_device:
	test	es:DH_ATTRIB[di],DA_ISCIN
	 jz	char_d10		; is this the standard console device?
	mov	condev_off,di		; save console device driver address
	mov	condev_seg,es
	ret

char_d10:
	test	es:DH_ATTRIB[di],DA_ISCLK
	 jz	char_d20		; is this the standard clock device?
	mov	clkdev_off,di		; save clock device driver address
	mov	clkdev_seg,es
char_d20:
	ret


;	Block  device  driver  initialised  OK.  Save  the values
;	returned from the INIT call so we can later build all the
;	required internal tables.
;
;	entry:	DS:BX -> request header
;		ES:DI -> device driver header
;
	public	block_device
block_device:
	mov	al,BLKDEV_LENGTH	; bytes per block device table entry
	mul	byte ptr num_blkdev	;   * # of block devices installed
	add	ax,offset blkdev_table	; AX -> block dev init result table
	xchg	ax,si			; pointer to next block device struct
	mov	devoff,di
	mov	devseg,es		; point to device driver header
	mov	0[si],di
	mov	2[si],es		; save device driver address for later
	mov	ax,ds:RH0_BPBOFF
	mov	4[si],ax		; save BPB table address (offset)
	mov	ax,ds:RH0_BPBSEG
	mov	6[si],ax		; save BPB table address (segment)
	mov	cl,ds:RH0_NUNITS
	mov	8[si],cl		; get # of units supported by driver
	mov	es:DH_NAME[di],cl	; set # of units in device name
	inc	num_blkdev		; we've installed another block device
	add	next_drv,cl		; update drive base for next driver
	add	dev_count,cl		; number of new units

	mov	ax,boot_device		; now for Andy's bit about boot device
	or	ax,boot_device+2	; have we already got a boot device?
	 jnz	not_boot_dev
	mov	ch,init_drv
	sub	ch,next_drv		; is sub unit in this driver
	 ja	not_boot_dev		; no, skip it
	add	ch,cl			; work out which sub unit it is
	mov	boot_drv,ch		; and remember it
	mov	boot_device,di
	mov	boot_device+2,es
not_boot_dev:

	push	si ! push es ! push di
	mov	cl,8[si]
	mov	ch,0			; CX = # of drives found in driver
	les	si,4[si]		; ES:SI -> BPB array in BIOS
	mov	bpbseg,es		; remember the segment
blkdev_loop:
	lods	es:ax			; AX = offset of next BPB
	push es ! push si ! push cx
	mov	bpboff,ax		; remember the offset
	xchg	ax,di			; ES:DI -> next BPB
	mov	ax,es:[di]		; AX = sector size for BPB
	cmp	ax,max_secsize		; new maximum for sector size
	 jbe	blkdev_next1		; skip if sector size not grown
	mov	max_secsize,ax		; else set new maximum
blkdev_next1:
	mov	dl,es:2[di]		; get sectors per cluster
	mov	dh,0			; make this a word
	mul	dx			; AX = bytes per cluster
	cmp	ax,max_clsize		; more than previous maximum
	 jbe	blkdev_next2		; skip if no new high score
	mov	max_clsize,ax		; else record max. sector size
blkdev_next2:
	les	bx,func52_ptr		; ES:BX -> internal data
	mov	ax,es
	or	ax,bx			; DOS data area present yet?
	 jz	blkdev_next3		; skip if BDOS not present yet
	call	setup_drives		; update drives in BDOS data
	mov	es,mem_current		; MUST create a DDSC just after driver
	add	mem_current,(DDSC_LEN+15)/16
	xor	bp,bp			; ES:BP points to the DDSC
	call	setup_ddsc		; add new DDSC_ to chain
	call	setup_ldt		; initialise LDT for that drive
blkdev_next3:
	pop cx ! pop si ! pop es
	loop	blkdev_loop		; repeat for all BPBs in driver
	pop	di ! pop es ! pop si
	ret

resident_ddscs:
;--------------
; Allocate DDSC's for the resident device drivers - we can only do this
; after the DOS data area is established.
;
	sub	bx,bx			; start with 1st block device
	mov	cx,num_blkdev		; get # of block devices
	 jcxz	res_ddsc40		; skip if no block devices
res_ddsc10:
	push	bx ! push cx
	mov	ax,BLKDEV_LENGTH
	mul	bx
	add	ax,offset blkdev_table
	xchg	ax,si			; SI -> block device table
	sub	cx,cx
	mov	cl,8[si]		; CX = # of units on device
	sub	bx,bx			; BX = relative unit # * 2
	mov	rel_unit,bx		; start with relative unit # 0

res_ddsc20:				; CX = remaining units
	push	bx ! push cx ! push si	; BX = offset, SI -> drive structure
	lodsw
	mov	devoff,ax		; save device header offset
	lodsw
	mov	devseg,ax		; save device header segment 
	les	si,[si]			; get offset of BPB array
	mov	ax,es:[bx+si]		; get offset for our BPB
	mov	bpboff,ax
	mov	bpbseg,es		; save pointer to BPB

	les	bp,res_ddsc_ptr		; point to position for DDSC_
	add	ds:word ptr res_ddsc_ptr,DDSC_LEN
	call	setup_ddsc		; setup one unit
	pop	si ! pop cx ! pop bx
	inc	bx ! inc bx		; increment (unit index*2)
	loop	res_ddsc20		; repeat for next unit, same driver

	pop	cx ! pop bx
	inc	bx
	loop	res_ddsc10		; repeat for next driver
res_ddsc40:				; all block devices done
	ret

	
setup_ddsc:
;----------
; On Entry:
;	ES:BP -> DDSC_ to initialise and link into chain
;	bpbptr -> BPB to initialise from
;	devseg:devoff -> device driver header
;	abs_unit, rel_unit reflect drive
; On Exit:
;	None
;
	push	ds
	lds	si,bpbptr		; DS:SI points to the BPB
	mov	ah,53h			; build DDSC from BPB call
	int	DOS_INT			;  initialises the structure
	pop	ds
	mov	ax,devoff
	mov	es:DDSC_DEVOFF[bp],ax
	mov	ax,devseg
	mov	es:DDSC_DEVSEG[bp],ax

	mov	ax,abs_unit
	inc	abs_unit
	mov	es:DDSC_UNIT[bp],al	; set absolute unit (global)
	mov	ax,rel_unit
	inc	rel_unit
	mov	es:DDSC_RUNIT[bp],al	; set relative unit (driver relative)

	mov	ax,-1			; set link to FFFFh:FFFFh
	mov	es:word ptr DDSC_LINK[bp],ax
	mov	es:word ptr DDSC_LINK+2[bp],ax
	mov	es:DDSC_FIRST[bp],al	; set drive never accessed flag
	mov	ax,es			; now link into device chain
;
	les	bx,func52_ptr		; ES:BX -> secret 52h data
	lea	bx,F52_DDSCPTR-(offset .DDSC_LINK)[bx]
setup_ddsc10:
	cmp	es:word ptr DDSC_LINK[bx],0FFFFh
	 je	setup_ddsc20		; is there another one ?
	les	bx,es:DDSC_LINK[bx]	; onto next DDSC_
	jmps	setup_ddsc10
setup_ddsc20:				; link new DDSC to end of chain
	mov	es:word ptr DDSC_LINK[bx],bp
	mov	es:word ptr DDSC_LINK+2[bx],ax
	ret				; now RAF will be happy
	

	Public	setup_ldt

setup_ldt:
	push	ds
	push	es
	les	bx,func52_ptr		; get internal data in ES:BX
	mov	al,LDT_LEN		; we need this many bytes per drive
	mul	es:F52_LASTDRV[bx]	; *lastdrive
	xchg	ax,cx			; CX = size to initialise
	mov	al,es:F52_LASTDRV[bx]	; lastdrive
	push	ax			; save for later
	les	di,es:F52_PATHPTR[bx]	; now initialise the CSD's
	xor	al,al			; to zero
	rep	stosb			; zero them
	pop	ax			; recover lastdrive

	xor	bx,bx			; start with zero offset
	xor	cx,cx			; start with drive A
	xchg	al,cl			; AH = physical limit, CX logical limit
ldt_init:
	push	ax
	push	cx
	push	ax
	lea	di,LDT_NAME[bx]
	add	al,'A'			; make drive ASCII
	stosb
	mov	ax,'\:'			; point at the root
	stosw
	mov	ax,0FFFFh
	lea	di,LDT_BLK[bx]		; set to FFFF to force LDT_ rebuild
	stosw ! stosw ! stosw		;  next two words are FFFF too
;	lea	di,LDT_ROOTLEN[bx]
	mov	ax,2
	stosw				; set the length field
	pop	ax
	lds	si,cs:func52_ptr	; get internal data in DS:SI
	sub	si,offset .DDSC_LINK
ldt_init20:
	lds	si,ds:DDSC_LINK[si]	; point to next PDT
	cmp	si,-1			; skip if there isn't one
	 je	ldt_init40
	cmp	al,ds:DDSC_UNIT[si]	; is this the DDSC for the drive
	 jne	ldt_init20		; if not try another
	mov	es:word ptr LDT_PDT[bx],si
	mov	es:word ptr LDT_PDT+2[bx],ds
	cmp	ds:DDSC_NFATS[si],0	; no FATS, then it's a reserved drive
	 je	ldt_init40		
	push	es
	push	bx
	push	ax			; save drive we are processing
	mov	ax,4A11h
	xor	bx,bx
	int	2Fh			; do an STACKER installation check
	pop	dx			; DL = drive we are processing
	test	ax,ax
	mov	ax,LFLG_PHYSICAL	; assume a physical drive
	 jnz	ldt_init30		; no STACKER, it's physical
	sub	cl,'A'			; zero base STACKER drive returned
	cmp	cl,dl			; should we check this drive ?
	 ja	ldt_init30		; below 1st drive, it's physical
	push	ax
	push	dx
	mov	ax,4A11h
	mov	bx,1			; ask STACKER for host drive
	int	2Fh
	pop	dx
	pop	ax
	cmp	bl,dl			; is this the host drive ?
	 jne	ldt_init30
	xor	ax,ax			; drive is invalid
ldt_init30:
	pop	bx
	pop	es
	mov	es:LDT_FLAGS[bx],ax
ldt_init40:
	add	bx,LDT_LEN		; move onto next LDT_
	pop	cx
	pop	ax
	inc	ax			; and next drive
	loop	ldt_init		; done to lastdrive ? no, do another

	pop	es
	pop	ds
	ret

	Public	device_insert

device_insert:
;-------------
; insert device drivers at ES:DI into global chain
; if we are initialising the resident device drivers then we don't have
;  a global chain, so insert them on a local chain and try again later
	push	ds
	lds	bx,func52_ptr		; Internal Data Pointer
	lea	si,F52_DEVROOT[bx]	; DS:SI -> NUL device
	mov	ax,ds			; if BDOS data area isn't present
	or	ax,bx			;  we are initialising resident
	 jnz	dev_ins_next		;  devices
	push cs ! pop ds
	mov	si,offset resdev_chain	; it's resident devices
dev_ins_next:
	cmp	di,-1			; end of device chain reached?
	 je	devins_done		; yes, all devices inserted
	mov	ax,0[si]
	mov	dx,2[si]		; DX:AX = original chain 
	mov	0[si],di
	mov	2[si],es		; link our device at head of chain
	xchg	ax,es:0[di]		; link old global chain to device
	xchg	dx,es:2[di]		;  & get next device in local chain
	mov	di,ax			; point to next device in chain
	mov	es,dx
	jmps	dev_ins_next		; repeat until chain empty
devins_done:
	pop	ds
	ret


	public	config_finish

config_finish:		; finish off configuration
;-------------
	cmp	resdev_off,-1		; are resident devices already
	 je	cfg_fin10		;  installed
	les	di,resdev_chain		; insert all the resident device
	call	device_insert		;  drivers into DOS chain
	call	resident_ddscs		; build DDSC's for resident devices
	mov	resdev_off,-1		; only do this once...
cfg_fin10:

	les	bx,func52_ptr		; ES:BX -> base of DOS variables
	call	setup_drives		; Update No of Physical Drives in case
					; this is the first pass
	call	setup_ldt		; setup the ldt's
	les	bx,func52_ptr
	lea	di,F52_CLKDEV[bx]	; ES:DI -> clock ptr, console ptr
	mov	si,offset clkdev_off	; DS:SI -> local pointer values
	movsw				; set offset of clock device driver
	movsw				; set segment of clock device driver
	movsw				; set offset of console device driver
	movsw				; set segment of console device driver

	call	setup_doshndl		; Allocate DOS compatible Handles
					; NB must immediately follow devices !
	call	setup_buffers		; allocate the requested #
	ret


setup_buffers:
;-------------
;	entry:	num_buf = minimum # of buffers required
;		          0 - use temporary high buffers
;
	les	bx,func52_ptr		; ES:BX -> internal data structure
	mov	ax,num_buf		; fill in info in DOS for diagnostic
	mov	es:F52_NUM_BUF[bx],ax	;  programs
	mov	al,num_read_ahead_buf
	mov	es:F52_READ_AHEAD[bx],ax
	mov	ax,es:F52_SECSIZE[bx]	; get DOS data sector size
	cmp	ax,max_secsize		; has it been poked to a bigger value ?
	 ja	setup_b10		; if so we must discard anyway
	mov	ax,max_secsize		; get max. sector size found
setup_b10:
	mov	es:F52_SECSIZE[bx],ax	; update max. sector size in PCMODE
	mov	max_secsize,ax		; update max. sector size locally
	add	ax,offset .BCB_DATA	; add in the header size

	mov	es,init_dseg		; ES:DI -> init buffers
	mov	di,offset INIT_BUFFERS
	mov	cx,NUM_BUFFS		; CX buffs, DX in size, at ES:DI
	mov	dx,SIZEOF_BUFFS		; size of init buffers
	cmp	num_buf,0		; (zero at init time)
	 je	setup_b70		; go ahead and initialise

	push	ax			; save size of buffer
	mul	num_buf			; AX = total bytes required
	test	dx,dx			; > 64 K ?
	 jz	setup_b30
	mov	ax,0FFFFh		; do the maximum
setup_b30:
	pop	bx			; BX = size of a buffer
	mov	cx,ax			; CX bytes required
	xor	dx,dx
	div	bx			; AX = # buffers

	push	ax			; save # buffers
	push	bx			; save size of a buffer
	push	cx			; save bytes wanted
	test	buffersIn,BUFFERS_IN_HMA
	stc				; do we want buffers at FFFF ?
	 jz	setup_b40
	call	SetupHMA		; make sure HMA chain is established
	pop cx ! push cx		; CX = bytes wanted
	mov	dx,0FFFFh		; anywhere is OK
	call	AllocHMA		; ES:DI -> allocated data
setup_b40:
	pop	ax			; AX = bytes wanted
	pop	dx			; DX = size of a buffer
	pop	cx			; CX = number of buffer
	 jnc	setup_b70		; if CY clear ES:DI -> our space

	shr ax,1 ! shr ax,1
	shr ax,1 ! shr ax,1
	inc	ax			; convert from bytes to para's
	push	dx
	mov	dl,'B'			; allocate as a Buffer
	test	buffersIn,BUFFERS_IN_UMB
	 jz	setup_b50		; allocation from UMB's OK ?
	call	alloc_hiseg		; yes, try and allocate memory there
	 jnc	setup_b60
setup_b50:
	call	alloc_seg		; allocate memory in bottom 640 K
setup_b60:
	pop	dx
	mov	es,ax			; ES = segment
	xor	di,di			; ES:DI -> start of buffer
setup_b70:
; Buffer space for CX buffers, of size DX, allocated at ES:DI
	mov	si,di			; remember where 1st buffer is
setup_b80:
	push	cx
	mov	bx,di			; BX = current buffer
	mov	cx,dx
	xor	ax,ax
	rep	stosb			; zero the buffer, ES:DI -> next buffer
	mov	es:BCB_DRV[bx],0FFh	; invalidate buffer
	mov	es:BCB_NEXT[bx],di	; point to where "next" will be
	mov	ax,bx
	sub	ax,dx			; work out what our previous was
	mov	es:BCB_PREV[bx],ax	;  and point to it
	pop	cx
	loop	setup_b80		; do them all

	mov	es:BCB_NEXT[bx],si	; the last's "next" is our first buffer
	mov	es:BCB_PREV[si],bx	; the first's "previous" is our last
	mov	ax,es			; AX:SI -> 1st buffer
	les	bx,func52_ptr		; ES:BX -> internal data structure
	mov	es:F52_BCBOFF[bx],si
	mov	es:F52_BCBSEG[bx],ax	; fixup buffer pointers
	mov	es:word ptr F52_BUF_INFO[bx],si
	mov	es:word ptr F52_BUF_INFO+2[bx],ax
	inc	ax			; seg FFFF ?
	 jnz	setup_b90		; skip if not
	mov	es:F52_HMAFLAG[bx],1	; buffers are in HMA
	mov	ax,es:F52_SECSIZE[bx]
	add	ax,15
	mov	cl,4
	shr	ax,cl			; convert to para size
	mov	dl,'B'			; allocate as a Buffer
	call	alloc_seg		; allocate a deblocking buffer
	mov	es:F52_DEBLOCK[bx],ax
	les	bx,drdos_ptr		; ES:BX -> data area
	mov	es:DRDOS_DEBLOCK[bx],ax	;  of deblocking buffer
setup_b90:
	ret

setup_doshndl:
	push	es
	les	bx,func52_ptr			; Internal Data Pointer
	lea	bx,F52_FILEPTR[bx]		; Start of Handle List
	mov	cx,num_files			; Number of DOS Handles
	add	cx,num_fcbs			; + some for FCB's
	cmp	cx,255
	 jbe	setup_dh10
	mov	cx,255				; maximum IFN is 255
setup_dh10:
	cmp	es:DCNTRL_DSOFF[bx],0FFFFh	; Last entry ?
	 je	setup_dh20			; no, loop round again
	les	bx,es:DCNTRL_DSADD[bx]		; Get the Next Entry
	sub	cx,es:DCNTRL_COUNT[bx]		; Update the count
	 jc	setup_dh30			; going negative isn't allowed
	jmps	setup_dh10

setup_dh20:
	jcxz	setup_dh30			; any left to allocate ?

	mov	ax,DHNDL_LEN			; How many bytes do we need
	mul	cx				; for the structure
	mov	dx,ax				; including the control

	add	ax,DCNTRL_LEN+15		; Ensure the new structure is
	shr ax,1 ! shr ax,1			; a paragraph value
	shr ax,1 ! shr ax,1			; allocate some memory
	mov	dl,'F'				; allocate for Files
    call    alloc_seg           

	mov	es:DCNTRL_DSOFF[bx],0		; link the new seg
	mov	es:DCNTRL_DSSEG[bx],ax		; to the end of the list
	
; We can now initialise the new structure
	les	bx,es:DCNTRL_DSADD[bx]		; Get the New Entry
	mov	es:DCNTRL_DSOFF[bx],0FFFFh	; terminate the list
	mov	es:DCNTRL_DSSEG[bx],0FFFFh	; with -1,-1
 	mov	es:DCNTRL_COUNT[bx],cx		; Number of elements
; Now zero the tables
	mov	ax,DHNDL_LEN			; How many bytes do we have
 	mul	cx				; with this number of elements
	mov	cx,ax				; in the structure
	lea	di,DCNTRL_LEN[bx]		; Zero the contents of the
	sub	al,al				; structure
	rep	stosb
	 
setup_dh30:
	pop	es
	ret


setup_drives:
	mov	al,next_drv		; AL = # of drives supported
	push	es ! push bx
	les	bx,func52_ptr		; ES:BX -> base of DOS variables
	mov	es:F52_PHYDRV[bx],al	; set # of Physical drives installed
	mov	es:F52_LASTDRV[bx],al	; set # of Logical drives installed
	pop	bx ! pop es
	ret


setup_fopen:		; allocate file hashing information
;-----------
	les	bx,drdos_ptr
	mov	ax,num_fopen		; get # of hashed directory entries
	cmp	ax,-1			; has it been set yet ?
	 jne	setup_fopen10		; yes, then leave it alone
	mov	ax,DEF_NUM_FOPEN
	cmp	es:DRDOS_HIMEM_ROOT[bx],0; do we have a high memory chain ?
	 jne	setup_fopen10		; high memory means no TPA hit
	xor	ax,ax			; keep things small otherwise
setup_fopen10:
	xor	dx,dx			; AX/DX = 32 bit # of entries
	mov	si,max_clsize		; max. cluster size
	mov	cl,5			; 32 byte per directory entry
	shr	si,cl			; SI = directory entries per cluster
	add	ax,si			; round up count to multiple of cluster
	dec	ax
	div	si			; AX = # of hashed blocks
	mov	cx,ax
	 jcxz	setup_fopen90		; skip if hashing disabled

	mov	es:DRDOS_HASHMAX[bx],si	; maximum # dir entries allowed

	shl	si,1			; SI = bytes required for data
	lea	si,HCB_DATA[si]		;  + control information
	mul	si			; AX bytes of data required
	test	dx,dx
	 jnz	setup_fopen90		; overflow (shouldn't happen)

; Allocate CX HCB_'s of size SI bytes, AX bytes in total

	mov	bx,es:DRDOS_HIMEM_ROOT[bx]; do we have a high memory chain ?
	test	bx,bx			;  zero indicates we don't
	 jz	setup_fopen30
	mov	dx,0FFFFh		; use the magic FFFF segment
	mov	es,dx
	cmp	ax,es:2[bx]		; is there enough room ?
	 ja	setup_fopen30		; no, forget try conventinal memory
	sub	es:2[bx],ax		; else allocate the memory
	mov	di,es:2[bx]		; get base+length
	add	di,bx			;  = our allocation
	mov	ax,es:2[bx]		; if the section left is under
	cmp	ax,2*WORD		;  2 words discard it
	 jae	setup_fopen20		;  as we may overwrite size/link
	mov	ax,es:[bx]		; get next entry
	les	bx,drdos_ptr		;  and make it the new himem root
	mov	es:DRDOS_HIMEM_ROOT[bx],ax
setup_fopen20:
	xchg	ax,dx			; AX = FFFF
	jmps	setup_fopen40		; AX:DI -> data block allocated

setup_fopen30:
	shr	ax,1			; convert size to para's
	shr	ax,1
	shr	ax,1
	shr	ax,1
	inc	ax			; allow for rounding
    mov dl,'E'          
	call	alloc_hiseg		; allocate it para aligned
	dec	ax			; zero offset terminates the chain
	mov	di,10h			;  so start with a non-zero offset
;	jmps	setup_fopen40		; AX:DI -> data block allocated

setup_fopen40:
; setup CX HCB_'s of size SI at AX:DI
	les	bx,drdos_ptr
	mov	es:DRDOS_HASHOFF[bx],di
	mov	es:DRDOS_HASHSEG[bx],ax
	mov	es,ax

setup_fopen50:
	mov	es:HCB_DRV[di],-1	; discard the HCB initially
	mov	bx,di			; remember where it is
	add	di,si			; onto next HCB_
	mov	es:HCB_LINK[bx],di	; link it to previous HCB_
	loop	setup_fopen50		; allocate all hash control blocks
	mov	es:HCB_LINK[bx],0	; zero terminate the list
setup_fopen90:				; all HCBs done, return
	push	cs
	pop	es			; back to 8080 model
	ret


	Public	whitespace

whitespace:
	lodsb				; Skip any White Space in the 
	cmp al,' ' ! jz whitespace	; CR/LF terminated string
	cmp al,TAB ! jz whitespace
	dec	si
	ret

	Public	build_cmd_tail
build_cmd_tail:
	push ds ! pop es
	mov	cx,length cfg_buffer - 3 ; (leave room for 3 extra chars)
	mov	di,offset cfg_buffer
build_cl1:
	lodsb			; Copy the device name
	cmp	al,' '		; until a Control char (end of line)
	 jbe	build_cl2	; or a Space (end of name)
	cmp	al,'/'		; Also stop scanning when a switch 
	 je	build_cl2	; character is detected
	stosb
	loop	build_cl1
	mov	al,CR		; indicate we can go no more....
build_cl2:
	cmp	al,CR		; it it really the end ?
	mov	al,' '		; now insert a space character
	stosb
	 je	build_cl_exit	; CR meant it's time to go
	dec	si		; rewind the source one character	
build_cl3:
	lodsb			; Copy the tail
	cmp	al,CR		; until we find a CR
	 je	build_cl4	; at the end of the line
	stosb
	loop	build_cl3
	mov	al,CR		; no more room, so terminate
build_cl4:
	stosb
build_cl_exit:
	mov	al,LF		; now insert the terminating linefeed
	stosb			; at the end of the buffer
	mov	si,offset cfg_buffer
	ret

save_vecs:
; save interrupt vectors so we can restore if device init fails
	push ds ! push es
	push si ! push di ! push cx
	mov	cx,(length vec_save_buf)*2	; CX = words to save
	xor	si,si
	mov	ds,si				; DS:SI -> vectors to save
	push cs ! pop es
	mov	di,offset vec_save_buf		; ES:DI -> save area
	rep	movsw				; save them
	pop cx ! pop di ! pop si
	pop es ! pop ds
	ret
	
restore_vecs:
; replace interrupt vectors after a dd_init fails
	push ds ! push es
	push si ! push di ! push cx
	mov	cx,length vec_save_buf		; CX = vectors to restore
	push cs ! pop ds
	mov	si,offset vec_save_buf		; DS:SI -> save area
	xor	di,di
	mov	es,di				; ES:DI -> vectors to restore
rest_vec1:
	mov	ax,es:2[di]			; get updated vector
	cmp	ax,mem_current			; below attempted driver?
	 jb	rest_vec2			; yes, don't zap it
	cmp	ax,mem_max			; above attempted driver?
	 jae	rest_vec2			; yes, don't zap it
	movsw ! movsw				; else restore vector
	jmps	rest_vec3			;   overwritten by driver
rest_vec2:
	add	si,dword			; skip vector in source
	add	di,dword			;   and in destination
rest_vec3:
	loop	rest_vec1			; next vector
	pop cx ! pop di ! pop si
	pop es ! pop ds
	ret


INITDATA	DSEG 'INITDATA'
	
	extrn	shell:byte		; Default Command Processor
	extrn	shell_cline:byte	; Default Command Line
	extrn	num_files:word		; default # of file handles
	extrn	num_fcbs:word		; default # of fcb file handles
	extrn	num_fopen:word		; default value for fast open
	extrn	country_code:word	; Requested Country Code (Default US)
	extrn	num_stacks:word		; # hardware stacks wanted
	extrn	stack_size:word		; size of a hardware stack


	extrn	mem_current:word	; Current Load Address
	extrn	mem_max:word		; Top of Available Memory
	extrn	init_dseg:word		; Current init Data Segment

	extrn	dos_dseg:word
	extrn	bios_seg:word
	extrn	func52_ptr:dword
	extrn	drdos_ptr:dword
	extrn	res_ddsc_ptr:dword




include	initmsgs.def				; Include TFT Header File


	extrn	preload_drv:byte
	extrn	init_drv:byte		; the initial boot drive

	Public	dev_load_seg, dev_reloc_seg, dev_epb, dev_name, dev_count
	Public	rel_unit, dev_epb
	Public	strategy_off, strategy_seg, interrupt_off, interrupt_seg, request_hdr
	Public	next_drv, strategy_seg, strategy, interrupt
	Public	strategy_seg, condev_off, condev_seg, clkdev_off, clkdev_seg
	Public	num_blkdev, blkdev_table, next_drv, max_secsize
	Public	max_clsize, init_buf, num_read_ahead_buf
	Public	buffersIn, history_flg, history_size
	Public	dbcs_tbl, ctry_info, boot_device, boot_drv, resdev_chain


history_flg	db	0	; Disable history buffers to save RAM
history_size	dw	256	; When enabled 2*history size are used for bufs

cfg_buffer	rb	CFG_BUF_LEN	; extra termination for buggy Windows
		db	CR,LF,0		; device driver - give it CR/LF to hit
	
init_buf	db	MIN_NUM_BUFFS	; default # of buffers
num_read_ahead_buf db	DEF_READ_AHEAD	; default # of read-ahead
buffersIn	db	0		; default is low

dev_count	db	0		; count of new drives (used by preload)
next_drv	db	0		; Next Drive to Allocate
num_blkdev	dw	0		; # of block devices installed

boot_device	dw	0,0		; ptr to boot device
boot_drv	db	0		; and the sub unit number

max_secsize	dw	0		; max. sector size encountered
max_clsize	dw	0		; max. cluster size encountered

;	Do not change the order of the next four words:
clkdev_off	rw	1		; clock device driver
clkdev_seg	rw	1
condev_off	rw	1		; console device driver
condev_seg	rw	1

bpbptr		rd	0		; temporary BPB pointer
bpboff		rw	1
bpbseg		rw	1

devptr		rd	0		; temporary device header pointer
devoff		rw	1
devseg		rw	1

resdev_chain	rd	0		; head of chain for resident device
resdev_off	dw	-1		; drivers
resdev_seg	dw	-1
	
abs_unit	dw	0		; absolute unit #
rel_unit	dw	0		; relative unit #

blkdev_table	rb	BLKDEV_LENGTH*26 ; save block device driver addr. here

;
;	Variable for the FUNC_DEVICE and DEV_INIT sub routines
;

strategy	rd	0		; Device Strategy Entry Point
strategy_off	rw	1		; Offset 
strategy_seg	rw	1		; Segment Address

interrupt	rd	0		; Device Entry Point
interrupt_off	rw	1		; Offset
interrupt_seg	rw	1		; Segment Address

dev_root	rd	0		; Pointer to Root of Device List
dev_offset	rw	1		; Offset of First Device
dev_segment	rw	1		; Segment of First Device

request_hdr	rb	RH_SIZE		; DOS Request Header

dev_name	rb	MAX_FILELEN

dev_epb		rw	0
dev_load_seg	rw	1		; Load Segment for Device Driver
dev_reloc_seg	rw	1		; Relocation Factor to be Applied

;
; A number of routines share this common buffer as they are never required
; at the same time. The current clients are -
;
; BDOSLDR.A86:	a sector buffer for boot load of IBMDOS
; CONFIG.A86:	vector save buffer (during devicehigh)
; NLSFUNC.A86:	scratch area for country info

	Public	sector_buffer
sector_buffer	rb	0
;		rb	512		; we need to read a single sector

	Public	nls_temp_area
nls_temp_area	rb	0		; NLS buffer can be shared with
;		rb	258		;  vec_save_buf as they are never
					;  used together


vec_save_buf	rd	256		; reserve space to save int vectors

dbcs_buf	rb	1		; BDOS puts a 7 here
dbcs_tbl	rd	1		; pointer to DBCS table in BDOS

ctry_info	rb	CI_LENGTH	; country information 

num_buf		dw	0		; # of buffers allocated


; The following are used for detecting old bus master controllers:

removableMediaRequest	db	13		; length of request
			db	0		; unit
			db	15		; removable media check command
			dw	0		; status
			rb	8		; reserved bytes


readRequest		db	30		; length of request
			db	0		; unit
			db	4		; read command
			dw	0		; status
			rb	8		; reserved bytes
			db	0F8h		; media ID
			dw	0,0		; buffer address
			dw	1		; read one sector
			dw	0FFFFh		; use big sector read
			dw	0,0		; Volume ID
			dw	1,0		; starting sector zero


UpperMemoryBuffer	dw	0

numUnits		dw	0

deblockPointer		rd	0
deblockOffset		dw	0
deblockSeg		dw	0

	Public	DeblockSetByUser
DeblockSetByUser	db	FALSE


	end
