
include kernel.inc
include protect.inc
.list

.386p

DataBegin

externB	WinFlags
externW hGlobalHeap
externW pGlobalHeap
externW ArenaSel
externD hBmDPMI
externD FreeArenaList
externD FreeArenaCount
externD SelTableStart
externW SelTableLen
externW InitialPages

DataEnd


sBegin	INITCODE
assumes CS,CODE

externNP get_physical_address
externNP set_physical_address
externNP set_selector_address32
externNP set_selector_limit32
externNP alloc_arena_header
externNP AssociateSelector32
externNP alloc_data_sel
externNP alloc_data_sel32
externNP GrowHeap
externNP DPMIProc
	 
GA_ALIGN_BYTES = (((GA_ALIGN+1) SHL 4) - 1)
GA_MASK_BYTES = NOT GA_ALIGN_BYTES

;-----------------------------------------------------------------------;
; get_selector_address32						;
;									;
; Function to translate return of get_physical_address (DX:AX) into EAX ;
;									;
;-----------------------------------------------------------------------;
cProc	get_selector_address32,<PUBLIC,NEAR>
	parmW	selector
cBegin
	push	dx
	cCall	get_physical_address,<selector>
	shl	eax, 16
	mov	ax, dx
	ror	eax, 16
	pop	dx
cEnd

;-----------------------------------------------------------------------;
; GlobalInit								;
;									;
; Procedure to initialize the global heap.  Called with the starting	;
; and ending paragraph addresses.					;
;									;
; Arguments:								;
;	parmW	hdelta	= size (.25k) of master object			;
;	parmW	palloc	= block to mark allocated			;
;			= (ROM) Size of DS == allocated block		;
;	parmW	pstart	= first available address			;
;	parmW	pend	= last available address			;
;									;
; Returns:								;
;	AX = handle for allocated block					;
;									;
; Error Returns:							;
;									;
; Alters:								;
;	ES								;
; Calls:								;
;	ginit								;
;	hthread 							;
;	ghalloc								;
; History:								;
;									;
;  Sat Jun 20, 1987 05:55:35p  -by-  David N. Weise     [davidw]	;
; Making real EMS work with the fast boot version.			;
; 									;
;  Sun Mar 15, 1987 06:38:04p  -by-  David N. Weise	[davidw]	;
; Added support for the linked free list long ago.			;
; 									;
;  Wed Feb 18, 1987 08:09:18p  -by-  David N. Weise	[davidw]	;
; Added the initialization of the gi_alt entries.			;
; 									;
;  Mon Sep 08, 1986 07:53:41p  -by-  David N. Weise	[davidw		;
; Changed the return values so that this can be called from other than	;
; initialization code.							;
;-----------------------------------------------------------------------;

cProc	GlobalInit,<PUBLIC,NEAR>,<ds,si,di>
	parmW   hdelta
	parmW   palloc
	parmW   pstart
	parmW   pend
	localW	emspalloc
	localW	hInitMem
	localD	allocated_arena
	localW	next_block
	localW	free_hi
	localW	free_lo

cBegin
	mov	ax,palloc		; AX = block to mark allocated
	mov	bx,pstart		; BX = first available address
	mov	cx,hdelta		; CX = size (.25k) of master object
	mov	dx,pend			; DX = last available address
	call	ginit
	jc	FailGlobalInit

	SetKernelDS	es
	mov	allocated_arena,eax
	xor	edi,edi			; Initialize master object
	mov	[di].phi_first,ebx	; Fill in pointers to first and
	mov	[di].phi_last,edx	; last blocks
	mov	[di].hi_count,4		; 4 arena entries
	or	eax,eax	       	  
	jnz	short allocated_block
	mov	[di].hi_count,3		; 3 arena entries
allocated_block:	       	  
	mov	[di].gi_lruchain,edi	; Nothing in LRU list so far
	mov	[di].gi_lrucount,di
	mov	[di].gi_lrulock,di	; ...and not locked
	mov	[di].gi_reserve,edi	; No discardable code reserve area yet
	push	eax
	mov	eax, ds:[edx.pga_address]
	mov	word ptr [di].gi_disfence,ax	; gi_disfence = hi_last
	shr	eax, 16
	mov	word ptr [di].gi_disfence_hi,ax
	pop	eax

	mov	[di].gi_alt_first,-1	; Fill in pointers to first and
	mov	[di].gi_alt_last,-1	;  last blocks, the -1 is necessary!!
	mov	[di].gi_alt_count,di	; # of arena entries
	mov	[di].gi_alt_lruchain,di
	mov	[di].gi_alt_lrucount,di	; MUST be 0!
	mov	[di].gi_alt_free_count,di
	mov	[di].gi_alt_reserve,di
	mov	[di].gi_alt_disfence,di
	mov	[di].gi_alt_pPhantom,-1	; MUST be -1!

	mov	ax, ds:[esi.pga_handle] ; Pick up master objects selector
	mov	hGlobalHeap,ax
	mov	ds:[esi].pga_count,0
	mov	ds:[esi].pga_selcount,1

if ROM
	mov	esi,ds:[ebx].pga_next	; in ROM, alloced == first.next
else
	mov	esi,ds:[edx].pga_prev	; Point to allocated object before
endif
	mov	bx,ds:[esi.pga_handle]
	StoH	bx			; It is moveable
	mov	ds:[esi].pga_handle,bx
	mov	hInitMem,bx
if ROM
	mov	ds:[esi].pga_count,1	; LOCKED in ROM
else
	mov	ds:[esi].pga_count,0
endif
no_allocated_object:

; initialize free list

	mov	[di].gi_free_count,1
	mov	eax,[di].phi_first
	mov	ecx,[di].phi_last

if ROM
	mov	ebx,ds:[ecx].pga_prev
else
	mov	ebx,ds:[eax].pga_next
endif
	mov	ds:[eax].pga_freeprev,-1		; Fill in first sentinal
	mov	ds:[eax].pga_freenext,ebx

	mov	ds:[ecx].pga_freeprev,ebx		; Fill in last sentinal
	mov	ds:[ecx].pga_freenext,-1

	mov	ds:[ebx].pga_freeprev,eax		; Link in free block
	mov	ds:[ebx].pga_freenext,ecx
	pushad
	SetKernelDS fs
	mov	edx, 060000h			; Enough to boot quickly in
	call	GrowHeap			  
	jnc	short @F
	mov	edx, 020000h			; Insist on enough for fence
	call	GrowHeap
@@:
	UnSetKernelDS fs
	popad	     
	jc	short FailGlobalInit
	mov	ax,hInitMem
	clc
FailGlobalInit:
cEnd


;-----------------------------------------------------------------------;
; ginit									;
;									;
; Procedure to initialize the global heap.				;
;									;
;   The global heap looks as follows after this procedure returns:	;
;									;
;	BX - first object in arena, alway busy, zero length.		;
;	   - free object						;
;	AX - allocated object						;
;	DS - master object						;
;	DX - last object in arena, alway busy, zero length.		;
;									;
;									;
; Arguments:								;
;	AX = address of block to mark allocated.  May be zero.		;
;	   = (ROM) length of allocated block (which is DS)		;
;	BX = address of first paragraph in arena			;
;	DX = address of last  paragraph in arena			;
;	CX = initial size of master object, in bytes			;
;									;
; Returns:								;
;	AX = aligned address of block marked allocated.			;
;	BX = aligned address of first object in arena			;
;	CX = size of master object, in bytes				;
;	DX = aligned address of last object in arena			;
;	DS = aligned address of master object				;
;									;
; Registers Preserved:							;
;									;
; Registers Destroyed:							;
;	DI,SI,ES							;
; Calls:								;
;	nothing								;
; History:								;
;									;
;  Thu Sep 11, 1986 04:22:02p  -by-  David N. Weise	[davidw]	;
; Commented it, made it handle the case of no allocated block correctly.;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc	ginit,<PUBLIC,NEAR>

	localD	size_free
	localD	size_allocated
	localD	size_master
	localD	allocated_arena
	localW	allocated_sel
	localD	BurgerMaster_arena
	localW	BurgerMaster_sel
	localD	new_last_arena

cBegin

; ESI = the first block address (sentinel, always busy)

	SetKernelDS	es
	push	ax

	cCall	get_selector_address32,<bx>	; Start of our memory
	add	eax, GA_ALIGN_BYTES		; Align our start
	and	al, GA_MASK_BYTES
	push	eax				; Save the start address
	cCall	set_selector_address32,<bx,eax>	; This will be our arena list
	mov	ArenaSel, bx
	mov	BurgerMaster_sel, bx
	mov	si, bx

	push	bx
	push	di
	xor	bx, bx				; # physical pages available
	sub	sp, 30h
	mov	di, sp
	push	es
	smov	es, ss
	DPMICALL 0500h
	pop	es
	mov	eax, 16*1024			; Size of selector table
	jc	short default_sel_table
	mov	bx, word ptr ss:[di][10h]
	cmp	bx, 256	     
	jb	short default_sel_table
     ;;;mov	eax, 31*1024
	mov	eax, 32*1024
default_sel_table:
	mov	[InitialPages], bx
; For WOW we assume 386 Kernel is running on paging System
; So always set WF1_PAGING
ifndef WOW
	mov	ecx, ss:[di][20h]		; Paging file size
	inc	ecx
	cmp	ecx, 1				; 0 or -1?
	jbe	short @F
endif
	or	byte ptr WinFlags[1], WF1_PAGING
@@:
	mov	ecx, ss:[di][0]
	shr	ecx, 7				; Bytes reserved for arenas
	add	sp, 30h
	pop	di
	pop	bx

	add	ecx, GA_ALIGN_BYTES		; Align length of arena table
	and	cl, GA_MASK_BYTES				  
	add	eax, GA_ALIGN_BYTES		; Align length of selector table
	and	al, GA_MASK_BYTES
	mov	SelTableLen, ax
	cmp	ecx, DEFAULT_ARENA_SIZE		; Number of bytes of arenas
	jae	short @F
	mov	ecx, DEFAULT_ARENA_SIZE
@@:	 
	mov	ebx, Size GlobalInfo+GA_ALIGN_BYTES
	and	bl, GA_MASK_BYTES		; EBX start of arenas
	mov	size_master, ebx
	add	ecx, ebx
	mov	SelTableStart, ecx
	add	eax, ecx			; EAX total length of segment

	push	ecx
	push	ebx
	push	eax
	push	si
	mov	cx, ax				; Make BX:CX required
	mov	ebx, eax			; length of Burgermaster
	shr	ebx, 16
	push	bx				; Save length
	push	cx
	DPMICALL 0501h
	mov	hBmDPMI[0], di			; Save DPMI handle
	mov	hBmDPMI[2], si
	pop	di				; SI:DI now length
	pop	si
	jc	Failginit
if 0
	mov	di, 4096			; Lock first page
	xor	si, si
	DPMICALL 0600h			     ; Now page lock it
	push	bx
	push	cx
	add	cx, word ptr SelTableStart 
	adc	bx, 0
	and	cx, not 4095
	DPMICALL 0600h
	pop	cx
	pop	bx
endif

	shl	ebx, 16
	mov	bx, cx				; Address in EBX
	pop	si
	pop	eax
	cCall	set_selector_address32,<si,ebx>
	mov	edi, ebx
;;;	cCall	set_selector_limit32, <si,eax>
	push	eax
	push	dx
	dec	eax
	mov	ecx, eax			; Limit in CX:DX
	shr	ecx, 16
	mov	dx, ax
	test    cx,0fff0h 	                ; bits 20-31 set?
	jz	@F				; No.
	or	dx,0fffh			; Yes, page align limit.
@@:
	mov	bx, si				; Selector in BX
	DPMICALL 8
	pop	dx
	pop	eax
	pop	ebx	
	pop	ecx
	jc	Failginit

	mov	ds, si
	sub	ecx, ebx			; Subtract out BurgerMaster
	cCall	InitialiseArenas,<ebx,ecx>
	mov	ecx, eax
	pop	esi				; Sentinel address
	cCall	alloc_arena_header,<esi>	; Sentinel
	mov	esi, eax
	cCall	alloc_arena_header,<edi>	; BurgerMaster
	mov	BurgerMaster_arena, eax
	mov	ds:[eax].pga_handle, ds		; Save selector in handle field
	mov	ds:[eax].pga_size, ecx

	push	edi
	push	ecx
	movzx	ecx, SelTableLen
	mov	edi, SelTableStart
	push	es
	smov	es, ds				; Zero this area
	UnSetKernelDS	es
	shr	ecx, 2
	xor	eax, eax
	rep	stos	dword ptr es:[edi]
	pop	es
	ReSetKernelDS	es
	pop	ecx
	pop	edi
	cCall	AssociateSelector32,<ds,BurgerMaster_Arena>	; Set back link

; EDI = the Free block address (initial free block)

ife ROM

	mov	eax, ds:[esi].pga_address
;;;	add	eax, ecx
	cCall	alloc_arena_header,<eax>
	mov	edi, eax			; Free block arena in EDI

	movzx	eax, dx				; Get linear address
	shl	eax, 4
else
	cCall	get_selector_address32, <dx>
endif

; EDX = the last block address (sentinel, always busy)

	and	al, GA_MASK_BYTES
	push	eax
	cCall	alloc_arena_header,<eax>
	mov	new_last_arena, eax	; Save it away
	pop	ebx

if ROM
	; in ROM, the alloced segment is the data segment, and we are
	; passed the length, not the address
	;
	; the length (a word) is on the stack from that very first push way
	; up there, and we want the base in eax and the length in ebx
	;
	CheckKernelDS ES
	cCall	get_selector_address32, <es>
	and	al, GA_MASK_BYTES
	xor	ebx, ebx		; clear hi word of length
	pop	bx
	add	ebx, GA_ALIGN_BYTES
	and	bl, GA_MASK_BYTES

	cCall	set_selector_limit32, <es, ebx>
else
	pop	ax			; Allocated Block
	cCall	get_selector_address32, <ax>
	and	al, GA_MASK_BYTES	; "Align" it

	sub	ebx, eax		; Length in bytes
endif

ife ROM
	push	eax
endif
	push	eax
	cCall	alloc_arena_header, <eax>
	mov	ds:[eax].pga_size, ebx	; Record size
	mov	allocated_arena, eax
	mov	ebx, eax
	pop	eax
if ROM
	CheckKernelDS	ES
	mov	allocated_sel, es
	mov	ds:[ebx.pga_handle], es
	cCall	AssociateSelector32,<es, ebx>
else
	cCall	alloc_data_sel32, <eax, ds:[ebx].pga_size>
	mov	allocated_sel, ax
	mov	ds:[ebx.pga_handle], ax	; Save selector
	cCall	AssociateSelector32,<ax,ebx>	; Set back link
endif

if ROM
	; now deal with the free sucker
	mov	eax, ds:[ebx].pga_address
	add	eax, ds:[ebx].pga_size
	add	eax, GA_ALIGN_BYTES
	and	al, GA_MASK_BYTES
	cCall	alloc_arena_header, <eax>
	mov	edi, eax

	cCall	get_selector_address32, <dx>
	mov	ebx, eax
else
	pop	ebx
endif
	sub	ebx, ds:[edi].pga_address	; Length in bytes
	mov	ds:[edi].pga_size, ebx

	mov	eax, allocated_arena
	mov	ecx, BurgerMaster_arena
	mov	edx, new_last_arena

; Fill in first block

	xor	ebx, ebx
	mov	ds:[esi].pga_sig,GA_SIGNATURE
	mov	ds:[esi].pga_size,ebx
	mov	ds:[esi].pga_owner,-1
	mov	ds:[esi].pga_flags,bx
	mov	ds:[esi].pga_prev,esi	; first.prev = self
if ROM
	mov	ds:[esi].pga_next,eax	; first.next = alloced (ds)
else
	mov	ds:[esi].pga_next,edi	; first.next = free
endif
	mov	ds:[esi].pga_handle,bx
	mov	ds:[esi].pga_lrunext,ebx
	mov	ds:[esi].pga_lruprev,ebx

; Fill in the last block (sentinel block)

	mov	ds:[edx].pga_sig,GA_ENDSIG
	mov	ds:[edx].pga_size,ebx
	mov	ds:[edx].pga_owner,-1	; Always allocated
	mov	ds:[edx].pga_next,edx	; last.next = self
if ROM
	mov	ds:[edx].pga_prev,edi	; last.prev = free
else
	mov	ds:[edx].pga_prev,eax	; last.prev = alloced
endif
	mov	ds:[edx].pga_flags,bx
	mov	ds:[edx].pga_handle, bx
	mov	ds:[edx].pga_lrunext,ebx
	mov	ds:[edx].pga_lruprev,ebx

; Fill in the master object

	mov	ds:[ecx].pga_next,ecx
	mov	ds:[ecx].pga_prev,ecx
	mov	ds:[ecx].pga_sig,GA_SIGNATURE
	mov	ds:[ecx].pga_owner,-3
	mov	ds:[ecx].pga_flags,bx
	mov	ds:[ecx].pga_lruprev,ebx
	mov	ds:[ecx].pga_lrunext,ebx

; Fill in the allocated block

if ROM
	mov	ds:[eax].pga_next,edi	; next = free
	mov	ds:[eax].pga_prev,esi	; prev = first
else
	mov	ds:[eax].pga_next,edx	; next object is Sentinel
	mov	ds:[eax].pga_prev,edi	; Previous object is second block
endif
	mov	ds:[eax].pga_sig,GA_SIGNATURE
	mov	ds:[eax].pga_owner,-1
	mov	ds:[eax].pga_flags,bx
	mov	ds:[eax].pga_lruprev,ebx
	mov	ds:[eax].pga_lrunext,ebx

; Fill in free block

	mov	ds:[edi].pga_sig,GA_SIGNATURE
	mov	ds:[edi].pga_owner,bx	; This is a free block
	mov	ds:[edi].pga_flags,bx
if ROM
	mov	ds:[edi].pga_next,edx	; next = last
	mov	ds:[edi].pga_prev,eax	; prev = alloced
else
	mov	ds:[edi].pga_next,eax	; Next obj allocated block
	mov	ds:[edi].pga_prev,esi
endif
	mov	ds:[edi].pga_handle,bx
	mov	ds:[edi].pga_lruprev,ebx
	mov	ds:[edi].pga_lrunext,ebx

; Initialize master object


	mov	ecx, size_master
	mov	dx,BurgerMaster_sel
	shr	ecx, 1

	push	ecx			; save size in words

	mov	pGlobalHeap,dx
	mov	es,dx
	UnSetKernelDS	es
	xor	eax,eax
	xor	di,di			; Init master object to zero
	shr	ecx, 1
	rep	stosd

	mov	ds,dx			; Switch to master object as our DS

	pop	ecx
	shl	ecx,1			; ECX = size of master object in bytes

	mov	eax, allocated_arena	; EAX = address of allocated block
	mov	edx, new_last_arena	; EDX = address of last block
	mov	ebx, esi		; EBX = address of first block
	mov	esi, BurgerMaster_arena	; ESI = arena of BurgerMaster
	clc
Failginit:
cEnd

	assumes	ds, nothing
	assumes	es, nothing

cProc	InitialiseArenas,<PUBLIC,NEAR>
	parmD	StartOffset
	parmD	BytesAllocated
cBegin
	push	ecx
	push	esi
	push	edi
	CheckKernelDS	es
	ReSetKernelDS	es
	mov	esi, StartOffset
	mov	ecx, BytesAllocated
	add	ecx, esi
	mov	FreeArenaList, esi		; Point to list
	lea	edi, [esi+size GlobalArena32]
IA_loop:
	cmp	edi, ecx
	jae	short IA_done
	mov	[esi.pga_next], edi
	add	esi, size GlobalArena32
	add	edi, size GlobalArena32
	inc	FreeArenaCount
	jmps	IA_loop
IA_done:
	mov	ds:[esi.pga_next], -1		; Terminate list
	UnSetKernelDS	es
	pop	edi
	pop	esi
	pop	ecx
cEnd

sEnd	INITCODE

end
