; ; File: LinkedPatchMacros.a ; ; Contains: macros for use in linked patch source ; ; Note that the code generated by the macros in this file is closely tied to the ; patch loader and to the patch linker. Use care in changing one of these three ; without looking at the others. ; ; This file also contains ROM versions of many macros. This is so that the same ; code can be used in ROM and in a patch without lots of conditionals throughout. ; ; Symbols and macros that end with a '$' symbol are for internal use of the system ; that links patches. ; ; Written by: Darin Adler ; ; Copyright: © 1990-1992 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; <49> 1/31/92 JSM Add new SuperMario ROM version. This has the effect of creating ; two new 'lpch' resources in the System. 'lpch' 63 is the new ; universal patch resource, it contains the extra universal ; information formerly put in 'lpch' 31, which was the universal ; resource when there were only 5 ROMs. The actual patches all ; remain in 'lpch' 31, since none of them apply to SuperMario. ; 'lpch' 32 is the patch resource for only the SuperMario ROM. ; Neither of these 'lpch' resources currently contain any code, ; since SuperMario doesn’t have any patches. ; <48> 1/20/92 PN Nullify the cmpROM for ROM build ; <47> 1/11/92 JSM Add MakeDispatcherTable macro so we can go back to defining a ; common EndDispatcher for both the ROM and patch versions, get ; rid of the selectorNum global array which was never really used ; by the Dispatcher macros. ; <46> 1/8/92 RB (JSM really). Created ROM versions of the EndDispatch macro and ; dummy versions of jsrOld, jmpOld, etc. So that the ROM build ; can use files used in LinkedPatches. ; <45> 10/28/91 SAM/KSM Rolled in Regatta file. ; Added hasC96 and hasPwrMgr conditionals. ; Added hasTERROR and notTERROR conditionals to identify the ; TERROR $067C overpatch ROM. ; <44> 8/19/91 FM Remove obsolete LPFilename code. LPFileName is now passed in as ; a -d parameter from the build scripts. ; <43> 8/17/91 FM Added Comment records that will allow the LinkPatch tool to ; generate object file information about patch entries. Also fixed ; a couple of off by 2 errors that Darin pointed out. ; <42> 5/28/91 FM Simplified dcOld ; <41> 5/28/91 FM Added new dcOLD macro for use in building dispatch tables ; <40> 4/2/91 dba KIP: add hasEricksonSoundMgr, since the newer Sound Mgr. is on ; some machines that have the overpatch mistake fixed ; <39> 1/30/91 gbm sab, #38: Change the ‘already including this file’ variable to ; all uppercase (for security reasons) ; <38> 1/27/91 PKE bbm: (also dba) fixed optimization of jsrROM, since it was ; bogus. Fix was deleting LastJMP$&name parameter from Try$ call ; in JsrROM, which caused it to BSR to another nearby BSR instead ; of to the JMP into ROM. Whiteboard bug, no BRC #. ; <37> 1/15/91 stb & dba; Fix the scratchRegister feature of the xxxROM macros. ; They were broken, once again proving that nothing works until ; you test it. ; <36> 1/14/91 JDR (dba) Add .far option to peaROM, jsrROM, and jmpROM, as long as ; you provide a scratch register. ; <35> 1/14/91 JDR (dba) Changed the leaROM macro to support absolute addresses. ; <34> 12/18/90 gbm (dba) Add new condition noPatchProtector and introduce the ; concept of constant values for conditions. ; <33> 12/14/90 BBM (dba) add two conditionals for linked patching, using24BitHeaps, ; using32BitHeaps. Added support for vectors in ; ComeFromPatchProc. ; <32> 12/8/90 dba create symbols for the ROM versions ; <31> 12/4/90 dba Add the ComeFromAfterPatchProc macro for Kevin and Brian. ; Went nuts and added rejoinROM, too. Allow # signs in cmpROM. Put ; markers in the file. ; <30> 10/22/90 JSM Change Dispatcher macro to take advantage of new absolute ; address support in DispatchHelper, don't optimize for all even ; selectors for now. ; <29> 10/8/90 JDR get rid of hasPMMUNoVM and add hasPMMU and notVM ; <28> 9/25/90 KIP (really dba) Added new conditional noEricksonSoundMgr. ; <27> 8/21/90 csd Added a condition which excludes the Elsie: hasMemoryDispatch. ; <26> 8/19/90 dba add six new loading conditions that were needed for Sony and MMU ; patching ; <25> 8/10/90 DTY Make PatchProc take offsets into ExpandMem. ; <24> 7/23/90 dba put some checks in for some common errors ; <23> 7/20/90 CCH Unfixed the fix below. ; <22> 7/17/90 CCH Fixed dcImport macro so that the patch linker recognizes it. ; <21> 7/16/90 DTY (Really Darin.) Added conditional in cmpROM so TRY$ is only ; executed when &operand is a register. ; <20> 7/11/90 dba add dcROM, dcImport, and dcImportResident ; <19> 7/11/90 dba fix bug from last check-in ; <18> 7/10/90 dba turn off the DefineConditions$ for ROM builds ; <17> 7/10/90 gbm Fix undefined SysVers to SYSVERS, because butt-head Darin turns ; CASE ON in LInkPatch.a ; <16> 7/10/90 DTY Added not32BitCQD conditional for System 6 builds. ; <15> 7/7/90 JSM Include DispatchHelperPriv.a and use _DispatchHelper. ; <14> 6/22/90 DTY Fix forROM case. ; <13> 6/11/90 JSM Fix calculation of ReturnAddressDepth. ; <12> 6/8/90 JSM Fix cmpROM macro. ; <11> 6/8/90 dba fix all macros that use offsets from the PC (e.g., *+xxx); this ; gets rid of bugs with complicated effective addresses ; <10> 5/30/90 dba allow ROM lists on MakePatch/MakeInstall; make trap numbers work ; without TrapNumbers.a; split dispatcher handling to support ; dispatchers with huge numbers of traps; fix ComeFromPatchProc to ; support all kinds of OS traps, ones that save A0 and ones that ; do not save A0 ; <8> 4/20/90 KSM Modified the Dispatcher macro to allow the routine selector ; number to be defaulted. The default symbol generated for a ; routine named “MyProc” is “selectMyProc” (how easy can this ; get?). ; <7> 4/19/90 dba make the Dispatcher macro with Kevin; improve error messages and ; some of the looping logic ; <6> 4/17/90 dba get rid of LastJSROld$ which was never used; fix bugs in some of ; the optimizations in ROM calls ; <5> 4/4/90 dba fix so it complains when patching for no ROMs ; <4> 3/21/90 dba fix ROM version number for the SE ; <3> 3/9/90 KSM Now INCLUDES TrapNames.d -- TrapNames.a is too huge! ; <2> 3/9/90 KSM Now includes “TrapNames.a.” ; <1> 2/20/90 KSM First checked in. ; 2/12/90 dba use the real opcode for xxxROM macros instead of NOPs ; 1/27/90 dba added branches to ROM addresses ; 1/25/90 dba added a header ; ; To Do: ; make ROM versions of more macros ; optimize for 020 (especially the ROM versions of the macros) ; if &type('__INCLUDINGLINKEDPATCHMACROS__') = 'UNDEFINED' then __IncludingLinkedPatchMacros__: set 1 include 'DispatchHelperPriv.a' ; —————————————————————————————————————————————————————————————————————————————————————————————————— ; equates ; this file is for patches by default if &type('forROM') = 'UNDEFINED' then forROM: equ 0 endif ; This magic cookie is used to do fixups for jmpOld, jsrOld, etc. OldTrapAddressMagicCookie: equ $ACBDADFB ; these two words must never occur in normal code ; —————————————————————————————————————————————————————————————————————————————————————————————————— ; some macros that are the same for ROM or patches; more of these at the end of the file ; OptimizeFor sets up so that the macros will try to generate small code or fast code. macro OptimizeFor &sizeOrSpeed gbla &optimizeForSpeed if &lowcase(&sizeOrSpeed) = 'size' then &optimizeForSpeed: seta 0 elseif &lowcase(&sizeOrSpeed) = 'speed' then &optimizeForSpeed: seta 1 else aerror &concat('only “size” or “speed” allowed, not “',&sizeOrSpeed,'”') endif endm ; ExportSymbol$ exports a symbol. macro ExportSymbol$ &symbol ; make a proc to put the symbol in if the the module name is empty ; *** this won’t work for unnamed modules, but I can’t figure out a better way lcla &makeProc &makeProc: seta &sysmod = '' if &makeProc then proc endif export &symbol &symbol: equ * if &makeProc then endproc endif endm ; Try$ tries an optimize for space case out and is used by many macros below. macro Try$ &trySymbol,&distance,&opcode,&operand,&defineSymbol gbla &optimizeForSpeed gbla &noCodeYet ; try this case if &noCodeYet and (not &optimizeForSpeed) and (&type(&trySymbol) = 'CODE LABEL') then if * - &eval(&trySymbol) <= &eval(&distance) then if &operand = '' then @where: &opcode &trySymbol else @where: &opcode &trySymbol,&operand endif &noCodeYet: seta 0 if &defineSymbol ≠ '' then &defineSymbol: set @where endif endif endif endm ; —————————————————————————————————————————————————————————————————————————————————————————————————— if forROM then ; —————————————————————————————————————————————————————————————————————————————————————————————————— ; macros for ROM (so we don’t have to heavily conditionalize code used both in ROM and patches) macro ROMs endm macro leaROM.&size &name,®ister import &name if &upcase(&size) = 'FAR' then if &setting('MACHINE') = 'MC68000' then @local: lea (&name-@local).L,®ister lea @local(pc,®ister..L),®ister else lea ((&name).l,pc),®ister endif else lea &name,®ister endif endm macro peaROM.&size &name,&scratchRegister import &name if &upcase(&size) = 'FAR' then if &setting('MACHINE') = 'MC68000' then if &substr(&type(&scratchRegister),1,5) = 'REG A' then @local: lea (&name-@local).L,&scratchRegister pea @local(pc,&scratchRegister..L) else aerror 'must specify address register to use peaROM.far on a 68000' endif else pea ((&name).l,pc) endif else pea &name endif endm macro jmpROM.&size &name,&scratchRegister gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMP$&name,128-2,bra.s,,LastJMP$&name Try$ LastJMP$&name,128-2,bra.s,,LastJMP$&name ; if optimized cases didn’t kick in, generate a JMP for the patch linker if &noCodeYet then import &name if &upcase(&size) = 'FAR' then if &setting('MACHINE') = 'MC68000' then if &substr(&type(&scratchRegister),1,5) = 'REG A' then @local: lea (&name-@local).L,&scratchRegister jmp @local(pc,&scratchRegister..L) else aerror 'must specify address register to use jmpROM.far on a 68000' endif else jmp ((&name).l,pc) endif else @where: jmp &name LastJMP$&name: set @where LastRealJMP$&name: set LastJMP$&name endif endif endm macro jsrROM.&size &name,&scratchRegister gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMP$&name,128-2,bsr.s Try$ LastJMP$&name,128-2,bsr.s ; if optimized cases didn’t kick in, generate a JSR for the patch linker if &noCodeYet then import &name if &upcase(&size) = 'FAR' then if &setting('MACHINE') = 'MC68000' then if &substr(&type(&scratchRegister),1,5) = 'REG A' then @local: lea (&name-@local).L,&scratchRegister jsr @local(pc,&scratchRegister..L) else aerror 'must specify address register to use jsrROM.far on a 68000' endif else jsr ((&name).l,pc) endif else jsr &name endif endif endm macro BranchROM$ &name,&condition,&oppositeCondition gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMP$&name,128-2,b&condition..s Try$ LastJMP$&name,128-2,b&condition..s ; if optimized cases didn’t kick in, generate a JMP for the patch linker if &noCodeYet then b&oppositeCondition..s @skip import &name @where: jmp &name @skip: LastJMP$&name: set @where LastRealJMP$&name: set LastJMP$&name endif endm macro leaResident &name,®ister import &name lea &name,®ister endm macro peaResident &name import &name pea &name endm macro &name: ROMBind endm macro DefineConditions$ endm macro MakePatch endm ; MakeDispatcherTable ; Called by EndDispatcher to generate the dispatch table for _DispatchHelper ; Unlike the version for patches, we don't use absolute addresses in the dispatch table ; since the ROM may move in memory. Instead, we use word offsets from the beginning ; of the table. macro MakeDispatcherTable gbla &firstSelector gbla &lastSelector gbla &allSelectorsEven gblc &selectorName[256] gbla &selectorIndex[256] ; make table header DispatchTable: if &allSelectorsEven then dc.w (1< 15 then aerror 'too many ROMs; up to 15 allowed' exitm endif ; define a condition bit numberfor each non-ROM condition &index: seta 0 while &index < &nbr(&syslist) do &index: seta &index + 1 if &nbr(&syslist[&index]) ≠ 2 then Condition$&syslist[&index]: equ &numConditions &numConditions: seta &numConditions + 1 endif endwhile NumConditions$: equ &numConditions ; check to see that a mask with 1 bit per condition will fit in 31 bits if NumConditions$ > 31 then aerror 'too many conditions; up to 31 allowed' exitm endif ; make masks ROMsMask$: equ (1 << NumROMs$) - 1 NonROMConditionsMask$: equ (1 << NumConditions$) - 1 - ROMsMask$ endm ; ParseLoadConditions$ parses a list of conditions. ; It gets passed a string containing '(condition,condition,...)'. macro ParseLoadConditions$ &conditions gbla &loadConditions &loadConditions: seta 0 ; parse string passed with conditions into a list if (&substr(&conditions,1,1) ≠ '(') or (&substr(&conditions,&len(&conditions),1) ≠ ')') then aerror 'missing parentheses' exitm endif lclc &conditionList[NumConditions$] lcla &numConditions &numConditions: seta &list(&substr(&conditions,2,&len(&conditions)-2),'&conditionList') ; for each condition, set the appropriate bit in &loadConditions lcla &index &index: seta 0 lclc &conditionName lclc &symbol lcla &mask while &index < &numConditions do &index: seta &index + 1 &conditionName: setc &conditionList[&index] &symbol: setc &concat('Condition$',&conditionName) if &type(&symbol) = 'EQU' then &mask: seta 1 << &eval(&symbol) if (&mask and &loadConditions) ≠ 0 then aerror &concat('ROM “',&conditionName,'” mentioned twice') else &loadConditions: seta &loadConditions or &mask endif else aerror &concat('never heard of ROM named “',&conditionName,'”') endif endwhile ; at least one ROM must be mentioned if (&loadConditions and ROMsMask$) = 0 then aerror 'no ROMs mentioned' endif endm ; ROMs defines which ROM versions apply as a default from this point on. macro ROMs gbla &loadConditions,&defaultLoadConditions ; build a string out of the ROMs passed lclc &roms &roms: setc '' lcla &index &index: seta 0 while &index < &nbr(&syslist) do &index: seta &index + 1 &roms: setc &concat(&roms,',',&syslist[&index]) endwhile ; now parse the list, and set the conditions scope ParseLoadConditions$ (&substr(&roms,2,32767)) &defaultLoadConditions: seta &loadConditions endm ; ROMBind binds pairs of ROM versions and addresses. macro &name: ROMBind ; go through each pair, checking that the ROM name is good, and making table entries lcla &seenMask &seenMask: seta 0 lcla &index &index: seta 0 lclc &romName lclc &symbol lcla &romNum lcla &mask while &index < &nbr(&syslist) do &index: seta &index + 1 &romName: setc &syslist[&index,1] &symbol: setc &concat('Condition$',&romName) if &type(&symbol) = 'EQU' then &romNum: seta &eval(&symbol) &mask: seta 1 << &romNum if (&mask and ROMsMask$) = 0 then aerror &concat('can’t bind with a condition like “',&romName,'”, only a ROM') elseif (&mask and &seenMask) ≠ 0 then aerror &concat('“',&romName,'” mentioned twice') else ExportSymbol$ BIND$&name.$&romNum.$&eval(&syslist[&index,2])$ &seenMask: seta &seenMask or &mask endif else aerror &concat('never heard of ROM named “',&romName,'”') endif endwhile endm ; leaOld loads the old address of a trap into a register. macro leaOld ®ister gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastOld$,32768-4,move.l,®ister ; if optimized cases didn’t kick in, generate a MOVE.L with the magic cookie if &noCodeYet then if &type('InComeFrom$') = 'UNDEFINED' then @where: move.l #OldTrapAddressMagicCookie,®ister LastOld$: set @where + 2 else aerror 'old reference too far in come-from patch' endif endif endm ; peaOld pushes the old address of a trap onto the stack. macro peaOld gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastOld$,32768-4,move.l,-(sp) ; if optimized cases didn’t kick in, generate a MOVE.L with the magic cookie if &noCodeYet then if &type('InComeFrom$') = 'UNDEFINED' then @where: move.l #OldTrapAddressMagicCookie,-(sp) LastOld$: set @where + 2 else aerror 'old reference too far in come-from patch' endif endif endm ; jmpOld jumps to the old implementation of a trap. ; If we are set to OptimizeFor size, then branch to a previous jmpOld. macro jmpOld gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMPOld$,128-2,bra.s,,LastJMPOld$ Try$ LastJMPOld$,128-2,bra.s,,LastJMPOld$ Try$ LastRealJMPOld$,32768-4,bra.w,,LastJMPOld$ Try$ LastJMPOld$,32768-4,bra.w,,LastJMPOld$ ; if optimized cases didn’t kick in, generate a JMP.L with the magic cookie if &noCodeYet then if &type('InComeFrom$') = 'UNDEFINED' then @where: jmp (OldTrapAddressMagicCookie).L LastJMPOld$: set @where LastRealJMPOld$: set LastJMPOld$ LastOld$: set LastJMPOld$ + 2 else aerror 'old reference too far in come-from patch' endif endif endm ; jsrOld calls the old implementation of a trap. ; If we are set to OptimizeFor size, then branch to a previous jmpOld or jsrOld. macro jsrOld gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMPOld$,128-2,bsr.s Try$ LastJMPOld$,128-2,bsr.s Try$ LastRealJMPOld$,32768-4,bsr.w Try$ LastJMPOld$,32768-4,bsr.w ; if optimized cases didn’t kick in, generate a JSR.L with the magic cookie if &noCodeYet then if &type('InComeFrom$') = 'UNDEFINED' then @where: jsr (OldTrapAddressMagicCookie).L LastOld$: set @where + 2 else aerror 'old reference too far in come-from patch' endif endif endm ; BranchOld$ branches to the old implementation of a trap. ; It is used by the specific branch macros to build a variety of branches. macro BranchOld$ &condition,&oppositeCondition gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMPOld$,128-2,b&condition..s Try$ LastJMPOld$,128-2,b&condition..s Try$ LastRealJMPOld$,32768-4,b&condition..w Try$ LastJMPOld$,32768-4,b&condition..w ; if optimized cases didn’t kick in, generate a JMP.L with the magic cookie if &noCodeYet then if &type('InComeFrom$') = 'UNDEFINED' then b&oppositeCondition..s @skip @where: jmp (OldTrapAddressMagicCookie).L @skip: LastJMPOld$: set @where LastRealJMPOld$: set LastJMPOld$ LastOld$: set LastJMPOld$ + 2 else aerror 'old reference too far in come-from patch' endif endif endm ; bccOld macro bccOld BranchOld$ cc,cs endm ; bcsOld macro bcsOld BranchOld$ cs,cc endm ; beqOld macro beqOld BranchOld$ eq,ne endm ; bgeOld macro bgeOld BranchOld$ ge,lt endm ; bgtOld macro bgtOld BranchOld$ gt,le endm ; bhiOld macro bhiOld BranchOld$ hi,ls endm ; bhsOld macro bhsOld BranchOld$ hs,lo endm ; bleOld macro bleOld BranchOld$ le,gt endm ; bloOld macro bloOld BranchOld$ lo,hs endm ; blsOld macro blsOld BranchOld$ ls,hi endm ; bltOld macro bltOld BranchOld$ lt,ge endm ; bmiOld macro bmiOld BranchOld$ mi,pl endm ; bneOld macro bneOld BranchOld$ ne,eq endm ; bnzOld macro bnzOld BranchOld$ nz,z endm ; bplOld macro bplOld BranchOld$ pl,mi endm ; braOld macro braOld jmpOld endm ; bvcOld macro bvcOld BranchOld$ vc,vs endm ; bvsOld macro bvsOld BranchOld$ vs,vc endm ; bzOld macro bzOld BranchOld$ z,nz endm ; leaROM loads the fixed ROM address of something. ; There must be a corresponding ROMBind somewhere else. macro leaROM &name,®ister gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastROM$&name,32768-4,move.l,®ister ; if optimized cases didn’t kick in, generate an LEA for the patch linker if &noCodeYet then @where: move.l #'dba',®ister org @where + 2 import ROM$&name.$ jmp ROM$&name.$ LastROM$&name: set @where + 2 endif endm ; peaROM pushes the fixed ROM address of something. ; There must be a corresponding ROMBind somewhere else. macro peaROM &name gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastROM$&name,32768-4,move.l,-(sp) ; if optimized cases didn’t kick in, generate a PEA for the patch linker if &noCodeYet then @where: move.l #'dba',-(sp) org @where + 2 import ROM$&name.$ jmp ROM$&name.$ LastROM$&name: set @where + 2 endif endm ; jmpROM jumps to something by fixed ROM address. ; There must be a corresponding ROMBind somewhere else. macro jmpROM &name gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMPROM$&name,128-2,bra.s,,LastJMPOld$&name Try$ LastJMPROM$&name,128-2,bra.s,,LastJMPROM$&name Try$ LastRealJMPROM$&name,32768-4,bra.w,,LastJMPROM$&name Try$ LastJMPROM$&name,32768-4,bra.w,,LastJMPROM$&name ; if optimized cases didn’t kick in, generate a JMP for the patch linker if &noCodeYet then @where: jmp 'dba' org @where + 2 import ROM$&name.$ jmp ROM$&name.$ LastJMPROM$&name: set @where LastRealJMPROM$&name: set LastJMPROM$&name LastROM$&name: set LastJMPROM$&name + 2 endif endm ; jsrROM calls something by fixed ROM address. ; There must be a corresponding ROMBind somewhere else. macro jsrROM &name gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMPROM$&name,128-2,bsr.s Try$ LastJMPROM$&name,128-2,bsr.s Try$ LastRealJMPROM$&name,32768-4,bsr.w Try$ LastJMPROM$&name,32768-4,bsr.w ; if optimized cases didn’t kick in, generate a JSR for the patch linker if &noCodeYet then @where: jsr 'dba' org @where + 2 import ROM$&name.$ jmp ROM$&name.$ LastROM$&name: set @where + 2 endif endm ; cmpROM compares an operand with a fixed ROM address. ; There must be a corresponding ROMBind somewhere else. macro cmpROM &nameWithPossiblePoundPrefix,&operand gbla &noCodeYet &noCodeYet: seta 1 ; strip off the '#' prefix on a name, if it exists lclc &name if &substr(&nameWithPossiblePoundPrefix,1,1) = '#' then &name: setc &substr(&nameWithPossiblePoundPrefix,2,&len(&nameWithPossiblePoundPrefix)-1) else &name: setc &nameWithPossiblePoundPrefix endif ; try optimal case if &substr(&type(&operand),1,4) = 'REG ' then Try$ LastROM$&name,32768-4,cmp.l,&operand endif ; if optimized cases didn’t kick in, generate a PEA and CMP for the patch linker if &noCodeYet then @where: cmp.l #'dba',&operand org @where + 2 import ROM$&name.$ jmp ROM$&name.$ LastROM$&name: set @where + 2 org endif endm ; BranchROM$ branches to the ROM implementation of a trap. ; It is used by the specific branch macros to build a variety of branches. macro BranchROM$ &name,&condition,&oppositeCondition gbla &noCodeYet &noCodeYet: seta 1 ; try optimal case Try$ LastRealJMPROM$&name,128-2,b&condition..s Try$ LastJMPROM$&name,128-2,b&condition..s Try$ LastRealJMPROM$&name,32768-4,b&condition..w Try$ LastJMPROM$&name,32768-4,b&condition..w ; if optimized cases didn’t kick in, generate a JMP for the patch linker if &noCodeYet then b&oppositeCondition..s @skip @where: jmp 'dba' @skip: org @where + 2 import ROM$&name.$ jmp ROM$&name.$ LastJMPROM$&name: set @where LastRealJMPROM$&name: set LastJMPROM$&name LastROM$&name: set LastJMPROM$&name + 2 endif endm ; dcROM creates a long word containing a ROM address. ; There must be a corresponding ROMBind somewhere else. macro dcROM &name import ROM$&name.$ LastROM$&name: jmp ROM$&name.$ endm ; dcImport creates an absolute address from an imported symbol. macro dcImport &name import &name @where: jmp &name org @where dc.w 0 org endm ; dcImportResident creates an absolute address from an imported symbol. ; The module containing the address will be copied into the system heap. macro dcImportResident &name dcImport &name export RESIDENT$&name.$&sysmod.$&sysindex.$ RESIDENT$&name.$&sysmod.$&sysindex.$: equ *-2 endm ; dcOLD defines a long word magic cookie which will be filled in by the LinkPatch ; loader macro &lab dcOld &lab dc.l (OldTrapAddressMagicCookie) endm ; leaResident gets the address of a routine, and also marks it resident. ; It will be copied into the system heap if the install code with the leaResident is executed. macro leaResident &name,®ister import &name lea &name,®ister export RESIDENT$&name.$&sysmod.$&sysindex.$ RESIDENT$&name.$&sysmod.$&sysindex.$: equ *-2 endm ; peaResident gets the address of a routine, and also marks it resident. ; It will be copied into the system heap if the install code with the peaResident is executed. macro peaResident &name import &name pea &name export RESIDENT$&name.$&sysmod.$&sysindex.$ RESIDENT$&name.$&sysmod.$&sysindex.$: equ *-2 endm ; MakePatch makes the specified routine installed as a patch. ; It works by creating a strange symbol that is recognized by the LinkPatch tool. macro MakePatch &entry,&trap,&roms gbla &loadConditions,&defaultLoadConditions gbla &trapNum gbla &comeFromAllowed gblc &LPFileName if &nbr(&syslist) > 3 then aerror 'too many parameters to MakePatch' exitm endif ; figure out which ROMs this patch applies for if &roms = '' then &loadConditions: seta &defaultLoadConditions else ParseLoadConditions$ &roms endif ; at least one ROM must be mentioned if (&loadConditions and ROMsMask$) = 0 then aerror 'no ROMs mentioned (each patch or install must apply to at least one ROM)' exitm endif ; evaluate the trap if &trap = '' then aerror 'no trap mentioned (each patch must specify a trap)' exitm elseif &type(&upcase(&trap)) = 'OPWORD' then if &findsym(&sysglobal, &upcase(&trap)) then &trapNum: seta &sysvalue elseif &findsym(&syslocal, &upcase(&trap)) then &trapNum: seta &sysvalue else aerror 'internal error in MakePatch (an OPWORD, but not in any symbol table)' exitm endif elseif &substr(&upcase(&trap),1,13) = 'EXPANDMEMREC.' then &trapNum: seta &eval(&trap) + $4000 if (&trapNum < $4000) or (&trapNum > $7FFC) then aerror 'ExpandMem global that is not in the $0000 - $3FFC range' exitm endif else &trapNum: seta &eval(&trap) if ((&trapNum < 0) or (&trapNum > $3FFC)) and ((&trapNum and $F000) ≠ $A000) then aerror 'not a low memory global ($0002 - $3FFC) or a trap number ($A000 - $AFFF)' exitm endif endif ; if come-from, check to see that it is a real trap number and not just a vector number ; changing the high nibble to C instead of A makes it a come-from patch if &type('InComeFrom$') = 'EQU' then if (&trapNum and $F000) = $A000 then &trapNum: seta $C000 or (&trapNum and $0FFF) else aerror 'come-from patch on a trap that is not in the $A000 range' exitm endif endif ; export a symbol for the patch linker ExportSymbol$ PATCH$&entry.$&trapNum.$&loadConditions.$ COMMENT 'PATCH$&LPFileName.$&entry.$&trapNum.$&loadConditions.$' endm ; ComeFromPatchProc makes a proc that is installed as a come-from patch. ; Specify the trap, and the name of a fixed ROM address that should trigger the patch. macro &module: ComeFromPatchProc &trap,&fromWhere,&roms gbla &trapNum if &nbr(&syslist) > 3 then aerror 'too many parameters to ComeFromPatchProc' exitm endif if &module = '' then aerror 'come-from patches must be named; invent one that I’ll like' exitm endif &module: proc export bra.s @skipOld jmpOld @skipOld: InComeFrom$: equ 1 MakePatch &module,&trap,&roms ; figure out the depth of the return address if &trapNum < $A000 then ; not a trap <33> ReturnAddressDepth: equ 0 elseif &trapNum and $800 then ; Toolbox trap ReturnAddressDepth: equ 0 elseif &trapNum and $100 then ; OS trap (no save A0) ReturnAddressDepth: equ 6*4 else ; OS trap (save A0) ReturnAddressDepth: equ 7*4 endif ; generate a compare with the return address if that is appropriate gblc &comeFromWhere ; remember address for rejoinROM &comeFromWhere: setc &fromWhere if &fromWhere ≠ '' then cmpROM &fromWhere,ReturnAddressDepth(sp) bneOld endif endm ; ComeFromAfterPatchProc makes a proc that is installed as a come-from patch. ; Specify the trap, and the name of a fixed ROM address that should trigger the patch. ; It triggers *after* the original trap has been called. ; You must return by jumping back into the ROM, not with an rts. macro &module: ComeFromAfterPatchProc &trap,&fromWhere,&roms if &nbr(&syslist) > 3 then aerror 'too many parameters to ComeFromAfterPatchProc' exitm endif if &fromWhere = '' then aerror 'ComeFromAfterPatchProc must have a fromWhere address' exitm endif &module: ComeFromPatchProc &trap,&fromWhere,&roms ; generate the code that will call the original trap and return if ReturnAddressDepth = 0 then addq.w #4,sp jsrOld else pea @returnHere ; push the return address move.l (sp)+,ReturnAddressDepth(sp) ; put it where the trap dispatcher will find it jmpOld @returnHere: endif endm ; rejoinROM returns to the ROM address that triggered a ComeFromPatchProc. macro rejoinROM if &type('ReturnAddressDepth') = 'UNDEFINED' then aerror 'rejoinROM can only be used within a ComeFromPatchProc or ComeFromAfterPatchProc.' exitm endif gblc &comeFromWhere if &comeFromWhere = '' then aerror 'rejoinROM can only be used if you specify a come-from address in ComeFromPatchProc' exitm endif jmpROM &comeFromWhere endm ; MakeDispatcherTable ; Called by EndDispatcher to generate the dispatch table for _DispatchHelper ; Unlike the version for ROM, we can use absolute addresses in the dispatch table. macro MakeDispatcherTable gbla &firstSelector gbla &lastSelector gbla &allSelectorsEven gblc &selectorName[256] gbla &selectorIndex[256] ; make table header DispatchTable: ; ***DispatchHelper doesn't support even selectors and absolute entries for now. ; If it did support them, &allSelectorsEven is a flag that allows us to shrink ; the size of the dispatch table. For now, we'll just set it to false again and ; fill the unused entries in the table with the unimplemented routine. ; ; If we decide to make DispatchHelper support even selectors and absolute entries, ; just remove the following if statement. if &allSelectorsEven then &allSelectorsEven: seta 0 endif if &allSelectorsEven then dc.w (1< 2 then aerror 'too many parameters to MakeInstall' exitm endif MakePatch &entry,0,&roms endm ; PatchProc makes a proc that is installed as a patch. macro &module: PatchProc &trap,&roms if &nbr(&syslist) > 2 then aerror 'too many parameters to PatchProc' exitm endif if &module = '' then aerror 'patches must be named, ranked, and serial numbered' exitm endif &module: proc export MakePatch &module,&trap,&roms endm ; InstallProc makes a proc that will be called while patching. macro &module: InstallProc &roms if &nbr(&syslist) > 1 then aerror 'too many parameters to InstallProc' exitm endif if &module = '' then aerror 'install routines must be named, so that they can be lined up and executed' exitm endif &module: proc export MakePatch &module,0,&roms endm ; BeginDispatcher macro &module: BeginDispatcher &trap,&roms gbla &inDispatcher gblc &dispatcherModule gblc &dispatcherTrap gblc &dispatcherROMs gbla &numSelectors gbla &firstSelector gbla &lastSelector gbla &allSelectorsEven if &nbr(&syslist) > 2 then aerror 'too many parameters to BeginDispatcher' exitm endif if &inDispatcher then aerror 'two BeginDispatchers without an intervening EndDispatcher' exitm endif ; initialize variables for registering selectors &inDispatcher: seta -1 ; means an error initializing if &module = '' then aerror 'dispatchers must be named, pick a cool one from your sordid past' exitm endif &dispatcherModule: setc &module &dispatcherTrap: setc &trap &dispatcherROMs: setc &roms &numSelectors: seta 0 &firstSelector: seta 127 &lastSelector: seta -128 &allSelectorsEven: seta 1 &inDispatcher: seta 1 ; means success endm ; DispatchSelectors macro DispatchSelectors &selectors gbla &inDispatcher gbla &numSelectors gbla &firstSelector gbla &lastSelector gbla &allSelectorsEven gblc &selectorName[256] gbla &selectorIndex[256] if &inDispatcher < 0 then exitm elseif &inDispatcher = 0 then aerror 'DispatchSelector must be used within a Begin/EndDispatcher pair' exitm endif ; find first and last, also notice if all selectors are even lcla &count lcla ¬List lclc &element lclc &name lcla &num lcla &index lcla &equalPos &count: seta &nbr(&selectors) ¬List: seta &count = 0 if ¬List then &count: seta &nbr(&syslst) endif &index: seta 0 while &index < &count do &index: seta &index + 1 if ¬List then &element: setc &syslst[&index] else &element: setc &selectors[&index] endif ; find the equal sign to split the parameter in two if &element ≠ '' then &equalPos: seta &pos('=',&element) if (&equalPos = 1) or (&equalPos >= &len(&element)) then aerror &concat('selector must have the form “name=number”, not “',\ &element,'”, you unfortunate slime') else if (&equalPos = 0) then &name: setc &trim(&element) &num: seta &eval(&concat('select', &element)) else &name: setc &trim(&substr(&element,1,&equalPos-1)) &num: seta &eval(&substr(&element,&equalPos+1,32767)) endif if (&num > 127) or (&num < -128) then aerror &concat('selector must be between -128 and 127, “',\ &element,'” is byteing off more than you can chew') else ; file away the results in a cool array &numSelectors: seta &numSelectors + 1 if &numSelectors > 256 then aerror 'maximum is 256 selectors (go overload some other trap)' exitm endif &selectorName[&numSelectors]: setc &name if &selectorIndex[&num + 129] ≠ 0 then aerror &concat('selector ',&inttostr(&num),' was used twice, ',\ 'for “',&selectorName[&selectorIndex[&num + 129]],'” and “',&name,\ '”; make up your mind') endif &selectorIndex[&num + 129]: seta &numSelectors ; keep track of common properties of all the selectors if &num < &firstSelector then &firstSelector: seta &num endif if &num > &lastSelector then &lastSelector: seta &num endif if &num and 1 then &allSelectorsEven: seta 0 endif endif endif endif endwhile endm ; EndDispatcher ; Specify the trap for the dispatcher, the ROMs, and each selector, along with its ID number. macro EndDispatcher gbla &inDispatcher gblc &dispatcherModule gblc &dispatcherTrap gblc &dispatcherROMs gbla &numSelectors gbla &firstSelector gbla &lastSelector gbla &allSelectorsEven gblc &selectorName[256] gbla &selectorIndex[256] if &inDispatcher < 0 then &inDispatcher: seta 0 exitm elseif &inDispatcher = 0 then aerror 'EndDispatcher without a matching BeginDispatcher' exitm endif &inDispatcher: seta 0 ; check parameters if &numSelectors = 0 then aerror 'you must specify at least one selector, otherwise there’s not much to dispatch' exitm endif ; make proc &dispatcherModule: proc export MakePatch &dispatcherModule,&dispatcherTrap,&dispatcherROMs ; call the dispatch helper with the address of the table lea DispatchTable,a0 _DispatchHelper ; make the dispatch table (different formats depending on whether for ROM or for patches) MakeDispatcherTable ; end the proc, and we are outa here endproc endm ; Dispatcher makes a patch for a dispatched trap. ; Specify the trap for the dispatcher, the ROMs, and each selector, along with its ID number. macro &module: Dispatcher &trap,&roms,&selectors if &nbr(&syslist) > 3 then aerror 'too many parameters to Dispatcher' exitm endif &module: BeginDispatcher &trap,&roms DispatchSelectors &selectors EndDispatcher endm ; —————————————————————————————————————————————————————————————————————————————————————————————————— ; initialization (add new ROMs and conditions here) ; hasEricksonOverpatchMistake is currently used for: ; a patch to the Sony driver that needs different addresses than the IIci ROMs ; a patch to the Sound Mgr. that needs to know if the new Sound Mgr. is present ; notEricksonSoundMgr is currently used for: ; a patch to the Sound Mgr. ; we should probably rethink these two names, and stick to our ROM version strategy in the future if not FORROM then DefineConditions$ (Plus,$0075),(SE,$0276),(II,$0178),(Portable,$037A),(IIci,$067C),(SuperMario,$077D),\ noPatchProtector,\ notVM,notAUX,\ hasHMMU,hasPMMU,hasMemoryDispatch,\ has800KDriver,hasFDHDDriver,hasIWM,\ hasEricksonOverpatchMistake,\ hasEricksonSoundMgr,notEricksonSoundMgr,\ using24BitHeaps,using32BitHeaps,notTERROR,hasTERROR,\ hasC96,hasPwrMgr endif ; —————————————————————————————————————————————————————————————————————————————————————————————————— endif ; ...already included