The as12 assembler

Version 1.2: 8 July 1996 by Karl Lunt, http://www.seanet.com/~karllunt

Version 1.2a: 7 April 1999 by Tom Almy, http://www.aracnet.com/~tomalmy

Version 1.2b: 18 January 2003 by Eric Engler, http://www.geocities.com/SiliconValley/Network/2114/

Version 1.2c: 29 January 2003 by Eric Engler, http://www.geocities.com/SiliconValley/Network/2114/

Version 1.2d: 29 March 2003 by Eric Engler, http://www.geocities.com/SiliconValley/Network/2114/

Version 1.2e   June 19, 2005 by Matthew Kincaid, Eric Engler, Tom Almy   http://www.ericengler.com/AsmIDE.aspx

The current version of as12 is available here: http://www.ericengler.com/AsmIDE.aspx


as12 constructs


Invoking the as12 assembler

To start the as12 assembler, enter the following command at the prompt:

as12 foo.asm

Where as12 is the name of the assembler you want to use, and foo.asm is the path, name, and extension of the file you want to assemble.
The as12 assembler will assemble the file, sending the listing output to the console and writing the S19 object output to a file named file.ext.

To save the listing output in a text file for later review, use the -L option. For example:

as12 foo.asm -Lfoo.lst

will assemble the file foo.asm and write the output listing to a file named foo.lst.
Entering as12 with no arguments will display a short help file describing the available commandline options.


Commandline Options

Commandline options allow you to specify input file names, define global symbols, and declare paths for library files. For example:

as12 foo.asm -dTIMERS -l\mylib\hc12 -Lfoo.lst

will assemble the file foo.asm, using the library files found in the directory \mylib\hc12. Additionally, the label TIMERS will be defined with a value of 1 and the listing output will be written to file foo.lst.

When specifying the assembler source file, the extension ".asm" is assumed if no extension is explicitly given.

The full set of commandline options includes:

-o<filename> Define object file (default extension is .s19)
-d<symbol> Define the symbol 'name' with a value of 1
-l<dir> Define a library directory with path 'lib'
-L<filename> Define listing file (default extension is .lst)
-D Turn on debugging printout
-s<filename> Create a symbol table file (use dflt: filename.sym)
-p<part #> Define MCU part number, such as 68hc12a4 (see #ifp)
--list Display list file to console
--cycles Display the cycle count
--line-numbers Display line numbers in list file
--no-warns Suppress warnings being displayed to console and list file


-d Define a label

The -d option allows you to define a label on the commandline. Labels defined in this manner are treated by the as12 assembler as if they had been defined within your source file and assigned a value of 1. Your source file can then refer to these labels in #ifdef tests. For example:

as12 foo.asm -dMY_ALGORITHM

causes the as12 assembler to behave as if your source file began with the line:

#define MY_ALGORITHM

This ability to define labels from the commandline adds great power to the as12 assembler. You can use this feature to selectively assemble blocks of source code based on arguments you specify in the commandline, without first having to edit the source code before each assembly.


-l Define a library path

Normally, as12 first checks in the current directory for needed include files. If as12 cannot find a needed file in the current directory, it will try the library path specified with the -l option on the commandline, if any.

For example:

as12 foo.asm -lc:\mypath


-p Define the target processor

as12 allows you to pass in a specific processor or board to identify the part you are compiling the program for. When combined with the #ifpart conditional assembly directive, can give users a powerful way to compile source code which may depend upon what part is being targeted.

For example:

as12 foo.asm -pDragon12


-D Debug the as12 assembler

Turns on the internal debug features of as12. Mostly for the developer of as12, but might be helpful if you are having a problem understanding what as12 is doing.

For example:

as12 foo.asm -D


-L Listing file

Specifies a listing file. If no filename extension is given, ".lst" is assumed. If no file name is given, then the file name will be that of the first source file, with an extension ".lst".

For example:

as12 foo.asm -L


Pound Sign (#) Operators


#include

The include directive allows other assembly source files to be inserted in the code immediately after the include statement, as if the contents of the included file were actually in the file that contained the include statement. Stated differently, the include statement works as you might expect. The syntax of the include statement is shown below...

#include \my_dir\myfile.asm

In linux, it is important to note that the filename expansion will only be as good as the filename expansion as the shell that you are operating in. For example, if you are running shell (/bin/sh) then the tilde username (~user) lookup may not work correctly. It is best to put in relative or absolute filepath specifications that are not shell dependent.

Include statements may be used within #ifdef statements.

We support quoted filenames within #include. This lets us use filenames that might have embedded spaces, or the directory name may have embedded spaces:

#include "c:\program files\as12\my defs.h"


#define

The define statement allows labels to be defined. This statement is simply an alternate form for an equ assembler directive. The alternate form is provided so that users will be alerted to the opportunities to write more sophisticated code that the #ifeq, and related statements allow. The proper use of the define statement is:

#define MY_LABEL expression

The define statement is as if the user had typed the following:

MY_LABEL: EQU expression

Both forms are equally valid, and both forms are implemented internally thee same way. The EQU is probably more portable of the two constructs.


#ifeq

The ifeq command allows for the user to conditionally compile different sections of assembly language based on whether or not a label is equal to a value. Example:

#ifeq MY_SYMBOL expression_to_compare_to
  ... 
  (this code will be assembled if MY_SYMBOL has the same value as expression_to_compare_to)
  ...
#endif

I show the #endif statement because for every form of #if there needs to be a marker so that as12 knows what code is to be conditionally compiled. Restated, for every if there needs to be an endif.

If the expression resolves to the same value as the label in an #ifeq directive, then every line between the #ifeq and the #endif is executed. If the expression resolves to a different value than the label, all of the lines between the #ifeq and the #endif are ignored.


#ifndef

The ifndef command allows for the user to conditionally compile different sections of assembly language based on whether or not a label is defined. Example:

#ifndef MY_LABEL
  ... 
  (this code will be assembled if MY_LABEL has not been defined)
  ...
#endif

I show the #endif statement because for every form of #if there needs to be a marker so that as12 knows what code is to be conditionally compiled. Restated, for every if there needs to be an endif.


#ifdef

The ifdef command allows for the user to conditionally compile different sections of assembly language based on whether or not a label is defined (via a #define or an EQU. Example...

#ifdef MY_LABEL 
  ...
  (this code will be assembled if MY_LABEL has been defined)
  ...
#endif

I show the #endif statement because for every form of #if there needs to be a marker so that as12 knows what code is to be conditionally compiled. Restated, for every if there needs to be an endif.

If the label in an #ifdef directive is defined, then every line between the #ifeq and the #endif is executed. If the label is not defined, all of the lines between the #ifdef and the #endif are ignored.


#ifpart

This is the only directive that allows for a string comparison. A special internal variable is the only variable which is a string variable. The only way to set that variable is with the -p commandline option. The sole purpose of this directive is to allow for conditional assembly based upon the value of the string. This seemed natural for handling the different part types. Example..

#ifpart b32
  ... 
  (this code will be assembled if the string <b32> is same as string in -p commandline option
  ...
#endif

I show the #endif statement because for every form of #if there needs to be a marker so that as12 knows what code is to be conditionally compiled. Restated, for every if there needs to be an endif.

If the string that follows the #ifpart directive matches the string that was passed in via the -p option, then the lines betwee n the #ifpart and the #endif will be executed. If the strings do not match, the lines between the #ifpart and the #endif will be ignored.


#else

This directive must be coupled with any of the if directives. This allows either or compilation and performs just like you expect an else to perform. Example..

#ifdef MY_LABEL
  ... 
  (this code will be assembled if MY_LABEL is defined)
  ...
#else
  ... 
  (this code will be assembled if MY_LABEL is NOT defined)
  ...
#endif

I show the #endif statement because for every form of #if there needs to be a marker so that as12 knows what code is to be conditionally compiled. Restated, for every #if there needs to be an #endif.

If the #if statement that goes with the #else statement is true, the statements between the #if and the #else will be assembled, and the statements between the #else and the #endif will be ignored. If the #if statement is false, the statements between the #if and the #else will be ignored and the statements between the #else and the #endif will be executed

There can only be one #else for each #if statement. But #else is optional, so you do not have to use it.


#endif

The #endif statement tells the assembler when the conditional assembly section of the code is finished. Otherwise the assembler would have no way of knowing when to quit.

For every #if statement there needs to be one #endif. If there is an #if and an #else, then there should be one #endif statement also.

Examples:

#ifpart part_name
  ...
#else
  ...
#endif

#ifndef MY_LABEL>
  ...
#endif

#ifndef MY_LABEL
  ...
#else
  ...
#endif


Typical Conditional Assembly Examples

Notice how easy you could build a library of different parts and make your source code compile accordingly.


Files


Assembler executable, as12

Filename: as12.exe.

NOTE: On linux the .exe extension is not typically used.

These are the cross assemblers that  allow you to convert your Motorola source code to Motorola machine code on your PC.


Motorola machine code, *.s19

A file with the same name as the first source file but with the extension ".s19" is used to hold the binary machine code instructions. This is produced by the assembler: the assembler translates text commands into binary commands. The binary data is stored in the s-record format, which is contained in the .s19 file.

This is the file that is sent to the embedded board, where your program will be executed.

For additional  information regarding s-records visit Seattle Encoder's s-record article


Listing files, *.lst

The listing file is useful for debugging. Simply add the commandline option "-L" to create the listing file.


Source files,  *.asm

Standard ASCII source files. These should be created with the extension ".asm" since that is the default used by the assembler, but is not required.


Features


Expressions

Expressions may consist of symbols, constants or the character '*' (denoting the current value of the program counter) joined together by one of the operators: +-*/%&|^. You may nest expressions using parentheses up to 10 levels deep. The operators are the same as in C:

+       add
-       subtract
*       multiply
/       divide
%       remainder after division
&       bitwise and
|       bitwise or
^       bitwise exclusive-or

In addition, the unary minus (-) and complement (~) operators are allowed when preceding a symbol, constant, or character '*' only.

Examples of valid expressions...

  (5*8)
  (my_val-10+20*(16-label)/10)
  10
  $10
  *
  %10010
  my_value
  ~$20

Starting with version 1.2e you can NOT have spaces in an expression:

  ldaa foo + 1

will produce erronous assembly. The correct way to write this expression is:
  ldaa foo+1

Note: When the asterisk (*) is used in a context where the as12 is expecting a label, the asterisk (*) represents the value of the current program counter.


Symbols

Symbols consist of one or more characters where the first character is alphabetic and any remaining characters are alphanumeric. Symbols ARE case sensitive.


Constants

'       followed by ASCII character
!       followed by a decimal constant (decimal is assumed, so ! is optional)
$       followed by hexadecimal constant
@       followed by octal constant
%       followed by binary constant
digit   decimal constant

Examples:

'A
46
$2E
@07
%10001001


Labels

A symbol starting in the first column is a label and may optionally be ended with a ':'. A label may appear on a line by itself and is then interpreted as:

        Label   EQU     *

Note that labels are NOT case sensitive.  You can use labels named LABEL interchangebly with LaBeL.


Comments

Here are some notes about comments...


AS12 Directives (or pseudo-opcodes)


bsz Pseudo Opcode - Block Set Zeros

Sets a block of memory to zero values. Same as zmb.


db Pseudo Opcode - Define Byte

Syntax and examples:

	db	Byte_Definition[,Byte_Definition]
	db	$55,$66,%11000011
	db	10
half	db	0.5*100

db Defines the value of a byte or bytes that will be placed at a given address.

The db directive assigns the value of the expression to the current program counter. Then the program counter is incremented.

Multiple bytes can be defined at a time by comma separating the arguments. Each comma separated argument can be a separate expression that the as 12 will evaluate.

Notes:

Related To:

Useful With:

Things to look out for:


dc.b Pseudo Opcode - Define Constant Byte - declare a byte of memory

Identical to db


dc.w Pseudo Opcode - Define Constant Word - declare a word of memory

Identical to dw.


ds Pseudo Opcode - Define Storage

Syntax and examples:

	ds	Number_of_Bytes_To_Advance_Program_Counter

The ds increments the program counter by the value indicated in the Number of Bytes argument.

Notes:

Related To:

Useful With:

Things to look out for:


ds.b Pseudo Opcode - Define Storage Bytes - declare bytes of storage

Identical to ds.


ds.w Pseudo Opcode - Define Storage Word

Syntax and examples:

	ds.w	Number_of_Words_To_Advance_Program_Counter

The ds.w increments the program counter by the value indicated in the argument multiplied by two. In other words, if the ds.w expression evaluates to 4 then the program counter is advanced by 8.

Notes:

Related To:

Useful With:

Things to look out for:


dw Pseudo Opcode - Define Word

Syntax and examples:

	dw	Word_Definition[,Word_Definition]
	dw	$55aa,$66,%11000011
	dw	10
half	dw	0.5*65536

Defines the value of a word or words that will be placed at a given address.

The dw directive assigns the value of the expression to the current program counter. Then the program counter is incremented by 2.

Multiple words can be defined at a time by comma separating the arguments. Each comma separated argument can be a separate expression that the as 12 will evaluate.

Notes:

Related To:

Useful With:

Things to look out for:


equ Pseudo Opcode - Equate

Syntax and examples:

Label	EQU	Value_To_Assign_To_The_Label

Directly assigns a numeric value to a label.

Notes:

Related To:

Useful With:

Things to look out for:


fcb Pseudo Opcode - Form Constant Byte - declare bytes of storage

Identical to db.


fcc Pseudo Opcode - Form Constant Characters

Syntax and examples :

	fcc	delim_characterstring_to_encodedelim_character
	fcc	/my_string/
	fcc	*// string with slashes //*
	fcc	'best to use single quotes'

FCC allows the encoding of a string.

The first character is the delimiter. By allowing the flexibility of selecting delimiters, you can easily make strings which have slashes and tick marks in them. The only catch is that if you choose a delimiter, it

In the second example, my_string will be encoded as an ASCII string. The /'s simply mark the ending and beginning of the string. This also lets you put spaces in the string.

In the third example, the * (asterisk) is the delimiter and the slashes will be encoded with their ASCII values into the ASCII string.

Notes:

Related To:

Useful With:


fdb Pseudo Opcode - Form Double Byte - declare words of storage

Identical to dw.


fill Pseudo Opcode - Fill Memory

Syntax and examples:

	fill	byte_to_fill_memory_with,num_of_bytes_to_fill

FILL allows a user to fill memory with a byte. See my comments in zmb about the value of these Pseudo Opcodes.

Notes:

Related To:

Useful With:

Things to look out for:


loc Pseudo Opcode - creates automatically incrementing labels

WARNING: Some people do not like to see this command used in your programs

Increments and produces an internal counter used in conjunction with the backwards tick mark (`). By using LOC's and the ` mark you can write code like the following without worrying about thinking up new labels.

        LOC
        ldaa    #1
loop`
        deca
        bra     loop`
        LOC
loop`
        brset   0,x $55 loop`

This code will work perfectly fine because the second loops label is really loop002 and the first ones is loop001. The assembler really sees this:

 
        LOC
        ldaa    #1
loop001
        deca
        bra     loop001
        LOC
loop002
        brset   0,x $55 loop002

You may also seed the LOC with a valid expression or number by putting that expression or number in the operand field. This gives you the ability to over ride the automatic numbering. This is also sometimes handy if you need to keep track of what your local variable is. (you lose track in the source if you aren't careful, because the tick ' mark is the only thing you see).


opt Pseudo Opcode - Assembler List Options

There are five permissible operands for this instruction:


The org Pseudo Opcode - Origin

Specify the address in memory where the following code should be located.

Syntax and examples:

	org	value_to_set_program_counter_to
	org	$800
	org	MY_PROGRAM_START ; use a symbol defined elsewhere with EQU
	org	LAST_MEMORY_LOCATION-(LAST_PROGRAM_BYTE-FIRST_PROGRAM_BYTE) ; calculate a value

The org Pseudo Opcode allows the assembler's program counter to be set to a value. This is useful for locating your software and its elements (tables, ram, constants, etc) in useful (intelligent) locations within the memory space of the microcontroller.

In better multi-pass assemblers (not as12), the org statement is rarely used because the code is located at the link, and not during compilation. Since as12 is a simple two-pass assembler, orgs must be used so that the code is compiled where it is supposed to.

Notes:

Related To:

Things to look out for:


redef Pseudo Opcode - Redefine

WARNING: Some people do not like to see this command used in your programs

Used to redefine first operand (which must be a label) to value of second operand (an expression)

Example:

foo     equ     10
        ldaa    #foo        ; Accumulator A gets value 10
        redef   foo 12
        ldab    #foo      ; Accumulator B gets value 12


rmb Pseudo Opcode - Reserve Memory Bytes

Equivalent to ds.b and ds.


rmw Pseudo Opcode - Reserve Memory Words

Equivalent to ds.w.


zmb Pseudo Opcode - Zero Memory Bytes

Operand specifies number of bytes to allocate and fill with zero. Similar to bss on some assemblers.


AS12 Opcode Mnemonics - Names for Machine Code Instructions

*  means a new opcode that was not supported on the 68hc11


Non-standard Opcode Mnemonics

These mnemonics are defined in as12, but are not considered standard.

I recommend that you do not use these in your programs. This is mostly an issue for people who might want to migrate their code to a different assembler in the future - that other assembler won't understand these opcodes (although in many cases you can get around it by defining a macro for each of these):