; ; 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 : The offset from change <51> should be $14, not ; #14. ; <58> 4/8/92 DTY #1026928,: 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,: 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,: 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 ; ; 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