; ; 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