supermario/base/SuperMarioProj.1994-02-09/LinkedPatches/LinkedPatchLoader.a

2413 lines
104 KiB
Plaintext
Raw Permalink Normal View History

2019-06-29 15:17:50 +00:00
;
; File: LinkedPatchLoader.a
;
; Contains: code to load linked patch resources
;
; Note that this patch loader is closely tied to the patch macros (LinkedPatchMacros.a)
; and patch linker (LinkPatch tool). Use care in changing one of these three without
; looking at the others.
;
; Written by: Darin Adler
;
; Copyright: © 1990—1991 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <63> 8/1/92 DTY Change a bsr.s to a bsr.w to keep this file building for linked
; patch INITs. (Dont ask me while it compiles differently for
; the two.)
; <62> 7/31/92 DTY Allow use of link patch debugging globals in linked patch INITs
; because we cant think of a good reason why this was
; conditionalized out in the first place. Also added comments to
; document which low mem globals to set to stop on a particular
; link patch entry, or set of entries on boot.
; <61> 7/7/92 JSM Add comment about 'ptbl' being passed in via A3, rename
; g.patchTable global to g.patchRangeTable to avoid terminology
; confusion, document format of patch range table in header of
; CheckPatchTableRange.
; <60> 6/3/92 DTY #1026928: If were loading linked patches from ROM, ROMMapInsert
; needs to be set for all GetResource calls, not just the first
; one. Since CurMap gets reset for the ROM case, the value needs
; to be saved in a register before loading 'lpch' resources.
; <59> 4/30/92 DTY #1023455 <JSM>: The offset from change <51> should be $14, not
; #14.
; <58> 4/8/92 DTY #1026928,<FM>: Check CurMap before loading a 'lpch' resource. If
; its 1, set ROMMapInsert to true to get linked patches from ROM.
; <57> 4/7/92 pvh Calculate end of boot globals using (a5+BootGlobals.size)
; <56> 4/6/92 DTY #1024666,<JSM>: Three lines were lumped into one of the NOT
; INITVERSION conditionals, breaking linked patch INITs. Fix this
; up, and added judicious comments which Fred was too whipped to
; do because his ride was here.
; <55> 4/4/92 FM #1024666: Made LinkedPatches more memory friendly by
; copying the 'lpch' resources up above the boot globals and
; releasing the resources. This was necessary because there wasn't
; enough room for Mac II class machines with 2 megs to load their
; linked patches. This copying over the boot globals isn't done
; with the init version.
; <54> 3/26/92 DTY #1024282,<gbm>: Doing a _MoveHHi isnt good enough, so just take
; it out. Were going to load the Process Manager segments in
; BeforePatches.a ('PTCH' 0) so theyll be even lower, and theyll
; load before linked patches.
; <53> 3/25/92 DC FM - Heeeyyyy! somebody fogot to set up a0 to point to the
; 'ptbl' resource for the HLock call. As a result gPatchTable
; doesn't get set up and linked patch inits fail.
; <52> 3/16/92 DTY Dont try to release the patch range table for linked patch
; INITs, since we dont know whether it came from a resource or
; from the static table. Well, we do know, but its a pain to
; figure out again.
; <51> 3/13/92 DTY #1023455: In CheckTerror, set hasTerror if this is a Horror ROM
; as well.
; <50> 3/10/92 DTY #1024282: Before locking the 'lpch' resources down, move them
; high so free block coalescing will be better. Among other
; things, this allows the Process Manager segments to load a
; little lower.
; <49> 2/27/92 DTY For linked patch inits, if a 'ptbl' resource couldnt be found,
; use the static patch range table at the end of the loader which
; causes all linked patches to load.
; <48> 2/12/92 DTY Using _GetResource to fetch 'lpch' resources would get 'lpch'
; resources from the System file if a Gibbly doesnt have that
; particular 'lpch' resource. Always use _Get1Resource now to get
; 'lpch's so this wont happen.
; <47> 2/4/92 JSM (with FM) Move patchTable global above residentTop so we can use
; the same version of whip for 7.0 and CubeE.
; <46> 2/3/92 JSM (with FM) Add more comments so we wont break the whip dcmd in
; the future.
; <45> 1/31/92 JSM Update comments on format of 'lpch' resources to show extra
; field thats only in those resources for a single ROM.
; <44> 1/27/92 DTY Add some boundary conditions to CheckPatchTableRange: Provide a
; way out for empty range lists.
; <43> 1/6/92 DTY Linked patch INITs dont get passed a patch table, causing traps
; to point to wierd places, resident code not being there, and
; other strange things. If the loader is being built as part of an
; INIT, read in a 'ptbl' resource explicitly instead of expecting
; a pointer to one in A3.
; <42> 1/3/92 DTY Check the jump table entry number when making modules resident,
; so that entries that a Gibbly wants to skip will not be loaded.
; <41> 12/12/91 DTY Nick boo-bood in AllocateResidentBlock. D6 wasnt getting set
; up with the block pointer in the non-INIT case.
; <40> 12/12/91 ngk Use locked handle instead of pointer for code when compiling for
; INIT version. This is so MacsBug symbols show up.
; <39> 11/12/91 DTY Add support for patch table in InstallOrExecute. Check the
; routine number, and see if its in a range specified in the
; patch table before installing the patch.
; <38> 10/28/91 SAM/KSM Rolled in Regatta changes:
; Adding hasC96 and hasPwrMgr conditionals and made hasTerror
; identify Zydeco ROM too.
; Added code for hasTERROR and notTERROR.
; <37> 8/14/91 JSM Completely remove debugging support for old version of whip,
; add/change lots and lots more comments, especially in the areas
; of ForPatchTable, ForAllReferences, and Find/MakeResident mess.
; <36> 8/12/91 JSM Add lots more comments, especially in the area of unpacking the
; jump table.
; <35> 8/8/91 FM Delete obsolete code and fixing up comments. Nuked 32-bitQD
; check, 'bnvs' resource, and HasSystem6LinkedPatches
; <34> 8/6/91 FM Fixed bug in recursion for Install Procs. Required splitting the
; jump table into two parts. Also made it possible for references
; to use the “real thing” if an island was out of range. Added
; some comments to help readability. Also added another field in
; the CurApName debug info that tells whip that the jump tables
; have changed in this version of the loader.
; <33> 4/2/91 dba KIP: add new hasEricksonSoundMgr conditional, correct
; notEricksonSoundMgr
; <32> 12/20/90 gbm (dba) Change so that debugging information is kept out of the
; way of people who check first long of CurApName to see if it is
; boot time.
; <31> 12/18/90 gbm (dba) Adding conditional for preventing loading of the patch
; protector twice. Check for 32-bit addressing with Gestalt
; instead of low memory.
; <30> 12/14/90 BBM (dba) add two conditionals for linked patching, using24BitHeaps,
; using32BitHeaps
; <29> 10/12/90 JDR (dba) turn off more of the debugging stuff in the INITVERSION
; <28> 10/8/90 JDR Add notVM, change hasPMMUNoVM to hasPMMU
; <27> 9/26/90 JDR Made pre-7.0 INITs save the current zone, since INITs would run
; in an application heap and not the System heap. (Darin told me
; what to do.)
; <26> 9/25/90 KIP (really dba) Added new conditional noEricksonSoundMgr.
; <25> 9/7/90 stb & vl. add conditional test for INIT builds
; <24> 8/31/90 stb conditionalize out debugger if building with -init
; <23> 8/21/90 csd Added code for hasMemoryDispatch condition.
; <22> 8/19/90 dba add six new loading conditions that were needed for Sony and MMU
; patching; add new debugging features that work through low
; memory globals $912-91B; fixed greater than one byte conditions
; case that is used now that we have eleven conditions total
; <21> 8/10/90 DTY (w/ dba) Make PatchProc take offsets into ExpandMem.
; <20> 8/2/90 MR Fix initversion bug in FlushInstructionCache to reload registers
; before rts
; <19> 7/31/90 DTY Check the vector at jCacheFlush to make sure its an implemented
; routine before jumping to it for linked patch INITs.
; <18> 7/30/90 dnf (really dba) fix recursion for FindResident; this caused
; “force-resident” stuff to be ineffective, except in the topmost
; level of an InstallProc
; <17> 7/16/90 dba add support for counting the number of patches
; <16> 7/11/90 dba get dcImport to work
; <15> 7/10/90 DTY Test for presence of 32 Bit Color Quickdraw, and set not32BitCQD
; conditional flag appropriately. This flag is only defined for
; System 6 builds which have linked patches, or the patch is being
; built as an INIT.
; <14> 7/6/90 dba add a case for 32-bit references
; <13> 6/22/90 dba fix island case for LEA and PEA
; <12> 6/22/90 dba stuff the number of the patch being loaded into low memory so
; that we can debug more easily
; <11> 6/6/90 dba make code for islands
; <10> 5/1/90 BBM enable linked patches on 6.0 world
; <9> 4/15/90 dba delete unnecessary FlushInstructionCache from GetPatchAddress
; <8> 4/11/90 BBH remove flashmenu/sysbeep from INIT version
; <7> 4/10/90 dba make A5 world to pass to linked patches
; <6> 4/9/90 dba change so FlushInstructionCache does not trash registers
; <5> 4/9/90 BBH added compiler directive INITVERSION so that we can make a
; standalone INIT version of this code (see SysObj.make for build
; rules for LinkedPatchLoaderINIT.rsrc and the example
; CommToolboxPatchTest)
; <4> 4/9/90 dba make FlushInstructionCache routine that uses the jCacheFlush
; vector (not jFlushCache, which is for the file system cache)
; <3> 3/22/90 dba change name of loading routine; fix case with no lpchs
; <2> 3/21/90 dba fix range check so that it will work from -32768 to 32767
; <1> 3/9/90 KSM First checked in.
; 2/19/90 dba add judicious flushing of instruction cache
; 2/11/90 dba change so it loads a set of resources (for minimal install)
; 2/1/90 dba integrate changes from code review; move resident to high bit
; 1/30/90 dba patch and install in link order
; 1/25/90 dba added a header
;
; This file contains a patch loader that completes the linking begun by the LinkPatch tool.
; The code consists of a number of modules with entry points, references to other modules and
; entry points, and references to ROM addresses. The loader reads a table in the patch which
; specifies which patches and install routines should be executed on the machine. The routines
; which need to remain resident after patches are loaded are moved into a block in the system
; heap, and references within this block are changed to be PC-relative. The ROM references are
; fixed up to point to the appropriate ROM, and a jump table is built at A5 so that non-resident
; code can reference resident code (which may be more than 32K away). After that, all the patches
; are installed, and all of the install code is executed. Then the non-resident code and the jump
; table are deleted, and we return.
;
; ———————————————————————
; a note about conditions
; ———————————————————————
;
; A “ROM set” is a bitmap of ROMs that a particular ROM bind applies to. It may occupy 1-4 bytes
; depending on the number of ROMs. The loader is reassembled if the number of ROMs changes
;
; A “condition set” is a ROM set combined with bits for other conditions. It too may occupy 1-4
; bytes (always at least as many as a ROM set). Each condition must have code in this loader to
; determine if the condition is true (at label GetNonROMConditions).
;
; ——————————————————
; some of the limits
; ——————————————————
;
; ROMs must be less than 2^23 bytes long (8M)
; we can patch up to 31 total different ROMs and conditions total
;
; —————————————————————————————
; format of the 'lpch' resource
; —————————————————————————————
;
; all references to ROM are linked through the code, but when the reference is a distance
; greater than 128K, there is an entry in the ROM reference exception table; the word following
; the link in the code contains the ROM table entry number; this is 0 for the first entry, 1 for
; the next, etc.
;
; all references within a module are linked in a list; the head of this list is encoded in
; the packed jump table; the low bit of the link is set to 1 for “force resident” references;
; if a “force resident” reference is encountered in install code, the referenced module is moved
; into resident space in the system heap
;
; † 2 bytes number of 'lpch' resources for this ROM
; * 2 bytes number of entries in the bound ROM address table
; * 2 bytes number of entries in the jump table
; 4 bytes size of the code
; code
; 2 bytes first ROM table entry number (-1 means no packed ROM table entry)
; packed ROM table:
; 3 bytes per ROM ROM offset for each ROM (high bit set for end of table)
; ROM reference exception table:
; 3 bytes per exception 0 entry in code means exception (0 means end of table)
; packed jump table:
; 1 byte size
; 0-251 distance from current position in the code to next entry or
; module specified in the packed jump table, divided by 2 (i.e. 0-502 bytes)
; 252 means skip entries in the jump table
; 0 means end of packed jump table
; 1-254 means number of jump table entries to skip
; 255 means word follows with number of jump table entries to skip
; 253 means previous was reference list head for this module
; 254 means previous was an entry, not a new module
; 255 means word distance from current position in the code to next
; entry or module specified in the packed jump table follows
; * packed patch table (sorted in jump table order):
; * 1-4 bytes condition set for which the following patches/installs apply
; * 1 byte number of jump table entries to the next patch
; * 255 means word follows (0 word is end of table)
; * 254 means new condition set follows
; * 2 bytes trap number (0 means an install rather than a patch)
;
; † - Means only in 'lpch' resources that are for a single ROM (e.g. those with resource id 1, 2, 4, 8, etc.)
; * - Means only in the universal 'lpch' resource. The universal 'lpch' resource is the
; resource that is loaded on all known ROMs, that is, its resource id has the bit for each ROM set to one.
;
; ————————————————————————————
; thumbnail sketch of the code
; ————————————————————————————
;
; LoadPatch
; load code from resource and detach it
; allocate A5 world and jump table and large block for resident code
; unpack ROM addresses into jump table
; fix ROM references to point to ROM (traverse long linked list of references)
; unpack real jump table (store size of routine and start of reference list for each proc)
; traverse patch table:
; for each triggered routine:
; FindResident or MakeResident (depending on whether it is a install routine or patch)
; cut back resident code
; traverse patch table:
; for each triggered patch routine (not install routine):
; traverse routine:
; put in old address wherever we see the magic cookie
; install patch
; for each triggered install routine:
; MakeNonResident
; execute
; dispose non-resident code and A5 world
;
; FindResident(entry)
; walk back the jump table until you reach a proc
; traverse linked list of references
; if the reference is marked resident:
; MakeResident(entry)
;
; MakeResident(entry)
; do nothing if this entry is already active
; walk back the jump table until you reach a proc
; allocate space in the resident area (size is in proc)
; copy routine to the resident area
; assign addresses to all items in the jump table up to the next proc
; traverse linked list of references
; MakeResident(entry)
; fix up that pesky reference
;
; MakeNonResident(entry)
; do nothing if this entry is already active
; walk back the jump table until you reach a proc
; assign addresses to all items in the jump table up to the next proc
; traverse linked list of references
; MakeNonResident(entry)
; fix up that pesky reference
;
print push,off
load 'StandardEqu.d'
include 'MMUEqu.a'
include 'BootEqu.a'
include 'GestaltEqu.a'
include 'HardwarePrivateEqu.a'
include 'UniversalEqu.a'
include 'Traps.a'
include 'LinkedPatchMacros.a'
print pop
_GetTrapWordAddress opword $A546 ; *** move into Traps.a
_SetTrapWordAddress opword $A447 ; *** move into Traps.a
dsLinkedPatchReferenceTooFar equ 97 ; *** move into SysErr.a
k32BitCQDTrap equ $AB03 ; trap number for 32-bit QuickDraw
kUnimplementedTrap equ $A89F ; trap number for unimplemented trap
kGestaltTrap equ $A0AD ; trap number for Gestalt
kMemoryDispatchTrap equ $A05C ; trap number for MemoryDispatch
if &type('INITVERSION') = 'UNDEFINED' then ; <5>
INITVERSION: equ 0
endif
;————————————————————————————————————————————————————————————————————————————————————————————————————
; special low-memory used in conjunction with the whip dcmd for debugging linked patches
LinkedPatchDebuggerPtr equ CurApName+8 ; call this routine so dcmd can snarf symbols
NewLoaderActive equ CurApName+$16 ; set to zero so that dcmd knows the new loader is active
; special low-memory used for debugging linked patches.
; loader will drop into debugger when about to load specified jump table entry or range of entries
StashJumpTableEntryNum equ CurApName+$10 ; place jump table entry number here as we go <32>
StopOnJumpTableEntryNum equ CurApName+$12 ; drop into debugger when loading this entry <32>
StopOnJumpTableEntryNumHighest equ CurApName+$14 ; or loading this range if this one is ¬ffff <32>
;————————————————————————————————————————————————————————————————————————————————————————————————————
; equates from the patch linker
kAbsoluteOpcode equ 15 ; this opcode means an absolute address
;————————————————————————————————————————————————————————————————————————————————————————————————————
; entry in the jump table
; note loader depends on the fact that both jump table and ParallelJumpTableEntryInfo
; are equal in size! The parallel jump table entry info is walked in step with the
; jump table. By keeping the records equal in size the parallel info will always be a fixed
; offset away from the corresponding jump table entry.
; This is the record for a jump table entry. It allows non-resident install code
; to call the resident patch proc routines. We also use the opcode field to
; indicate when a routine has been "nailed-down".
JumpTableEntry record 0 ; for each entry in the jump table:
opcode ds.w 1 ; initially 0 but becomes a JMP.L once this entry
; is nailed down.
address ds.l 1 ; address, in either resident non-resident block
size equ *
endr
; This is a record for additional information pertaining to each jump table entry.
; Keeping this extra information in a seperate record allows more jump table
; entries.
ParallelJumpTableEntryInfo record 0 ; Extra info for each entry in the jump table:
referenceListHead ds.w 1 ; head of list (even) or 1 for no list (odd) it
; is an offset into the code of the 'lpch' resource
flags ds.w 1 ; flags for indicating islands and for preventing
; cycles in FindResident and MakeResident recursion.
routineSize ds.w 1 ; size of the routine is here; 0 for entry point
size equ *
endr
; these bits are set in the flags field of the parallel info reord.
islandFlagBit equ 0 ; this is the bit that identifies an island
findSeenFlagBit equ 1 ; this is the bit set during FindResident
; to prevent cycles during the recursion
makeSeenFlagBit equ 2 ; this is the bit set during a MakeResident
; to prevent cycles during the recursion
; constants used in the manipulation of jump table information
kNailedDown equ $4EF9 ; A JMP instructiion. Stuck in the opcode field
; of a jump table entry when it is nailed-down
kInitialReferenceListHead equ 1 ; A good initial value because there cannot be
; an odd offset. Couldnt use zero cause thats a valid offset
kInitialRoutineSize equ -1 ; unused jump table entries (low bit clear)
;————————————————————————————————————————————————————————————————————————————————————————————————————
; All 'lpch' resources that need to be loaded are referenced by PatchResourceEntry records.
; The records are kept on the stack. The last record is followed by a 0 (i.e. endOfList is 0).
; Whenever we apply a function to all patches we can walk down the stack until we hit a 0.
PatchResourceEntry record 0 ; each entry on the stack
endOfList equ * ; list of entries is terminated by 0 on the stack
romSet ds.w 1 ; number of the ROM set
resourceHandle ds.l 1 ; handle to the 'lpch' resource
ptr ds.l 1 ; pointer into the resource (advances as we go)
codePtr ds.l 1 ; pointer to code in the resource
size equ *
endr
;————————————————————————————————————————————————————————————————————————————————————————————————————
; globals
g record {qdGlobalsPtr} ; an A5 world
start equ *
romNumber ds.w 1 ; ROM index number
ParallelJumpTableEntryInfoTableOffset ds.l 1 ; Offset to extra info for jump table entries
; normally held in register D3
nonROMConditions ds.l 1 ; other loading conditions (like VM, sound, etc.)
patchRangeTable ds.l 1 ; <39> Saved pointer to a patch range table ('ptbl')
; NOTE: whip depends on the location of residentTop
; if you add more globals, add them •before• here
residentTop ds.l 1 ; pointer to available space in resident block
; Bumped up as we go so we know how far to cut
; back the resident block when were done.
offset equ *-start
qdGlobalsPtr ds.l 1 ; QuickDraw globals
romTable ds.b 0 ; place for ROM table to start
; NOTE: whip depends on the location of jumpTable
jumpTable ds.b 0 ; place for jump table to start
size equ *-start
endr
;————————————————————————————————————————————————————————————————————————————————————————————————————
LoadLinkedPatches proc export
; Load a patch from a resource.
;
; In:
; a3 pointer to patch range table ('ptbl') from 'boot' 3
; if this is a linked patch INIT, uses static table instead
;
; Out:
; can trash d0-d2/a0-a1
import ForPatchTable
import FindOrMakeResident
import InstallOrExecute
import ROMVersions
LoadLinkedPatchesRegisters reg d3-d7/a2-a5
movem.l LoadLinkedPatchesRegisters,-(sp)
GetROMNumber
; get the number of this ROM
moveq #NumROMs$-1,d3 ; ROM number counter
lea ROMVersions,a0 ; point to a table of ROM versions
move.l ROMBase,a1 ; get ROM version word from ROM
move.w 8(a1),d1 ; now we have the ROM version
@next
cmp.w (a0)+,d1 ; is this the version?
dbeq d3,@next ; keep looking
beq.s @ok ; yes, ROM found
moveq #dsNoPatch,d0
_SysError
@ok
;————————————————————————————————————————————————————————————————————————————————————————————————————
; There used to be essentially two copies of the linked patches in memory, one in the detached
; 'lpch' resources, and a copy of all the resident routines in the resident block. This is fine except
; that this doesnt leave enough memory to boot on 2 Meg machines. Fred was astute enough
; to realize that we have half of memory not being used sitting above MemTop/2 + 1K,
; so we now copy the 'lpch' resources into this area and disposes the detached resource
; handle. While we still have two copies around, one is not in the system heap,
; leaving more free space there for the patches themselves to load and waste memory.
LoadPatchResources
; load the patch resources
if NOT INITVERSION then
move.l MemTop,-(sp) ; <55>
; calculate end of boot globals in a1 (a5+BootGlobals.size)
move.l a5,a1 ; <57>
add.l #BootGlobals.size,a1 ; <57> Skip past globals
endif
moveq #0,d7 ; calculate the total size of resources
moveq #0,d4 ; count the number of resources
clr.w -(sp) ; mark end of PatchResourceEntries
; <60> Save CurMap in case its 1 for the ROM map, since it will get set to
; SysMap by the Resource Manager when leaving a GetResource call.
move.w CurMap,d6 ; <60>
moveq #0,d1 ; start going through the resources
@next
addq.w #1,d1 ; advance to the next resource
btst #NumROMs$,d1 ; have we done all of the ROMs?
bnz.s @done
btst d3,d1 ; is this resource needed for this ROM?
bz.s @next ; no, go on
cmp.w #1,d6 ; <58> <60> Check if CurMap is 1.
bne.s @linkedPatchNotInROM ; <58> If its not 1, its a resource in a real file
move.w #MapTrue,ROMMapInsert ; <58> Set ROMMapInsert so the call to _Get1Resource will look in the ROM resource map for the 'lpch'
@linkedPatchNotInROM ; <58>
subq.l #4,sp ; make room for the resource
move.l #'lpch',-(sp)
move.w d1,-(sp)
_Get1Resource ; <5> BBH - Init Version does 1 deep search ONLY!
move.l (sp)+,d2
bnz.s @ok ; yes, enough memory to load the patch
subq.l #2,sp ; make room for error
_ResError ; check if the error is real
move.w (sp)+,d0 ; get the error code
bz.s @notFound ; no error is OK
cmp.w #resNotFound,d0 ; resNotFound?
beq.s @notFound ; yes, resNotFound is also OK
moveq #dsMemFullErr,d0
_SysError
@notFound
tst.w d4 ; is this the first resource (i.e. the one for this ROM only)?
bnz.s @next ; no, its OK that it be missing
moveq #dsNoPatch,d0 ; yes, if it is missing there is an error
_SysError
@ok
addq.w #1,d4 ; count the resources
move.l d2,-(sp)
_DetachResource ; detach the resource
move.l d2,a0
_GetHandleSize ; add its size in
add.l d0,d7 ; calculate total size of all resources
if NOT INITVERSION then
; Ensure that each copy of the linked patch data starts at an even address by copying
; an even number of bytes.
btst #0,d0 ; <56> Is the size of the 'lpch' resource odd?
bz.s @copyLinkedPatchAboveBootGlobals ; <56>
addq.l #1,d0 ; <55> If it is, make it even
; Copy the 'lpch' resource data up above the boot globals. A1 is set up
; to be the address where the data should be copied to. Its initially MemTop/2 + 1K.
; After each _BlockMove, it gets bumped up, so the next 'lpch' is copied above this
; one.
;
; Before moving the data, we make space for a faked up handle to point to it:
;
; +------------------+
; + lpch +
; + data +
; +------------------+
; + ptr to lpch data +
; +------------------+
; + . +
; + . +
; + . +
; +------------------+
; + boot globals +
; +------------------+
@copyLinkedPatchAboveBootGlobals
addq.l #4,a1 ; <55> Leave space for a faked up handle
move.l d0,-(sp) ; <56> Save size of 'lpch' resource
move.l (a0),a0 ; <55> Get pointer to data
_BlockMove ; <55> Copy it above the boot globals
move.l (sp)+,d0 ; <56> Restore size.
lea -4(a1),a0 ; <55> Get longword we reserved for the fake handle
move.l a1,(a0) ; <55> And point it to the 'lpch' data
add.l d0,a1 ; <55> Bump A1 to the address the next 'lpch' resource should be copied to.
; Create the PatchResourceEntry record for this 'lpch' resource
subq.l #8,sp ; <55> make room for codePtr (used later)
move.l a0,-(sp) ; <55> push “Handle”
move.w d1,-(sp) ; <55> also push the romSet (same as resource ID)
move.l d2,a0 ; <56>
_DisposeHandle ; <56> Dispose of the handle to the 'lpch' resource (now that we have a copy)
else
subq.l #8,sp ; make room for codePtr and ptr (used later)
move.l d2,-(sp) ; push resourceHandle
move.w d1,-(sp) ; also push the romSet (same as resource ID)
endif
bra.s @next
@done
tst.w PatchResourceEntry.endOfList(sp) ; did we load any?
bz Done ; no, get out of here
; Inside Mac IV p. 257 states that BufPtr may not go lower than MemTop/2 + 1K. For the algebraically
; impaired, that means that MemTop = (minBufPtr - 1K) * 2. In case some linked patches change
; BufPtr, move MemTop higher so that patches that want to can move BufPtr down without trashing
; the 'lpch' data we copied above the boot globals.
if NOT INITVERSION then ; <56>
sub.w #1024,a1 ; <55> end of lpch data - 1024
add.l a1,a1 ; <55> 2*(end of lpch data - 1024) <30>
move.l a1,MemTop ; <55> MemTop := 2*(end of lpch data - 1024)
endif ; <56>
;————————————————————————————————————————————————————————————————————————————————————————————————————
AllocateResidentBlock
; allocate resident pointer the same size as all of patch resources *** better initial size?
move.l d7,d0
if INITVERSION then ; <40>
_NewHandle sys ; <40> alloc a locked handle instead of of pointer
bnz.s @allocErr ; <40> this is to get MacsBug to show labels
_HLock ; <40>
move.l (a0),a0 ; <41> Get pointer to block
bra.s @ok ; <40>
else
_NewPtr sys
bz.s @ok ; must succeed
endif
@allocErr
moveq #dsMemFullErr,d0
_SysError
@ok
move.l a0,d6 ; keep pointer to resident code in d6
;————————————————————————————————————————————————————————————————————————————————————————————————————
LockDownNonResidentPatches
; now we can lock down the non-resident part of the patches
move.l sp,a4 ; get first PatchResourceEntry
@next
tst.w (a4)+ ; end of table?
bz.s @done
move.l (a4)+,a0 ; now we can lock down each resource
if INITVERSION then
_HLock
move.l (a0),d0 ; dereference it (must be locked)
_StripAddress
else
move.l (a0),d0 ; <55> Dereference the fake handle to the 'lpch' data above the boot globals.
endif
move.l d0,(a4)+ ; store the dereferenced pointer in ptr
add.l #4,a4 ; skip over space for the codePtr
bra.s @next
@done
CheckForMissingResources
; now we check the number of 'lpch' resources stored in the first resource
; (which is the one used for just this ROM) to see if it is too low
lea -2-PatchResourceEntry.size+PatchResourceEntry.ptr(a4),a4 ; position at the ptr
move.l (a4),a0 ; get the pointer
cmp.w (a0)+,d4 ; is it the right number of resources?
beq.s @ok
moveq #dsBadPatch,d0 ; no, a resource was missing for this ROM
_SysError
@ok
move.l a0,(a4) ; advance past the count
;————————————————————————————————————————————————————————————————————————————————————————————————————
AllocateA5World
; get size of A5 world by looking at sizes at start of universal resource for all ROMs
; First we calculate how much space will be needed for the ROM table.
; Then we calculate how much will be needed for the patch proc and install
; proc jump table information. Since we use the same allocated space for both
; the larger of the two will determine the size of our non resident block
; we also set up the global offset into the jump tables extra information
move.l PatchResourceEntry.ptr(sp),a0 ; point at start of universal resource
moveq #0,d0
move.w (a0)+,d0 ; calculate the size of the ROM table
lsl.l #2,d0 ; x4 (ROM table entries are 4 bytes)
moveq #0,d5
move.w (a0)+,d5 ; calculate the size of the jump table
move.l d5,d1
addq.l #1,d1 ; one extra entry for coding convenience
move.l d1,d2
lsl.l #3,d1 ; x8 (jump table entries are 6 bytes)
lsl.l #2,d2 ; x4 (jump table entries are 6 bytes)
add.l d2,d1 ; 8+4 = 12 ( 6 bytes per jump table entry
; 6 bytes parallel info per entry )
move.l a0,PatchResourceEntry.ptr(sp) ; now all resources point to their code size
cmp.l d0,d1 ; get the larger of the two
bls.s @gotLarger
move.l d1,d0
@gotLarger
moveq #g.size,d2
add.l d2,d0 ; larger table + globals = total size
_NewPtr ,Clear ; create the A5 block
bz.s @ok ; must succeed
moveq #dsMemFullErr,d0
_SysError
@ok
move.l (a5),g.qdGlobalsPtr+g.offset(a0) ; copy the QuickDraw globals pointer
lea g.offset(a0),a5 ; switch to the new A5
move.w d3,g.romNumber(a5)
move.l d6,g.residentTop(a5) ; none of the resident pointer is used yet
if INITVERSION then ; <43>
Import StaticRangeTable ; <49>
lea StaticRangeTable,a3 ; <49> Use a built in patch range table for linked patch INITs.
endif ; <43>
move.l a3,g.patchRangeTable(a5) ; <39> Save the pointer to the patch range table.
lsr.l #1,d1 ; d1 is the size of the Jump Table Entries plus
; the parallel info entries. halfway thru jump table
lea g.jumpTable(a5),a0 ; space is start of info table so set up the offset to
move.l d1,g.ParallelJumpTableEntryInfoTableOffset(a5) ; info table for later
;————————————————————————————————————————————————————————————————————————————————————————————————————
GetNonROMConditions
; figure out non-ROM conditions that apply
import MrGestalt,TrapImplemented
; condition evaluation can trash standard registers (d0-d2/a0-a1)
; condition bit should be set in the flags long-word, d3
moveq #0,d3 ; no flags
CheckC96 ; <3>
; hasC96
MOVE.L ROMBase,A0 ; Get base of ROM
CMPI.W #$067C,8(A0) ; Do we have universal tables?
BNE.S @no ; -> Nope, no C96
TestFor SCSI96_1Exists ; C96 SCSI chip?
BEQ.S @no ; -> Nope, no C96
@yes BSET.L #Condition$hasC96,D3 ; We have a C96 SCSI chip!
@no
CheckPowerMgr ; <3>
; hasPwrMgr
MOVE.W HwCfgFlags,D0 ; Get them Hardware Config Flags
BTST.L #hwCbPwrMgr,D0 ; Do we have a PowerMgr?
BEQ.S @no ; -> No.
@yes BSET.L #Condition$hasPwrMgr,D3 ; We have a PowerMgr chip!
@no
CheckTERROR ; <SAM>
; notTERROR
MOVE.L ROMBase,A0 ; Get base of ROM
CMPI.W #$067C,8(A0) ; Is the ROM major version 67C?
BNE.S @no ; -> Nope, say notTERROR
CMPI.B #$15,18(A0) ; Is the ROM minor version #$15?
BEQ.S @yes ; -> Yes, we have TERROR
CMPI.B #$17,18(A0) ; Is the ROM minor version #$17? <3>
BEQ.S @yes ; -> Yes, we have Zydeco ( == TERROR) <3>
btst #0,20(a0) ; <51> <59> Horror ROM?
bnz.s @yes ; <51> Yes. Load Terror patches for this ROM too.
@no
BSET.L #Condition$notTERROR,D3 ; We do NOT have TERROR
BRA.S @done
@yes BSET.L #Condition$hasTERROR,D3 ; We have a TERROR ROM
@done
CheckAUX
; notAUX
btst.b #hwCbAUX,HWCfgFlags ; are we under A/UX?
bnz.s @no ; yes, no flag to set
bset.l #Condition$notAUX,d3
@no
CheckVM
; notVM
move.l #gestaltVMAttr,d0 ; check for VM
jsr MrGestalt
btst #gestaltVMPresent,d0
bnz.s @done ; bit is set, we have VM, so dont set flag
@no
bset.l #Condition$notVM,d3
@done
CheckMMU
; hasHMMU
; hasPMMU
cmp.b #2,CPUFlag ; are we on a 68020 or better?
blo.s @done ; no, there is no MMU
move.b MMUType,d0 ; get the MMU type
cmp.b #HMMU,d0 ; is it an HMMU?
beq.s @HMMU ; yes, there is an HMMU
cmp.b #PMMU851,d0 ; is there some kind of PMMU?
blo.s @done ; no, there is no PMMU
bset.l #Condition$hasPMMU,d3
bra.s @done
@HMMU
bset.l #Condition$hasHMMU,d3
@done
CheckMemoryDispatch
; hasMemoryDispatch
move.w #kMemoryDispatchTrap,d0
_GetTrapAddress newOS ; get address of _MemoryDispatch
jsr TrapImplemented
beq.s @no ; no VM
bset.l #Condition$hasMemoryDispatch,d3
@no
CheckSonyDriverVersion
; has800KDriver/hasFDHDDriver
move.l UTableBase,a0 ; look at the Sony drivers DCE
move.l 16(a0),a0 ; get the DCE handle
move.l (a0),a0 ; dereference it
cmp.b #1,dCtlQueue+1(a0) ; is it the old 800K driver?
ble.s @800K
bset.l #Condition$hasFDHDDriver,d3
bra.s @done
@800K
bset.l #Condition$has800KDriver,d3
@done
CheckIWM
; hasIWM
move.l ROMBase,a0 ; get ROM version
cmp.w #$067C,8(a0) ; is this the IIci ROM family?
bne.s @yes
TestFor IWMExists ; are we running with an IOP?
bz.s @no ; yes, so we dont have an IWM
@yes
bset.l #Condition$hasIWM,d3
@no
CheckEricksonMistake
; hasEricksonOverpatchMistake
move.l ROMBase,a0 ; get ROM version
cmp.w #$067C,8(a0) ; is this the IIci ROM family?
bne.s @no
cmp.w #$12F1,18(a0) ; check sub-version for Erickson ROM
bne.s @no
bset.l #Condition$hasEricksonOverpatchMistake,d3 ; the Erickson ROM has 1-ROM-only bugs
@no
CheckEricksonSoundMgr
; hasEricksonSoundMgr
; notEricksonSoundMgr
move.l ROMBase,a0 ; get ROM version
cmp.w #$067C,8(a0) ; is this the IIci ROM family?
bne.s @no
cmp.w #$12F1,18(a0) ; check sub-version for Erickson ROM or newer
blo.s @no
bset.l #Condition$hasEricksonSoundMgr,d3 ; Erickson and newer ROMs have a new Sound Mgr.
bra.s @done
@no
bset.l #Condition$notEricksonSoundMgr,d3 ; older ROMs have the original IIci Sound Mgr.
@done
CheckHeapsMode ; <30><31>
; using24BitHeaps
; using32BitHeaps
move.l #gestaltAddressingModeAttr,d0 ; check attributes
jsr MrGestalt
btst #gestalt32BitAddressing,d0
bnz.s @32
bset.l #Condition$using24BitHeaps,d3 ; set the flag for the 24 bit heap
bra.s @done ; time for the next conditional check
@32
bset.l #Condition$using32BitHeaps,d3 ; set the flag for the 32 bit heap
@done ; <30><31>
CheckNoPatchProtector
; noPatchProtector ; <31>
move.w #$A800,d0 ; get address of first Toolbox trap
_GetTrapWordAddress ; using new patched version of GetTrapAddress
move.l a0,a1
moveq #0,d0 ; get address of first OS trap
_GetTrapAddress ; using standard GetTrapAddress
cmp.l a0,a1 ; did we get the same answer both times?
bne.s @no ; no, the patch protector is working
bset.l #Condition$noPatchProtector,d3 ; yes, there is no patch protector yet
@no ; <31>
; *** new conditions should be added here ***
StoreConditions
if NumConditions$ <= 8 then
move.b d3,g.nonROMConditions(a5) ; store all the flags
elseif NumConditions$ <= 16 then
move.w d3,g.nonROMConditions(a5) ; store all the flags
else
move.l d3,g.nonROMConditions(a5) ; store all the flags
endif
;————————————————————————————————————————————————————————————————————————————————————————————————————
UnpackROMTable
; advance through all the resources, unpacking the ROM address tables
; also initialize the codePtr field of the PatchResourceEntries
move.l sp,a4 ; get first PatchResourceEntry
@nextResource
move.w PatchResourceEntry.romSet(a4),d0 ; get the ROM set (same as endOfList)
bz.s @done ; no ROMs means we are done
move.l PatchResourceEntry.ptr(a4),a2 ; get pointer into resource (advance as we go)
move.l (a2)+,d1 ; get size of the code
move.l a2,PatchResourceEntry.codePtr(a4) ; store pointer to code
add.l d1,a2 ; point at ROM table
; unpack all of the items in the ROM table
moveq #0,d2
move.w (a2)+,d2 ; get the first ROM table entry number
bmi.s @doneResource ; -1 means no ROM references in this 'lpch'
lsl.l #2,d2 ; turn into offset into ROM table (ROM table enntries are 4 bytes)
lea g.romTable(a5,d2.l),a0 ; point at this entry in the ROM table
; each entry in the ROM table has a ROM bind for every ROM this 'lpch' applies to
; *** isnt the offset into each entry in the packed ROM table constant for the ROM were loading on?
; *** if so, why not calculate it once instead of looping through NumROMs$ and bit testing each time?
; *** also, the size of each entry in the packed ROM table is constant, too (3 bytes * number of ROMs
; *** supported by this 'lpch')
@nextEntry
moveq #NumROMs$-1,d1 ; start with the last ROM
@loopROM
btst.l d1,d0 ; does this ROM have an entry for this 'lpch'?
bz.s @nextROM ; no, go on to the next ROM
cmp.w g.romNumber(a5),d1 ; is this the ROM we are loading for?
bne.s @skipEntry ; no, skip this entry in the packed ROM table
moveq #$7F,d2 ; yes, clear high bit (used as flag for end of table)
and.b (a2)+,d2 ; get the first byte
swap d2 ; move it up
move.b (a2)+,d2 ; get the second byte
lsl.w #8,d2 ; move it up
move.b (a2)+,d2 ; get the third byte
add.l ROMBase,d2 ; add offset to the ROMBase
bra.s @nextROM ; we have the ROM bind for this ROM,
; but we have to continue on to the end of this entry
@skipEntry
addq #3,a2 ; skip the offset *** define a constant for packed ROM table offset size
@nextROM
dbra d1,@loopROM ; move on to the next ROM
move.l d2,(a0)+ ; store the ROM address (or 0) *** why 0?
btst #7,-3(a2) ; test high bit of last entry
bz.s @nextEntry
@doneResource
move.l a2,PatchResourceEntry.ptr(a4) ; PatchResourceEntry.ptr now points to ROM reference exception table
lea PatchResourceEntry.size(a4),a4 ; advance to the PatchResourceEntry for the next resource
bra.s @nextResource
@done
;————————————————————————————————————————————————————————————————————————————————————————————————————
FixupROMReferences
; advance through all the resources, fixing up ROM references inside the code
move.l sp,a4 ; get first PatchResourceEntry
@nextResource
move.w PatchResourceEntry.romSet(a4),d0 ; get the ROM set (same as endOfList)
bz.s @done ; no ROMs means we are done
move.l PatchResourceEntry.ptr(a4),a2 ; get pointer to ROM reference exception table
; find the ROM references and fix them up
move.l PatchResourceEntry.codePtr(a4),a0 ; point at the start of the code
@nextTableOffset
moveq #0,d0
move.b (a2)+,d0 ; get high byte
swap d0
move.b (a2)+,d0 ; get middle byte
lsl.w #8,d0
move.b (a2)+,d0 ; get low byte
tst.l d0 ; check for end of table
bz.s @doneResource
@useOffset
add.l d0,a0 ; point at the ROM reference in code
moveq #0,d0
move.w (a0)+,d0 ; get the offset to the next ROM reference
moveq #0,d1
move.w (a0)+,d1 ; get the ROM table entry number for this ROM reference
lsl.l #2,d1 ; convert to ROM table offset
move.l g.romTable(a5,d1.l),-4(a0) ; jam the value into the code
add.l d0,d0 ; convert offset in words to offset in bytes
bnz.s @useOffset ; if valid, use the offset we just got from the code
bra.s @nextTableOffset ; otherwise, use a long offset from the ROM reference table
@doneResource
move.l a2,PatchResourceEntry.ptr(a4) ; PatchResourceEntry.ptr now points to packed jump table
lea PatchResourceEntry.size(a4),a4 ; advance to the resource
bra.s @nextResource
@done
;————————————————————————————————————————————————————————————————————————————————————————————————————
InitializeJumpTable
; Turns out things will work best if routineSizes are initialized to -1.
; This ensures that any unused entries will look like the beginning of a module, so
; we wont loop past the end of a module that does not have another module after it.
; This includes one extra entry at the end of the jump table for that end case.
; Note that d5 contains the number of entries in the table, not counting the extra one.
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; Make d3 offset to parallel jump table entries
lea g.jumpTable(a5),a0 ; Make a0 the first jump table entry
moveq #kInitialRoutineSize,d0 ; a good routine size to initialize to
; loop through jump table and parallel jump table initializing values
move.w d5,d1 ; initialize loop counter with number of entries in jump table
@next
move.w d0,ParallelJumpTableEntryInfo.routineSize(a0,d3.l) ; jam in the routine size
clr.w ParallelJumpTableEntryInfo.flags(a0,d3.l) ; start off with no flags set
clr.w JumpTableEntry.opcode(a0) ; start off not nailed down (0 opcode)
addq.l #JumpTableEntry.size,a0 ; advance to the next entry
dbra d1,@next ; loop through the whole jump table
;————————————————————————————————————————————————————————————————————————————————————————————————————
UnpackJumpTable
; now, go through all the 'lpch' resources on the stack unpacking the jump table
;
; Some registers we use consistently:
;
; a0 = pointer to jump table entry in g.jumpTable for current entry point or module
; a1 = pointer to jump table entry in g.jumpTable for previous module
; a2 = pointer to current entry in the packed jump table
; a4 = pointer to current PatchResourceEntry on the stack
;
; d0 = pointer to current entry point or module in the 'lpch' code
; d1 = pointer to previous module in the 'lpch' code so we can calculate the size of modules
; (or -1 if there is no previous module)
; d2 = current size of special opcode from packed jump table
; d3 = g.ParallelJumpTableEntryInfoTableOffset, the offset into g.jumpTable of the parallel jump table
; d4 = boolean indicating whether we have a jump table entry for a pending module to fix up
move.l sp,a4 ; get first PatchResourceEntry
@nextResource
tst.w PatchResourceEntry.endOfList(a4) ; are we at the end ot the list?
bz @done ; zero means yes
move.l PatchResourceEntry.ptr(a4),a2 ; get pointer to packed jump table
; unpack the real jump table, trashing the ROM table
lea g.jumpTable(a5),a0 ; build the jump table here
move.l PatchResourceEntry.codePtr(a4),d0 ; keep the running address in d0
moveq #-1,d1 ; there is no previous module
; the main loop itself
;
; A reminder about the format of the packed jump table:
;
; 0-251 distance from current position in the code to next entry or
; module specified in the packed jump table, divided by 2 (i.e. 0-502 bytes)
; 252 means skip entries in the jump table
; 0 means end of packed jump table
; 1-254 means number of jump table entries to skip
; 255 means word follows with number of jump table entries to skip
; 253 means previous was reference list head for this module
; 254 means previous was an entry, not a new module
; 255 means word distance from current position in the code to next
; entry or module specified in the packed jump table follows
@pendingModule
moveq #1,d4 ; when we start, we have a pending module
@loop
moveq #0,d2
move.b (a2)+,d2 ; get the size of the routine
cmp.b #254,d2 ; check if it is a special opcode
bhi.s @word ; word length size follows, so get it
beq.s @doEntry ; we just got an entry (as opposed to a new module)
cmp.b #252,d2 ; check for other special opcodes
bhi.s @doReferenceList ; we just got a reference list head for the current module
beq.s @skipJumpTableEntries ; we should skip ahead some entries before the next module
; otherwise, not a special opcode
add.w d2,d2 ; quick sizes are stored as half, double it
@gotSize
tst.b d4 ; is there a pending module?
bz.s @noPending
; yes, so the size must be for the current module
bsr.s @finishModule ; put size in previous module jump table entry
bsr.s @emitEntry ; emit the entry
@noPending
add.l d2,d0 ; no pending module, advance code pointer by the size
bra.s @pendingModule
; emitEntry initializes the referenceListHead and routineSize in the parallel jump table entry,
; stores a pointer to the routine in the code in the address field of the jump table entry,
; and advances the pointer into g.jumpTable to the next entry
;
; a0 = pointer to jump table entry in g.jumpTable for current entry point or module
; d0 = pointer in the code to current entry point or module
@emitEntry
move.w #kInitialReferenceListHead,ParallelJumpTableEntryInfo.referenceListHead(a0,d3.l) ; real reference list head comes later I promise
move.l d0,JumpTableEntry.address(a0) ; put code pointer here (actual address calculated later)
clr.w ParallelJumpTableEntryInfo.routineSize(a0,d3.l) ; routine size goes here later for a module
addq.l #JumpTableEntry.size,a0 ; move to next entry!
rts
; finishModule stores the size of any previous module in the routineSize field of its parallel jump table entry,
; then updates the pointer to the last modules jump table entry and the pointer to the previous module
;
; a1 = pointer to jump table entry in g.jumpTable for last module
; d1 = pointer to previous module in the code
@finishModule
tst.l d1 ; first one?
bmi.s @noOld ; no, this is the first module
sub.l d0,d1 ; calculate length of previous module
neg.w d1 ; by subtracting from where we are now
move.w d1,ParallelJumpTableEntryInfo.routineSize(a1,d3.l) ; so we can store the size in the parallel info for
; this jump table entry
@noOld
move.l a0,a1 ; always keep pointer to jump table entry of the last module here
move.l d0,d1 ; keep pointer to the last module here
rts
; we got a special opcode saying that current entry in packed jump table is word length
@word
move.b (a2)+,d2 ; get high byte
lsl.w #8,d2 ; push it up
move.b (a2)+,d2 ; get low byte
bra.s @gotSize
@doEntry
bsr.s @emitEntry
; fall through
@noPendingModule
moveq #0,d4 ; no pending module
bra.s @loop
; we got a special opcode saying that current entry is the first reference inside this module
@doReferenceList
move.l d0,d2 ; calculate offset of reference list head by subtracting pointer to
sub.l d1,d2 ; first reference from the beginning of the module
move.w d2,ParallelJumpTableEntryInfo.referenceListHead(a1,d3.l); now the reference list head is fixed as promised earlier
bra.s @noPendingModule ; go back and loop
; we got a special opcode saying to skip some entries in the jump table
@skipJumpTableEntries
move.b (a2)+,d2 ; get number of entries to skip
bz.s @doneResource ; 0, that means end of table
cmp.b #255,d2 ; 255 means number is two bytes
bne.s @gotSkip ; otherwise, got number of entries to skip
move.b (a2)+,d2 ; number of entries to skip is a word
lsl.w #8,d2 ; push it up
move.b (a2)+,d2 ; get low byte
@gotSkip
lsl.l #2,d2 ; each jump table entry is 6 bytes, so multiply size * 4
add.l d2,a0 ; advance to a new place in the jump table
lsr.l #1,d2 ; and add size * 2
add.l d2,a0 ; advance to a new place in the jump table
bra.s @loop ; continue in the loop
@doneResource
bsr.s @finishModule ; finish the last module
move.l a2,PatchResourceEntry.ptr(a4) ; PatchResourceEntry.ptr now points to packed patch table
lea PatchResourceEntry.size(a4),a4 ; advance to the next resource
bra @nextResource
@done
;————————————————————————————————————————————————————————————————————————————————————————————————————
FindAllResidentCode
; find resident entries in install routines, make patches resident
; note that the packed patch table is only in the universal 'lpch' resource
move.l PatchResourceEntry.ptr(sp),a2 ; point at the packed patch table
lea FindOrMakeResident,a1
bsr ForPatchTable ; do that for each triggered entry in the table
;————————————————————————————————————————————————————————————————————————————————————————————————————
CutBackResidentCode
; cut back the block containing the resident code
if INITVERSION then
; in INIT version of loader, code block is a locked handle ; <40>
move.l d6,a0 ; <40> get the start of the resident code block
_RecoverHandle ; <40>
move.l g.residentTop(a5),d0 ; <40> get the end of the resident code block
sub.l d6,d0 ; <40> calculate the size of the resident code block
_SetHandleSize ; <40>
else
move.l g.residentTop(a5),d0 ; get the end of the resident code block
move.l d6,a0 ; get the start of the resident code block
sub.l a0,d0 ; calculate the size of the resident code block
_SetPtrSize
endif
;————————————————————————————————————————————————————————————————————————————————————————————————————
GetIslandsOutOfJumpTable
; get references to islands out of the jump table
; MakeResident creates the islands, and points the jump table at them.
lea g.jumpTable(a5),a0 ; point at jump table
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; offset to parallel jump table
move.w d5,d1 ; initialize loop counter with number of entries in jump table
@loop
bclr #islandFlagBit,ParallelJumpTableEntryInfo.flags(a0,d3.l); was this an island?
bz.s @next ; no, go on to the next
move.l JumpTableEntry.address(a0),a1 ; yes, we cleared the island bit, point at island
move.l JumpTableEntry.address(a1),JumpTableEntry.address(a0) ; jam in the real address found in the island
@next
addq.l #JumpTableEntry.size,a0 ; advance to the next jump table entry
dbra d1,@loop ; loop through the whole jump table
;————————————————————————————————————————————————————————————————————————————————————————————————————
TellDebuggerToLookAtJumpTable
; call the debugger if he is here, and notify him that we just nailed everything down
; debugger interface is:
; d5.w -> number of jump table entries
; d6.l -> pointer to patch block
; 4(a5) -> first jump table entry (jumpTable global)
; -4(a5) -> end of patch block (residentTop global)
clr.w NewLoaderActive ; tells whip new loader is active
move.l LinkedPatchDebuggerPtr,d0 ; is the debugger here?
bmi.s @done
move.l d0,a0
jsr (a0) ; call the debugger
@done
;————————————————————————————————————————————————————————————————————————————————————————————————————
InstallAndExecute
; run through installing all the patches and executing all of the install routines
move.l PatchResourceEntry.ptr(sp),a2 ; point at the packed patch table
lea InstallOrExecute,a1
bsr ForPatchTable ; install each triggered patch in the table
;————————————————————————————————————————————————————————————————————————————————————————————————————
Done
; dispose all the handles left on the stack
@next
tst.w PatchResourceEntry.endOfList(sp) ; is this the end?
bz.s @done
; The System version of the linked patch loader disposes of the 'lpch' handles
; after copying the data above the boot globals so that there would not be
; two copies of the linked patches floating around in the system heap.
if INITVERSION then ; <55> we only use handles for the Init Version
move.l PatchResourceEntry.resourceHandle(sp),a0 ; <55> get rid of the resource
_DisposeHandle
endif
lea PatchResourceEntry.size(sp),sp ; pop the rest of the entry
bra.s @next
@done
addq.l #2,sp ; pop off the word that flags end of table
lea -g.offset(a5),a0 ; get pointer to the jump table block
_DisposePtr ; dispose the jump table block
if NOT INITVERSION then
move.l (sp)+,d0 ; <55> Restore the original value of <55> FM ;restore MemTop
move.l d0,MemTop ; <55> MemTop that we saved earlier <55> FM
endif
movem.l (sp)+,LoadLinkedPatchesRegisters ; restore all of the registers
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
ROMVersions proc
macro
EmitROMVersions
lcla &index
; for each ROM, emit the version
&index: seta NumROMs$
while &index > 0 do
&index: seta &index - 1
dc.w ROMVersion$&index
endwhile
endm
EmitROMVersions
dc.w 0
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
ForPatchTable proc
; Traverse the patch table, calling the passed function for each patch that is triggered.
;
; In:
; g.romNumber(a5) number of the ROM we are running on
; g.nonROMConditions(a5) mask of other conditions
;
; a2 start of the patch table
; a1 function to be called for each patch routine
; In:
; a0 pointer to jump table entry
; d0.w trap number
; ccr result of tst.w d0
; a5 globals
; Out:
; can trash d0-d7/a0-a6
; Out:
; a2 points right *after* the condition table
;
; trashes d0-d2/a0-a1
;
; A reminder about the format of the packed patch table:
;
; Patches are grouped in sets of patches which have the same loading conditions. Each
; set is preceded by the load condition (1 to 4 bytes), followed by offsets for each
; entry, with the following encoding:
;
; 0-253 number of entries until the next patch
; 254 end of this set of patches, new load condition and another set follows
; 255 two bytes follow with the number of entries until the next patch (0 for end of table)
;
; After each offset is the trap number, which is two bytes. A trap number of 0 indicates an install proc.
; The table ends with a 255 followed by two bytes of 0.
ForPatchTableRegisters reg d3-d7/a3-a4/a6
ForPatchTableFunctionRegisters reg d2/a0-a2/a5
movem.l ForPatchTableRegisters,-(sp)
lea g.jumpTable(a5),a0 ; start out pointer at first jump table entry
NextCondition
; for each condition, check if it is active and skip it if not
if NumConditions$ <= 8 then
move.b (a2)+,d0 ; get the conditions (1 byte)
elseif NumConditions$ <= 16 then
move.b (a2)+,d0 ; get the conditions (2 bytes)
lsl.w #8,d0 ; push it up
move.b (a2)+,d0 ; get the next byte
elseif NumConditions$ <= 24 then
move.b (a2)+,d0 ; get the conditions (3 bytes)
swap d0 ; push it up
move.b (a2)+,d0 ; get the next byte
lsl.w #8,d0 ; push it up
move.b (a2)+,d0 ; get the next byte
else
move.b (a2)+,d0 ; get the conditions (4 bytes)
lsl.w #8,d0 ; push it up
move.b (a2)+,d0 ; get the next byte
swap d0 ; push it up
move.b (a2)+,d0 ; get the next byte
lsl.w #8,d0 ; push it up
move.b (a2)+,d0 ; get the next byte
endif
; the ROM must be one of the ROMs specified in the mask
move.w g.romNumber(a5),d1 ; get the ROM number
btst.l d1,d0 ; is the condition true for this ROM?
bz.s ConditionFalse ; no, condition not met
; all conditions specified must be true
if NumConditions$ <= 8 then
and.b #NonROMConditionsMask$,d0 ; conditions that must be true (1 byte)
move.b g.nonROMConditions(a5),d1 ; conditions that are true
and.b d0,d1 ; dont care about conditions not in the check word
cmp.b d0,d1 ; if all are true, then we should load
elseif NumConditions$ <= 16 then
and.w #NonROMConditionsMask$,d0 ; conditions that must be true (2 bytes)
move.w g.nonROMConditions(a5),d1 ; conditions that are true
and.w d0,d1 ; dont care about conditions not in the check word
cmp.w d0,d1 ; if all are true, then we should load
else
and.l #NonROMConditionsMask$,d0 ; conditions that must be true (3 or 4 bytes)
move.l g.nonROMConditions(a5),d1 ; conditions that are true
and.l d0,d1 ; dont care about conditions not in the check word
cmp.l d0,d1 ; if all are true, then we should load
endif
beq.s ConditionTrue ; required conditions are all met, go load
ConditionFalse
moveq #0,d2 ; flag condition false
bra.s NextPatch
ConditionTrue
moveq #1,d2 ; flag condition true
NextPatch
moveq #0,d1 ; get number of jump table entries until next patch
move.b (a2)+,d1 ; from patch table
cmp.b #254,d1 ; check for special opcodes
bhi.s @word ; 255 means number of entries is word sized, get it
beq.s NextCondition ; 254 means end of this set of patches, get next condition
@gotDistance
add.w d1,d1 ; number of entries * 2
add.l d1,a0 ; advance in the jump table
add.w d1,d1 ; number of entries * 4
add.l d1,a0 ; advance in the jump table (entries are 6 bytes)
move.b (a2)+,d0 ; get high byte of trap number
lsl.w #8,d0 ; push it up
move.b (a2)+,d0 ; get low byte of trap number
tst.b d2 ; was condition true?
beq.s NextPatch ; no, continue with the next patch
tst.w d0 ; set up condition codes, testing for install proc
movem.l ForPatchTableFunctionRegisters,-(sp)
jsr (a1) ; call the function (can trash anything)
movem.l (sp)+,ForPatchTableFunctionRegisters
bra.s NextPatch ; continue with the next patch
; we got a special opcode saying that current entry in packed patch table is word length
@word
move.b (a2)+,d1 ; get high byte of word
lsl.w #8,d1 ; move into the high byte
move.b (a2)+,d1 ; get low byte of word
tst.w d1 ; test whole word
bz.s Done ; 0 means end of table
bra.s @gotDistance
Done
movem.l (sp)+,ForPatchTableRegisters
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
proc
entry FindOrMakeResident
entry MakeResident
entry MakeNonResident
entry FindResident
import CheckPatchTableRange
;————————————————————————————————————————————————————————————————————————————————————————————————————
MakeResidentConservative
; Fixes the routine down in resident space, only if all modules referenced
; in the routine can be nailed down first. Note that this will always happen,
; except when there are cycles in the reference graph.
moveq #0,d7 ; flag resident
moveq #0,d6 ; flag Make, not Find
moveq #1,d5 ; flag conservative
bra.s MakeFindResidentAnyPoliticsCommon
;————————————————————————————————————————————————————————————————————————————————————————————————————
FindOrMakeResident
; Do a FindResident if the CCs say zero, or a MakeResident otherwise.
;
; This comes in handy when iterating through jump table entries in the patch table, because the
; trap number is tested just before calling here, and a trap number of 0 means install code, which
; should be FoundResident, while any other trap number means a patch, which should be MadeResident.
bz.s FindResident
; fall into MakeResident
;————————————————————————————————————————————————————————————————————————————————————————————————————
MakeResident
; Fixes the routine down in resident space. No matter what (i.e. non-conservative)
; even if some of its references arent nailed down yet. This takes care of cycles
moveq #0,d7 ; flag resident
bra.s MakeResidentCommon
;————————————————————————————————————————————————————————————————————————————————————————————————————
MakeNonResident
; Fixes the routine down in non-resident space. No matter what (i.e. non-conservative)
; This is used by install routines for their non resident entries
moveq #1,d7 ; flag non-resident
; fall into MakeResidentCommon
;————————————————————————————————————————————————————————————————————————————————————————————————————
MakeResidentCommon
moveq #0,d6 ; flag Make, not Find
bra.s MakeFindResidentCommon
;————————————————————————————————————————————————————————————————————————————————————————————————————
FindResident
; Makes all “resident” references in a routine resident.
moveq #0,d7 ; flag resident (when we get down a level)
moveq #1,d6 ; flag Find, not Make
; fall into MakeFindResidentCommon
;————————————————————————————————————————————————————————————————————————————————————————————————————
MakeFindResidentCommon
moveq #0,d5 ; not conservative
; fall into MakeFindResidentAnyPoliticsCommon
;————————————————————————————————————————————————————————————————————————————————————————————————————
MakeFindResidentAnyPoliticsCommon
; Now begins the one routine that does it all:
;
; Make/Find[Non]Resident[Conservative]
;
; or, to be more explicit:
;
; FindResident
; MakeResident
; MakeResidentConservative
; MakeNonResident
;
; In:
; g.jumpTable(a5) pointer to start of jump table
; g.residentTop(a5) pointer to next available resident code
;
; a0 pointer to jump table entry
; a5 globals
; d7 0 means resident, 1 means non-resident
; d6 0 means make, 1 means find
; d5 0 means non-conservative, 1 means conservative
;
; Out:
; g.residentTop(a5) pointer to next available resident code
;
; trashes d0-d7/a0-a3
;
; The algorithm for all these cases is similar. Here is a sketch:
;
; do nothing if this module is already nailed down (i.e. a jump instruction in its opcode field)
; do nothing if this module has its FindSeenFlagBit set AND we are doing a FindResident (to prevent cycles)
; do nothing if this module has its MakeSeenFlagBit set AND we are doing a Make[Non]Resident (to prevent cycles)
; walk back the jump table until you reach the beginning of the module
; if find
; set FindSeenFlagBit for this module and all the references in it to prevent cycles
; else (doing a make)
; if resident
; set MakeSeenFlagBit for this module and all the references in it to prevent cycles
; for each thing this module refers to, do a MakeResidentConservative
; unmark MakeSeenFlagBit for this module and all the references in it (it is no longer “half-nailed”)
; if conservative
; if not all modules made themselves resident, return
; make space in resident area for routine
; FoundFinalRestingPlace:
; fix up all jump table entries, now that we have found a final resting place
; if resident
; for each thing this module refers to that is too far away, make an island
;
; (then for both make and find)
; FollowReferences:
; for each thing this module refers to,
; if make
; do a Make[Non]Resident
; fix up the reference
; if find
; if the reference is marked resident, do a MakeResident
; <42> Before an attempt is made to nail down this module, see if the entry number for
; the module is in a range specified by the Gibblys patch table range list. If
; its not in any of the ranges specified by this list, dont nail it down.
;
CheckPatchNumber
move.l a0,d1 ; calculate jump table offset
sub.l a5,d1
subq.w #g.jumpTable,d1
divu.w #JumpTableEntry.size,d1 ; ÷6 jump table entries are 6 bytes
bsr CheckPatchTableRange ; See if patch is in range
tst.b d2 ; CheckPatchTableRange returns a condition in d2
bnz.s ShortCircuitActive ; Its in a range. Try to nail it down.
move.l JumpTableEntry.address(a0),d2 ; get address
bra Done
ShortCircuitActive
; do nothing if this module is already nailed down or if we hit a cycle (findSeen and makeSeen flag bits)
move.w JumpTableEntry.opcode(a0),d1 ; get the opcode
move.l JumpTableEntry.address(a0),d2 ; get address
cmp.w #kNailedDown,d1 ; already nailed down (JMP instruction)?
beq Done ; yes, we are done
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; get offset to entry table
tst.b d6 ; doing make or find?
bz @doingMake
btst #FindSeenFlagBit,ParallelJumpTableEntryInfo.flags(a0,d3.l) ; prevent cycles in find
bnz Done
bra.s WalkBackToProc
@doingMake
btst #MakeSeenFlagBit,ParallelJumpTableEntryInfo.flags(a0,d3.l) ; prevent cycles in make
bnz Done
WalkBackToProc
; walk back the jump table until you reach the beginning of the module
moveq #0,d0 ; make module size a long word
@next
move.w ParallelJumpTableEntryInfo.routineSize(a0,d3.l),d0 ; the beginning of the module (non-0 size)?
bnz.s @gotBeginning ; yes, found beginning
subq #ParallelJumpTableEntryInfo.size,a0 ; go back one jump table entry
bra.s @next
@gotBeginning
move.l JumpTableEntry.address(a0),a1 ; get pointer to start of this module
move.l a0,a2 ; save jump table entry for start of this module
moveq #0,d1
move.w ParallelJumpTableEntryInfo.referenceListHead(a0,d3.l),d1 ; get pointer to first reference inside module
; Check whether we are doing a find or a make and set the appropriate flag to prevent cycles
; Setting these flags could be considered “half-nailing” because we are trying to nail down
; the flagged entries but we cant finish until we have nailed down all the entrys references
MarkOurselvesHalfNailed
move.l a0,a3 ; go through our references
tst.b d6 ; find references?
bz @doingMake ; no, doing a make
; Set the FindSeenFlagBit for this module and all its entries so we wont get into an infinite loop later
@nextFindReference
bset #FindSeenFlagBit, ParallelJumpTableEntryInfo.flags(a3,d3.l) ; prevent cycles in find
addq.l #ParallelJumpTableEntryInfo.size,a3 ; point to the next jump table entry
tst.w ParallelJumpTableEntryInfo.routineSize(a3,d3.l) ; is this a new module (non-0 size)?
bz.s @nextFindReference ; no, still an entry within the current module
bra FollowReferences ; yes, check to see which of these references
; are marked resident
@doingMake
tst.b d7 ; resident or non-resident?
bnz.s FoundFinalRestingPlace ; non-resident, no need to move it out of the 'lpch'
; Set the MakeSeenFlagBit for this module and all its entries so we wont get into an infinite loop later
@next
bset #MakeSeenFlagBit, ParallelJumpTableEntryInfo.flags(a3,d3.l) ; prevent cycles in make
addq.l #ParallelJumpTableEntryInfo.size,a3 ; point to the next jump table entry
tst.w ParallelJumpTableEntryInfo.routineSize(a3,d3.l) ; is this a new module (non-0 size)?
bz.s @next ; no, still an entry within the current module
LureInTheConservatives
; for each thing this module refers to, do a MakeResidentConservative
; also keep track to see that all is nailed down. If a referenced entry
; is not nailed down we return an error by clearing d4
moveq #1,d4 ; assume for now that all will be nailed down
lea @doOne,a3 ; lets go makin
bsr ForAllReferences ; get those evil right wingers in memory
bra.s @continue
; LureInTheConservatives iterator
; this routine is iterated over each entry referenced by the current module
; it attempts to nail down as many of the references as possible by calling MakeResidentConservative
@doOne
movem.l d4/a0,-(sp) ; remember him
bsr.s MakeResidentConservative ; go lock him in
movem.l (sp)+,d4/a0
cmp.w #kNailedDown,JumpTableEntry.opcode(a0) ; did he get nailed?
beq.s @ok
clr.b d4 ; nope, we have a cycle in the graph, gentlemen
@ok
rts
@continue
MarkOurselvesNotHalfNailed
; we can clear our MakeSeenFlagBit now that we are done luring in the conservatives
; we MIGHT end up recursing later in “MakeIslands” but by that time we will be nailed
; down and the protection of the MakeSeenFlagBit will be unecessary
move.l a0,a3 ; go through our references
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; offset to parallel info list
@next
bclr #MakeSeenFlagBit, ParallelJumpTableEntryInfo.flags(a3,d3.l) ; clear the flag bit
addq.l #ParallelJumpTableEntryInfo.size,a3 ; point to the next entry
tst.w ParallelJumpTableEntryInfo.routineSize(a3,d3.l) ; is this a new module (non-0 size)?
bz.s @next ; no, still an entry within the current module
ExitIfConservativeFailure
; if were conservative, and we couldn't make all our references resident, then fail
tst.b d5 ; are we conservative?
bz.s @survive ; no, we survive no matter what
; if not all modules made themselves resident, return
tst.b d4 ; anyone blow it?
bnz @survive ; no, continue
bra Done
@survive
MoveResident
; make space in resident area for routine, and move it there
move.l a1,a0 ; point to the module
move.l g.residentTop(a5),a1 ; get place to put it in resident area
add.l d0,g.residentTop(a5) ; advance past it
_BlockMove
FoundFinalRestingPlace
; fix up all jump table entries, now that we have found a final resting place
; this is either in the resident block or in the code of the 'lpch' if non-resident
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; offset to parallel jump table
move.l a1,d0 ; calculate fixup
sub.l JumpTableEntry.address(a2),d0 ; by subtracting offset of start of module
add.l d0,d2 ; save away the resident address for the entry
; were looking at (we return this later)
@next
add.l d0,JumpTableEntry.address(a2) ; convert address in non-resident space to a real address
move.w #kNailedDown,JumpTableEntry.opcode(a2) ; mark as nailed down (this is a JMP opcode)
addq.l #JumpTableEntry.size,a2 ; point to the next entry
tst.w ParallelJumpTableEntryInfo.routineSize(a2,d3.l) ; is this a new module (non-0 size)?
bz.s @next ; no, keep looping
MakeIslands
; if module is resident, for each thing the module refers to that is too far away, make an island
tst.b d7 ; resident or non-resident?
bnz.s FollowReferences ; non-resident, no need for islands
lea @doOne,a3 ; lets go to the islands
bsr ForAllReferences ; make em
bra.s @continue
; MakeIslands iterator
; This routine is iterated over each entry referenced by the current module.
; It attempts to make an island in the resident block for references more than 32k away.
; Note that an island is the same as a nailed down jump table entry, i.e. JMP address.
@doOne
cmp.b #kAbsoluteOpcode*2,d0 ; never make islands for absolute references
beq.s @done
cmp.w #kNailedDown,JumpTableEntry.opcode(a0) ; a backward reference (already nailed)?
beq.s @backRef ; yes, can only make an island for backward refs.
bra @done
@backRef
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; offset to parallel info list
move.l JumpTableEntry.address(a0),a4 ; get address of routine
move.l a4,a2
sub.l a1,a2 ; get offset from here to routine
cmpa.w a2,a2 ; is it in range? (warning! 68000 hack!)
beq.s @done ; yes, no need for an island
; mark as island, and if we already had an island, get real address from the island itself
bset #islandFlagBit,ParallelJumpTableEntryInfo.flags(a0,d3.l)
bz.s @gotAddress ; island flag wasnt set, we have the real address
move.l 2(a4),a4 ; extract the address from the island itself
; well be making an island to an island
@gotAddress
; lay down the island, and point jump table at it
move.l g.residentTop(a5),a2 ; point to new place for island
move.l a2,JumpTableEntry.address(a0) ; store island address in the jump table
move.w #kNailedDown,JumpTableEntry.opcode(a2) ; lay down a jump
move.l a4,JumpTableEntry.address(a2) ; to the real routine address
addq.l #JumpTableEntry.size,a2
move.l a2,g.residentTop(a5) ; island is allocated now
@done
rts
@continue
; executed for both finds and makes
FollowReferences
; Recurse calling FindResident or MakeResident for each entry referenced by the current module.
lea @doOne,a3 ; recurse over the references
bsr ForAllReferences
tst.b d6 ; doing find?
bz @continue ; no, its a make
; if we were doing a find we need to clear our FindSeenFlagBit now
move.l a0,a3 ; yes, go through our references
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; offset to parallel info list
@next
bclr #FindSeenFlagBit, ParallelJumpTableEntryInfo.flags(a3,d3.l) ; clear the flag bit
addq.l #ParallelJumpTableEntryInfo.size,a3 ; point to the next entry
tst.w ParallelJumpTableEntryInfo.routineSize(a3,d3.l) ; is this a new module (non-0 size)?
bz.s @next ; no, still an entry within the current module
bra.w @continue
; FollowReferences iterator
; This routine is iterated over each entry referenced by the current module.
; Recurses doing a FindResident or MakeResident on each entry.
; Finally, we fix up the reference inside the code itself.
@doOne
; recurse with either FindResident or MakeResident
movea.l a0,a3 ; save address of jump table entry (keep carry flag)
@registers reg d0/d2/d3/d6/a1/a3
; if we are doing a FindResident, and this is a force-resident reference
; we have found something that we want to MakeResident, so recurse with a MakeResident
; on it instead of recursing with a FindResident
movem.l @registers,-(sp) ; keep carry flag
bcc.s @noForce ; reference not marked force-resident, skip over this
moveq #0,d6 ; force use to make resident, instead of just finding
@noForce
bsr MakeFindResidentCommon ; make this resident, or find more resident references
movem.l (sp)+,@registers
; in find case, do nothing else
tst.b d6 ; is this a find (not make)?
bnz.s @done ; yes, our job is done
; now, let us fix up the reference (at long last)
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; offset to parallel info list
cmp.b #kAbsoluteOpcode*2,d0 ; is it a 32-bit reference?
beq.s @32Bit
tst.b d7 ; non-resident?
bnz.s @nonResident ; yes, go handle it that way
; take special case of LEA or PEA to island into account (need address, not JMP opcode)
btst #islandFlagBit,ParallelJumpTableEntryInfo.flags(a3,d3.l)
bz.s @noOffset ; some island opcodes need offsets
add.w IslandOffsets(d0.w),a0 ; point to address instead of to JMP in island
@noOffset
; now check the distance
move.l a0,a2 ; get address of routine
sub.l a1,a2 ; get offset from here to routine
cmpa.w a2,a2 ; is it in range? (warning! 68000 hack!)
beq.s @ok ; yes, everything is OK
; Sometimes we can use the real thing instead of the island and it wont be out of range.
btst #islandFlagBit,ParallelJumpTableEntryInfo.flags(a3,d3.l)
bz.s @notOK ; if not island then we cant try indirection
move.l 2(a0),a0 ; get what the island points to
move.l a0,a2 ; get address of routine
sub.l a1,a2 ; get offset from here to routine
cmpa.w a2,a2 ; is it in range? (warning! 68000 hack!)
bne.s @notOk ; no, everything is notOK
move.w a2,(a1) ; store the offset
bra.s @notIsland ; continue with a non-island address
@notOk
moveq #dsLinkedPatchReferenceTooFar,d0 ; yes, big problem
_SysError
@ok
; now store the offset, and then the opcode
move.w a2,(a1) ; store the offset
btst #islandFlagBit,ParallelJumpTableEntryInfo.flags(a3,d3.l)
bnz.s @island ; yes, go handle it
@notIsland
move.w PCRelativeOpcodes(d0.w),-(a1) ; store the opcode
@done
rts
@island
move.w IslandOpcodes(d0.w),-(a1) ; store the opcode (use MOVE instead of LEA)
rts
@nonResident
add.w A5RelativeOffsets(d0.w),d2 ; turn jump table offset into A5 offset
move.w d2,(a1) ; store the offset
move.w A5RelativeOpcodes(d0.w),-(a1) ; store the opcode
rts
@32Bit
btst #islandFlagBit,ParallelJumpTableEntryInfo.flags(a3,d3.l)
bz.s @noIsland ; dont have to follow the island
move.l 2(a0),a0 ; get address of real thing from the island
@noIsland
move.l a0,-2(a1) ; store the address as a long absolute
rts
@continue
Done
; we are done, return the resident address
move.l d2,a0 ; result
rts
IslandOffsets
dcb.w 9,2 ; address for MOVE.L
dcb.w 2,0 ; opcode for JSR, JMP
PCRelativeOpcodes
dc.w $41FA,$43FA,$45FA,$47FA,$49FA,$4BFA,$4DFA,$4FFA,$487A ; LEA A0-A7, PEA
dc.w $4EBA,$4EFA ; JSR, JMP
IslandOpcodes
dc.w $207A,$227A,$247A,$267A,$287A,$2A7A,$2C7A,$2E7A,$2F3A ; MOVEA.L A0-A7, -(SP)
dc.w $4EBA,$4EFA ; JSR, JMP
A5RelativeOpcodes
dc.w $206D,$226D,$246D,$266D,$286D,$2A6D,$2C6D,$2E6D,$2F2D ; MOVEA.L A0-A7, -(SP)
dc.w $4EAD,$4EED ; JSR, JMP
A5RelativeOffsets
dcb.w 9,g.jumpTable+JumpTableEntry.address ; address for MOVE.L
dcb.w 2,g.jumpTable+JumpTableEntry.opcode ; opcode for JSR, JMP
;————————————————————————————————————————————————————————————————————————————————————————————————————
ForAllReferences
; Traverse the reference list, calling the passed function for each reference.
;
; In:
; a5 pointer to the globals (including the jump table)
; d1.l reference list head (offset into routine); 1 means do nothing
; a1 pointer to beginning of module
; d4/d6 values to be passed through
; a3 function to be called repeatedly
; In:
; a0 jump table entry of this reference
; d0.w opcode, expressed as an offset into a table of words
; a1 pointer to code (after opcode)
; d2.w jump table offset
; ccr carry set if this is a “force-resident” reference
; d4 passed through (in and out)
; d5-d7 passed through
; trashes d0-d7/a0-a4
;
; trashes d4
;
; A reminder about the format of references within modules:
;
; Before they are fixed up, references are 4 bytes as follows.
;
; +---------------------------------
; 2 bytes | F | offset to next reference |
; +---------------------------------
; +---------------------------------
; 2 bytes | OPCD | jump table entry number |
; +---------------------------------
;
; The first two bytes consist of a flag (F) in the first bit (1 for force-resident, 0 otherwise),
; followed by the offset to the next reference in the module, in words.
;
; The second two bytes consist of 4 bits (OPCD) corresponding to the opcode number, followed by
; 12 bits corresponding to the jump table entry number for this reference (limiting the number
; of jump table entries to 4096 max).
;
ForAllReferencesRegisters reg d0-d3/a0-a2/a4
ForAllReferencesFunctionRegisters reg d1/d5-d7/a1/a3
movem.l ForAllReferencesRegisters,-(sp)
bclr #0,d1 ; head of list can be 0 (1 means no references)
bz.s @skipTest ; if low bit is clear, skip the check
@next
tst.w d1 ; 0 means the end of the reference list
bz.s @done
@skipTest
add.l d1,a1 ; advance to item in reference list
move.w (a1),d1 ; get offset to next reference in module
move.w 2(a1),d0 ; get opcode number and jump table entry number
lea g.jumpTable(a5),a0 ; point to jump table
move.w d0,d2 ; calculate jump table entry number
lsl.w #4,d2 ; clear high 4 bits (opcode number)
lsr.w #3,d2 ; x2 (jump table entries are 6 bytes)
add.w d2,a0 ; advance in the jump table
move.w d2,d3 ; d3 = entry number * 2
add.w d2,d2 ; x4 (jump table entries are 6 bytes)
add.w d2,a0 ; advance in the jump table
add.w d3,d2 ; d3 = entry number * 6 for function call
lsr.w #8,d0 ; calculate offset into opcode table
lsr.w #4,d0 ; shift by 12 (roll high 4 bits down to the bottom)
add.w d0,d0 ; make into opcode table offset
add.w d1,d1 ; calculate offset to next reference (which was in words)
; and test the high bit (force-resident flag)
movem.l ForAllReferencesFunctionRegisters,-(sp)
addq #2,a1 ; point after opcode, before operand
jsr (a3) ; call the iterator
movem.l (sp)+,ForAllReferencesFunctionRegisters
bra.s @next
@done
movem.l (sp)+,ForAllReferencesRegisters
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
proc
export InstallOrExecute
import MakeNonResident
import GetPatchAddress
import SetPatchAddress
import FlushInstructionCache
import CheckPatchTableRange
InstallOrExecute
; Installs a patch or executes an install routine.
;
; In:
; a0 pointer to jump table entry
; d0.w trap number
; ccr z for install routine, nz for patch
;
; trashes d0-d7/a0-a6
; debugging code to break before installing specified jump table entries
;
; <62> This code used to be turned off for linked patch INITs, and we couldnt figure out
; why so we turned it on. If someone figures out why this was turned off for INITs,
; change it back, then come yell at us for not knowing any better.
move.l a0,d1 ; calculate jump table offset
sub.l a5,d1
subq.w #g.jumpTable,d1
divu.w #JumpTableEntry.size,d1 ; ÷6 jump table entries are 6 bytes
move.w d1,StashJumpTableEntryNum ; stash the jump table entry number
; <39> The conditions for this patch have been met. Check the patch number against the ranges
; in the patch table to determine if it should be installed or not.
bsr CheckPatchTableRange ; <39> See if patch is in range
tst.b d2 ; <39> CheckPatchTableRange returns a condition in d2
bz.s Done ; <39> Zero means dont load the patch.
; Chris, Greg and I always have trouble how to use these globals, so Im adding comments
; about it so we dont have to do this by trial and error any more.
;
; To stop on a particular linked patch entry:
; StopOnJumpTableEntryNum ($922) = linked patch entry to stop on
; StopOnJumpTableEntryNumHighest ($924) = -1
;
; To stop on a range of linked patch entries
; StopOnJumpTableEntryNum ($922) = lower end of range of entries to break on
; StopOnJumpTableEntryNumHighest ($924) = upper end of range of entries to break on
tst.w StopOnJumpTableEntryNumHighest ; should we test a range?
bmi.s @notRange
cmp.w StopOnJumpTableEntryNum,d1
bhs.s @stop ; stop if we are <= target
cmp.w StopOnJumpTableEntryNumHighest,d1
bls.s @stop ; stop if we are >= target
bra.s @dontStop
@notRange
cmp.w StopOnJumpTableEntryNum,d1
bne.s @dontStop ; stop if we hit the target entry <31>
@stop
_Debugger
@dontStop
tst.w d0
; end of debugging code (have to do the tst.w since we screwed up the condition codes)
bnz.s InstallPatch ; its a patch, go install it
ExecuteInstallRoutine
; execute an install routine
bsr MakeNonResident ; nail down install routine and references
bsr FlushInstructionCache ; this better not trash a0
if INITVERSION then
move.l TheZone,-(sp) ; save the current zone
move.l SysZone,TheZone ; set to the System heap
jsr (a0) ; call it
move.l (sp)+,TheZone ; restore the zone
rts
else
jmp (a0) ; call it
endif
InstallPatch
; install a patch
move.l g.ParallelJumpTableEntryInfoTableOffset(a5),d3 ; parallel entry table offset in d3
move.l a0,a1 ; keep jump table pointer here
move.w d0,d6 ; save trap number
FixOld
; find magic cookies and replace them with the old patch address
bsr.s GetPatchAddress ; get the old address of this patch
move.l a0,d4 ; keep it here to fix up
move.l JumpTableEntry.address(a1),a0 ; get the address of the patch routine
move.l a0,a2 ; and get ready to traverse it
move.w ParallelJumpTableEntryInfo.routineSize(a1,d3.l),d1 ; get the size of the patch routine
lsr.w #1,d1 ; turn the size into a DBRA count (words)
subq.w #2,d1 ; 1 for DBRA, 1 for last word (cookie is long)
blo.s @done ; routine very small
move.w #(OldTrapAddressMagicCookie >> 16),d2 ; look for high word of magic cookie
move.w #(OldTrapAddressMagicCookie and $FFFF),d3 ; look for low word of magic cookie
@next
cmp.w (a2)+,d2 ; did we find it?
@compared
dbeq d1,@next ; go until we do
bne.s @done
cmp.w (a2),d3 ; look for the low word
bne.s @compared ; nope, keep going
move.l d4,-2(a2) ; do a fixup right here
bra.s @compared ; keep going
@done
SetNew
; point at the new patch address
move.w d6,d0
bsr.w SetPatchAddress ; point at the new address of this patch
Done
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
GetPatchAddress proc
; Get the address of a trap or vector. (Similar interface to GetTrapAddress.)
;
; In:
; d0.w trap number (or vector number)
; Out:
; a0 address of trap or vector
;
; trashes d0/a0
cmp.w #$A000,d0 ; is it a trap?
blo.s @notTrap
_GetTrapWordAddress
rts
@notTrap
move.w d0,a0 ; get vector address in address register
cmp.w #$4000,a0 ; are we getting a vector, or ExpandMem offset?
blo.s @vector ; getting vector
@expandMem
sub.w #$4000,a0 ; change into an offset
add.l ExpandMem,a0 ; point to place within the ExpaneMem block
@vector
move.l (a0),a0 ; get old value from vector
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
SetPatchAddress proc
import FlushInstructionCache
; Set the address of a trap or vector. (Similar interface to SetTrapAddress.)
;
; In:
; d0.w trap number (or vector number)
; a0 address to set to
;
; trashes d0/a0
cmp.w #$A000,d0 ; is it a trap?
blo.s @notTrap
_SetTrapWordAddress
rts
@notTrap
ext.l d0 ; make vector address long
exg d0,a0 ; put vector address in address register
cmp.w #$4000,a0 ; are we setting a vector, or ExpandMem offset?
blo.s @vector ; setting vector
@expandMem
sub.w #$4000,a0 ; change into an offset
add.l ExpandMem,a0 ; point to place within the ExpaneMem block
@vector
move.l d0,(a0) ; put new address into vector
exg d0,a0 ; put everything back
bsr.s FlushInstructionCache ; this better not trash a0
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
FlushInstructionCache proc
; Flush the instruction cache, if there is one.
; This must be called after code that is modified in RAM.
;
; trashes no registers
movem.l d0-d2/a0-a1,-(sp)
if INITVERSION then
; *** we really only need to check if CacheFlush is implemented for pre-7.0 systems
; *** someday, we wont have to support these systems even with linked patch INITs
move.w #$9f,d0
_GetTrapAddress ; Get _Unimplemented
cmp.l jCacheFlush,a0 ; is CacheFlush implemented?
beq.s @exitFlushInstructionCache ; No, return.
endif
move.l jCacheFlush,a0 ; call through this vector
jsr (a0)
@exitFlushInstructionCache ; <20> mrr
movem.l (sp)+,d0-d2/a0-a1
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
CheckPatchTableRange Proc Export
;
; <39> Check the patch count index against the ranges stored in the patch range table. Dont load the
; patch if it isnt in one of the ranges.
;
; In: D1: Jump table entry number
;
; Out: D2: Zero if entry is in a range, non zero if not.
;
; Format of the patch range table (pointed to by g.patchRangeTable) is:
;
; 2 bytes version
; 2 bytes number of ranges that follow - 1
;
; for each range:
;
; 2 bytes entry number of first patch count index to load
; 2 bytes entry number of last patch count index to load
;
; note: ranges must be sorted in ascending order
;
movem.l a0/d0-d1,-(sp) ; Save off registers that are used
move.l g.patchRangeTable(a5),a0 ; Get pointer to patch table ranges
addq #2,a0 ; Skip past version
move.w (a0)+,d0 ; Get range count
bmi.s @dontLoadPatch ; <44> If negative, there are no ranges in this list.
moveq #1,d2 ; Assume patch should be loaded
@checkRangeLoop
cmp.w (a0)+,d1 ; Check against lower bound
blt.s @dontLoadPatch ; If lower, dont load
cmp.w (a0)+,d1 ; Check against upper bound
bls.s @loadPatch ; Patch should be loaded
dbra d0,@checkRangeLoop ; Loop through all the ranges
@dontLoadPatch
moveq #0,d2 ; If patch was not in any range, dont load it
@loadPatch
movem.l (sp)+,a0/d0-d1 ; Restore everything that was used except for D2
rts
EndProc
;————————————————————————————————————————————————————————————————————————————————————————————————————
TrapImplemented proc ; <31>
; Pass in a trap address in a0 and figure out whether it is implemented.
; If eq, the trap is not implemented.
; Trashes no registers.
movem.l d0/a0-a1,-(sp)
move.l a0,a1
move.w #kUnimplementedTrap,d0 ; get address of unimplemented trap
_GetTrapAddress
cmp.l a0,a1 ; see if trap is implemented
movem.l (sp)+,d0/a0-a1
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
MrGestalt proc ; <31>
; Call Gestalt, and get the result into d0.
; Return 0 for result if Gestalt or selector is unknown.
; This wont work for all Gestalt selectors, but it will for most.
; Trashes d0 only.
move.l a0,-(sp)
move.l d0,-(sp)
move.w #kGestaltTrap,d0
_GetTrapAddress newOS ; get address of Gestalt
jsr TrapImplemented
beq.s @dropStackReturn0 ; no Gestalt, return 0
move.l (sp)+,d0 ; avoid trashing the condition codes
_Gestalt
bne.s @return0 ; on error, return 0
move.l a0,d0 ; put result into d0
bra.s @done
@dropStackReturn0
addq #4,sp ; drop value of selector
@return0
move.l #0,d0 ; always use 0 for result
@done
movea.l (sp)+,a0 ; avoid trashing the condition codes
rts
endproc
;————————————————————————————————————————————————————————————————————————————————————————————————————
;
; <49> For linked patch INITs trying to run on Systems before Cube-E, keep this patch range
; table around in case they dont have a 'ptbl' resource handy.
;
if INITVERSION then ; <49>
StaticRangeTable Proc Export ; <49>
dc.w $0001 ; <49> Version
dc.w $0000 ; <49> Range count - 1
dc.w $0000 ; <49> First linked patch entry in range to load
dc.w $FFFF ; <49> Last entry to load
EndProc ; <49>
endif ; <49>
end