;
; Block.aii
;
; Heap block allocation and deallocation routines
;
; Copyright © 1989 Claris Corporation
;
; 1/25/89 Begun by Kevin A. Watts
;

;--------------------------------------------------------------
; Routines:	all operate on the current heap
;
; H_NewBlock(Size:xy): Index:ax		; allocate a new block and index
; H_DisposeBlock(Index:xy)			; dispose of a block
; H_ResizeBlock(Index:l,DataSize:xy): Ptr:ax	; resize a block
; H_CopyBlock(OldIndex:xy): Index:ax, Ptr:l	; copy a block
; H_GetBlockPtr(Index:xy): Ptr:ax		; get pointer to block
; H_GetBlockSize(Index:xy): Size:ax		; get size of block
; H_TruncHeap()			; shrink the heap to its minimum size
;
; NewIndex()		; allocate a new index
; DisposeIndex()		; dispose of an index
; NewBlock()		; allocate a new block
; DisposeBlock()		; dispose of a block
; CompactMoveBlock()	; move a block in SqueezeHeap or CompactSegment
; SqueezeHeap(DisposeFlag:x)	; squeeze the heap into as few segments as possible
; CompactSegment()		; compact a data segment
; NewDataSegment()		; create and initialize a new data segment
; VerifyIndex()		; verify the index in Index

;--------------------------------------------------------------

	PRINT	PUSH
	PRINT	OFF

	LOAD	'Macros.Dump'

	gblc	&__FILE__
&__FILE__	setc	'Block'

	include	'Heap.mac'
	include	'HeapPrivate.equ'
	include	'm16.profile'
	include	'Driver.equ'

	ENTRY	H_NewBlock
	ENTRY	H_DisposeBlock
	ENTRY	H_ResizeBlock
	ENTRY	H_CopyBlock
	ENTRY	H_GetBlockPtr
	ENTRY	H_GetBlockSize
	ENTRY	H_TruncHeap

	ENTRY	NewIndex
	ENTRY	DisposeIndex
	ENTRY	NewBlock
	ENTRY	DisposeBlock
  IF Squeezing THEN
	ENTRY	SqueezeHeap
  ENDIF
	ENTRY	CompactSegment
	ENTRY	NewDataSegment
  IF ErrorCheck THEN
	ENTRY	VerifyIndex
  ENDIF

	PRINT	POP

;¦;--------------------------------------------------------------
; H_NewBlock(DataSize:xy): Index:ax, NewBlockPtr:l
;	error status returned in y, with x used to indicate
;	error in index (-1) or block (0) allocation
;
; Allocate a new block of the specified size in the current heap.
; NewBlockPtr and Index are invalid if any error occurs.

; Zpage variables modified:
;	DataSize
;	NewIndex()
;	NewBlock()
;	DisposeIndex()

H_NewBlock	PROC	EXPORT

	BeginZ

NewBlockPtr	equ	6
		; stack frame w/offsets:
		;	1: return address (3 bytes)
		;	4: saved direct page (2 bytes)
		;	6: space for returned pointer (4 bytes)

	MoveLong	xy,DataSize	; save the requested block size

	jsr	NewIndex
	bcs	noIndex

  IF UseLastResort THEN
	stz	SkipLastResort
  ENDIF
	jsr	NewBlock
	bcs	noBlock

	sta	NewBlockPtr,s
	txa
	sta	NewBlockPtr+2,s
	MoveLong	Index,ax
	;ldy	#0		- y = 0 after NewBlock without error
	ReturnZ

noIndex
  IF VerboseErrors THEN
	phy
 	Call	D_AlertBox,in=(#OkBox:w,#NoIndexMesg:l),out=(a:w)
	ply
	sec
	DEBRK3
  ENDIF
	ldx	#-1
	ReturnZ

noBlock
	phy
	jsr	DisposeIndex
  IF VerboseErrors THEN
 	Call	D_AlertBox,in=(#OkBox:w,#NoBlockMesg:l),out=(a:w)
	DEBRK3
  ENDIF
	ply
	ldx	#0
	sec
	ReturnZ

  IF VerboseErrors THEN
NoIndexMesg	str	'H_NewBlock: Unable to allocate index'
NoBlockMesg	str	'H_NewBlock: Unable to allocate block'
  ENDIF
	ENDP

;¦;--------------------------------------------------------------
; H_DisposeBlock(Index:xy)
;	no error status is returned
;
; Dispose of the specified block in the current heap.

; Zpage variables modified: 
;	Index
;	ISegPtr
;	DisposeBlock()
;	DisposeIndex()

H_DisposeBlock	PROC	EXPORT

	BeginZ

	MoveLong	xy,Index		; save the index

	tya
	beq	disposeZeroError

  IF Profile THEN
	ProfileIn 9,_H
	ldx	Index
  ENDIF
	; get ptr to index segment
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2
	lda	[H_ISegPtrLo],y
	sta	ISegPtr

	jsr	DisposeBlock
	jsr	DisposeIndex

	ProfileOut 9,_H
	
  IF (ErrorCheck = 0) THEN
disposeZeroError
  ENDIF
	ReturnZ

  IF ErrorCheck THEN
disposeZeroError
	SpaceWord
	PushWord	#OkBox
	PushLong	#dzMesg
	jsl	D_AlertBox
	pla
	DEBRK2
	ReturnZ

dzMesg	str	'H_DisposeBlock: Attempt to dispose of index $00xx!'
  ENDIF	; ErrorCheck

	ENDP

;¦;--------------------------------------------------------------
; H_ResizeBlock(Index:l,DataSize:xy): Ptr:ax
;	error status returned in y
;
; Resize the specified block in the current heap.
; Ptr is valid even if an error occurs
; This routine is a mess.

; Zpage variables modified: 
;	BlockSize
;	DataSize
;	DP		(also aliased to NextOffset)
;	ErrorFlag
;	Index
;	ISegPtr
;	Offset
;	OldIndex
;	Ptr
;	SegPtr
;	DisposeBlock()
;	NewBlock()
;	H_GetBlockPtr()

H_ResizeBlock	PROC	EXPORT

NextOffset	equ	DP

	BeginZ

	MoveLong	xy,DataSize

	ProfileIn 10,_H

; remove the input parameter from the stack and restore the return address
	PullWord	DP	; pull the old direct page value
	php		; push an extra byte onto the stack
	plx		; pull the return address & extra byte in xy
	ply

	PullLong	Index

	phy		; push the return address & extra byte back
	phx
	plp		; pull the extra byte off again
	PushWord	DP	; push the old direct page value again


  IF ErrorCheck THEN
	MoveLong	Index,xy
	jsr	VerifyIndex
	bcc	@1
	ReturnZ
@1
  ENDIF
	ldy	Index+2		; index segment 0 => zero size (virtual) block
	bne	indexOk		; can't resize block - not a real index
  IF ErrorCheck THEN
	Call	D_AlertBox,in=(#OkBox:w,#ziMesg:l),out=(a:w)
  ENDIF
	lda	#0
	tax
	ldy	#-1
	sec
	ReturnZ
indexOk

	; get ptr to index segment
	lda	[H_ISegPtrLo],y
	sta	ISegPtr
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2

	; get data segment:offset from index
	ldy	Index		; offset within I seg to index
	lda	[ISegPtr],y	; offset within D seg to block
	sta	Offset
	iny			; adjust to high word
	iny
	lda	[ISegPtr],y	; data segment

; two cases: Sign bit clear => small block (index contains segment:offset)
;	   Sign bit set => large block (index contains flag:handle)
;		         or free index (index contains flag:next_index)
	JMI	resizeHandle
	; we now know that BlockSize < H_LARGE_BLOCK_SIZE < 64K

  IF UseLastResort THEN
	stz	SkipLastResort
  ENDIF

; get the data segment pointer
	tay
	lda	[H_DSegPtrLo],y
	sta	SegPtr
	lda	[H_DSegPtrHi],y
	sta	SegPtr+2

; get current size of block
	ldy	Offset
	beq	zeroBlock		; zero offset => zero size (virtual) block
	lda	[SegPtr],y	; size
	sta	BlockSize

; are we growing or shrinking the block?
	lda	DataSize+2
	JNE	newAndDispose	; must be growing - the old block is < 64K
	lda	DataSize
	cmp	#H_LARGE_BLOCK_SIZE	; are we growing to a handle?
	JGE	newAndDispose	; yes - old block is < H_LARGE_BLOCK_SIZE
	cmp	BlockSize
	beq	returnPtr		; same size
	JGE	grow

; shrinking the block
	; round up the DataSize to multiple of H_BLOCK_GRAN
	;lda	DataSize
  IF H_BLOCK_GRAN = 2 THEN
 	ina
  ELSE
	clc
	adc	#H_BLOCK_GRAN-1
  ENDIF
	and	#-H_BLOCK_GRAN
	sta	DataSize
	beq	shrinkToZero

	SubWord	BlockSize,DataSize,BlockSize	; how much are we shrinking by?
	beq	returnPtr			; - nothing (same size)
	; BlockSize is now the difference between the old and new sizes

	; shrink the block by storing the new data size
	;ldy	Offset	; offset to block	- already in y
	lda	DataSize
	sta	[SegPtr],y

	; create a new free block out of the freed space at the end of the block
	tya			; offset to block
	clc
	adc	#H_BLOCK_OVERHEAD	; won't set carry
	adc	DataSize
	tay			; offset to new free block

	lda	BlockSize		; block size of new free block
  IF H_BLOCK_OVERHEAD = 2 THEN
	dea
	dea
  ELSE
	sec
	sbc	#H_BLOCK_OVERHEAD
  ENDIF
	sta	[SegPtr],y	; data size of new free block

disposeNewFreeBlock
	; put the new free block onto the appropriate free list
	; we fake an index for the block by modifying the old one
	; temporarily, and use DisposeBlock
	MoveWord	Offset,OldIndex
	tya			; offset to new free block
	ldy	Index
	sta	[ISegPtr],y
	jsr	DisposeBlock
	lda	OldIndex
	ldy	Index
	sta	[ISegPtr],y
	sta	Offset

returnPtr			; return a pointer to the block
	ProfileOut 10,_H
	lda	Offset
	clc
	adc	#H_BLOCK_OVERHEAD
	adc	SegPtr
	ldx	SegPtr+2
	bcc	@1
	inx
@1
	ldy	#0
	clc
	ReturnZ

shrinkToZero		; create a zero size (virtual) block
	jsr	DisposeBlock
	lda	#0
	ldy	Index
	sta	[ISegPtr],y
	iny
	iny
	sta	[ISegPtr],y
	; DataSize = 0, so we'll fall through hereÉ
zeroBlock
	lda	DataSize		; are we growing the block?
	ora	DataSize+2
	JNE	newAndDispose

  IF Profile THEN	
	ProfileOut 10,_H
	lda	#0
  ENDIF
	; return 0 pointer to zero size (virtual) block
	;lda	#0	- a = 0 already
	tax
	tay		; no error
	clc
	ReturnZ


resizeHandle
  IF ErrorCheck THEN
	cmp	#H_LARGE_BLOCK_FLAG	; H_LARGE_BLOCK_FLAG > H_FREE_INDEX_FLAG
	blt	freeIndex
  ENDIF	; ErrorCheck

	lda	DataSize
	ora	DataSize+2
	beq	shrinkToZero

	; if the new block will definitely be a handle we'll just resize the
	; one we've got, otherwise we'll allocate a new block, copy the data
	; and dispose of the handle.  We could just shrink the handle, but
	; then handles would never go away.
  IF UseLastResort THEN
	MoveWord	#1,SkipLastResort
  ENDIF
	lda	DataSize+2
	bne	resizeH
	CmpWord	DataSize,#H_LARGE_BLOCK_SIZE
	JLT	newAndDispose

resizeH
	PushLong	DataSize
	ldy	Index
	lda	[ISegPtr],y
	sta	Ptr
	iny
	iny
	lda	[ISegPtr],y
	and	#$00FF
	sta	Ptr+2
	pha
	pei	Ptr
	jsl	D_GrowHandle
	bcc	growOk
	pha			; save error code
  IF VerboseErrors THEN
 	Call	D_AlertBox,in=(#OkBox:w,#NoGrowHandleMesg:l),out=(a:w)
	DEBRK3
  ENDIF
  	; we failed, but return a pointer to the block anyway
	MoveLong	[Ptr],ax
	ply			; error code
	sec
	ReturnZ

growOk
	ProfileOut 10,_H

; return the data pointer
	MoveLong	[Ptr],ax
	ldy	#0		; no error
	clc
	ReturnZ


  IF ErrorCheck THEN
freeIndex
	Call	D_AlertBox,in=(#OkBox:w,#fiMesg:l),out=(a:w)
	lda	#0
	tax
	ldy	#-1
	sec
	DEBRK2
	ReturnZ

fiMesg	str	'H_ResizeBlock: Attempt to resize a free index!'
  ENDIF	; ErrorCheck


grow			; new size > old size
  IF GrowBlocks THEN
	; try to extend the block in place
	; a = DataSize
	; y = Offset to block

	; is this block at the end of the used portion of the segment?
	tya			; adjust offset to end of block
	clc
	adc	#H_BLOCK_OVERHEAD
	adc	BlockSize
	ldy	#H_SegEnd
	cmp	[SegPtr],y
	blt	checkNextBlock	; not at end of used portion

	AddWord	Offset,DataSize,a	; offset to new end of block - won't set carry
	adc	#H_BLOCK_OVERHEAD
    IF VariableDSegSize THEN
	dea
	ldy	#H_SegSize
	cmp	[SegPtr],y
    ELSE
	cmp	#H_DSEG_SIZE+1
    ENDIF
	JGE	newAndDispose	; block would extend past end of segment

	; adjust SegEnd and SegFreeSpace
	SubWord	DataSize,BlockSize,BlockSize
    IF VariableDSegSize THEN
	ldy	#H_SegEnd
    ENDIF
	AddWord	a,[SegPtr]:y,[SegPtr]:y
	ldy	#H_SegFreeSpace
	SubWord	[SegPtr]:y,BlockSize,[SegPtr]:y

	; extend the block into the unused portion of the segment
	ldy	Offset
	lda	DataSize
	sta	[SegPtr],y

	brl	ReturnPtr
	
checkNextBlock		; just checking 1 block - could check more
	tay				; a = offset to next block
	lda	[SegPtr],y		; size field
	bpl	newAndDispose		; next block is in use

	and	#$7FFF			; clear sign bit
	clc
	adc	#H_BLOCK_OVERHEAD
	adc	BlockSize
	cmp	DataSize
	blt	newAndDispose

	; extend block into next block
	sty	NextOffset
	ldy	Offset
	lda	DataSize
	sta	[SegPtr],y

	; adjust SegFreeSpace
	ldy	NextOffset		; get next block size
	lda	[SegPtr],y
	and	#$7FFF
  IF H_BLOCK_OVERHEAD = 2 THEN			; add H_BLOCK_OVERHEAD
	ina
	ina
  ELSE
	clc
	adc	#H_BLOCK_OVERHEAD
  ENDIF
	pha
	ldy	#H_SegFreeSpace
	SubWord	[SegPtr]:y,1:s,[SegPtr]:y
	pla

	; remove next block from its free list (if any)
	ldy	NextOffset
	lda	[SegPtr],y
	and	#$7FFF
	cmp	#H_MAX_FREE_LIST+1		; is this block too large too
	bge	leftover2			; large to be on a free list?

	DivByPwr2	a,(H_BLOCK_GRAN/2)	; calculate free list offset
	bra	removeLoopEntry

removeLoop			; find this block on its free list
  IF H_BLOCK_OVERHEAD = 2 THEN
	ina
	ina
  ELSE
	clc
	adc	#H_BLOCK_OVERHEAD
  ENDIF
removeLoopEntry
	tay
	lda	[SegPtr],y
	beq	leftover		; reached end of free list
	cmp	NextOffset
	bne	removeLoop
				; at block preceding used block in free list
	tyx
  IF H_BLOCK_OVERHEAD = 2 THEN
	ina
	ina
  ELSE
	clc
	adc	#H_BLOCK_OVERHEAD
  ENDIF
	tay
	lda	[SegPtr],y	; get link to block following used block
	txy
	sta	[SegPtr],y	; replace link to used block

leftover		; create a new block out of the leftover space
	ldy	NextOffset
	lda	[SegPtr],y		; get amount of leftover space
	and	#$7FFF			; (minus H_BLOCK_OVERHEAD)
leftover2
	AddWord	a,BlockSize,a
	SubWord	a,DataSize,a
	JMI	returnPtr			; no leftover space
	tax
	lda	Offset			; get offset to new block
	clc
	adc	#H_BLOCK_OVERHEAD
	adc	DataSize
	tay
	MoveWord	x,[SegPtr]:y		; store size of new block

	brl	disposeNewFreeBlock		; backwards branch
  ENDIF


newAndDispose	; allocate a new block, copy, and dispose of the old block
	; need a new index (temporarily) - first remember the current one
	MoveLong	Index,OldIndex
	jsr	NewIndex
	bcs	errorI

	jsr	NewBlock
	bcc	okNewBlock

	jsr	DisposeIndex	; dispose of the temporary index

	ldx	#0
	bra	errorB
errorI
	ldx	#-1
errorB
	sty	ErrorFlag
	ldy	OldIndex
	sty	Index
	ldy	OldIndex+2
	sty	Index+2
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2
	lda	[H_ISegPtrLo],y
	sta	ISegPtr

	ldy	Index
	iny
	iny
	lda	[ISegPtr],y
	JMI	resizeH		; handle - try D_GrowHandle

  IF VerboseErrors THEN
	phx
	SpaceWord
	PushWord	#OkBox
	txa
	beq	@1
	PushLong	#NoIndexMesg
	bra	@2
@1	PushLong	#NoBlockMesg
@2	jsl	D_AlertBox
	pla
	plx
	DEBRK3
  ENDIF
  	; we failed, but return a pointer to the block anyway
	MoveLong	Index,xy
	jsl	H_GetBlockPtr
	ldy	ErrorFlag
	sec
	ReturnZ

okNewBlock
	MoveLong	ax,Ptr		; save ptr to new block

; here Index => new block, OldIndex => old block
; copy the data
	ldy	OldIndex+2
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2
	lda	[H_ISegPtrLo],y
	sta	ISegPtr
	ldy	OldIndex
	lda	[ISegPtr],y
	sta	Offset
	iny
	iny
	lda	[ISegPtr],y		; segment # or flag
	bpl	notHandle

	and	#$00FF		; store handle into OldIndex
	sta	OldIndex+2
	lda	Offset
	sta	OldIndex
	ldy	#2		; deref handle
	lda	[OldIndex],y
	pha
	lda	[OldIndex]
	pha
	bra	doMove

notHandle
	tay
	lda	[H_DSegPtrLo],y
	sta	SegPtr
	lda	[H_DSegPtrHi],y
	tax

	lda	Offset
	clc
	adc	#H_BLOCK_OVERHEAD
	adc	SegPtr
	bcc	@1
	inx
@1
	phx
	pha
doMove
	PushLong	Ptr
	PushLong	DataSize
	_BlockMove

; dispose of the old block
	PushLong	Index		; save the new Index

	MoveLong	OldIndex,Index
	jsr	DisposeBlock

	PullLong	Index		; restore the new Index

; set the old index to refer to the new block
; ISegPtr refers to the old index segment, SegPtr will refer to the temporary
	ldy	Index+2
	lda	[H_ISegPtrHi],y
	sta	SegPtr+2
	lda	[H_ISegPtrLo],y
	sta	SegPtr

	ldy	Index
	lda	[SegPtr],y
	ldy	OldIndex
	sta	[ISegPtr],y

	ldy	Index
	iny
	iny
	lda	[SegPtr],y
	ldy	OldIndex
	iny
	iny
	sta	[ISegPtr],y

; dispose of the temporary index
	MoveLong	SegPtr,ISegPtr
	jsr	DisposeIndex

; return a pointer to the new block in ax
	ProfileOut 10,_H

	MoveLong	Ptr,ax
	ldy	#0		; no error
	ReturnZ

  IF VerboseErrors THEN
NoGrowHandleMesg	str	'H_ResizeBlock: Unable to grow block handle'
NoIndexMesg	str	'H_ResizeBlock: Unable to get temporary index'
NoBlockMesg	str	'H_ResizeBlock: Unable to get new block'
  ENDIF
  IF ErrorCheck THEN
ziMesg	str	'H_ResizeBlock: Attempt to resize index $00xx!'
  ENDIF
	ENDP

;¦;--------------------------------------------------------------
; H_CopyBlock(OldIndex:xy): Index:ax, Ptr:l
;	error status returned in y, with x used to indicate
;	error in index (FF) or block (0) allocation
;
; Copy a block in the current heap.
; Index and Ptr are invalid if any error occurs.

; Zpage variables modified: 
;	DataSize
;	OldIndex
;	Ptr
;	H_GetBlockPtr()
;	H_GetBlockSize()
;	NewIndex()
;	NewBlock()

H_CopyBlock	PROC	EXPORT

NewBlockPtr	equ	6
		; stack frame w/offsets:
		;	1: return address (3 bytes)
		;	4: saved direct page (2 bytes)
		;	6: space for returned pointer (4 bytes)

	BeginZ

	MoveLong	xy,OldIndex

  IF ErrorCheck THEN
	jsr	VerifyIndex
	bcc	@1
	ReturnZ
@1
  ENDIF

  IF Profile THEN
 	ProfileIn 11,_H
	ldx	OldIndex
  ENDIF

	jsl	H_GetBlockSize

	MoveLong	ax,DataSize

	jsr	NewIndex
	bcs	noIndex

  IF UseLastResort THEN
	stz	SkipLastResort
  ENDIF
	jsr	NewBlock
	bcs	noBlock

	MoveLong	ax,Ptr		; ptr to new block
	;MoveLong	ax,NewBlockPtr:s
	sta	NewBlockPtr,s
	txa
	sta	NewBlockPtr+2,s

	MoveLong	OldIndex,xy
	jsl	H_GetBlockPtr

	PushLong	ax
	PushLong	Ptr
	PushLong	DataSize
	_BlockMove

	ProfileOut 11,_H

	MoveLong	Index,ax
	ldy	#0		; no error
	clc
	ReturnZ

noIndex
  IF VerboseErrors = 0 THEN
	ldx	#-1
	ReturnZ
  ELSE
	phy
	PushWord	#-1
	SpaceWord
	PushWord	#OkBox
	PushLong	#NoIndexMesg
	bra	error
  ENDIF

noBlock
	phy
	jsr	DisposeIndex

  IF VerboseErrors = 0 THEN
	ply
	ldx	#0
	ReturnZ
  ELSE
	PushWord	#0
	SpaceWord
	PushWord	#OkBox
	PushLong	#NoBlockMesg
error	jsl	D_AlertBox
	pla
	plx			; 0 or -1
	ply			; error code
	ReturnZ
  ENDIF

  IF VerboseErrors THEN
NoIndexMesg	str	'H_CopyBlock: Unable to allocate index'
NoBlockMesg	str	'H_CopyBlock: Unable to allocate block'
  ENDIF
	ENDP

;¦;--------------------------------------------------------------
; H_GetBlockPtr(Index:xy): Ptr:ax
;	nil pointer returned in case of error
;
; Return a pointer to the specified block in the current heap.
; In case of error or a zero size block, Ptr will be nil.

; Zpage variables modified: 
;	Offset
;	ISegPtr
;	SegHandle

H_GetBlockPtr	PROC	EXPORT

	BeginZ

  IF ErrorCheck THEN
	jsr	VerifyIndex
	bcc	@9
	ReturnZ
@9
  ENDIF
  IF Profile THEN
	phx
	ProfileIn 12,_H
	plx
  ENDIF

	; index: y = index segment #, x = offset within index segment

	tya			; index segment 0 => zero size (virtual) block
	beq	zeroBlock

	; get ptr to index segment
	lda	[H_ISegPtrLo],y
	sta	ISegPtr
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2

	; get data segment:offset from index
	txy			; offset within I seg to index
	lda	[ISegPtr],y	; offset within D seg to block
	sta	Offset
	iny			; adjust to high word
	iny
	lda	[ISegPtr],y	; data segment
	
; two cases: Sign bit clear => small block (index contains segment:offset)
;	   Sign bit set => large block (index contains flag:handle)
;		         or free index (index contains flag:next_index)
	bmi	handleOrFree
	beq	zeroBlock		; data segment 0 => zero size (virtual block)

; case 1: small block
; accumulator contains segment #
	tay

; add offset to the segment pointer (at [H_DSegPtrLo/Hi],y).
; add H_BLOCK_OVERHEAD yielding ptr to data in block (returned in ax)
	lda	[H_DSegPtrHi],y
	tax			; x = hi word
	lda	Offset		; really the block offset
	clc
	adc	#H_BLOCK_OVERHEAD	; this will NOT set carry on a valid heap
	adc	[H_DSegPtrLo],y
	bcc	@1
	inx			; inc high word
@1
  IF Profile THEN
	PushLong	ax
	ProfileOut 12,_H
	PullLong	ax
  ENDIF
	ReturnZ

zeroBlock				; a = 0
	tax
	ReturnZ


; case 2: handle or free index
handleOrFree
  IF ErrorCheck THEN
	cmp	#H_LARGE_BLOCK_FLAG	; H_LARGE_BLOCK_FLAG > H_FREE_INDEX_FLAG
	blt	freeIndex
  ENDIF	; ErrorCheck

	; extract the handle from the index and store into SegHandle
	and	#$00FF
	sta	SegHandle+2
	lda	Offset
	sta	SegHandle

	; now dereference to get the data pointer in ax
	ldy	#2
	lda	[SegHandle],y
	tax
	lda	[SegHandle]

  IF Profile THEN
	PushLong	ax
	ProfileOut 12,_H
	PullLong	ax
  ENDIF
	ReturnZ

  IF ErrorCheck THEN
freeIndex
	SpaceWord
	PushWord	#OkBox
	PushLong	#fiMesg
	jsl	D_AlertBox
	pla
	lda	#0
	tax
	DEBRK2
	ReturnZ

fiMesg	str	'H_GetBlockPtr: Attempt to dereference a free index!'
  ENDIF	; ErrorCheck

	ENDP

;¦;--------------------------------------------------------------
; H_GetBlockSize(Index:xy): Size:ax
;	zero size returned in case of error
;
; Return the size of the specified block in the current heap.

; Zpage variables modified: 
;	Offset
;	ISegPtr
;	SegPtr

H_GetBlockSize	PROC	EXPORT

	BeginZ

  IF ErrorCheck THEN
	jsr	VerifyIndex
	bcc	@1
	ReturnZ
@1
  ENDIF
  IF Profile THEN
	phx
	ProfileIn 13,_H
	plx
  ENDIF

	; index: y = index segment #, x = offset within index segment

	tya			; index segment 0 => zero size (virtual) block
	beq	zeroBlock

	; get ptr to index segment
	lda	[H_ISegPtrLo],y
	sta	ISegPtr
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2

	; get data segment:offset from index
	txy			; offset within I seg to index
	lda	[ISegPtr],y	; offset within D seg to block
	sta	Offset
	iny			; adjust to high word
	iny
	lda	[ISegPtr],y	; data segment
	
; two cases: Sign bit clear => small block (index contains segment:offset)
;	   Sign bit set => large block (index contains flag:handle)
;		         or free index (index contains flag:next_index)
	bmi	handleOrFree
	beq	zeroBlock		; data segment 0 => zero size (virtual block)

; case 1: small block
; accumulator contains segment #
	tay

; use offset and the segment pointer (at [H_DSegPtrLo/Hi],y)
; to get the size from the first word of the block.
	lda	[H_DSegPtrLo],y
	sta	SegPtr
	lda	[H_DSegPtrHi],y
	sta	SegPtr+2

	ldy	Offset
	lda	[SegPtr],y	; size
zeroBlock
	ldx	#0
  IF Profile THEN
	PushLong	ax
	ProfileIn 13,_H
	PullLong	ax
  ENDIF
	ReturnZ


; case 2: handle or free index
handleOrFree

  IF ErrorCheck THEN
	cmp	#H_LARGE_BLOCK_FLAG	; H_LARGE_BLOCK_FLAG > H_FREE_INDEX_FLAG
	blt	freeIndex
  ENDIF	; ErrorCheck

	SpaceLong
	and	#$00FF
	pha
	PushWord	Offset
	_GetHandleSize

	ProfileOut 13,_H

	PullLong	ax
	ReturnZ

  IF ErrorCheck THEN
freeIndex
	SpaceWord
	PushWord	#OkBox
	PushLong	#fiMesg
	jsl	D_AlertBox
	pla
	lda	#0
	tax
	DEBRK2
	ReturnZ

fiMesg	str	'H_GetBlockSize: Attempt to dereference a free index!'
  ENDIF	; ErrorCheck

	ENDP

;¦;--------------------------------------------------------------
; H_TruncHeap()
;
; shrink the heap to its minimum size

; Zpage variables modified:
;	SqueezeHeap()

H_TruncHeap	PROC	EXPORT

	BeginZ

  IF Squeezing THEN
	ldx	#1
	jsr	SqueezeHeap
  ENDIF
	ReturnZ
	ENDP

;¦
;=====================================================================;
;			LOCAL ROUTINES			;
;=====================================================================;

;--------------------------------------------------------------
; NewIndex()
;
; Allocate a new index.
;
; post:	Index will contain the index (segment:offset)
;	ISegPtr will point to the index segment used
;	return errors in y with carry set

; Zpage variables modified: 
;	H_EndIndex
;	H_FreeIndex
;	H_NumISegments
;	Index
;	ISegPtr

NewIndex	PROC	ENTRY

	ProfileIn 7,_H

; first, try the free list
	ldy	H_FreeIndex+2	; segment of free index
	beq	emptyFreeIndexList

	; found a free index

	sty	Index+2
	lda	[H_ISegPtrLo],y
	sta	ISegPtr
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2

	ldy	H_FreeIndex	; offset of free index
	sty	Index

	; update the free list (the index contains the next free index)
	lda	[ISegPtr],y	; offset to next free index
	sta	H_FreeIndex
  IF ErrorCheck THEN
	cmp	#H_ISEG_SIZE
	bge	indexFreeListCorrupted
	and	#%11
	bne	indexFreeListCorrupted
  ENDIF
	iny
	iny
	lda	[ISegPtr],y	; segment to next free index + flag

  IF ErrorCheck THEN
	cmp	#H_FREE_INDEX_FLAG+$1000
	bge	indexFreeListCorrupted
	cmp	#H_FREE_INDEX_FLAG
	bge	iflOk
indexFreeListCorrupted
	Call	D_AlertBox,in=(#OkBox:w,#iflcMesg:l),out=(a:w)
	ldy	#-1		; error value in y
	sec
	DEBRK2
	rts
iflOk
  ENDIF	; ErrorCheck

	and	#$FF		; mask out the free index flag
	sta	H_FreeIndex+2
  IF ErrorCheck THEN
	cmp	H_NumISegments
	bgt	indexFreeListCorrupted
	and	#%11
	bne	indexFreeListCorrupted
  ENDIF

	ProfileOut 7,_H
	ldy	#0
	clc
	rts


emptyFreeIndexList		; nothing in free list - try end of last index segment
	ldy	H_NumISegments
	beq	newSeg			; no index segments
	lda	H_EndIndex
	cmp	#H_ISEG_SIZE
	bge	newSeg			; no space left in last segment

	sty	Index+2		; = H_NumISegments
	sta	Index		; = H_EndIndex
	clc
	adc	#4
	sta	H_EndIndex

	lda	[H_ISegPtrLo],y
	sta	ISegPtr
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2

	ldy	#0		; no error
	clc
	rts

newSeg
  IF ErrorCheck THEN
 	beq	@endIndexOk
	Call	D_AlertBox,in=(#OkBox:w,#beiMesg:l),out=(a:w)
	ldy	#-1			; error value in y
	sec
	DEBRK2
	rts
@endIndexOk
  ENDIF	; ErrorCheck

  IF Verbose THEN
    IF Verbose > 1 THEN
	Call	D_AlertBox,in=(#OkBox:w,#growIndexMesg:l),out=(a:w)
    ELSE
	Call	D_WordBox,in=(#growIndexMesg:l)
    ENDIF
	DEBRK
	ldy	H_NumISegments
  ENDIF
	cpy	H_MaxSegments	; verify there are < H_MaxSegments
	blt	@1
	ldy	#memErr		; fake error code
	rts			; carry set
@1
	; allocate the handle for the segment (locked)
	SpaceLong
	PushLong	#H_ISEG_SIZE
	PushWord	#attrLocked
	ProfileIn 15,_H
	jsl	D_NeedHandle
	tay			; error code
	PullLong	SegHandle
	bcc	@2

	ProfileOut 7,_H
  IF Verbose = 1 THEN
	phy
	Call	D_CloseWordBox
	ply
  ENDIF
	rts			; carry set, error in y

@2	; increment the # of index segments and store the handle in the header
	AddWord	H_NumISegments,#4,H_NumISegments	; result left in a
	ldy	#H_NumISegments
	sta	[H_HeaderPtr],y
	tay
	lda	SegHandle
	sta	[H_ISegHandleLo],y
	lda	SegHandle+2
	sta	[H_ISegHandleHi],y

	; deref the handle and store the pointer in the header
	lda	[SegHandle]
	sta	ISegPtr
	sta	[H_ISegPtrLo],y
  IF ZeroInit THEN
	tax
  ENDIF
	ldy	#2
	lda	[SegHandle],y
	ldy	H_NumISegments
	sta	ISegPtr+2
	sta	[H_ISegPtrHi],y

	; initialize the index segment
  IF ZeroInit THEN
	pha
	phx
	PushWord	#H_ISEG_SIZE
	jsl	D_ZeroBlock
  ENDIF
	; return the first index in the new segment
	ldy	H_NumISegments
	sty	Index+2
	stz	Index
	MoveWord	#4,H_EndIndex

  IF Verbose = 1 THEN
	Call	D_CloseWordBox
  ENDIF
	ProfileOut 7,_H
	ldy	#0		; no error
	clc
	rts

  IF ErrorCheck THEN
iflcMesg	str	'NewIndex: Index Free List Corrupted!'
beiMesg	str	'NewIndex: H_EndIndex > H_ISEG_SIZE!'
  ENDIF
  IF Verbose THEN
growIndexMesg	str	'NewIndex: allocating new index segment'
  ENDIF
	ENDP

;¦;--------------------------------------------------------------
; DisposeIndex()
;
; Dispose of an index by placing it at the head of the free list
;
; pre:	Index = segment & offset of index to dispose
;	ISegPtr = ptr to index segment

; Zpage variables modified: 
;	H_FreeIndex

DisposeIndex	PROC	ENTRY

	ldy	Index+2
	beq	disposeZeroError

  IF ErrorCheck THEN
	ldx	Index
	jsr	VerifyIndex
	bcc	@1
	rts
@1
  ENDIF
	
; copy old free list head into index, and flag index as free
	lda	H_FreeIndex
	ldy	Index
	sta	[ISegPtr],y
	lda	H_FreeIndex+2
	ora	#H_FREE_INDEX_FLAG
	iny
	iny
	sta	[ISegPtr],y

; free list head = index
	MoveLong	Index,H_FreeIndex

  IF (ErrorCheck = 0) THEN
disposeZeroError
  ENDIF
	rts

  IF ErrorCheck THEN
disposeZeroError
	SpaceWord
	PushWord	#OkBox
	PushLong	#dzMesg
	jsl	D_AlertBox
	pla
	DEBRK2
	rts

dzMesg	str	'DisposeIndex: Attempt to dispose of index $00xx!'
  ENDIF	; ErrorCheck
	ENDP

;¦;--------------------------------------------------------------
; NewBlock()
;
; Allocate a new block.
;
; pre:	DataSize = data size of block
;	Index = segment & offset of index to use
;	ISegPtr = ptr to index segment
;
; post:	return ptr to block in ax.
;	return errors in y with carry set.
;
; Zpage variables modified: 
;	BlockSize
;	DataSize
;	FreeListOffset
;	ISegPtr (recalc only)
;	MaxFreeSeg
;	MaxFreeSpace
;	Offset
;	Ptr
;	SegPtr
;	Segment
;	CompactSegment()
;	NewDataSegment()
;	SqueezeHeap()

NewBlock	PROC	ENTRY

; if the block size is > H_LARGE_BLOCK_SIZE, allocate a handle for the block
; if the block size is 0, return a 'virtual' block
; otherwise, add H_BLOCK_OVERHEAD to the requested size to obtain the actual block size

	ProfileIn 8,_H

  IF ErrorCheck THEN
	MoveLong	Index,xy
	jsr	VerifyIndex
	bcc	@1
	rts
@1
  ENDIF
	lda	DataSize+2
	JNE	largeBlock

	lda	DataSize
	bne	nonZero

	; zero size block - return virtual block at Dseg #0, offset 0
	; a = 0
	ldy	Index
	sta	[ISegPtr],y
	iny
	iny
	sta	[ISegPtr],y

	;lda	#0
	tax
	tay		; no error
	clc
	rts

nonZero
	cmp	#H_LARGE_BLOCK_SIZE	; check if too large
	JGE	largeBlock

	; round size up to multiple of H_BLOCK_GRAN
  IF H_BLOCK_GRAN = 2 THEN
 	ina
  ELSE
	;clc		already clear since we fell through a bge
	adc	#H_BLOCK_GRAN-1
  ENDIF
	and	#-H_BLOCK_GRAN
	sta	DataSize

	;clc		already clear
	adc	#H_BLOCK_OVERHEAD
	sta	BlockSize
	
	cmp	#H_MAX_FREE_LIST+H_BLOCK_OVERHEAD+1	; too big to be on
	JGE	mediumBlock			; a free list?

	; calculate the offset into the free list:
	; free list offset = data_size / (H_BLOCK_GRAN/2)
	; we assume that H_BLOCK_GRAN is a power of 2
  IF (H_BLOCK_GRAN ² 2) THEN
	ldy	DataSize
  ELSE
	lda	DataSize		; the block size
	DivByPwr2	a,(H_BLOCK_GRAN/2)	; divide a by H_BLOCK_GRAN/2
	tay
  ENDIF
	; get segment # from free list
	lda	[H_FreeListArray],y
	JEQ	mediumBlock	; 0 => this free list is empty
	sta	Segment
checkNextSegLoop
	sty	FreeListOffset	; save the free list offset
	; get pointer to data segment
	tay
	lda	[H_DSegPtrLo],y
	sta	SegPtr
	lda	[H_DSegPtrHi],y
	sta	SegPtr+2

	; get offset from data segment free list
	ldy	FreeListOffset
	lda	[SegPtr],y
	bne	gotFreeBlock

	; check next segment
	;ldy	FreeListOffset
	AddWord	Segment,#4,Segment
	sta	[H_FreeListArray],y
	cmp	H_NumDSegments
	BLE	checkNextSegLoop

	; free lists empty in all segments - clear head
	lda	#0
	sta	[H_FreeListArray],y
	bra	mediumBlock
	

gotFreeBlock			; update free list
  IF ErrorCheck THEN
	; check that the offset is valid
	cmp	#DSEG_HDR_SIZE
	blt	corrupt
    IF VariableDSegSize THEN
	ldy	#H_SegSize
	cmp	[SegPtr],y
    ELSE
	cmp	#H_DSEG_SIZE
    ENDIF
	bge	corrupt
  ENDIF
	; a = Offset (to this block)
	; save offset to this block in index
	ldy	Index
	sta	[ISegPtr],y
	sta	Offset

	; get offset to next free block out of this block
  IF H_BLOCK_OVERHEAD = 2 THEN
	ina
	ina
  ELSE
	clc
	adc	#H_BLOCK_OVERHEAD
  ENDIF
	tay
	lda	[SegPtr],y

	; and store in the free list head
	ldy	FreeListOffset
	sta	[SegPtr],y

	; save segment of this block in Index
	lda	Segment
	ldy	Index
	iny
	iny
	sta	[ISegPtr],y

  IF ErrorCheck THEN
	; verify that the size in the block is correct for the free list
	; it was on
	ldy	Offset
	lda	[SegPtr],y
    IF GrowBlocks THEN
	bpl	corrupt		; not a free block!
	and	#$7FFF  		; clear high bit of size field
    ENDIF
	cmp	DataSize
	beq	sizeOK

corrupt	Call	D_AlertBox,in=(#OkBox:w,#bbsMesg:l),out=(a:w)
	ldx	#0		; error in block allocation
	ldy	#-1		; error value in y
	sec
	DEBRK2
	rts

sizeOK
  ENDIF
	bra	returnBlock


mediumBlock		; allocate a block from the end of a segment

	stz	MaxFreeSpace
	stz	MaxFreeSeg

; search segments in order, beginning with the last segment,
; looking for space at the end of segment
	ldx	H_NumDSegments
	JEQ	newSeg			; no data segments
allocLoop
	txy
	lda	[H_DSegPtrLo],y
	sta	SegPtr
	lda	[H_DSegPtrHi],y
	sta	SegPtr+2

	ldy	#H_SegEnd
	lda	[SegPtr],y
	sta	Offset
	clc
	adc	BlockSize
	bcs	nextSeg		; data segments are ² 64K long
  IF VariableDSegSize THEN
	ldy	#H_SegSize
	cmp	[SegPtr],y
  ELSE
	cmp	#H_DSEG_SIZE
  ENDIF
	blt	foundSpace
	beq	foundSpace
nextSeg
 	; need to check free space and keep track of segment with
	; the most, and also the total (for later use in compact)
	ldy	#H_SegFreeSpace
	lda	[SegPtr],y
	cmp	MaxFreeSpace
	blt	@1
	sta	MaxFreeSpace
	stx	MaxFreeSeg
@1
	dex			; decrement to previous segment
	dex
	dex
	dex
	bne	allocLoop		; no segment #0

	bra	compactSeg

foundSpace		;found the space we need - allocate the block
  IF VariableDSegSize THEN
	ldy	#H_SegEnd
  ENDIF
	sta	[SegPtr],y	; update EndSeg
	lda	Offset		; store offset in low word of index
	ldy	Index
	sta	[ISegPtr],y
	txa			; x = segment #
	iny
	iny
	sta	[ISegPtr],y	; store segment # in high word of index

returnBlock
	; store data size in block
	ldy	Offset
	lda	DataSize
	sta	[SegPtr],y

	; decrement segment free space by block size
	tyx			; save offset to block in x
	ldy	#H_SegFreeSpace
	lda	[SegPtr],y
	SubWord	a,BlockSize,a
	sta	[SegPtr],y

  IF (&Type('H_SegNumBlocks') ­ 'UNDEFINED ') AND (ErrorCheck > 1) THEN
	; increment # of blocks in segment
	ldy	#H_SegNumBlocks
	lda	[SegPtr],y
	ina
	sta	[SegPtr],y
  ENDIF
	
  IF Profile THEN
  	phx
	ProfileOut 8,_H
	plx
  ENDIF
	; return a pointer to the data pointer of the block
	; x = Offset (to this block)
	txa
	clc
	adc	#H_BLOCK_OVERHEAD	; won't set carry
	adc	SegPtr
	ldx	SegPtr+2
	bcc	@1
	inx
@1
	ldy	#0	; no error
	clc
	rts


compactSeg		; compact the segment with the most free space
	lda	#0		; make sure the index doesn't reference
	ldy	Index		; any data segment
	iny
	iny
	sta	[ISegPtr],y

  	lda	MaxFreeSpace
  IF H_LARGE_BLOCK_SIZE > H_MIN_COMPACT THEN
	cmp	BlockSize		; is there enough free space for the
	blt	newSeg		; block?
  ENDIF
	cmp	#H_MIN_COMPACT	; is there enough free space to be worth
	blt	newSeg		; compacting?

	PushLong	ISegPtr		; CompactSegment modifies ISegPtr
  	jsr	CompactSegment
	PullLong	ISegPtr
	bcs	newSeg		; couldn't compact the segment
	
	; now jump into allocLoop at the iteration for the compacted
	; segment to allocate the block - allocLoop WILL succeed, since
	; we checked above that BlockSize ² MaxFreeSpace
	ldx	MaxFreeSeg
	brl	allocLoop	; backwards branch


newSeg			; allocate a new segment
	jsr	NewDataSegment

	; recalculate ISegPtr in case the index table was moved during the
	; call to NewDataSegment (which can happen even if NewDataSegment fails)
	ldy	Index+2
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2
	lda	[H_ISegPtrLo],y
	sta	ISegPtr
					; carry from NewDataSegment still good
	bcs	squeeze			; couldn't get a new segment
					; error is in ErrorFlag

	; now jump into allocLoop at the last iteration to allocate the block
	; allocLoop WILL succeed, since BlockSize < H_LARGE_BLOCK_SIZE < SegSize
	ldx	H_NumDSegments
	brl	allocLoop		; backwards branch


squeeze		; call SqueezeHeap to try and find more space
  IF Squeezing THEN
	PushLong	ISegPtr		; SqueezeHeap modifies ISegPtr
	ldx	#0		; don't dispose of segments
	jsr	SqueezeHeap
	PullLong	ISegPtr

	lda	MaxFreeSpace	; did we find enough space?
	cmp	BlockSize
    IF UseLastResort THEN
	blt	tryLargeBlock	; no - try finding a handle for the block
				; as a last resort
    ELSE
	blt	noBlock
    ENDIF

	; now jump into allocLoop at the iteration for the compacted
	; segment to allocate the block - allocLoop WILL succeed, since
	; we checked above that BlockSize ² MaxFreeSpace
	ldx	MaxFreeSeg
	brl	allocLoop	; backwards branch
  ELSE
	; if we didn't compact but could have, do it now
	lda	H_NumDSegments
    IF UseLastResort THEN
	beq	tryLargeBlock	; nothing to compact - try largeBlock
    ELSE
	beq	noBlock		; nothing to compact - failed
    ENDIF
	
	CmpWord	MaxFreeSpace,BlockSize
    IF UseLastResort THEN
	blt	tryLargeBlock	; not enough space even if we compact
    ELSE
	blt	noBlock		; not enough space even if we compact
    ENDIF
	
  	jsr	CompactSegment
    IF UseLastResort THEN
	bcs	tryLargeBlock	; couldn't compact the segment
    ELSE
	bcs	noBlock		; couldn't compact the segment
    ENDIF
	
	; now jump into allocLoop at the iteration for the compacted
	; segment to allocate the block - allocLoop WILL succeed, since
	; we checked above that BlockSize ² MaxFreeSpace
	ldx	MaxFreeSeg
	brl	allocLoop	; backwards branch
  ENDIF

  IF UseLastResort THEN
tryLargeBlock
	lda	SkipLastResort
	bne	noBlock	; don't bother if SkipLastResort flag set
			; i.e. we came from resizing a handle
  ENDIF

largeBlock		; allocate a handle for a large block
  IF Verbose THEN
    IF Verbose > 1 THEN
	Call	D_AlertBox,in=(#OkBox:w,#largeBlockMesg:l),out=(a:w)
    ELSE
	Call	D_WordBox,in=(#largeBlockMesg:l)
    ENDIF
	DEBRK
  ENDIF
	SpaceLong
	PushLong	DataSize
	ProfileIn 15,_H
	jsl	D_NeedHand
	sta	ErrorFlag			; save error
  IF Profile THEN
	php
	ProfileOut 15,_H
	plp
  ENDIF
	; recalculate ISegPtr in case the index table was moved during the
	; call to D_NeedHand (which can happen even if D_NeedHand fails)
	ldy	Index+2
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2
	lda	[H_ISegPtrLo],y
	sta	ISegPtr

	pla
	sta	Ptr
	ldy	Index
	sta	[ISegPtr],y
	pla
	sta	Ptr+2
	ora	#H_LARGE_BLOCK_FLAG
	iny
	iny
	sta	[ISegPtr],y
	bcs	noLBlock		; carry has been preserved from D_NeedHand

  	ProfileOut 8,_H
  IF Verbose = 1 THEN
	Call	D_CloseWordBox
  ENDIF
  	; return pointer to block
	ldy	#2
	lda	[Ptr],y
	tax
	lda	[Ptr]
	ldy	#0	; no error
	clc
	rts

noLBlock
  IF Verbose = 1 THEN
	Call	D_CloseWordBox
  ENDIF
noBlock
	lda	#0
	tax
	ldy	ErrorFlag		; return error in y
	sec
	rts

  IF ErrorCheck THEN
bbsMesg	str	'NewBlock: corrupt data free list!'
  ENDIF

  IF Verbose THEN
largeBlockMesg	str	'NewBlock: allocating large block'
  ENDIF
	ENDP

;¦;--------------------------------------------------------------
; DisposeBlock()
;
; Dispose of a block.
;
; pre:	Index = segment & offset of index to block to be disposed
;	ISegPtr = ptr to index segment
;
; Zpage variables modified: 
;	DataSize
;	FreeListOffset
;	Offset
;	SegPtr
;	Segment

DisposeBlock	PROC	ENTRY

  IF ErrorCheck THEN
	MoveLong	Index,xy
	jsr	VerifyIndex
	bcc	@1
	rts
@1
  ENDIF
	; get data segment:offset from index
	ldy	Index		; index offset
	lda	[ISegPtr],y	; data offset
	sta	Offset
	iny
	iny
	lda	[ISegPtr],y	; data segment

; two cases: Sign bit clear => small block (index contains segment:offset)
;	   Sign bit set => large block (index contains flag:handle)
;		         or free index (index contains flag:next_index)
	bmi	handleOrFree
	beq	done		; zero segment => zero size (virtual) block

; case 1: small block
; accumulator contains segment #
	sta	Segment
	tay

; use offset and the segment pointer (at [H_DSegPtrLo/Hi],y)
; to get the size from the first word of the block.
	lda	[H_DSegPtrLo],y
	sta	SegPtr
	lda	[H_DSegPtrHi],y
	sta	SegPtr+2

	ldy	Offset
	beq	done		; zero offset => zero size (virtual) block
	lda	[SegPtr],y	; data size

  IF GrowBlocks THEN
	ora	#$8000		; set high bit of size to flag block as deleted
	sta	[SegPtr],y
	and	#$7FFF
  ENDIF
	beq	adjustSegment	; zero size real block (created by resizing)
	cmp	#H_MAX_FREE_LIST+1	; is this block too large
	bge	adjustSegment	; to be on a free list?
	sta	DataSize

	; calculate the offset into the free list:
	; free list offset = data_size / (H_BLOCK_GRAN/2)
	; = (block_size / (H_BLOCK_GRAN/2)) - 2*H_BLOCK_OVERHEAD/H_BLOCK_GRAN
	; we assume that H_BLOCK_GRAN is a power of 2
	;lda	DataSize		; the data size (already in a)
	DivByPwr2	a,(H_BLOCK_GRAN/2)	; divide a by H_BLOCK_GRAN/2
	sta	FreeListOffset	; save the free list offset

	; insert block at head of data segment free list
  IF H_BLOCK_OVERHEAD = 2 THEN
	iny			; adjust block offset to past overhead
	iny
	tyx			; and save in x
  ELSE
	tya			; adjust block offset to past overhead
	clc
	adc	#H_BLOCK_OVERHEAD
	tax			; and save in x
  ENDIF
	ldy	FreeListOffset
	lda	[SegPtr],y	; get free list head
	txy			; restore adjusted offset to block
	sta	[SegPtr],y	; store free list head in disposed block

	lda	Offset		; save block offset in free list head
	ldy	FreeListOffset
	sta	[SegPtr],y

	; if seg of block < seg in header block free list,
	; or if header block free list is empty, then update
	;ldy	FreeListOffset	; get free list head
	lda	[H_FreeListArray],y
	beq	updateFreeListHead	; update if empty

	cmp	Segment		; update if > block segment
	BLE	noUpdateFreeListHead

updateFreeListHead
	lda	Segment
	sta	[H_FreeListArray],y
noUpdateFreeListHead
	lda	DataSize

adjustSegment
	; a = data size
	; increment segment free space by block size
	ldy	#H_SegFreeSpace
	clc
	adc	#H_BLOCK_OVERHEAD	; won't set carry
	adc	[SegPtr],y
	sta	[SegPtr],y
	
  IF (&Type('H_SegNumBlocks') ­ 'UNDEFINED ') AND (ErrorCheck > 1) THEN
	; decrement # of blocks in segment
	ldy	#H_SegNumBlocks
	lda	[SegPtr],y
	dea
	sta	[SegPtr],y
  ENDIF
done
	rts


; case 2: handle or free index
handleOrFree
	; a = high word of index = flag + high word of handle
  IF ErrorCheck THEN
	cmp	#H_LARGE_BLOCK_FLAG	; H_LARGE_BLOCK_FLAG > H_FREE_INDEX_FLAG
	blt	freeIndex
  ENDIF
	and	#$FF		; mask out flag
	pha
	lda	Offset		; low word of handle
	pha
	_DisposeHandle
	rts

  IF ErrorCheck THEN
freeIndex
	Call	D_AlertBox,in=(#OkBox:w,#ErrorMesg:l),out=(a:w)
	DEBRK2
	rts

ErrorMesg	str	'H_DisposeBlock: Attempt to dispose of a free index!'
  ENDIF	; ErrorCheck

	ENDP

;¦;--------------------------------------------------------------
; CompactMoveBlock
;
; Move a block from one data segment to another
;
; pre:	x should contain the offset in the destination data segment.
;	[ISegPtr],y should reference the high word of the index of the block
;	to be moved.
;	The source data segment address should be punched into the code
;	wherever !0000 appears, and the data bank should be set accordingly.
;	The destination data segment address should be punched into the code
;	wherever >000000 appears.
;	If the source segment begins within 16 bytes of the end of a bank,
;	moveLoop should be puched at address whichLoop+1, otherwise
;	check16 should be puched there.
;
; post:	x will contain the offset in the destination segment just past the
;	end of the block.
;	y will contain the offset in the source segment just past the
;	end of the block.

CompactMoveBlock	PROC	ENTRY

	ENTRY	loadSize, whichLoop, check16, moveLoop
	ENTRY	l0, s0, l2, s2, l4, s4, l8, s8, l10, s10, l12, s12, l14, s14
	ENTRY	load, store

	; y = offset (from ISegPtr) to high word of index to block to be moved.
	dey
	dey
	lda	[ISegPtr],y		; old (src) offset
	sta	Offset

	; store the new block offset in the index,
	; and store the offset to the end of the block
	; in the code to check loop termination
	txa			; x = new (destination) offset
	sta	[ISegPtr],y
	ldy	Offset
	clc			; add overhead and block size
	adc	#H_BLOCK_OVERHEAD
loadSize	adc	!0000,y		; - MODIFIED
	sta	>checkEnd+1	; store in code
	sec
	sbc	#16
	sta	>check16+1	; store 16 less in code

	; "jmp moveLoop" if the segment is too close to the end of a bank
whichLoop	jmp	check16		; - MODIFIED
	
	; now copy the block (16 bytes at a time)
moveLoop16
l0	lda	!0000,y		; - MODIFIED
s0	sta	>000000,x
l2	lda	!0000,y
s2	sta	>000000,x
l4	lda	!0000,y
s4	sta	>000000,x
l6	lda	!0000,y
s6	sta	>000000,x
l8	lda	!0000,y
s8	sta	>000000,x
l10	lda	!0000,y
s10	sta	>000000,x
l12	lda	!0000,y
s12	sta	>000000,x
l14	lda	!0000,y
s14	sta	>000000,x
	tya
	clc
	adc	#16	; will not set carry
	tay
	txa
	adc	#16
	tax
check16	cpx	#0000	; (new offset to end of block)-16 - MODIFIED
	blt	moveLoop16

	; copy the rest, a word at a time
moveLoop
load	lda	!0000,y		; - MODIFIED
store	sta	>000000,x		; - MODIFIED
	iny
	iny
	inx
	inx
checkEnd	cpx	#0000		; new offset to end of block - MODIFIED
	blt	moveLoop

	rts

	ENDP
	
;¦;--------------------------------------------------------------
  IF Squeezing THEN
; SqueezeHeap(DisposeSegs:x)
;
; Compact the heap, squeezing blocks into segments
; Returns with the size of largest free block in MaxFreeSpace
; and the corresponding segment in MaxFreeSeg
; Algorithm:
;     for each segment from 4 to H_NSegments
;         compact segment
;	fill segment (until H_SegFreeSpace < H_MIN_SQUEEZE) from higher segments
;     if DisposeSegs then dispose of any empty segments

; Zpage variables modified:
;	H_NSegments
;	DP	(aliased to Size)
;	EndPtr
;	MaxFreeSeg
;	MaxFreeSpace
;	Offset
;	OldPtr
;	Ptr
;	SegHandle
;	SegPtr

SqueezeHeap	PROC	ENTRY

Size	equ	DP

	txa
	sta	>DisposeSegs	; this space is below, in the code

	ProfileIn 6,_H

	stz	MaxFreeSpace

	jsl	D_CheckPurge	; verify System Handle is available
	bcc	@9
	rts			; oops - it's not available
@9

  IF Verbose > 1 THEN
	Call	D_AlertBox,in=(#OkBox:w,#SqueezeMesg:l),out=(a:w)
	DEBRK
  ENDIF

	phb			; save data bank

	lda	#4		; first segment
fillSegsLoop
	cmp	H_NumDSegments
	bge	filledSegs

	sta	MaxFreeSeg
	jsr	CompactSegment	; compact this segment.  sets up SegPtr
	jsr	FillSeg		; fill leftover space.   uses SegPtr
	AddWord	MaxFreeSeg,#4,a
	bra	fillSegsLoop

filledSegs
	bne	@1		; a > H_NSegments (only happens if H_NSegments = 0)
	sta	MaxFreeSeg	; compact last segment
	jsr	CompactSegment
@1

; find segment with most free space - dispose of empty segments (if DisposeSegs)
	stz	MaxFreeSpace

	MoveWord	H_NumDSegments,Segment
	beq	foundMax

findMaxLoop
	tay

	lda	[H_DSegPtrLo],y
	sta	SegPtr
	lda	[H_DSegPtrHi],y
	sta	SegPtr+2

	ldy	#H_SegFreeSpace
	lda	[SegPtr],y
	ldy	Segment

	cmp	MaxFreeSpace
	blt	@1
	sta	MaxFreeSpace
	sty	MaxFreeSeg
@1
  IF VariableDSegSize THEN
	clc
	adc	#DSEG_HDR_SIZE
	ldy	#H_SegSize
	cmp	[SegPtr],y
  ELSE
	cmp	#H_DSEG_SIZE-DSEG_HDR_SIZE
  ENDIF
	blt	nextSeg
	
	lda	>DisposeSegs
	beq	nextSeg

	; dispose of the segment
  IF VariableDSegSize THEN
	ldy	Segment
  ENDIF
	lda	[H_DSegHandleHi],y
	pha
	lda	[H_DSegHandleLo],y
	pha
	_DisposeHandle
	SubWord	H_NumDSegments,#4,H_NumDSegments
	MoveWord	a,[H_HeaderPtr]:#H_NumDSegments
	
nextSeg
	SubWord	Segment,#4,Segment
	bne	findMaxLoop
foundMax

; zero out the data free lists in the header
	ldy	#2*(H_N_FREE_LISTS-1)
	;lda	#0		- a already 0 at foundMax
zeroHdrFreeListLoop
	sta	[H_FreeListArray],y
	dey
	dey
	bpl	zeroHdrFreeListLoop

	plb			; restore data bank

	ProfileOut 6,_H

	clc
	rts

DisposeSegs	DS.W	1

  IF Verbose THEN
SqueezeMesg	str	'Squeezing HeapÉ'
  ENDIF

;-------------------------------------------
FillSeg	; fill the segment whose number is in MaxFreeSeg.
	; segment must already be compacted.
	; while H_SegFreeSpace ³ H_MIN_SQUEEZE
	;   for each segment from H_NumDSegments down to MaxFreeSeg+4
	;     for each block
	;       if size ² H_SegFreeSpace
	;         transfer block

	ldy	#H_SegFreeSpace
	lda	[SegPtr],y
	cmp	#H_MIN_SQUEEZE
	bge	@1
	rts

@1	sta	MaxFreeSpace

	; set data bank = code bank to modify CompactMoveBlock code
	phk
	plb

	; copy SegPtr
	lda	SegPtr+2
	sta	Ptr+2
	lda	SegPtr
	sta	Ptr

	sta	store+1		; store the destination address in
	lda	SegPtr+1		; the code
	sta	store+2

	; setup the store addresses in moveLoop16 (in CompactMoveBlock)
	ldx	#0
	ldy	#8
storeLoop
	lda	Ptr+1
	sta	s0+2,x
	lda	Ptr
	sta	s0+1,x

	clc
	adc	#2
	sta	Ptr
	bcc	@1
	inc	Ptr+2
@1
	AddWord	x,#s2-s0,x
	dey
	bne	storeLoop

	MoveWord	[SegPtr]:#H_SegEnd,x	; offset to the end of data

	ldy	H_NumDSegments		; segment to fetch blocks from
	jsr	SetFetchSegment		; sets Segment, data bank

	ldy	H_NumISegments
	beq	filled
fillLoop
	phy			; save index segment #
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2
	lda	[H_ISegPtrLo],y
	sta	ISegPtr
	cpy	H_NumISegments
	bne	@1
	ldy	H_EndIndex
	bra	fillSegLoop
@1	ldy	#H_ISEG_SIZE

fillSegLoop			; loop through index segment backwards
	dey			; offset to high word of (previous) index
	dey
	lda	[ISegPtr],y
	cmp	Segment
	bne	nextBlock

	; found a block in the fetch segment
	phy

	; is the block small enough to fit in the fill segment?
	dey
	dey
	lda	[ISegPtr],y	; offset to block
	tay
	lda	(OldPtr),y	; data size of block
  IF H_BLOCK_OVERHEAD = 2 THEN
	ina
	ina
  ELSE
	clc
	adc	#H_BLOCK_OVERHEAD
  ENDIF
	; a = block size
	cmp	MaxFreeSpace
	BGT	@1		; too big
	sta	Size		; block size

	lda	1,s		; restore saved y, leave on stack
	tay
	lda	MaxFreeSeg	; set the new segment #
	sta	[ISegPtr],y

	jsr	CompactMoveBlock	; move the block

	; no attempt is made to put the space freed up by moving the block
	; onto a free list, because the segment will be compacted later

	ldy	#H_SegFreeSpace
	lda	[OldPtr],y	; adjust source segment free space
	clc
	adc	Size
	sta	[OldPtr],y

	lda	MaxFreeSpace	; adjust destination segment free space
	sec
	sbc	Size
	sta	MaxFreeSpace

	cmp	#H_MIN_SQUEEZE	; is the segment full enough yet?
	bge	@1		; no - keep going

	; yes - done with segment - clean up stack
	ply
	ply			; index segment #
	bra	doneFillSeg

@1	ply

nextBlock
	dey			; offset to low word of index
	dey
	bne	fillSegLoop

	pla			; index segment #
	SubWord	a,#4,y
	bne	fillLoop
filled


; try next segment until segment ² MaxFreeSeg

	SubWord	Segment,#4,a
	cmp	MaxFreeSeg
	beq	doneFillSeg	; done - no more segments to use

	tay			; segment to fetch from
	jsr	SetFetchSegment	; sets Segment, data bank

	ldy	H_NumISegments
	bne	fillLoop

doneFillSeg
	MoveWord	x,[SegPtr]:#H_SegEnd	; save new offset to end of data
	MoveWord	MaxFreeSpace,[SegPtr]:#H_SegFreeSpace	; new free space
	rts

;------------------------------------------------------
SetFetchSegment

	phx			; save x

	sty	Segment
	lda	[H_DSegPtrHi],y
	sta	OldPtr+2
	lda	[H_DSegPtrLo],y
	sta	OldPtr

	; set data bank = code bank to modify CompactMoveBlock code
	phk
	plb

	; store the source address in the code
	sta	loadSize+1
	sta	load+1

	; make sure we're not too close to the end of a bank
	; if we are, we can't use the loop to move 16 bytes at a time
	cmp	#-16
	blt	@1
	lda	#moveLoop
	bra	@2
@1	lda	#check16
@2	sta	whichLoop+1	; modify the jmp accordingly

	; setup the load addresses in moveLoop16 (in CompactMoveBlock)
	lda	OldPtr
	ldx	#0
	ldy	#8
loadLoop
	sta	l0+1,x
	ina
	ina
	pha
	AddWord	x,#l2-l0,x
	pla
	dey
	bne	loadLoop

	; set data bank = segment
	pei	OldPtr+1	; 2nd & 3rd bytes of address
	plb		; 2nd byte
	plb		; 3rd byte (bank)

	plx		; restore x
	rts

	ENDP
  ENDIF

;¦;--------------------------------------------------------------
; CompactSegment()
;
; Compact the data segment whose number * 4 is in MaxFreeSeg
; by copying each block into a temporary area (D_SystemHandle)
; and then copying it back again

; Zpage variables modified: 
;	ISegPtr
;	Offset
;	OldPtr
;	Ptr
;	SegPtr

CompactSegment	PROC	ENTRY

	ProfileIn 14,_H

	jsl	D_CheckPurge	; verify System Handle is available
	bcc	@9
	rts			; oops - it's not available
@9
  IF Verbose THEN
    IF Verbose > 1 THEN
	Call	D_AlertBox,in=(#OkBox:w,#CompactMesg:l),out=(a:w)
    ELSE
	Call	D_WordBox,in=(#CompactMesg:l)
    ENDIF
  ENDIF
  
	; get SegPtr
	ldy	MaxFreeSeg
	lda	[H_DSegPtrHi],y
	sta	SegPtr+2
	lda	[H_DSegPtrLo],y
	sta	SegPtr

	; check to see if the segment is already compacted
	; i.e. if SegSize - SegEnd - SegFreeSpace = 0
  IF VariableDSegSize THEN
	MoveWord	[SegPtr]:#H_SegSize,a
  ELSE
	lda	#H_DSEG_SIZE
  ENDIF
	SubWord	a,[SegPtr]:#H_SegEnd,a
	SubWord	a,[SegPtr]:#H_SegFreeSpace,a
	bne	@8
	clc
	rts
@8
	phb		; save data bank

	; set the data bank to the code bank to modify code
	phk
	plb

	lda	SegPtr
	sta	loadSize+1	; store the source address in the code
	sta	load+1		; store the source address in the code
	
	; make sure we're not too close to the end of a bank
	; if we are, we can't use the loop to move 16 bytes at a time
	cmp	#-16
	blt	@1
	lda	#moveLoop
	bra	@2
@1	lda	#check16
@2	sta	whichLoop+1	; modify the jmp accordingly

	; setup the load addresses in moveLoop16 (in CompactMoveBlock)
	lda	SegPtr
	ldx	#0
	ldy	#8
loadLoop
	sta	l0+1,x
	ina
	ina
	sta	Ptr
	AddWord	x,#l2-l0,x
	lda	Ptr
	dey
	bne	loadLoop

	lda	>D_SystemHandle
	sta	OldPtr
	lda	>D_SystemHandle+2
	sta	OldPtr+2
	Deref	OldPtr		; leaves result in ax
	sta	Ptr
	stx	Ptr+2
	sta	store+1		; store the destination address in
	lda	OldPtr+1		; the code
	sta	store+2

	; setup the store addresses in moveLoop16 (in CompactMoveBlock)
	ldx	#0
	ldy	#8
storeLoop
	lda	Ptr+1
	sta	s0+2,x
	lda	Ptr
	sta	s0+1,x

	clc
	adc	#2
	sta	Ptr
	bcc	@1
	inc	Ptr+2
@1
	AddWord	x,#s2-s0,x
	dey
	bne	storeLoop

	
	; set the data bank to the segment
	pei	SegPtr+1	; 2nd & 3rd bytes of address
	plb		; 2nd byte
	plb		; 3rd byte (bank)


; traverse the index table (in reverse order) to find all blocks in the segment
; and move each in turn

	ldx	#DSEG_HDR_SIZE	; initialize new data block offset

	ldy	H_NumISegments
	beq	compacted
compactLoop
	sty	Segment
	lda	[H_ISegPtrHi],y
	sta	ISegPtr+2
	lda	[H_ISegPtrLo],y
	sta	ISegPtr
	cpy	H_NumISegments
	bne	@1
	ldy	H_EndIndex
	bra	compactSegLoop
@1	ldy	#H_ISEG_SIZE

compactSegLoop			; loop through index segment backwards
	dey			; offset to high word of (previous) index
	dey
	lda	[ISegPtr],y
	cmp	MaxFreeSeg
	bne	nextBlock

	phy
	jsr	CompactMoveBlock
	ply

nextBlock
	dey			; offset to low word of index
	dey
	bne	compactSegLoop

	SubWord	Segment,#4,y
	bne	compactLoop
compacted


; fix up the segment header
	txa			; x = offset to next block
	ldy	#H_SegEnd		; = end of used portion of segment
	sta	[SegPtr],y
	
	; SegFreeSpace has not changed
	
	; zero the free list
  IF (H_SegFreeList = 0) THEN
 	ldy	#2*(H_N_FREE_LISTS-1)
	lda	#0
zeroFreeListLoop
	sta	[SegPtr],y
	dey
	dey
	bpl	zeroFreeListLoop
  ELSE 
	ldy	#H_SegFreeList
	ldx	#H_N_FREE_LISTS
	lda	#0
zeroFreeListLoop
	sta	[SegPtr],y
	iny
	iny
	dex
	bne	zeroFreeListLoop
  ENDIF

; now copy the compacted data back to the data segment
; _BlockMove(Ptr+DSEG_HDR_SIZE,SegPtr+DSEG_HDR_SIZE,SegSize-#DSEG_HDR_SIZE)
	MoveLong	OldPtr,ax		; OldPtr = [D_SystemHandle]
	clc
	adc	#DSEG_HDR_SIZE
	bcc	@1
	inx
@1	phx
	pha

	MoveLong	SegPtr,ax
	clc
	adc	#DSEG_HDR_SIZE
	bcc	@2
	inx
@2	phx
	pha
  IF VariableDSegSize THEN
	PushWord	#0
	SubWord	[SegPtr]:#H_SegSize,#DSEG_HDR_SIZE,s
  ELSE
	PushLong	#H_DSEG_SIZE-DSEG_HDR_SIZE
  ENDIF
	_BlockMove
	
	plb		; restore data bank

  IF Verbose = 1 THEN
	Call	D_CloseWordBox
  ENDIF
	ProfileOut 14,_H

	clc
	rts

  IF Verbose THEN
CompactMesg	str	'Compacting Heap SegmentÉ'
  ENDIF
  
	ENDP

;¦;--------------------------------------------------------------
; NewDataSegment()
;
; Create a new data segment and initialize its header
; Returns ErrorFlag and sets carry appropriately

; Zpage variables modified:
;	ErrorFlag
;	H_NumDSegments
;	SegHandle
;	SegPtr

NewDataSegment	PROC	ENTRY

  IF Verbose THEN
    IF Verbose > 1 THEN
	Call	D_AlertBox,in=(#OkBox:w,#newSegMesg:l),out=(a:w)
    ELSE
	Call	D_WordBox,in=(#newSegMesg:l)
    ENDIF
	DEBRK
  ENDIF
	lda	H_NumDSegments	; verify there are < H_MaxSegments
	cmp	H_MaxSegments
	blt	@1
	rts			; carry set
@1
	MoveWord	#H_DSEG_SIZE,SegSize
	MoveWord	#0,>SecondTry	; in the code, below
allocSeg
	; allocate the handle for the segment (locked)
	SpaceLong
	PushWord	#0
	PushWord	SegSize
	PushWord	#attrLocked
	ProfileIn 15,_H
	jsl	D_NeedHandle
	sta	ErrorFlag
  IF Profile THEN
	php
	ProfileOut 15,_H
	plp
  ENDIF
	PullLong	SegHandle
  IF ÂVariableDSegSize THEN
	bcs	exit
  ELSE
	bcc	gotSeg

	lda	>SecondTry		; was this the second try?
	bne	retryFailed		; yes - sec and return
	ina
	sta	>SecondTry

	; try _MaxBlock for a smaller segment
	SpaceLong
	_MaxBlock
	PullLong	ax		; x must be 0, else first allocation
	cmp	#H_MIN_DSEG_SIZE	;	would have worked
	bge	smallSeg
retryFailed
	sec
	rts
smallSeg
	sta	SegSize
	bra	allocSeg
gotSeg
  ENDIF
	; increment the # of segments and store the handle in the header
	AddWord	H_NumDSegments,#4,H_NumDSegments	; result left in a
	ldy	#H_NumDSegments
	sta	[H_HeaderPtr],y
	tay
	lda	SegHandle
	sta	[H_DSegHandleLo],y
	lda	SegHandle+2
	sta	[H_DSegHandleHi],y

	; deref the handle and store the pointer in the header
	lda	[SegHandle]
	sta	SegPtr
	sta	[H_DSegPtrLo],y
  IF ZeroInit THEN
	tax
  ENDIF
	ldy	#2
	lda	[SegHandle],y
	ldy	H_NumDSegments
	sta	SegPtr+2
	sta	[H_DSegPtrHi],y

	; initialize the data segment header
  IF ZeroInit THEN
	pha
	phx
	PushWord	SegSize
	jsl	D_ZeroBlock
  ELSE
    IF (H_SegFreeList = 0) THEN
 	ldy	#2*(H_N_FREE_LISTS-1)
	lda	#0
zeroFreeListLoop
	sta	[SegPtr],y
	dey
	dey
	bpl	zeroFreeListLoop
    ELSE 
	ldy	#H_SegFreeList
	ldx	#H_N_FREE_LISTS
	lda	#0
zeroFreeListLoop
	sta	[SegPtr],y
	iny
	iny
	dex
	bne	zeroFreeListLoop
    ENDIF
  ENDIF

	MoveWord	#DSEG_HDR_SIZE,[SegPtr]:#H_SegEnd
	SubWord	SegSize,#DSEG_HDR_SIZE,[SegPtr]:#H_SegFreeSpace

  IF &Type('H_SegSize') ­ 'UNDEFINED ' THEN
	MoveWord	SegSize,[SegPtr]:#H_SegSize
  ENDIF
  IF &Type('H_SegNumBlocks') ­ 'UNDEFINED ' THEN
	MoveWord	#0,[SegPtr]:#H_SegNumBlocks
  ENDIF

  IF Verbose = 1 THEN
	Call	D_CloseWordBox
  ENDIF
	clc
exit
	rts

SecondTry	DC.W	0

  IF Verbose THEN
newSegMesg	str	'NewBlock: allocating new segment'
  ENDIF

	ENDP

;¦;--------------------------------------------------------------
; VerifyIndex()
;
; Verify that the index in xy is valid.  Put up an alert if it's not.
;

  IF ErrorCheck THEN

VerifyIndex	PROC	ENTRY

	cpy	H_NumISegments
	beq	@1
	bge	badindexError
	cpx	#H_ISEG_SIZE
	bra	@2
@1	cpx	H_EndIndex
@2	bge	badIndexError
	txa
	and	#%11
	bne	oddIndexError
	tya
	beq	@3
	and	#%11
	bne	oddIndexError

@ok	clc
	rts

@3	txa
	beq	@ok
zeroIndexError
	SpaceWord
	PushWord	#OkBox
	PushLong	#ziMesg
	bra	error

badIndexError
	SpaceWord
	PushWord	#OkBox
	PushLong	#biMesg
	bra	error

oddIndexError
	SpaceWord
	PushWord	#OkBox
	PushLong	#oiMesg
error
	txa
	sta	>theIndex
	tya
	sta	>theIndex+2

	jsl	D_AlertBox
	pla
	DEBRK2
	sec
	rts

theIndex	DS.L	1
ziMesg	str	'H_VerifyIndex: Index 00xx'
biMesg	str	'H_VerifyIndex: Index out of range!'
oiMesg	str	'H_VerifyIndex: Misaligned Index!'

	ENDP
  ENDIF


	END
