ASM8

A two-pass absolute macro cross-assembler for the 68HC08/HCS08/9S08

 

 

Quick Reference Guide

ASM8 - Copyright © 2001-2017 by Tony Papadimitriou <tonyp@acm.org>

Latest Update: September 29, 2017 for ASM8 v9.73

Command-Line Syntax and Options

 

ASM8 [-option [...]] [[@]filespec [...]] [>errfile]

 

·         option(s) may appear before, in between or after filespec(s).

·         option(s) apply to all files assembled, regardless of command line placement.

·         Text file(s) containing list(s) of files to be processed may be specified by naming the text file on the command line, prefixed with a «@» character.  These text files may not contain command line options.

·         filespec(s) may include wildcard characters (?,*).  Wildcards are not allowed in filespec(s) that are prefixed with a «@» but are allowed in filespecs inside @files.

·         If the file extension for a source filespec is omitted, the extension «.ASM» is assumed (see description of the –R.ext option below).

·         Assembler errors may be redirected to errfile using standard DOS output redirection syntax.  This capability may be used in conjunction with, or as an alternative to the –E+ option.  Currently, error redirection is only possible with the DOS version.

·         All shaded areas are pertinent to the MMU enabled versions (Win32/Linux only).

·         Any label can hold a value that is 32-bit long.  Even though the CPU cannot understand numbers larger than 16-bit for data or addressing (MMU not withstanding), the ability to have 32-bit labels allows keeping constants that are larger than 16-bit for use in later constant calculations.  Decimal numbers are signed; the largest number is +/-2147483647.  Hex or binary numbers are unsigned and can go up to the full 32-bit value (2^32-1).  For example, a symbol holding the crystal frequency of operation can be expressed with Hz detail to be used later to derive other constant values (such as bps rates or cycle-based delays). The 32-bit capability is NOT available in the DOS version.

·         The assembler will set the DOS ERRORLEVEL variable when it terminates as indicated:
0          No error, assembly of last file was successful
1          System error (hardware I/O failure, out of disk space, etc.), or
-W option failure
2          Error(s) generated (or Escape pressed) during assembly of last file
3          Warning(s) generated during assembly of last file
4          Assembler was not started (help screen displayed,
-W option used with success)

 

Option

Default

Description

 

 

 

-C[±]

-C-

Label case sensitivity: + = case sensitive (See also #CASEON/#CASEOFF)

-Dlabel [:expr]

 

Use up to ten times to define symbols for use with conditional assembly (IFDEF and IFNDEF directives).  Symbols are always uppercase (regardless of -C option).  If they are not followed by a value (or expression) they assume the value zero.  Expression is limited to 19 characters.  Character constants should not contain spaces, and they are converted to uppercase.  Cannot be saved with -W.

-E[±]

-E-

Generate *.ERR file (one for each file assembled).  *.ERR files are not generated for file(s) that do not contain errors.

-EH[±]

-EH+

If –E+ is in effect, hide (do not display) error messages on screen.

-EXP[±]

-EXP-

When on, an .EXP file is created containing all symbols defined with an EXP rather than an EQU pseudo-opcode.  The resulting file can be used as an #INCLUDE file for other programs.  This allows for automatic creation of include files with global exported symbols.

-F:symb

-F:num

 

During assembly, if it finds the given symbol, it prints a ‘Hint’ message showing the file and line number when that symbol defines or redefines its value.  This is very useful for debugging hard to locate errors of where exactly in the source code a symbol acquired its value.

Alternatively, you can give it a number (either in decimal or hex format the way the assembler understands numbers).  In this case, the ‘Hint’ message will show the file and line number when that memory address was occupied by either data or code.  This is very useful to help you resolve overlap type errors.

You may use either : or = with this option after the -F.

This option cannot be saved with the –W switch

-F2[±]

-F2-

Forces a P&E 16-bit map when in MMU mode.  Useful to overcome bugs in certain P&E products that do not handle MMU addresses correctly.

This option cannot be saved with the –W switch

-FD[±]

-FD-

When on, the assembler uses a fake/fixed date (specifically, Jan 1, 2011) for the internal symbols :YEAR, :MONTH, and :DATE.  It does not falsify the date shown on the listing header, however.

 

This option is useful to let you always get the same S19 CRC value (shown both at the end of the listing file, and on the command-line next to each successfully assembled file), even if you use the :YEAR, :MONTH, and :DATE internal symbols in your source code, which based on the compilation date of your program would normally alter the resulting S19 CRC.  This would, in turn, make it more difficult to quickly check if your program produces the same, or a different binary, since last time you checked.  Keeping a record in your source of the most recent S19 CRC produced with the –FD option, let’s you know if something has (perhaps, inadvertently) changed.  Without the –FD option, you can’t be sure if it’s just the date that changed, or something else.

 

WARNING: Do NOT include this option in batch or makefiles that compile your programs automatically, or you risk producing consistently misdated firmware.  It should only be used for manual verification purposes.

 

It’s not by accident this option cannot be saved with the –W switch.

-FH[±]

-FH-

Forces hidden macros in listings (#HIDEMACROS) and ignores all #SHOWMACROS directives.

This option cannot be saved with the –W switch

-FL[±]

-FL-

Ignores all #LISTOFF or #NOLIST directives.

This option cannot be saved with the –W switch

-FM[±]

-FM-

Ignores all #MAPOFF directives.

This option cannot be saved with the –W switch

-FQ[±]

-FQ-

Does not show the assembly progress. Slightly better speed.  For use with IDEs and makefiles.

This option cannot be saved with the –W switch

-FW[±]

-FW-

Converts warnings to harmless messages.  No error code is returned and warnings are not counted.

This option cannot be saved with the –W switch

-FX[±]

-FX-

Enable macro line number display in FATAL, ERROR, WARNING, MESSAGE, and HINT directives. The default is off for less cluttered display.

This option cannot be saved with the –W switch

-HCS[±]

-HCS-

When on, the assembler understands the extended HCS08 instruction set.  The cycle counts in the listing also reflect the HCS08 core.  To check the current status of this switch look at the help screen’s second line from top.  The software will say it’s either a MC68HC08 or a MC68HCS08 assembler based on this setting.  See also the directives #HCSON, #HCSOFF, #IFHCS, and #IFNHCS

-Ix

 

Define default INCLUDE directory root(s).  Relative path files will be tried relative to this directory (or each directory in the given directory list, searched from left to right).

Multiple directory roots may be specified in the form of a list.  A directory root list is separated by semi-colons when on Windows, or colons when on Linux.

The * character can be used as a placeholder for the current text of this option (e.g., when you want to add extra directories either before or after the current ones without having to specify the whole thing again.)

Since v9.56 two special placeholders may be used in the directory list specification: ?1 which refers to the path of the current main file, and ?2 which refers to the path of the current (parent) file.  Since the main file is now searched last, and the current (parent) file’s path is no longer searched by default, to get the same behavior as was the default in previous versions, you should run this to convert the current path to the new format but with compatible behavior: -i.;?1;?2;* -w (for Linux, use -i.:?1:?2:* -w)

 

Since v9.58 FOSSIL source control management users can use the ?F (case-insensitive) placeholder to specify the root directory of the repository.  This allows for truly portable installations of your code base.

 

Since v9.61 users can use the ?A (case-insensitive) placeholder to specify the assumed root directory, which must contain a filename named _asm_ (lowercase in Linux) of zero length, even.  This allows for truly portable installations of your code base.

 

This switch does not affect absolute path file definitions.  Both the INCLUDE and the IF(N)EXISTS directives are affected by this switch.

-J[±]

-J+

Effective only while the MMU is disabled: When on, CALL/RTC instructions are treated as if they were JSR/RTS instructions, respectively.  When off, CALL/RTC instructions produce errors.  Makes it possible to write common library functions using CALL/RTC instead of JSR/RTS and have them used in all MCUs, regardless if they have an MMU or not.  See also the directives #JUMP, and #CALL

-L[±]

-L+

Create a *.LST file (one for each file assembled).

-LC[±]

-LC+

List any conditional directives fully (the directives only, not the contents in between), even when they are False.

-LLnum

-LL19

Define the maximum recognizable Label Length from the legacy 19 characters up to an absolute maximum of 50 characters.  See also the directive #MAXLABEL.

-LS[±]

-LS-

Create a *.SYM symbol list (one for each file assembled).  May be useful for debuggers that do not support the P&E map file format.

-LSx

-LSS

x may be either S (default) for simple SYM file, E for EM11/Shadow11 SYM compatible format (possibly not useful for HC08), or N for NoICE SYM format.

-M[±]

-M+

Create a *.MAP (one for each file assembled).  *.MAP files created may be used with debuggers that support the P&E source-level map file format.

-MMU[±]

-MMU-

Enable the MMU features (e.g., CALL/RTC instructions, 24-bit addresses and expressions).  See also the directives #MMU, #NOMMU, #IFMMU, and #IFNOMMU

-MTx

-MTP

Specifies type of MAP file to be generated (if –M+ in effect):

-MTA : Generate parsable ASCII map file

-MTP : Generate P&E-style map file

-O[±]

-O+

Enables these four warnings:  ‘S19 overlap’, ‘RMB overlap’, ‘Violation of MEMORY directive’, and ‘Violation of VARIABLE directive’.

-P[±]

-P+

When on it tells the assembler to stop after Pass 1 if there were any errors.  Provides for faster overall assembly process and less confusion by irrelevant side errors of Pass 2.  Warnings do not affect this.

-Q[±]

-Q-

Specifies quiet run (no output) when redirecting to an error file (DOS only).  Useful for IDEs that call ASM8 and don’t want to have their display messed up.

Beginning with v1.29, this option can also be used to suppress all output from #Message directives.

-Rn

-R74

Specifies maximum length of S-record files.  The length count n includes all characters in an S-record, including the leading «S» and record type, but not the CR/LF line terminator.  Minimum value is 12 while maximum is 250.

-R.ext

-R.ASM

Specifies the default extension to assume for source files specified on the command line, which do not directly specify an extension.

-REL[±]

-REL+

Allows generation of  «BRA/BSR instead of JMP/JSR» optimization warnings when enabled.  (See also OPTRELON/OPTRELOFF)

-RTS[±]

-RTS-

Allows generation of  «JSR followed by RTS» subroutine call optimization warnings when enabled. (See also OPTRTSON/OPTRTSOFF)

-S[±]

-S+

Generate *.S19 object file (one for each file assembled).

-S2[±]

-S2-

Force the generation of S2 records (24-bit addresses) even for 16-bit addresses.  Although 24-bit addresses are enabled, no MMU features are enabled.  Useful mostly for forcing 16-bit addresses to appear as 24-bit (with leading byte as $00) so that S19 loaders can use that as the PPAGE value.  See also #S1 and #S2

-S9[±]

-S9+

This option can be used to turn off generation of the final S9 (or S8) record found by default in all S19 files.  This may be useful when assembling code in parts that will be combined with other S19 files.  Since you only need a single S9 record in the final S19 file, you can use this option to not produce S9 records for all but one of the files that will be merged together to produce a single object file with a single S9 record.  This option cannot be saved with the –W switch.

 

Example: Application and bootloader merging.  Assuming you merge the first with the second (in that order), the bootloader should be assembled as usual, and the application with the –S9 option in effect.

-SH[±]

-SH-

Include dummy «S0» record (header) in object file (only if –S+).

-SP[±]

-SP-

When enabled, the operand part of an instruction is stripped of spaces before parsing.  In this case, possible comments must begin with semi-colon.  (See also SPACESON/SPACESOFF)

-T[±]

-T-

Makes all errors look like Borland errors (useful to fool certain IDEs).

-Tn

-T8

Specifies tab field width to use in *.LST files.   Tab characters embedded in the source file are converted to spaces in the listing file such that columns are aligned to 1 + every nth character.

-Ux

 

Define default OUTPUT directory.  If this option is defined, all produced files will end up in this directory, regardless of where the source file is located.  When this option is undefined (no path given), produced files will end up in the same directory as the primary source file.

Not available in the DOS version.

-X[±]

-X+

Allow recognition of extra, non-68HC08-standard mnemonics and simulated index modes in source files. (See also EXTRAON/EXTRAOFF)

-Z[±]

-Z-

Convert the paged addresses (PAGE:ADDR16) to linear (extended) address in the produced S19 file(s).  In effect, all addresses within the ranges $xx8000-$xxBFFF are converted to their linear format.  The code or listing is not affected at all.

This is useful for S19 loaders that expect addresses in linear format, instead of paged format.

Warning: The ambiguous case of $8000-$BFFF is treated as PAGE0, which is converted to linear addresses: $000000-$003FFF.  If you want to place something at PAGE2, position it (ORG) at $028000, which will convert to linear address $008000.

-WRN[±]

-WRN+

Enables or disables the display of all warnings.  When enabled, only warnings that aren’t disabled individually will be generated. When disabled, it overrides local warning options (such as -REL and -RTS).

-W

(none)

Write options specified on command line to the ASM8 executable (DOS), or ASM8.CFG file (Win/Linux).  The user-specified options become the default values used by ASM8 in subsequent invocations.  Filespec(s) on the command line are ignored.  Assembly of source files does not take place if this option is specified.

 

 

Source File Pseudo-Opcodes (Pseudo-Instructions)

All shaded areas are pertinent to the MMU enabled versions (Win32/Linux only).

Pseudo-Op

Description

 

 

[label] ALIGN expr

Case 1. If no label is present, it aligns the current location counter to be a multiple of the given expression.

 

Case 2. If a label is present it aligns the value of that label to be a multiple of the given expression.  (In this case, however, it does nothing to the current location counter.)  It issues an error if the label is not already defined.

 

COMPATIBILITY ISSUE WITH VERSIONS PRIOR TO 8.30:

 

Prior to version 8.30, the optional label would be assigned the current location counter value after the alignment.  The label could not be defined earlier, or you would get an error.

With v8.30 and later, you get an error if the label is not already defined by the time ALIGN is reached because the new behavior requires a previous definition so it can align the existing value of the label.   This makes it easy to catch all incompatible ALIGN statements written for the previous version(s).  If you get an error, simply move the label after the ALIGN statement.

DB string|expr[,...]

Define Byte(s).  expr may be a constant numeric, a label reference, an expression, or a string.  DB encodes a single byte in the object file at the current location counter for each expr encountered (using the LSB of the result) or one byte for each character in strings.

DS blocksize

Define Storage.  The assembler’s location counter is incremented by blocksize. Forward references not allowed.  No code is generated.

DW expr[,...]

Define Word(s).  expr may be a constant numeric, a label or an expression.  expr is always interpreted as a word (16-bit) quantity, and is stored in the object file at the current location counter, high byte followed by low byte.

END [expr]

Provided for compatibility.  The END directive cannot be used to terminate assembly; ASM8 always processes the source file to the end of file.  If expr is specified, the word result of the final END directive is encoded in the S9 record of the object file.

If the expr specified is 24-bit (bits 23-16, collectively, are non-zero), which is possible only when the MMU option is enabled, the 24-bit result is encoded in the S8 record of the object file (no S9 record is produced in that case).

[Label] ENDM

Ends definition of a macro.

The optional label is defined prior to the ENDM processing, which means it is in the same scope as the rest of the macro.

label DEF expr[,size]

Assigns a DEFault value to a label.  In other words, this is a conditional EQU.  It only assigns the label if the label is currently undefined.

 

LABEL      DEF     EXPR

 

is equivalent to:

 

#IFNDEF    LABEL

LABEL      EQU     EXPR

#ENDIF

 

Note: The value that appears in the listing file is the actual new value of the label, which may be different from the value of the expression, since the assignment may not occur.

label EQU expr[,size]

Assigns the value of expr to label. See also EXP and SET

label EXP expr[,size]

Assigns the value of expr to label.  This is similar to EQU but with the following difference: Labels defined thus will be included in the .EXP file as regular SETs. This effectively allows exporting symbols for use from other source files.  It makes it possible to give only object code to others along with the produced .EXP file so that they can «link» the object to their source.

FAR expr[,...]

Define 24-bit word(s) when the MMU is enabled.  expr may be a constant numeric, a label or an expression.  expr is always interpreted as a 24-bit quantity, and is stored in the object file at the current location counter in big-endian order.

If, however, the MMU option is disabled, FAR is treated as DW.

FCB string|expr[,...]

Form Constant Byte(s).  Same as DB.

FCC string|expr[,...]

Form Constant Character(s).  Same as DB.

FCS string|expr[,...]

Form Constant String.  Similar to FCC, but automatically appends a terminating null (0) byte to the end of the string defined (for ASCIZ strings).

FDB expr[,...]

Form Double Byte(s).  Same as DW.

LONG expr[,...]

Form 32-bit long word(s).  expr may be a constant numeric, a label or an expression.  expr is always interpreted as a 32-bit quantity, and is stored in the object file at the current location counter in big-endian order.

Not available in the DOS version.

MacroName MACRO comments

... REMACRO ...

MACRO begins the definition of a new macro.

 

REMACRO begins the definition of a new macro over a possibly existing same name macro.  Hint: Use #DROP to remove the latest definition, restoring the previous one, if any.

 

  • Macros must be defined anytime before they are invoked, and they can be invoked until the end of the current assembly (for global macros), end of current file (for local macros), or until a #DROP directive undefines them, in either global or local case.
  • The body of macros is placed between MACRO and ENDM keywords, and it can contain any text.  All that text is associated with the specified MacroName, as is.  Normal semi-colon beginning comments are copied also.  If you want comments to appear only in the macro definition but not in each later expansion of the macro, use double semi-colon (;;) for those comments to cause them not to be saved along with the macro.
  • By default, macros are invoked using the @MacroName[,parm separator] syntax (see #MACRO, #@MACRO, #MCF, and #MCF2 directives).  Note: You can also use the %macro call syntax (i.e., % prefix, instead of @) to force all macro counters (:MINDEX, :INDEX, :LOOP), except for :MACRONEST, for the specific macro to reset, as if you had dropped and recreated the macro.
  • During invocation, the macro name may be followed by a comma and any non-alphanumeric single character (if more characters found, only the first matters).  If this parameter override option is present, then the character right after the comma will act as a one-time parameter delimiter (just for this macro call.  The #PARMS defined delimiter will not be affected.)  If the character is a space, it does not require yet another space as field separator between macro name and parameters.
  • The macro may refer to yet undefined labels or macros, as the code or definitions inside it are not truly parsed until the macro is actually used, if at all.
  • The macro is expanded on a line-by-line basis.  Each line in the macro body (the text between the macro and endm keywords), is expanded and then assembled, before the next line of the macro body is fetched.
  • Conditionals used inside macros are always local to the current macro invocation, i.e., you cannot open a condition (like #IFDEF) inside a macro and then close it (#ENDIF) outside the macro.
  • Local macro names start with the ? symbol (like it is done with normal local labels).
  • The special local macro named ? (just a single question-mark) is to be used ad-hoc.  This one special macro name is automatically dropped (without warning) at each new redefinition.  It’s useful for quickly defining a temporary macro to be used immediately afterwards, and considered discarded later.  For example, an instruction (or series of instructions) with a  complex operand expression can be embedded inside a ? local macro using as parameter the variable part of the expression.  This can often make your code more readable (and, editable more easily.)

§  Parameters are passed during invocation in the operand field separated by commas (or whatever delimiter you have defined with the #PARMS directive, or the special one-time parameter separator override.)

§  To use a null parameter, just put two delimiters next to each other (e.g., @MACRO PARM1,,PARM3).  Note: This will work for any delimiter except for space; two or more consecutive spaces – outside a string, of course – are seen by the assembler as one space in the parameter field.  Space delimiters can only be used with sequential parameters without gaps in between (which is good for the majority of cases, but not all).  If you must know, this is because the assembler trims multiple spaces between fields to locate the operand field.  If spaces were allowed to separate null parameters, it would also have to count the spaces from the macro name to the parameter field less one that is required to separate the two fields and possibly less one more that could be used with a “space” parameter override, and since the null parameters could be first in the list of parameters, this would be very confusing, and hard to get it to work correctly (especially since you can’t easily count spaces) while also maintaining the desired code formatting.  So, when calling a macro with non-trailing null parameters, make sure the parameter separator is NOT a space (either by default or by override), or you will get incorrect macro expansions (and, depending on what the macro does and how it expands, you may not always get side errors).

  • Macro-local labels must include the string $$$ at least once anywhere inside their name (except at the very beginning), e.g. Loop$$$ or Main$$$Loop
  • Parameter text replaces placeholders anywhere within the body of the macro (label, operation, operand, comment fields) without regard to context.  Parameter placeholders are ~0~ thru ~9~ (where ~0~ is reserved for the macro name itself, and ~1~ thru ~9~ for actual parameters.)
  • ~Cn~ where n is a number from 0 thru 9 is equivalent to ~{n}.{:loop}.1~
  • ~-Cn~ where n is a number from 0 thru 9 is equivalent to ~{n}.{:{n}-:loop+}.1~
  • ~cn~ where n is a number from 0 thru 9 is equivalent to ~{n}.{:mloop}.1~
  • ~-cn~ where n is a number from 0 thru 9 is equivalent to ~{n}.{:{n}-:mloop+}.1~
  • The body of a macro may contain nested embedded expressions (in any field, even comments) of the form {<expr>}, like one can do with strings, where <expr> is any valid expression, normally including some parameter placeholder(s).  Expressions are evaluated last, after expansion of parameter placeholders but before the ~n.s.l~ type placeholder (described later).
  • To accommodate indexed mode instruction operands within any one parameter (provided the macro is called with a non-comma parameter separator), you can use the following variations of the placeholders: ~n,~ and ~,n~ (where n is the number 1 thru 9) and the comma position (either after or before the number) defines whether we want the part before the index (excluding the comma), or the index itself (including the comma), respectively.  For example, the instruction lda ~1,~+1~,1~ will expand correctly whether parm ~1~ contains an index or not.  (Using the simpler lda ~1~+1 will not expand as  intended, when used with indexed operands, as the +1 will follow the index, and not the offset before the index.)  If no index is within the parameter, ~n,~ is the same as ~n~ while ~,n~ is null.  The assembler will pick anything following a possible comma (the first one) within a parameter as being an index (so you could get creative and use the feature for other purposes also).
  • The special placeholder ~#~ returns either a null string or the character # if the first parameter’s (~1~) first character is a # (possibly, indicating immediate mode use).  With conditional assembly (e.g., #IFPARM ~#~) one can treat the ~1~ parameter differently, assuming immediate mode.
  • Similarly, the placeholder ~#n~ (where n is a number from 1 thru 9, zero also accepted but it is pointless) returns the parameter part after a possible # sign, if one is present.  This allows getting an immediate mode type parameter in a form (stripped of the # symbol) that can be used in expressions (for example, in an #IF directive expression).  Note: If no # is inside the parameter, ~#n~ is the same as ~n~ alone.
  • Since one may often call a macro with a non-comma delimiter (such as when a parameter contains a comma in an indexed operand – e.g. 1,x), a possible chained macro call passing this parameter to another macro, or to self while looping, must use the same parameter delimiter that was used to call the original macro, or else the parameter may not be passed on correctly, or not even as a single parameter.  Using the default parameter separator (a comma) from within a macro to call another macro (or self) is problematic in those cases.  To solve this problem, two equivalent special placeholders have been introduced.  One is the ASCII code 149 [] (e.g., use the ALT-7 method in the numeric keypad for entry in Win-PCs), and the other is the two-character sequence \, (a backslash followed by a comma) which should be possible to type in any editor.  Either of these placeholders will be replaced by the same delimiter as the one used for the most recent macro call (either by default or by override), unless there is a new explicit one-time delimiter override (@macro,char call format).
  • The special placeholder ~label~ (case-insensitive) returns the actual text of a label appearing in the label column of the last macro invocation (after expanding possible label embedded {<expr>}).  This can be used with ‘function-like’ macros that need to set a label to a specific value (without having to pass the name of the label as a regular parameter).  If no label is used in the same line as the macro invocation, then it returns a null (empty) string.  If, however, no label is used with a chained (or nested) macro invocation (a macro invocation occurring from inside a macro) then the text value of ~label~ is not changed from the original macro’s.  This way, a macro can chain to itself (for looping), or another macro and still have the ~label~ placeholder expand correctly.  Note: The length of the actual text inside ~label~ can be found in the internal variable :label.
  • The special placeholder ~macro~ (case-insensitive) returns the name of the top-level macro call (useful when used inside nested or chained macros).  For example, if macro A calls macro B, which then calls macro C, then ~macro~ equals A inside all three macros.
  • The placeholder ~00~ returns the name of the macro calling this macro (i.e. the macro one-level above, or the same macro if calling itself).  If at the top-level, ~00~ is the same as ~0~.  Useful when combining common functionality macros but need the name of the previous macro calling this one.  For example, if macro A calls macro B, which then calls macro C, then ~00~ equals A (when inside A or B) but ~00~ equals B (when inside C).
  • The special placeholder ~self~ (case-insensitive) returns the original name of the current macro (useful if you use #RENAME from within the macro and then need to restore the actual name the macro had when entered, using  #REMACRO).
  • The special placeholder ~text~ (case-insensitive) returns the current temporary text parameter of the current macro.  This is an temporary placeholder that remembers its macro-unique value across different macro calls, adding extreme flexibility.  You can also use it as temporary text workspace when manipulating regular macro parameters.  ~text~ can be changed with MSET, MSWAP, MDEF using zero for the parameter index.  The current length of ~text~ can be found in the internal variable :TEXT
  • Similarly, the placeholder ~#text~ (case-insensitive) returns the part after a possible # symbol(if one is present).
  • The case-insensitive placeholder ~filename~ returns the current file’s filename including the file extension, while  ~basename~ returns the filename without extension, and ~path~ returns the full path with filename and extension.  The variant starting with m (for macro) shows the corresponding filename for where the macro definition is located (~mfilename~ etc.), which is not necessarily in the current file.
  • The placeholder ~@~ is an alias for the full list of placeholders separated by (starting from ~1~).   Useful if you want to pass all parameters to another macro.  The sequence produced by ~@~ is: ~1~•~2~•~3~•~4~•~5~•~6~•~7~•~8~•~9~
  • The placeholder ~@@~ is an alias for the full list of placeholders separated by but starting from ~2~.  Useful if you want to pass the remaining parameters to the same macro when looping (assuming each loop only processes the first parameter, until that becomes null).  The sequence produced by ~@@~ is: ~2~•~3~•~4~•~5~•~6~•~7~•~8~•~9~
  • Trailing parameter separators (commas by default) and trailing commas due to macro expansion of null parameters are automatically removed.  This is particularly useful when writing macros, which replace or enhance single-operand instructions.  One can write the macro so that it does not require a parameter separator override during invocation, just so it can recognize a possible indexed operand.  Example: LDA ~1~,~2~ will work even if ~2~ is null, because the now ‘dangling’ comma after ~1~ will be automatically removed, preventing an otherwise expected syntax error.  Similarly, LDA ~1,~+1~,1~,~2~ will work for the following location pointed to by the expression in parm 1, regardless of the presence of an index in parm 1, parm 2, or at all.
  • An alternative to the above method can be achieved (in most cases) by using the ~[n.p]~ format of the parameter placeholder (where n is the parameter number, or the case-insensitive keyword text) to extract the pth byte of the argument (e.g., if ~1~ contains my_var,sp ~[1.1]~ will return my_var,sp while ~[1.2]~ will return my_var+1,sp but  if ~1~ contains an immediate value such as #$1234 ~[1.-1]~ will return #$12 while ~[1.-2]~ will return #$34 etc.  For a 32-bit example, for parm #$12345678 ~[1.1]~ will return #$12 while ~[1.-1]~ will return #$56.  The p number can be any integer (positive or negative) but for immediate mode parameters the sign matters, and it only works up to 32-bit if positive (i.e., 1..4), or up to 16-bit if negative (i.e., -1..-2).  For immediate mode only, a number above 4 or below –2 will return #0 since that is the effective value.  This ~[n.p]~ placeholder makes it particularly easy to deal in a unified way with any parameter, be it immediate, direct, extended, or indexed mode.  Care has to be taken to use a negative p if working with 16-bit values, however.
  • You can use the ~n[set]i~ format of the parameter placeholder to extract the i-th part of the n-th parameter using the character set [set].  The character set can be given as a string of characters with or without quotes (square brackets, instead).  Therefore, quotes can also be part of the character set.  Example, ~2[,.]3~ will return the 3rd part of the 2nd parameter, where parts are separated by any instance of comma and dot.  (Note: ‘n’ and ‘i’ are optional.  If ‘n’ is missing, the value one is assumed.  If ‘i’ is missing, the value one is assumed.  Care must be taken not to allow both to be missing, if the character set contains a dot because it will then be interpreted as a ~[n.p]~ placeholder, which is processed earlier.)  If the character set contains only a single character, then embedded strings will be skipped over, otherwise characters even inside strings will be matched by the characters in the set.
  • You can use the ~n.s.l~ format of the parameter placeholder (where n is the parameter number, or the case-insensitive keyword text or label, or a constant string enclosed in quotes, s is the starting position, or a constant string to search for, and l is the needed length, or a constant string to search for but past the s position) to extract only a portion of the text of the corresponding parameter or constant.  The first dot is required (to disambiguate from ~n~ type parms) even if nothing follows.  The s and l are optional.  If s is not entered its value is assumed to be one, so that ~1.~ is the same as ~1~ alone.  If l is not entered, its value is the length from s to the end of the parameter (i.e., the remaining string).  Note: The assembler forces s and l to always be within the limits of the text length.  So, specifying a position past the end of the parameter text will always return the last character.  To check for past-of-text, check against the :nnn length internal symbol for the specific parameter (e.g., :1 for parm one). If you need to make n, s, or l the result of an expression you can use {expr} (for example: ~1.{:loop}.2~).

 

SPECIAL CASE: When inside a string, the expression will be evaluated when the string is processed by the assembler, which is after macro expansion of the various placeholders.  This means we have lost our chance to expand this placeholder.  But, we can use the \@ instead of quotes for strings inside a macro which contain ~n.s.l~ embedded expressions, and not only those (example: fcc \@~{PARM}.{FROM}.{LENGTH}~\@ to have it expand correctly.  Because of the \@ the string does not appear as a string yet, and the expressions can be calculated during macro expansion.  This way all expressions become simple constants, and the placeholder can be processed.  Finally, the \@ dummy string delimiters are turned into single, double, or back quotes, depending on which of these three doesn’t appear in the string at all, making the whole thing a proper string.

 

IMPORTANT COMPATIBILITY ISSUE: A couple or so versions compiled prior to 2010/09/24 23:00 used @@ instead of \@.  The @@ was an unfortunate selection of dummy quote delimiter and it had to be replaced with a better one (\@) even though  it meant possibly causing problems with existing code (hopefully, not that many macros utilizing this feature were written in the few days the feature has been available with the wrong delimiter) because it caused syntax errors in certain cases, e.g. if single character string contained the @ char (with or without macro parameter expansion), or labels containing @@ inside their name.

  • Order of placeholder expansion is: ~@~, ~@@~, ~label~, ~macro~, ~00~, ~self~, ~text~, ~#~, ~#n~, ~n~ (where n = 0..9, in that order), \,, and , {expression}, ~[n.p]~, ~n.s.l~, ~n[set]i~, \@string\@, and ~Cn~ variations.
  • During macro invocation, any parameter text may contain embedded expressions of the form {<expr>}, like one can do with strings, where <expr> is any expression, possibly including some parameter placeholder(s), if already inside a macro.  This may be needed in situations where the parameter may be intrepreted incorrectly while used inside the macro.  For example, if the * (normally used to indicate ‘here’, as in BRA *) is passed as a parameter to be used inside the macro, it may have a different value, depending on where it is used.  Passing this parameter as {*} is first ‘expanded’ using the current value, and then passed in the macro as a simple constant.  Note: You can also do the same expansion from within the macro, making it worry-free for the user of the macro.  For example, one of the first macro lines can change * to {*} (using MSET) if the specific parameter is found to have this text.
  • Macros cannot #INCLUDE files, but can ‘chain’ to one.
  • Macros cannot define other macros.
  • Macro-embedded macros are not supported.  (Tip: Simple ‘embedded macros’ can be emulated by using any unused parameters to contain the text of the ‘embedded macro’.  The MSET keyword can be used from within the macro to ‘define’ the ‘embedded macro’ in one or more unused parameters, – each parameter representing a single line of the ‘embedded macro’ – then use just the relevant placeholders alone wherever you want to expand the ‘embedded macro’.)
  • Macros can ‘chain’ to self or other macros (with no automatic return).  This allows, among other things, for creating loops, making macros very powerful.
  • Macros can temporarily invoke other macros, and then return back to continue with the original macro.  Use the double @ (@@ or %%) notation when calling a macro from within another macro if you want to return back (as opposed to chain to another macro), regardless of macro mode.  The default maximum nesting level is 100 (which should be more than adequate for most cases) but it can be changed to as high as 10,000 with the directive #MLimit, or as low as zero, which disables this capability completely.  Note: Prefer using macro chaining over nested macro calling when feasible, or to get a looping effect, as it is more efficient both in terms of memory usage and assembly speed.  Tip: To use macros as with some other assemblers, i.e., without having to type @ prefix, and having a default nesting (rather than ‘chaining’) behavior, enable the #MACRO @@ mode (see the relevant section for details) .  Macro-chaining will be altogether disabled, however.
  • To break out of an accidental endless macro loop, press [ESC] on the command-line.
  • Macro labels may be case-sensitive (depending on #CaseOn/Off directives) when defined, but are always case-insensitive when invoked (like normal opcode names).  Tip: A case-sensitive macro definition is important when using the ~0~, ~00~, and ~macro~ placeholders to have it correctly match a normal label named the same as the macro, under #CaseOn mode.
  • Virtually unlimited number of macro definitions (memory permitting.)
  • Virtually unlimited size of each macro (memory permitting.)
  • Unlimited number of macro invocations (all internal macro counters are 32-bit).

MERROR [text]

 

Combines an #ERROR directive followed immediately by an MEXIT, which is commonly found in macros.  This can only be used inside macros.

MEXIT [expr]

Causes an unconditional early exit from a macro expansion.  (Normally, used inside a conditional block.)

 

If the optional expression (without any forward references) is present, its value will be placed in the :MEXIT internal variable.  If the expression is missing, the current value of :MEXIT will not be changed, allowing for cascaded return values from nested macros.

MSUSPEND

MRESUME

 

MSUSPEND can be used only from within a macro (usually once, but since there is no limit, more than once, if needed) to temporarily suspend the execution of the current macro.

 

Suspending a macro preserves the current macro state (parms, counters, etc.) just like nested macros do to protect the parent macro’s state, but it allows for code outside any macros to be assembled in place of the MSUSPEND keyword, as if it were part of the macro (except that it is actually assembled outside the macro, so none of the macro-only features can be used, and none of the macro limitations apply – for example, normal use of #INCLUDE is possible, as well as definition of new macros, etc.)

 

This makes it much easier to create nestable macros that emulate block structures, than by using two separate macros (one for block begin, and one for block end) and trying to keep them synchronized.

 

Note: When a nested macro is suspended, all macros leading to the currently executing macro are indirectly suspended as a side effect.

 

MRESUME can be used only from outside any macros to resume execution of the most recently suspended macro.

 

You can have several macros in the suspended state, but they can only be resumed in a LIFO order (i.e., stack order).  This allows for the creation of nested blocks (like WHILE, FOR, IF, REPEAT, etc.) commonly found in higher-level languages.

 

The recursion limit (see #MLimit) counts suspended macros also, because these are stacked just like when doing normal nested macro calls.

 

This MSUSPEND/MRESUME feature makes it particularly easy to replace pairs of macros (like FORENDFOR) that normally appear right before and after a code section to create a block structure, with a single macro that does all the required work and simply allows (via the use of the keyword MSUSPEND) the inclusion of any arbitrary code in between (i.e., between the macro call and the MRESUME keyword).

 

Simple nested example (counts lines of intermediate source code, and issues warning if optional limit is exceeded):

 

                    org       *

 

CountLines          macro     [Limit][,Description]

                    mset      2, ~@@~

                    #temp     :lineno+1

                    msuspend

                    #temp     :lineno-:temp

                    #Message  Section~2~ spans {:temp} lines

          #ifnb ~1~

          #if :temp > ~1~

                    #Warning  Too many lines (>{~1~})

          #endif

          #endif

                    endm

; To use:

                    @CountLines ,OUTER

                    nop

                    @CountLines ,INNER

                    nop

                    nop

                    mresume

                    nop

                    mresume

MSTOP [#ALL#]

MSTOP [text]

When used inside a macro, it causes an unconditional early termination of all currently executing macros, and regardless of nesting level.  (Normally, used inside a conditional block.)

 

When used outside a macro, it causes the most recent suspended macro to stop being suspended.  When the optional #ALL# parameter is used, then all nested suspended macros are stopped (become no longer suspended).

MSTR index[,index]*

MSTR tests each one of the specified indexed macro parameter text for being a string, and, if not a string, it changes it to one using the appropriate delimiters based on the contents of the parameter text.

 

It is equivalent to the following sequence (but repeated for each specified index n):

 

#IFPARM ~n.~

    #IFNOSTR   ~n.~

           MSET    n,\@~n.~\@

    #ENDIF

#ENDIF

MSET index[,text]

MSET #

MSET #’charset’

MDEF index[,text]

MSWAP index,index

MDEL index

MTRIM index[,index]*

 

MSET changes the current macro’s index-ed parameter to the text that follows, or to null if no text follows.  There are many potential uses for this capability (such as using the macro parameters as temporary text variables.)  It is particularly useful, however, with macro loops using the MTOP command.

 

A second variation of MSET (added in v8.90) allows to unite all parameters into one, and optionally split back into as many parameters using a user-defined character set given as string.  For example, MSET # will unite all current macro parameters into just one (using the currently active macro parameter separator.)  MSET #’abc’ will first unite alls parms into one and then split into separate parms for every occurrence of characters a, b, or c, while skipping over parenthesized parts, and strings.  If the string after # has just one character, this will be eliminated (as it will be treated as the new parm separator.)   If it has more than one character, the parm will be split right before the character, and the found character will be the first character of the next parm.  This feature allows you to re-arrange the parameters based on a different separator.

 

MDEF is similar to MSET but it only changes the text of the parameter if the parameter is currently null.  This is the same as using MSET within an #IFNOPARM conditional block.  It’s useful for setting default macro parameters (normally, at the top of the macro).

 

MSWAP simply swaps the text of any two parameters.  (Swapping a parameter with itself has no effect.)

As an example for MSWAP, in macros with multiple single operands, you can use it to bring the working operand always in, say, ~1~, which may be simpler to use than the equivalent ~{:loop}.~ from inside a loop.

 

MDEL deletes the current macro’s index-ed parameter.  Note: This is different that using MSET without the text parameter to delete the content of a parameter placeholder.  MDEL deletes the actual parameter location, which means all following parameters will move down one location.  For example, MDEL 1, will move ~2~ to ~1~, ~3~ to ~2~, and so on, for all parameters that follow.

 

MTRIM will trim all non-string spaces from the respective parameter(s).

 

Note: In all cases, index is any expression that doesn’t contain forward references.

MREQ ind[,ind]*[:errmsg]

Checks each of the specified macro parameters (separated with commas) for null value (empty).  If the parameter is null, an appropriate internal error message is displayed, and the macro expansion is terminated at that point.

If the optional errmsg parameter is present (which must follow a colon), this error message will be displayed instead of the default error message.

 

This can be used to specify which macro parameters are required, and print an error message, if these parameters are null.  If more than one of the specified parameters are null, the message will repeat for each one of them.

You may use MREQ multiple times, perhaps, once for each parameter, so that you can have a unique error displayed for each parameter.

 

Note: ind is any expression that doesn’t contain forward references.  errmsg is any text.  If ind contains an internal variable (such as :LOOP), it must be enclosed in { ... } because the colon is also used as the beginning of the errmsg.

MTOP [limit expr]

Causes an immediate unconditional jump to the top line of the current macro, while incrementing the :LOOP counter.  It can be used either alone or within conditionals.  The advantage to using MTOP over @~0~ (a macro call to self) is that whatever parameters were passed in the macro do not need to be specified again as the macro is never exited.  Also, no counters are incremented, except for :LOOP.  This means, however, that $$$ based labels (which are unique to a macro invocation) are still in the same scope as before the MTOP command since no new macro has been invoked.

 

If the optional limiting expression (containing only non-forward references) is present, its value will be compared to the :LOOP counter and MTOP will execute only if the current value of :LOOP is less than the value of the expression.  Example (shift word right one or more times):

 

lsr.w     macro     Address[,Count]

          mdef      2,1    ;default Count=1

          lsr       ~1~

          ror       ~1,~+1~,1~

          mtop      ~2~

          endm

 

As another example, an expression like the one that follows can be used to loop while the next parameter is not null:

mtop :loop+:{:loop+1}

MDO [start expr]

MLOOP [limit expr]

MDO and MLOOP work together to form a local DO ... LOOP inside a macro.  Note:  MDO and MLOOP cannot be nested because MLOOP always matches the most recent MDO of the current macro.

 

MDO simply marks the current line (i.e., the line containing the MDO keyword) as the beginning of a local loop, and (re)initializes the :MLOOP counter to one (1), or to the value of the non-forward expression, if one is present.

 

MLOOP causes an immediate unconditional jump to the line following the most recent MDO keyword, while incrementing the :MLOOP counter (not to be confused with the :MACROLOOP or :LOOP counter).  If no MDO was used up to this point in the macro, MLOOP jumps to the top of the current macro (just like MTOP would), but it only affects the :MLOOP counter (whereas MTOP only affects the :LOOP counter).

 

If the optional limiting expression (containing only non-forward references) is present, its value will be compared to the :MLOOP counter and MLOOP will execute only if the current value of :MLOOP is less than the value of the expression.  Example (multi-byte addition):

 

add.m     macro     Op1,Op2,Ans[,Size]

          mdef      4,1       ;default size = 1

          #push

          #spauto   :sp

          psha

 

          mdo

          lda       ~1,~+{~4~-:mloop}~,1~

#if :mloop = 1

          add       ~2,~+{~4~-:mloop}~,2~

#else

          adc       ~2,~+{~4~-:mloop}~,2~

#endif

          sta       ~3,~+{~4~-:mloop}~,3~

          mloop     ~4~

         

          pula

          #pull

          endm

 

As another example, an expression like the one that follows can be used to loop while the next parameter is not null:

mloop :mloop+:{:mloop+1}

label NEXP symbol[,expr]

Assigns the current value of symbol to label as if with EXP.  Then, it increments the value of symbol by one (as if with SET) or, if the optional expression is present, by the value of that expression.  Useful for defining a series of symbols based on a common starting value.  Note: symbol is a single label and not an expression.  See also NEXT, SETN

[label] NEXT symbol[,expr]

Assigns the current value of symbol to label as if with EQU.  Then, it increments the value of symbol by one (as if with SET) or, if the optional expression is present, by the value of that expression.  Useful for defining a series of symbols based on a common starting value.  Note: symbol is a single label and not an expression.

Since v9.41, a special case of NEXT is when the label to the left is missing.  In that case, NEXT is used as an anonymous placeholder that simply increments the symbol to the right, as usual.

See also NEXP, SETN

ORG [[s19_expr],]expr

Sets the assembler’s location counter for the active segment.  Code generated after this directive will be assembled starting at the location specified by expr.

 

If s19_expr is present, then the S19 file runs with an offset from the actual location counter.  This allows for different segments of code to be assembled at the same physical address but, obviously, be placed in different addresses in the loadable S19 file.

 

The current offset is available in the :OFFSET internal symbol.

 

To cancel any offsets without changing the current position, simply give ORG followed by a single comma without any expressions.

label PROC

[label] ENDP

PROC first advances the @@ local label counter, and then it assigns the value of the program counter (*) to label.  This allows using symbols locally for a specific section of code (e.g., a subroutine).  The symbol to the left of PROC is always in the new scope.  The name of the symbol is stored in the ~procname~ macro placeholder.  Each time PROC (or #PROC) is encountered, the assembler increments an internal 32-bit local symbol counter.  Symbols containing @@ anywhere inside their name (except at the very beginning) at least once (for example, Loop@@) will have the @@ part replaced with a special control character (different from what is used with macro local $$$) and the current value of the internal local symbol counter (similar to $$$ with macro local labels).

Up until a PROC or #PROC is encountered in the program, the @@ is not treated specially (i.e., the @@ is not converted to a special number).  This makes this feature compatible with code written prior to its introduction.  The current value of the corresponding internal counter can be found in the internal symbol :PROC while the maximum proc number can be found in the internal symbol :MAXPROC

 

ENDP is optional and marks the end of the corresponding PROC.  Its use allows one to nest procs (e.g., for code coherency as when keeping a subroutine close to the actual point of use).  The optional label is defined prior to the ENDP processing, which means it is in the same scope as the rest of the proc.

 

See also #PROC and #ENDP

RMB blocksize

Reserve Memory Byte(s).  Same as DS.

label SET expr[,size]

Assigns the value of expr to label even if label is already defined with a different value.

This is similar to EQU but allows making multiple re-definitions.  The value set will be used until another SET pseudo-instruction or to the end of the assembly process.

Warning: Careless, or simply wrong use of this directive can lead to multiple side errors or warnings (please note this is a two-pass assembler).  Using a forward SET defined symbol may lead to problems, as the value used will be the one from the last SET definition, which is not necessarily the one we want.

Correct behavior is guaranteed if any symbols re-defined with SET are used only after each new re-definition, otherwise, the first reference in Pass 2 will use the value from the last re-definition in Pass 1.

Example of wrong use:

1.             lda  #Value  ;we expect 123, actual is 234

2. Value       equ  123

               ...

3.             lda  #Value  ;we expect 234, actual is 123

4. Value       set  234

Value in line 1 will be 234 (the last known value from Pass 1) while Value in line 3 will be 123 (most recent value in current Pass 2).

 

Example of correct use:

1. Value       equ  123

2.             lda  #Value  ;we expect 123, actual is 123

               ...

3. Value       set  234

4.             lda  #Value  ;we expect 234, actual is 234

See also EXP and EQU

label SETN symbol[,expr]

Assigns the current value of symbol to label as if with SET.  Then, it increments the value of symbol by one (as if with SET) or, if the optional expression is present, by the value of that expression.  Useful for (re-)defining a series of symbols based on a common starting value.  Note: symbol is a single label and not an expression.  See also NEXP, NEXT

Source File Processing Directives

 

·         All processing directives must be prefixed with a $ or # character.  ASM8 will recognize either character as the start of a processing directive.

·         If a directive has a corresponding command-line option, the directive in the source file will override the command line directive at the point in which the source file directive is encountered.

·         [text] will be trimmed of duplicate spaces.  To have more than one consecutive spaces display, use the Alt-255 character, as many times as needed.

·         All shaded areas are pertinent to the MMU enabled versions (Win32/Linux only).

 

Directive

Description

 

 

#AIS [symbol]

#AIS checks the current value of the :SP internal variable against the most recent AIS instruction’s value, and issues a warning if the two numbers do NOT differ by the exact value in the symbol (note: a plain symbol, not an expression), indicating a possible stack frame definition error (assuming correct placement of the relevant directives).

 

The warning also shows the correct AIS instruction that is required to correct the problem.
 
This directive makes it very easy to correct the numeric value in AIS instructions to match the following stack frame definition (normally made using the internal :: symbol in the various #SPAUTO modes, and the next/setn method for defining records/structures. )  This is useful to prevent having to define the stack frame before the AIS instruction using a one-based starting offset just so you can use a label with AIS and then having to re-define it for dynamic assignment of offsets based on the current :SP.

 

v8.70+: If, however, a symbol is not specified, then this directive simply resets the value of the :AIS internal symbol to the current :SP value difference (as if a negative AIS instruction had been used in its place).  This can be used when no actual AIS instruction is used (for example, a series of PSHx instructions are used, but we want to use the :AIS variable later on to de-allocate any local variables.)

 

The associated :AIS symbol returns the difference between the current :SP and the value saved during the most recent AIS instruction.  This can be used to de-allocate just the number of stack bytes that are still left on the stack between the two points in your source (inclusive of the previous AIS instruction).  This is only meant for use in #SPAUTO modes, which automatically adjusts the current value of the :SP internal symbol.

 

#PUSH and #PULL will save/restore the value of this setting.

 
Example use:

 

                    #spauto

 

Subroutine          ais       #-4                 ;local data

 

?                   set       ::

?Parm1              setn      ?,2

?Parm2              setn      ?,2

 

                    #ais      ?                   ;check frame definition

 

#CALL

Effective only while the MMU is disabled: When active, CALL/RTC instructions are NOT treated as if they were JSR/RTS instructions, but they issue errors instead.  See also the directives #JUMP, #MMU, #NOMMU

Equivalent to the –J- command line option.

#CASEOFF

When #CASEOFF is in effect, all symbol references that follow are converted to uppercase internally before they are searched for or placed in the symbol table.

Equivalent to the –C- command line option.

#CASEON

When #CASEON is in effect, symbol references are NOT internally converted to uppercase before they are searched for or placed in the symbol table.

Equivalent to the –C+ command line option.

#CRC expr

The two CRCs (user and S19) maintained by the assembler are 16-bit each, and they are updated only during PASS2 by each produced user code/data byte that is put into the S19 file.  The starting CRC value for both CRCs is zero.

 

With this directive you can alter the user CRC value at any time (either before the very first byte of code/data to produce a different CRC for the same firmware, or several times in between to skip certain volatile sections, for example).

 

The computed CRCs are available by accessing the internal symbols :CRC and :S19CRC

 

The formula used for the 16-bit CRC calculation is very simple to be easily implemented even in tiny bootloaders:

 

16BitCRC := 16BitCRC + 16BitAddress*8BitData

 

:S19CRC is mostly useful with the END directive (END :S19CRC) as it is not affected by the #CRC directive.  An S19 loader can check the overall integrity of the S19 file.

 

:CRC, on the other hand, is mostly useful for checking code after it has been loaded into the MCU, at each reset, for example.

 

Please note that for both CRCs all $00 bytes do not affect the calculation while, for the user CRC only (:CRC), all $FF bytes are intentionally skipped.  This allows for the CRC in an S19 file (which does not necessarily fill a contiguous block of memory) to match the CRC computed by the MCU over a complete block of memory without the MCU bootloader knowing in advance the actual addresses used within that block, provided any unused bytes are in the erased state.

As a side effect, however, any $00->$FF or $FF->$00 alterations in the file cannot be detected with the user CRC.

#CYCLES [expr]

First, the optional expression is calculated using the current values of any internal symbols.

Then, the current value of :CYCLES is copied to :OCYCLES.

Finally, the internal:CYCLES counter is set to zero (if the optional expression is missing), or to any arbitrary value (the result of the expression).

This directive can also be used inside macros to restore the cycle counter of surrounding code, if the macro cycles should be counted in a special way, or not at all.

#DATA

Activation of the DATA segment.  Default starting value is $FE00.

#DROP macro[,macro]*

Undefines one or more macros.  If a macro is not currently defined, a warning will be issued (to protect from possible typing errors).

 

To drop all macros (global and local) with a single command, use * (asterisk) in place of the macro name.  There is no warning if no macros found.

 

To drop all local macros (for the current file only) with a single command, use ?* (question mark followed by asterisk) in place of the macro name.  There is no warning if no local macros found.

 

If used from inside a macro, and that macro is dropped, the macro will terminate at that point.  The rest of the macro will not be processed.

 

The special macro named ? (just a single question-mark) is to be used ad-hoc, and it is automatically dropped (without warning) at each new redefinition.  You may also drop it with #DROP but only need to do so if you want to force errors in later use of the macro, so you can easily locate them.

 

You cannot drop macros that are currently active above the current macro level (e.g. nested macros leading to current one.)

#EEPROM

Activation of the EEPROM segment.  Default starting value is $0000.

#EJECT

See #PAGE

#ELSE [IFxxx]

When used in conjunction with conditional assembly directives (#IF, #IF[N]DEF, $IF[N]Z, #IFMAIN, #IFINCLUDED, etc.), code following the #ELSE directive is assembled if the conditional it is paired with evaluates to a not-true result.

Optionally, you can follow with another IF directive (of any kind) to create a ‘chained’ condition check, like:

#IF … #ELSE IF … #ELSE IF … #ELSE … #ENDIF

The optional IF should not start with a # or $ directive symbol but it should be separated with at least one space.

#ENDIF

Marks the end of a conditional-assembly block.

Conditional assembly statements may be nested if they are properly blocked with #ENDIF directives.

#ERROR [text]

When encountered in the source, the assembler issues an error message in the same form as internally-generated errors, using the text specified, prefixed with «USER: »

#EXIT [expr]

If no expression is present, it immediately exits the current #INCLUDE file.  (Does nothing if used inside a main file.)

 

If the optional expression is present (normally though, this might be just a single label), the exit occurs only if the expression is defined (as if when checked with #IFDEF).

 

This can be used in the top of #INCLUDE files, like so:

 

           #EXIT    _COMMON_

_COMMON_

 

In this example, the first time this file is included, the symbol _COMMON_ is undefined, so the #EXIT is ignored.  Consequent times this file is included, it exits upon hitting the #EXIT directive.

 

Note:  Due to how #INCLUDE files are counted internally, and there being a limit on how many total files you can #INCLUDE, it’s better when working with larger projects that you do not #INCLUDE a file at all when already processed, rather than #INCLUDE it and #EXIT it.  (See also #USES)

#EXTRAOFF

Disables recognition of ASM8’s extended instruction set for source lines that follow this directive.

Equivalent to the –X- command line option.

#EXTRAON

Enables recognition of ASM8’s extended instruction set for source lines that follow this directive.

Equivalent to the –X+ command line option.

#EXPORT symbol[,symbol]*

Export one or more symbols (as if with EXP).  File-local symbols cannot be exported.  If a symbol is not currently defined, a warning will be issued.

#FATAL [text]

Similar to the #ERROR directive, but generates an assembler fatal error message and terminates the assembler (possible further files in the list will not be processed).

#HCSOFF

Disables the HCS08 instruction set mode.  See also #IFHCS #IFNHCS #HCSON

Equivalent to the –HCS- command line option.

#HCSON

Enables the HCS08 instruction set mode.  See also #IFHCS #IFNHCS #HCSOFF

Equivalent to the –HCS+ command line option.

#HOMEDIR [path]

Makes the specified path the current home directory.  Although this cannot affect where any output files will go, it does make a difference on where any following relative #INCLUDE files will be searched.  Relative file path specifications will now be relative to the directory specified by the #HOMEDIR directive, including any relative #INCLUDE references in nested include files.

If [path] is missing, the original main file path is restored.

#IF expr1 cond expr2

Evaluates expr1 and expr2 (which may be any valid ASM8 expression) and compares them using the specified cond conditional operator.  If the condition is true, the code following the #IF operator is assembled, up to its matching #ELSE or #ENDIF directive.

Cond may be any one of:  <  <=  =  >=  >  <>

The condition is always evaluated using unsigned arithmetic.

If a symbol referenced in expr1 or expr2 is not defined, the statement will always evaluate as false.

#IFDEF expr

Attempts to evaluate expr, and if successful, assembles the code that follows, up to the matching #ELSE or #ENDIF directive.  This directive is used to test if a specified symbol has been defined.  Symbol(s) referenced in expr must be defined before the directive for the result to evaluate true (e.g., forward references will evaluate as false). #IFDEF without an expr following will always evaluate to False.

#IFEXISTS fpath

Checks for the existence of the file specified by fpath (using the same rules as those used for #INCLUDE directives) and assembles the code that follows if the specified fpath exists.

#IFHCS

Assembles the following code if the assembler is in the extended HCS08 instruction set mode.  See also #IFNHCS #HCSON #HCSOFF

#IFINCLUDED

Assembles the code which follows if the file containing this directive is a file used in an INCLUDE directive of a higher-level file (regardless of nesting level).  See also #IFMAIN

#IFMAIN

Assembles the code that follows if the file containing this directive is the main (primary) file being assembled.  See also #IFINCLUDED.

#IFMDEF macro

#IFNOMDEF macro

#IFMDEF checks if the specified macro exists, and if so, assembles the code that follows, up to the matching #ELSE or #ENDIF directive.  This directive is used to test if the specified macro has been defined.  #IFNOMDEF does the opposite check.

#IFMMU

Assembles the code that follows if the MMU option is enabled.  See also the directives #MMU, #NOMMU, and #IFNOMMU

#IFNOMMU

Assembles the code that follows if the MMU option is disabled.  See also the directives #MMU, #NOMMU, and #IFMMU

#IFPARM text [= text]

#IFPARM text [== text]

 

#IFNOPARM text [= text]

#IFNOPARM text [== text]

 

Aliases:

#IFB same as #IFNOPARM

#IFNB same as #IFPARM

Normally used inside macros.  If text is non-blank, assembles the code that follows, up to the matching #ELSE or #ENDIF directive.  This directive is used to test if a specified macro parameter has been defined.  #IFPARM without text following (after macro expansion) will always evaluate to False. text is usually a parameter placeholder (e.g., ~1~).

You can also make a case-insensitive (using the = sign) or case-sensitive (using the == sign) comparison of the parameter to a specific text string (with or without quotes, depending on your intent) by separating the two text strings with an ‘equals’ (=), or  double-equals (==) sign, depending on the desired case-sensitivity.  For example,

#IFPARM ~1~ = *

tests if parameter one is a plain asterisk (normally used to indicate the current location pointer.)

#IFNOPARM performs the opposite test.

#IFSPAUTO

Assembles the code that follows, up to the matching #ELSE or #ENDIF directive, if the assembler is currently in #SPAUTO (automatic SP adjustment) mode.  See also #SPAUTO #SP

#IFSTR text

#IFNOSTR text

Normally used inside a macro.  If text is a quoted string, assembles the code that follows, up to the matching #ELSE or #ENDIF directive.  This directive is used to test if a specified macro parameter is a string.  #IFSTR without text following (after macro expansion) will always evaluate to False. text is usually a parameter placeholder (e.g., ~1~).

#IFNOSTR performs the opposite test.

#IFNUM text

#IFNONUM text

Normally used inside a macro.  If text represents a number, assembles the code that follows, up to the matching #ELSE or #ENDIF directive.  This directive is used to test if a specified macro parameter is a number.  #IFNUM without text following (after macro expansion) will always evaluate to False. text is usually a parameter placeholder (e.g., ~1~).

#IFNONUM performs the opposite test.

#INCLUDE fpath

#USES fpath

Includes the specified fpath file in the assembly stream, as if the contents of the file were physically present in the source at the point where the #INCLUDE directive is encountered.  #INCLUDE’s may be nested, up to 100 or 125 levels (the main source file counts as one level).  Relative fpath specifications are always referenced to the directory in which the main source file resides, including any relative #INCLUDE fpath references in nested include files.

 

#USES is an alternative, slightly different method to include a file.  It will #INCLUDE the file specified (using the same file-finding rules as #INCLUDE) but only if the same file path has not been included (via #INCLUDE or #USES) at least once, already.  #USES is useful for creating #INCLUDE file dependencies (normally, from a higher level to a lower level – e.g., an analog temperature sensor driver module #USES the A/D driver module, but not the other way around).  This allows directly #USING (an alias for #USES) only the module of interest in your application, and it should take care to use whatever other modules it requires (in a recursive sort of way).  If another included module in the same application #USES the same lower-level module, it will not be included a second time.  This is similar to the common

 

#IFNDEF _MODULE_

_MODULE_

 ...your module code goes here...

#ENDIF

 

technique used to prevent multiple inclusions of the same file, but only have it included the first time it is referenced.  Normally, the #IFNDEF … #ENDIF block is found inside the file, meaning the assembler must enter the file before it ‘knows’ it doesn’t need it.  The advantage with #USES, however, is (1) that you do not need a specific symbol definition for each file, and (2) you never enter an already included file (which would use up a sometimes precious file count towards the maximum number of #INCLUDE files.)

 

Bi-directional, or circular co-dependencies (e.g., file A depends on file B, while file B depends on A) are possible in some cases, and then they require some extra attention in the respective files’ internal organization, or it could not work as you might have expected, and leave you confused by ‘spurious’ errors.  In general though, you should try to avoid them.

 

Also, you cannot use #USES in place of #INCLUDE for modules that must be included multiple times (e.g., including the same SCI driver module, once for each hardware SCI available), although you could use #USES to include a file that itself does #INCLUDE the same file multiple times.

 

Note: The assembler will only generate a standard error (not an assembly-terminating fatal error) if a file specified in a #INCLUDE (or #USES) directive is not found.  The #IFEXISTS and #IFNEXISTS directives may be used in conjunction with #FATAL if termination of assembly is desired under such conditions.

#IFNDEF expr

Evaluates expr and assembles the code that follows if the expression could NOT be evaluated, usually as the result of a reference to an undefined symbol.  This directive is the functional opposite of the #IFDEF directive.

#IFNEXISTS fpath

The opposite of #IFEXISTS; code following this directive is assembled if the specified fpath does NOT exist.  The -Ix directory will be used also to determine if a file exists or not.

#IFNHCS

Assembles the following code if the assembler is in the regular HC08 instruction set mode.  See also #IFHCS #HCSON #HCSOFF

#IFNZ expr

Evaluates expr and assembles the code that follows if the expression evaluates to a non-zero value.  #IFNZ always evaluates to false if expr references undefined or forward-defined symbols.

#IFTOS expr

If top-of-stack evaluates expr+:SP (+:SP is implied) and assembles the code that follows if the expression is equal to one (when in #SP[AUTO] modes), or zero (when in #SP1 mode), i.e., expression points to top-of-stack in all modes.  #IFTOS always evaluates to false if expr references undefined or forward-defined symbols.

Useful mostly in #SP[AUTO] modes.

#IFZ expr

Evaluates expr and assembles the code that follows if the expression is equal to zero.  #IFZ always evaluates to false if expr references undefined or forward-defined symbols.

#JUMP

Effective only while the MMU is disabled: When active, CALL/RTC instructions are treated as if they were JSR/RTS instructions, respectively.  Makes it possible to write common library functions using CALL/RTC instead of JSR/RTS to be used in any MCU, regardless if an MMU is available/used or not.  See also the directives #CALL, #MMU, #NOMMU

Equivalent to the –J+ command line option.

#LISTOFF

#NOLIST

Turns off generation of source and object data in the *.LST file for all lines which follow this directive.  Useful for excluding the contents of #INCLUDE files in the *.LST file.

#LISTON

#LIST

Enables generation of source and object data in the *.LST file for the source code following this directive.  Has no effect if list file generation is disabled (-L- command line option in effect).

#MACRO [@@]

#MCF [@@]

#MCF2 [@@]

#@MACRO [@@]

 

#MACRO tells the assembler to treat unknown assembly language operations as possible macros.  Normal instructions (including the built-in macro instructions) have priority over macros, so macros named the same as active built-in operations can only be called with the @ prefix.

In effect, when in this mode, the assembler automatically adds the @ symbol if an unknown operation is found to be a macro name.  In this mode, one can invoke macros either way, with or without the @ prefix, but instructions have priority over same name macros.

Note: To avoid problems, all macros should internally use the @macro syntax so they can be properly expanded regardless of mode.

 

#MCF (“Macros Come First”) is similar to #MACRO (i.e., no @ prefix is required for calling macros) but in this case macros have priority over same-name instructions but only when called from outside any macros.  Macro chaining (i.e., jumping to a macro from inside a macro) is still only possible using the @ prefix when a macro name collides with an active instruction name.   So, using this mode is 100% compatible with macros written before this mode was introduced and does not require editing macros to use the !instruction format mentioned next.

If you’re in #MCF mode, and you want to temporarily give priority to a real instruction (without changing to #Macro or #@Macro mode), you must prefix it with a ! (exclamation point.)

The #MCF mode is most useful when you want to override the functionality of any internal instruction with something more involved (a macro), as for example, when porting code from another CPU with similar instructions but different functionality (e.g. LDX in 68HC11 is a word operation, and it may compile without errors in the 68HC[S]08 but with incorrect operation as it will not affect the full HX register).

I do not recommend casual use of this mode as it may make the source code totally misleading (if instructions which are now possibly macros aren’t what they seem but something completely different.)

 

#MCF2 is almost the same as #MCF but it doesn’t have the restriction where macros named the same as instructions require the @macro format from within macros.  This is the most ‘dangerous’ of all available modes, since it is always the macro which has precedence.  If you need to be certain you use a real instruction and not a possible macro with the same name, you MUST use the !instruction format.

 

#@MACRO turns off this option.  This is the default setting when a new assembly begins.  In this mode, you can only invoke macros with the @ prefix.  This is the recommended mode for most normal applications.

 

Hint: The macro is normally invoked as an instruction, which means its name must appear after column one. Regardless of the current macro mode, when a macro call is made using the default @macro (or %macro) format, its invocation can start even in column one, since it can’t ever be a symbol that starts with one of these two characters [@ and %].

 

Note: If the optional @@ parameter is provided to any of the four directives mentioned above, macro chaining is effectively disabled, and any otherwise ‘chained’ calls now become truly nested calls (as if the @@macro format is used at all times a macro is called).  WARNING: Macros written based on the default ‘chain’ behavior may no longer operate the same (since non-@@ macro calls include an implied following mexit).  To simulate the same behavior, when the @@ option is active, make sure you add an MEXIT command after each otherwise ‘chained’ macro call.  By the way, this will make the macro work the same way regardless of the @@ sub-mode being in effect or not.

When the @@ sub-mode is in effect, you still need to observe the various calling methods based on which of the three macro modes you’re in.  To cancel the @@ sub-mode, simply give any of these directives without it.

#MAXLABEL number

Define the maximum recognizable Label Length from the legacy 19 characters up to an absolute maximum of 50 characters.  See also the command-line option -LL.

 

#PUSH and #PULL will save/restore the value of this setting.

#MEXPORT macro[,macro]*

Export one or more macros in the EXP file (if one is produced).  File-local macros cannot be exported.  If a macro is not currently defined, a warning will be issued.

#MLIMIT [expr]

Sets the maximum macro nesting limit to the value of the optional expression.

If no expression follows the default value of 100 is used.  This value should be more than adequate for nearly all cases.

Minimum value is zero (which practically disables macro call nesting).  Maximum is 10000 (ten thousand).

Note: Macro nesting uses extra memory during assembly.  You should avoid using macro nesting if the same functionality can be achieved by using macro chaining, or even the most efficient simple looping (MTOP instruction).

#MLISTOFF

#NOMLIST

 

Turns off generation of source and object data in the *.LST file for all macro body lines which follow this directive.  Useful for excluding the body of macros in the *.LST file.

#MLISTON

#MLIST

Enables generation of source and object data in the *.LST file for all macro body lines following this directive.  Has no effect if list file generation is disabled (-L- command line option in effect).  This is the default setting.

#HIDEMACROS

#SHOWMACROS

Note: These two directives work only when the –LC- (List Conditionals = OFF) command-line option is in effect.

 

#HideMacros treats all macro-specific keywords (the @macro call, mexit, mtop, endm) the same as ‘conditional’ directives only for the purposes of display in the listing.  So, when –LC- is in effect, they won’t appear in the *.LST file at all.  This leaves only the expanded macro contents.  When this directive is in effect, it is no longer possible to know where a macro begins or ends, or how many times it iterates itself.

 

Note: The corresponding macro definitions will not display at all, regardless of the –LC mode.

 

#ShowMacros (re-)enables normal display.  The default setting when a new assemby begins is #SHOWMACROS.

 

#PUSH and #PULL will save/restore the value of this setting.

#MAPOFF

Suppresses generation of source-line information in the *.MAP file for the code following this directive.  Symbols which are defined following this directive are still included in the *.MAP file.

#MAPON

Enables generation of source-line information in the *.MAP file for the code following this directive.  #MAPON is the default state when assembly is started when map file generation is enabled (-M+ command line option).

#MEMORY addr1 [addr2]

#MEMORY #OFF#

 

Maps a memory location (or range, if addr2 is also supplied) of object code and/or data areas as valid.  Use multiple directives to specify additional ranges.  Any code or data that falls outside the given range(s) will produce a warning (if the -O option is enabled) for each violating byte.  Very useful for segmented memory devices, etc.  Addr1 and addr2 may be specified in any order.  The range defined will always be between the smaller and the higher values.

The special keyword #OFF# removes all current definitions.

See also #VARIABLE

#MESSAGE [text]

#HINT [text]

Displays text on screen during the first pass of assembly when this directive is encountered in the source.  Messages or hints are not written to the error file.  They are meant to inform the user of options used or conditional paths taken.

#HINT cannot be masked with the –Q+ option.

#MMU

Enable the MMU features (e.g., CALL/RTC instructions, 24-bit addresses and expressions).  See also the directives #NOMMU, #IFMMU, and #IFNOMMU

Equivalent to the –MMU+ command line option.

#NOMMU

Disable the MMU features (e.g., CALL/RTC instructions, 24-bit addresses and expressions).  See also the directives #MMU, #IFMMU, and #IFNOMMU

Equivalent to the –MMU- command line option.

#NOWARN

Turns warnings off.  Equivalent to the –WRN- command line option.  See also #WARN

#OPTRELOFF

Disable «BRA/BSR instead of JMP/JSR» optimization warnings.

Equivalent to the –REL- command line option.

#OPTRELON

Enable warning generation when an absolute branch or subroutine call (JMP or JSR) is encountered that could be successfully implemented using the relative form of the same instruction (BRA or BSR).  This option is on by default.

Equivalent to the –REL+ command line option.

#OPTRTSOFF

Disable RTS-after-JSR/BSR optimization warning (default).

Equivalent to the –RTS- command line option.

#OPTRTSON

Enable warning generation when a subroutine call (JSR or BSR) is immediately followed by a RTS.  This option is off by default.  Command-line option -RTS+ does the same thing.

#PARMS [char|SPACE]

Allows changing the delimiter used to separate macro parameters when invoking the macro.  If char is defined the new delimiter will be the same as char.  If there is no character following the directive, the default parameter delimiter (a comma) will be used.

To use a regular space as a parameter separator, the [char] part of the command should be the special keyword SPACE (case-insensitive).

 

#PUSH and #PULL will save/restore the value of this setting.

#PPC

#PPC (stands for Preserve PC) simply keeps a copy of the current :PC value to be used later by the :PPC internal symbol.

#PUSH and #PULL will save/restore the value of this setting.

#PROC

#ENDP

Advances the @@ local label counter.  Nullifies the contents of the ~procname~ macro placeholder. See also PROC

#ENDP closes the corresponding PROC section. See also ENDP

#PSP

#PSP (stands for Preserve SP) simply keeps a copy of the current :SP value to be used later by the :PSP internal symbol.

 

The :PSP symbol returns the difference between the then current :SP and the value saved with this directive.  This can be used to de-allocate just the number of stack bytes that were pushed in between.  This is only meant for use in #SPAUTO mode, which automatically adjusts the current value of the :SP internal symbol.

 

#PUSH and #PULL will save/restore the value of this setting.

#RENAME oldname,newname

#REMACRO oldname,newname

Renames a macro from its current (old) name to a new name.

 

An error message is issued if the old name is not a defined macro, the new name is a defined macro, or either name is an invalid symbol name.

 

#REMACRO is the same as #RENAME except that it does NOT check if the new name exists.  If it exists, there will now be one extra instance of that macro.  Note: The most recently defined macro of the same name is visible when more than one macro share the same name.  #DROP-ping the macro always drops the visible instance, making a possible previous instance now visible.

 

Tip: An example of where #RENAME might be useful:

Say, you have a library (or OS system) macro that is called many times in your application, but you want to modify that macro’s behavior just for this one application.  Your options are:

[1] Write a new (differently named) macro, and change all calls from the old macro to new macro.  Problem: If some of these calls are inside shared library code, you can’t change those calls, as it will affect other applications using those macros, as well.  Too much work, and error prone.

[2] Alter the library macro to include the new behavior.  Problem: Other applications may not like the new behavior.

[3] Use #RENAME in your application to have the old library macro change name just for this application’s sake.  Then, use the original name to write a brand new compatible macro but with the new behavior.  It is also now possible for the new macro to ‘borrow’ the functionality of the old macro (by calling it internally as needed), so the new macro doesn’t necessarily have to repeat the whole original macro body.  This allows for an easy way to extend or replace any general-purpose library macros for each application, separately.

 

Example for #REMACRO that allows front-ending a previous macro to add code before and after the macro call.

 

a         macro

          #Message  Inside original ~0~

          endm

 

a         remacro

          #Message  Inside inner ~0~

          #rename   ~0~,_{:totalmacrocalls}_

          @@a

          #remacro  ~0~,~self~

          #Message  Inside inner ~0~

          endm

 

a         remacro

          #Message  Inside outer ~0~

          #rename   ~0~,_{:totalmacrocalls}_

          @@a

          #remacro  ~0~,~self~

          #Message  Inside outer ~0~

          endm

 

          @a

          #Message  -----------------------------

          @a

#S19FLUSH

Forces the immediate termination of an S-record line when encountered, rather than waiting for the record to reach the size specified by the –Rn command line directive.  This directive may be used to make identification of the end of code blocks easier when viewing the *.S19 file.

#S1

#S2

#S2 forces the generation of S2 records (24-bit addresses) even for 16-bit addresses.  (#S1 reverts to normal, which produces S1 or S2 records based on address.)  Although 24-bit addresses are enabled, no MMU features are enabled.  Useful mostly for forcing 16-bit addresses to appear as 24-bit (with leading byte as $00) so that S19 loaders can use that as the PPAGE value.

Equivalent to the –S2- and -S2+ command line options.

#SIZE symbol[,expr]

Assigns the value of the expression to the size attribute of the specified previously-defined label.  If no expression is present, then the difference between the current location and the label is used (as if the expression was: *-LABEL) which is the most common use of this directive.  You can access the size attribute at a later time by using the internal symbol ::symbol (where symbol is the symbol whose size you want to get.)

#SP [expr]

#SP1 [expr]

#SPAUTO [expr][,expr]

#SPADD [expr]

#SP1 automatically adds one to all SP indexed offsets.  It does this without affecting the current value of the :SP internal symbol.

 

#SP without any expression cancels #SP1 and #SPAUTO modes (reverts to default/normal operation).

 

#SP followed by any expression (including a zero value) sets the :SP offset to the value of that expression but does not affect the current #SPAUTO mode.

 

#SPADD adds a [signed] number to the current value of the :SP offset (regardless of mode).  It does not reset the :SPCHECK variable, as with #SPAUTO.

 

When #SP1 is enabled, all SP indexed instructions use the same (zero-based) offsets as their corresponding X indexed instructions right after a TSX instruction.  This allows using the same [named or numeric] offsets for both addressing modes to access the same memory location(s)!

 

If the optional signed expression is present, its value will be added, also.  This makes it easier to adjust for any stack depth changes, such as for subroutines or in-line stack changes.

 

#SPAUTO (or its shorter alias, #SPA) will automatically adjust the offset based on the instructions used.  All push and pull instructions (including the extra ones) as well as all AIS instructions will automatically adjust the offset by as many bytes as required by each instruction.  Use the #SP directive (without any parameter offset, not even zero) to turn off the #SPAUTO mode and zero the SP offset (or, use #SPAUTO with the special #OFF# parameter to turn off the #SPAUTO mode without changing the current SP offset.)

 

Since v8.90 #SPAUTO takes an optional second argument (any valid constant expression).  If this value is specified, then the assembler (while in SPAUTO modes) will produce warnings when the stack depth increases beyond the value of the given expression.  This value will remain active until it is explicitly turned off by using –1 in a subsequent #SPAUTO directive (e.g., #SPAUTO ,-1).  The current value of this expression you can find in the internal variable :SPLIMIT

The maximum actual stack depth used will be in the :SPMAX variable which is reset only when :SPLIMIT is rewritten with a new value.  This allows to check the maximum stack depth for a single routine, a collection of routines (e.g., in a module), or the whole application.

 

Manual alterations of the stack size, however (such as when you push an extra byte per loop iteration) cannot be automatically detected as the assembler will not follow your code’s logic.  In those cases, you’ll have to adjust the offset ‘manually’ using #SPADD and an appropriate offset, like so:

 

#SPADD LOOPCOUNT-1

 

#PUSH and #PULL will save/restore the current setting of all modes of this option.

 

The assembler always starts in plain #SP mode (no offsets).

 

See also internal symbols :SP and :SP1 and the simulated indexed modes ,ASP and ,LSP

#SPCHECK

#SPCHECK checks the current value of the :SP internal symbol against the last used #SPAUTO value (found in :SPCHECK internal symbol), and issues a warning if the two numbers do NOT match, indicating a possible unbalanced stack situation (assuming correct placement of the relevant directives).

 

The current difference between :SP and :SPCHECK is found in :SPFREE (e.g., use with AIS #:SPFREE)

 

The warning also shows the number of bytes by which the stack is off.  This can be used as a first-line of defense against unbalanced stack coding errors, especially in situations where there is heavy manipulation of the stack, and a visual inspection may prove confusing.  Positive numbers indicate the stack contains so many extra bytes.  Negative numbers indicate the stack is missing so many bytes.

 

Hint: If you do not wish to use the #SPAUTO function for a particular section of code (or anywhere in your program) you can still temporarily place the #SPAUTO directive at the beginning of a code section to check, and the #SPCHECK at the end of the same code section, until you verify there are no related compilation warnings.  Then you can remove the two directives (possibly even with the use of conditional directives), and continue with other coding work.

 

See also #SPAUTO

#X [expr]

When #X is enabled (i.e., followed by a non-zero signed offset), all X indexed instructions will have that offset value automatically added to them (on top of whatever offset is actually specified with the instruction).  This has a lot of potential uses, such as pointer adjustments (after TSX), or anytime the same constant needs to be added to a series of X-indexed instructions within a block of code.

 

#PUSH and #PULL will save/restore the current setting of this option.

 

The assembler always starts in plain #X mode (no offsets).

 

See also internal symbol :X and the simulated indexed mode ,AX

#UNDEF symbol[,symbol]*

Undefines one or more symbols.  If a symbol is not currently defined, a warning will be issued (to protect from possible typing errors).

Careless, or simply wrong use of this directive can lead to multiple side errors or warnings (please note this is a two-pass assembler).

If you simply want to redefine the value of a symbol, prefer using the SET pseudo-op, rather than using #UNDEF followed by a repeated symbol definition.

#UNDEF can be used, for example, to completely remove unrelated or conflicting conditionals.

#PAGE

Outputs a Form Feed (ASCII 12) character followed by a Carriage Return (ASCII 13) in the *.LST file just before displaying the line that contains this directive.

#PUSH

Pushes on an internal stack the current segment and the current settings of the following directives: MAPx, LISTx, CASEx, EXTRAx, SPACESx, OPTRELx, OPTRTSx, [NO]WARN, HCSx, MMU, JUMP, CALL, S1, S2, SP1, SP, SPAUTO, X, TRACEx, MACRO, @MACRO, MCF, MACROSHOW, MACROHIDE, various SP-based offsets (eg., :AIS) , :PSP, :PPC:, TRACE[ON/OFF], MLISTx and TABSIZE.  Useful in included files that want to change any of these options without affecting parent files.  See also #PULL

#PULL

Pulls from an internal stack the most recently pushed options.

See also #PUSH

#RAM

Activation of the RAM segment. Default starting value is $0080.

#ROM

Activation of the ROM segment. Default starting value is $F600.  This is the default segment if none is specified.

#SEGn

Activation of the SEGn segment (n is a number from 0 through 9).  Default starting value for all ten segments is $0000.

#TABSIZE n

Specifies the field width of tab stops used in the source file.  Proper use of this directive ensures that the *.LST files generated by ASM8 are formatted in the same way as your source files appear in your text editor.  This directive overrides the setting of the –Tn command line option for the source file(s) in which it is encountered.

#TEMP [expr]

#TEMP simply assigns any value (possibly the result of an non-forward expression) to the internal general-purpose :TEMP variable.  If no expression follows #TEMP, :TEMP is zeroed.

 

:TEMP can be used any time in lieu of defining any ‘helper’ symbol for intermediate calculations (either inside or outside macros).  The only restriction is that :TEMP always refers to the most recent #TEMP directive, so it cannot be used to look forward.

 

Although :TEMP is a single variable, its use is transparent in relation to macros.  In other words, changing :TEMP from within any macro does not affect the value of :TEMP outside all macros, or macros above the current level.

 

Although macros inherit their initial value of :TEMP from their higher level (either a caller macro, or normal code), they do not affect their parent’s :TEMP value, so you can use it without worrying about side effects from any intermediate macro calls.

 

:TEMP is also assigned indirectly when used as label with th any of the following directives/pseudo-ops: NEXT, NEXP, SETN, and #AIS

#TRACEON

#TRACEOFF

#TRACEON enables generation of source-line information in the *.MAP file for any code found in the body of macros following this directive.  The map info is generated in such a way that while tracing the debugger will display the actual source of the macro.  This can be used globally (to affect all macro invocations), inside a specific macro (to debug that one macro), or around a specific macro invocation (to debug that one macro call.)

#TRACEOFF turns this option off making macros appear as a single line in the debugger.  #TRACEOFF is the default state when assembly is started.

#VARIABLE addr1 [addr2]

#VARIABLE #OFF#

 

Maps a location (or range, if addr2 is also supplied) of variable allocation area (normally in RAM) as valid.  Use multiple directives to specify additional ranges.  Any RMB or DS definitions that fall (fully or partially) outside the given range(s) will produce a warning (if the -O option is enabled) for each such definition.  Addr1 and addr2 may be specified in any order.  The range defined will always be between the smaller and the higher values.

The special keyword #OFF# removes all current definitions.

See also #MEMORY

#VECTORS

Activation of the VECTORS segment. Default starting value is $FFC0.

#WARN

Turns warnings on.  Equivalent to the -WRN+ command line option.  See also #NOWARN

#WARNING [text]

Similar to the #ERROR directive, but generates an assembler warning message instead of an error message.

#XRAM

Activation of the XRAM segment.  Default starting value is $2000.

#XROM

Activation of the XROM segment.  Default starting value is $EC00.

 

Note: [text] in directives and all strings may contain nested expressions enclosed in curly brackets, e.g. {expr}.  The expression may not contain spaces (regardless of the –SP option state, or #SPACESON directive.  An optional format modifier (case-insensitive) within parentheses after the expression can force the display in the specified format.  (D) for default/decimal, (H) for hex, (S) for signed decimal, (1) thru (4) (or, thru (9) for the 32-bit versions) for the corresponding number of decimal places after division by 10n where n is a number from 1 to 4 (or 9), (X) for expanded, (Fn) for space left filled, and (Zn) for zero left filled, where n is optional (default is 2) and can range from 1 to 0 (0 meaning 10).  If no format modifier is used, (D) is assumed.  Some examples using this feature:

 

ROM EQU $F000

#Message ROM is at {ROM}

will display:

ROM is at 61440

 

Adding a format modifier will have the following effect:

 

#Message ROM is at {ROM(x)}

will display:

ROM is at 61440 [$F000]

 

#Message ROM is at {ROM(d)}

will display:

ROM is at 61440

 

#Message ROM is at {ROM(h)}

will display:

ROM is at $F000

 

#Message ROM is at {ROM(s)}

will display:

ROM is at -4096

 

#Message Clock: {:year}-{:month(z)}-{:date(z)} {:hour(z)}:{:min(z)}:{:sec(z)}

will display something like:

Clock: 2013-11-05 13:00:00

 

It can also be used in strings, like so:

 

VERSION equ 101 ;Firmware version as x.xx

MsgVersion fcs ‘Firmware v{VERSION(2)}’,LF

is equivalent to

MsgVersion fcs ‘Firmware v1.01’,LF

 

but it will automatically adjust the MsgVersion string each time the symbol VERSION changes value.  No need to re-adjust all relevant messages manually.  The potential uses of this capability are only limited by imagination.

 

An expression that cannot be evaluated (due to forward references or undefined symbols) will display as three question marks (???) when used in directives, but no error or warning message will be issued.  When used in strings, however, errors will be displayed as usual.

 

To prevent an expression evaluation in directives, enclose the [text] that contains the curly brackets within quotes.

 

To prevent an expression evaluation in strings, break the string into two so that both curly brackets are not part of the same string, e.g.:

 

instead of fcc ‘{Hello}’ which tries to evaluate the symbol Hello use: fcc ‘{‘,’Hello}’.

 

Internally defined symbols

 

Some special internal symbols are defined by the assembler.  All such symbols begin with a colon (:) character.  Currently, the following internal symbols are defined:

 

§  :: (without a symbol following) returns the current (dynamically assigned) stack offset.  Very useful mostly in #SPAUTO mode so that you can assign labels to stack contents as they are created.  (Same as 1-:SP in #SP[AUTO] modes, or 0-:SP if in #SP1 [sub-]mode.)  Note: Beginning with v5.70 if any push instruction is followed by a label, that label will be SET to the current :: value (must be in #ExtraOn mode).

§  ::symbol (where symbol is any previously defined symbol) returns the current ‘size’ for the given symbol.  A symbol’s size is determined either automatically (e.g., RMB pseudo-instructions), or manually via the #SIZE directive.

§  :SP returns the current offset of the #SP or #SP1 directives.  This value is the basis for several other internal symbols.

§  :SPLIMIT returns the currently effective value of the #SPAUTO stack depth check option (i.e., the optional 2nd parameter of the #SPAUTO directive.) #PUSH/#PULL save/restore this value.

§  :SPMAX returns the maximum used stack depth since the last time :SPLIMIT was explicitly set (even if to the same value it had already.)  You can use this internal variable to find the maximum stack depth for a single routine, a collection of routines (e.g., a whole module), or even your whole application’s.  Keep in mind, however, that it only counts stack depth in a linear fashion, i.e., without considering possible subroutine calls, recursion, or other indirect methods of altering the stack, such as the LDHX #STACKTOP / TXS sequence.

§  :SPX returns :SP-1 when in #SP[AUTO] modes and :SP-0 when in #SP1 [sub-]mode. Useful with #X as in #X :SPX.  Alternatively (and preferably), you may use the ,SPX simulated indexed mode, which does not depend on the :SPX value, and which is actually X-indexed mode but stack-relative to the most recent TSX instruction, and possible subsequent AIX and MOV X+ instructions. (Note: there are many ways to alter the contents of the HX index register; the assembler cannot automatically account for all those possibilities; use #X expr where needed to manually adjust the offset, and use the plain X-indexed mode).  This feature provides a very simple way of optimizing any ,SP relative instruction to ,X relative (by simply appending an X to ,SP making it ,SPX and using TSX anywhere before these instructions.  All offsets are automatically adjusted.)

§  :TSX is similar to :SPX but, although relative to the most recent TSX instruction, and possible subsequent AIX and MOV X+ instructions (like :SPX), it disregards possible following stack depth changes, unlike :SPX. (Note: there are many ways to alter the contents of the HX index register; the assembler cannot automatically account for all those possibilities; use #X expr where needed to manually adjust the offset, and use the plain X-indexed mode).

§  :SPCHECK returns the actual offset used with the most recent #SPAUTO directive.

§  :SPFREE returns the current stack depth change (same as :SP-:SPCHECK).  For example, you may use it with the AIS instruction to free so many bytes of stack.    (The symbol :SP alone will not work for this purpose – releasing remaining stack bytes – unless #SPAUTO is used with a zero offset, while :SPFREE works, regardless of the initial offset.)

§  :AIS returns the current stack depth change since and including the last stack-increasing AIS instruction (or #AIS directive when given without any parameters).  For example, you may use it with a new [normally, stack-reducing] AIS instruction to free so many bytes of stack.  :AIS is updated automatically after each stack-increasing AIS instruction, i.e., a negative AIS instruction with an actual instruction operand of $80 to $FF, losing whatever previous value was in :AIS, and it can be used to free whatever stack bytes remain since the last stack-increasing AIS instruction (used like so:, AIS #:AIS).  #SP, #SPAUTO, and #SP1 reset the :AIS symbol to zero or the value of the parameter used with the corresponding directive until the next AIS instruction.

§  :PSP returns the current stack depth change since the last #PSP directive.  For example, you may use it with the AIS instruction to free so many bytes of stack.  Unlike :SPFREE which is related to the automatically updated :SPCHECK during any #SPAUTO directive, :PSP is only updated manually with the #PSP directive, and can be used locally (eg., around a sub-routine call) to free the number of stack bytes for only a specific section of code (eg., whatever parameters were pushed on the stack for use by the sub-routine).

§  :SP1 returns the current offset of the #SP or #SP1 directives (like :SP), but also adds one only if we’re currently in the #SP1 mode.  This value is always the true effective offset for both #SP and #SP1 modes.

§  :AB returns the number of Address Bytes (useful for stack offsets).  2 for normal, 3 for MMU

§  :X returns the current offset of the #X directive.

§  :YEAR returns the year at assembly time (e.g., 2017)  Hint: Use :YEAR\100 for two-digit year.

§  :MONTH returns the month at assembly time (e.g., 9)

§  :DATE returns the date at assembly time (e.g., 29)

§  :HOUR returns the hour at assembly time (e.g., 13)

§  :MIN returns the minute at assembly time (e.g., 0)

§  :SEC returns the second at assembly time (e.g., 0)

§  :CPU returns a number representing the CPU type (6808 or 908)

§  :CRC returns the current value of the running user CRC

§  :S19CRC returns the current value of the running S19 CRC.

§  :PAGE_START returns the page window’s starting address.

§  :PAGE_END returns the page window’s ending address.

§  :CYCLES returns the current value of the cycles counter, and then it is reset to zero.

§  :OCYCLES returns the older value of the cycles counter (but does not reset it).

§  :TOTALMACROCALLS returns the current value of the total macro invocations.  Use it for display, or even to restrict macro use (e.g., #IFNZ :TOTALMACROCALLS … #ERROR No macros allowed for this application … #ENDIF).

§  :MACRONEST returns the current value of the macro (chain) ‘loop level’ regardless if calling the same, or a different macro (think of it as the ‘nesting level’).  A value of zero is returned if used outside any macros.  First level is number 1.  Each time the top-level macro is called, the number is reset to 1.  Each time the same or a different macro is called from within the current macro, the number is incremented by 1.  The macro (chain) can also initialize itself during, say, count one.

§  :MACROLOOP (or, simply, :LOOP) is similar to :MACRONEST but it returns the current value of the macro ‘loop level’ only for the current macro.  A value of zero is returned if used outside any macros.  First level is number 1.  Each time the macro is called from outside any macros, or from a different macro, the number is reset to 1.  Each time the macro calls itself (by either a chained macro call, or the MTOP directive), the number is incremented by 1.  This can be used as an automatic loop counter.  The macro can also initialize itself during, say, count one.  This differs from :MACRONEST in that chained macro calls will restart this counter for each new macro.  This counter is also reset with a %macro syntax call.

§  :MEXIT holds the most recent MEXIT defined value.  :MEXIT is reset to zero each time a macro is (re)entered, but its value can be changed by MEXIT instructions that specify an explicit expression.  This feature can be used to pass back to the higher level any value from inside a (nested) macro (such as success/error status, the result of some computation, etc.) without using any label definitions.

§  :MLOOP is similar to :LOOP but it is only affected by the MDO and MLOOP keywords.  First count is number 1.  Each time the MDO keyword is encountered, the number is reset to 1.  Each time the MLOOP keyword is encountered, the number is incremented by 1.  This can be used as an automatic loop counter.  This counter is also reset with a %macro syntax call.

§  :MACROINDEX (or :MINDEX) returns the current value of the current macro’s number of invocations.  A value of zero is returned if used outside any macros.  First call of each macro is number 1.  If the specific macro is dropped and re-created, the number is reset (it is, after all, a new macro).  An example use is to create different labels at each invocation (not to be confused with automatic $$$ label generation, which assumes values based on :TOTALMACROCALLS and cannot be guaranteed to take sequential values between consecutive calls of the exact same macro since other macros may have increased the counter in between), or instruction offsets (e.g., with the special ad-hoc macro named “?”), etc.  This counter is also reset with a %macro syntax call.

§  :INDEX returns the next value of the current macro’s internal user index.  A value of zero is returned if used outside any macros.  First use in each macro is number 1.  If the specific macro is dropped and re-created, the number is reset (it is, after all, a new macro).  Its use is similar to :MACROINDEX but there is a significant difference.  :INDEX is only updated each time it is accessed, regardless of how many times the macro is actually called.  So, if used inside a conditional block of code, it will only be incremented when that part is expanded.  Note: Because of the auto-increment on access, if you want to use the same value more than once in the same macro invocation, you must first assign the value to some label, and then use the label, instead.  This counter is also reset with a %macro syntax call.

§  :0 to :9 return the length of the text of the corresponding macro parameter.  You can use it alone or along with the ~n.s.l~ parameter placeholder.  This can only be used from within a macro.  It is not recognized as valid symbol outside a macro.

§  :DOW returns the day-of-week number at assembly time, from zero (Sunday) to six (Saturday).

§  :PC returns the current program counter (same as *) but can be used even in expressions where the use of * is ambiguous.

§  :PPC returns the previously saved program counter (see the #PPC directive).  It can be used to get the byte distance between any two points without having to define a symbol just for this.  It is also useful inside frequently called macros; for example, to avoid the use of a macro local label definition for simple loops (helps keep the symbol table smaller in large applications).

§  :PROC returns the current value of the internal local symbol counter (See PROC and #PROC).

§  :MAXPROC returns the currently maximum value of the internal local symbol counter (See PROC and #PROC).

§  :LABEL returns the length of the ~label~ placeholder’s content used inside macros.

§  :TEMP returns the current value of this internal user-defined assembly-time variable.  (See the #TEMP directive for more details.)

§  :TEXT returns the length of the current text of the ~text~ macro parameter (only from within macros.)

§  :LINENO returns the current file line number.

§  :MAXLABEL returns the current value of the maximum label length.

§  :MLINENO returns the current macro line number (only from within macros).

§  :N returns the current macro number of contiguous arguments (only from within macros).

§  :NN returns the current macro number of all arguments (only from within macros).

§  :ANRTS returns the address of the most recent RTS instruction (i.e., always points back).

§  :ANRTC returns the address of the most recent RTC instruction (i.e., always points back).

§  :ROM, :RAM, etc. All segment directives have a corresponding internal variable that returns the current value of that segment.

§  :OFFSET returns the current S19 addressing offset from the physical address (see ORG).

§  :WIDTH returns the current width of the console screen.  Useful for formatting user messages.

 

Notes about :SP and :SP1:

 

:SP returns the current automatic SP offset (the same for both #SP and #SP1 modes).  It will NOT account for the extra ‘plus one’ of the #SP1 mode, however.  Use it to adjust the current SP offset manually, or to work with labels that are dependent on the current #SP or #SP1 mode; for example, the current level labels while a one-based or zero-based offset is in effect.  The local labels will still need to be in the same (zero or one) base, however.  You can also use the simulated indexed mode ,LSP (Local SP) to get the same effect.

 

:SP1, on the other hand, returns the current automatic SP offset just like :SP but which will be ‘plus one’ if we’re currently in #SP1 mode.  This is always the true difference between automatic and actual stack offset, regardless of mode.  You may also use the simulated indexed mode, ,ASP (Absolute SP) to get the same effect.

 

For example, use ,ASP (or –:SP1) to cancel out all offsets for dealing with absolute numbers:

 

#sp1   SomeValue

...

?LocalA                    equ    1

pshx

cmp    1-:sp1,sp            ;absolute offset

cmp    ?LocalA-:sp1,sp      ;absolute offset

cmp    ?LocalA,asp          ;absolute offset

pulx

 

All three CMP instructions above will compare against the stacked value from the immediately preceding PSHX instruction, regardless if the directive earlier is #SP or #SP1, and regardless of the presence or value of the optional parameter SomeValue.  It is equivalent to CMP 1,sp when #SP mode is off, and you can use it regardless of the current #SP mode and offset, and even if the #SP directive is never used, use it to lock the offsets (so that possible future #SP/#SP1 automatic offsets in related code will not affect these lines).

 

This feature is most useful when a section of code is under #SP control (say, because most instructions refer to the parent routine’s stack frame, and coding becomes simpler and more readable under #SP control) but you temporarily want to access local stack without any automatic offsets.  That way, you don’t have to turn off #SP/#SP1 mode just for one instruction, or so.

 

To switch from zero-based label [or numeric] offsets to one-based (normal) stack offsets, without changing the current stack depth (automatic SP offset), you must do this:

 

#sp           :sp

 

Similarly, to switch back to zero-based offsets without changing the current automatic SP offset (current stack depth), you must do this:

 

#sp1          :sp

 

You can also use the :sp internal symbol to adjust X-indexed offsets after a TSX to refer to higher stack levels (say, a parent routine).  For example:

 

MyOffset                          equ           0                    ;zero-based offset

 

ais           #-1                  ;allocate temp space

...

tsx

sta           MyOffset,x           ;save to local stack

bsr           Sub

...

#sp1          2                    ;account for RTS (zero-based)

 

Sub                               tsx

lda           MyOffset,sp          ;gets A from parent stack

lda           MyOffset+:sp,x             ;(equivalent to above)

 

Examine the assembler listing on the following page to see the corresponding produced offsets for each case.

    1      0000                 ?                   equ       0

    2                          

    3      F600                                     org       *

    4                          

    5                                              ;#sp                           ; default mode, no offsets

    6                          

    7 F600:9EE6 01         [ 4]                     lda       1-:sp,sp            ; SP/SP1 relative offset

    8 F603:9EE6 01         [ 4]                     lda       1,lsp               ; SP/SP1 relative offset

    9 F606:9EE6 01         [ 4]                     lda       1-:sp1,sp           ; absolute offset

   10 F609:9EE6 01         [ 4]                     lda       1,asp               ; absolute offset

   11 F60C:9EE6 00         [ 4]                     lda       ?,sp

   12                          

   13                                               #sp1                          ; zero-based offset mode

   14                          

   15 F60F:9EE6 02         [ 4]                     lda       1-:sp,sp            ; SP/SP1 relative offset

   16 F612:9EE6 02         [ 4]                     lda       1,lsp               ; SP/SP1 relative offset

   17 F615:9EE6 01         [ 4]                     lda       1-:sp1,sp           ; absolute offset

   18 F618:9EE6 01         [ 4]                     lda       1,asp               ; absolute offset

   19 F61B:9EE6 01         [ 4]                     lda       ?,sp

   20                          

   21                                               #sp       10                  ; one-based plus 10

   22                          

   23 F61E:9EE6 01         [ 4]                     lda       1-:sp,sp            ; SP/SP1 relative offset

   24 F621:9EE6 01         [ 4]                     lda       1,lsp               ; SP/SP1 relative offset

   25 F624:9EE6 01         [ 4]                     lda       1-:sp1,sp           ; absolute offset

   26 F627:9EE6 01         [ 4]                     lda       1,asp               ; absolute offset

   27 F62A:9EE6 0A         [ 4]                     lda       ?,sp

   28                          

   29                                               #sp1      :sp                 ; zero-based plus :SP (10)

   30                          

   31 F62D:9EE6 02         [ 4]                     lda       1-:sp,sp            ; SP/SP1 relative offset

   32 F630:9EE6 02         [ 4]                     lda       1,lsp               ; SP/SP1 relative offset

   33 F633:9EE6 01         [ 4]                     lda       1-:sp1,sp           ; absolute offset

   34 F636:9EE6 01         [ 4]                     lda       1,asp               ; absolute offset

   35 F639:9EE6 0B         [ 4]                     lda       ?,sp

 

Notes about :AIS:

 

:AIS returns the number of stack bytes still allocated since the most recent stack-increasing AIS instruction (normally useful only in #SP[AUTO] modes).  Example usage:

 

                    #spauto                       ;auto mode and zero offset

 

                    push                          ;protect all registers

 

                    ais       #-7                 ;(negative AIS marks the current :SP)

                    ...

                    ais       #2                  ;de-allocate some locals (no marking)

                    ...

                    pula                          ;de-allocate some locals (no marking)

                    ...

                    ais       #:ais               ;deallocate however many bytes (4 in this case)

                                                  ;still left on stack since negative AIS

                    pull                          ;restore all registers

                    rts

 

                    #sp                           ;cancel auto mode and offsets

 

Notes about #SPAUTO:

 

Besides providing a method for automatic adjustment of stack offsets that refer outside/within a certain block of code (so they remain ‘apparently’ constant), #SPAUTO can also be used to automatically de-allocate the correct number of stack bytes at the end of a block of code.  This allows for easier and more accurate coding (since no mental accounting is required) and possible future changes in existing code do not require adjustment of any ‘usually’ affected instructions.

 

The following is an example of code that shows both of the above uses:

 

                    #spauto                       ;auto mode and zero offset

 

                    ldhx      Size,sp

                    pshhx

                    ldhx      ToAddress,sp        ;no need for: ToAddress+2,sp

                    pshhx

                    ldhx      FromAddress,sp      ;no need for: FromAddress+4,sp

                    pshhx

                    call      CopyMemory          ;call the action routine

                    ais       #:spfree            ;deallocate however many bytes

                                                  ;were allocated for parameters

                    ...

                    #sp                           ;cancel auto mode and offsets

 

Now, as an example, if you wanted to add yet another set of parameters to the CopyMemory routine (assuming the routine was modified to accept them) you would add some extra push instructions to the above code (shown in orange in the modified code below), but no other code or offsets would need adjusting.  The same example becomes:

 

                    #spauto                       ;auto mode and zero offset

 

                    ldhx      #SkipTo             ;new instruction added later

                    pshhx                         ;new instruction added later

                    ldhx      #SkipFrom           ;new instruction added later

                    pshhx                         ;new instruction added later

                    ldhx      Size,sp

                    pshhx

                    ldhx      ToAddress,sp        ;no need for: ToAddress+2,sp

                    pshhx

                    ldhx      FromAddress,sp      ;no need for: FromAddress+4,sp

                    pshhx

                    call      CopyMemory          ;call the action routine

                    ais       #:spfree            ;deallocate however many bytes

                                                  ;were allocated for parameters

                    ...

                    #sp                           ;cancel auto mode and offsets

 

To appreciate how #SPAUTO can help, look at the same changed code but without #SPAUTO, where all three SP indexed instructions plus the AIS instruction would have to have their offsets appended with an extra +4 (not to mention the original extra offset that must be in place as stack grows with each push).  After a while this can get messy and error-prone.

 

                    ldhx      #SkipTo             ;new instruction added later

                    pshhx                         ;new instruction added later

                    ldhx      #SkipFrom           ;new instruction added later

                    pshhx                         ;new instruction added later

                    ldhx      Size+4,sp

                    pshhx

                    ldhx      ToAddress+2+4,sp    ;2 (original) +4 (for new)

                    pshhx

                    ldhx      FromAddress+4+4,sp  ;4 (original) +4 (for new)

                    pshhx

                    call      CopyMemory          ;call the action routine

                    ais       #6+4                ;deallocate 6 (original)

                                                  ;+4 (for new)

 

With automatic SP offsets, you can’t be sure at all times of the actual offset used (after all, the idea is to let the assembler decide automatically), so certain optimizations that depend on specific stack offsets may no longer work if stack ordering is later changed.  For example:

 

                    pshhx                         ;Place number to divide on stack

 

                    ldhx   #10

                    pula

                    div                           ;MSB (assuming it is at TOS)

                    psha

                    lda       2,sp

                    div                           ;LSB

                    sta       2,sp

                    pulhx                         ;deallocate however many bytes

                                                  ;were allocated for parameters

 

will work.  But, if it were changed to:

 

                    pshhx                         ;Place number to divide on stack

 

                    #spauto                       ;auto mode and zero offset

 

                    psha                          ;added later to protect A

 

                    ldhx   #10

                    pula

                    div                           ;MSB (assuming it is at TOS)

                    psha

                    lda       2,sp

                    div                           ;LSB

                    sta       2,sp

 

                    pula                          ;added later ...

                    ...

                    #sp                           ;cancel auto mode and offsets

 

it will no longer work because PULA/PSHA (shown in orange) always work on the top-most stack element (#SPAUTO cannot alter those instructions), which in this case is no longer the number we want to divide (as PSHA before LDHX – shown in green – has placed its own value on the top-of-stack, as we now decided to protect register A from destruction during the division operation) moving the MSB of the number we want to divide to the true offset 2,SP.

 

Here’s then what to do to have the assembler use the optimization but only if the TOS (top-of-stack) has the number:

 

                    #spauto                       ;auto mode and zero offset

 

Routine             proc

                    pshhx                         ;Place number to divide on stack

number@@            set       ::                  ;Give name to just-stacked number

 

;;;;;;;;;;;;;;;;;;; pshhx     number@@            ;Equivalent to previous two lines combined (#ExtraOn required)

 

                    psha                          ;added later to protect A

 

                    ldhx   #10

        #iftos number@@

                    pula

                    div                           ;MSB (assuming it’s at TOS)

                    psha

        #else

                    lda       number@@,sp

                    div                           ;MSB

                    sta       number@@,sp

        #endif

                    lda       number@@+1,sp

                    div                           ;LSB

                    sta       number@@+1,sp

 

                    pula                          ;added later ...

 

                    ...

                    ais       #:spfree            ;deallocate however many bytes

                                                  ;still allocated on stack

                    ...

                    #sp                           ;cancel auto mode and offsets

 

Here, we use #SPAUTO outside the whole code block.  We then assign a name to the stacked number (two methods shown, 2nd one commented out) using the :: internal variable (which always – i.e., regardless of current stack depth and the #SP/#SP1 setting of #SPAUTO mode – refers to the top-of-stack item, so it must be placed immediately following the stack operation – i.e., before another stack altering operation).  Finally, using conditional assembly (#iftos), we test for the number@@ sp offset having the effective value 1 (i.e., TOS), and if so, we can use the optimization, otherwise we code it as if it were any other stack offset.  (This is particularly useful in general-purpose macros, where it’s not possible to know in advance if the operand is going to be the TOS or not, but we want to have the best possible code generation for each case.)  If the instruction pair PSHA/PULA (shown in green) is later removed, no changes are required to any of the remaining code.  The PULA/PSHA optimization will automatically be activated, and due to #SPAUTO, all offsets will be adjusted accordingly.

 

The above example also introduces the use of the :: internal symbol.

 

The special :: internal symbol (a shortcut for the equivalent expression 1-:SP for #SP[AUTO] modes, or 0-:SP for #SP1 mode) is a very useful dynamic symbol for quickly and easily (i.e., without any mental calculations) assigning labels to any stacked content, and at the exact time you push anything on the stack (no need to have your stack frame organized in advance, just define it as you use the stack).  Because of the automatic SP adjustment while in the #SPAUTO mode, a symbol defined this way will always point to the correct location in the stack.  No possibility of user error (assuming correct placement of #SPAUTO and/or related directives).  This symbol will always point to 1,SP if used outside of #SP[AUTO] modes (or 0,SP in #SP1 mode – which is practically useless), and a warning will be issued whenever you use a stack-related internal symbol outside of a relevant SP-adjusting mode.

 

To use, always start a sub-routine with #SPAUTO.  Any stack offsets ‘above’ the current routine (normally, that would be in the parent routine’s stack) will be defined ‘before’ the #SPAUTO directive (not necessarily ‘physically before’ but ‘logically before’, i.e., based on a fixed starting offset value, usually the value one, and NOT the dynamic value of :SP), while stack contents within the routine will be defined ‘after’ the #SPAUTO directive using the :: internal symbol as a base offset.  An #SP directive after the end of the sub-routine turns all automatic SP adjustments off.

 

You may even redefine a stack-offset symbol (using the SET pseudo-instruction) and use it again with an updated stack location to prevent accidentally accessing the wrong stack item.  For example, copying a parent routine’s variable to local stack so that you may change the local copy without affecting the parent stack’s variable is as simple as:

 

                    #spauto                       ;auto mode without any offsets

 

Parent              proc

                    pshhx     ?Parm               ;Place parm on stack and name it

                    ...

?SP_before_call     equ       :sp                 ;Mark SP offset before call

                    bsr       Child

                    ...

                    sthx      ?Parm,sp            ;Affects Parent’s stack variable

                    ...

                    rtc

 

                    #spauto   2+?SP_before_call   ;auto mode plus RTS and parent SP offsets

 

Child               proc

                    ldhx      ?Parm,sp            ;Get parm from parent’s stack

                    pshhx     ?Parm               ;Place parm on local stack and name it

                    ...                           ;(Name “?Parm” now refers to Child’s copy)

                    sthx      ?Parm,sp            ;Affects Child’s stack variable

                    ...

                    rts

 

                    #sp                           ;cancel auto mode and offsets

 

Once you see in practice how this (#SPAUTO and related language extensions) can help you maintain the correctness of all stack references in a program as you alter stack operations (e.g., add new push/pulls, or change the order of push/pulls – you’re still responsible for keeping the correct LIFO order yourself, however), you may be surprised how you ever managed without it.  Certainly, I was!

 

Several coding examples that collectively implement all the features described here can be found at http://www.aspisys.com/code followed by the link shown on that page, for the corresponding example.  (To prevent auto-cloning of these pages, you may have to manually type the rest of the URL, as no direct links may be provided for each separate example.)

 

Notes about :X and #X mode.

 

:X will return the current automatic X offset.  This allows you to get the true difference between automatic and actual X offset.    You can also use the simulated indexed mode ,AX (Absolute X) to get the same effect.

 

As an example, you can use the #X mode to adjust X-indexed offsets after a TSX to refer to higher stack levels (e.g., parent routine).  For example:

 

MyOffset                          equ           0                    ;zero-based offset

 

ais           #-1                  ;allocate temp space

...

tsx

sta           MyOffset,x           ;save to local stack

bsr           Sub

...

#sp1          2                    ;account for RTS (zero-based)

#x            :sp

 

Sub                               tsx

lda           MyOffset,sp          ;gets A from parent stack

lda           MyOffset,x           ;(equivalent to above)

 

Notes about :cycles:

 

§  The cycles counter is reset to zero right after it is accessed.  To count cycles for a section of code, you must access :cycles twice, once before the code section to reset its value to zero (if not already zero from a previous access to :cycles or a #CYCLES directive), and once right after the code section to get the accumulated cycles.

§  Because of the auto-reset on access, if you need to use the same value in more than one place at a time (e.g., code and #MESSAGE directive), you must assign it to a label first, then use the label.

§  The obvious advantage is that if you alter code as in the example loop below (e.g., by adding conditional early escape code inside the loop), it will still be timed correctly without requiring a manual adjustment of the delay constant.  Another advantage is that you do not have to keep separate cycle counts for 9S08 and HC08 families.  A third advantage is that conditionally enabled code will be accounted for correctly in all cases, again without requiring a manual recalculation for each conditional case.

§  Example use of :cycles that automatically calculates the appropriate delay constant:

 

                    #Cycles                       ; reset cycles counter

Delay10ms           pshhx

                    ldhx      #10*BUS_KHZ-?ExtraCycles/?LoopCycles

?ExtraCycles        equ       :cycles             ; grab counter (and reset)

?Delay.Loop         aix       #-1

                    cphx      #0

                    bne       ?Delay.Loop

?LoopCycles         equ       :cycles             ; grab counter (and reset)

                    pulhx

                    rts

?ExtraCycles        set       ?ExtraCycles+:cycles

 

(SET instead of EQU allows re-using symbols, so you can use it to accumulate related cycles.)

Example assembly code for calculating user CRC

 

;*******************************************************************************

; Purpose: Calculate the same user CRC as that produced by ASM8

; Input  : StackLo = StartAddress

;        : StackMd = EndAddress

;        : StackHi = Initial/Previous CRC

; Output : Stacked CRC updated

; Note(s): Call repeatedly for different address ranges, if skipping sections

; Call   :          ldhx      #CRC_SEED

;        :          pshhx

;        :          ldhx      #EndAddress

;        :          pshhx

;        :          ldhx      #StartAddress

;        :          pshhx

;        :          call      GetAsmCRC

;        :          ais       #4

;        :          pulhx                         ;HX = CRC

 

?                   set       1

?StartAddress       next      ?,2

?EndAddress         next      ?,2

?CRC                next      ?,2

 

                    #spauto   :ab                 ;account for [RTS/RTC]

 

GetAsmCRC           push

 

?GetAsmCRC.Loop     ldhx      ?StartAddress,sp

                    cphx      ?EndAddress,sp

                    bhi       ?GetAsmCRC

 

                    lda       ,x

          #ifdef COP

                    sta       COP                 ;in case of many iterations

          #endif

                    beq       ?GetAsmCRC.Next

                    cbeqa     #$FF,?GetAsmCRC.Next

 

                    mul                           ;low address with data byte

                    add       ?CRC+1,sp

                    sta       ?CRC+1,sp

                    txa

                    tsx

                    adc       ?CRC,spx

                    sta       ?CRC,spx

 

                    ldhx      ?StartAddress,sp

                    lda       ,x

                    thx

                    mul                           ;high address with data byte

                    tsx

                    add       ?CRC,spx

                    sta       ?CRC,spx

 

?GetAsmCRC.Next     tsx

                    inc       ?StartAddress+1,spx

                    bne       ?GetAsmCRC.Loop

                    inc       ?StartAddress,spx

                    bra       ?GetAsmCRC.Loop

 

?GetAsmCRC          pull

                    rtc

 

                    #sp

Example coding for skipping CRC calculation for volatile sections

 

?crc                set       :crc                ;use SET, not EQU

 

       ;CODE/DATA TO SKIP FROM CRC CALCULATION HERE

 

                    #CRC      ?crc

Expression Operators and Other Special Characters

Recognized by ASM8

 

·         Expressions are evaluated in the order they are written (left to right).
All operators have equal precedence.