mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-25 09:30:50 +00:00
453 lines
18 KiB
Plaintext
453 lines
18 KiB
Plaintext
;
|
||
; File: MemoryMgrExtensions.a
|
||
;
|
||
; Contains: New routines for the Memory Mgr.
|
||
;
|
||
; Written by: A bunch of people. Merged into this file by Tim Nichols
|
||
;
|
||
; Copyright: © 1990-1993 by Apple Computer, Inc., all rights reserved.
|
||
;
|
||
; Change History (most recent first):
|
||
;
|
||
; <SM4> 6/14/93 kc Roll in Ludwig.
|
||
; <LW2> 5/11/93 chp MoveHLow was depending on the CCR to indicate error conditions
|
||
; reported by _RecoverHandle. This weirdass trap only returns its
|
||
; result in MemErr, so insert an explicit test to avoid crashing
|
||
; in the subsequent _HGetState. Partial fix for RADAR #1081681.
|
||
; <SM3> 10/22/92 CSS Change some branch short instructions to word branches.
|
||
; <SM2> 7/10/92 RLM add CycloneTranslate24To32
|
||
; <1> 4/8/92 tcn New today. Code from MemoryMgrPatches.a
|
||
;
|
||
|
||
|
||
load 'StandardEqu.d'
|
||
|
||
MACHINE MC68020 ;<SM2> RLM
|
||
|
||
; ——————————————————————————————————————————————————————————————————————————————————————————————————
|
||
;
|
||
; MoveHLow
|
||
;
|
||
; entry:
|
||
; A0 - handle requested
|
||
; exit:
|
||
; D0 - error (word)
|
||
; internal:
|
||
; A4 - (long) handle to original block
|
||
; A3 - (long) pointer to new block
|
||
; D3 - (long) size of block
|
||
; D4 - (long) offset to block tags (24 = -8; 32 = -12)
|
||
; info:
|
||
; This code does the opposite in principle to MoveHHi. The handle input
|
||
; is moved as low as possible in the heap that it belongs. This call works
|
||
; with 24 and 32 bit zones.
|
||
; to do:
|
||
; • roving pointer point at newly freed block (is this good?)
|
||
|
||
myError EQU nsStackErr ;QuickDraw error meaning not enough stack
|
||
blkSize EQU -8 ;note that this offset is the same for 24/32 bit zones
|
||
StackSlop EQU (3*1024) ;3k slop for interupts and our minimal buffer
|
||
BlockTagBits EQU $C0 ;
|
||
|
||
MakeBlocksNonPurgeableStack record {a6Link},decr
|
||
startParams equ *
|
||
paramSize equ startParams-*
|
||
return ds.l 1
|
||
a6Link ds.l 1
|
||
HPurgeAddress ds.l 1 ;trap address for HPurge
|
||
hNoPurgeAddress ds.l 1 ;trap address for HNoPurge
|
||
RecoverHAddress ds.l 1 ;trap address for RecoverHandle
|
||
HGetStateAddress ds.l 1 ;trap address for HGetState
|
||
largestFreeSpace ds.l 1 ;size of largest free space available
|
||
freeSpaceAccumulator ds.l 1 ;a place to add up free space
|
||
keepCheckingFreeSpace ds.w 1 ;should we keep checking free space?
|
||
passedOurBlock ds.w 1 ;are we past our block yet?
|
||
localSize equ * ;size of all the local variables
|
||
endR
|
||
|
||
MoveHLow Proc Export
|
||
|
||
@Regs REG a3-a6/d3-d7
|
||
|
||
movem.l @Regs,-(sp) ;save regs
|
||
move.l theZone,-(sp) ;save theZone
|
||
move.l a0,a4 ;save callers A0,
|
||
; original MoveHHi did this
|
||
|
||
;check for nil master pointer
|
||
move.w #nilHandleErr,MemErr ;
|
||
move.l (a0),d0 ;check for nil MP
|
||
beq @RestoreZoneStateAndExit ;
|
||
|
||
;check for locked handle passed in, and save the state of the handle
|
||
;move.l a4,a0 ;get handle to original block
|
||
_HGetState ;
|
||
move.b d0,d2 ;save handle state of original block
|
||
move.w #memLockedErr,MemErr ;
|
||
btst #lock,d0 ;
|
||
bne @RestoreZoneStateAndExit ; <SM3> CSS
|
||
|
||
;set theZone to zone that this handle lives in
|
||
move.l a4,a0 ;get handle to original block
|
||
_HandleZone ;
|
||
tst.w d0 ;
|
||
bne @RestoreZoneStateAndExit ; <SM3> CSS
|
||
move.l a0,theZone ;temporarily set theZone to contain block
|
||
|
||
;set up offset in d4 to MemMgr Block tags (be 24/32 bit zone friendly)
|
||
moveq #-8,d4 ;-8 offset MemMgr Block tags in 24 zone
|
||
move.l Lo3Bytes,d6 ;setup mask for MakeBlocksNonPurgeable
|
||
tst.b maxNRel(a0) ;check if 32 zone
|
||
beq.s @not32bitzone ;
|
||
add.l #-4,d4 ;-12 offset MemMgr Block tags in 32 zone
|
||
moveq #-1,d6 ;setup mask for MakeBlocksNonPurgeable
|
||
@not32bitzone
|
||
|
||
;mark all purgeable blocks as non-purgeable when/if we call Newptr
|
||
|
||
;mark our block as non-purgeable
|
||
move.l a4,a0 ;get handle to original block
|
||
_HNoPurge
|
||
|
||
;always allocate a buffer on the stack
|
||
link A6,#MakeBlocksNonPurgeableStack.localSize ;Unlk A6 happens in RestorePurgeableBlocks
|
||
clr.l -(sp) ;ensure end of handle list is a nil
|
||
bsr MakeBlocksNonPurgeable ; <SM3> CSS
|
||
bne.s @DoCommonExit ;condition codes are set for failure
|
||
|
||
;allocate block of same size in lower in this heap. (first get size of handle)
|
||
move.l a4,a0 ;get handle to original block
|
||
_GetHandleSize ;
|
||
move.l d0,d3 ;save size of block and check for error
|
||
bmi.s @DoCommonExit ;
|
||
_NewPtr ;d0 from _GetHandleSize
|
||
bne.s @DoCommonExit ;check for error
|
||
move.l a0,a3 ;save pointer to new block
|
||
|
||
;the result from NewPtr may actually be higher in memory than Handle passed in
|
||
;check for this and leave handle where it is
|
||
move.l (a4),d0 ;get pointer to old block
|
||
_StripAddress ;in 24 bit world, this call needed
|
||
cmp.l a0,d0 ;is new block higher in memory than old?
|
||
blt.s @DisposeBlock ;yes, go dispose the new block
|
||
|
||
;copy junk from old handle to new pointer
|
||
move.l a0,a1 ;set up destination for blockmove
|
||
move.l (a4),a0 ;set up source for blockmove
|
||
move.l d3,d0 ;set up size for blockmove
|
||
_BlockMove
|
||
|
||
;make the handle point at the new block, and make the old block a pointer block
|
||
;fix relhandle
|
||
;move.l (a4),a0 ;point a0 at old block
|
||
move.l -(a0),-4(a3) ;fix relhandle for new block
|
||
move.l theZone,(a0)+ ;change relhandle to pointer block info
|
||
|
||
;change master pointer to point at new
|
||
move.l a3,(a4) ;no more references to old block
|
||
|
||
;mark old block as pointer block, and new block as relocateable
|
||
eori.b #BlockTagBits,(a3,d4) ;toggle new block from nonrel to rel
|
||
eori.b #BlockTagBits,(a0,d4) ;toggle old block from rel to nonrel
|
||
|
||
;Dispose of the old block (this frees the block)
|
||
@DisposeBlock
|
||
_DisposePtr
|
||
;fall through to exit code (ResoreHandleStateAndExit)
|
||
|
||
@DoCommonExit ;
|
||
;walk the heap to restore Purgeable Blocks and cleanup the stack
|
||
bsr RestorePurgeableBlocks ;
|
||
move.l (sp)+,d0 ;remove the nil marker
|
||
unlk a6 ;distroy stack buffer
|
||
|
||
;restore the master pointer flags
|
||
move.w MemErr,d3 ;preserve MemErr across _HSetState call
|
||
move.l a4,a0 ;get handle to new block
|
||
move.b d2,d0 ;restore handle state of original block
|
||
_HSetState ;
|
||
move.w d3,MemErr ;restore MemErr after _HSetState call
|
||
;set up D0, fall through to exit code (RemoveStackBuffer)
|
||
moveq #0,d0 ;
|
||
|
||
@RestoreZoneStateAndExit
|
||
move.l (sp)+,theZone ;restore theZone
|
||
move.l a4,a0 ;preserve handle across MoveHHi
|
||
move.w MemErr,d0 ;make d0 agree with MemErr
|
||
movem.l (sp)+,@Regs ;restore regs, leave cc's unchanged
|
||
rts ;return without calling through
|
||
|
||
; ——————————————————————————————————————————————————————————————————————————————————————————————————
|
||
;
|
||
; MakeBlocksNonPurgeable
|
||
;
|
||
; entry:
|
||
; theZone - the zone we will walk
|
||
; d4 - negative size of heapblock header.
|
||
; exit :
|
||
; a0-a1 - trashed
|
||
; a2-a3 - unchanged
|
||
; a4 - unchanged; handle of block passed to MoveHLow
|
||
; a5 - trashed, used to jump through as return address
|
||
; a6 - should be used to unlink buffer on stack
|
||
; a7,sp - points to buffer on stack (first long is number of entries)
|
||
; d0 - error (long) (0 means no error, else bail; leave buffer on stack)
|
||
; d1 - trashed
|
||
; d2 - unchanged
|
||
; d3 - address of block passed to MoveHLow
|
||
; d4-d6 - unchanged
|
||
; d7 - trashed
|
||
;
|
||
; info:
|
||
; Walk the heap and mark purgeable blocks Non-purgeable. The problem is that
|
||
; the original MoveHHi did not purge any blocks. We need to save all purgeable
|
||
; handles, make a NewPtr call, and then restore them. Remember which
|
||
; blocks we change by putting the master pointers in a buffer on the stack.
|
||
; Warning! This routine leaves a buffer on the stack, which will be
|
||
; removed by calling RestorePurgeableBlocks. That means this routine
|
||
; can’t save anything on the stack except master pointers.
|
||
;
|
||
; todo:
|
||
; ——————————————————————————————————————————————————————————————————————————————————————————————————
|
||
|
||
with MakeBlocksNonPurgeableStack
|
||
MakeBlocksNonPurgeable
|
||
move.l a3,d1 ;preserve a3 across call
|
||
move.l (sp)+,a5 ;get return address
|
||
|
||
|
||
;see if we can make a buffer with storage for at least entry
|
||
_StackSpace ;
|
||
sub.l #StackSlop,d0 ;need some slop on stack for interupts
|
||
bmi @ErrorExit ;
|
||
|
||
;initialize regs for @loop
|
||
;clear free space counters
|
||
;clear flags to see if we should check free space and if we’ve passed our block
|
||
;set up our block address for free space counter
|
||
;A1 points at end of stack buffer
|
||
;A3 points at first block in heap
|
||
;D7 points at end of heap
|
||
clr.l largestFreeSpace(a6) ;start with zero as the largest free space
|
||
clr.l freeSpaceAccumulator(a6) ;and the accumulator starts out empty, too
|
||
clr.w keepCheckingFreeSpace(a6) ;clear the flag for free space checks
|
||
clr.w passedOurBlock(a6) ;clear the flag for our block in the heap
|
||
move.l sp,a1 ;pointer to stack buffer
|
||
sub.l d0,a1 ;point to the end of the stack buffer
|
||
move.l (a4),d0 ;get address of our block
|
||
_StripAddress ;shave off nasty bits, ’cause we’re gonna compare against it
|
||
move.l d0,d3 ;keep block address in d3
|
||
move.l theZone,a3 ;get pointer to start of this zone
|
||
move.l bkLim(a3),d7 ;save end of heap
|
||
add.l #heapData,a3 ;skip past zone header
|
||
sub.l d4,a3 ;skip past heap block header
|
||
;this loop walks the heap, and marks all purgeable blocks non-purgeable
|
||
@loop
|
||
cmp.l a1,sp ;check if stack buffer is full
|
||
ble @ErrorExit ;no more room in buffer, signal error
|
||
cmp.l a3,d7 ;check if end of heap
|
||
blt @NormalExit ;successfull, exit loop
|
||
cmp.l a3,d3 ;check if we’re to our block
|
||
bgt.s @pastCheckForOurBlock ;if we’re not there, then move on
|
||
move.w #-1, passedOurBlock(a6) ;otherwise, set the flag that says we’re past our block
|
||
;now treat block passed in as a special case terminator for free space checks
|
||
cmp.l a3,d3
|
||
beq.s @resetFreeSpaceAccumulator
|
||
@pastCheckForOurBlock
|
||
|
||
;low two bits in d0 will be the block type
|
||
move.b (a3,d4),d0 ;get the blocktype
|
||
lsr.b #6,d0 ;only use the tag bits
|
||
|
||
;if free d0 = 0
|
||
beq.s @addToFreeSpaceAccumulator
|
||
|
||
@notFreeBlock
|
||
;bail if pointer block
|
||
cmp.b #tybkNRel,d0 ;check if pointer block
|
||
beq.s @resetFreeSpaceAccumulator ;advance to next block
|
||
|
||
;we are sure it is a relocateable block now, but check just in case.
|
||
cmp.b #tybkRel,d0 ;check if relocateable block
|
||
bne.s @resetFreeSpaceAccumulator ;if not relocatateable, error, go next?
|
||
|
||
;we have a pointer to the relocatable block, we need a handle to mark it non-purgeable
|
||
move.l a3,a0 ;set up pointer to block
|
||
_RecoverHandle ;if this fails, then this block is
|
||
;orphaned, and we can move it at will
|
||
tst.w MemErr ;(RecoverHandle result in MemErr only!) <LW2>
|
||
bmi.s @pastFreeSpaceChecks ;
|
||
|
||
;if already locked, we don’t need to change its state
|
||
_HGetState ;
|
||
btst #lock,d0 ;check if handle is locked
|
||
bne.s @resetFreeSpaceAccumulator ;is locked, go to nextBlock
|
||
|
||
;last test, is it a purgeable block?
|
||
btst #purge,d0 ;check if handle is purgeable
|
||
beq.s @pastFreeSpaceChecks ;if not purgable go to nextBlock
|
||
|
||
;change its state, and save a handle to this block in our buffer.
|
||
_HNoPurge ;keep this block from purgeing
|
||
move.l a0,-(sp) ;save handle to this block on stack
|
||
bra.s @pastFreeSpaceChecks
|
||
|
||
;we found a relocatable or free block, so add this block to the possible space for our ptr
|
||
@addToFreeSpaceAccumulator
|
||
move.l blkSize(a3),d0 ;get size of this block
|
||
and.l d6,d0 ;strip off tags 24 bit world (32 is ok)
|
||
tst.w keepCheckingFreeSpace(a6) ;should we bother (have we passed our block)
|
||
bmi.s @endOfLoop ;don’t bother if we’ve passed our block
|
||
add.l d0, freeSpaceAccumulator(a6) ;and add the size to the free space for this island
|
||
bra.s @endOfLoop
|
||
|
||
;we ran across an immovable block, so reset our counter and update the largest free area we’ve found
|
||
@resetFreeSpaceAccumulator
|
||
tst.w keepCheckingFreeSpace(a6) ;should we bother with this? (past our block?)
|
||
bmi.s @pastFreeSpaceChecks ;if the flag is set, skip the free space stuff
|
||
tst.w passedOurBlock(a6) ;have we (just now) passed our block in the heap
|
||
beq.s @pastPreflightCheck ;if the flag is not set, keep checking free spaces
|
||
;now comes the good part... should we bail out early because there isn’t sufficient free space
|
||
;lower that our block in the heap?
|
||
move.l a3, -(sp) ;save block pointer, ’cause we need an address register
|
||
move.l d3,a3 ;put block passed in here
|
||
move.l blkSize(a3),d0 ;get size of our block
|
||
and.l d6,d0 ;strip off tags 24 bit world (32 is ok)
|
||
cmp.l largestFreeSpace(a6),d0 ;do we have room for our block lower in the heap?
|
||
move.l (sp)+, a3 ;put old block pointer back, don’t affect cc
|
||
bgt.s @ErrorExit ;no room in heap, bail out!
|
||
move.w #-1,keepCheckingFreeSpace(a6) ;now that we have good news, stop checking free space
|
||
bra.s @pastFreeSpaceChecks
|
||
@pastPreflightCheck
|
||
move.l freeSpaceAccumulator(a6), d0 ;get the space we counted this time
|
||
cmp.l largestFreeSpace(a6), d0 ;see if the largest is still king of the hill
|
||
ble.s @pastFreeSpaceChecks ;if so, move on
|
||
move.l d0, largestFreeSpace(a6) ;else make a new king
|
||
clr.l freeSpaceAccumulator(a6) ;and clear the accumulator
|
||
@pastFreeSpaceChecks
|
||
move.l blkSize(a3),d0 ;get size of this block
|
||
and.l d6,d0 ;strip off tags 24 bit world (32 is ok)
|
||
|
||
;end of @loop, bump a3 to point at next block
|
||
@endOfLoop
|
||
add.l d0,a3 ;point at next block
|
||
bra @loop ;loop back for more
|
||
|
||
|
||
;if error occured, we must signal error by putting a zero count into stack buffer
|
||
@ErrorExit
|
||
move.w #myError,d0 ;signal an error to the main routine
|
||
move.w d0,MemErr ;set MemErr
|
||
bra.s @CommonExit ;exit through CommonExit
|
||
|
||
@NormalExit
|
||
moveq #0,d0 ;signal no error to the main routine
|
||
|
||
@CommonExit
|
||
;condition codes must be set at this point, caller depends on this
|
||
movea.l d1,a3 ;preserve a3 across call
|
||
jmp (a5) ;rts
|
||
endwith
|
||
|
||
|
||
; ——————————————————————————————————————————————————————————————————————————————————————————————————
|
||
;
|
||
; RestorePurgeableBlocks
|
||
;
|
||
; entry:
|
||
; A6 - should be used to unlink buffer on stack
|
||
; exit :
|
||
; D1 - trashed
|
||
; A5 - trashed
|
||
; A6 - restored
|
||
;
|
||
; info:
|
||
; Traverse the stack buffer and restore the saved handles to their purgeable
|
||
; state. Deallocate the stack buffer.
|
||
; Warning! This routine removes the buffer on the stack. This buffer was
|
||
; allocated by MakeBlocksNonPurgeable.
|
||
; todo:
|
||
; ——————————————————————————————————————————————————————————————————————————————————————————————————
|
||
|
||
RestorePurgeableBlocks
|
||
move.l (sp)+,a5 ;get return address
|
||
|
||
@loop
|
||
tst.l (sp) ;check for end of list
|
||
beq.s @ExitRestorePurgeableBlocks ;nil meant end of list
|
||
move.l (sp)+,a0 ;get handle
|
||
_HPurge ;mark as purgeable, ignore any error
|
||
bra.s @loop ;check if anymore
|
||
|
||
@ExitRestorePurgeableBlocks
|
||
jmp (a5) ;return to caller
|
||
EndProc
|
||
|
||
; ——————————————————————————————————————————————————————————————————————————————————————————————————
|
||
|
||
; •••••••••••••••••••••••••• Patch for CycloneTranslate24To32 •••••••••••••••••••••••••• <P8> RMP
|
||
;
|
||
; FUNCTION CycloneTranslate24To32(Addr24: long): Addr32;
|
||
;
|
||
; Translate an Cyclone 24-bit address to a valid Cyclone 32-bit address.
|
||
;
|
||
; INPUT D0=Addr24;
|
||
; OUTPUT D0=Addr32;
|
||
;
|
||
; All other registers preserved
|
||
;
|
||
; Translation algorithm from Cyclone Hardware ERS:
|
||
;
|
||
; Old first 3 nibbles New first 3 nibbles
|
||
; ------------------- -------------------
|
||
; xx0 000
|
||
; : :
|
||
; xx7 007
|
||
; xx8 400-->408 for safety in 24 bit mode; also since the emulator doesn't alias
|
||
; xx9 409 second meg of ROM is in tradional slot 9 space.
|
||
; xxA FA0
|
||
; : :
|
||
; xxE FE0
|
||
; xxF 500
|
||
;
|
||
;
|
||
;
|
||
; NOTE: Unlike _StripAddress, this routine does not necessarily
|
||
; return an address which can also be used in 24 bit mode.
|
||
; Furthermore, Translate24To32 can not be called meaningfully
|
||
; with the result of a previous translation.
|
||
;
|
||
;
|
||
;
|
||
|
||
CycloneTranslate24To32 Proc Export
|
||
|
||
AND.L Lo3Bytes,D0 ;Clean high byte for fast exit
|
||
BCLR #23,D0 ;Is the address in RAM?
|
||
BNE.S @NotRam ;no, go patch up address
|
||
RTS ;quick exit
|
||
|
||
@NotRam MOVE.W D1,-(SP) ;save a work register
|
||
SWAP D0 ;get relevant nibbles into low word
|
||
MOVE.W D0,D1 ;...and into index register
|
||
LSR.W #4,D1 ;move nibble 3 to use as index
|
||
AND.W (@TABLE+2,PC,D1.W*4),D0 ;mask off bits that will be added.
|
||
ADD.W (@TABLE,PC,D1.W*4),D0 ;translate by adding table value
|
||
SWAP D0 ;move translated nibbles back to high word
|
||
MOVE.W (SP)+,D1 ;restore work register
|
||
RTS
|
||
|
||
@TABLE
|
||
DC.W $4080,$FFFF ; 008x -> 408x
|
||
Dc.w $4080,$FFFF ; 009x -> 409x
|
||
Dc.w $FA00,$000F ; 00Ax -> FA0x
|
||
Dc.w $FB00,$000F ; 00Bx -> FB0x
|
||
Dc.w $FC00,$000F ; 00Cx -> FC0x
|
||
Dc.w $FD00,$000F ; 00Dx -> FD0x
|
||
Dc.w $FE00,$000F ; 00Ex -> FE0x
|
||
Dc.w $5000,$000F ; 00Fx -> 500x
|
||
|
||
ENDPROC
|
||
|
||
|
||
end |