mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-22 04:31:30 +00:00
1843 lines
45 KiB
Plaintext
1843 lines
45 KiB
Plaintext
;
|
||
; 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 <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 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<<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('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<<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 ¬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
|