;    File              : $BIOSINIT.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$
;    BIOSINIT.A86 1.43 93/12/03 00:38:19
;    Fix bug in AllocHMA when base not para aligned
;    BIOSINIT.A86 1.42 93/11/29 21:40:03
;    Fill in name field of system DMD's (owner=8) with 'SC'
;    BIOSINIT.A86 1.41 93/11/18 15:43:14 
;    Add primitive multi-master checking
;    BIOSINIT.A86 1.40 93/11/11 12:25:29 
;    VDISK header changes
;    BIOSINIT.A86 1.39 93/11/08 23:19:22 
;    SetupHMA does CALL5 initialisation
;    BIOSINIT.A86 1.38 93/10/29 20:03:48
;    BIOS relocation services restored for possible 3rd party memory manager use
;    BIOSINIT.A86 1.37 93/10/29 19:42:27
;    Change HIDOS default to off
;    BIOSINIT.A86 1.36 93/09/22 15:22:14
;    Change int21/4458 to les bx,cs:drdos_ptr (smaller, faster)
;    BIOSINIT.A86 1.35 93/09/03 20:10:55
;    Support intl YES/NO
;    BIOSINIT.A86 1.34 93/09/02 22:34:42
;    Add header to system allocations
;    BIOSINIT.A86 1.33 93/09/01 17:36:57
;    increase stack size for aspi4dos.sys
;    BIOSINIT.A86 1.31 93/08/06 20:55:16
;    re-arrange device init order for SCREATE.SYS on a VDISK.SYS
;    BIOSINIT.A86 1.28 93/08/02 14:45:43
;    hide preload drives from func_device
;    ENDLOG

	include	i:msdos.equ		; DOS Function Equates
	include	i:psp.def		; PSP Definition
	include i:f52data.def		; Internal DOS data area
	include	i:doshndl.def		; Dummy DOS structures
	include	config.equ
	include	i:fdos.equ
	include	i:modfunc.def

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

;
;	Equates for INIT_FLAGS which can be modified by the BIOS
;	the default is a RAM based BDOS (Code and Data) with INIT_DRV
;	specifing the default drive and the initial drive for COMSPEC
;

INIT_ROMCODE	equ	0001h		; Rom based DOS CODE
INIT_COMSPEC	equ	0002h		; COMSPEC_DRV specifies the default
					; Command Processor Drive
INIT_WINDOWS	equ	0004h		; Disable windows support

COMMAND_BASE	equ	000E0h		; must cover FFFF:D0 for CALL5 fixup
COMMAND_SIZE	equ	015C0h

CGROUP	GROUP	CODE, INITCODE, INITDATA, INITPSP, INITENV, DATAEND
CODE	CSEG
eject
;
;	The DOS Code Segment is formatted as follows.
;
DOS_OFFSET	equ	word ptr  .0008h	; Offset of code in segment
HISTORY_CODE	equ	word ptr  .000Ch	; Start of history code
INIT_CODE	equ	word ptr  .000Eh	; Start of initialisation code
DOS_FLAG	equ	word ptr  .001Ch	; Compressed Data Flag
DOS_CODE	equ	word ptr  .001Eh	; DOS Code Length (Bytes)
DOS_DATA	equ	word ptr  .0020h	; DOS Data Length (Bytes)
NO_YES_CHARS	equ	word ptr  .0028h	; DOS Data No/Yes characters

INT31_SEGMENT	equ	word ptr .00C6h		; DOS Data Segment pointer
						; for ROM systems
JMPF_OPCODE	equ	0EAh			; 8086 JMPF instruction

INITCODE	CSEG	PARA 'INITCODE'
	extrn	cleanup:near			; BIOS Clean Up routine
	extrn	config_init:near		; CONFIG Code Init
	extrn	config_finish:near		; Update DOS with Device Info
	extrn	config:near			; CONFIG.SYS Processor
	extrn	crlf:near			; Output CR/LF to screen
	extrn	resident_device_init:near	; Device Driver Init
	extrn	read_dos:near			; load DOS file
	extrn	setup_ldt:near
	extrn	setup_stacks:near
	extrn	dos_version_check:near

		db	'Copyright (c) 1983,1996 '
		db	'Caldera, Inc. All Rights Reserved '
		db	'XXXX-0000-987654321X'


	Public	biosinit
;========
biosinit:
;========
;	entry:	MEM_SIZE    = memory size in paragraphs
;		DEVICE_ROOT = address of 1st resident device driver
;		INIT_DRV    = boot drive (0 = A:, 1 = B:, etc.)
;		INIT_BUF    = minimum # of disk buffers
;		CURRENT_DOS = code segment of DOS (if loaded)
;		INIT_FLAGS  = Control Flags
;		COMSPEC_DRV = Drive for Command Processor
;	
;
; we set up the following variables
;		BIOS_SEG    = low memory BIOS code/data (static)
;		DOS_DSEG    = low memory DOS data area (static)
;		RCODE_SEG   = relocated BIOS code segment
;		DOS_CSEG    = relocated DOS code segment
;		INIT_DSEG   = segment based initialisation data
;
	cld
	cli
	mov	ax,cs			; Initialise our stack and Data Segment
	mov	ds,ax
	mov	ss,ax
	mov	sp,offset stack
	sti

	mov	bios_seg,ax		; Save the BIOS Segment

; Now some code which allows Remote Program Loader to reserve some memory
; which will be safe from being trampled on by the system.
; The RPL takes over Int 2F and has a magic signature "RPL" at offset 3 from
; it's entry point. If this is detected an Int2f is issued
;
; On Entry:
;	AX = 4A06, DX = Segment address of top of memory
; On Exit:
;	DX = segment address of the RPL
;
; On return the system will build a DMD entry for the RPL, with an owner field
; of 8 (ie. System). The RPL can poke this entry to 0 when it wishes to free
; the memory.
;
; In addition we now look for "RPLOADER", and if found we remember the address
; of the entry point so we can call it with status information

	mov	dx,mem_size		; get existing size
	dec	dx			; one para less for upper mem DMD link
	xor	ax,ax
	mov	es,ax			; point to vectors
	mov	bx,4*2fh		; we want Int 2F vector
	les	bx,es:dword ptr [bx]	; pick up the contents
	lea	di,3[bx]		; point to magic signature "RPL"
	mov	si,offset rpl_name
	mov	cx,3
	repe	cmpsb			; does the signature match ?
	 jne	biosinit20
	mov	cx,5			; look also for "RPLOADER"
	repe	cmpsb
	 jne	biosinit10
	mov	rpl_off,bx		; save entry point for use later
	mov	rpl_seg,es
biosinit10:
	mov	ax,4a06h		; magic number for RPL
	int	2fh			; does anyone want to steal memory ?
	inc	dx
	cmp	dx,mem_size		; is memory size unchanged ?
	 jnb	biosinit20
	dec	dx			; point back at start of memory
	dec	dx			;  then one below for DMD start
	mov	es,dx			; ES points to DMD
	mov	DMD_ID,IDZ		; make it last in the chain
	mov	DMD_PSP,8		; owned by system
	lea	di,DMD_NAME		; point to name field
	mov	si,offset rpl_name
	mov	cx,(length rpl_name)/2
	rep	movsw			; initialise name field too
	inc	dx			; skip the DMD for real top
	xchg	dx,mem_size		; replace memory size with new value
	sub	dx,mem_size		; whats the difference ?
	mov	DMD_LEN,dx		; save it's this length
biosinit20:

; End of RPL support
	mov	ax,mem_size		; get top of memory
	sub	ax,MOVE_DOWN
	mov	mem_max,ax		; last available paragraph

	mov	init_dseg,ax		; initialisation data lives here
	mov	cl,4
	mov	dx,offset DYNAMIC_DATA_END+15
	shr	dx,cl			; we need this much dynamic data
	add	ax,dx

; Now we try to relocate the BIOS
	mov	dx,rcode_len		; we want to keep this much BIOS code
	add	systemSize,dx		;  so add to reserved space in HMA
	mov	dx,icode_len		; how much do we want to move ?
	shr	dx,cl
	 jz	biosinit30		; if ROMed we have nothing to relocate
	mov	rcode_seg,ax		; relocated BIOS lives here
	add	ax,dx			; remember how much we allocated
	mov	dx,rcode_offset
	mov	si,dx
	mov	di,dx
	shr	dx,cl			; DX = para offset of data
	sub	rcode_seg,dx		; adjust our segment value
	mov	es,rcode_seg
	mov	cx,icode_len
	rep	movsb			; copy it up
biosinit30:
	mov	dos_cseg,ax		; a relocated DOS image will live here

	mov	ax,offset biosinit_end+32
	mov	cl,4			; Leave the Last Paragraph Free for
    shr ax,cl           ;  himem DMD 
	neg	ax			; Calculate the destination
	add	ax,mem_size		; Segment for the BIOS relocation

	mov	cx,offset biosinit_end	; Relocate the BIOSINIT code to 
	mov	si,offset biosinit	; the top of available memory
	mov	di,si
	sub	cx,si			; Size of BIOSINIT
	mov	es,ax			; Initialize ES and copy CX words
	rep	movsb

	push	es			; fiddle RETF to relocated code
	mov	ax,offset relocated_init
	push	ax
	retf
;
;	Generic BIOS INIT Patch area
;
	include	i:patch.cod
;
;	BIOSINIT CODE and DATA have now been relocated to high memory
;
relocated_init:
	mov	ax,cs
	cli
	mov	ss,ax
	mov	sp,offset stack
	sti
	mov	ds,ax			; All Segment registers now point
	mov	es,ax			; to the relocated BIOSINIT


	call	config_init		; initialize setup module
	call	dd_fixup		; fixup relocatable device drivers
	les	di,device_root		; initialize all the resident
	call	resident_device_init	;   device drivers
	push cs ! pop es

	mov	dx,1			; phase one of RPL initialisation
	call	rploader		;  inform RPLoader if present
	call	Verify386		; CY set if not a 386
	mov	ax,mem_current		; get ending address returned by BIOS
	 jc	dont_align
	cmp	ax,0100h
	 jae	dont_align		; lets be 4 KByte aligned to benefit
	mov	ax,0100h		;  the multi tasker (386 or above)
dont_align:
	mov	free_seg,ax		;  and save as first Free Segment
	cmp	current_dos,0		; does the OEM want us to read
	 jnz	dos_reloc		;   the DOS file from disk?
	mov	ax,dos_cseg
	mov	current_dos,ax		; the file is held on the INIT_DRV with
	call	read_dos		;   the name specified in DOS_NAME

eject
;
;	The following code will relocate the DOS code.
;
dos_reloc:
;
; We now move the DOS data to low memory
;
	mov	ax,current_dos
	mov	dos_cseg,ax		; Update the DOS Code Segment
	mov	ds,ax

	mov	cl,4

	mov	ax,ds:DOS_CODE		; get size of DOS code
	add	cs:systemSize,ax	;  and add to the system size
	shr	ax,cl			; convert to para's
	mov	cs:dosCodeParaSize,ax	; save for EMM386.SYS
	
	mov	ax,ds:DOS_OFFSET	; remember we have padding
	add	cs:dos_coff,ax		;  and adjust DOS init offset
	shr	ax,cl			; also adjust DOS segment
	sub	cs:dos_cseg,ax		;  to account for padding

	xor	ax,ax
	mov	es,ax			; ES -> interrupt vectors
	mov	ax,ds:DOS_DATA		; get # of bytes of DOS data
	shr	ax,cl			; get para size of DOS data
	xchg	ax,cs:free_seg		; get seg for DOS data
	add	cs:free_seg,ax		; remember how much we used
	mov	es:INT31_SEGMENT,ax	; update the segment value of INT31
	mov	es,ax			;  so ROMMED systems can find PCM_DSEG
	mov	cs:dos_dseg,ax		; we need to remember where too...

	mov	si,ds:DOS_CODE		; offset of DOS Data
	xor	di,di			; destination offset

	test	DOS_FLAG,1		; has the DOS Data been compressed
	 jnz	dos_r20			; yes so call the decompress routine
	mov	cx,ds:DOS_DATA		;  otherwise just copy the data.
	rep	movsb
	jmps	dos_r40

;
;	This routine will decompress the DOS data area which has 
;	been compressed after linking using Andy Wightmans data 
;	compression algorithm.
;
dos_r20:
	lodsw				; get control word
	mov	cx,ax			; as a count
	 jcxz	dos_r40			; all done
	test	cx,8000h		; negative ?
	 jnz	dos_r30			; yes do zeros
	rep	movsb			; else move in data bytes
	jmps	dos_r20			; and to the next

dos_r30:
	and	cx,7fffh		; remove sign
	 jcxz	dos_r20			; none to do
	xor	ax,ax
	rep	stosb			; fill with zeros
	jmps	dos_r20

dos_r40:
	push cs ! pop ds
	push cs ! pop es

	mov	cl,4			; reserve space for resident DDSC's
	mov	ax,DDSC_LEN
	mul	dev_count		; AX byte are required
	add	ax,15
	shr	ax,cl			; AX para are required
	xchg	ax,free_seg
	add	free_seg,ax		; we have allocated the space
	mov	res_ddsc_seg,ax		; point res_ddsc_ptr at the space
	mov	dx,dos_dseg
	sub	ax,dx			; DOS resident DDSC_'s use DOS data seg
	cmp	ax,1000h		; surely we must fit ?
	 jae	dos_r50
	shl	ax,cl			; offset within pcmode data segment
	mov	res_ddsc_off,ax
	mov	res_ddsc_seg,dx		; setup pointer to resident DDSC's
dos_r50:

;
;	Call the DOS INIT Code passing all the information setup
;	by the BIOS.
;
	mov	ax,mem_size		; pass the Memory Size, the first free
	mov	bx,free_seg		;  segment and the initial 
	mov	dl,init_drv		;  drive to the DOS init routine
	cli
	mov	ds,dos_dseg		; DS -> DOS data segment
	callf	cs:dos_init

	mov	es,cs:dos_dseg
	mov	bx,26h			; ES:BX -> list of lists
	mov	ax,es:word ptr F52_FCBPTR[bx]
    shr ax,1 ! shr ax,1     
    shr ax,1 ! shr ax,1     
	and	es:word ptr F52_FCBPTR[bx],15
	add	es:word ptr F52_FCBPTR+2[bx],ax
	sti
	push cs ! pop ds
	mov	es,current_dos		; internationalise the yes/no chars
	mov	di,es:NO_YES_CHARS
	mov	es,dos_dseg		; ES:DI -> internal table
	mov	ax,word ptr no_char
	stosw				; replace default no chars
	mov	ax,word ptr yes_char
	stosw				; replace default yes chars
	push cs ! pop es
	add	dos_coff,3		; next dos_init call just fixes up
					;  segment relocations

	mov	dx,2			; phase two of RPL initialisation
	call	rploader		;  inform RPLoader if present

	call	config_start		; get free memory
	call	config			; read and process CONFIG.SYS
	call	config_end		; relocate DOS code and free memory

	mov	ax,(MS_X_OPEN*256)+2	; Open for Write
	mov	dx,offset idle_dev	; Get the IDLE Device Name#
	int	DOS_INT			; Open the device
	 jc	dos_r70			; Quit on Error
	push	ax			; Save the Handle
	mov	ax,4458h		; Get the address of the IDLE data
	int	DOS_INT			; area in ES:AX
	pop	bx			; Restore the Handle
	mov	idle_off,ax		; Save the data area offset and 
	mov	idle_seg,es		; segment
	mov	ax,4403h
	mov	dx,offset idle_off
	mov	cx,DWORD
	int	DOS_INT
	mov	ah,MS_X_CLOSE
	int	DOS_INT

dos_r70:
	call	mark_system_memory	; ensure any memory we have allocated
					; is marked as system
	mov	bios_offset,offset cleanup
	callf	bios			; execute BIOS cleanup code

	mov	ax,(MS_M_STRATEGY*256)+3
	xor	bx,bx			; unlink in upper memory region
	int	21h

	mov	dx,3			; phase three of RPL initialisation
	call	rploader		;  inform RPLoader if present

	mov	ax,12ffh		; magic cleanup call to MemMAX
	mov	bx,5			;  to do any tidy ups it wishes
	xor	cx,cx
	xor	dx,dx
	int	2fh

	push cs ! pop es
load_e10:
	mov	ax,(MS_X_EXEC * 256)+0	; Exec the Command Processor
	mov	bx,offset exec_env	; Get the Parameter Block Address
	mov	dx,offset shell		; and the Command Processor
	mov	exec_clseg,ds
	mov	exec_fcb1seg,ds
	mov	exec_fcb2seg,ds
	int	DOS_INT			; Go for it
	mov	ah,MS_C_WRITESTR	; Print an error message and wait for
	mov	dx,offset bad_exec	;  the user to enter new name
	int	DOS_INT
	mov	ah,MS_C_READSTR		; get user to input new COMMAND
	mov	dx,offset shell_ask	;  location
	int	DOS_INT
	call	crlf			; tidy up with CR/LF
	xor	bx,bx
	mov	bl,shell_end
	mov	shell[bx],bh		; replace CR with NULL
	jmps	load_e10

eject
;
;	Initialise the PSP and inform DOS of the
;	location of the BIOSINIT PSP. The MS_P_SETPSP *MUST* be the first 
;	INT21 function call because the PSP Address is used during the
;	entry code except when the INDOS flag is set and certain function 
;	calls are made.
;
;	Then open the Resident character devices so that the dynamically
;	devices can output messages to the screen etc.
; 
config_start:
	mov	cl,4
	mov	bx,ds
	mov	ax,offset psp		; Now force DOS Plus to use the 
	shr	ax,cl			; internal PSP for all disk and
	add	bx,ax			; character I/O
	mov	xftbl_seg,bx		; Update the Handle Table Pointer
	mov	parent_psp,bx		; and make this the root process
	mov	ah,MS_P_SETPSP		; Set the current PSP
	int	DOS_INT
if DOS5
	mov	ax,3306h
	int	21h			; get true version
	mov	dosVersion,bx		; and plant in initial PSP
endif
	call	dos_version_check	; make sure we are on correct DOS

	mov	ax,4458h
	int	DOS_INT			; we need to access local data
	mov	drdos_off,bx		; so save a pointer to it
	mov	drdos_seg,es
	mov	ax,ext_mem_size
	mov	es:DRDOS_EXT_MEM[bx],ax	; save extended memory size in DOS
	mov	ax,5200h
	int	DOS_INT
	mov	func52_off,bx
	mov	func52_seg,es		; save pointer to internal data
if DOS5
	mov	ax,ext_mem_size
	mov	es:F52_EXT_MEM[bx],ax	; save extended memory size in DOS
endif
	mov	ax,(offset TEMP_LDT)/16	; use our temporary LDT's
	add	ax,init_dseg		;  during system init
	mov	es:F52_PATHOFF[bx],0	; point at the LDT's
	mov	es:F52_PATHSEG[bx],ax
	push cs ! pop es

	mov	ah,MS_M_ALLOC		; Allocate all available memory
	mov	bx,0FFFFh		; BX is returned with the maximum
	int	DOS_INT			; available block size

	mov	ah,MS_M_ALLOC		
	int	DOS_INT
	mov	mem_first_base,ax	; Base of 1st allocated block
	mov	mem_current_base,ax	; Base of allocated memory
	mov	mem_current,ax		; Next available Segment

	call	config_finish		; Update DOS with the information
					; obtained from loading the resident
					; drivers.
	mov	ah,MS_DRV_SET		; Select the Default Drive
	mov	dl,init_drv		; passed to us by the BIOS
	int	DOS_INT

	mov	ah,MS_F_DMAOFF		; Initialise the DMA address for 
	mov	dx,offset search_state	; the Search First State data
	int	DOS_INT

	mov	al,init_drv		; get the boot drive then check
	test	init_flags,INIT_COMSPEC	; flags to see if this is the
	 jz	config_s05		; default COMSPEC drive.
	mov	al,comspec_drv

config_s05:
	add	shell,al		; update the drive letter of shell
	add	shell_drv,al		;  and the reload path

	call	open_stdaux		; Open STDAUX as internal handle #0
	call	open_stdcon		; Open Standard CON Devices as #1
	mov	ah,MS_X_CLOSE		; now close AUX again
	mov	bx,STDAUX		; for CONFIG processing
	int	DOS_INT
	ret


;
;	Relocate the DOS CODE from high memory to immediately above
;	the device drivers, buffers etc. Then call the DOS_CLEANUP code
;	so that any self segment pointers maintained in the DOS DATA
;	can be updated. Then free all the unused memory and reopen the
;	standard devices.
;
config_end:
	push	es
	mov	al,last_drv		; get lastdrive value
	les	bx,func52_ptr
	cmp	al,es:F52_PHYDRV[bx]	; less than the # of Physical drives ?
	 ja	config_end10
	mov	al,es:F52_PHYDRV[bx]	; ensure minimum of # physical drives
config_end10:
	mov	es:F52_LASTDRV[bx],al	; set # of drives installed
	mov	cl,4			; we will be converting byte-paras
	mov	ah,LDT_LEN		; we need this many bytes per drive
	mul	ah			; *lastdrive
	add	ax,15			; round LDT's size up to para
	shr	ax,cl
	mov	dl,'L'			; allocate LDT's
	call	alloc_instseg		; Allocate memory AX is destination
	mov	es:F52_PATHOFF[bx],0	; point at the LDT's
	mov	es:F52_PATHSEG[bx],ax	; save seg we just allocated
	pop	es

	call	setup_ldt		; initialise LDT structures

	call	setup_stacks		; allocate stacks

	call	relocate_system		; relocate system as requested

	push	es			; Free all of the unused memory
	mov	es,mem_current_base	; ES: Base Allocated Memory
	mov	bx,mem_current		; Get the currently allocated memory
	sub	bx,mem_current_base	; and subtract mem_current_base to
	mov	ah,MS_M_SETBLOCK	; give the number of paragraphs used
	int	DOS_INT			; and modify block accordingly

; Kludge - if the CONFIG file has had a line of the form INSTALL= to load a TSR
; then that TSR will have inherited the handles, so bumping the open count, but
; the func 31 exit leaves all these files open. As a result we will get the
; wrong internal file numbers unless we force complete closure. So we keep
; trying to close each internal handle until we get an error.

	mov	ah,MS_P_GETPSP
	int	DOS_INT			; get current PSP
	mov	es,bx
	mov	cx,PSP_XFNMAX		; Close all the standard handles
	les	di,PSP_XFTPTR		; and then reopen them in case a
	mov	bx,0			; dynamicly loadable device has
cfg_e10:				; replaced the BIOS driver
	mov	dl,es:[di+bx]		; save old internal handle
	mov	ah,MS_X_CLOSE
	int	DOS_INT			; try and close this handle
	mov	es:[di+bx],dl		; put the internal handle back 
	 jnc	cfg_e10			; and try and close it again
	mov	es:byte ptr [di+bx],0ffh
	inc	bx			; mark as closed and try next handle
	loop	cfg_e10
	pop	es
;;	jmps	open_std	

open_std:
	call	open_stdaux		; open AUX device as STDAUX
	call	open_stdcon		; now STDIN, STDOUT, STDERR
;	jmp	open_stdprn		; finally STDPRN

open_stdprn:
	mov	ax,(MS_X_OPEN * 256) + 1
	mov	dx,offset printer	; Open the PRN device
	int	DOS_INT
	 jc	open_sp10		; No PRN device
	cmp	ax,STDPRN		; If all the previous Opens were
	 jz	open_sp10		; successful then this is STDPRN
	mov	bx,ax			; otherwise force this to STDPRN
	mov	cx,STDPRN
	mov	ah,MS_X_DUP2
	int	DOS_INT
	mov	ah,MS_X_CLOSE
	int	DOS_INT
open_sp10:
	ret

open_stdcon:
	mov	ax,(MS_X_OPEN * 256) + 2
	mov	dx,offset console	; Open the CON device
	int	DOS_INT
	 jc	open_sc10		; No CON device
	mov	bx,ax			; First Open should be STDIN
	mov	cx,STDOUT		; Force Duplicate to STDOUT
	mov	ah,MS_X_DUP2
	int	DOS_INT
	mov	cx,STDERR		; Then Force Duplicate to STDERR
	mov	ah,MS_X_DUP2
	int	DOS_INT
open_sc10:
	ret
	
open_stdaux:
	mov	ax,(MS_X_OPEN * 256) + 2
	mov	dx,offset auxilary	; Open the AUX device
	int	DOS_INT			; to get internal handle 0
	 jc	open_sa10		; No AUX device
	mov	bx,ax			; Force DUP to STDAUX
	mov	cx,STDAUX
	mov	ah,MS_X_DUP2
	int	DOS_INT
	mov	ah,MS_X_CLOSE
	int	DOS_INT
open_sa10:
	ret


eject
relocate_system:
	push ds ! push es
	cmp	dos_target_seg,0FFFFh	; is the OS going high ?
	 jne	relocate_system10
	call	SetupHMA		; make sure HMA chain is established
	xor	cx,cx
	xchg	cx,systemHMA		; free up any space reserved for the OS
	call	FreeHMA
	call	ReserveCommandHMA	; reserve space for COMMAND.COM
relocate_system10:
	call	reloc_bios		; move down relocatable drivers
	call	reloc_dos		; move DOS above drivers if RAM based
	xor	cx,cx
	xchg	cx,commandHMA
	call	FreeHMA			; return command.com HMA space to pool
	cli
	mov	ds,dos_dseg		; DS -> DOS data segment
	callf	cs:dos_init		; (in case of CS relative fixups)
	sti
	pop es ! pop ds
	ret

eject
reloc_dos:				; move DOS down to just above drivers
;----------
	push	ds
	push	es
	test	init_flags,INIT_ROMCODE	; Run the DOS code in ROM
	 jz $+5 ! jmp reloc_dos90	; at CURRENT_DOS - No Code Reloc
	mov	es,current_dos
	mov	cx,es:DOS_CODE		; get DOS code size in bytes
	mov	ax,dos_target_seg	; get DOS target
	cmp	ax,0FFFFh		; it it seg FFFF ?
	 jne	reloc_dos10
	mov	es,current_dos
	mov	dx,es:DOS_OFFSET
	call	AllocHMA		; allocate CX bytes, offset < DX
	 jnc	reloc_dos50		;  if we can use high memory
	xor	ax,ax			; can't, so try auto-allocation
reloc_dos10:
	test	ax,ax			; has a specific address been
	 jnz	reloc_dos40		;  specified ?
	push	cx			; save DOS code size
	xchg	ax,cx
	mov	cl,4
	shr	ax,cl			; convert to paragraphs
	pop	cx
	cmp	hidos,0			; do we want to relocate DOS ?
	 je	reloc_dos20		;  no, allocate conventionally
	call	alloc_upper		;  else allocate space for the DOS
	 jnc	reloc_dos40		;  in upper memory if possible
reloc_dos20:
	mov	es,current_dos		; if conventional memory we
	mov	ax,es:INIT_CODE		;  can discard INIT code
	cmp	history_flg,0		; is history enabled ?
	 jne	reloc_dos30
	mov	ax,es:HISTORY_CODE	; no, discard history code as well
reloc_dos30:
	push	cx
	add	ax,15
	mov	cl,4			; convert to paragraphs
	shr	ax,cl
	pop	cx
	call	alloc_seg_with_padding	; allocate in conventional memory
reloc_dos40:
	xchg	ax,dx			; save segment address
	mov	es,current_dos		; point at code
	mov	ax,es:DOS_OFFSET	; get offset of code start
	xor	di,di
	mov	es,dx			; ES:DI -> destination address
	push	cx			; save DOS size
	mov	cl,4
	shr	ax,cl			; AX = header size in para's
	sub	dx,ax			; adjust DOS segment accordingly
	pop	cx			; CX = DOS size in bytes
reloc_dos50:
; At this point
; CX = # bytes to move
; ES:DI -> destination
; DX = segment to fixup
;
	mov	dos_cseg,dx		; new code segment for DOS
	mov	ds,current_dos		; DS -> DOS code
	xor	si,si
	shr	cx,1			; CX = # of words in DOS
	rep	movsw			; copy DOS down

reloc_dos90:				; fixups performed
	pop	es
	pop	ds
	ret



	Public	HookInt2F

HookInt2F:
;---------
; Hook Int 2F during device driver initialisation so we can intercept
; some broadcasts
; On Entry:
;	None (beware DS/ES can be anything)
; On Exit:
;	None (All regs preserved)
;
	push	es
	push	ax
	push	bx
	les	bx,cs:drdos_ptr
	mov	bx,es:DRDOS_INT2F[bx]	; ES:BX -> Int 2F hooks
	mov	ax,offset Int2FHandler
	xchg	ax,es:4[bx]		; get Int 2F offset
	mov	cs:int2FOff,ax
	mov	ax,cs
	xchg	ax,es:6[bx]		; get Int 2F segment
	mov	cs:int2FSeg,ax
	pop	bx
	pop	ax
	pop	es
	ret

	Public	UnhookInt2F

UnhookInt2F:
;-----------
; Device driver initialisation has finished, so unhook from Int 2F
; On Entry:
;	None (beware DS/ES can be anything)
; On Exit:
;	None (All regs preserved)
;
	push	es
	push	ax
	push	bx
	les	bx,cs:drdos_ptr
	mov	bx,es:DRDOS_INT2F[bx]	; ES:BX -> Int 2F hooks
	mov	ax,cs:int2FOff
	mov	es:4[bx],ax		; restore Int 2F offset
	mov	ax,cs:int2FSeg
	mov	es:6[bx],ax		; restore Int 2F segment
	pop	bx
	pop	ax
	pop	es
	ret


; During device driver init we provide some services on Int 2F
; eg. 12FF for EMM386.SYS and 4A01/4A02 for Windows HIMEM.SYS

Int2FHandler:
;------------
; On Entry:
;	callers DS on stack
; On Exit:
;	if not handled pass on to BIOS, callers DS on stack, all regs preserved
;
	pop	ds			; pop DS from stack
	cmp	ax,4A01h		; Query Free HMA Space ?
	 je	HMAQueryFree
	cmp	ax,4A02h		; Allocate HMA Space ?
	 je	HMAAlloc
	cmp	ax,12FFh		; is it a relocation service ?
	 jne	OldInt2F
	sti				; if we RETF don't leave IF disabled
	cmp	bx,9			; register upper memory link
	 je	DOSUpperMemoryRoot
	cmp	bx,1			; Relocate BDOS
	 jb	DOSQuerySize		; what's the size of DOS
	 je	DOSRelocate		; where to put it
	cmp	bx,3			; Relocate BIOS
	 jb	BIOSQuerySize		; what's the size of BIOS
	 je	BIOSRelocate		; where to put it

OldInt2F:
	push	ds			; DS on stack as expected
		db	JMPF_OPCODE
int2FOff	dw	0
int2FSeg	dw	0


; Enquire DOS size
DOSQuerySize:
;------------
; On Entry:
;	None
; On Exit:
;	AX = 0
;	DX = DOS Size in para's
;
	mov	dx,cs:dosCodeParaSize	; DX = para's required for DOS code
	jmps	RelocExit

; Relocate DOS
DOSRelocate:
;-----------
; On Entry:
;	DX = para to reloacte to (FFFF=HMA)
; On Exit:
;	AX = 0
;
	mov	cs:dos_target_seg,dx	; save where
	jmps	RelocExit


; Enquire BIOS size
BIOSQuerySize:
;-------------
; On Entry:
;	None
; On Exit:
;	AX = 0
;	DX = BIOS Size in para's
;
	mov	dx,cs:rcode_len		; DX = bytes required for BIOS code
	add	dx,15
	mov	cl,4
	shr	dx,cl			; DX para's required
	jmps	RelocExit

; Relocate BIOS
BIOSRelocate:
;------------
; On Entry:
;	DX = para to reloacte to (FFFF=HMA)
; On Exit:
;	AX = 0
;
	mov	cs:bios_target_seg,dx	; save where
;	jmps	RelocExit

RelocExit:
	xor	ax,ax			; indicate success
	retf	2


DOSUpperMemoryRoot:
;------------------
	les	bx,cs:drdos_ptr
	mov	es:DRDOS_DMD_UPPER[bx],dx	; remember upper memory link
if DOS5
	les	bx,cs:func52_ptr
	mov	es:F52_DMD_UPPER[bx],dx		; remember upper memory link
endif
	xor	ax,ax
	retf	2

HMAAlloc:
;--------
; On Entry:
;	BX = # bytes to allocate
; On Exit:
;	ES:DI -> start of allocated block
;	BX trashed
;
	push	ds
	push	ax
	push	cx
	push	dx
	push	si
	push	bp
	push cs ! pop ds		; establish data seg
	mov	cx,bx			; CX = bytes wanted
	mov	dx,0FFFFh		; anywhere is OK
	call	AllocHMA		; ES:DI -> allocated data
	pop	bp
	pop	si
	pop	dx
	pop	cx
	pop	ax
	pop	ds
	iret


HMAQueryFree:
;------------
; On Entry:
;	None
; On Exit:
;	BX = Size of block remaining (0 if no HMA)
;	ES:DI -> start of available HMA (FFFF:FFFF if no HMA)
;
	push	ds
	push	ax
	push	cx
	push	dx
	push	si
	push	bp
	push cs ! pop ds		; establish data seg
	call	SetupHMA		; allocate the HMA for OS use
	les	bx,cs:drdos_ptr
	mov	di,es:DRDOS_HIMEM_ROOT[bx]
	mov	ax,0FFFFh		; get offset of HMA entry
	mov	es,ax
	test	di,di			; do we have a himem root ?
	 jz	HMAQueryFree10		; no, return failure
	mov	bx,es:2[di]		; BX = size of region
	mov	ax,di			; para align the base
	add	ax,15			; because the allocation will
	and	ax,not 15
	sub	ax,di			; AX bytes left in the para
	add	di,ax			; bias the starting location
	sub	bx,ax			; that many less available
	 ja	HMAQueryFree20		; if non-zero, return it
HMAQueryFree10:
	xor	bx,bx			; BX = zero on failure
	mov	di,0FFFFh		; ES:DI -> FFFF:FFFF
HMAQueryFree20:
	pop	bp
	pop	si
	pop	dx
	pop	cx
	pop	ax
	pop	ds
	iret


	Public	AllocHMA

AllocHMA:
;--------
; On Entry:
;	CX = bytes to allocate
;	DX = offset of allocation
; On Exit:
;	CY set if no can do and ES:DI = FFFF:FFFF
; else
;	ES:DI -> memory allocated (para aligned)
;	CX preserved
;	DX = segment to fixup
;
	les	bx,cs:drdos_ptr
	mov	di,es:DRDOS_HIMEM_ROOT[bx]
	test	di,di			; have we a HIMEM chain ?
	 jz	AllocHMA20
	cmp	di,dx			;  low enough for us
	 ja	AllocHMA20
	mov	ax,0FFFFh		; relocate to magic segment	
	mov	es,ax			; lets examine high memory
	mov	ax,es:2[di]		; get size of himem entry
	mov	si,es:[di]		;  and get himem link
	mov	bx,di
	add	bx,15
	and	bx,not 15		; BX is now para aligned
	sub	bx,di			; BX is bytes left in para
	sub	ax,bx			; so we only have this much
	 jc	AllocHMA20		;  less than a para ?
	add	di,15			; para align the base, dropping
	and	di,not 15		;  non-aligned bit on floor
	cmp	ax,cx			; is himem entry big enough ?
	 jb	AllocHMA20		; no, allocate from 1st MByte
	 je	AllocHMA10		; just made it!
	sub	ax,cx			; how much is left
	cmp	ax,2*WORD		; is it to small to keep ?
	 jb	AllocHMA10		; no, discard the remainder
	mov	bx,di			; point to new entry
	add	bx,cx			;  this many byte up
	mov	es:[bx],si		; fill in link field
	mov	es:2[bx],ax		;  and length
	mov	si,bx			; make this new root
AllocHMA10:
	push	cx			; save length of CODE
	push	dx			;  and offset of CODE
	les	bx,cs:drdos_ptr
	mov	es:DRDOS_HIMEM_ROOT[bx],si
	pop	ax			; AX = offset of CODE
	mov	cl,4
	shr	ax,cl			; make it paras
	mov	dx,0ffffh
	mov	es,dx			; ES:DI -> destination of CODE
	mov	dx,di
	mov	cl,4
	shr	dx,cl			; DX = offset from FFFF in para's
	dec	dx			; DX = offset from 10000
	sub	dx,ax			; DX = fixup segment
	pop	cx			; CX = bytes to move
	clc				; made it!
	ret

AllocHMA20:
	mov	di,0FFFFh		; set ES:DI = FFFF:FFFF
	mov	es,di
	stc				; can't do it
	ret

	Public	SetupHMA

SetupHMA:
;--------
; We have a client for the high memory area at segment FFFF
; We should try and setup a high memory free chain
; XMS only supports allocation of the complete area, so try and grab
;  it all and do our own sub-allocations within it.
;
	push	es
	les	bx,cs:drdos_ptr
	cmp	es:DRDOS_HIMEM_ROOT[bx],0; do we already have a chain ?
	 jnz	SetupHMA10		;  if so skip XMS allocation
	mov	ax,4300h		; check for XMS installation
	int	2fh
	cmp	al,80h
	 jne	SetupHMA20
	mov	ax,4310h		; get address of XMS driver
	int	2fh
	mov	word ptr xms_driver,bx
	mov	word ptr xms_driver+2,es
	mov	ah,0			; version number check
	callf	xms_driver
	cmp	dx,1			; does HiMem exist ?
	 jne	SetupHMA20
	mov	ah,1			; allocate whole HiMem
	mov	dx,0ffffh
	callf	xms_driver
	cmp	ax,1			; did we succeed ?
	 jne	SetupHMA20
	mov	ah,3			; enable a20 gate
	callf	xms_driver
	cmp	ax,1			; did we succeed ?
	 jne	SetupHMA20
	les	bx,cs:drdos_ptr
	mov	es:DRDOS_HIMEM_ROOT[bx],COMMAND_BASE
	mov	ax,0FFFFh		; one entry of FFF0 bytes covers
	mov	es,ax			;  the complete HMA
	inc	ax
	mov	es:word ptr .COMMAND_BASE,ax
	mov	es:word ptr .COMMAND_BASE+2,-COMMAND_BASE
	mov	di,10h			; copy a dummy VDISK header
	mov	si,offset dummyVDISK
	mov	cx,10h
	rep	movsw			; copy up 0x20 bytes
	push	ds			; now fixup JMPF in hi-memory for CALL5
	mov	ds,ax			;  link for PC-NFS
	mov	si,4*30h		; DS:SI -> Int 30 vector
	lea	di,10h[si]		; ES:DI -> himem alias
	movsw ! movsw ! movsb		; copy the JMPF
	pop	ds
SetupHMA10:
	les	bx,cs:drdos_ptr		; private data area in ES:BX
	mov	dx,COMMAND_BASE
	cmp	dx,es:DRDOS_HIMEM_ROOT[bx]
	 jne	SetupHMA20		; should we be reserving space for OS?
	mov	cx,systemSize		; we should reserve this much
	call	ReserveHMA		;  for the OS in the HMA
	 jc	SetupHMA20
	mov	systemHMA,ax		; save for re-use
SetupHMA20:
	pop	es
	ret


	Public	alloc_instseg

alloc_instseg:
; allocate AX paragraphs for data that will have to be instanced during
; multitasking. if Vladivar kernel available ask that, or else just
; try for normal upper memory
	push	ax
	push	bx			; save registers
	push	cx
	mov	cx,F_Version		; is the multi-tasker loaded ?
	mov	ax,OS386_FUNC
	int	OS386_INT
	int	2Fh			; check for Vladivar
	test	cx,cx			; CX=0 if it's there
	pop	cx
	pop	bx
	pop	ax
	 jnz	alloc_hiseg		; no, allocate normally
	push	ax
	push	bx
	push	cx
	push	dx
	mov	dx,ax			; DX = paragraphs required
	mov	cx,F_RealAllocI		; ask nicely for memory
	mov	ax,OS386_FUNC
	int	OS386_INT
	pop	dx
	pop	cx
	pop	bx
	 jc	alloc_intseg10		; did we get any ?
	add	sp,WORD
	clc				; we've done it !!
	ret	

alloc_intseg10:
	pop	ax			; we didn't manage it...
;	jmp	alloc_hiseg

	Public	alloc_hiseg
alloc_hiseg:
; allocate AX paragraphs in high memory if possible, otherwise allocate
; it in conventional memory
	cmp	hidos,0			; do we want to relocate DOS ?
	 je	alloc_seg		;  no, allocate conventionally
	call	alloc_upper		; try to allocate some upper memory
	 jc	alloc_seg		;  can't, so allocate conventional
	ret				;  else return address of allocated mem

alloc_seg_with_padding:
; On Entry:
;	AX = para's required
;	DX = minimum acceptable offset
; On Exit:
;	AX = base para
;
; If gate A20 is enabled we can't use negative offset's for DOS/BIOS so
; we pad conventional memory to avoid this. Avoid seg=0 while here.
	push	cx
	push	dx
	add	dx,15+16		; DX is the offset we will be using
	mov	cl,4			;  so make sure base is high enough
	shr	dx,cl			; convert "offset" to a segment value
	cmp	dx,mem_current		;  make sure we don't generate a
	 jbe	alloc_seg_nopad		;  negative segement value as this
	mov	mem_current,dx		;  will crash if a20 enabled
alloc_seg_nopad:			; pad if necessary
	mov	dl,'M'			; allocate for DOS
	call	alloc_seg		; now we can allocate OK
	pop	dx
	pop	cx
	ret

	Public	alloc_seg
alloc_seg:
;---------
; On Entry:
;	AX = para's required
;	DL = subsegment type
; On Exit:
;	AX = base para
;
	push	ds
	push	cx
	mov	cx,ax			; remember how much was wanted
	inc	ax			; allow an extra para for a header
	add	ax,mem_current		; Return a pointer to AX paragraphs
	cmp	ax,mem_max		; of memory to the calling routine.
	 jae	alloc_s10
	xchg	ax,mem_current
	mov	ds,ax			; DS:0 -> header
	inc	ax			; AX:0 -> buffer
	mov	ds:DMD_ID,dl		; remember the type
	mov	ds:DMD_PSP,ax		; owner = itself
	mov	ds:DMD_LEN,cx		; size in para
	xor	cx,cx			; zero rest for cosmetic reasons
	mov	ds:word ptr DMD_NAME-3,cx
	mov	ds:word ptr DMD_NAME-2,cx
	mov	ds:word ptr DMD_NAME,'S'+256*'D'
	mov	ds:word ptr DMD_NAME+2,cx
	mov	ds:word ptr DMD_NAME+4,cx
	mov	ds:word ptr DMD_NAME+6,cx
	pop	cx
	pop	ds
	ret

alloc_s10:
	hlt				; ##jc##
	jmps	alloc_s10

alloc_upper:
;-----------
; On Entry:
;	AX = paragraphs required
; On Exit:
;	CY clear: 	AX = paragraphs address of allocated memory
;	CY set:		cannot allocate memory (All regs preserved)
;
	push	bx
	push	ax			; save para required
	cmp	himem_base,0		; we have already allocated some ?
	 je	alloc_upper10		; nothing to grow, allocate new block
	mov	bx,himem_size		; himem was this big
	add	bx,ax			; try and extend it
	push	es
	mov	es,himem_base		; point at existing himem
	mov	ah,MS_M_SETBLOCK	; and try and set to new size
	int	DOS_INT
	pop	es
	 jc	alloc_upper10		; can't grow, so allocate new block
	mov	ax,himem_base
	add	ax,himem_size		; return seg above old alloc
	pop	bx			; recover para required
	add	himem_size,bx		; add into himem size
	pop	bx
	clc				; success..
	ret				; return AX = seg

alloc_upper10:
	mov	ax,(MS_M_STRATEGY*256)+1; set allocation strategy
	mov	bl,41h			;  to best fit, high only
	int	DOS_INT
	pop bx ! push bx		; recover para required in BX
	mov	ah, MS_M_ALLOC		;  and try to allocate them
	int	DOS_INT
	pushf ! push ax			; save CF and possible address
	mov	ax,(MS_M_STRATEGY*256)+1; set allocation strategy
	mov	bl,0			;  to first fit
	int	DOS_INT
	pop ax ! popf			; restore CF and possible address
	 jc	alloc_upper20		; can't allocate, use conventional
	cmp	ax,mem_size		; is it from upper memory ?
	 ja	alloc_upper15		; yes, we can use it
	push	es			; it's conventional, free it up
	mov	es,ax			;  seg address in ES
	mov	ah,MS_M_FREE
	int	DOS_INT			; free up this memory
	pop	es
	jmps	alloc_upper20		; try again with XMS

alloc_upper15:
	mov	himem_base,ax		; save base value
	pop	himem_size		; save size
	pop	bx			; and return seg in AX
	clc				; success..
	ret

alloc_upper20:
	pop	ax
	pop	bx			; restore regs
	push ds ! push es
	push bx ! push cx ! push dx ! push si ! push di ! push bp
	push	ax			; save allocation size
	mov	ax,4300h		; check for XMS installation
	int	2fh
	cmp	al,80h
	 jne	alloc_upper30
	mov	ax,4310h		; get address of XMS driver
	int	2fh
	mov	word ptr xms_driver,bx
	mov	word ptr xms_driver+2,es
	pop dx ! push dx		; DX = allocation size
	mov	ah,10h			; allocate upper memory block
	callf	xms_driver
	cmp	ax,1			; did we succeed ?
	 jne	alloc_upper30
	pop	ax			; recover allocation size
	mov	ax,bx			; return para address of himem
	pop bp ! pop di ! pop si ! pop dx ! pop cx ! pop bx
	pop es ! pop ds
	clc				; success
	ret
	
alloc_upper30:
	pop	ax			; recover allocation size
	pop bp ! pop di ! pop si ! pop dx ! pop cx ! pop bx
	pop es ! pop ds
	stc				; failure....
	ret


mark_system_memory:
;------------------
; ensure any memory we have allocated is owned by PSP 0008, a magic value
;  used to indicate system memory
	push	es
	les	bx,func52_ptr		; get internal data in ES:BX
	mov	es,es:F52_DMDROOT[bx]	; get 1st DMD entry
	mov	ah,MS_P_GETPSP
	int	DOS_INT			; get our PSP in BX
mark_sm10:
	cmp	es:DMD_ID,'M'
	 je	mark_sm20		; check we have a valid DMD
	cmp	es:DMD_ID,'Z'
	 jne	mark_sm50		; stop if we don't
mark_sm20:
	cmp	bx,es:DMD_PSP		; is it ours ??
	 jne	mark_sm30
	mov	es:DMD_PSP,0008		; mark as system
mark_sm30:
	cmp	es:DMD_PSP,0008		; if system mark as SC
	 jne	mark_sm40
	xor	ax,ax			; zero rest for cosmetic reasons
	mov	ds:word ptr DMD_NAME-3,ax
	mov	ds:word ptr DMD_NAME-2,ax
	mov	ds:word ptr DMD_NAME,'S'+256*'C'
	mov	ds:word ptr DMD_NAME+2,ax
	mov	ds:word ptr DMD_NAME+4,ax
	mov	ds:word ptr DMD_NAME+6,ax
mark_sm40:
	cmp	es:DMD_ID,'Z'		; is it the last DMD ?
	 je	mark_sm50		;  then stop
	mov	ax,es
	inc	ax			; skip DMD header and add
	add	ax,DMD_LEN		;  length to find next DMD
	mov	es,ax
	jmps	mark_sm10		; now go and look at that
mark_sm50:
	pop	es
	ret

; Relocate the BIOS code from top of memory
reloc_bios:
	mov	dx,rcode_offset
	mov	cx,rcode_len		; we need to relocate this much
	test	cx,cx			; do we need to move anything ?
	 jnz	reloc_bios10
	ret
reloc_bios10:
	add	cx,15			; round rcode size up to a para
	and	cx,not 15
	mov	ax,bios_target_seg	; where do we go
	test	ax,ax
	 jz	reloc_bios20		; zero - do it ourselves
	inc	ax			; FFFF - unlikely as it's not
	 jz	reloc_bios25		;  currently supported
	dec	ax			; else we've been given a seg
	jmps	reloc_bios40
reloc_bios20:	
	cmp	dos_target_seg,0FFFFh	; if DOS goes up, so does BIOS
	 jne	reloc_bios30
reloc_bios25:
	call	AllocHMA		;  in HIGH memory
	 jnc	reloc_bios50
reloc_bios30:
	mov	ax,cx			; allocate conventionally
	shr ax,1 ! shr ax,1		;  in para's of course
	shr ax,1 ! shr ax,1
	cmp	hidos,0			; do we want to relocate DOS ?
	 je	reloc_bios35		;  no, allocate conventionally
	call	alloc_upper		; try to allocate some upper memory
	 jnc	reloc_bios40		;  can't, so allocate conventional
reloc_bios35:				;  padding out if required
	call	alloc_seg_with_padding
reloc_bios40:
	mov	es,ax
	xor	di,di			; ES:DI -> destination
	shr dx,1 ! shr dx,1		; convert offset to para's
	shr dx,1 ! shr dx,1
	sub	ax,dx			; bias segment appropriately
	xchg	ax,dx			;  and have in DX
reloc_bios50:
	push	es
	push	cx
	push	di

	push	ds
	mov	si,rcode_offset
	mov	ds,rcode_seg
	rep	movsb
	pop	ds
	mov	rcode_seg,dx		; new RCODE location

	call	dd_fixup		; fixup any device drivers

	pop	di
	pop	cx
	pop	es
	ret


;
; The following code performs the fixups necessary for RELOCATABLE executable
; internal device drivers.

dd_fixup:
; On Entry:
;	None
; On Exit:
;	None

	push	es
	mov	di,rcode_seg		; fixup to this segment
	mov	si,rcode_fixups		; get fixup table
	test	si,si			; is there one ?
	 jz	dd_fixup20
	mov	es,bios_seg
dd_fixup10:
	lodsw				; get a fixup offset
	test	ax,ax			; last of the fixups ?
	 jz	dd_fixup20
	xchg	ax,di			; point to the fixup
	stosw				; do the fixup
	xchg	ax,di			; save segment again
	jmps	dd_fixup10
dd_fixup20:
	pop	es
	ret


ReserveOSHMA:
;------------
; reserve space in HMA for OS
; On Entry:
;	None
; On Exit:
;	None
;

ReserveCommandHMA:
;----------------
; reserve space in HMA for COMMAND.COM
; On Entry:
;	None
; On Exit:
;	None
;
	cmp	commandHMA,0		; been here already ??
	 jne	ReserveCommandHMA10
	mov	cx,COMMAND_SIZE
	mov	dx,COMMAND_BASE
	call	ReserveHMA		; reserve the space in HMA
	 jc	ReserveCommandHMA10	;  if we can
	mov	commandHMA,ax		; save for re-use
ReserveCommandHMA10:
	ret

ReserveHMA:
;----------
; reserve some space in the HMA
; On Entry:
;	CX = size require
;	DX = maximum offset acceptable
; On Exit:
;	AX = offset of reserved space
;
	push	es
	call	AllocHMA		; allocate space in HIGH memory
	 jc	ReserveHMA10
	mov	es:word ptr [di],0	; no link, it's this big
	mov	es:word ptr 2[di],cx
	mov	bx,es
	mov	ax,0FFFFh
	sub	ax,bx			; AX = para offset adjustment required
	mov	cl,4
	shl	ax,cl			; convert to byte offset
	add	ax,di			; AX = offset from FFFF:0
ReserveHMA10:
	pop	es
	ret


FreeHMA:
;-------
; Return reserved HMA space to pool
; On Entry:
;	CX = offset of HMA block to relink (0 = noblock)
; On Exit:
;	None
;
	 jcxz	free_himem10		; no block, don't recycle
	push	es
	push	cx			; save offset
	les	bx,cs:drdos_ptr
	pop	ax			; recover offset
	mov	di,ax			; remember offset for later
	xchg	ax,es:DRDOS_HIMEM_ROOT[bx]; become new head of HMA
	mov	bx,0FFFFh
	mov	es,bx			; point ES:DI at our section
	stosw				;  chain on rest of HMA
	pop	es
free_himem10:
	ret


rploader:
;--------
; On Entry:
;	DX = phase code
; On Exit:
;	None, All regs preserved
	push	es
	push	ax
	push	bx
	push	cx
	push	dx
	mov	ax,rpl_off		; do we have an RPL sitting on
	or	ax,rpl_seg		;  Int 13h
	 jz	rploader10
	mov	ax,12ffh		; magic cleanup call to RPL
	mov	bx,5			;  to do any tidy ups it wishes
	xor	cx,cx			;  following resident BIOS
	mov	dx,1			;  initialisation
	pushf
	cli
	callf	rpl_entry		; fake an INT
rploader10:
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	pop	es
	ret

	Public	Verify386

Verify386:
;---------
; On Entry:
;	None
; On Exit:
;	CY clear if 386 or above
;
	push	sp			; really old CPU's inc SP
	pop	ax			;  before pushing
	cmp	ax,sp			; newer ones push original SP
	 jne	Verify386fail
	mov	ax,3000h		; now try to set IOPL = 3
    push    ax
	popf
	pushf
	pop	bx
	and	ax,bx			; any IOPL bits set ?
	 jz	Verify386fail
;	clc				; it's at least a 386
	ret
Verify386fail:
	stc				; it's not a 386
	ret


INITDATA	dseg	'INITDATA'


include	initmsgs.def				; Include TFT Header File


	extrn	history_flg:byte
	extrn	next_drv:byte
	extrn	dev_count:byte

;
;	PUBLIC Variables which are initialised by the BIOS before the
;	BIOSINIT code has been executed. 
;
data_start	rb	0		; used to para-align PSP & ENV

	Public	func52_ptr
func52_ptr	rd	0		; address of internal BDOS variables
func52_off	dw	0		; offset	"	"	"
func52_seg	dw	0		; segment	"	"	"

	Public	drdos_ptr
drdos_ptr	rd	0		; address of internal BDOS variables
drdos_off	dw	0		; offset	"	"	"
drdos_seg	dw	0		; segment	"	"	"


	Public	res_ddsc_ptr
res_ddsc_ptr	rd	0
res_ddsc_off	dw	0
res_ddsc_seg	dw	0

	Public	rcode_offset, rcode_seg, icode_len, rcode_len, rcode_fixups

rcode_offset	dw	0		; current offset of relocated code
rcode_seg	dw	0		; current segment of relocated code
icode_len	dw	0		; initial size of relocated code
rcode_len	dw	0		; final size of relocated code
rcode_fixups	dw	0		; offset of rcode fixup table

	Public	current_dos
current_dos	dw	0		; Current Segment Address of DOS Code

	Public	dos_target_seg, bios_target_seg
dos_target_seg	dw	0		; target address for DOS relocation
bios_target_seg	dw	0		; 0000 - auto-relocate
					; FFFF - high memory (not allocated)
					; xxxx - driver allocated address

dosCodeParaSize	dw	0		; Size of DOS code in para's

systemSize	dw	COMMAND_SIZE	; BIOS+DOS code sizes are added to
					;  give total size to reserve in HMA

systemHMA	dw	0		; offset of area in HMA reserved 
					;  for SYSTEM (BIOS/DOS/COMMAND)
commandHMA	dw	0		; offset of area in HMA reserved 
					;  for COMMAND.COM


	Public	device_root
device_root	rd	1		; Root of Resident Device driver Chain

	Public	mem_size, ext_mem_size, comspec_drv
	Public	init_flags, init_drv

mem_size	dw	0		; Total Memory Size (in Paragraphs)
ext_mem_size	dw	0		; Total Extended Memory Size (in KB.)
init_flags	dw	0		; BIOS INIT Flags
init_drv	db	0		; Boot Drive (A is 0 .....)
comspec_drv	db	0		; Default COMSPEC Drive

	Public	num_stacks, stack_size

num_stacks	dw	DEF_NUM_STACKS
stack_size	dw	DEF_SIZE_STACK

	Public	num_files, num_fcbs, num_fopen
	Public	country_code, code_page
	
num_files	dw	DEF_NUM_FILES	; # of file handles
num_fcbs	dw	DEF_NUM_FCBS	; # of fcb file handles
num_fopen	dw	-1		; "unset" value for fast open
country_code	dw	DEF_COUNTRY	; Country Code 
code_page	dw	DEF_CODEPAGE	; Code Page

	Public	dos_name
IF DRDOS35
dos_name	db	'DRBDOS  SYS',0 
ELSE
dos_name	db	'IBMDOS  COM',0	; default DOS filename
ENDIF

rpl_name	db	'RPLOADER'

rpl_entry	rd	0		; remember RPL entry point for
rpl_off		dw	0		;  startup broadcasts
rpl_seg		dw	0

eject
;
;	Internal variables used by the BIOSINIT code
;
	Public	bios_seg
bios		rd	0		; Far pointer to the BIOS Cleanup
bios_offset	rw	1		; routines.
bios_seg	rw	1

	Public	init_dseg
init_dseg	dw	0		; Init data segment

	Public	dos_dseg
dos_dseg	dw	0		; DOS Data Segment Address

	Public	mem_current_base, mem_current, mem_max
mem_first_base	rw	1		; Base of First Allocated Memory
mem_current_base rw	1		; Base of Current Allocated Memory
mem_current	rw	1		; Next Free Paragraph
mem_max		rw	1		; Last available Paragraph


dos_init	rd	0		; DOS Initialization Code
dos_coff	dw	0		; DOS Init Code Offset
dos_cseg	rw	1		; DOS Init Code Segment

free_seg	rw	1		; First available paragraph.

xms_driver	rd	1		; address of himem driver

	Public	hidos
hidos		db	0		; set true if HIDOS requested
himem_base	dw	0		; base of HIMEM seg allocations
himem_size	dw	0		; length of HIMEM seg allocations

	Public	last_drv

last_drv	db	5		; default is "E:"

console		db	'CON',0		; Default Console Device
printer		db	'PRN',0		; Default Printer Device
auxilary	db	'AUX',0		; Default Auxilary Device

idle_dev	db	'$IDLE$',0	; Idle Device Name
idle_off	rw	1		; Idle Data Area Offset
idle_seg	rw	1		; Idle Data Area Segment

dummy_fcb	db	0,'           '

exec_env	dw	0		; Environment Segment
exec_cloff	dw	shell_cline	; Command Line Offset
exec_clseg	dw	0		; Command Line Segment
		dw	dummy_fcb
exec_fcb1seg	dw	0		; FCB 1 Offset and Segment
		dw	dummy_fcb
exec_fcb2seg	dw	0		; FCB 2 Offset and Segment
		rd	1		; SS:SP
		rd	1		; CS:IP

	Public	shell_cline
shell_cline	db	length shell_drv; Initial Command Line
shell_drv	db	'A: /P'
		db	0Dh
		rb	126 - length shell_drv

dummyVDISK	db	0, 0, 0		; jump instruction
		db	'VDISK3.3'	; OEM name
		dw	128		; bytes per sector
		db	1		; sectors per allocation unit
		dw	1		; number of reserved sectors
		db	1		; number of FATs
		dw	40		; number of root directory entries
		dw	512		; total number of sectors
		db	0FEh		; media descriptor byte
		dw	6		; sectors per FAT
		dw	8		; sectors per track
		dw	1		; number of heads
		dw	0		; number of hidden sectors
		dw	1024+64		; KB of extended memory used

search_state	rb	43		; Search First/Next State

		rw	384		; big stack for ASPI4DOS.SYS driver
stack		rw	0


INITPSP		DSEG	PARA 'INITDATA'
		db	'Z'			; dummy DMD header
		dw	0008h			; owner is system
		dw	0010h			; length of PSP
		rb	3			; pad to 8 bytes
		db	'DOS',0,0,0,0,0		; name field (must be 8 bytes)
psp		rb	16h			; Zero Fill PSP Header
parent_psp	dw	0			; parent, patched to itself
		db	0FFh, 0FFh, 0FFh	; STDIN, STDOUT, STDERR
		db	0FFh, 0FFh		; STDAUX, STDPRN
		db	0FFh, 0FFh, 0FFh	; Remainder CLOSED
		db	0FFh, 0FFh, 0FFh
		db	0FFh, 0FFh, 0FFh
		db	0FFh, 0FFh, 0FFh
		db	0FFh, 0FFh, 0FFh
		dw	0000			  ; PSP Environment Pointer
		dw	0000, 0000		  ; DOS User SS:SP
		dw	20			  ; Maximum of 20 Handles
		dw	offset PSP_XFT		  ; Handle Table Offset
xftbl_seg	dw	0			  ; Handle Table Segment
		rb	offset PSP_VERSION - offset PSP_RES1
	Public	dosVersion
dosVersion	dw	7			  ; DOS version is 7.0
		rb	PSPILEN - offset PSP_VERSION - 2
						  ; PAD to Partial PSP Size

INITENV		DSEG	PARA 'INITDATA'

shell_ask	db	79			; max len
shell_end	db	0			; end of the line
	Public	shell
shell		db	'A:\COMMAND.COM', 0	
		rb	80-length shell

DATAEND	DSEG	PARA 'INITDATA'

	Public	biosinit_end
biosinit_end	rb	0

	end
