ASM modes and directives¶
A skool file may contain directives that are processed during the parsing phase. Exactly how a directive is processed (and whether it is executed) depends on the ‘substitution mode’ and ‘bugfix mode’ in which the skool file is being parsed.
Substitution modes¶
There are three substitution modes: @isub, @ssub, and @rsub. These
modes are described in the following subsections.
@isub mode¶
In @isub mode, @isub directives are executed, but @ssub, and
@rsub directives are not. The main purpose of @isub mode is to make the
minimum number of instruction substitutions necessary to produce an ASM file
that assembles.
For example:
@isub=LD A,(32512)
25396 LD A,(m)
This @isub directive ensures that LD A,(m) is replaced by the valid
instruction LD A,(32512) when rendering in ASM mode.
@isub mode is invoked by default when running
skool2asm.py.
@ssub mode¶
In @ssub mode, @isub and @ssub directives are executed, but
@rsub directives are not. The main purpose of @ssub mode is to replace
LSBs, MSBs and full addresses in the operands of instructions with labels, to
make the code amenable to some degree of relocation, but without actually
removing or inserting any code.
For example:
@ssub=LD (27015+1),A
*27012 LD (27016),A ; Change the instruction below from SET 0,B to RES 0,B
; or vice versa
27015 SET 0,B
This @ssub directive replaces LD (27016),A with LD (27015+1),A; the
27015 will be replaced by the label for that address before rendering.
(27016 cannot be replaced by a label, since it is not the address of an
instruction.)
@ssub mode is invoked by passing the -s option to
skool2asm.py.
@rsub mode¶
In @rsub mode, @isub, @ssub and @rsub directives are executed.
The main purpose of @rsub mode is to make code unconditionally relocatable,
even if that requires the removal of existing code or the insertion of new
code.
For example:
23997 LD HL,32766
@ssub=LD (HL),24002%256
24000 LD (HL),194
@rsub+begin
INC L
LD (HL),24002/256
@rsub+end
24002 XOR A
This @rsub block directive inserts two instructions that ensure that the
address stored at 32766 will have the correct MSB as well as the correct LSB,
regardless of where the code originally at 24002 now lives.
@rsub mode is invoked by passing the -r option to
skool2asm.py. @rsub mode also implies
@ofix mode.
Bugfix modes¶
There are three bugfix modes: @ofix, @bfix and @rfix. These
modes are described in the following subsections.
@ofix mode¶
In @ofix mode, @ofix directives are executed, but @bfix and
@rfix directives are not. The main purpose of @ofix mode is to fix
instructions that have faulty operands.
For example:
@ofix-begin
27872 CALL 27633 ; This should be CALL 27634
@ofix+else
CALL 27634
@ofix+end
These @ofix block directives fix the faulty operand of the CALL
instruction.
@ofix mode is invoked by passing the -f 1 option to
skool2asm.py.
@bfix mode¶
In @bfix mode, @ofix and @bfix directives are executed, but
@rfix directives are not. The main purpose of @bfix mode is to fix bugs
by replacing instructions, but without changing the start address of any
routines, routine entry points, or data blocks.
For example:
@bfix-begin
32205 JR Z,32232 ; This should be JR NZ,32232
@bfix+else
JR NZ,32232 ;
@bfix+end
@bfix mode is invoked by passing the -f 2 option to
skool2asm.py.
@rfix mode¶
In @rfix mode, @ofix, @bfix and @rfix directives are executed.
The purpose of @rfix mode is to fix bugs that cannot be fixed without
moving code around (to make space for the fix).
For example:
28432 DEC HL
@rfix+begin
LD A,H
OR L
@rfix+end
28433 JP Z,29712
These @rfix block directives insert some instructions to fix the faulty
check on whether HL holds 0.
@rfix mode is invoked by passing the -f 3 option to
skool2asm.py. @rfix mode implies @rsub mode.
ASM directives¶
The ASM directives recognised by SkoolKit are described in the following subsections.
@assemble¶
The @assemble directive controls whether assembly language instructions,
DEFB, DEFM, DEFS and DEFW statements, and @defb,
@defs and @defw directives are converted into byte values for the
purpose of populating the memory snapshot.
@assemble=H,A
H is an integer value that determines what is converted in HTML mode, and
A is an integer value that determines what is converted in ASM mode:
-1- do not convert anything (this is the default in ASM mode)0- convertDEFB,DEFM,DEFSandDEFWstatements and@defb,@defsand@defwdirectives only (this is the default in HTML mode)1- convert assembly language instructions as well
If H or A is blank or omitted, its value is left unchanged.
For example:
; The eight bytes of code in this routine are also used as UDG data.
; .
; #HTML(#UDG44919)
@assemble=1
c44919 LD DE,46572 ;
44922 CP 200 ;
44924 JP 45429 ;
@assemble=0
The @assemble=1 directive is required to define the bytes for addresses
44919-44926. If it were not present, the memory snapshot would contain zeroes
at those addresses, and the image created by the #UDG macro would be blank.
| Version | Changes |
|---|---|
| 6.3 | Added support for specifying what’s converted in HTML mode and ASM mode separately, and for switching off conversion entirely |
| 6.1 | Added the ability to assemble instructions whose operands contain arithmetic expressions |
| 5.0 | New |
@bfix¶
The @bfix directive makes an instruction and comment substitution in
@bfix mode.
@bfix=INSTRUCTION[ ; comment]
INSTRUCTIONis the replacement instructioncommentis the replacement comment; if not given, the existing comment is left unchanged
For example:
@bfix=AND B ; Apply the mask
29713 AND C ; This should be 'AND B'
This @bfix directive replaces AND C with AND B, and also replaces
the comment.
| Version | Changes |
|---|---|
| 6.4 | Added support for replacing the comment |
@bfix block directives¶
The @bfix block directives define a block of lines that will be inserted or
removed in @bfix mode.
The syntax for defining a block that will be inserted in @bfix mode (but
left out otherwise) is:
@bfix+begin
... ; Lines to be inserted
@bfix+end
The syntax for defining a block that will be removed in @bfix mode (but
left in otherwise) is:
@bfix-begin
... ; Lines to be removed
@bfix-end
Typically, though, it is desirable to define a block that will be removed in
@bfix mode right next to the block that will be inserted in its place. That
may be done thus:
@bfix-begin
... ; Instructions to be removed
@bfix+else
... ; Instructions to be inserted
@bfix+end
which is equivalent to:
@bfix-begin
... ; Instructions to be removed
@bfix-end
@bfix+begin
... ; Instructions to be inserted
@bfix+end
For example:
@bfix-begin
32205 JR Z,32232 ; This should be JR NZ,32232
@bfix+else
JR NZ,32232 ;
@bfix+end
@defb¶
The @defb directive inserts byte values into the memory snapshot at a given
address.
@defb=address:value1[,value2...]
addressis the addressvalue1,value2etc. are the byte values (as might appear in aDEFBstatement)
The sequence of comma-separated values may be followed by a semicolon (;)
and arbitrary text, which will be ignored.
For example:
@defb=30000:5,"Hello" ; Welcome message
This will insert the value 5 followed by the ASCII codes of the characters in “Hello” into the memory snapshot at address 30000.
@defb directives are processed not only when skool2asm.py or
skool2html.py is run, but also when sna2skool.py is run on a
control file or skool file template; thus the @defb directive can be used
to override the contents of the snapshot that is read by sna2skool.py.
| Version | Changes |
|---|---|
| 6.3 | New |
@defs¶
The @defs directive inserts a sequence of byte values into the memory
snapshot at a given address.
@defs=address:length[,value]
addressis the addresslengthis the length of the sequencevalueis the byte value (default: 0)
The directive may be followed by a semicolon (;) and arbitrary text, which
will be ignored.
For example:
@defs=30000:5,$FF ; Five 255s
This will insert the value 255 into the memory snapshot at addresses 30000-30004.
@defs directives are processed not only when skool2asm.py or
skool2html.py is run, but also when sna2skool.py is run on a
control file or skool file template; thus the @defs directive can be used
to override the contents of the snapshot that is read by sna2skool.py.
| Version | Changes |
|---|---|
| 6.3 | New |
@defw¶
The @defw directive inserts word values into the memory snapshot at a given
address.
@defw=address:value1[,value2...]
addressis the addressvalue1,value2etc. are the word values (as might appear in aDEFWstatement)
The sequence of comma-separated values may be followed by a semicolon (;)
and arbitrary text, which will be ignored.
For example:
@defw=30000:32768,32775 ; Message addresses
This will insert the word values 32768 and 32775 into the memory snapshot at addresses 30000 and 30002.
@defw directives are processed not only when skool2asm.py or
skool2html.py is run, but also when sna2skool.py is run on a
control file or skool file template; thus the @defw directive can be used
to override the contents of the snapshot that is read by sna2skool.py.
| Version | Changes |
|---|---|
| 6.3 | New |
@end¶
The @end directive may be used to indicate where to stop parsing the skool
file for the purpose of generating ASM output. Everything after the @end
directive is ignored by skool2asm.py.
See also @start.
| Version | Changes |
|---|---|
| 2.2.2 | New |
@equ¶
The @equ directive defines an EQU directive that will appear in the ASM
output.
@equ=label=value
labelis the labelvalueis the value assigned to the label
For example:
@equ=ATTRS=22528
c32768 LD HL,22528
This will produce an EQU directive (ATTRS EQU 22528) in the ASM output, and
replace the operand of the instruction at 32768 with a label: LD HL,ATTRS.
| Version | Changes |
|---|---|
| 5.4 | New |
@if¶
The @if directive conditionally processes other ASM directives based on the
value of an arithmetic expression.
@if(expr)(true[,false])
expris the arithmetic expressiontrueis processed whenexpris truefalse(if given) is processed whenexpris false
See Numeric parameters for details on the operators and replacement
fields that may be used in the expr parameter.
For example:
@if({case}==1))(replace=/#hl/hl,replace=/#hl/HL)
would process replace=/#hl/hl if in lower case mode, or replace=/#hl/HL
otherwise.
The true and false parameters may be supplied in the same way as they
are for the #IF macro. See String parameters for more details.
| Version | Changes |
|---|---|
| 6.4 | New |
@ignoreua¶
The @ignoreua directive suppresses any warnings that would otherwise be
reported concerning addresses not converted to labels in the comment that
follows; the comment may be an entry title, an entry description, a register
description section, a block start comment, a mid-block comment, a block end
comment, or an instruction-level comment.
To apply the directive to an entry title:
@ignoreua
; Prepare data at 32768
c32768 LD A,(HL)
If the @ignoreua directive were not present, a warning would be printed
(during the rendering phase) about the entry title containing an address
(32768) that has not been converted to a label.
To apply the directive to an entry description:
; Prepare data in page 128
;
@ignoreua
; This routine operates on the data at 32768.
c49152 LD A,(HL)
If the @ignoreua directive were not present, a warning would be printed
(during the rendering phase) about the entry description containing an address
(32768) that has not been converted to a label.
To apply the directive to a register description section:
; Prepare data in page 128
;
; This routine operates on the data in page 128.
;
@ignoreua
; HL 32768
c49152 LD A,(HL)
If the @ignoreua directive were not present, a warning would be printed
(during the rendering phase) about the register description containing an
address (32768) that has not been converted to a label.
To apply the directive to a block start comment:
; Prepare data in page 128
;
; This routine operates on the data in page 128.
;
; HL 128*256
;
@ignoreua
; First pick up the byte at 32768.
c49152 LD A,(HL)
If the @ignoreua directive were not present, a warning would be printed
(during the rendering phase) about the start comment containing an address
(32768) that has not been converted to a label.
To apply the directive to a mid-block comment:
28913 LD L,A
@ignoreua
; #REGhl now holds either 32522 or 32600.
28914 LD B,(HL)
If the @ignoreua directive were not present, warnings would be printed
(during the rendering phase) about the comment containing addresses (32522,
32600) that have not been converted to labels.
To apply the directive to a block end comment:
44159 JP 63152
@ignoreua
; This routine continues at 63152.
If the @ignoreua directive were not present, warnings would be printed
(during the rendering phase) about the comment containing an address (63152)
that has not been converted to a label.
To apply the directive to an instruction-level comment:
@ignoreua
60159 LD C,A ; #REGbc now holds 62818
If the @ignoreua directive were not present, a warning would be printed
(during the rendering phase) about the comment containing an address (62818)
that has not been converted to a label.
| Version | Changes |
|---|---|
| 4.2 | Added support for register description sections |
| 2.4.1 | Added support for entry titles, entry descriptions, mid-block comments and block end comments |
@isub¶
The @isub directive makes an instruction and comment substitution in
@isub mode.
@isub=INSTRUCTION[ ; comment]
INSTRUCTIONis the replacement instructioncommentis the replacement comment; if not given, the existing comment is left unchanged
For example:
@isub=LD A,(32512)
25396 LD A,(m)
This @isub directive ensures that LD A,(m) is replaced by the valid
instruction LD A,(32512) when rendering in ASM mode.
| Version | Changes |
|---|---|
| 6.4 | Added support for replacing the comment |
@isub block directives¶
The @isub block directives define a block of lines that will be inserted or
removed in @isub mode.
The syntax is equivalent to that for the @bfix block directives.
@keep¶
The @keep directive prevents the substitution of labels for numeric values
in the operand of the next instruction:
@keep[=val1[,val2...]]
val1,val2etc. are the values to keep; if none are specified, all values are kept
In HTML mode, the @keep directive also prevents the operand from being
hyperlinked.
For example:
@keep
28328 LD BC,24576 ; #REGb=96, #REGc=0
If the @keep directive were not present, the operand (24576) of the
LD BC instruction would be replaced with the label of the routine at 24576
(if there is a routine at that address); however, the operand is meant to be a
pure data value, not a variable or routine address.
| Version | Changes |
|---|---|
| 6.2 | Added the ability to specify the values to keep; the @keep
directive is applied to instructions that have been replaced by
an @isub, @ssub or @rsub directive |
@label¶
The @label directive sets the label for the next instruction.
@label=LABEL
LABELis the label to apply
For example:
@label=ENDGAME
c24576 XOR A
This sets the label for the routine at 24576 to ENDGAME.
If LABEL is blank (@label=), the next instruction is prevented from
having a label automatically generated.
| Version | Changes |
|---|---|
| 6.3 | LABEL may be blank (to prevent the next instruction from
having a label automatically generated) |
@nolabel¶
The @nolabel directive prevents the next instruction from having a label
automatically generated.
For example:
@label=TOGGLE
c48998 LD HL,32769
@bfix+begin
@label=LOOP
@bfix+end
49001 LD A,(HL)
@bfix+begin
@nolabel
@bfix+end
*49002 XOR L
49003 LD (HL),A
49004 INC L
@bfix-begin
49005 JR NZ,49002
@bfix+else
49005 JR NZ,49001
@bfix+end
The @nolabel directive here prevents the instruction at 49002 from being
labelled in @bfix mode (because no label is required; instead, the
previous instruction at 49001 will be labelled).
The output in @bfix mode will be:
TOGGLE:
LD HL,32769
LOOP:
LD A,(HL)
XOR L
LD (HL),A
INC L
JR NZ,LOOP
And the output when not in @bfix mode will be:
TOGGLE:
LD HL,32769
LD A,(HL)
TOGGLE_0:
XOR L
LD (HL),A
INC L
JR NZ,TOGGLE_0
Note
The @nolabel directive is deprecated since version 6.3. Use a blank
@label directive (@label=) instead.
| Version | Changes |
|---|---|
| 6.2 | The @nolabel directive is processed in HTML mode |
@nowarn¶
The @nowarn directive suppresses any warnings that would otherwise be
reported for the next instruction concerning:
- a
LDoperand being replaced with a routine label (if the instruction has not been replaced using@isubor@ssub) - an operand not being replaced with a label (because the operand address has no label)
For example:
@nowarn
25560 LD BC,25404 ; Point #REGbc at the routine at #R25404
If this @nowarn directive were not present, a warning would be printed
(during the parsing phase) about the operand (25404) being replaced with a
routine label (which would be inappropriate if 25404 were intended to be a pure
data value).
For another example:
@ofix-begin
@nowarn
27872 CALL 27633 ; This should be CALL #R27634
@ofix+else
CALL 27634 ;
@ofix+end
If this @nowarn directive were not present, a warning would be printed
(during the parsing phase, if not in @ofix mode) about the operand (27633)
not being replaced with a label (usually you would want the operand of a CALL
instruction to be replaced with a label, but not in this case).
@ofix¶
The @ofix directive makes an instruction and comment substitution in
@ofix mode.
@ofix=INSTRUCTION[ ; comment]
INSTRUCTIONis the replacement instruction (with a corrected operand)commentis the replacement comment; if not given, the existing comment is left unchanged
For example:
@ofix=JR NZ,26067
25989 JR NZ,26068
This @ofix directive replaces the operand of the JR NZ instruction with
26067.
| Version | Changes |
|---|---|
| 6.4 | Added support for replacing the comment |
@ofix block directives¶
The @ofix block directives define a block of lines that will be inserted or
removed in @ofix mode.
The syntax is equivalent to that for the @bfix block directives.
@org¶
The @org directive inserts an ORG assembler directive.
@org[=address]
addressis theORGaddress; if not specified, it defaults to the address of the next instruction
Note that the @org directive works only on the first instruction in an
entry.
| Version | Changes |
|---|---|
| 6.3 | The address parameter is optional |
@rem¶
The @rem directive may be used to make an illuminating comment about a
nearby section or other ASM directive in a skool file. The directive is ignored
by the parser.
@rem=COMMENT
COMMENTis a suitably illuminating comment
For example:
@rem=The next section of data MUST start at 64000
@org=64000
| Version | Changes |
|---|---|
| 2.4 | The = is required |
@remote¶
The @remote directive creates a remote entry in a skool file. A remote
entry enables JR, JP and CALL instructions to be hyperlinked to an
entry defined in another skool file.
@remote=code:address[,address2...]
codeis the ID of the disassembly defined in the other skool fileaddressis the address of the remote entryaddress2etc. are addresses of other entry points in the remote entry
For example:
@remote=main:29012,29015
This directive, if it appeared in a secondary skool file, would enable references to the routine at 29012 and its entry point at 29015 in the main disassembly. It would also enable the #R macro to create a hyperlink to a remote entry point using the form:
#R29015@main
| Version | Changes |
|---|---|
| 6.3 | New |
@replace¶
The @replace directive replaces strings that match a regular expression in
skool file annotations and ref file section names and contents.
@replace=/pattern/repl
or:
@replace=/pattern/repl/
patternis the regular expressionreplis the replacement string
(If the second form is used, any text appearing after the terminating / is
ignored.)
For example:
@replace=/#copy/#CHR(169)
This @replace directive replaces all instances of #copy with
#CHR(169).
If / appears anywhere in pattern or repl, then an alternative
separator should be used; for example:
@replace=|n/a|not applicable
As a convenience for dealing with decimal and hexadecimal numbers, wherever
\i appears in pattern, it is replaced by a regular expression group
that matches a decimal number or a hexadecimal number preceded by $. For
example:
@replace=/#udg\i,\i/#UDG(\1,#PEEK\2)
This @replace directive would replace #udg$a001,40960 with
#UDG($a001,#PEEK40960).
Note that string replacements specified by @replace directives are made
before skool macros are expanded, and in the order in which the directives
appear in the skool file. For example, if we have:
@replace=/#foo\i/#bar\1
@replace=/#bar\i/#EVAL\1,16
then #foo31 would be replaced by #EVAL31,16, but if these directives
were reversed:
@replace=/#bar\i/#EVAL\1,16
@replace=/#foo\i/#bar\1
then #foo31 would be replaced by #bar31.
See also Defining macros with @replace.
| Version | Changes |
|---|---|
| 6.0 | Replaces strings in ref file section names |
| 5.1 | New |
@rfix¶
The @rfix directive makes an instruction and comment substitution in
@rfix mode.
@rfix=INSTRUCTION[ ; comment]
INSTRUCTIONis the replacement instructioncommentis the replacement comment; if not given, the existing comment is left unchanged
For example:
@rfix=LD HL,0 ; Clear #REGhl
27519 LD L,0 ; Clear #REGl
This @rfix directive replaces the instruction at 27519 with LD HL,0 and
also replaces the comment.
| Version | Changes |
|---|---|
| 6.4 | Added support for replacing the comment |
| 5.2 | New |
@rfix block directives¶
The @rfix block directives define a block of lines that will be inserted or
removed in @rfix mode.
The syntax is equivalent to that for the @bfix block directives.
@rsub¶
The @rsub directive makes an instruction and comment substitution in
@rsub mode.
@rsub=INSTRUCTION[ ; comment]
INSTRUCTIONis the replacement instructioncommentis the replacement comment; if not given, the existing comment is left unchanged
For example:
@rsub=LD BC,0
30143 LD C,0 ; Reset #REGbc to 0
This @rsub directive replaces the instruction at 30143 with LD BC,0 and
leaves the comment unchanged.
| Version | Changes |
|---|---|
| 6.4 | Added support for replacing the comment |
@rsub block directives¶
The @rsub block directives define a block of lines that will be inserted or
removed in @rsub mode.
The syntax is equivalent to that for the @bfix block directives.
@set¶
The @set directive sets a property on the ASM writer.
@set-name=value
nameis the property namevalueis the property value
@set directives must be placed somewhere after the @start directive,
and before the @end directive (if there is one).
Recognised property names and their default values are:
bullet- the bullet character(s) to use for list items specified in a #LIST macro (default:*)comment-width-min- the minimum width of the instruction comment field (default:10)crlf-1to use CR+LF to terminate lines, or0to use the system default (default:0)handle-unsupported-macros- how to handle an unsupported macro:1to expand it to an empty string, or0to exit with an error (default:0)indent- the number of spaces by which to indent instructions (default:2)instruction-width- the width of the instruction field (default:23)label-colons-1to append a colon to labels, or0to leave labels unadorned (default:1)line-width- the maximum width of each line (default:79)tab-1to use a tab character to indent instructions, or0to use spaces (default:0)warnings-1to print any warnings that are produced while writing ASM output (after parsing the skool file), or0to suppress them (default:1)wrap-column-width-min- the minimum width of a wrappable table column (default:10)
For example:
@set-bullet=+
This @set directive sets the bullet character to ‘+’.
| Version | Changes |
|---|---|
| 3.4 | Added the handle-unsupported-macros and
wrap-column-width-min properties |
| 3.3.1 | Added the comment-width-min, indent,
instruction-width, label-colons, line-width and
warnings properties |
| 3.2 | New |
@ssub¶
The @ssub directive makes an instruction and comment substitution in
@ssub mode.
@ssub=INSTRUCTION[ ; comment]
INSTRUCTIONis the replacement instructioncommentis the replacement comment; if not given, the existing comment is left unchanged
For example:
@ssub=LD (27015+1),A
*27012 LD (27016),A ; Change the instruction below from SET 0,B to RES 0,B
; or vice versa
27015 SET 0,B
This @ssub directive replaces LD (27016),A with LD (27015+1),A; the
27015 will be replaced by the label for that address before rendering.
(27016 cannot be replaced by a label, since it is not the address of an
instruction.)
| Version | Changes |
|---|---|
| 6.4 | Added support for replacing the comment |
@ssub block directives¶
The @ssub block directives define a block of lines that will be inserted or
removed in @ssub mode.
The syntax is equivalent to that for the @bfix block directives.
| Version | Changes |
|---|---|
| 4.4 | New |
@start¶
The @start directive indicates where to start parsing the skool file for
the purpose of generating ASM output. Everything before the @start
directive is ignored by skool2asm.py.
See also @end.
@writer¶
The @writer directive specifies the name of the Python class to use to
generate ASM output. It must be placed somewhere after the @start
directive, and before the @end directive (if there is one).
@writer=package.module.classname
or:
@writer=/path/to/moduledir:module.classname
The second of these forms may be used to specify a class in a module that is outside the module search path (e.g. a standalone module that is not part of an installed package).
The default ASM writer class is skoolkit.skoolasm.AsmWriter. For information on how to create your own Python class for generating ASM output, see the documentation on extending SkoolKit.
| Version | Changes |
|---|---|
| 3.3.1 | Added support for specifying a module outside the module search path |
| 3.1 | New |