.. _skoolMacros: Skool macros ============ Skool files and ref files may contain skool macros that are 'expanded' to an appropriate piece of HTML markup (when rendering in HTML mode), or to an appropriate piece of plain text (when rendering in ASM mode). Syntax ^^^^^^ Skool macros have the following general form:: #MACROrparam1,rparam2,...[,oparam1,oparam2,...] where: * ``MACRO`` is the macro name * ``rparam1``, ``rparam2`` etc. are required parameters * ``oparam1``, ``oparam2`` etc. are optional parameters If an optional parameter is left blank or omitted entirely, it assumes its default value. So, for example:: #UDG39144 is equivalent to:: #UDG39144,56,4,1,0,0,0,1 and:: #UDG30115,,2 is equivalent to:: #UDG30115,56,2 .. _numericParameters: Numeric parameters ^^^^^^^^^^^^^^^^^^ Numeric parameters may be written in decimal notation:: #UDG51673,17 or in hexadecimal notation (prefixed by ``$``):: #UDG$C9D9,$11 Wherever a sequence of numeric parameters appears in a macro, that sequence may optionally be enclosed in parentheses: ``(`` and ``)``. Parentheses are `required` if any numeric parameter is written as an expression containing arithmetic operations or skool macros:: #UDG(51672+1,#PEEK51672) The following operators are permitted in an arithmetic expression: * arithmetic operators: ``+``, ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (power) * bitwise operators: ``&`` (AND), ``|`` (OR), ``^`` (XOR) * bit shift operators: ``>>``, ``<<`` * Boolean operators: ``&&`` (and), ``||`` (or) * comparison operators: ``==``, ``!=``, ``>``, ``<``, ``>=``, ``<=`` Parentheses and spaces are also permitted in an arithmetic expression:: #IF(1 == 2 || (1 <= 2 && 2 < 3))(Yes,No) The parameter strings of the :ref:`asm-if` directive, the :ref:`EVAL` macro, the :ref:`IF` macro, and the :ref:`MAP` macro also recognise some replacement fields: * ``asm`` - 1 if in :ref:`isubMode`, 2 if in :ref:`ssubMode`, 3 if in :ref:`rsubMode`, or 0 otherwise * ``base`` - 10 if the ``--decimal`` option is used with :ref:`skool2asm.py` or :ref:`skool2html.py`, 16 if the ``--hex`` option is used, or 0 if neither option is used * ``case`` - 1 if the ``--lower`` option is used with :ref:`skool2asm.py` or :ref:`skool2html.py`, 2 if the ``--upper`` option is used, or 0 if neither option is used * ``fix`` - 1 if in :ref:`ofixMode`, 2 if in :ref:`bfixMode`, 3 if in :ref:`rfixMode`, or 0 otherwise * ``html`` - 1 if in HTML mode, 0 otherwise * ``vars`` - a dictionary of variables defined by the ``--var`` option of :ref:`skool2asm.py` or :ref:`skool2html.py`; accessing an undefined variable in this dictionary yields the value '0' For example:: #IF({case}==1)(hl,HL) expands to ``hl`` if in lower case mode, or ``HL`` otherwise. Note that if a replacement field is used, the numeric parameter must be enclosed in parentheses. .. versionchanged:: 6.4 The ``asm`` replacement field indicates the exact ASM mode; added the ``fix`` and ``vars`` replacement fields. .. _stringParameters: String parameters ^^^^^^^^^^^^^^^^^ Where a macro requires a single string parameter consisting of arbitrary text, it must be enclosed in parentheses, square brackets or braces:: (text) [text] {text} If ``text`` contains unbalanced brackets, a non-whitespace character that is not present in ``text`` may be used as an alternative delimiter. For example:: /text/ |text| Where a macro requires multiple string parameters consisting of arbitrary text, they must be enclosed in parentheses, square brackets or braces and be separated by commas:: (string1,string2) [string1,string2] {string1,string2} When a comma-separated sequence of string parameters is split, any commas that appear between parentheses are retained. For example, the string parameters of the outer ``#FOR`` macro in:: #FOR0,1(n,#FOR(0,1)(m,(n,m),;),;) are split into ``n``, ``#FOR(0,1)(m,(n,m),;)`` and ``;``, and the string parameters of the inner ``#FOR`` macro are split into ``m``, ``(n,m)``, and ``;``. Alternatively, an arbitrary delimiter - ``d``, which cannot be whitespace - and separator - ``s``, which can be whitespace - may be used. (They can be the same character.) The string parameters must open with ``ds``, be separated by ``s``, and close with ``sd``. For example:: //same/delimiter/and/separator// | different delimiter and separator | Note that if an alternative delimiter or separator is used, it must not be '&', '<' or '>'. .. versionchanged:: 6.4 When a comma-separated sequence of string parameters is split, any commas that appear between parentheses are retained. SMPL macros ^^^^^^^^^^^ The macros described in this section constitute the Skool Macro Programming Language (SMPL). They can be used to programmatically specify values in the parameter string of any macro. .. _hash: #() --- The ``#()`` macro expands the skool macros in its sole string parameter. :: #(text) It takes effect only when it immediately follows the opening token of another skool macro, and is expanded `before` that macro. For example:: #UDGARRAY#(2#FOR37159,37168,9||n|;(n+1),#PEEKn||)(item) This instance of the ``#()`` macro expands the ``#FOR`` macro first, giving:: 2;(37159+1),#PEEK37159;(37168+1),#PEEK37168 It then expands the ``#PEEK`` macros, ultimately forming the parameters of the ``#UDGARRAY`` macro. See :ref:`stringParameters` for details on alternative ways to supply the ``text`` parameter. Note that if an alternative delimiter is used, it must not be an alphanumeric character (A-Z, a-z, 0-9). .. _EVAL: #EVAL ----- The ``#EVAL`` macro expands to the value of an arithmetic expression. :: #EVALexpr[,base,width] * ``expr`` is the arithmetic expression * ``base`` is the number base in which the value is expressed: ``2``, ``10`` (the default) or ``16`` * ``width`` is the minimum number of digits in the output (default: ``1``); the value will be padded with leading zeroes if necessary For example:: ; The following mask byte is #EVAL(#PEEK29435,2,8). 29435 DEFB 62 This instance of the ``#EVAL`` macro expands to '00111110' (62 in binary). See :ref:`numericParameters` for details on the replacement fields that may be used in the parameter string. +---------+-------------------------------------------------------------------+ | Version | Changes | +=========+===================================================================+ | 8.0 | Added support for replacement fields in the parameter string | +---------+-------------------------------------------------------------------+ | 6.0 | Hexadecimal values are rendered in lower case when the | | | ``--lower`` option is used | +---------+-------------------------------------------------------------------+ | 5.1 | New | +---------+-------------------------------------------------------------------+ .. _FOR: #FOR ---- The ``#FOR`` macro expands to a sequence of strings based on a range of integers. :: #FORstart,stop[,step](var,string[,sep,fsep]) * ``start`` is first integer in the range * ``stop`` is the final integer in the range * ``step`` is the gap between each integer in the range (default: ``1``) * ``var`` is the variable name; for each integer in the range, it evaluates to that integer * ``string`` is the output string that is evaluated for each integer in the range; wherever the variable name (``var``) appears, its value is substituted * ``sep`` is the separator placed between each output string (default: the empty string) * ``fsep`` is the separator placed between the final two output strings (default: ``sep``) For example:: ; The next three bytes (#FOR31734,31736||n|#PEEKn|, | and ||) define the ; item locations. 31734 DEFB 24,17,156 This instance of the ``#FOR`` macro expands to '24, 17 and 156'. See :ref:`stringParameters` for details on alternative ways to supply the ``var``, ``string``, ``sep`` and ``fsep`` parameters. +---------+---------+ | Version | Changes | +=========+=========+ | 5.1 | New | +---------+---------+ .. _FOREACH: #FOREACH -------- The ``#FOREACH`` macro expands to a sequence of output strings based on a sequence of input strings. :: #FOREACH([s1,s2,...])(var,string[,sep,fsep]) or:: #FOREACH(svar)(var,string[,sep,fsep]) * ``s1``, ``s2`` etc. are the input strings * ``svar`` is a special variable that expands to a specific sequence of input strings (see below) * ``var`` is the variable name; for each input string, it evaluates to that string * ``string`` is the output string that is evaluated for each input string; wherever the variable name (``var``) appears, its value is substituted * ``sep`` is the separator placed between each output string (default: the empty string) * ``fsep`` is the separator placed between the final two output strings (default: ``sep``) For example:: ; The next three bytes (#FOREACH(31734,31735,31736)||n|#PEEKn|, | and ||) ; define the item locations. 31734 DEFB 24,17,156 This instance of the ``#FOREACH`` macro expands to '24, 17 and 156'. The ``#FOREACH`` macro recognises certain special variables, each one of which expands to a specific sequence of strings. The special variables are: * ``ENTRY[types]`` - the addresses of every entry of the specified type(s) in the memory map; if ``types`` is not given, every type is included * ``EREFaddr`` - the addresses of the routines that jump to or call a given instruction (at ``addr``) * ``REFaddr`` - the addresses of the routines that jump to or call a given routine (at ``addr``), or jump to or call any entry point within that routine For example:: ; The messages can be found at #FOREACH(ENTRYt)||n|n|, | and ||. This instance of the ``#FOREACH`` macro expands to a list of the addresses of the entries of type ``t`` (text). See :ref:`stringParameters` for details on alternative ways to supply the ``var``, ``string``, ``sep`` and ``fsep`` parameters. +---------+---------+ | Version | Changes | +=========+=========+ | 5.1 | New | +---------+---------+ .. _IF: #IF --- The ``#IF`` macro expands to an arbitrary string based on the truth value of an arithmetic expression. :: #IFexpr(true[,false]) * ``expr`` is the arithmetic expression * ``true`` is the output string when ``expr`` is true * ``false`` is the output string when ``expr`` is false (default: the empty string) For example:: ; #FOR0,7||n|#IF(#PEEK47134 & 2**(7-n))(X,O)|| 47134 DEFB 170 This instance of the ``#IF`` macro is used (in combination with a ``#FOR`` macro and a ``#PEEK`` macro) to display the contents of the address 47134 in the memory snapshot in binary format with 'X' for one and 'O' for zero: XOXOXOXO. See :ref:`stringParameters` for details on alternative ways to supply the ``true`` and ``false`` output strings. See :ref:`numericParameters` for details on the replacement fields that may be used in the ``expr`` parameter. +---------+----------------------------------------------------------------+ | Version | Changes | +=========+================================================================+ | 6.0 | Added support for replacement fields in the ``expr`` parameter | +---------+----------------------------------------------------------------+ | 5.1 | New | +---------+----------------------------------------------------------------+ .. _MAP: #MAP ---- The ``#MAP`` macro expands to a value from a map of key-value pairs whose keys are integers. :: #MAPkey(default[,k1:v1,k2:v2...]) * ``key`` is the integer to look up in the map * ``default`` is the default output string (used when ``key`` is not found in the map) * ``k1:v1``, ``k2:v2`` etc. are the key-value pairs in the map For example:: ; The next three bytes specify the directions that are available from here: ; #FOR56112,56114||q|#MAP(#PEEKq)(?,0:left,1:right,2:up,3:down)|, | and ||. 56112 DEFB 0,1,3 This instance of the ``#MAP`` macro is used (in combination with a ``#FOR`` macro and a ``#PEEK`` macro) to display a list of directions available based on the contents of addresses 56112-56114: 'left, right and down'. Note that the keys (``k1``, ``k2`` etc.) may be expressed using arithmetic operations. They may also be expressed using skool macros, but in that case the *entire* parameter string of the ``#MAP`` macro must be enclosed by a :ref:`hash` macro. See :ref:`stringParameters` for details on alternative ways to supply the default output string and the key-value pairs. See :ref:`numericParameters` for details on the replacement fields that may be used in the ``key`` parameter. +---------+---------------------------------------------------------------+ | Version | Changes | +=========+===============================================================+ | 6.0 | Added support for replacement fields in the ``key`` parameter | +---------+---------------------------------------------------------------+ | 5.1 | New | +---------+---------------------------------------------------------------+ .. _PC: #PC --- The ``#PC`` macro expands to the address of the closest instruction in the current entry. :: #PC For example:: c32768 XOR A ; This instruction is at #PC. This instance of the ``#PC`` macro expands to '32768'. In an entry header (i.e. title, description, register description or start comment), the ``#PC`` macro expands to the address of the first instruction in the entry. In a mid-block comment, the ``#PC`` macro expands to the address of the following instruction. In an instruction-level comment, the ``#PC`` macro expands to the address of the instruction. In a block end comment, the ``#PC`` macro expands to the address of the last instruction in the entry. +---------+---------+ | Version | Changes | +=========+=========+ | 8.0 | New | +---------+---------+ .. _PEEK: #PEEK ----- The ``#PEEK`` macro expands to the contents of an address in the memory snapshot. :: #PEEKaddr * ``addr`` is the address For example:: ; At the start of the game, the number of lives remaining is #PEEK33879. This instance of the ``#PEEK`` macro expands to the contents of the address 33879 in the memory snapshot. See also :ref:`POKES`. +---------+---------+ | Version | Changes | +=========+=========+ | 5.1 | New | +---------+---------+ General macros ^^^^^^^^^^^^^^ .. _CALL: #CALL ----- In HTML mode, the ``#CALL`` macro expands to the return value of a method on the `HtmlWriter` class or subclass that is being used to create the HTML disassembly (as defined by the ``HtmlWriterClass`` parameter in the :ref:`ref-Config` section of the ref file). In ASM mode, the ``#CALL`` macro expands to the return value of a method on the `AsmWriter` class or subclass that is being used to generate the ASM output (as defined by the :ref:`writer` ASM directive in the skool file). :: #CALL:methodName(args) * ``methodName`` is the name of the method to call * ``args`` is a comma-separated list of arguments to pass to the method For example:: ; The word at address 32768 is #CALL:word(32768). This instance of the ``#CALL`` macro expands to the return value of the `word` method (on the `HtmlWriter` or `AsmWriter` subclass being used) when called with the argument ``32768``. For information on writing methods that may be called by a ``#CALL`` macro, see the documentation on :ref:`extending SkoolKit `. +---------+--------------------------------------------------------------+ | Version | Changes | +=========+==============================================================+ | 5.1 | Added support for arithmetic expressions and skool macros in | | | numeric method arguments | +---------+--------------------------------------------------------------+ | 3.1 | Added support for ASM mode | +---------+--------------------------------------------------------------+ | 2.1 | New | +---------+--------------------------------------------------------------+ .. _CHR: #CHR ---- In HTML mode, the ``#CHR`` macro expands to a numeric character reference (``&#num;``). In ASM mode, it expands to a unicode character in the UTF-8 encoding. :: #CHRnum For example: .. parsed-literal:: :class: nonexistent 26751 DEFB 127 ; This is the copyright symbol: #CHR169 In HTML mode, this instance of the ``#CHR`` macro expands to ``©``. In ASM mode, it expands to the copyright symbol. +---------+------------------------------------------------------------------+ | Version | Changes | +=========+==================================================================+ | 5.1 | Added support for arithmetic expressions and skool macros in the | | | ``num`` parameter | +---------+------------------------------------------------------------------+ | 3.1 | New | +---------+------------------------------------------------------------------+ .. _D: #D -- The ``#D`` macro expands to the title of an entry (a routine or data block) in the memory map. :: #Daddr * ``addr`` is the address of the entry. For example:: ; Now we make an indirect jump to one of the following routines: ; . ; #TABLE(default,centre) ; { =h Address | =h Description } ; { #R27126 | #D27126 } This instance of the ``#D`` macro expands to the title of the routine at 27126. +---------+------------------------------------------------------------------+ | Version | Changes | +=========+==================================================================+ | 5.1 | Added support for arithmetic expressions and skool macros in the | | | ``addr`` parameter | +---------+------------------------------------------------------------------+ .. _HTML: #HTML ----- The ``#HTML`` macro expands to arbitrary text (in HTML mode) or to an empty string (in ASM mode). :: #HTML(text) The ``#HTML`` macro may be used to render HTML (which would otherwise be escaped) from a skool file. For example:: ; #HTML(For more information, go here.) ``text`` may contain other skool macros, which will be expanded before rendering. For example:: ; #HTML[The UDG defined here (32768) looks like this: #UDG32768,4,1] See :ref:`stringParameters` for details on alternative ways to supply the ``text`` parameter. Note that if an alternative delimiter is used, it must not be an upper case letter. See also :ref:`UDGTABLE`. +---------+---------+ | Version | Changes | +=========+=========+ | 3.1.2 | New | +---------+---------+ .. _INCLUDE: #INCLUDE -------- In HTML mode, the ``#INCLUDE`` macro expands to the contents of a ref file section; in ASM mode, it expands to an empty string. :: #INCLUDE[paragraphs](section) * ``paragraphs`` specifies how to format the contents of the ref file section: verbatim (``0`` - the default), or into paragraphs (``1``) * ``section`` is the name of the ref file section The ``#INCLUDE`` macro can be used to insert the contents of one ref file section into another. For example:: [MemoryMap:RoutinesMap] Intro=#INCLUDE(RoutinesMapIntro) [RoutinesMapIntro] This is the intro to the 'Routines' map page. See :ref:`stringParameters` for details on alternative ways to supply the ``section`` parameter. +---------+---------+ | Version | Changes | +=========+=========+ | 5.3 | New | +---------+---------+ .. _LINK: #LINK ----- In HTML mode, the ``#LINK`` macro expands to a hyperlink (```` element) to another page. :: #LINK:PageId[#name](link text) * ``PageId`` is the ID of the page to link to * ``name`` is the name of an anchor on the page to link to * ``link text`` is the link text to use In HTML mode, if the link text is blank, it defaults either to the title of the entry being linked to (if the page is a :ref:`box page ` and contains an entry with the specified anchor), or to the page's link text. In ASM mode, the ``#LINK`` macro expands to the link text. The page IDs that may be used are the same as the file IDs that may be used in the :ref:`paths` section of a ref file, or the page IDs defined by :ref:`page` sections. For example:: ; See the #LINK:Glossary(glossary) for a definition of 'chuntey'. In HTML mode, this instance of the ``#LINK`` macro expands to a hyperlink to the 'Glossary' page, with link text 'glossary'. In ASM mode, this instance of the ``#LINK`` macro expands to 'glossary'. To create a hyperlink to an entry on a memory map page, use the address of the entry as the anchor. For example:: ; Now we update the #LINK:GameStatusBuffer#40000(number of lives). In HTML mode, the anchor of this ``#LINK`` macro (40000) is converted to the format specified by the ``AddressAnchor`` parameter in the :ref:`ref-Game` section. +---------+------------------------------------------------------------------+ | Version | Changes | +=========+==================================================================+ | 5.4 | When linking to an entry on a :ref:`box page `, the | | | link text, if left blank, defaults to the title of the entry (in | | | HTML mode) | +---------+------------------------------------------------------------------+ | 5.2 | An entry address anchor in a link to a memory map page is | | | converted to the format specified by the ``AddressAnchor`` | | | parameter | +---------+------------------------------------------------------------------+ | 3.1.3 | If left blank, the link text defaults to the page's link text in | | | HTML mode | +---------+------------------------------------------------------------------+ | 2.1 | New | +---------+------------------------------------------------------------------+ .. _LIST: #LIST ----- The ``#LIST`` macro marks the beginning of a list of bulleted items; ``LIST#`` is used to mark the end. Between these markers, the list items are defined. :: #LIST[(class[,bullet])][][items]LIST# * ``class`` is the CSS class to use for the ``