ASHC11 - 68HC11 Macro Assembler

    ASHC11 is a DOS based macro assembler for the Motorola 68HC11 and similar processors. It has been used by the author to generate code for a number of vehicle applications, particularly for the GM C3, P4 and higher EC Us. ASHC11's Features include a very capable macro facility and additional (Intel style) mnemonics. The author's companion disassembler DHC11 creates output that can be used immediately by ASHC11. The author's binary conversion utility BINCVT may be used to convert S19 or Hex records to a binary image, and GMCKS to create GM checksums from binary images.

    Get version 1.2 of ASHC11 here.

The assembler is described in enough detail here to enable anyone familiar with assembler concepts to begin using it immediately. It was designed to be largely compatible with existing HC11 assemblers and in particular the Motorola freeware assembler (available here as as11.exe). Note that ASHC11 is not a "relocating" assembler, and produces S19 or Hex output only.

As with a lot of free software, the documentation is never complete. We're sure you'll find enough information here to effectively use ASHC11 as long as you've used some kind of assembler before.

Features of the Macro Assembler.

This section briefly lists the assembler's features as compared with the standard Motorola freeware assembler. A tutorial style in following sections expands on the syntax and usage of these features. See an explanation of why I wrote this assembler. Your feedback is appreciated.

Enhanced Expression Analysis - using simple left to right parsing with the addition of brackets allowing arbitrarily complex expressions to be created on a single assembly line. No complex hierarchy rules need be learnt. White space can be freely used to make expressions easier to analyse. Multiple formats are supported for different number base schemes (eg. 100B, or %100 are equivalent).

MACRO support has been added. Macros may have embedded definitions (ie. macros defined within another macro) and may call other macros (including themselves) from within the macro definition. LOCAL labels used to avoid symbol name clashes may be defined within macros. Compound arguments (ie. arguments containing embedded arguments) may be passed to macros. Early macro exit is possible with the EXITIF clause.

Repeat Blocks using REPT, IRPC and IRP can be declared. REPT blocks repeat a section of code a certain number of times. IRPC blocks repeat a block of code and iterate each pass with the next character from a supplied string. IRP is supplied with a list of arguments that are used successively for each iteration. LOCAL labels can also be used within repeat blocks.

Conditional Assembly is supported with the standard IF, ELSE, ENDIF construct. An ELSEIF clause allows more complex expressions to be expressed quite simply. An IFD and IFND adds a test for symbols being defined or not. Conditional expressions can be nested to many levels.

INCLUDE Files are supported to multiple levels with full listing support.

List Pseudo-ops and command line switches allow control over many variables including macro expansion listing, page length and width, title and subtitle, etc.

Alternative Opcodes have been defined such as EI for STI (or is that CLI? see what I mean?) and DI to enable and disable interrupts. All existing opcodes are supported but you can use more standard names like XOR, PUSH, POP, CALL, RET, JR, etc.

Compound Opcodes like PUSH D (PUSH A + PUSH B) and POP D (POP B + POP A) have been added. These enhancements have been made to allow easy migration from other processors, and are designed to allow programmers to focus on the programming task rather than on learning a new set of obscure and machine specific mnemonics.

ASHC11 Quick Start

ASHC11 is currently a 16 bit DOS program and is limited to 8.3 DOS filenames. It is invoked from a DOS command prompt and reports errors directly to the console for both pass 1 and pass 2. Here is its general invocation syntax.

 C:\> ASHC11 sourcefilelist [-switchlist]
Each item in the sourcefilelist and the switchlist must be separated by at least one white space. The square brackets indicate that the switchlist is optional, but if supplied, it is delimited from the file names by a preceding hyphen (-) so that a valid source file name may not begin with a hyphen. Here are some practical examples of command lines:

 ASHC11 file.ext
This form will assemble the file with the assembler's default switch settings so that an S19 output file called file.S19 will be produced. No listing file is produced.

 ASHC11  x  y  -intel -listing
This will assemble file x then file y producing x.HEX and y.HEX and corresponding listing files x.LST and Y.LST.

Only an abbreviated form of each switch is required, eg, -l is enough to specify that a listing should be created. The minimum number of characters required for each switch is indicated by the upper case characters in the description below. Where a parameter is required the switch and the parameter should be separated with a colon (:), or an equals (=) sign. All switches must be separated with white space and the first switch is required to be proceeded by a hyphen. Subsequent switches may be optionally proceeded by a hyphen.

 ASHC11  x  -i l=list.lst
This command line assembles file X and produces a listing list.list and output file x.hex.

ASHC11 Switches

 Cycles  Displays the number of cycles for each instruction in the listing. this is currently not supported
 Errorlimit=count    Number of errors reported before aborting. Default is 20.
 Intel    Produce an Intel (or Intellec) HEX output file rather than the default Motorola S19 file.
 List [=filename]  Produce a listing file. The default filename is made up of the source files name and the extension .LST If the optional filename is supplied, that will be the full name of the listing file.
 Macrolist  List all macros used in the source code (goes at the end of the list file).
 Outfile=filename  Name of output file produced. Default is derived from the input file name and the type of output file requested (either S19 or HEX)
 P1list  The Pass 1 (one) switch forces a listing to be output to the console during pass one of the assembler's operation. This can be useful for debugging macros and repeat block that aren't working as you expect.
 Symbollist  The symbol table is appended to the list file.
 Timeinfo  Start time and end time information is sent to the console.
 Verbose  Displays additional information about the file(s) being processed.
 Xpand  Expands repeats and macros in the list file. Again useful for debugging.
 ?  Force the basic help screen to be displayed.

Expressions, Symbols & Data Types

This section describes how expressions are formed and evaluated, and the representation of symbols and data types so there is no confusion in expression analysis.


Expression evaluation is carried out strictly left to right, however bracketed expressions are themselves completely evaluated from left to right (this process is recursive) as they are encountered. An expression is made up of a left and right expression with an operator between, eg.
        left_expr operator right_expr      example:    3 + 7
        left_expr operator (right_expr)    example:  1 % (3 + 1)
an expression is constructed like this:
   expr     ::=  term operator term    |         eg.  a + b
                 term operator expr    |         eg.  a + b / c
                 term operator (expr)  |         eg.  a + (b / c)
                 expr operator expr              eg. (a + d) / (b + c)

   operator ::=      -   |  minus operator
                     +   |  plus operator
                     *   |  multiply operator
                     /   |  divide operator (integer truncation)
                     %   |  modulus operator (integer remainder)
                     |   |  OR operator
                     &   |  AND operator
                     ^   |  XOR (exclusive OR)
                     <<  |  left shift
                     >>  |  right shift

   comparison ::=    <   |  less than
                     >   |  greater than
                     >=  |  equal or greater than
                     <=  |  equal or less than
                     =   |  equal
                     !=     not equal

    term ::=    -term    |  (unary minus)
                ~term    |  (unary NOT or one's complement, 0->1, and 1->0)
                *        |  (the current PC)
                symbol   |  see the symbols section and labels and equates
                constant    see next section

An expression list is simply a comma separated list of individual expressions, and is used for the definition of data storage. Macro parameter lists are also like expression lists. (all are described below).

        expresion_list ::= expression, expression_list

Constant Data Types

  constant         ::=  hex constant     |
                        binary constant  |
                        octal constant   |
                        decimal constant |
                        ascii constant

  hex constant     ::= ${hex digits} |
                       {hex digits}H |
                       {hex digits}h

  octal constant   ::= {octal digits}Q |
                       {octal digits}q |
                       {octal digits}O |
                       {octal digits}o

  binary constant  ::= %{binary digits} |
                       {binary digits}B |
                       {binary digits}b

  decimal constant ::= decimal digits

  ASCII            ::= any printing ASCII character

  ascii constant   ::= '{ASCII character}  |  '{ASCII character}'

  ascii string     ::=  "{ASCII characters}"

  binary digits    ::= 0 | 1

  octal digits     ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7

  decimal digits   ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

  hex digits       ::= decimal digits  | A | B | C | D | E | F


Symbols are used as representations of constants that evaluate to a fixed 16 bit quantity. A symbol may be the address of a program's instruction (called a label), a constant used in an instruction (a literal), or the result of an expression that is evaluated at assembly time. Symbols are stored in the symbol table space that is dynamically allocated. This space however is a finite resource and may sometimes determine if a program can in fact be assembled without running out of this space.

To distinguish symbols from the start of an expression or a constant, all symbols must begin with an upper or lower case alphabetic character, or one of the special characters, underscore (_), dot (.), question mark (?), or the "at" character (@). Numeric characters (ie. 0 through 9) and the dollar symbol ($) are allowed within the body of a symbol. Symbols may be up to at least 80 characters long (but symbol table space is finite) and all symbols are case sensitive (ie. symbol DELAY is NOT the same as Delay).

Note that some characters that are acceptable symbols for Motorola's freeware assembler are not valid for ASHC11. In particular, the dollar ($) symbol is NOT a valid starting character for a symbol, although it may be used within the symbol.


Pseudo operation codes (called pseudo-ops) are directives to the assembler, rather than real machine instructions (machine instructions are also called operation codes, or "opcodes" for short). Pseudo-ops tell the assembler how to assemble the code, as well as providing mechanisms to create data structures, control listings, and other functions too.

Pseudo-ops may be in upper or lower case and can begin in any column other than the first. They may have a "." (dot) in front of them (to remain compatible with some other assemblers) so that ".eject" is the same as "eject" or "EJECT" or even ".Eject". Additionally, if a "#" (hash, or pound symbol) is in column one, it may be immediately followed by a pseudo-op. So that "   include" is the same as "#include" (# is in the first column!).

For the purpose of describing them, pseudo-ops have been arbitrarily divided into the following alphabetically listed categories:

  1. Conditional expressions that selective assembly blocks of code.
  2. Data-ops that generate constant and variable data definitions.
  3. Errors and Warnings are user definable messages that are flagged to the command line.
  4. Include Files allow (nested) reusable source code.
  5. Ignored pseudo-ops are recognised but not actioned and do NOT generate errors or warnings.
  6. Labels and Equates allow fixed and variable references and definitions.
  7. Listing control allows control over what's sent to the list file, and how it’s formatted.
  8. Macros and Repeat blocks that are short definitions used to automatically generate code and data statements.
  9. Segment control allows code and various data to be easily combined in the one source file.

Following is an alphabetical listing of all recognised and actioned pseudo-ops. More information on some is found in the section following this list.

Recognised Pseudo-Ops

= A variable equate (it may be changed), equivalent to SET. See labels and equates.
== A fixed equate, equivalent to EQU. See labels and equates.
align Aligns the PC to the next modulo <expression> boundary. See data operations.
alignz As for ALIGN, but zero fills the space between. See data operations.
auto (not implemented in this release) See segment control.
bss See segment control.
bsz Block Storage of Zeros, Synonymous with ZMB. See data operations.
code Code is assembled in this segment. See segment control.
data Data is defined in this segment. See segment control.
db Define Byte(s). See data operations.
ds Define Storage. See data operations.
dw Define Word(s). See data operations.
equ A fixed equate (the same as ==) See labels and equates.
eject Places a page break in the output listing. See listing control.
else May follow IF or ELSEIF, and before ENDIF. See conditional expressions.
elseif Allows multiple conditions to be tested, like a SWITCH statement. See conditional expressions.
end See Ignored Pseudo-ops.
endif Completes an IFx/ELSEIF/ELSE statement. See conditional expressions.
endm Completes a MACRO definition block. See macros and repeat blocks.
endr Completes a REPT, IRPC, or IRP definition block. See macros and repeat blocks.
EQU Defines a fixed value that may not be redefined. See labels and equates.
error Produces a user defined error message during assembly. See errors and warnings.
exitif See macros and repeat blocks.
fcb Form Constant Byte(s). Synonymous with DB. See data operations.
fcc Form Constant Character(s). Is a subset of DB. See data operations.
fdb Form Double Byte(s). Synonymous with DW. See data operations.
fill Fills a section of memory with a constant expression. See data operations.
if Begins an IF expression and tests the expression for TRUE/FALSE. See conditional expressions.
ifd If Defined is a specific IF expression that tests if a variable is defined. See conditional expressions.
ifnd If Not Defined tests if a variable is NOT defined. The converse if IFD. See conditional expressions.
include Allows source code to have references to other source files. See include files.
irp Iterated Repeat. A section of code is iterated for various arguments. See macros and repeat blocks.
irpc Iterated Repeat for a Character argument. See macros and repeat blocks.
list Turns on listing output. See listing control.
local Defines local labels within MACRO, IRP, IRPC and REPT blocks. See macros and repeat blocks.
macro A collection of source lines that are invoked by "calling it up". See macros and repeat blocks.
name See ignored pseudo-ops.
nolist Turns off listing output. See listing control.
org Sets the PC, or location counter to a specific value. See segments.
page Defines page width and length and optionally specifies an EJECT. See listing control.
pagelength Specifies the maximum length in lines of each listing page. See listing control.
pagewidth Specifies the maximum width of the listing in characters. See listing control.
ram Declares uninitialised data. See segment control.
rept Defines lines of source that will be repeated a number of times. See macros and repeat blocks.
rmb Reserve Memory Byte(s). Allocates storage space. See data operations.
set Defines a variable equate. Synonymous with =. See labels and equates.
spc See Ignored Pseudo-op.
subtitle Declares a subtitle shown on the listing page. See listing control.
title Declares a title shown on the listing page. See listing control.
warning Produces a user defined warning message during assembly. See errors and warnings.
zmb Zero Memory Bytes. Synonymous with BSZ. See data operations.

conditional expressions


Conditional expressions are used to vary the way a source file is processed. As well as the simple if, else and endif, the elseif pseudo-op creates a form of switch statement. The ifd and ifnd test to see if symbols have been defined.

IF <expr>

The simplest form of if statement is as follows:

IF <expr>

An if must be paired with an endif. The "..." represents lines of code that are assembled if the expression <expr> evaluates to a non-zero value. A terminating endif tells the assembler to commence normal (unconditional) assembly of the source. Here is an example that will generate two lines of code.

    DEBUG equ 3
    IF DEBUG >= 2        ;Assemble if symbol DEBUG is >= to 2
        ldd     MSG      ;This will be assembled in this example
        call    MSGOUT
    ENDIF                ;This terminates the conditional section of code

IFD <expr>
IFND <expr>

The IFD (IF Defined) and IFND (IF Not Defined) conditional pseudo-ops are like IF, but test if a symbol name is defined, ie. the symbol name is either a label or an equate.

    IFD <symbolname>
         ...            ;assembles if <symbolname> IS defined

    IFND <symbolname>
         ...            ;assembles if <symbolname> is NOT defined
This situation often occurs when include files are used to pre-define constants and IFD or IFND can be used to selectively include a definition file if that definition has not already been defined.

    IFND   PRINTF               ;Tests if symbol PRINTF is defined
        INCLUDE ""    ;it's not so include the definitions

ELSEIF <expr>

For any of the conditionals IF, IFD or IFND (collectively called IFx), the ELSE conditional may be used to define code that will be assembled if the current IFx< expr> (or any ELSEIF expression) does not evaluate to true. Note that ELSE does not have an expression and an ELSE must be paired with a previous IF (or ELSEIF, see below) and terminated with a following ENDIF.

Here is an example where the ELSE statement's code will be assembled.

        J = 5
    IF  J < 4           ;so this is NOT true
        DB    $01       ;this is NOT assembled
        DB    $02       ;this will be assembled
Multiple conditions may be tested for using the ELSEIF conditional. The syntax for ELSEIF is

    IFx <expr or symbol> ;IFx may be IF, IFD or IFND
    ELSEIF  <expr>
    ELSEIF  <expr>      ;optional multiple ELSEIF clause
    ELSE                ;optional ELSE clause
ELSEIF may only appear after an IF and before an ELSE or ENDIF. Multiple "ELSEIF <expr>" statements may appear. Note that <expr> will not be evaluated if a previous IF (or ELSEIF) <expr> evaluates to TRUE. This means that none or exactly one section of code associated with an IF or ELSEIF expressions will be evaluated in any IF/ELSEIF/ELSE/ENDIF conditional block.

Here is an example where two ELSEIF code sections would evaluate true, but only the first will generate code.

    K = 2
    IF K > 2            ;NOT true
        ldaa    #K      ;NOT assembled
    ELSEIF K = 2        ;IS true
        ldaa    VAR1    ;IS assembled
    ELSEIF K < 3        ;IS true but NOT assembled as we already matched TRUE
        ldaa    VAR2    ;NOT assembled
    ELSE                ;ignored as we already have a TRUE match
        ldaa    #4      ;NOT assembled
    ENDIF               ;completes this IF block
Nested IFx expressions are possible (refer to the Maximums sections for nesting limits) as the following example shows, but note that complex nested expressions may sometimes be used to obviate the need for nested IF expressions.

    IF A = B
      IF C >= D
        dw     C + D
      ELSE              ;ELSE clause for "IF C >= D"
        dw     D - C
      ENDIF             ;terminates the "IF C >= D" expression
    ELSE                ;ELSE clause for "IF A = B"
        dw     $123
    ENDIF               ;terminates the "IF A = B" expression
Note also that as an IF expression must be terminated with an ENDIF clause, there is no confusion about what IF expression an ELSE refers to. Indenting can sometimes be used to improve the readability of nested IF expressions but remember the assembler ignores indenting and relies on you getting everything in the correct logical sequence.

data operations

Control over the definition and generation of data is achieved via the various data-ops. these include ALIGN, ALIGNZ, BSZ, DB, DS, DW, FCB, FCC, FDB, FILL, RMB, and ZMB.

ALIGN <expr>
ALIGNZ <expr>

ALIGN <expr> is used to align the PC to an address boundary. It advances the PC to the next modulo <expr> address. for example, ALIGN 2 will ensure the PC is even. ALIGN 256 will set the PC to an address of 0XX00H.

Note that ALIGN <expr> advances the PC by PC % <expr> (ie. the remainder when the PC is divided by <expr>. Some assemblers call this directive EVEN.

ALIGNZ <expr> works like ALIGN but explicitly sets the skipped bytes to zero.

BSZ <expr>

BSZ means Block Size Zeroed and defines a block of memory of size <expr> set to zeros. Also called ZMB (Zeroed Memory Bytes)

DB <expr> [[, <expr>]..]

Define Byte(s) sets memory to the value of the expression <expr>. Single values, of any given radix base, may be defined. Single characters are defined by enclosing them in single quotes. Strings may also be defined by enclosing them within double quotes. Multiple comma separated values may be defined on the one line.

        DB     $12              ;hex byte
        DB     %10001101        ;binary byte
        DB     'C'              ;character ('C) also works
        DB     "text"           ;double quoted text

        DB     10, $ff, %1011001, 'A', "string", 0   ;multiple format too...
Note that FCB and FDB are just special cases of the general DB syntax.

DS <expr>

Define Storage (DS), also called RMB for Reserve Memory Bytes, allocates memory of size given by the expression <expr>. The current PC is just bumped up by the value of the expression, and the data between is not initialised.

DW <expr> [[, <expr>]..]

Define Word (DW), also called FDB for Form Double Byte, evaluates the expression as a 16 bit quantity and stores it in normal Motorola fashion with the most significant byte (MSB) first. Multiple comma separated expressions may be supplied on the one line. Note that strings are not evaluated as they are for DB (See above).

FCB - see DB
FCC - see DB <string>
FDB - see DW

FILL <expression>, <count>

Fill, as it's name suggests, fills memory with the value that expression evaluates to (a byte value), count times, and is equivalent to repeating "DB <expression>" count times.

RMB - see DS
ZMB - see BSZ

errors and warnings

These pseudo-ops provide a means of sending a text string to the console during assembly. This may be used to alert the operator of various conditions.

ERROR <text>

Error forces the assembler to display the text message and to fail at the end of the current pass. The error counter is incremented each time, and if the error limit is reached, the assembler will abort the current operation.

WARNING <text>

Warning simply send the message text to the console and increments the warning counter that is displayed at the end of each pass.

Include Files

INCLUDE <filename>

Include files are simply sections of code that can be re-used in other programs and are collected into one or more files that are included in the current file. A file that is included can itself have an include file defined. Once the include file has been processed, it is closed and assembly continues at the point following the include statement in the previous file. Up to 10 nested levels of include files may be declared before an error is flagged. The listing file page number field indicates the current line of each include file.

Ignored Pseudo-ops


These pseudo-ops are currently ignored by the assembler. Tell me if you think they should do something!

Labels & Equates

Global & Local Lables

All lables must start in the first column (otherwise they will be interpreted as a pseudo-op) and may optionally be terminated with one or more colons (ie. :, or ::, etc.) the colon(s) are not considered part of the label.

Local labels begin with the at symbol (@) and are followed by one or more digits, and are used between normal (ie. non-local) labels when it's not convenient to define a unique identifier. Local labels are checked at assembly time to ensure they are not duplicated, and that their scope does not cross a "symbol boundary".

EQU, = =

The equate or double equals sign (= =, there is no space between the two equals symbols) is used to define a symbol with a fixed value, as follows:

symbolname   equ   expression
anothername  ==    another+expression

SET, =

The set or equals sign (=) creates a symbol whose value may be redefined without generating an error. Set statements are often used within macros or repeat blocks to create symbols whose value vary in a controlled way as the macros is expanded.

counter    set   25
counter    =     counter+1

listing control

When the -list command line switch is supplied a listing is produced that shows all code and data bytes that are assembled for each line of the source code. The default list file is in the format of a single "page" with a header showing the assembler version, page number, source file name and the date and time. The listing pseudo-ops modify this default behaviour.

Here's an example of a page from a list file showing the page's header block, and a number of selected lines of the source file (the ... indicates where lines have been removed):

ASHC11, 68HC11 Macro Assembler v1.2 (c) Copyright 2000 Tech Edge Pty. Ltd.
page 2 -  amxy.a  - Fri 1 Sep 2000 10:58:09.45
Re-assembly of AMXY.BIN

                =005d   112 kProgramID      == $5D          ;$5D for AMXY
                        114 #include "macs.a"
                        250 KKPGMID                         ;($c008) Program ID
c008 5d                 251         db      kProgramID      ;$5d in AMXY
d081 86 60             1522         ldaA    #$60            ;Read AN6
d083 bd ec d8          1523         call    ReadADC         ;Read fuel pump voltage


This forces a page break in the list file. A page break consists of a form feed character (0Ch) and the page header described above. Note that the line containing the eject pseudo-op appears as a blank line in the list file.


The list output can be suppressed with nolist or enabled with list. suppressed list output between a nolist and list pair appears as a single blank line at the line number of the list pseudo-op.

PAGE [<pagelength>] [, <pagewidth>]

If no parameters are supplied, page has the same effect as eject.

The number of lines listed per page is controlled by the pagelength parameter, and the default is to have a single page (ie. no page breaks in the list file). A page length of zero (0) will restore the default of a single page. Alternatively, you can use the pagelength pseudo-op.

The number of character columns on each list page is controlled by the pagewidth parameter. A maximum of 255 columns will be listed in the output. All characters beyond pagewidth will simply be truncated. Note that you can specify the page width independently of the page length by using a null pagelength parameter and a comma (,) before the pagewidth parameter. Alternatively, you can use the pagewidth pseudo-op.

PAGELENGTH <pagelength>
PAGEWIDTH <pagewidth>

Both pagelength and pagewidth pseudo-ops are used in an obvious manner and have the same effect as the corresponding pagewidth and pagelength parameters to the page pseudo-op described above.

TITLE <text>

By default, on each page of the list file, there are two blank lines following the standard two lines of header information. All text following the title pseudo-op is placed on the first of these lines. All text following the subtitle pseudo-op is placed on the second of these lines.

Macros and Repeat Blocks

Macros and repeat blocks include the pseudo-ops exitif, irp, irpc, local, macro, rept, endm, endr. The following sections describe each pseudo-op.

<name> MACRO  [[, <param>] ..]
 MACRO <name> [[, <param>] ..]

Macros allow a piece of code to be defined once and then invoked by simply writing the macro’s name along with any arguments defined for the macro. This aids the construction of complex and correct code by eliminating typing errors as a major cause of programmer error. It also allows a programmer to define easily reusable sections of code, speeding up future tasks.

Here’s a simple parameterless macro definition defined with the MACRO keyword and terminated with the ENDM keyword.

setflag MACRO           ;declare macro "setflag"
        ldab    #1      ;set B register to 1
        stab    flag    ;set the variable called flag
The macro is called "setflag" and is invoked by specifying its name in the opcode field, as follows.

        setflag         ;invoke macro example called "setflag"
It will produce the code defined in the macro definition’s body (ie. between the MACRO and ENDM pseudo-ops). Specifically the code:

        ldab    #1
        stab    flag
will be generated every time the macro "setflag" is used. Note that we could have begun the macro definition as follows (with the macro's name following the MACRO keyword):

        MACRO   setflag         ;also declares macro "setflag"
If the macro is not declared with a label field then the field to the right of "MACRO" is the macro’s name.

Typically a macro will be used with parameters to vary how it is expanded. The macro’s parameters are declared to the right of the MACRO pseudo-op, and if there is more than one parameter then they must be comma separated. An example using two parameter fields, "here" and "value", follows.

setvar  MACRO   here, value
        ldab    #value          ;set B with immediate value "value"
        stab    here            ;store B into location "here"

Each occurrence of "here" and "value" in the macro’s definition will be replaced with the arguments supplied with the macro. This macro is invoked three times below, along with two arguments for each invocation:

        setvar  loc1, 1         ;set variable "loc1" to immediate value 1
        setvar  loc2, 2         ;set loc2 to 2
        setvar  $124, 'X'       ;set $124 to ASCII 'X'

The first two invocation of setvar will produce the code:

        ldab    #1      ;'value' is substituted with parameter '1'
        stab    loc1    ;'here'is substituted with the parameter 'loc1'
        ldab    #2      ;'value' is substituted with parameter '2'
        stab    loc2    ;'here'is substituted with the parameter 'loc2'

Repeat Blocks:
 REPT <count>

Repeat Blocks are used to define sections of code that are to be repeated and/or iterated a number of times. The REPT pseudo-op simply repeats a block of code a certain number of times. Here’s a REPT block that initialises a data area. Defining a REPT block is like defining a macro, but the REPT block is immediately actioned and is not saved for future use.

    REPT 10             ;repeat the next code section 10 times
        db      'A'     ;define a single byte 'A'
    ENDR                ;end of REPT block

The above REPT block produces the equivalent of the following code (although the listing would show 10 individual db ‘A’ instructions).

        db      "AAAAAAAAAA"

Iterated Repeat Blocks:
 IRP <symbol>, <expr> [[, <expr>]..]
 IRPC <symbol>, <string>

IRP and IRPC (collectively know as IRPx) allow a single parameter to be substituted within a repeat block. They are even more like macros than REPT. They use a single parameter (chr in the following example) that is substituted from a list of supplied values. IRPC is the simplest, and uses a string from which successive characters are taken from (see example file "x1"):

    IRPC chr, "JKLMNOP"
        db     'chr'    ;chr will take on the values J, K, L, etc.

The above IRPC produces the following code

         db     'J'
         db     'K'
         db     'P'.

IRP is slightly more complex and uses a (comma separated) list of values to be substituted, as the following example shows (see example file "x3"):

    IRP param, $20, $31, $82
        dw      param   ;param will take the values $20, then $31, …
produces the following code:

         dw     $20
         dw     $31
         dw     $82

More complex code structures are possible by combining REPT or IRPx with one or more macros. Here’s an example that uses IRPC to set up a data area invocation (see example file "x4") First we define a macro, then a repeat block using that macro:

defchar MACRO chr
Lchr    db      'chr'   ;define a label and a data byte

    IRPC _ch, <ABCD>
        defchar _ch
This IRPC will iterate four times for each character of the string "ABCD" and will generate the four lines:

        defchar     A
        defchar     B
        defchar     C
        defchar     D
and the macro will be expanded to produce the following code defining four labels and four data bytes:

LA      db      'A'     ;Label LA for data byte "A"
LB      db      'B'     ;etc..
LC      db      'C'
LD      db      'D'

Macro Support:
 EXITIF <expr>
 LOCAL <name> [[,<name>] ..]

The keywords exitif and local support more complex macro and repeat constructs. They are only valid within these construct.

Macro expansion can be prematurely terminated with exitif if the expression evaluates to a non-zero value.

The local pseudo-op defines local lables that have a scope only within the current macro or repeat block.

segment control

ORG <expr>

Is used to set the PC to the expression following.


Segments are predefined names for four different areas within your source code. The PC may be independently set for each segment (using org), and when you change segments, the PC will automatically be set to that segment's last used value. code and data segments can be used in obvious ways when you'd like to separate code and data areas that end up in an EPROM. ram is used for RAM data that will be stored in volatile memory.

Note that bss selects the same segment as data (some compilers use this name) and the auto segment is the fourth independent segment, and you can use this any way you like.

Disclaimer, Bugs, Limitations, Enhancements

This version of ASHC11 is provided free, however ASHC11 is Copyright © Tech Edge Pty. Ltd. and you may not use it for any commercial development without the written consent of Tech Edge.

Tech Edge does not warrant ASHC11 for any use what so ever. You use ASHC11 at your own risk.


There are no serious bugs that I know of. The most complex part of ASHC11 is the macro processing code which amounts to over half the source code that generates assembler output. I have had random occasional failures when using complex and convoluted test scripts. This indicates that perhaps there are some improperly initialised variables that still need to be tracked down. I'm happy however that ASHC11 is stable for the level of macro complexity that the majority of users will code.


ASHC11's potentially biggest limitation is that it's a 16 bit DOS program that I have constrained to a small model in order to minimise its program size and maximise it execution speed. The immediate implication is that symbol table space (including macro definition space) is limited to 64 kbytes less the code size of ASHC11 itself. Desipte this, some thousands of "normal" sized symbols may defined.

Only eight dot three (8.3) file names are recognised by ASHC11. This means you may have to use these funny ~ symbols in file names.


Macros may be called with parameters that are themselves lists of parameters. This is done by enclosing parameters within < and > brackets. This can be a very powerful facility, and when I have removed all of the bugs in this part of macro processing, I'll update ASHC11 and the documentation to describe how it works. For the moments however you should assume this feature doesn't work. The reason I mention this feature here is so you understand possible strange behaviour when you use < & > within macro argument expressions.

I may change ASHC11 to use far pointer logic within symbol table processing to improve the number of labels that may be defined.

I appreciate all reports of bugs you find. Send them to me and I'll try and fix it for everyone ASAP.

Different Mnemonics

The following mnemonics are accepted by ASHC11, they are compared with those as specified by Motorola.
  ASHC11  Mnemonics           Motorola's     Function Performed
  call                           JSR         Call
  callr                          BSR         Call Relative (short call)
  cmpD, cmpX cmpY                CP?         Compare (16 bit register)
  decX, decY, decS               DE?         Decrement (16 bit register)
  di                             SEI         Disable Interrupts
  ei                             CLI         Enable Interrupts
  incX, incY, incS               IN?         Increment (16 bit register)
  jr                             BRA         Jump Relative (short jump)
  push, pushB, pushX, pushY      PSH?        Push on to stack
  popA, popB, popX, popY         PUL?        Pop off stack
  ret                            RTS         Return (from subroutine)
  reti                           RTI         Return From Interrupt
  xorA, xorB                     EOR?        eXclusive Or

Why I wrote the ASHC11 Macro Assembler

When faced with a fairly simple project requiring a 68HC11 assembler, I readily obtained a freeware assembler. This tool almost did what I wanted. It certainly assembled, and I obtained some HEX output I could blow into an EPROM and test out on my board. But then the problems started. Why didn't my code do what I expected? It was really quite simple. The assembler was not telling me I had simple syntax errors, and was assuming I meant something else.

I was very upset, but not discouraged, so I looked for another assembler. To cut a long story short, I looked and looked for quite some time before I came to the conclusion there was nothing that gave me all the features I needed. Time to write my own assembler, with the features I wanted.

I started with the source for Motorola's freeware assembler, but quickly replaced all of the code, leaving just the tables that define the opcodes. I eventually replaced even these tables. I ended up with a generic assembler with lots of features, and a 68HC11 specific assembler that adds even more features.

Last Updated 29 Nov. 2001 (links)

Statistics by
Shows approximate hits since 1 Sept 2000.


This document is copyright © 2000, Tech Edge Pty. Ltd.
Author P. Gargano

Home | ASHC11 Feedback | Copyright