645 lines
25 KiB
Raw Permalink Normal View History

; File: DeCompressorPatch.a
; By Donn Denman
; Contains: patch to Resource Manager's CheckLoad hook for resource decompression.
; Copyright: <09> 1990-1991 by Apple Computer, Inc., all rights reserved.
; Change History (most recent first):
; <18> 12/18/91 pvh Add '040 cache flushing at a point after the resources have been
; decompressed. This helps executable code out a great deal.
; Catches both DonnBits & GreggyBits (and any new ones that may
; come along).
; <17> 2/7/91 gbm csd, #82402: Make this patch reentrant.
; <16> 1/30/91 gbm sab, #38: Change the name of one of the decompressor header
; files (because Sheila said it was okay)
; <15> 12/20/90 gbm (dba) Reintroduce the saving and restoring of some key Resource
; Manager globals. This will make decompression work.
; <14> 10/30/90 dba only accept decompressor defprocs from resource files with a
; special bit set
; <13> 9/25/90 gbm Stop caching what we think was the last defproc... this was
; bogus
; <12> 9/4/90 gbm make a new calling interface for new defProcs
; <11> 9/3/90 gbm add knowledge of new header types. Types newer (higher) than 8
; have a different format. DefProcs called when the new header is
; present have a calling interface different from previous
; defprocs.
; <10> 9/2/90 csd Made the resource handle NIL if we don<6F>t have enough memory to
; load the resource
; <9> 8/31/90 stb Change var table allocation from NewPtr to NewHandle. It<49>s
; faster and won<6F>t burp the new resource handle.
; <8> 3/23/90 DD Fixed a bug in which the variable table was not being made large
; enough.
; <7> 3/9/90 DD Fixed both decompression and SizeRsrc patch to be robust about
; resources with the extended attributes bit that don't have the
; right signiture. Note that MacPaint has the 'compressed' bit
; set on all of their resources.
; <6> 2/28/90 DD major rewrite includes decompress in place, and separate
; decompress defproc 'dcmp'
; <6> 2/2/90 DD Changed the error code returned when a resource can't be
; decompressed to CantDecompress: -186, new from SysErr.a
; <5> 1/29/90 DD Removed a userbreak so you won't go into Macsbug during an error
; condition.
; <4> 1/18/90 DD reorganized the resource header for extended attribute
; resources, and compressed flavor extended attribute resources
; <3> 1/18/90 DD Updated the source to match a newer version with several bug
; fixes.
; <2> 1/18/90 DD Removed a user break, and changed the message in a user break
; that informs of an out-of-memory condition
; <1> 1/17/90 DD Adding for the first time into BBS.
ROMVersions MacPlus, MacSE, MacII, Esprit, Aurora
; Location of the _Read trap in Resource Manager's RdData routine.
FromReadInResMgr Bind (MacPlus,$14250),(MacSE,$E864),(MacII,$1319E),(Esprit,$147F6),(Aurora,$1BA1A)
; Location of the Bsr RdData in Resource Manager's SizeRsrc routine.
FromSizeRsrc Bind (MacPlus,$141DA),(MacSE,$E7EE),(MacII,$13128),(Esprit,$14780),(Aurora,$1B9A4)
; Location of the Bsr SizeRsrc in Resource Manager's SizeResource routine - the _SizeRsrc code.
FromSizeResource Bind (MacPlus,$1487A),(MacSE,$EE9C),(MacII,$137D6),(Esprit,$14E2E),(Aurora,$1C052)
; Location of the second Bsr RdData in Resource Manager<65>s NewMap routine.
FromNewMap Bind (MacPlus,$136B4),(MacSE,$DCB0),(MacII,$125E4),(Esprit,$13C54),(Aurora,$1AE6C)
DecompressionLocals RECORD {a6Link},decr ;<17>
return DS.L 1
a6Link DS.L 1
ResHeaderBuffer DS.B ResourceTemplate.ResTemplateSize ; room for a ExtResRecord
DoDonnDecompress DS.B 1 ; boolean set if we're doing "old" decompression
extra DS.B 1 ; and a little padding
localSize EQU *
ENDR ;<17>
DeCompression PROC
EXPORT OldCheckLoadJump,MyCheckLoad
With ExtendedResource, ExtResRecord, VarTableRec, ResourceTemplate, DecompressionLocals
; ReadResData - Read the resource data for D0 bytes into the buffer (A0).
; Entry: D0 - number of bytes to read.
; D1 - relative data location within the resource data block.
; A0 - pointer to the data buffer.
; A4 - resource map handle.
; A6 - LINKed locals for decompression <17>
; Exit: D0 - result code.
; A0 - pointer to the header data.
; all other registers saved.
Lea ResHeaderBuffer(a6),A0 ; <17>
MoveM.L D1-D2/A0-A3,-(SP)
MoveQ #ioQElSize,D2 ;create a param block on the stack.
Sub.L D2,SP
Move.L A0,ioBuffer(SP) ;place to put the data.
Move.L SP,A0 ;for the read call.
Move.L (A4),A1 ;deref the map handle.
Move.W MRefNum(A1),ioRefNum(A0) ;refNum from the resource map.
Move.L D0,ioReqCount(A0) ;number of bytes to read.
Move.W #1,ioPosMode(A0) ;read from start of file.
Add.L ResDataOffset(A1),D1 ;add the base data offset in the file.
Move.L D1,ioPosOffset(A0) ;computed offset from file start.
_Read ;read the resource data.
Add.L D2,SP ;strip the ioParam block.
MoveM.L (SP)+,D1-D2/A0-A3
; Buffer for I/O with read.
;starting with change <17>, this is all on a local stack frame (for recursion)
; DS.B ResTemplateSize ;room for a ExtResRecord
; DS.B 1 ; boolean set if we're doing "old" decompression
; Register conventions throughout the Resource Manager:
; A0, D0, D1, D2, D7 scratch.
; A1 is the handle to the resource
; A2 points to an entry in the map
; A3 points to the beginning of a sub-block
; A4 is the map handle
; D3 is the resource type, if any
; D4 is the number of resource entries (-1) for looping
; D5 is the number of types (-1) for looping
; D6 is the resource map reference number
; A6 is always LINKed, through StdEntry. There is only 1 local variable,
; an IOStackFrame. A0 also always points to 8(A6) when returning from StdEntry.
; Slight modification to above.... we have our own locals LINKed on A6
* MyCheckLoad
* Description: If we are reading in a packed resource, then unpack the
* data as it is loaded.
* New
* Operation: If the resource is not compressed,
* or if the handle has data then Jump OldCheckLoad.
* Remember ResLoad, Clear ResLoad.
* Call OldCheckLoad to allocate the data handle.
* Restore ResLoad, if False then exit.
* Allocate the data handle (in the right heap), with a
* little extra slop (using resrvemem if locked).
* Unpack the data in place.
* Trim away the extra slop.
* Exit.
* Input: A2: pointer to resource list entry
* A4: resource map handle
* ResLoad: true if load, false if no load
* Output: A0: handle to resource
* A1: trashed
* A2-A7: unchanged
* D0-D2: trashed
* D3-D7: unchanged
* Called by: Resource Manager
* Reister usage: D2 - offset of raw data in this resource.
* D3 - handle's zone.
* D4 - resource attributes.
* D5 - unpacked size.
* D6 - offset to this resource's data, then the var table handle.
* D7 - original resLoad state.
* A2/A3/A4 - as the Resource Manager needs them.
* A5 - the resource handle.
* A6 - LINKed locals <17>
;is it compressed? Flag that it isn't in case it gets modified and written.
BTst.B #resExtended,RAttr(A2)
BNZ.S TestResHandle ;no, then just do the old checkload stuff.
AERROR 'The decompression patch for the CheckLoad hook needs to be reworked to go into ROM!'
Jmp (0).L ;gets stuffed with the old address.
; if there is alredy data in the handle, then just call old checkload.
Move.L RHndl(A2),D0 ;is the handle loaded already?
BZ.S DeCompressor ;no, then we will try to load it.
Move.L D0,A0
Move.L (A0),D0 ;check if the master pointer is nil
BNZ.S OldCheckLoadJump ; it has data, so no need to load, or decompress.
LINK A6, #localSize ; save space for a read buffer and stuff <17>
MoveM.L D3-D7/A4-A5,-(SP) ;save some regs.
; Remember ResLoad, Set ResLoad False.
Move.B ResLoad,D7 ;remember ResLoad. ?Should I use _GetResLoad and SetResLoad?
Clr.B ResLoad ;don't load the data in old checkLoad.
; The resource is compressed but not loaded yet. Make sure the handle gets created.
BSr.S OldCheckLoadJump ;create the handle for the data.
Move.B D7,ResLoad ;restore ResLoad.
BZ.S @IfZExit2
Move.L A0,A5 ;remember the handle.
Move.L A0,D0 ;is the handle allocated now?
BZ Exit2 ; no, then we can't do any more.
Move.B RAttr(A2),D4 ;remember the attribute flags.
; read in the resource data header. This gives us decompressed size.
MoveQ #ResHeaderSize,D0 ;amount we wish to read.
Move.L RLocn(A2),D6 ;get the relative data location.
And.L Lo3Bytes,D6 ;mask off the resource attributes.
Move.L D6,D1 ;this is where we want to start reading from.
Bsr ReadResBuffer ;read in the resource data.
; do a robustness check to see if the handle really seems to be compacted.
Cmp.L #RobustnessData,ResHeader.signiture(A0) ;is it really an extended resource?
BEQ.S @TestHeaderVersion
MoveM.L (SP)+,D3-D7/A4-A5 ;restore the regs, but don't pop them yet.
UNLK A6 ; <17>
Bra.S OldCheckLoadJump ;read in the data, now that resLoad is restored.
Move.W #CantDecompress,D2 ;assume bad extended resource.
lea DoDonnDecompress(a6), A1 ;get flag location <17>
sf (a1) ;set the flag, assuming that's what we want
Cmp.B #CurHeaderVersion,ResHeader.headerVersion(A0) ;is this DonnBits?
BEQ @headerVersionOK ;yep, okay
st (a1) ;nope, clear the flag
cmp.b #greggyHeaderVersion, ResHeader.headerVersion(a0) ;is this GreggyBits?
bne ErrorExit ;no, bail
@headerVersionOK ;move along
; Make the pointers into the Map relative offsets, so we can rebuild them after a heap shuffle.
Sub.L (A4),A2 ; pointer to map entry becomes an offset.
Sub.L (A4),A3 ; some callers have a block pointer here.
; assume it's just a non-compressed extended resource, compute data size and offset.
MoveQ #ExtendedResource.extendedData,D2 ;offset to start of data for a normal extended attribute resource
Move.L ResSize(A0),D5 ;get the size, including header.
Sub.L D2,D5 ;actual amount of data to read.
BTst #resCompressed,ResHeader.extendedAttributes(A0)
if DoDebug then
BNZ @SkipDebugBreak
Break 'Simple Extended Resource'
BZ HaveSizeAndOffset ;not compressed, then we have the size and offset.
; check which def proc to unpack it with. We use a simple robust caching scheme to avoid _GetResource calls.
Move.W #badExtResource,D2 ;assume an error if it was the wrong version!
btst #0, DoDonnDecompress(a6) ; are we doing DonnBits <17>
bne.s @notDonn ; no, get ID from revised general header format
Move.W ResHeader.decompressID(A0),D5 ;get the Donn ID this resource needs
bra.s @gotID ; ...and keep going
move.w ResHeader.defprocID(a0), d5 ; get defProc
; here, we look for decompression defprocs in all files below the current one
Move.W #CantDecompress,D2 ;assume bad extended resource
move.l a4,a1 ;get a handle to the first file to look in
move.l a1,d0 ;are we done?
bz ErrorExit ;yes, we looked and didn<64>t find anything
move.l (a1),a0 ;get map pointer
btst #decompressionPasswordBit,mInMemoryAttr(a0)
bz.s @nextFile
move.w CurMap,-(sp)
Move.B ResOneDeep,-(SP) ;preserve the depth setting
move.w ROMMapInsert,-(sp) ;save the value of ROMMapInsert <15>
move.l ResErrProc,-(sp) ;remember the ResErrProc <15>
move.w mRefNum(a0),CurMap ;get resource from this file
clr.w ROMMapInsert ;don<6F>t even think about ROM map <15>
clr.l ResErrProc ;and don<6F>t call the app. <15>
SubQ #4,SP
Move.L #DeCompressDefType,-(SP) ;'dcmp'
Move.W D5,-(SP)
_Get1Resource ;get the decompress def. proc.
Move.L (SP)+,A0
move.l (sp)+,ResErrProc ;back to normal <15>
move.w (sp)+,ROMMapInsert ;restore ROM guy too <15>
Move.B (SP)+,ResOneDeep ;restore depth
move.w (sp)+,CurMap
Move.L A0,D0
bne @gotDecompressor ;if we got the decompressor, then use it
move.w ResErr,d0 ;if there was no dcmp, go on
beq.s @nextFile
cmp.w #resNotFound,d0 ;if there was an error, quit with the error
bne ErrorExit
move.l (a1),a1 ;get map pointer
move.l mNext(a1),a1 ;go on to the next file in the chain
bra.s @tryMap
; remember the defproc handle. (mostly 'cause we don't have any registers left...)
Lea DefProcHandle,A1 ;stuff away the defproc ID
Move.L A0,(A1)+ ;stuff away the defproc handle into DefProc
; it is a compressed resource, make sure we can handle it, and get its size from the header.
Lea ResHeaderBuffer(a6),A0 ;point into our frame. <17>
btst #0, DoDonnDecompress(a6) ; are we DonnBits? <17>
bne.s @cTableDontCare ; no, then cTables are not used
Tst.W ResHeader.cTableID(A0) ;is the built in table to be used?
Bne ErrorExit ; for now, we don't load decompression tables.
MoveQ #ExtendedResource.compressedData,D2 ;offset to start reading from.
Move.L ResHeader.actualSize(A0),D5 ;get the unpacked size.
MoveQ #0,D0 ;overRun indicates the amount of extra room we need to
btst #0, DoDonnDecompress(a6) ; are we DonnBits? <17>
bne.s @getWordSizedSlop ; no, then slop size is a word
Move.B ResHeader.overRun(A0),D0 ; avoid the input and output pointers crossing during decompression.
bra.s @addOverRun ;
@getWordSizedSlop ;
move.w ResHeader.decompressSlop(a0), d0 ; get word sized slop size
@addOverRun ;
Add.L D0,D5 ; block size must include overRun.
; Remember the current heap zone, and switch to the zone of the compressed handle.
_GetZone ;get the current heap zone.
Move.L A0,D3 ;remember current heap.
Cmp.L RomMapHndl,A4 ; is it in the ROM map?
Bne.S @NotROMMap ; then go recover information
Break 'RomMapHndl Zone'
Move.L A4,A0
_HandleZone ; use the zone that the ROM Map comes from.
Bra.S @SetRightZone
BTst.L #ResSysHeap,D4 ; Is it in the system heap?
BZ.S @SetRightZone ; No, use same heap
Move.L SysZone,A0 ; otherwise set the system zone for now
_SetZone ; and use it, for now.
; If the handle was locked, then we reserve low memory for the bigger unpacked handle.
BTst.L #resLocked,D4 ;should the handle be locked?
BZ.S @DoneReserve
Move.L D5,D0 ;amount of space we will need.
_ResrvMem ;reserve room in locked space for the handle.
; Reallocate the handle.
Move.L A5,A0
Move.L D5,D7 ;remember new size.
Move.L D5,D0
_ReallocHandle ;set up the unpacked handle's size.
Move.W D0,D1
BZ.S @GotHandle ;did it get allocated without an error?
Break 'Error from _ReallocHandle'
Move.W D1,ResErr
Suba.l A5, A5 ; return NIL since we don<6F>t have enough memory <10>
Bra @ExitPastHSetState ;exit if an error happened. Skip HSetState <10>
; compute the amount of data to be read.
Move.L ResHeaderBuffer+ExtResRecord.ResSize(a6),D0 ;amount of raw resource data. <17>
Sub.L D2,D0 ;subtract the (offset of data from the header) from size.
; compute where the data should go.
Move.L (A5),A0 ;point to the handle base.
Sub.L D0,D7 ;subtract the data size from the block size.
Add.L D7,A0 ;point to the last place where the data will fit.
; Read the data into the end part of the handle.
Move.L D6,D1 ;where the size and resource header start.
AddQ.L #ResHeader-ResSize,D1 ; point past the size.
Add.L D2,D1 ; we want to read from beyond the header.
Bsr ReadResData ;read the data into the block.
BZ.S @NoReadError ; do an error check.
Move.W D0,ResErr ;set up res error from I/O error.
Break 'error during read'
Bra @Exit
; if it is not a compressed resource, then we are done.
BTst #resCompressed,ResHeaderBuffer+ExtResRecord.ResHeader.extendedAttributes(a6) ;compressed resource? <17>
Beq @Exit
; create the room for the variable table. It is a percentage of the unpacked size.
btst #0, DoDonnDecompress(a6); DonnBits? <17>
bne.s @dontCallDonn ; no, call defProc via new and improved interface
MoveQ #0,D0 ;get the ratio of var table to unpacked size.
Move.B ResHeaderBuffer+ResHeader.varTableRatio(a6),D0 ;<17>
Beq.S @HaveVarSize
AddQ.L #1,D0 ;round up.
Move.L D5,D1 ;unpacked size - is it a long?
Swap D1
Mulu D0,D1 ;in case it is, scale the high word too.
Swap D1
Clr.W D1 ;clear out the overflow.
Mulu D5,D0
Add.L D1,D0
Lsr.L #8,D0
AddQ.L #VarRecSize,D0 ;add enough room for at least one record.
Move.L D0,D1 ;save var table size.
_NewHandle ;get the table. Used to be a _NewPtr, but that moves the Realloc'ed handle
Bne ErrorExitBlowoffHandle ;blow off the handle and return a nil handle
_HLock ;don<6F>t let it move
Move.L A0,D6 ;save var table ptr.
; Finally we are ready to decompress the data in the clone into our larger handle.
Move.L (A5),A0
Add.L D7,A0 ;point to the data.
Move.L A0,-(SP) ;source ptr
Move.L (A5),-(SP) ;destination ptr.
Move.L D6,A0 ;var table handle
Move.L (A0),-(SP) ;pass var table ptr.
Move.L D1,-(SP) ;pass var table size
Move.L DefProcHandle,A0 ;get the defproc handle.
Move.L (A0),A0
Jsr (A0) ;call the defproc. Note: the defProc can't move memory!
; dispose of the var table handle.
Move.L D6,A0
_DisposHandle ;dispose of the var table handle.
bra.s @cutBackHandle
pea ResHeaderBuffer+ExtResRecord.ResHeader(a6) ; header ptr <17>
move.l DefProcHandle, a0 ;
move.l (a0), a0 ;
adda.w 0(a0), a0 ; move to offset of "Prepare" routine
jsr (a0) ; call defProc "Prepare" routine
move.l (a5), a0 ; get start of block
add.l d7, a0 ; point to the start of the data
move.l a0, -(sp) ; source
move.l (a5), -(sp) ; destination
pea ResHeaderBuffer+ExtResRecord.ResHeader(a6) ; header ptr <17>
move.l DefProcHandle, a0 ;
move.l (a0), a0 ;
adda.w 2(a0), a0 ; move to offset of "Decompress" routine
jsr (a0) ; call defProc "Decompress" routine
pea ResHeaderBuffer+ExtResRecord.ResHeader(a6) ; header ptr <17>
move.l DefProcHandle, a0 ;
move.l (a0), a0 ;
adda.w 4(a0), a0 ; move to offset of "Done" routine
jsr (a0) ; call defProc "Done" routine
@cutBackHandle ; cut back the handle to get rid of the slop.
Move.L ResHeaderBuffer+ExtResRecord.ResHeader.actualSize(a6),D0 ;final size. <17>
Move.L A5,A0
_SetHandleSize ;shrink the handle to it's correct size.
; restore the handle state, and restore the zone.
Move.L A5,A0 ;The following bit-smashing is from the resource manager.
LSL.B #3,D4 ; Shift over lock to bit 7 in D4, purge bit to X .
RoL.B #1,D4 ; Rotate Lock to bit 0
RoXR.B #2,D4 ; Rotate L & P to bits 7 & 6
Or.B #$20,D4 ; Set Resource Flag bit of D4.B
And.B #$E0,D4 ; <20>lear all but LPR bits of D4.B
_HGetState ; D0 <- LPR bits Is this needed?
AND.B #$1F,D0 ; Clear LPR bits only
OR.B D4,D0 ; Set up new LPR bits
_HSetState ; MemMgr bits <- D0
@ExitPastHSetState ; <10>
Move.L D3,A0
_SetZone ; restore the zone.
; Make the offsets into the Map pointers again, so they will be valid after a heap shuffle.
Add.L (A4),A2 ; pointer to map entry.
Add.L (A4),A3 ; some callers have a block pointer here.
Move.L A5,A0 ;return the handle.
MACHINE MC68040 ; (should we cache flush on all cpus?) <18>
; (if we do then make sure to save D1 since trashed by '030 flush routine) <18>
cmp.b #cpu68040,CPUFlag ; check if we are on a 68040 <18>
bne.s @not040 ; If we are then <18>
jsr ([jCacheFlush]) ; flush data cache for consistency! <18>
; we should really do this cache line by line for speed... maybe later <18>
MoveM.L (SP)+,D3-D7/A4-A5
UNLK A6 ; <17>
; We got an error cloning the handle, or creating the unpacked handle. Toss the clone (even if Nil).
Move.W #MemFullErr,D2 ;not enough to allocate var table
Move.L A5,A0
*** Break 'DeCompressor - not enough RAM - set up the error code for the Resource Man'
Move.W D2,ResErr ;set up the error code for the Resource Man.
Sub.L A0,A0 ;return with nil.
Bra.S Exit2
; Storage
DefProcHandle DC.L 0 ;current defProc.
; SizeRsrc patch
SizeRsrcStack Record 0
dispatcherReturn DS.L 1 ;return address to the dispatcher
dispatcherRegs DS.L 4 ;dispatcher saves five regs. We ignore the first four.
oldRegisterA2 DS.L 1 ;register A2 is saved here by the dispatcher.
dispatcherResult DS.L 1 ;dispatcher pops this result long.
readReturn DS.L 1 ;return address for the _Read trap.
RdResgisters DS.L 4 ;four registers saved by RdData.
RdDataReturn DS.L 1 ;return address (to SizeRsrc?)
SzResgisters DS.L 4 ;four registers saved by SizeRsrc.
RdDataResult DS.L 1 ;room for result from RdData
SizeRsrcReturn DS.L 1 ;return address (to SizeResource?)
With ExtendedResource, ExtResRecord, SizeRsrcStack
Export ReadDecompression
bra.s SkipOldRead ;the special header here for patch protector
JumpOldRead BackToTrap Read ;now do the old read.
CmpBind FromReadInResMgr,readReturn(SP) ;check return from _Read -- coming from resource manager?
BNE.S JumpOldRead ;nope, skip the patch.
CmpBind FromNewMap,RdDataReturn(SP) ;will RdData return to NewMap?
BEQ.S NewMapPatch ;yes, do the NewMap patch
CmpBind FromSizeRsrc,RdDataReturn(SP) ;will RdData return to SizeRsrc?
BNE.S JumpOldRead ;nope, skip the patch.
CmpBind FromSizeResource,SizeRsrcReturn(SP) ;will SizeRsrc return to SizeResource?
BNE.S JumpOldRead ;nope, skip the patch.
; now we know that we are about to read the length for _SizeRsrc on an extended resource.
Move.L oldRegisterA2(SP),A1 ;rebuild A2 here.
Add.L (A4),A1 ; restore to a pointer <30may85> BBM
BTst.B #resExtended,RAttr(A1) ;is this an extended resource?
Bz.S JumpOldRead ;nope, skip the patch.
; make sure this is really a compressed resource by reading in the header data, and testing for our signiture.
LINK A6, #localSize ; <17>
MoveM.L D1/A0,-(SP)
MoveQ #ResHeaderSize,D0 ;amount we wish to read.
Move.L RLocn(A1),D1 ;get the relative data location.
And.L Lo3Bytes,D1 ;mask off the resource attributes.
Bsr ReadResBuffer ;read in the resource data.
; do a robustness check to see if the handle really seems to be compacted.
Cmp.L #RobustnessData,ResHeader.signiture(A0) ;is it really an extended resource?
MoveM.L (SP)+,D1/A0
UNLK A6 ; <17>
BNE.S JumpOldRead ;didn't match, then just do old sizersrc.
; adjust the position offset to point within the header to the actual length.
MoveQ #ExtResRecord.ResHeader.actualSize,D0 ;get delta to the new position.
Add.L D0,ioPosOffset(A0) ;bump the offset by our header and a long.
BRA.S JumpOldRead ;go do the read
; we have already read in the map header, and are about to read in the extended attributes
move.l a0,-(sp)
move.l (a4),a0 ;get pointer to the map
clr.b mInMemoryAttr(a0) ;clear attributes that are set at run-time only
move.l (sp)+,a0
BRA.S JumpOldRead ;go do the read