mirror of
https://github.com/elliotnunn/sys7.1-doc-wip.git
synced 2024-12-12 04:29:09 +00:00
1131 lines
32 KiB
Plaintext
1131 lines
32 KiB
Plaintext
;
|
||
; File: PatchMacros.a
|
||
;
|
||
; Contains: macros for patching
|
||
;
|
||
; Written by: Ed KeyCaps
|
||
;
|
||
; Copyright: © 1989-1992 by Apple Computer, Inc., all rights reserved.
|
||
;
|
||
; Change History (most recent first):
|
||
;
|
||
; <15> 4/10/92 SAH Fixed the leaFar macro for non 68000 machines by having it do
|
||
; the same thing for all machines.
|
||
; <14> 10/28/91 SAM/KSM Clean up header.
|
||
; <12> 1/30/91 gbm sab, #38: Change the ‘already including this file’ variable to
|
||
; all uppercase (for security reasons)
|
||
; <11> 1/25/91 stb JDR: remove betaStage and finalStage
|
||
; <10> 11/9/90 dba <JSM> add macros for protected come-from patches
|
||
; <9> 7/11/90 gbm stop using temp as a weapon
|
||
; <8> 5/2/90 BBM change default freespace for 6.0 compatibility.
|
||
; <6> 4/11/90 dba change so MakeSysFree is no longer used under 7.0
|
||
; <5> 2/26/90 EMT Added LeaTrap.
|
||
; <4> 1/31/90 csd Added the required $ to the hex constant. (What do you expect
|
||
; for 5:11 am?)
|
||
; <3> 1/31/90 csd In MakeSysFree, mask off the low bit of the amount of space
|
||
; requested so that the MacPlus won’t address error in SetAppBase.
|
||
; <2> 12/20/89 dba use betaStage and finalStage instead of Beta and Final
|
||
; <3.1> 11/27/89 dvb Fixed DcBind, and added DcROM
|
||
; <3.0> 8/29/89 EMT Added ACTR in FixRomAddresses to allow for more looping. Removed
|
||
; references to SPLINE_FONT per Sampo and Charlton.
|
||
; <2.9> 8/29/89 dba added OSTrapReturnAddressDepth equate
|
||
; <2.8> 8/15/89 dba NEEDED FOR 6.0.4: added leaFar, jmpFar, jsrFar, setTrapA0,
|
||
; getTrapA0, setTrap, setTrapFar, getTrap, getTrapFar,
|
||
; stuffOldTrap, and stuffOldTrapFar macros and made InstToolTp,
|
||
; InstOSTp, PatchOSJump, and PatchToolJump macros use them;
|
||
; changed leaROM so that destination can be non-address register
|
||
; <2.7> 8/7/89 EMT Per PKE, force SEEK_ALLOWED to be false on all final builds FOR
|
||
; 6.0.4.
|
||
; <2.6> 8/3/89 EMT Roll in some of the more urgent code review changes FOR 6.0.4:
|
||
; Added USE_MAKESYSFREE conditional in the same vein as
|
||
; SEEK_ALLOWED Fixed PtchInstall to correctly fail with SysError.
|
||
; Removed meaningless comments.
|
||
; <2.5> 8/2/89 CEL When building a beta system the seek function can not be used
|
||
; due to a conditional. The conditional warns a user - which is
|
||
; good. But a ROM may not be final which makes it impossible to
|
||
; build a system that works on those machines. The SEEK_ALLOWED
|
||
; variable was added that allows one to use the seek option if
|
||
; this flag is set. So the naive persons will be warned and the
|
||
; daring still can make systems that work on moving targets!!!
|
||
; <2.4> 6/28/89 EMT Only use MakeSysFree stuff for spline fonts. Otherwise, use 1.0
|
||
; version with 32-bit clean fixes (1.9, 2.2)
|
||
; <2.3> 6/12/89 EMT Cleaned up source code including 2.2 fixes.
|
||
; <2.2> 6/12/89 BAL Fixed bugs in 1.9 which caused the system heap to be huge and
|
||
; init31 to fail.
|
||
; <2.1> 6/10/89 CEL Took out old Macro labels…
|
||
; <2.0> 6/2/89 CEL ifdefed the old labels out of patchmacs
|
||
; <1.9> 6/1/89 EMT StripAddress before executing 'ptch's.
|
||
; <1.8> 5/31/89 EMT Added DcBind macro for Dave and Earsh.
|
||
; <1.7> 5/24/89 EMT Fixed "robust" Bind macros. Thanks Carl.
|
||
; <1.6> 5/23/89 CCH Backed out BIND macros that refused to be robust. This version
|
||
; same as 1.4.
|
||
; <1.5> 5/23/89 EMT Rewrote Bind macros to be more robust.
|
||
; <1.4> 5/12/89 EMT Shortened names for Bind macros. Old ones still work. Removed
|
||
; MaxSymbols, MaxReferences, and SysDifferent.
|
||
; <•1.3> 5/3/89 CEL Added seek capability in addition to hard offset binding, and
|
||
; merged macros from each PTCH file into here, so there is only
|
||
; one source for all the macros.
|
||
; <1.2> 4/12/89 EMT 'HardwareEqu.a' doesn't work due to scope, removed ROMStart
|
||
; reference.
|
||
; <1.1> 4/12/89 EMT Included 'HardwareEqu.a' for ROMStart equate.
|
||
; <1.0> 4/7/89 EMT New today.
|
||
;
|
||
|
||
IF &TYPE('__INCLUDINGPATCHMACROS__') = 'UNDEFINED' THEN
|
||
__INCLUDINGPATCHMACROS__ SET 1
|
||
|
||
IF &TYPE('__INCLUDINGFSEQU__') = 'UNDEFINED' THEN
|
||
INCLUDE 'FSEqu.a'
|
||
ENDIF
|
||
|
||
; conditionals for this file
|
||
|
||
IF (&TYPE('SEEK_ALLOWED') = 'UNDEFINED') OR (StageInt = finalStage) THEN
|
||
SEEK_ALLOWED: EQU (StageInt < betaStage)
|
||
ENDIF
|
||
|
||
IF (&TYPE('USE_MAKESYSFREE') = 'UNDEFINED') THEN
|
||
USE_MAKESYSFREE: EQU 0
|
||
ENDIF
|
||
|
||
; ROM versions
|
||
|
||
MacPlus EQU $0075
|
||
MacSE EQU $0276
|
||
MacII EQU $0178
|
||
Esprit EQU $037A
|
||
Aurora EQU $067C
|
||
|
||
; useful equates for patching
|
||
|
||
OSTrapReturnAddressDepth equ $1C
|
||
|
||
;
|
||
; MACRO leaFar &label,®ister
|
||
;
|
||
; This does an LEA that can reach farther than 32K.
|
||
;
|
||
; <SAH> Removed check for Machine other than 68000 as the macro generated
|
||
; a normal pc relative with 16 bit offset lea for these machines. All
|
||
; machines now use the 68000 version which does a real leaFar.
|
||
|
||
MACRO
|
||
leaFar &label,®ister
|
||
@local: lea (&label-@local).L,®ister
|
||
lea @local(pc,®ister..L),®ister
|
||
ENDM
|
||
|
||
;
|
||
; MACRO jmpFar &label,®ister
|
||
;
|
||
; This does an JMP that can reach farther than 32K.
|
||
;
|
||
|
||
MACRO
|
||
jmpFar &label,&scratch
|
||
IF &SETTING('MACHINE') = 'MC68000' THEN
|
||
@local: lea (&label-@local).L,&scratch
|
||
jmp @local(pc,&scratch..L)
|
||
ELSE
|
||
bra.L &label
|
||
ENDIF
|
||
ENDM
|
||
|
||
;
|
||
; MACRO jsrFar &label,®ister
|
||
;
|
||
; This does an JSR that can reach farther than 32K.
|
||
;
|
||
|
||
MACRO
|
||
jsrFar &label,&scratch
|
||
IF &SETTING('MACHINE') = 'MC68000' THEN
|
||
@local: lea (&label-@local).L,&scratch
|
||
jsr @local(pc,&scratch..L)
|
||
ELSE
|
||
bsr.L &label
|
||
ENDIF
|
||
ENDM
|
||
|
||
;
|
||
; MACRO BackToTrap &JumpLabel
|
||
;
|
||
; Insert an absolute JMP instruction, initially to the ROM reset vector, to be
|
||
; patched at installation time to the previous address from the trap table.
|
||
; the &JumpLabel provided refers to the 4-byte address to be backpatched.
|
||
;
|
||
MACRO
|
||
BackToTrap &JumpLabel
|
||
EXPORT &JumpLabel
|
||
JMP (0).L
|
||
&JumpLabel EQU *-4
|
||
ENDM
|
||
|
||
;
|
||
; MACRO JsrTrap &JumpLabel
|
||
;
|
||
; Insert an absolute JSR instruction, initially to the ROM reset vector, to be
|
||
; patched at installation time to the previous address from the trap table.
|
||
; the &JumpLabel provided refers to the 4-byte address to be backpatched.
|
||
;
|
||
MACRO
|
||
JsrTrap &JumpLabel
|
||
EXPORT &JumpLabel
|
||
JSR (0).L
|
||
&JumpLabel EQU *-4
|
||
ENDM
|
||
|
||
;
|
||
; MACRO LeaTrap &JumpLabel
|
||
;
|
||
; Insert an absolute LEA instruction, initially to the ROM reset vector, to be
|
||
; patched at installation time to the previous address from the trap table.
|
||
; the &JumpLabel provided refers to the 4-byte address to be backpatched.
|
||
;
|
||
MACRO
|
||
LeaTrap &JumpLabel, &Reg
|
||
EXPORT &JumpLabel
|
||
LEA (0).L, &Reg
|
||
&JumpLabel EQU *-4
|
||
ENDM
|
||
|
||
;
|
||
; MACRO setTrapA0 &trapNumber
|
||
;
|
||
; Install the patch pointed to by A0 as trap &trapNumber.
|
||
; This handles OS traps and Toolbox traps by checking the toolbox bit.
|
||
;
|
||
|
||
MACRO
|
||
setTrapA0 &trapNumber
|
||
LCLA &realTrapNumber
|
||
&realTrapNumber: SETA &EVAL(&trapNumber)
|
||
IF (&realTrapNumber AND $800) = 0 THEN ; it is an OS trap
|
||
&realTrapNumber: SETA &realTrapNumber AND $FF
|
||
IF &realTrapNumber < 128 THEN
|
||
moveq #&realTrapNumber,d0 ; move trap number (sometimes sign extended)
|
||
ELSE
|
||
moveq #&realTrapNumber-$100,d0 ; move trap number (sometimes sign extended)
|
||
ENDIF
|
||
_SetTrapAddress newOS
|
||
ELSE
|
||
&realTrapNumber: SETA &realTrapNumber AND $3FF
|
||
IF &realTrapNumber < 128 THEN
|
||
moveq #&realTrapNumber,d0 ; move trap number
|
||
ELSE
|
||
move.w #&realTrapNumber,d0 ; move trap number
|
||
ENDIF
|
||
_SetTrapAddress newTool
|
||
ENDIF
|
||
ENDM
|
||
|
||
;
|
||
; MACRO getTrapA0 &trapNumber
|
||
;
|
||
; Get the current address of trap &trapNumber into A0.
|
||
; This handles OS traps and Toolbox traps by checking the toolbox bit.
|
||
;
|
||
|
||
MACRO
|
||
getTrapA0 &trapNumber
|
||
LCLA &realTrapNumber
|
||
&realTrapNumber: SETA &EVAL(&trapNumber)
|
||
IF (&realTrapNumber AND $800) = 0 THEN ; it is an OS trap
|
||
&realTrapNumber: SETA &realTrapNumber AND $FF
|
||
IF &realTrapNumber < 128 THEN
|
||
moveq #&realTrapNumber,d0 ; move trap number (sometimes sign extended)
|
||
ELSE
|
||
moveq #&realTrapNumber-$100,d0 ; move trap number (sometimes sign extended)
|
||
ENDIF
|
||
_GetTrapAddress newOS
|
||
ELSE
|
||
&realTrapNumber: SETA &realTrapNumber AND $3FF
|
||
IF &realTrapNumber < 128 THEN
|
||
moveq #&realTrapNumber,d0 ; move trap number
|
||
ELSE
|
||
move.w #&realTrapNumber,d0 ; move trap number
|
||
ENDIF
|
||
_GetTrapAddress newTool
|
||
ENDIF
|
||
ENDM
|
||
|
||
;
|
||
; MACRO setTrap &patchLabel,&trapNumber
|
||
;
|
||
; Install the patch at &patchLabel as trap &trapNumber.
|
||
; This handles OS traps and Toolbox traps by checking the toolbox bit.
|
||
; As a special case, bypass IMPORTing absolute addresses written in hex with a leading '$'.
|
||
;
|
||
|
||
MACRO
|
||
setTrap &patchLabel,&trapNumber
|
||
IF &patchLabel[1] <> '$' THEN
|
||
IMPORT &patchLabel
|
||
ENDIF
|
||
lea &patchLabel,a0
|
||
setTrapA0 &trapNumber
|
||
ENDM
|
||
|
||
;
|
||
; MACRO setTrapFar &patchLabel,&trapNumber
|
||
;
|
||
; Install the patch at &patchLabel (which can be far away) as trap &trapNumber.
|
||
; This handles OS traps and Toolbox traps by checking the toolbox bit.
|
||
; As a special case, bypass IMPORTing absolute addresses written in hex with a leading '$'.
|
||
;
|
||
|
||
MACRO
|
||
setTrapFar &patchLabel,&trapNumber
|
||
IF &patchLabel[1] <> '$' THEN
|
||
IMPORT &patchLabel
|
||
ENDIF
|
||
leaFar &patchLabel,a0
|
||
setTrapA0 &trapNumber
|
||
ENDM
|
||
|
||
;
|
||
; MACRO stuffOldTrap &jumpLabel,&trapNumber
|
||
;
|
||
; Stuff the current address of trap &trapNumber at &jumpLabel,
|
||
; usually the effective address of a JMP instruction terminating a patch.
|
||
; This handles OS traps and Toolbox traps by checking the toolbox bit.
|
||
;
|
||
|
||
MACRO
|
||
stuffOldTrap &jumpLabel,&trapNumber
|
||
getTrapA0 &trapNumber
|
||
IMPORT &jumpLabel
|
||
lea &jumpLabel,a1
|
||
move.l a0,(a1)
|
||
ENDM
|
||
|
||
;
|
||
; MACRO stuffOldTrapFar &jumpLabel,&trapNumber
|
||
;
|
||
; Stuff the current address of trap &trapNumber at &jumpLabel (which can be far away),
|
||
; usually the effective address of a JMP instruction terminating a patch.
|
||
; This handles OS traps and Toolbox traps by checking the toolbox bit.
|
||
;
|
||
|
||
MACRO
|
||
stuffOldTrapFar &jumpLabel,&trapNumber
|
||
getTrapA0 &trapNumber
|
||
IMPORT &jumpLabel
|
||
leaFar &jumpLabel,a1
|
||
move.l a0,(a1)
|
||
ENDM
|
||
|
||
;
|
||
; MACRO InstOSTp &PatchLabel, &TrapNumber
|
||
;
|
||
; Install the patch at &PatchLabel as OS trap &TrapNumber. As a special case,
|
||
; bypass the IMPORTing of absolute addresses (presumably written in hex with a
|
||
; leading $) passed as &PatchLabel.
|
||
;
|
||
|
||
MACRO
|
||
InstOSTp &PatchLabel,&TrapNumber
|
||
setTrap &patchLabel,(&trapNumber AND $FF)
|
||
ENDM
|
||
|
||
;
|
||
; MACRO InstToolTp &PatchLabel, &TrapNumber
|
||
;
|
||
; Install the patch at &PatchLabel as Toolbox trap &TrapNumber. As a special case,
|
||
; bypass the IMPORTing of absolute addresses (presumably written in hex with a
|
||
; leading $) passed as &PatchLabel.
|
||
;
|
||
|
||
MACRO
|
||
InstToolTp &PatchLabel,&TrapNumber
|
||
setTrap &patchLabel,($800 OR (&trapNumber AND $3FF))
|
||
ENDM
|
||
|
||
;
|
||
; This is a macro that sets up the arguments for a vanilla Toolbox trap patch
|
||
; rom76Fix had a setfractenable with a number and not a routine
|
||
; The normal InstToolTp does not work
|
||
;
|
||
MACRO
|
||
InstToolTpNum
|
||
LCLC &trapid
|
||
&trapid SETC &Syslst[1]
|
||
IF (&trapid[1] <> '$') THEN
|
||
IMPORT &Syslst[1]
|
||
ENDIF
|
||
LEA &Syslst[1],A0
|
||
MOVE.w #&Syslst[2],D0
|
||
DC.W ($A647)
|
||
ENDM
|
||
|
||
;
|
||
; MACRO PatchOSJump &JumpLabel,&TrapNumber
|
||
;
|
||
; Stuff the current trap address of OS trap &TrapNumber into the longword at
|
||
; &JumpLabel, presumably a JMP instruction terminating a patch.
|
||
;
|
||
|
||
MACRO
|
||
PatchOSJump &JumpLabel,&TrapNumber
|
||
stuffOldTrap &jumpLabel,(&trapNumber AND $FF)
|
||
ENDM
|
||
|
||
;
|
||
; MACRO PatchToolJump &JumpLabel,&TrapNumber
|
||
;
|
||
; Stuff the current trap address of Tool trap &TrapNumber into the longword at
|
||
; &JumpLabel, presumably a JMP instruction terminating a patch.
|
||
;
|
||
|
||
MACRO
|
||
PatchToolJump &JumpLabel,&TrapNumber
|
||
stuffOldTrap &jumpLabel,($800 OR (&trapNumber AND $3FF))
|
||
ENDM
|
||
|
||
;_________________________________________________________________________________
|
||
; DcRom, JmpRom, JsrRom, PeaRom, LeaRom, CmpRA
|
||
;
|
||
; These macros are used to build a run-time symbol table for patching multiple
|
||
; ROMs.
|
||
;_________________________________________________________________________________
|
||
|
||
; A/UX Patching macros - JmpROM, JsrROM, CmpRA. <PB302>
|
||
; The ROM addresses in the instructions produced by these macros are offset by
|
||
; the actual ROM address (determined from ROMBase) at patch install time.
|
||
; Each location to be offset gets a label of the form RXXXnnn, with nnn
|
||
; being 000, 001, 002, 003, ...
|
||
; &RomFixIndex is a count of the number of locations to be fixed (and the next
|
||
; value of nnn to be used).
|
||
; The RXXXnnn labels are used to build a table of fixup locations; see FixupTbl at
|
||
; the end of this file.
|
||
;
|
||
gbla &RomFixIndex
|
||
&RomFixIndex seta 0
|
||
|
||
;
|
||
; MACRO DcRom &RomOffset <dvb>
|
||
;
|
||
; Define a longword which is a ROM address.
|
||
|
||
MACRO
|
||
DcRom &RomOffset
|
||
gbla &RomFixIndex
|
||
entry RXXX&I2S(&ROMFIXINDEX,-3)
|
||
DC.L (&RomOffset)
|
||
RXXX&I2S(&ROMFIXINDEX,-3) equ *-4
|
||
&RomFixIndex seta &RomFixIndex+1
|
||
ENDM
|
||
|
||
;
|
||
; MACRO JmpRom &RomOffset <PB302>
|
||
;
|
||
; Jump into the ROM.
|
||
|
||
MACRO
|
||
JmpRom &RomOffset
|
||
;jmp ([ROMBase],&RomOffset)
|
||
gbla &RomFixIndex
|
||
entry RXXX&I2S(&ROMFIXINDEX,-3)
|
||
jmp (&RomOffset).L ; <PB330>
|
||
RXXX&I2S(&ROMFIXINDEX,-3) equ *-4
|
||
&RomFixIndex seta &RomFixIndex+1
|
||
ENDM
|
||
|
||
;
|
||
; MACRO JsrRom &RomOffset <PB302>
|
||
;
|
||
; JSR Into the ROM
|
||
;
|
||
MACRO
|
||
JsrRom &RomOffset
|
||
gbla &RomFixIndex
|
||
entry RXXX&I2S(&ROMFIXINDEX,-3)
|
||
jsr (&RomOffset).L ; <PB330>
|
||
RXXX&I2S(&ROMFIXINDEX,-3) equ *-4
|
||
&RomFixIndex seta &RomFixIndex+1
|
||
ENDM
|
||
|
||
;
|
||
; MACRO CmpRA &RomOffset, &Effective_Address <PB302>
|
||
;
|
||
MACRO
|
||
CmpRA &RomOffset, &EffAddr
|
||
gbla &RomFixIndex
|
||
entry RXXX&I2S(&ROMFIXINDEX,-3)
|
||
RXXX&I2S(&ROMFIXINDEX,-3) equ *+2
|
||
cmp.l #&RomOffset, &EffAddr
|
||
&RomFixIndex seta &RomFixIndex+1
|
||
ENDM
|
||
|
||
;
|
||
; MACRO PeaRom &RomOffset <PB319>
|
||
;
|
||
; Pea ROM address
|
||
;
|
||
MACRO
|
||
PeaRom &RomOffset
|
||
gbla &RomFixIndex
|
||
entry RXXX&I2S(&ROMFIXINDEX,-3)
|
||
pea &RomOffset
|
||
RXXX&I2S(&ROMFIXINDEX,-3) equ *-4
|
||
&RomFixIndex seta &RomFixIndex+1
|
||
ENDM
|
||
|
||
;
|
||
; MACRO leaROM &ROMOffset,®ister
|
||
;
|
||
; LEA into the ROM
|
||
;
|
||
|
||
MACRO
|
||
leaRom &ROMOffset,®ister
|
||
GBLA &ROMFixIndex
|
||
ENTRY RXXX&I2S(&ROMFixIndex,-3)
|
||
IF &SUBSTR(&TYPE(®ister),1,5) = 'REG A' THEN
|
||
lea &ROMOffset,®ister
|
||
ELSE
|
||
move.l #&ROMOffset,®ister
|
||
ENDIF
|
||
RXXX&I2S(&ROMFixIndex,-3): equ *-4
|
||
&ROMFixIndex: seta &ROMFixIndex+1
|
||
ENDM
|
||
|
||
;_________________________________________________________________________________
|
||
; RomVersions, Bind, JmpBind, JsrBind, PeaBind, LeaBind, CmpBind, DcBind, FixRomAddresses
|
||
;
|
||
; These macros are used to build a run-time symbol table for patching multiple
|
||
; ROMs.
|
||
;_________________________________________________________________________________
|
||
|
||
;
|
||
; MACRO RomVersions &RomVersion1, RomVersion2, …
|
||
;
|
||
; Identify the ROM version numbers a paticular patch file calls.
|
||
; Allocates the symbol lists.
|
||
;
|
||
MACRO
|
||
RomVersions
|
||
GBLA &NumSymbols
|
||
&NumSymbols SETA 0
|
||
GBLA &NumReferences
|
||
&NumReferences SETA 0
|
||
GBLA &NumVersions
|
||
&NumVersions SETA &NBR(&SYSLIST)
|
||
GBLC &RomVersion[&NumVersions]
|
||
LCLA &i
|
||
&i SETA 1
|
||
|
||
WHILE &i <= &NumVersions DO
|
||
&RomVersion[&i] SETC &SYSLIST[&i]
|
||
&i SETA &i + 1
|
||
ENDWHILE
|
||
ENDM
|
||
|
||
;
|
||
; MACRO &Symbol Bind (&RomVersion1, &RomOffset1), (&RomVersion2, SEEK, &TrapNum, &Pattern1, &Pattern2, …), …
|
||
;
|
||
; Binds offsets to symbols based upon their RomVersion. &RomOffset is an offset from ROMBase,
|
||
; however, if it has memory manager bits set, an absolute must be used. These are not first class
|
||
; symbols; they only have meaning when used in these macros.
|
||
;
|
||
MACRO
|
||
&Symbol Bind
|
||
GBLA &NumSymbols
|
||
&NumSymbols SETA &NumSymbols + 1
|
||
Bind_&Symbol EQU &NumSymbols
|
||
LCLA &i, &j
|
||
&i SETA 1
|
||
|
||
WHILE &i <= &NBR(&SYSLIST) DO
|
||
IF &SYSLIST[&i,2] = 'SEEK' THEN ;test for SEEK
|
||
|
||
IF SEEK_ALLOWED THEN
|
||
RomSymTab_&SYSLIST[&i,1]_&NumSymbols EQU 0
|
||
Trap_&SYSLIST[&i,1]_&NumSymbols EQU &SYSLIST[&i,3]
|
||
NumPats_&SYSLIST[&i,1]_&NumSymbols EQU &NBR(&SYSLIST[&i]) - 3
|
||
&j SETA 1
|
||
WHILE &j <= (&NBR(&SYSLIST[&i]) - 3) DO
|
||
PatTab_&SYSLIST[&i,1]_&NumSymbols._&j EQU &SYSLIST[&i,&j+3]
|
||
&j SETA &j + 1
|
||
ENDWHILE
|
||
ELSE
|
||
! Seek not permitted on beta and final software!
|
||
ENDIF ;SEEK_ALLOWED
|
||
ELSE ;Not a SEEK
|
||
RomSymTab_&SYSLIST[&i,1]_&NumSymbols EQU &SYSLIST[&i,2]
|
||
ENDIF
|
||
&i SETA &i + 1
|
||
ENDWHILE
|
||
ENDM
|
||
|
||
;
|
||
; MACRO JmpBind &Symbol
|
||
;
|
||
; JMP through a Bind symbol.
|
||
;
|
||
MACRO
|
||
JmpBind &Symbol
|
||
GBLA &NumReferences
|
||
&NumReferences SETA &NumReferences + 1
|
||
ENTRY RomRefTab_&NumReferences
|
||
RomRefTab_&NumReferences EQU *+2
|
||
JMP (Bind_&Symbol).L
|
||
ENDM
|
||
|
||
;
|
||
; MACRO JsrBind &Symbol
|
||
;
|
||
; JSR through a Bind symbol.
|
||
;
|
||
MACRO
|
||
JsrBind &Symbol
|
||
GBLA &NumReferences
|
||
&NumReferences SETA &NumReferences + 1
|
||
ENTRY RomRefTab_&NumReferences
|
||
RomRefTab_&NumReferences EQU *+2
|
||
JSR (Bind_&Symbol).L
|
||
ENDM
|
||
|
||
;
|
||
; MACRO PeaBind &Symbol
|
||
;
|
||
; PEA a Bind symbol.
|
||
;
|
||
MACRO
|
||
PeaBind &Symbol
|
||
GBLA &NumReferences
|
||
&NumReferences SETA &NumReferences + 1
|
||
ENTRY RomRefTab_&NumReferences
|
||
RomRefTab_&NumReferences EQU *+2
|
||
PEA (Bind_&Symbol).L
|
||
ENDM
|
||
|
||
;
|
||
; MACRO LeaBind &Symbol, &Reg
|
||
;
|
||
; LEA a Bind symbol into a register.
|
||
;
|
||
MACRO
|
||
LeaBind &Symbol, &Reg
|
||
GBLA &NumReferences
|
||
&NumReferences SETA &NumReferences + 1
|
||
ENTRY RomRefTab_&NumReferences
|
||
RomRefTab_&NumReferences EQU *+2
|
||
LEA (Bind_&Symbol).L, &Reg
|
||
ENDM
|
||
|
||
;
|
||
; MACRO CmpBind &Symbol, &EA
|
||
;
|
||
; CMP a Bind symbol to an effective address.
|
||
;
|
||
MACRO
|
||
CmpBind &Symbol, &EA
|
||
GBLA &NumReferences
|
||
&NumReferences SETA &NumReferences + 1
|
||
ENTRY RomRefTab_&NumReferences
|
||
RomRefTab_&NumReferences EQU *+2
|
||
CMP.L #Bind_&Symbol, &EA
|
||
ENDM
|
||
|
||
;
|
||
; MACRO DcBind &Symbol
|
||
;
|
||
; Make a constant based upon a Bind symbol
|
||
;
|
||
MACRO
|
||
DcBind &Symbol
|
||
GBLA &NumReferences
|
||
&NumReferences SETA &NumReferences + 1
|
||
ENTRY RomRefTab_&NumReferences
|
||
RomRefTab_&NumReferences
|
||
DC.L Bind_&Symbol
|
||
ENDM
|
||
|
||
;
|
||
; MACRO FixRomAddresses
|
||
;
|
||
; Fixes the references in the patch file. Should be first thing executed by
|
||
; the patch and should live in the cut-back portion of the code. Also includes
|
||
; the assembler instructions to build the tables.
|
||
;
|
||
; This macro destroys A0-A4/D0-D3.
|
||
;
|
||
; The format of the tables are as follows:
|
||
; # of Symbols (word)
|
||
; # of Rom Versions (word)
|
||
; Rom Version Table
|
||
; Rom ID (word)
|
||
; Offset to Symbol Table (word)
|
||
; 1 per Rom Version
|
||
; Symbol Table
|
||
; Offset from ROMBase (long), 0 if a SEEK (to be replaced at runtime)
|
||
; Seek Table
|
||
; Reference ID (long)
|
||
; Trap # (word)
|
||
; # of longs to search (word)
|
||
; search pattern (n longs)
|
||
; 0.L at the end of each seek table
|
||
; Reference Table
|
||
; Offset to reference (long)
|
||
; 0.L
|
||
;
|
||
; After retreiving the correct address, it may be altered as follows to ensure it
|
||
; works correctly under A/UX.
|
||
; Addressing High Byte in Instruction Action
|
||
; 24 bit NZ Do not alter the address in the instruction.
|
||
; Used with CMPRA to a ROM Resource.
|
||
; 32 bit NZ Mask out 12 high bits from the address
|
||
; in the instruction, leaving a ROM offset
|
||
; Then add in ROMBase.
|
||
; either Zero Normal Case. Add ROMBase to the address
|
||
; in the instruction.
|
||
;
|
||
MACRO
|
||
FixRomAddresses
|
||
ACTR 10000 ; Allow for lots 'o looping <3.0>
|
||
; First, find the correct symbol and seek tables for the machine.
|
||
LEA RomTableStart, A2 ; Beginning of tables in A2
|
||
MOVE.L A2, A1 ; Copy to A1
|
||
MOVE.W (A1)+, D0 ; Number of symbols in D0
|
||
ASL.W #2, D0 ; * 4 for number of bytes
|
||
MOVE.W (A1)+, D1 ; Number of versions in D1
|
||
MOVE.L ROMBase, A0 ; ROMBase in A0
|
||
MOVE.W 8(A0), D2 ; Rom version number in D2
|
||
BRA.S @VersDBRA
|
||
|
||
@NextVersion
|
||
CMP.W (A1)+, D2 ; Is this it?
|
||
BEQ.S @FoundVersion ; Yes!
|
||
ADDQ.L #2, A1 ; Nope, go to next one.
|
||
@VersDBRA DBRA D1, @NextVersion
|
||
|
||
; Damn! Didn't find it.
|
||
CLR.L ResumeProc ; disable the resume button.
|
||
MOVEQ.L #dsBadPatch, D0
|
||
_SysError
|
||
|
||
@FoundVersion
|
||
MOVE.L A0, D3 ; ROMBase in D3
|
||
MOVE.W (A1)+, D1 ; Get the symbol table offset
|
||
LEA (A2, D1.W), A1 ; Symbol table starts at A1
|
||
|
||
IF SEEK_ALLOWED THEN
|
||
LEA (A1, D0.W), A2 ; Seek table starts at A2
|
||
|
||
@NextSeek
|
||
; Use seek table to patch symbol table.
|
||
MOVE.L (A2)+, D2 ; Symbol ID in D2
|
||
ASL.L #2, D2 ; * 4 for number of bytes
|
||
BEQ.S @DoneSeek ; If 0, we're done.
|
||
MOVE.W (A2)+, D0 ; Get trap number
|
||
CMP.W #$A800, D0 ; OS or TB trap?
|
||
BLO.S @OSTrap ; < $A800 is OS - use GetTrapWordAddress when available
|
||
_GetTrapAddress newTool
|
||
BRA.S @GotTrap
|
||
@OSTrap _GetTrapAddress newOS
|
||
@GotTrap
|
||
CMP.L A0, D3 ; Make sure we're looking at ROM.
|
||
BLO.S @InROM ; If ROMBase < the address, we're cool.
|
||
_Debugger
|
||
|
||
@InROM
|
||
MOVE.W (A2)+, D4 ; Get # of longs in D4
|
||
BEQ.S @GotMatch ; If 0, we got it already!
|
||
|
||
@NextMatch
|
||
MOVE.L (A2), D0 ; Get first pattern
|
||
@FirstMatch
|
||
; Try to match the first 4 byte pattern. Assume that the pattern is word aligned,
|
||
; since SEEK is used for matching instruction sequences.
|
||
ADDQ.L #2, A0 ; Next word, please.
|
||
CMP.L (A0), D0 ; Does first pattern match?
|
||
BNE.S @FirstMatch ; Nope, look for next one.
|
||
|
||
MOVE.W D4, D0 ; Get # of longs
|
||
SUBQ.W #1, D0 ; Subtract 1 because we've found first pattern
|
||
LEA 4(A0), A3 ; A3 is ROM without first long
|
||
LEA 4(A2), A4 ; A4 is search pattern without first long
|
||
BRA.S @LongDBRA
|
||
|
||
@NextLong
|
||
CMP.L (A3)+, (A4)+ ; Same?
|
||
BNE.S @NextMatch ; No, gotta start fresh.
|
||
@LongDBRA DBRA D0, @NextLong ; Seen enough?
|
||
|
||
@GotMatch
|
||
SUB.L D3, A0 ; Turn A0 into a ROMBase offset
|
||
MOVE.L A0, -4(A1, D2.L) ; Put it in the symbol table
|
||
ASL.W #2, D4 ; Convert # of longs to bytes
|
||
LEA (A2, D4.W), A2 ; Go to next entry in seek table
|
||
BRA.S @NextSeek
|
||
|
||
@DoneSeek
|
||
ENDIF ;SEEK_ALLOWED
|
||
|
||
MOVEQ.L #-1, D0
|
||
_StripAddress ; D0 is negative with 32 bit memory manager.
|
||
LEA RefTableStart, A0 ; Reference table in A0
|
||
|
||
@NextReference
|
||
MOVE.L (A0)+, D2 ; Offset in D2
|
||
BEQ RomTableEnd ; All done.
|
||
LEA RomTableStart(PC, D2.L), A2 ; Address of code to patch in A2
|
||
MOVE.L (A2), D1 ; Symbol ID in D1
|
||
ASL.L #2, D1 ; * 4 for number of bytes
|
||
MOVE.L -4(A1, D1.L), D1 ; Offset from ROMBase in D1
|
||
CMP.L #$00FFFFFF, D1 ; High bits set?
|
||
BLS.S @AddIt ; No, normal case
|
||
TST.L D0 ; 32 bit mode?
|
||
BPL.S @CopyReference ; No, don't touch!
|
||
AND.L #$000FFFFF, D1 ; Yes, clear high 12 bits.
|
||
@AddIt
|
||
ADD.L D3, D1 ; Add in ROMBase
|
||
@CopyReference
|
||
MOVE.L D1, (A2) ; Put it in the code.
|
||
BRA.S @NextReference
|
||
|
||
; Build the table.
|
||
ALIGN 4
|
||
RomTableStart
|
||
GBLA &NumSymbols
|
||
NumSymbols DC.W &NumSymbols
|
||
GBLA &NumVersions
|
||
NumVersions DC.W &NumVersions
|
||
GBLC &RomVersion[*]
|
||
LCLA &i, &j, &k
|
||
VersTableStart
|
||
&i SETA 1
|
||
WHILE &i <= &NumVersions DO
|
||
DC.W &RomVersion[&i]
|
||
DC.W SymTableStart_&i - RomTableStart
|
||
&i SETA &i + 1
|
||
ENDWHILE
|
||
|
||
&i SETA 1
|
||
WHILE &i <= &NumVersions DO
|
||
SymTableStart_&i
|
||
&j SETA 1
|
||
WHILE &j <= &NumSymbols DO
|
||
DC.L RomSymTab_&RomVersion[&i]_&j
|
||
&j SETA &j + 1
|
||
ENDWHILE
|
||
|
||
IF SEEK_ALLOWED THEN
|
||
SeekTableStart_&i
|
||
&j SETA 1
|
||
WHILE &j <= &NumSymbols DO
|
||
PatchMacros$temp SET RomSymTab_&RomVersion[&i]_&j ; Stupid assembler!
|
||
IF PatchMacros$temp = 0 THEN
|
||
DC.L &j
|
||
DC.W Trap_&RomVersion[&i]_&j
|
||
DC.W NumPats_&RomVersion[&i]_&j
|
||
&k SETA 1
|
||
PatchMacros$temp SET NumPats_&RomVersion[&i]_&j ; Stupid assembler!
|
||
WHILE &k <= PatchMacros$temp DO
|
||
DC.L PatTab_&RomVersion[&i]_&j._&k
|
||
&k SETA &k + 1
|
||
ENDWHILE
|
||
ENDIF
|
||
&j SETA &j + 1
|
||
ENDWHILE
|
||
DC.L 0
|
||
ENDIF ;SEEK_ALLOWED
|
||
|
||
&i SETA &i + 1
|
||
ENDWHILE
|
||
|
||
RefTableStart
|
||
GBLA &NumReferences
|
||
&i SETA 1
|
||
WHILE &i <= &NumReferences DO
|
||
DC.L RomRefTab_&i - RomTableStart
|
||
&i SETA &i + 1
|
||
ENDWHILE
|
||
|
||
DC.L 0
|
||
RomTableEnd
|
||
ENDM
|
||
|
||
;_________________________________________________________________________________
|
||
; EntryTable, PtchInst
|
||
;
|
||
; These macros are used in conjunction with 'ptch' resources.
|
||
;_________________________________________________________________________________
|
||
|
||
;
|
||
; MACRO EntryTable &entryPt, &trapID
|
||
;
|
||
; Placed at the end of a 'ptch' file, this macro specifies a patch to be
|
||
; applied by the macro which reads and executed the 'ptch'.
|
||
;
|
||
Macro
|
||
EntryTable &entryPt, &trapID
|
||
If (&entryPt <> '0') Then
|
||
Import &entryPt
|
||
Import Start
|
||
DC.L &entryPt-Start
|
||
DC.W &trapID
|
||
Else
|
||
DC.L 0
|
||
EndIf
|
||
EndM
|
||
|
||
;
|
||
; MACRO PtchInst &selector
|
||
;
|
||
; Placed at the appropriate point, this macro calls the ptchInstall routine,
|
||
; which loads and executes the given 'ptch' resource.
|
||
;
|
||
Macro ; PMAB308 24Nov87 RWW
|
||
PtchInst &selector
|
||
MoveQ #&Eval(&selector),D0
|
||
Import ptchInstall
|
||
Bsr ptchInstall
|
||
EndM
|
||
|
||
;
|
||
; MACRO UsesPtchInst
|
||
;
|
||
; Placed in the cutback section of a patch, this macro includes the code
|
||
; required by the PtchInst macro.
|
||
;
|
||
Macro
|
||
UsesPtchInst
|
||
|
||
IF USE_MAKESYSFREE THEN
|
||
ptchInstall Proc Export
|
||
|
||
MinSysExtra EQU 16*1024 ; Add in Extra space for system <2.3-4april89-CEL>
|
||
|
||
SubQ #4,SP ; room for result <2.3-4april89-CEL>
|
||
Move.L #'ptch',-(SP) ; signature of loadable patch resource <2.3-4april89-CEL>
|
||
Move.W D0,-(SP) ; pass along given ID <2.3-4april89-CEL>
|
||
SF -(SP) ; just get a handle before sizing... <2.3-4april89-CEL>
|
||
_SetResLoad ; <2.3-4april89-CEL>
|
||
_GetResource ; get the resource handle <2.3-4april89-CEL>
|
||
MOVE.L (SP)+,D4 ; save the resource handle <2.3-4april89-CEL>
|
||
BEQ.S @xit ; we didn't get it, so just go on <2.3-4april89-CEL>
|
||
CLR.L -(SP) ; slot for resource size <2.3-4april89-CEL>
|
||
MOVE.L D4,-(SP) ; saved handle <2.3-4april89-CEL>
|
||
_SizeRsrc ; (SP) := patch res size <2.3-4april89-CEL>
|
||
MOVE.L (SP)+,D3 ; prime input to heap sizer <2.3-4april89-CEL>
|
||
ADD.L #MinSysExtra,D3 ; handy value <2.3-4april89-CEL>
|
||
BSR MakeSysFree ; get D3 contiguous bytes in sys heap <2.3-4april89-CEL>
|
||
ST -(SP) ; just get a handle before sizing... <2.3-4april89-CEL>
|
||
_SetResLoad ; <2.3-4april89-CEL>
|
||
MOVE.L D4,-(SP) ; saved handle <2.3-4april89-CEL>
|
||
_LoadResource ; get it in this time <2.3-4april89-CEL>
|
||
|
||
MOVE.L D4,-(SP) ; saved handle <2.3-4april89-CEL>
|
||
Move.L D4,-(SP) ; make a copy of the handle <2.3>
|
||
_DetachResource
|
||
; Uh, should we lock it down too, in case somebody goofs up?
|
||
|
||
Move.L (SP),A0 ; fetch handle (and keep copy on stack)
|
||
Move.L (A0),D0 ; point at head of resource <1.9>
|
||
_StripAddress ; strip it <1.9>
|
||
Move.L D0,-(SP) ; save it on the stack <2.3>
|
||
Move.L D0,A0 ; put it into A0 <1.9>
|
||
Jsr (A0) ; go execute init code
|
||
|
||
; on return, A0 should hold cutback address
|
||
Move.L A0,D1 ; save cutback address
|
||
Move.L A0,A1 ; and make an index copy
|
||
|
||
Move.L (SP)+,D2 ; get previously stripped pointer <2.3>
|
||
|
||
@3 Move.L (A1)+,D0 ; get address
|
||
Beq.S @1 ; if it's zero, we be done
|
||
|
||
Add.L D2,D0 ; add base to get real address
|
||
Move.L D0,A0 ; move to A0 for SetTrapAddress
|
||
Move.W (A1)+,D0 ; get trap number
|
||
|
||
Cmp.W #$A800,D0 ; OS or TB trap?
|
||
Bcs.S @2 ; < $A800 is OS
|
||
|
||
_SetTrapAddress newTool
|
||
Bra.S @3
|
||
|
||
@2 _SetTrapAddress newOS
|
||
Bra.S @3
|
||
|
||
@1 Move.L D1,D0 ; cutback address
|
||
Sub.L D2,D0 ; cutback - resource start = new size
|
||
Move.L (SP)+,A0 ; fetch handle, for the last time
|
||
_SetHandleSize
|
||
|
||
Bra.S @done ; <PMAB335>
|
||
|
||
@xit clr.l ResumeProc ; disable the resume button. <PMAB442>
|
||
MoveQ.L #dsBadPatch, D0 ; <PMAB335>
|
||
_SysError ; <PMAB335>
|
||
|
||
@done Rts ; <PMAB335>
|
||
|
||
*********************************************************************** ;<2.3-4april89-CEL>
|
||
* PROCEDURE MakeSysFree
|
||
* Guarantee a contiguous block of free space in the sys heap.
|
||
* To avoid purging everything, we use a loop based on _CompactMem.
|
||
* Growing the zone depends on whether there is a distinct appl zone,
|
||
* as is the case with Plus and SE and in which case we use the
|
||
* traditional _SetApplBase, or whether sys and appl are coincident,
|
||
* as is the case with II and in which case we just allocate a handle
|
||
* and immediately release it. In the worst case, the loop should be
|
||
* executed twice. At this early stage, we don’t init the appl zone,
|
||
* just set its base. The initializations of InitApplZone are for later.
|
||
* Input:
|
||
* d3 = requested space
|
||
* Trashes:
|
||
* d0-d2/a0-a1
|
||
*********************************************************************** ;<2.3-4april89-CEL>
|
||
MakeSysFree ; new routine
|
||
move.l d3,d0 ; amount needed
|
||
_CompactMem SYS ; look for it in sys zone
|
||
sub.l d3,d0 ; gotten - needed
|
||
bge.s @loopEnd ; gotten ≥ needed --> done
|
||
|
||
andi.w #$FFFE, d0 ; make sure it’s even for MacPlus ;<3><4> csd
|
||
|
||
movea.l SysZone,a0 ; start of sys zone
|
||
cmpa.l ApplZone,a0 ; are they coincident?
|
||
beq.s @loopEnd ; same go to the end
|
||
|
||
movea.l SysZone,a0 ; start of sys zone
|
||
movea.l bkLim(a0),a0 ; end of sys zone
|
||
suba.l d0,a0 ; end of sys zone + (needed-gotten)
|
||
adda.w #128,a0 ; with slop
|
||
_SetApplBase ; free up more sys space
|
||
_InitApplZone ; init the Toolbox world, too
|
||
bra.s MakeSysFree ; be sure we have enough
|
||
@loopEnd
|
||
rts ; end of MakeSysFree
|
||
|
||
EndProc
|
||
|
||
ELSE ; USE_MAKESYSFREE
|
||
|
||
; ***************************************************************************
|
||
; ptchInstall - subroutine
|
||
; <PMAB308/RWW112187>
|
||
; Loads 'ptch' resource, initializes it, and installs its traps.
|
||
; 'ptch' resource is assumed to have SysHeap and Locked bits set!
|
||
;
|
||
; Entry: D0.W = rsrc ID of 'ptch' resource to load
|
||
;
|
||
; Uses: D0-D2, A0-A1
|
||
;
|
||
; ***************************************************************************
|
||
|
||
ptchInstall Proc Export
|
||
|
||
SubQ #4,SP ; room for result
|
||
Move.L #'ptch',-(SP) ; signature of loadable patch resource
|
||
Move.W D0,-(SP) ; pass along given ID
|
||
_GetResource ; just in case we want to ROM it later on
|
||
Tst.L (SP) ; handle returned?
|
||
Bnz.S @4 ; Yep, okay to continue
|
||
AddQ #4,SP ; Yank garbage off the stack
|
||
Bra.S @xit ; and depart
|
||
@4
|
||
Move.L (SP),-(SP) ; make a copy of the handle
|
||
_DetachResource
|
||
; Uh, should we lock it down too, in case somebody goofs up?
|
||
|
||
Move.L (SP),A0 ; fetch handle (and keep copy on stack)
|
||
Move.L (A0),D0 ; point at head of resource <1.9>
|
||
_StripAddress ; strip it <1.9>
|
||
Move.L D0,-(SP) ; save it on the stack <2.3>
|
||
Move.L D0,A0 ; put it into A0 <1.9>
|
||
Jsr (A0) ; go execute init code
|
||
|
||
; on return, A0 should hold cutback address
|
||
Move.L A0,D1 ; save cutback address
|
||
Move.L A0,A1 ; and make an index copy
|
||
|
||
Move.L (SP)+,D2 ; get previously stripped pointer <2.3>
|
||
|
||
@3 Move.L (A1)+,D0 ; get offset
|
||
Beq.S @1 ; if it's zero, we be done
|
||
|
||
Add.L D2,D0 ; add base to get real address
|
||
Move.L D0,A0 ; move to A0 for SetTrapAddress
|
||
Move.W (A1)+,D0 ; get trap number
|
||
|
||
Cmp.W #$A800,D0 ; OS or TB trap?
|
||
Bcs.S @2 ; < $A800 is OS
|
||
|
||
_SetTrapAddress NewTool
|
||
Bra.S @3
|
||
|
||
@2 _SetTrapAddress NewOS
|
||
Bra.S @3
|
||
|
||
@1 Move.L D1,D0 ; cutback address
|
||
Sub.L D2,D0 ; cutback - resource start = new size
|
||
Move.L (SP)+,A0 ; fetch handle, for the last time
|
||
_SetHandleSize
|
||
Bra.S @done
|
||
|
||
@xit clr.l ResumeProc ; disable the resume button.
|
||
MoveQ.L #dsBadPatch, D0
|
||
_SysError
|
||
|
||
@done Rts
|
||
|
||
EndProc
|
||
|
||
ENDIF ; USE_MAKESYSFREE
|
||
|
||
EndM
|
||
|
||
;
|
||
; MACRO protectedJmp &label,&toWhere
|
||
;
|
||
; This puts the special protection header on for the patch protector.
|
||
; See PatchProtector.a for details.
|
||
;
|
||
|
||
macro
|
||
protectedJmp &label,&toWhere
|
||
bra.s @skip
|
||
@jmp:
|
||
jmp (&toWhere).L
|
||
@skip:
|
||
&label: equ @jmp
|
||
endm
|
||
|
||
;
|
||
; MACRO protectedJmpBind &label,&toWhere
|
||
;
|
||
; This puts the special protection header on for the patch protector.
|
||
; See PatchProtector.a for details.
|
||
;
|
||
|
||
macro
|
||
protectedJmpBind &label,&toWhere
|
||
bra.s @skip
|
||
@jmp:
|
||
jmpBind &toWhere
|
||
@skip:
|
||
&label: equ @jmp
|
||
endm
|
||
|
||
;
|
||
; MACRO protectedJmpROM &label,&toWhere
|
||
;
|
||
; This puts the special protection header on for the patch protector.
|
||
; See PatchProtector.a for details.
|
||
;
|
||
|
||
macro
|
||
protectedJmpROM &label,&toWhere
|
||
bra.s @skip
|
||
@jmp:
|
||
jmpROM &toWhere
|
||
@skip:
|
||
&label: equ @jmp
|
||
endm
|
||
|
||
ENDIF ; ...already included |