www.pudn.com > drdossrc.zip > BIOSINIT.A86


;    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