supermario/base/SuperMarioProj.1994-02-09/Internal/Asm/LinkedPatchMacros.a

1843 lines
45 KiB
Plaintext
Raw Permalink Normal View History

2019-06-29 15:17:50 +00:00
;
; 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 doesnt 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 <gbm> create symbols for the ROM versions
; <31> 12/4/90 dba <BBM> 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 <dba> 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 wont work for unnamed modules, but I cant 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 dont have to heavily conditionalize code used both in ROM and patches)
macro
ROMs
endm
macro
leaROM.&size &name,&register
import &name
if &upcase(&size) = 'FAR' then
if &setting('MACHINE') = 'MC68000' then
@local: lea (&name-@local).L,&register
lea @local(pc,&register..L),&register
else
lea ((&name).l,pc),&register
endif
else
lea &name,&register
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 didnt 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 didnt 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 didnt 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,&register
import &name
lea &name,&register
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<<dhSelectorsEven)
else
dc.w 0 ; dispatch table entries are offsets from the start of the table
endif
dc.b &firstSelector
dc.b &lastSelector
; make table
lclc &name
lcla &num
lcla &index
&num: seta &firstSelector
while &num <= &lastSelector do
&index: seta &selectorIndex[&num + 129]
if &index = 0 then
; *** could be a problem if DispatchHelperBadSelector is too far away in ROM
import DispatchHelperBadSelector
dc.w DispatchHelperBadSelector-DispatchTable ; unused entry, set it to unimplemented routine in DispatchHelper.a
else
import &selectorName[&index]
dc.w &selectorName[&index]-DispatchTable ; get address of entry
endif
if &allSelectorsEven then
&num: seta &num + 2
else
&num: seta &num + 1
endif
endwhile
endm
; ComeFromPatchProc doesn't work for ROMs, but we need to have it compile
macro
&module: ComeFromPatchProc
&module: proc export
endm
; xxxOld macros don't work for ROM either, but like ComeFromPatchProc we need them to compile
macro
leaOld
endm
macro
peaOld
endm
macro
jmpOld
endm
macro
jsrOld
endm
macro
bccOld
endm
macro
bcsOld
endm
macro
beqOld
endm
macro
bgeOld
endm
macro
bgtOld
endm
macro
bhiOld
endm
macro
bhsOld
endm
macro
bleOld
endm
macro
bloOld
endm
macro
blsOld
endm
macro
bltOld
endm
macro
bmiOld
endm
macro
bneOld
endm
macro
bnzOld
endm
macro
bplOld
endm
macro
braOld
endm
macro
bvcOld
endm
macro
bvsOld
endm
macro
bzOld
endm
macro
cmpROM
endm
; ——————————————————————————————————————————————————————————————————————————————————————————————————
else ; not forROM
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; macros for use in patches
; DefineConditions$ defines all of the conditions, including ROM versions and other conditions.
; Each ROM version is an ordered pair of the ROM name and the ROM version number.
; Each other condition is a name.
macro
DefineConditions$
lcla &numConditions
lcla &index
; define a condition bit and ROM version for each ROM (e.g., $0075)
&numConditions: seta 0
&index: seta 0
while &index < &nbr(&syslist) do
&index: seta &index + 1
if &nbr(&syslist[&index]) = 2 then
Condition$&syslist[&index,1]: equ &numConditions
ROMVersion$&numConditions: equ &syslist[&index,2]
kROMVersion&syslist[&index,1]: equ &syslist[&index,2]
&numConditions: seta &numConditions + 1
endif
endwhile
NumROMs$: equ &numConditions
; check to see that we can use resource IDs to express ROM combinations
if NumROMs$ > 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('cant 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 &register
gbla &noCodeYet
&noCodeYet: seta 1
; try optimal case
Try$ LastOld$,32768-4,move.l,&register
; if optimized cases didnt kick in, generate a MOVE.L with the magic cookie
if &noCodeYet then
if &type('InComeFrom$') = 'UNDEFINED' then
@where: move.l #OldTrapAddressMagicCookie,&register
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 didnt 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 didnt 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 didnt 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 didnt 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,&register
gbla &noCodeYet
&noCodeYet: seta 1
; try optimal case
Try$ LastROM$&name,32768-4,move.l,&register
; if optimized cases didnt kick in, generate an LEA for the patch linker
if &noCodeYet then
@where: move.l #'dba',&register
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 didnt 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 didnt 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 didnt 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 didnt 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 didnt 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,&register
import &name
lea &name,&register
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 Ill 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<<dhSelectorsEven)+(1<<dhAbsoluteEntries)
else
dc.w (1<<dhAbsoluteEntries)
endif
dc.b &firstSelector
dc.b &lastSelector
; make table
lclc &name
lcla &num
lcla &index
&num: seta &firstSelector
while &num <= &lastSelector do
&index: seta &selectorIndex[&num + 129]
if &index = 0 then
dcImport DispatchHelperBadSelector ; unused entry, set it to unimplemented routine in DispatchHelper.a
else
dcImport &selectorName[&index] ; get address of entry
endif
if &allSelectorsEven then
&num: seta &num + 2
else
&num: seta &num + 1
endif
endwhile
endm
; ——————————————————————————————————————————————————————————————————————————————————————————————————
endif
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; more macros that are the same for ROM or patches (these mostly call other macros)
; bccROM
macro
bccROM &name
BranchROM$ &name,cc,cs
endm
; bcsROM
macro
bcsROM &name
BranchROM$ &name,cs,cc
endm
; beqROM
macro
beqROM &name
BranchROM$ &name,eq,ne
endm
; bgeROM
macro
bgeROM &name
BranchROM$ &name,ge,lt
endm
; bgtROM
macro
bgtROM &name
BranchROM$ &name,gt,le
endm
; bhiROM
macro
bhiROM &name
BranchROM$ &name,hi,ls
endm
; bhsROM
macro
bhsROM &name
BranchROM$ &name,hs,lo
endm
; bleROM
macro
bleROM &name
BranchROM$ &name,le,gt
endm
; bloROM
macro
bloROM &name
BranchROM$ &name,lo,hs
endm
; blsROM
macro
blsROM &name
BranchROM$ &name,ls,hi
endm
; bltROM
macro
bltROM &name
BranchROM$ &name,lt,ge
endm
; bmiROM
macro
bmiROM &name
BranchROM$ &name,mi,pl
endm
; bneROM
macro
bneROM &name
BranchROM$ &name,ne,eq
endm
; bnzROM
macro
bnzROM &name
BranchROM$ &name,nz,z
endm
; bplROM
macro
bplROM &name
BranchROM$ &name,pl,mi
endm
; braROM
macro
braROM &name
jmpROM &name
endm
; bvcROM
macro
bvcROM &name
BranchROM$ &name,vc,vs
endm
; bvsROM
macro
bvsROM &name
BranchROM$ &name,vs,vc
endm
; bzROM
macro
bzROM &name
BranchROM$ &name,z,nz
endm
; MakeInstall activates the routine specified (calls it while patching).
macro
MakeInstall &entry,&roms
if &nbr(&syslist) > 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 &notList
lclc &element
lclc &name
lcla &num
lcla &index
lcla &equalPos
&count: seta &nbr(&selectors)
&notList: seta &count = 0
if &notList then
&count: seta &nbr(&syslst)
endif
&index: seta 0
while &index < &count do
&index: seta &index + 1
if &notList 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 theres 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