; ; Heap.aii ; ; Heap memory managment module ; ; Copyright © 1989 Claris Corporation ; ; 1-13-89 Begun by Kevin A. Watts ; ; Heap routines: ; ; H_Init() ; Initialize heap code when loaded. ; H_SegLoaded() ; Called when the heap code is loaded. ; H_SegUnloaded() ; Called when the heap code is unloaded. ; H_CurrentHeap(): HeapID:ax ; Return the ID of the current heap. ; H_SetHeap(HeapID:xy) ; Set the current heap. ; H_UnsetHeap() ; Unset the current heap. ; H_ResetHeap() ; Reset (relock) the current heap. ; H_NewHeap(): HeapID:ax ; Create a new heap, make it current & return its ID. ; H_DisposeHeap() ; Dispose of the current heap. ; H_WriteHeap(FileID:x) ; Write the current heap to disk. ; H_ReadHeap(FileID:x): HeapID:ax ; Read the current heap from disk. ; CalcHeaderPtrs ; Calculate pointers to arrays within the header. ;-------------------------------------------------------------- PRINT PUSH PRINT OFF LOAD 'Macros.Dump' gblc &__FILE__ &__FILE__ setc 'Heap' include 'Heap.mac' include 'HeapPrivate.equ' include 'Heap.aii.i' include 'm16.profile' ; must be after H.equ include 'Driver.equ' IMPORT D_Deref ENTRY H_Init IF DriverSupport = 0 THEN ENTRY H_SegLoaded ENTRY H_SegUnloaded ENDIF IF ErrorCheck THEN ENTRY H_CurrentHeap ENDIF ENTRY H_SetHeap ENTRY H_UnsetHeap ENTRY H_ResetHeap ENTRY H_NewHeap ENTRY H_DisposeHeap ENTRY H_ReadHeap ENTRY H_WriteHeap ENTRY CalcHeaderPtrs PRINT POP ProfileData 16,_H ProfileCode _H ;¦;-------------------------------------------------------------- ; H_Init() ; no error status returned ; ; Store the address of the heap zero page into the other heap routines ; prologue. ; THIS ROUTINE MUST BE CALLED BEFORE ANY OTHER HEAP ROUTINE IS CALLED!! ; i.e. immediately after the code segment is loaded. ; Zpage variables modified: ; H_ZPptr H_Init PROC EXPORT ProfileInit _H ; get the Zpage address from the driver global IMPORT D_HeapZPage lda >D_HeapZPage phd ; store the Zpage address in the Zpage tcd ; set up the Zpage sta H_ZPptr stz H_ZPptr+2 ; redundant, but so what? ; determine the maximum possible number of data or index segments and thus header size. ; we allow the same # of each type of segment, equal to ; _TotalMem / H_DSEG_SIZE ; H_MaxSegments will be 3 times this (3/4, *4 bytes/segment handle). ; The 3/4 is based upon H_ISEG_SIZE = 1/4 H_DSEG_SIZE, and will allow the heap ; to use 15/16 of _TotalMem. This should be sufficient. SpaceLong _TotalMem pla ; discard low word pla ; # of banks sta H_MaxSegments ; multiply by 3 asl a asl a sec sbc H_MaxSegments MulByPwr2 a,($10000/H_DSEG_SIZE) ; # of banks * (64K/H_DSEG_SIZE) cmp #$1000 ; ensure high nibble unused blt @1 lda #$1000-4 @1 sta H_MaxSegments lda #HANDLE_ARRAY ; offset to **index_seg[] clc adc H_MaxSegments sta H_DSegHOffsetLo ; offset to **data_seg[] tax inx inx stx H_DSegHOffsetHi clc adc H_MaxSegments sta H_ISegPOffsetLo ; offset to *index_seg[] tax inx inx stx H_ISegPOffsetHi clc adc H_MaxSegments sta H_DSegPOffsetLo ; offset to *data_seg[] tax inx inx stx H_DSegPOffsetHi clc adc H_MaxSegments sta H_HeaderSize ; total size of heap header tdc ; load accumulator with Heap Zpage pld ; embed the ZP address in the other routines ; DefineZ will correctly modify any routine using BeginZ as a prologue. ; set data bank to code bank phb phk plb IF ErrorCheck THEN DefineZ H_CurrentHeap ENDIF DefineZ H_SetHeap DefineZ H_UnsetHeap DefineZ H_ResetHeap DefineZ H_NewHeap DefineZ H_DisposeHeap IF HeapIO AND 1 THEN DefineZ H_WriteHeap ENDIF IF HeapIO AND 2 THEN DefineZ H_ReadHeap ENDIF DefineZ H_TruncHeap DefineZ H_NewBlock DefineZ H_DisposeBlock DefineZ H_ResizeBlock DefineZ H_CopyBlock DefineZ H_GetBlockPtr DefineZ H_GetBlockSize plb ; restore data bank rtl ENDP ;¦;-------------------------------------------------------------- IF DriverSupport = 0 THEN ; H_SegLoaded() ; errors returned in accumulator (via ErrFlag) ; ; Allocate a zero page block for later use and store its address ; in the other routines so they can use it. ; THIS ROUTINE MUST BE CALLED BEFORE ANY OTHER HEAP ROUTINE IS CALLED!! ; ; Eventually, the driver should allocate the zpage for me and store the ; address in a global. Only the code modification portion of this routine ; need remain then. The driver will have to zero the block for me. ; Zpage variables modified: all (zero page cleared) H_SegLoaded PROC EXPORT local Zpage:l ; handle/pointer to Zero page error ErrFlag BEGIN +b Call D_NeedHandle,in=(#H_ZPSIZE:l,#H_ZPATTR:w),out=(Zpage:l),err=ErrFlag bcc @ok Call D_AlertBox,in=(#OkBox:w,#ErrorMesg:l),out=(a:w) bra exit @ok ; Zero the zero page block Call D_ZeroBlock,in=([Zpage]:l,#H_ZPSIZE:w) ; Embed the ZP handle in H_SegUnload so we can dispose of it later MoveWord Zpage+2,H_SegUnloaded+1 MoveWord Zpage,H_SegUnloaded+4 ; Now embed the ZP address in the other routines Deref Zpage ; Zpage = [Zpage] ; lda Zpage ; already there after Deref sta D_HeapZPage jsl H_Init exit return EXPORT D_HeapZPage D_HeapZPage DS.W 1 ErrorMesg str 'H_SegLoaded: Unable to allocate heap Z page' ENDP ;¦;-------------------------------------------------------------- ; H_SegUnloaded() ; no error status returned ; ; Deallocate the zero page block. ; NO OTHER HEAP ROUTINE (except H_SegLoaded) MAY BE CALLED AFTER THIS ROUTINE!! ; It is assumed that there is no current heap when this routine is called. ; Zpage variables modified: all (Zpage disposed) H_SegUnloaded PROC EXPORT pea 0000 ; Replace with the ZP handle (high word) pea 0000 ; Replace with the ZP handle (low word) _DisposeHandle rtl ENDP ENDIF ; DriverSupport = 0 ;¦;-------------------------------------------------------------- IF ErrorCheck THEN ; H_CurrentHeap(): HeapID:ax ; ; return the ID of the current heap ; Zpage variables modified: none H_CurrentHeap PROC EXPORT BeginZ MoveLong H_Header,ax ReturnZ ENDIF ; ErrorCheck ;¦;-------------------------------------------------------------- ; H_SetHeap(HeapID:xy) ; no error status returned ; ; Set the current heap: lock all its handles and copy its header information to the heap ZP. ; Zpage variables modified: ; NewHeap ; H_* ; CopyHdr ; HasMoved ; Offset ; SegHandle H_SetHeap PROC EXPORT BeginZ MoveLong xy,NewHeap ; save heap id (handle) IF Profile THEN ProfileIn 1,_H ldx NewHeap ENDIF ;CmpLong xy,H_Header ; is this already the current heap? cpy H_Header+2 bne @1 cpx H_Header beq @reSet @1 MoveWord #1,CopyHdr lda H_Header ; is there a current heap? ora H_Header+2 beq setHeap jsl H_UnsetHeap ; first unset the current heap bra setHeap @reSet ; trying to reSet the current heap lda H_IsLocked ; if it's already locked, we're done. beq @2 ReturnZ @2 stz CopyHdr setHeap lda NewHeap ora NewHeap+2 bne @notZero StzL H_Header ReturnZ @notZero IF ErrorCheck THEN PushLong NewHeap _CheckHandle bcc handleOk Call D_AlertBox,in=(#OkBox:w,#badHandleMesg:l),out=(a:w) DEBRK2 badHandleMesg str 'H_SetHeap: Bad Heap ID!' handleOk ENDIF ;MoveLong NewHeap,H_Header ; lock the heap header ;PushLong H_Header - combined for optimization lda NewHeap+2 sta H_Header+2 pha lda NewHeap sta H_Header pha _HLock ;MoveLong [H_Header],H_HeaderPtr - deref; leave result in ax ldy #2 lda [H_Header],y sta H_HeaderPtr+2 tax lda [H_Header] sta H_HeaderPtr ; find out if the heap header has moved by comparing the new ; pointer (in ax) to the old one (in the header) ; if it has not moved, we can avoid resetting pointers ; if it has moved, we need to copy the new pointer back to the header stz HasMoved ; ax = H_HeaderPtr ldy #H_HeaderPtr cmp [H_HeaderPtr],y beq @1 sta [H_HeaderPtr],y inc HasMoved @1 txa ldy #H_HeaderPtr+2 cmp [H_HeaderPtr],y beq @2 sta [H_HeaderPtr],y inc HasMoved @2 lda CopyHdr ; copy the header block if necessary beq doneCopy PushLong H_HeaderPtr PushLong H_ZPptr PushWord #0 lda HasMoved beq @3 PushWord #ZHEADER_SHORT_SIZE ; moved - don't copy ptrs; will recalc bra @4 @3 PushWord #ZHEADER_SIZE ; didn't move - copy ptrs @4 _BlockMove doneCopy ; if the heap header has moved since it was last set, ; reset pointers to arrays of data segment handles and pointers ; and to array of free lists lda HasMoved beq @noUpdatePointers jsr CalcHeaderPtrs @noUpdatePointers ; lock and deref the index segments - this loop starts at the end and counts down ldy H_NumISegments ; y = offset to last seg handle beq lockedISegs lockISegLoop ; lock the segment lda [H_ISegHandleHi],y sta SegHandle+2 pha lda [H_ISegHandleLo],y sta SegHandle pha sty Offset _HLock ; should I do a quick lock here? ; deref the locked handle and store the pointer into H_ISegPointers array ldy #2 lda [SegHandle],y ldy Offset sta [H_ISegPtrHi],y lda [SegHandle] sta [H_ISegPtrLo],y SubWord y,#4,y bne lockISegLoop ; there is no seg #0 lockedISegs ; lock and deref the data segments - this loop starts at the end and counts down ldy H_NumDSegments ; y = offset to last seg handle beq lockedDSegs lockDSegLoop ; lock the segment lda [H_DSegHandleHi],y sta SegHandle+2 pha lda [H_DSegHandleLo],y sta SegHandle pha sty Offset _HLock ; should I do a quick lock here? ; deref the locked handle and store the pointer into H_DSegPointers array ldy #2 lda [SegHandle],y ldy Offset sta [H_DSegPtrHi],y lda [SegHandle] sta [H_DSegPtrLo],y SubWord y,#4,y bne lockDSegLoop ; there is no seg #0 lockedDSegs MoveWord #1,H_IsLocked ; flag the heap as locked ProfileOut 1,_H ReturnZ ENDP ;¦;-------------------------------------------------------------- ; H_UnsetHeap() ; no error status returned ; ; Unset the current heap: copy its header information back from ; the heap ZP and unlock its handles ; ; Prerequisite: H_IsLocked ­ 0, H_Header ­ 0 (these are checked) ; Zpage variables modified: ; Offset ; H_IsLocked H_UnsetHeap PROC EXPORT BeginZ ProfileIn 2,_H ; if the current heap is not locked then there's nothing to do. lda H_IsLocked JEQ exit ; make sure there is a current heap lda H_Header ora H_Header+2 JEQ exit ; copy selected header information back to real header ; H_Header - always maintained ; H_HeaderPtr - always maintained ; H_Version - always maintained (constant) ; H_NumISegments - always maintained ; H_NumDSegments - always maintained MoveLong H_FreeIndex,[H_HeaderPtr]:#H_FreeIndex MoveLong H_EndIndex,[H_HeaderPtr]:#H_EndIndex MoveLong H_ISegHandleLo,[H_HeaderPtr]:#H_ISegHandleLo MoveLong H_ISegHandleHi,[H_HeaderPtr]:#H_ISegHandleHi MoveLong H_DSegHandleLo,[H_HeaderPtr]:#H_DSegHandleLo MoveLong H_DSegHandleHi,[H_HeaderPtr]:#H_DSegHandleHi MoveLong H_ISegPtrLo,[H_HeaderPtr]:#H_ISegPtrLo MoveLong H_ISegPtrHi,[H_HeaderPtr]:#H_ISegPtrHi MoveLong H_DSegPtrLo,[H_HeaderPtr]:#H_DSegPtrLo MoveLong H_DSegPtrHi,[H_HeaderPtr]:#H_DSegPtrHi MoveLong H_FreeListArray,[H_HeaderPtr]:#H_FreeListArray ; unlock the handles ; unlock the index segments - this loop starts at the end and counts down ldy H_NumISegments ; y = offset to last seg handle beq unlockedISegs unlockISegLoop ; unlock the segment lda [H_ISegHandleHi],y pha lda [H_ISegHandleLo],y pha sty Offset _HUnlock ; should I do a quick unlock here? SubWord Offset,#4,y bne unlockISegLoop ; there is no seg #0 unlockedISegs ; unlock the data segments - this loop starts at the end and counts down ldy H_NumDSegments ; y = offset to last seg handle beq unlockedDSegs unlockDSegLoop ; unlock the segment lda [H_DSegHandleHi],y pha lda [H_DSegHandleLo],y pha sty Offset _HUnlock ; should I do a quick unlock here? SubWord Offset,#4,y bne unlockDSegLoop ; there is no seg #0 unlockedDSegs PushLong H_Header ; unlock the heap header _HUnlock stz H_IsLocked ; flag heap as unlocked exit ProfileOut 2,_H ReturnZ ENDP ;¦;-------------------------------------------------------------- ; H_ResetHeap() ; no error status returned ; ; Reset (actually only relock) the current heap, if necessary ; ; Prerequisite: H_IsLocked = 0, H_Header ­ 0 ; Zpage variables modified: none, H_SetHeap() H_ResetHeap PROC EXPORT BeginZ lda H_IsLocked bne exit lda H_Header ora H_Header+2 beq exit ; make sure the top level call was made by a heap client module ; (SS or DB) ; lda D_TopModule ; cmp ??? ; beq @1 ; cmp ??? ; bne exit ;@1 MoveLong H_Header,xy jsl H_SetHeap exit ReturnZ ENDP ;¦;-------------------------------------------------------------- ; H_NewHeap(): HeapID:ax ; error status returned in y ; ; Allocate a new heap and return the handle to the heap header as the ID. ; HeapID is invalid if any error occurs. ; Zpage variables modified: ; H_* ; NewHeap ; ErrorFlag H_NewHeap PROC EXPORT BeginZ ProfileIn 4,_H ; unset the current heap, if any lda H_Header ora H_Header+2 beq noCurHeap jsl H_UnsetHeap StzL H_Header noCurHeap ; allocate the header handle ; no index or data segments are allocated yet - that's done by H_NewBlock (NewBlock,NewIndex) ; don't use H_Header for the result until we've successfully allocated the handle SpaceLong PushWord #0 PushWord H_HeaderSize jsl D_NeedHand sta ErrorFlag PullLong NewHeap bcc lockHeader stz H_IsLocked ; no heap, so not locked IF VerboseErrors THEN Call D_AlertBox,in=(#OkBox:w,#NoHeapMesg:l),out=(a:w) DEBRK3 ENDIF lda #0 ; return 0 HeapID tax ldy ErrorFlag ; return error in y sec ReturnZ lockHeader ; lock the header handle ;MoveLong NewHeap,H_Header - combined ;PushLong H_Header - for efficiency lda NewHeap+2 sta H_Header+2 pha lda NewHeap sta H_Header pha _HLock ; copy and deref handles and other header info MoveLong [H_Header],H_HeaderPtr IF H_THIS_VERSION ² $FFFF THEN ; MoveLong should do this for me stz H_Version+2 MoveWord #H_THIS_VERSION,H_Version ELSE MoveLong #H_THIS_VERSION,H_Version ENDIF StzL H_FreeIndex ; no free indices yet stz H_EndIndex ; no index segment yet ; set pointers to arrays of data segment handles and pointers ; and to array of free lists jsr CalcHeaderPtrs IF ZeroInit THEN ; zero header block PushLong H_HeaderPtr PushWord H_HeaderSize jsl D_ZeroBlock ELSE ; zero out the data free lists in the header ldy #2*(H_N_FREE_LISTS-1) lda #0 zeroFreeListLoop sta [H_FreeListArray],y dey dey bpl zeroFreeListLoop ENDIF ; no index or data segments yet stz H_NumISegments stz H_NumDSegments ; copy the zero page to the header PushLong H_ZPptr PushLong H_HeaderPtr PushLong #ZHEADER_SIZE _BlockMove ProfileOut 4,_H ldy #1 sty H_IsLocked ; flag the heap as locked MoveLong H_Header,ax ; return the heap id dey ; no error (y = 0) clc ReturnZ IF VerboseErrors THEN NoHeapMesg str 'H_NewHeap: Unable to allocate new heap' ENDIF ENDP ;¦;-------------------------------------------------------------- ; H_DisposeHeap() ; no error status returned ; ; Dispose of the current heap ; ; Prerequisite: H_Header, H_HeaderPtr ­ 0 ; Zpage variables modified: ; Offset ; H_Header ; H_HeaderPtr ; H_IsLocked ; Segment ; SegHandle ; SegPtr H_DisposeHeap PROC EXPORT BeginZ ProfileIn 5,_H ; dispose of all large blocks and index segments ; - we are traversing the index segments backwards ldy H_NumISegments beq disposedIndices disposeIndicesLoop sty Segment lda [H_ISegHandleHi],y ; check that the segment is really there tax ; (x for D_Deref below) ora [H_ISegHandleLo],y ; (might not be if recovering from beq nextISeg ; failed H_ReadHeap) ; don't get ptr from table - won't be there if called from H_ReadHeap lda [H_ISegHandleLo],y ; x was set above jsl D_Deref MoveLong ax,SegPtr CmpWord Segment,H_NumISegments bne @1 ldy H_EndIndex bra disposeLargeLoop @1 ldy #H_ISEG_SIZE disposeLargeLoop ; loop through index segment backwards dey ; offset to high word of (previous) index dey lda [SegPtr],y bpl nextLargeBlock cmp #H_LARGE_BLOCK_FLAG blt nextLargeBlock sty Offset and #$00FF pha dey ; offset to low word of index dey lda [SegPtr],y pha _DisposeHandle ldy Offset nextLargeBlock dey ; offset to low word of index dey bne disposeLargeLoop ; now dispose of the index segment ldy Segment lda [H_ISegHandleHi],y pha lda [H_ISegHandleLo],y pha _DisposeHandle nextISeg SubWord Segment,#4,y bne disposeIndicesLoop disposedIndices ; dispose of the data segments - this loop starts at the end and counts down ldy H_NumDSegments ; y = offset to last seg handle beq disposedDSegs disposeDSegLoop ; unlock the segment sty Segment lda [H_DSegHandleHi],y sta SegHandle+2 lda [H_DSegHandleLo],y sta SegHandle ora SegHandle+2 beq @next ; no handle here PushLong SegHandle _DisposeHandle @next SubWord Segment,#4,y bne disposeDSegLoop ; there is no seg #0 disposedDSegs disposeHdr PushLong H_Header ; dispose of the heap header _DisposeHandle StzL H_Header ; clear the current heap id StzL H_HeaderPtr ; and pointer stz H_IsLocked ; and flag as unlocked ProfileOut 5,_H ReturnZ ENDP ;¦;-------------------------------------------------------------- ; H_WriteHeap(FileID:x) ; ; Write the current heap to disk. ; Writes to the (currently open) file indicated by FileID. ; Format of heap as written to disk: ; Header - First 4 bytes are 'Heap' ; Index Segment H_NumISegments ; : ; : ; Index Segment 4 ; Data Segment H_NumDSegments ; : ; : ; Data Segment 4 ; Last large block (last in last index segment) ; : ; : ; First large block (first in first index segment) ; Zpage variables modified: ; DP ; Segment ; SegPtr ; Offset H_WriteHeap PROC EXPORT IF HeapIO AND 1 THEN BeginZ phx ; save the FileID ; squeeze heap down to minimum size jsl H_TruncHeap plx stx DP ; save the FileID in DP ; copy selected header information back to real header ; H_Header - always maintained ; H_HeaderPtr - always maintained ; H_Version - always maintained (constant) ; H_NumISegments - always maintained ; H_NumDSegments - always maintained MoveLong H_FreeIndex,[H_HeaderPtr]:#H_FreeIndex MoveWord H_EndIndex,[H_HeaderPtr]:#H_EndIndex ; H_ISegHandleLo - don't care here ; H_ISegHandleHi - don't care here ; H_DSegHandleLo - don't care here ; H_DSegHandleHi - don't care here ; H_ISegPtrLo - don't care here ; H_ISegPtrHi - don't care here ; H_DSegPtrLo - don't care here ; H_DSegPtrHi - don't care here ; H_FreeListArray - don't care here ; punch a fake value into the header to facilitate ; human finding of saved heaps IF H_Header = 0 THEN MoveLong #'paeH',[H_HeaderPtr] ; that's 'Heap' ELSE MoveLong #'paeH',[H_HeaderPtr]:#H_Header ENDIF ; write out header PushWord DP PushLong H_Header jsl D_WriteHandle2 php pha ; restore proper header value IF H_Header = 0 THEN MoveLong H_Header,[H_HeaderPtr] ELSE MoveLong H_Header,[H_HeaderPtr]:#H_Header ENDIF pla plp jcs exit writeDSegs ; write out data segments (first to last) lda H_NumDSegments beq writeISegs lda #0 writeDSegLoop AddWord a,#4,y sty Segment PushWord DP lda [H_DSegHandleHi],y pha lda [H_DSegHandleLo],y pha jsl D_WriteHandle2 bcs exit lda Segment cmp H_NumDSegments blt writeDSegLoop writeISegs ; write out index segments (last to first) lda H_NumISegments beq writeLarge tay writeISegLoop sty Segment PushWord DP lda [H_ISegHandleHi],y pha lda [H_ISegHandleLo],y pha jsl D_WriteHandle2 bcs exit SubWord Segment,#4,y bne writeISegLoop ; write out all large blocks - we are traversing the index segments backwards writeLarge ldy H_NumISegments beq writtenLargeBlocks writeBlockLoop sty Segment lda [H_ISegPtrHi],y sta SegPtr+2 lda [H_ISegPtrLo],y sta SegPtr cpy H_NumISegments bne @1 ldy H_EndIndex bra writeLargeLoop @1 ldy #H_ISEG_SIZE writeLargeLoop ; loop through index segment backwards dey ; offset to high word of (previous) index dey lda [SegPtr],y bpl nextLargeBlock cmp #H_LARGE_BLOCK_FLAG blt nextLargeBlock pei DP sty Offset and #$00FF pha dey ; offset to low word of index dey lda [SegPtr],y pha jsl D_WriteHandle2 bcs exit ldy Offset nextLargeBlock dey ; offset to low word of index dey bne writeLargeLoop SubWord Segment,#4,y bne writeBlockLoop writtenLargeBlocks lda #0 ; no error clc exit ; carry already set if we came here on a error tay ; return errors in y ReturnZ ELSE ldy #-1 sec rtl ENDIF ; HeapIO ENDP ;¦;-------------------------------------------------------------- ; H_ReadHeap(FileID:x): HeapID:ax ; ; Read a heap from disk, make it the current heap, and return the heap id. ; Reads from the (currently open) file indicated by FileID. ; The old current heap will be unset, even if an error occurs. ; Zpage variables modified: ; H_Header ; H_IsLocked ; H_HeaderPtr ; H_FreeIndex ; H_EndIndex ; H_NumISegments ; H_NumDSegments ; ErrorFlag ; = Version Flag ; DP ; = FileID ; NewHeap ; Ptr ; DataSize ; SegHandle ; SegPtr ; Segment ; OldPtr ; H_SetHeap() ; H_UnsetHeap() ; H_DisposeHeap() ; CalcHeaderPtrs() H_ReadHeap PROC EXPORT IF HeapIO AND 2 THEN BeginZ stx DP ; save the FileID ; unset the current heap, if any lda H_Header ora H_Header+2 beq noCurHeap jsl H_UnsetHeap StzL H_Header stz H_IsLocked noCurHeap ; read in the heap header - update header & Z page SpaceLong PushWord DP jsl D_ReadHandle2 PullLong NewHeap ; don't use H_Header in case H_UnsetHeap is called bcc @headerOk1 tay ; return errors in y sec ReturnZ @headerOk1 ; check header 'signature' MoveLong [NewHeap],Ptr IF H_Header = 0 THEN CmpLong #'paeH',[Ptr] ELSE CmpLong #'paeH',[Ptr]:#H_Header ENDIF beq @headerOk2 lda #H_BAD brl errorHeader @headerOk2 SpaceLong PushLong NewHeap _GetHandleSize PullLong OldSize beq @headerOk3 ; size must be < 1 bank lda #H_BAD brl errorHeader @headerOk3 PushLong [NewHeap] ; update the Z page PushLong H_ZPptr PushLong #ZHEADER_SHORT_SIZE _BlockMove StzL H_Header IF ReadV70 THEN stz ErrorFlag ENDIF CmpLong H_Version,#H_THIS_VERSION ; check version beq @headerVersionOk IF ReadV70 THEN inc ErrorFlag ; not the latest version CmpLong H_Version,#H_VERSION7 beq @headerVersionOk ENDIF blt @oldVersion lda #H_TOO_NEW ; the heap is too new to load brl errorHeader @oldVersion ; the heap is too old to load lda #H_TOO_OLD brl errorHeader @headerVersionOk IF ReadV70 THEN lda ErrorFlag beq @newVersion lda # 0 since DataSize > 0 readBlockLoop sty Segment ; recalculate H_ISegHandleLo (Header may have moved, sigh) ;AddLong [NewHeap],#HANDLE_ARRAY,H_ISegHandleLo MoveLong [NewHeap],ax clc adc #HANDLE_ARRAY sta H_ISegHandleLo txa bcc @2 ina @2 sta H_ISegHandleLo+2 ldy Segment lda [H_ISegHandleLo],y sta SegHandle iny ; not using H_ISegHandleLo because I don't want iny ; to recalculate it lda [H_ISegHandleLo],y sta SegHandle+2 MoveLong [SegHandle],SegPtr ldy Segment cpy H_NumISegments bne @1 ldy H_EndIndex bra readLargeLoop @1 ldy #H_ISEG_SIZE readLargeLoop dey ; offset to high word of (previous) index dey lda [SegPtr],y bpl nextLargeBlock ; not a large block cmp #H_LARGE_BLOCK_FLAG2 blt nextLargeBlock ; not a large block sty Offset SpaceLong PushWord DP jsl D_ReadHandle2 ; could move H_IndexTable - recalc ptr tay ; save error PullLong OldPtr ; handle to large block bcs error ; Index segment may have moved - recalculate SegPtr MoveLong [SegHandle],SegPtr ; store large block handle in index table lda OldPtr+2 ; handle to large block ora #H_LARGE_BLOCK_FLAG ; set flag to H_LARGE_BLOCK_FLAG ldy Offset sta [SegPtr],y dey dey lda OldPtr ; handle to large block sta [SegPtr],y DecL DataSize lda DataSize ora DataSize+2 beq lockHandles ; no more large blocks ldy Offset nextLargeBlock dey ; offset to low word of index dey bne readLargeLoop SubWord Segment,#4,y jne readBlockLoop lockHandles ; lock handles down and fix up pointers MoveLong [NewHeap],H_HeaderPtr IF H_Header = 0 THEN MoveLong NewHeap,[H_HeaderPtr] ; store the heap id (handle) ELSE MoveLong NewHeap,[H_HeaderPtr]:#H_Header ; store the heap id (handle) ENDIF ;StzL [H_HeaderPtr]:#H_HeaderPtr ; force recalc of pointers lda #0 ; within header ldy #H_HeaderPtr sta [H_HeaderPtr],y ldy #H_HeaderPtr+2 sta [H_HeaderPtr],y MoveLong NewHeap,xy jsl H_SetHeap MoveLong H_Header,ax ; return HeapID ldy #0 ; no error clc ReturnZ errorHeader ; error after header loaded pha ; save error code from D_ReadHandle2 PushLong NewHeap _DisposeHandle bra errExit error phy ; save error code from D_ReadHandle2 MoveLong NewHeap,H_Header MoveLong [NewHeap],H_HeaderPtr jsr CalcHeaderPtrs ; make sure ptrs are good for H_DisposeHeap jsl H_DisposeHeap ; dispose of what portions we did read errExit lda #0 ; return 0 HeapID tax ply ; restore error code sec ReturnZ ELSE ldy #-1 sec rtl ENDIF ; HeapIO ENDP ;¦;-------------------------------------------------------------- ; CalcHeaderPtrs() ; ; Calculate pointers to arrays within the header. ; Zpage variables modified: ; H_ISegHandleLo ; H_ISegHandleHi ; H_DSegHandleLo ; H_DSegHandleHi ; H_ISegPtreLo ; H_ISegPtrHi ; H_DSegPtreLo ; H_DSegPtrHi ; H_FreeListArray CalcHeaderPtrs PROC ENTRY ;MoveLong H_HeaderPtr,xy ldx H_HeaderPtr ldy H_HeaderPtr+2 ; Array of free list heads ;AddLong H_HeaderPtr,#H_FreeLists:w,H_FreeListArray txa clc adc #H_FreeLists sta H_FreeListArray tya bcc @fl ina @fl sta H_FreeListArray+2 ; Array of handles to index segments ;AddLong H_HeaderPtr,#HANDLE_ARRAY:w,H_ISegHandleLo txa clc adc #HANDLE_ARRAY sta H_ISegHandleLo tya bcc @iHlo ina @iHlo sta H_ISegHandleLo+2 ;AddLong H_HeaderPtr,#HANDLE_ARRAY+2:w,H_ISegHandleHi txa clc adc #HANDLE_ARRAY+2 sta H_ISegHandleHi tya bcc @iHhi ina @iHhi sta H_ISegHandleHi+2 ; Array of handles to data segments ;AddLong H_HeaderPtr,H_DSegHOffsetLo:w,H_DSegHandleLo txa clc adc H_DSegHOffsetLo sta H_DSegHandleLo tya bcc @dHlo ina @dHlo sta H_DSegHandleLo+2 ;AddLong H_HeaderPtr,H_DSegHOffsetHi:w,H_DSegHandleHi txa clc adc H_DSegHOffsetHi sta H_DSegHandleHi tya bcc @dHhi ina @dHhi sta H_DSegHandleHi+2 ; Array of pointers to index segments ;AddLong H_HeaderPtr,H_ISegPOffsetLo:w,H_ISegPtrLo txa clc adc H_ISegPOffsetLo sta H_ISegPtrLo tya bcc @iPlo ina @iPlo sta H_ISegPtrLo+2 ;AddLong H_HeaderPtr,H_ISegPOffsetHi:w,H_ISegPtrHi txa clc adc H_ISegPOffsetHi sta H_ISegPtrHi tya bcc @iPhi ina @iPhi sta H_ISegPtrHi+2 ; Array of pointers to data segments ;AddLong H_HeaderPtr,H_DSegPOffsetLo:w,H_DSegPtrLo txa clc adc H_DSegPOffsetLo sta H_DSegPtrLo tya bcc @dPlo ina @dPlo sta H_DSegPtrLo+2 ;AddLong H_HeaderPtr,H_DSegPOffsetHi:w,H_DSegPtrHi txa clc adc H_DSegPOffsetHi sta H_DSegPtrHi tya bcc @dPhi ina @dPhi sta H_DSegPtrHi+2 rts ENDP END