mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-24 02:33:28 +00:00
2413 lines
104 KiB
Plaintext
2413 lines
104 KiB
Plaintext
;
|
||
; 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. (Don’t 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 can’t 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 we’re 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
|
||
; it’s 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 isn’t good enough, so just take
|
||
; it out. We’re going to load the Process Manager segments in
|
||
; BeforePatches.a ('PTCH' 0) so they’ll be even lower, and they’ll
|
||
; 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 Don’t try to release the patch range table for linked patch
|
||
; INITs, since we don’t know whether it came from a resource or
|
||
; from the static table. Well, we do know, but it’s 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 couldn’t 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 doesn’t have that
|
||
; particular 'lpch' resource. Always use _Get1Resource now to get
|
||
; 'lpch's so this won’t 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 won’t break the whip dcmd in
|
||
; the future.
|
||
; <45> 1/31/92 JSM Update comments on format of 'lpch' resources to show extra
|
||
; field that’s 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 don’t 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-boo’d in AllocateResidentBlock. D6 wasn’t 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 it’s 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 it’s 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. Couldn’t 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 we’re 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 doesn’t 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 it’s 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 it’s not 1, it’s 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, it’s 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 it’s 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. It’s 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 table’s 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 don’t 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 driver’s 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 don’t 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
|
||
; *** isn’t the offset into each entry in the packed ROM table constant for the ROM we’re 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 won’t 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 module’s 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 ; don’t 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 ; don’t 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 ; don’t 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 it’s references aren’t 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 Gibbly’s patch table range list. If
|
||
; it’s not in any of the ranges specified by this list, don’t 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 ; It’s 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 can’t finish until we have nailed down all the entry’s 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 won’t 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 won’t 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 ; let’s 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 we’re 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
|
||
; we’re 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 ; let’s 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 wasn’t set, we have the real address
|
||
move.l 2(a4),a4 ; extract the address from the island itself
|
||
; we’ll 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, it’s 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 won’t be out of range.
|
||
|
||
btst #islandFlagBit,ParallelJumpTableEntryInfo.flags(a3,d3.l)
|
||
bz.s @notOK ; if not island then we can’t 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 ; don’t 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 couldn’t 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 don’t load the patch.
|
||
|
||
; Chris, Greg and I always have trouble how to use these globals, so I’m adding comments
|
||
; about it so we don’t 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 ; it’s 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 won’t 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. Don’t load the
|
||
; patch if it isn’t 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, don’t 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, don’t 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 won’t 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 don’t 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
|