Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
2133 lines (1916 sloc) 70.2 KB
.ds [ \s+1\z[\h'1p'\z[\h'-1p'\s-1\0
.ds ] \s+1\z]\h'-1p'\z]\s-1\0
.ds Z \s+1\z]\h'-1p'\z]\h'3p'\z*\s-1\0
.ds Da July 7, 1986
.TL
\s+6Macross 6502\s-6
\"Macross
.AU
an assembler for people who hate assembly language
by
Chip Morningstar
.AI
Lucasfilm Ltd. Games Division
\*(Da
.ds LH Macross
.ds CH \*(Da
.ds RH 6502 Version
.ds LF Lucasfilm Ltd. Proprietary Information
.ds CF - % -
.ds RF CONFIDENTIAL
.AB
This document describes the 6502 version of \fIMacross\fR, a super-duper
cross-assembler that has actually been used!
.AE
.SH
\s+3Introduction\s-3
\"Introduction
.PP
\fIMacross\fR is a generic cross assembler for a variety of different
microprocessors. This document describes the 6502 version of \fIMacross\fR.
\fIMacross\fR differs from many macro assemblers in that it provides a number
of ``higher level'' constructs not traditionally found in assembly language.
These include block-structured flow-of-control statements (\fCif\fR,
\fCwhile\fR, etc.) and the ability to define record-oriented data structures
(\fCstruct\fR). In addition, it contains a powerful macro capability that is
based on syntactic structural manipulations rather than simple text
substitution. \fIMacross\fR is, in fact, a complete block-structured
programming language in its own right which is interpreted at assembly time.
.SH
\s+3General Form of \fIMacross\fP Statements\s-3
\"General Form of \fIMacross\fP Statements
.PP
Stylistically, much of \fIMacross\fR is patterned after \fBC\fR. In
particular, the form of many keywords and of block structured entities is
derived from \fBC\fR. Unlike \fBC\fR however, \fIMacross\fR follows the
convention of more traditional assemblers that statements are delimited by
line boundaries (i.e., one statement per line, with the end of a line ending a
statement).
.PP
In general, spaces and tabs are treated as whitespace characters and are
ignored. Therefore, such characters may be used as the assembly language
programmer desires to format his or her program according to personal taste.
For the convenience of the programmer, \fIMacross\fR relaxes the syntax rule
that newlines always end a statement: newlines may also be treated as
whitespace characters (again, for purposes of formatting) in places where it
would be syntactically unambiguous to do so (i.e., where a statement obviously
cannot terminate, such as after a comma). For example:
.nf
\fCbyte 1, 2, 3, 4,
5, 6, 7, 8\fR
.fi
is allowed and is equivalent to
.nf
\fCbyte 1, 2, 3, 4, 5, 6, 7, 8
.fi
.PP
Comments begin with a semicolon (``\fC;\fR'') and continue to the end of the
line, as is common in many assemblers. In addition, \fIMacross\fR supports
\fBC\fR style comments bracketed by ``\fC/*\fR'' and ``\fC*/\fR''.
.PP
As with most assemblers, \fIMacross\fR statements are allowed to begin with a
label (or several labels, if you like). A label is denoted by an identifier
followed by a colon (``\fC:\fR''). There is no requirement that the label
start in column 1, or anything like that. Labels, if present, merely must
precede anything else in a statement.
.PP
An identifier is just what you'd expect from all your years of programming
experience: a string of letters and digits that must begin with a letter. As
is traditional in Unix*
.FS *
Unix is a footnote of Bell Laboratories.
.FE
land, the underscore character (``\fC_\fR'') is considered to be a letter
(smacks of hubris that, but tradition is tradition). Departing from Unix
tradition, upper- and lower-case characters are not distinct from each other
for purposes of distinguishing identifiers. If you use mixed case for
stylistic reasons, \fIMacross\fR will remember the case of the letters in an
identifier when it was first defined, so that symbol table dumps and
cross-reference listings will retain whatever case usage style you've adopted.
There is, in principle, no restriction imposed upon the length of identifiers.
.SH
\s+3The Language\s-3
.PP
In what follows, things in \fCthis typewriter like typeface\fR are keywords
and characters that are used literally. Things in \fIitalics\fR are other
kinds of syntactic entities. Double brackets (``\*['' and ``\*]'') enclose
things that are optional, while brackets followed by an asterisk (``*'')
enclose things that may be optionally repeated zero or more times.
.NH 1
The Instruction Statement
.PP
The most elementary \fIMacross\fR statement is the instruction statement,
wherein the programmer specifies a machine instruction to be assembled. The
instruction statement is
.nf
\*[ \fIlabel\fR \*Z \fIopcode\fR \*[ \fIoperand\fR \*[ \fC,\fR \fIoperand\fR \*Z \*]
.fi
just like every assembler ever made (except \fBa65\fR, of course).
\fIOpcode\fR is an identifier which is either a machine instruction mnemonic
(a list of which mnemonics are accepted by \fIMacross\fR is given in
\fBAppendix F\fR at the end of this document) or a macro name. For example:
.nf
\fCand foobar\fR
\fCsomeMacro foo, bar, baz, bletch\fR
.fi
.PP
The operands of an instruction may be any of the various types of operands
allowed by the various addressing modes of the target processor. In the case
of the 6502, these are:
.NH 2
\fRDirect addressing
.PP
Direct addresses take the form
.nf
\fIexpression\fR
.fi
and are used both for instructions that use direct addressing and ones that
use relative addressing (the offset is computed automatically by
\fIMacross\fR).
.NH 2
\fRIndirect addressing
.PP
Indirect addresses take the form
.nf
\fC@\fR \fIexpression\fR
.fi
Of course, the only 6502 instruction which accepts an indirectly addressed
operand is \fCjmp\fR.
.NH 2
\fRImmediate operands
.PP
Immediate operands take the form
.nf
\fC#\fR \fIexpression\fR
.fi
In the 6502, immediate mode operands are restricted to eight bit quantities.
\fIMacross\fR will give an error message if the operand value is larger than
this.
.NH 2
\fRIndexed addressing
.PP
Indexed addressing operands take the forms
.nf
\fCx\fR \fC\s-1[\s+1\fR \fIexpression\fR \fC\s-1]\s+1\fR
\fCy\fR \fC\s-1[\s+1\fR \fIexpression\fR \fC\s-1]\s+1\fR
.fi
An alternate form of indexed addressing which is supported by \fIMacross\fR
allows the symbolic selection of a field of a \fCstruct\fR pointed to by an
index register
.nf
\fCx\fR \fC.\fR \fIidentifier\fR \*[ \fC.\fR \fIidentifier\fR \*Z
\fCy\fR \fC.\fR \fIidentifier\fR \*[ \fC.\fR \fIidentifier\fR \*Z
.fi
This is explained in greater detail in the sections on \fCstruct\fRs and
expressions below.
.NH 2
\fRPre-indexed indirect addressing
.PP
Pre-indexed indirect addressing is specified by operands of the form
.nf
\fC@\fR \fCx\fR \fC\s-1[\s+1\fR \fIexpression\fR \fC\s-1]\s+1\fR
.fi
As with ordinary indexed addressing, there is a form of pre-indexed indirect
addressing which uses \fCstruct\fR fields
.nf
\fC@\fR \fCx\fR \fC.\fR \fIidentifier\fR \*[ \fC.\fR \fIidentifier\fR \*Z
.fi
.NH 2
\fRPost-indexed indirect addressing
.PP
Post-indexed indirect addressing is specified by operands of the form
.nf
\fCy\fR \fC\s-1[\s+1\fR \fC@\fR \fIexpression\fR \fC\s-1]\s+1\fR
.fi
There is no \fCstruct\fR-oriented form of post-indexed indirect addressing
since there doesn't seem to be any consistent interpretation of such a thing
that makes sense.
.NH 2
\fRRegister addressing
.PP
The only register in the 6502 which is used as an operand in its own right is
the accumulator
.nf
\fCa\fR
.fi
For the sake of completeness, so that macros may have them as operands,
\fIMacross\fR also allows either of the index registers to be used as operands
.nf
\fCx\fR
\fCy\fR
.fi
These are equivalent to
.nf
\fCx[0]\fR
\fCy[0]\fR
.fi
Note that \fCa\fR, \fCx\fR and \fCy\fR are reserved words in the \fIMacross\fR
language and so cannot be used as labels, variable names, etc. It might seem
natural to call a variable \fCx\fR but you can't. Sorry.
.NH 2
\fRText operands
.PP
For the sake of macros, text strings may also be used as operands
.nf
\fC"\fIany string you like\fC"\fR
.fi
The same conventions regarding escaped characters (using ``\fC\\\fR'') that
are followed by \fBC\fR are followed by \fIMacross\fR. These are documented
in \fBAppendix E\fR. Note that on many target machines the codes that these
escape sequences stand for are meaningless. They are provided primarily as a
convenience for writing calls to \fCprintf()\fR.
.NH 1
The Flow of Control Statements
.PP
\fIMacross\fR provides a number of statements which allow program flow of
control to be specified in a \fBC\fR-like block structured fashion. This
include a conditional execution statement (\fCif\fR) and three conditional
loop statements (\fCwhile\fR, \fCdo-while\fR and \fCdo-until\fR). These
statements assemble into the appropriate conditional branches and jumps to
realize the desired construct.
.NH 2
If\fR statement
.PP
The \fCif\fR statement has the following form
.nf
\*[ \fIlabel\fR \*Z \fCif\fR \fC(\fR \fIcondition\fR \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \*[ \fCelseif\fR \fC(\fR \fIcondition\fR \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \*Z \*[ \fCelse\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \*]
.fi
\fIcondition\fR is either the name of one of the target processor's hardware
condition codes such as can be tested for in a conditional branch instruction
(e.g., \fCcarry\fR, \fCoverflow\fR, etc.\(em the complete list is in
\fBAppendix B\fR) or one either of these negated using the ``logical not''
operator (``\fC!\fR'') or the name of one of the more complex conditions
which \fIMacross\fR understands (\fCgeq\fR, \fClt\fR, etc., discussed
shortly). The condition is used to determine the appropriate type of branch
instruction(s) to use. For example,
.nf
\fCif (plus) {\fR
\fIstatements-1\fR
\fC} elseif (carry) {\fR
\fIstatements-2\fR
\fC} else {\fR
\fIstatements-3\fR
\fC}\fR
.fi
expands into this (the labels are made up for illustrative purposes only):
.nf
\fCbmi temp1
\fIstatements-1
\fCjmp temp3
temp1: bcc temp2
\fIstatements-3
\fCjmp temp3
temp2: \fIstatements-3
\fCtemp3: \fIwhatever follows\fR
.fi
The keyword \fCelseif\fR may be used as shown, or specified as two separate
keywords, \fCelse if\fR, depending on the programmer's whim.
.PP
\fIMacross\fR knows about certain conditions which are more complex than those
than can be realized with single conditional branch instructions. These
conditions correspond to the results of comparison operations (such as
\fCgeq\fR \(em ``greater than or equal to'') that may require rather
complicated sequences of conditional branches to implement. These may be used
in any location where an ordinary condition may be used. One simply should
keep in mind that they can result in a non-trivial amount of code being
generated, if one is concerned about speed of execution. The complete list of
these complex conditions along with the object code that they produce is given
in \fBAppendix B\fR.
.NH 2
While\fR statement
.PP
The \fCwhile\fR statement has the following form
.nf
\*[ \fIlabel\fR \*Z \fCwhile\fR \fC(\fR \fIcondition\fR \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR
.fi
\fIcondition\fR is as described above for the \fCif\fR statement. An example
of the \fCwhile\fR statement would be
.nf
\fCwhile (!carry) {\fR
\fIstatements\fR
\fC}\fR
.fi
which would turn into
.nf
bcs temp1
\fCtemp2: \fIstatements
\fCbcc temp2
temp1: \fIwhatever follows\fR
.fi
.NH 2
Do-while\fR statement
.PP
The \fCdo-while\fR statement is similar to the \fCwhile\fR statement except
that the condition is tested at the bottom of the loop. It has the form
.nf
\*[ \fIlabel\fR \*Z \fCdo\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \fCwhile\fR \fC(\fR \fIcondition\fR \fC)\fR
.fi
For example
.nf
\fCdo {\fR
\fIstatements\fR
\fC} while (equal)\fR
.fi
which is equivalent to
.nf
\fCtemp: \fIstatements
\fCbeq temp\fR
.fi
.NH 2
Do-until\fR statement
.PP
The \fCdo-until\fR statement is the same as the \fCdo-while\fR statement
except that the sense of the condition is negated. It has the form
.nf
\*[ \fIlabel\fR \*Z \fCdo\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \fCuntil\fR \fC(\fR \fIcondition\fR \fC)\fR
.fi
For example
.nf
\fCdo {\fR
\fIstatements\fR
\fC} until (equal)\fR
.fi
which is equivalent to
.nf
\fCtemp: \fIstatements
\fCbne temp\fR
.fi
.NH 1
The Data Statements
.PP
The data statements allow the allocation of memory space and the storage of
constant data. These statements are like the ones found in most assemblers.
There are several different forms, each for a different type of data.
.NH 2
Block\fR statement
.PP
The \fCblock\fR statement allocates blocks of memory without initializing the
bytes to any particular value (actually, the loader will in all likelihood
initialize these to 0, but it is probably not really wise to rely on this).
It has the form
.nf
\*[ \fIlabel\fR \*Z \fCblock\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
.fi
The \fIexpression\fRs are the sizes of the blocks to reserve, expressed in
bytes.
.NH 2
Align\fR statement
.PP
The \fCalign\fR statement aligns the current location counter to an integer
multiple of some value (e.g., to align with a word boundary). It has the form
.nf
\*[ \fIlabel\fR \*Z \fCalign\fR \fIexpression\fR
.fi
The \fIexpression\fR is the multiple to which the current location counter is
to be aligned. For example,
.nf
\fCalign 2\fR
.fi
would align the current location to a word boundary, while
.nf
\fCalign 0x100\fR
.fi
would align to a page boundary.
.NH 2
Constrain\fR statement
.PP
The \fCconstrain\fR statement provides a means of constraining a portion of
code or data to be located within a region of memory bounded by addresses of
integer multiples of some value (e.g., within a page). Its form is
.nf
\fCconstrain\fR \fC(\fR \fIboundary\fR \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR
.fi
\fIBoundary\fR may be any expression which evaluates to a number. The
\fIstatement\fRs are assembled normally. If assembling in absolute mode, an
error message is issued if the current location counter crosses an integer
multiple of \fIboundary\fR. If assembling in relocatable mode, information
about the constraint will be output in the object file and the contents of the
constrained block will be relocated as needed to satisfy the constraint (note
that this means that it is unsafe to assume that the things in the assembly
source immediately before the \fCconstrain\fR statement, the contents of the
constrain block itself, and the things in the assembly source immediately
after the \fCconstrain\fR statement will be located in contiguous locations in
the eventual target machine address space). For example,
.nf
\fCconstrain (0x100) {\fR
\fIstatements\fR
\fC}\fR
.fi
constrains the given statements to all fit within a page.
.NH 2
Word\fR statement
.PP
The \fCword\fR statement allocates words, i.e., two byte chunks, of memory.
It takes the form
.nf
\*[ \fIlabel\fR \*Z \fCword\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
.fi
The \fIexpression\fRs must evaluate to quantities that can be contained in 16
bits, of course. For example,
.nf
\fCword 0x1234, foobar\fR
.fi
would allocate two words, the first of which would be initialized to the
hexadecimal value \fC0x1234\fR and the second to whatever the value of
\fCfoobar\fR is.
.NH 2
Dbyte\fR statement
.PP
The \fCdbyte\fR statement is just like the \fCword\fR statement, except that
the word is byte-swapped in memory. Its form is
.nf
\*[ \fIlabel\fR \*Z \fCdbyte\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
.fi
.NH 2
Long\fR statement
.PP
The \fClong\fR statement allocates longwords, i.e., four byte chunks, of
memory. It takes the form
.nf
\*[ \fIlabel\fR \*Z \fClong\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
.fi
The \fIexpression\fRs must evaluate to quantities that can be contained in 32
bits, of course. For example,
.nf
\fClong 0x12345678, foobar\fR
.fi
would allocate two longwords, the first of which would be initialized to the
hexadecimal value \fC0x12345678\fR and the second to whatever the value of
\fCfoobar\fR is.
.NH 2
Byte\fR statement
.PP
The \fCbyte\fR statement is similar to the \fCword\fR and \fCdbyte\fR
statements, except that it allocates single byte chunks. Its form is
.nf
\*[ \fIlabel\fR \*Z \fCbyte\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
.fi
An \fIexpression\fR, in this case, is either an ordinary expression (see
\fBExpressions\fR, below) which must evaluate to an 8-bit quantity, indicating
the value for a single byte to be reserved, or a string (see above, under the
discussion of text operands), indicating that the characters in the string
should be placed in memory at the current location.
.NH 2
String\fR statement
.PP
The \fCstring\fR statement is much like the \fCbyte\fR statement, except that
the values indicated are followed in memory by a zero byte. This enables the
convenient declaration and allocation of NULL terminated character strings.
This feature is of little use in the 6502 version of \fIMacross\fR but is
provided for compatability with future versions targeted at more sophisticated
processors. The form of the \fCstring\fR statement is
.nf
\*[ \fIlabel\fR \*Z \fCstring\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
.fi
.NH 2
Struct\fR statement
.PP
The \fCstruct\fR statement enables the declaration and allocation of
record-oriented data structures. There are two forms of the \fCstruct\fR
statement, the first of which declares a \fCstruct\fR record type, and the
second of which causes space to be set aside in memory for a \fCstruct\fR that
has already been declared. The form of the first type of \fCstruct\fR
statement is
.nf
\*[ \fIlabel\fR \*Z \fCstruct\fR \fC{\fR
\*[ \fIdataStatement\fR \*Z
\fC}\fR \fIname\fR
.fi
\fIdataStatement\fRs are any of the data statements described in this section
(section 3). \fIName\fR becomes the name of the \fCstruct\fR. Any labels
inside the \fCstruct\fR become \fIfields\fR of the data structure which may be
referred to later in expressions using the ``\fC.\fR'' operator, as in
\fBC\fR. A more complete description of the semantics of \fCstruct\fRs is
given in the section below on expressions.
.PP
The first form of the \fCstruct\fR statement, called a ``\fCstruct\fR
definition'', lays out the constituent parts of a data structure and gives
those names to those parts. The second form of the \fCstruct\fR statement,
called a ``\fCstruct\fR instantiation'',
.nf
\*[ \fIlabel\fR \*Z \fCstruct\fR \fIname\fR
.fi
causes storage for the \fCstruct\fR named by \fIname\fR to be allocated. A
\fCstruct\fR definition \fImay not\fR contain another \fCstruct\fR definition,
but it \fImay\fR contain a \fCstruct\fR instantiation. For example,
.nf
\fCstruct {
pointer: block 2
class: block 1
} fooThing\fR
.fi
would create a \fCstruct\fR called \fCfooThing\fR. Then,
.nf
\fCfooLabel: struct fooThing\fR
.fi
would allocate one at the current location at the address labeled
\fCfooLabel\fR. This could then be used as follows:
.nf
\fCand fooLabel.class
jmp @fooLabel.pointer\fR
.fi
which would AND the accumulator with the \fCclass\fR field of the \fCstruct\fR
and then jump to wherever the \fCpointer\fR field pointed to. If the \fCx\fR
index register already contained the address of this \fCstruct\fR, then one
could say
.nf
\fCand x.class\fR
.fi
.NH 1
The Symbol Definition Statements
.PP
The various symbol definition statements allow the declaration of symbolic
variables and values and the definition of macros and functions.
.NH 2
Define\fR statement
.PP
The \fCdefine\fR statement enables the programmer to create symbolic names for
values. It has two forms. The first
.nf
\fCdefine\fR \fIsymbolname\fR
.fi
creates a new symbol, \fIsymbolname\fR (an identifier), and gives it the
special value \fCunassigned\fR. Any attempt to take the value of an
unassigned symbol will cause an error message from the assembler. The symbol
will, however, cause the \fCisDefined()\fR built-in function (see
\fBExpressions\fR, below) to return \fCTRUE\fR if passed as an argument. It
is also an error to \fCdefine\fR a symbol that has already been \fCdefine\fRd.
.PP
The second form of the \fCdefine\fR statement
.nf
\fCdefine\fR \fIsymbolname\fR \fC=\fR \fIexpression\fR
.fi
creates the symbol and gives it the value obtained by evaluating
\fIexpression\fR (see \fBExpressions\fR, below). Actually, what \fCdefine\fR
does is create a symbolic name for \fIexpression\fR and the save this
expression away in a secret place. This means that symbols in
\fIexpression\fR may be forward references, e.g., labels that haven't been
encountered yet. It is also possible to forward reference to symbols that are
defined by future \fCdefine\fR statements, for example:
.nf
\fCdefine foo = bar + 2
define bar = 47\fR
.fi
effectively defines \fCfoo\fR to be \fC49\fR. Beware, however, as there is no
way for the assembler to detect mutually recursive references of this sort, so
that
.nf
\fCdefine foo = bar + 2
define bar = foo + 2\fR
.fi
will be happily swallowed without complaint, until you actually try to use
\fCfoo\fR or \fCbar\fR in an instruction, whereupon \fIMacross\fR's expression
evaluator will go into infinite recursion until it runs out of stack space and
crashes the assembler (it looks to see what \fCfoo\fR is and sees that it's
\fCbar + 2\fR, so it looks to see what \fCbar\fR and see that it's \fCfoo +
2\fR, so it looks to see what \fCfoo\fR is... To have the assembler detect
and signal this error would, in the general case, add much complication and
inefficiency (read: make your programs assemble a lot more slowly) for little
return).
.PP
The scope of symbols defined in either of these two ways extends in time from
the definition itself to the end of the assembly.
.NH 2
Variable\fR statement
.PP
The \fCvariable\fR statement enables the programmer to declare symbolic
variables for future use. Similar to the \fCdefine\fR statement, it has two
forms. The first
.nf
\fCvariable\fR \fIsymbolname\fR
.fi
creates a variable named \fIsymbolname\fR and gives it the special value
\fCunassigned\fR, just like the analogous \fCdefine\fR statement.
.PP
The second form of the \fCvariable\fR statement
.nf
\fCvariable \fIsymbolname\fR \fC=\fR \fIexpression\fR
.fi
creates the variable and gives it the value obtained by evaluating
\fIexpression\fR. The scope of variables defined in either of these two ways
extends from the \fCvariable\fR statement itself to the end of the assembly
(i.e., the variable is global).
.PP
The difference between the \fCdefine\fR statement and the \fCvariable\fR
statement is that the \fCdefine\fR statement creates what is in essence a
constant whereas the \fCvariable\fR statement creates a true variable. The
value of a variable may change (e.g., it may be assigned to) during the course
of assembly. In addition, the expression which establishes a symbol's value
in a \fCdefine\fR statement may contain forward references (i.e., labels whose
values are unknown because they haven't been encountered yet) whereas the
expression assigning an initial value to a variable must be made up of terms
all of whose values are known at the time the \fCvariable\fR statement is
encountered in the assembly.
.PP
A variable may also be declared as an array, using the form
.nf
\fCvariable \fIsymbolname \fC[ \fIlength \fC]\fR
.fi
where \fIlength\fP is an expression that indicates the number of elements the
array is to have. \fIMacross\fR arrays are zero-based, so the elements are
indexed from 0 to \fIlength\fP-1. As with ordinary variables, the elements of
the array may be initialized in the \fCvariable\fR statement using a statement
of the form
.nf
\fCvariable \fIsymbolname \fC[ \fIlength \fC] = \fIexpression
\fR\*[ \fC, \fIexpression \fR\*Z
.fi
The \fIexpression\fPs are assigned sequentially into the elements of the
array. If the array length is greater than the number of \fIexpression\fPs
given, the remaining elements are filled with zeroes. Of course, you should
not specify more than \fIlength\fP expressions or the assembler will complain
at you.
.NH 2
Macro\fR statement
.PP
The \fCmacro\fR statement is used to define macros (surprise!). Its syntax is
.nf
\fCmacro\fR \fImacroname\fR \*[ \fIargumentname\fR \*[ \fC,\fR \fIargumentname\fR \*Z \*] \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR
.fi
where \fImacroname\fR is just that and the \fIargumentname\fRs are identifiers
corresponding to the formal parameters of the macro (in the classical
fashion). When the macro is called, the call arguments are bound to these
symbols and then \fIMacross\fR assembles the \fIstatement\fRs which form the
macro body. The scope of these symbols is limited to the inside of the macro
body and their values go away when the macro expansion is completed. The
\fIstatement\fRs may be any valid \fIMacross\fR statements except for
\fCmacro\fR statements and \fCfunction\fR statements (i.e., macro and function
definitions may not be nested).
.PP
Statement labels used inside macros can be made local to the macro by
preceding the label identifier with a dollar sign (``\fC$\fR''). For
example,
.nf
\fCmacro fooMac arg {
jmp $foo
word arg
$foo: nop
}\fR
.fi
defines a macro named \fCfooMac\fR that emits a word of data that gets jumped
over. The label \fC$foo\fR is local to the macro: both the reference to it
in the first line of the macro and its definition on the third will only be
seen inside the macro. Each time the macro is called, the \fCjmp\fR will
refer to the location two instructions ahead, and any other macros that might
contain \fC$foo\fR will not affect this nor will they be affected by this.
.PP
It is possible to define macros which take a variable number of arguments.
This is accomplished by following the last argument in the \fCmacro\fP
statement by \fC[ ]\fR. This declares the argument to be an array, which gets
assigned a list of all of the parameters not accounted for by the other
declared arguments. This array may be interrogated with the
\fCarrayLength()\fR built-in function (to find out how many extra parameters
there were) and accessed just like a regular array. For example,
.nf
\fCmacro enfoon precision, args[] {
mvariable len = arrayLength(args)
mvariable i
word precision
mfor (i=0, i<len, ++i) {
byte args[i]
}
}\fR
.fi
declares the macro \fCenfoon\fP that takes one or more parameters. The first
parameter is bound to \fCprecision\fP, while the remainder are collected in an
array that is bound to \fCargs\fP. It emits the first parameter as a word
value and the remaining parameters as bytes.
.NH 2
Function\fR statement
.PP
The \fCfunction\fR statement is used to define functions. Its syntax is
.nf
\fCfunction\fR \fIfuncname\fR \fC(\fR \*[ \fIargumentname\fR \*[ \fC,\fR \fIargumentname\fR \*Z \*] \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR
.fi
where \fIfuncname\fR is the name of the function and the \fIargumentname\fRs
are identifiers corresponding to the formal parameters of the function. When
the function is called, the call arguments are evaluated and then bound to
these symbols and then \fIMacross\fR assembles the \fIstatement\fRs which form
the function body. The scope of these symbols is limited to the inside of the
function body and their values go away when the function evaluation is
completed. As with macro definitions, the \fIstatement\fRs may be any valid
\fIMacross\fR statements except for the \fCmacro\fR and \fCfunction\fR
statements. A function may return a value using the \fCfreturn\fR statement,
which is described below.
.PP
Just as you can define macros that take variable numbers of arguments, so too
can you define functions. The mechanism is the same. For example
.nf
\fCfunction sum(args[]) {
mvariable len = arrayLength(args)
mvariable i
mvariable result = 0
mfor (i=0, i<len, ++i) {
result += args[i]
}
freturn(result);
}\fR
.fi
which simply returns the sum of its arguments.
.NH 2
Undefine\fR statement
.PP
The \fCundefine\fR statement allows symbol and macro definitions to be removed
from \fIMacross\fR' symbol table. It takes the form
.nf
\fCundefine\fR \fIsymbolname\fR \*[ \fC,\fR \fIsymbolname\fR \*Z
.fi
The named symbols go away as if they never were \(em they are free to be
defined again and the \fCisDefined()\fR built-in function will return
\fCFALSE\fR if passed one of them as an argument.
.NH 1
Macro Body Statements
.PP
\fIMacross\fR provides several statements which are primarily intended to
manage the flow of control (or, rather, the ``flow of assembly'') within a
macro or function definition. Some of these statements are analogs to the
flow of control statements described above in section 2. However, one should
keep in mind that these statements are executed interpretively at assembly
time, whereas the previously described statements result in machine code in
the target-processor-executable output of the assembly process.
.PP
Although these statements are intended primarily for use within macros and
functions, their use is not restricted and they may be used at any point in a
program. In particular, the \fCmif\fR statement (to be described shortly) is
the means by which conditional assembly may be realized.
.NH 2
\fRBlocks
.PP
The construct
.nf
\fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR
.fi
is called a \fIblock\fR and is in fact a valid \fIMacross\fR statement type in
its own right. Blocks are used extensively in \fIMacross\fR to form the
bodies of flow-of-control statements, flow-of-assembly statements and macros.
.NH 2
Mdefine\fR statement
.PP
The \fCmdefine\fR statement
.nf
\fCmdefine\fR \fIsymbolname\fR
or
\fCmdefine\fR \fIsymbolname\fR \fC=\fR \fIexpression\fR
.fi
operates like (and is syntactically congruent with) the \fCdefine\fR
statement, except that the scope of the symbol definition is restricted to the
body block of the macro or function in which the \fCmdefine\fR appears. The
symbol definition is invisible outside that block, though it \fIis\fR visible
inside any blocks that may themselves be contained within the macro or
function.
.NH 2
Mvariable\fR statement
.PP
The \fCmvariable\fR statement
.nf
\fCmvariable\fR \fIsymbolname\fR
or
\fCmvariable\fR \fIsymbolname\fR \fC=\fR \fIexpression\fR
.fi
bears exactly the same relationship to the \fCvariable\fR statement that the
\fCmdefine\fR statement does to the \fCdefine\fR statement. It declares a
variable whose scope is limited to the function or macro in whose definition
it appears.
.NH 2
Mif\fR statement
.PP
The \fCmif\fR statement conditionally assembles the statements contained in
the block following it:
.nf
\fCmif\fR \fC(\fR \fIcondition\fR \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \*[ \fCmelseif\fR \fC(\fR \fIcondition\fR \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \*Z \*[ \fCmelse\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \*]
.fi
unlike the \fCif\fR statement, the \fIcondition\fR may be any expression
whatsoever. \fIMacross\fR follows the \fBC\fR convention that the value 0
represents \fCFALSE\fR and any other value represents \fCTRUE\fR (in fact, the
symbols \fCTRUE\fR and \fCFALSE\fR are ``predefined'' by the assembler to
have the values 1 and 0 respectively). The meaning of the \fCmif\fR construct
is the obvious one, but keep in mind that it is interpreted at assembly time
and has no direct bearing on the execution of the resulting assembled program.
.NH 2
Mwhile\fR statement
.PP
The \fCmwhile\fR statement repetitively assembles a block of statements so
long as a given condition remains true. Its form is
.nf
\fCmwhile\fR \fC(\fR \fIcondition\fR \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR
.fi
As with \fCmif\fR, the \fIcondition\fR may be any valid \fIMacross\fR
expression and the interpretation is what it seems.
.NH 2
Mdo-while\fR statement
.PP
The \fCmdo-while\fR statement provides an alternative to the \fCmwhile\fR
statement by testing at the bottom of the loop instead of at the top. Its
form is
.nf
\fCmdo\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \fCwhile\fR \fC(\fR \fIcondition\fR \fC)\fR
.fi
.NH 2
Mdo-until\fR statement
.PP
The \fCmdo-until\fR statement is the same as the \fCmdo-while\fR statement
except that the sense of the condition is negated. It has the form
.nf
\fCmdo\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \fCuntil\fR \fC(\fR \fIcondition\fR \fC)\fR
.fi
.NH 2
Mfor\fR statement
.PP
The \fCmfor\fR statement provides a more general looping construct analogous
to the \fBC\fR \fCfor\fR loop. Its form is
.nf
\fCmfor\fR \fC(\fR \fIexpression-1\fR \fC,\fR \fIexpression-2\fR \fC,\fR \fIexpression-3\fR \fC)\fR \fC{\fR
\*[ \fIstatement\fR \*Z
\fC}\fR
.fi
where \fIexpression-1\fR is an initialization expression, \fIexpression-2\fR
is a test to see if looping should continue, and \fIexpression-3\fR is
executed at the bottom of the loop to set up for the next time around, just as
in \fBC\fR. Note that, unlike \fBC\fR, the \fIexpression\fRs are separated by
commas, not semicolons. This is because semicolons are used to delimit line
comments.
.PP
The \fCmfor\fR statement is equivalent to
.nf
\fIexpression-1\fR
\fCmwhile (\fIexpression-2\fC ) {\fR
\fIstatements\fR
\fIexpression-3\fR
\fC}\fR
.fi
.NH 2
Mswitch\fR statement
.PP
The \fCmswitch\fR statement provides a means of selecting one of a number of
blocks of code for assembly depending upon the value of some expression. Its
form is:
.nf
\fCmswitch ( \fIselectionExpression \fC) {\fR
\*[ \fCmcase ( \fIexpression\fR \*[ \fC,\fI expression\fR \*Z \fC) {\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \*Z
\*[ \fCmdefault {\fR
\*[ \fIstatement\fR \*Z
\fC}\fR \*]
\fC}\fR
.fi
The way this works is as follows (it's actually easier to use than to
explain): \fIselectionExpression\fR is evaluated. Each of the
\fIexpression\fRs associated with the various \fCmcase\fR clauses (if any) is
then evaluated in turn and the resulting value compared to that of
\fIselectionExpression\fR. When and if one of these values ``matches'' the
value of \fIselectionExpression\fR the block of \fIstatement\fRs associated
with the corresponding \fCmcase\fR clause is immediately assembled and
execution of the \fCmswitch\fR statement is complete. If no such value
matches and there is an \fCmdefault\fR clause, the block of \fIstatement\fRs
associated with the \fCmdefault\fR clause is assembled. If no value matches
and there is no \fCmdefault\fR clause then nothing is assembled as a result of
the \fCmswitch\fR. When we say the values ``match'', we mean that either
the expressions evaluate to the same number or to strings which are identical
except for the case of alphabetic characters. For example:
.nf
\fCmswitch (foo) {
mcase ("hello", "fnord") {
\fIstatements-1\fC
}
mcase ("ZAP!") {
\fIstatements-2\fC
}
mdefault {
\fIstatements-3\fR
}
}\fR
.fi
would switch on the value of the symbol \fCfoo\fR. If the value of \fCfoo\fR
was \fC"hello"\fR, \fIstatements-1\fR would be assembled. If the value of
\fCfoo\fR was \fC"zap!"\fR, \fIstatements-2\fR would be assembled (since the
string comparison is done independent of case). If the value of \fCfoo\fR was
\fC"cromfelter"\fR or \fC47\fR, then \fIstatements-3\fR would be assembled by
default.
.NH 2
Freturn\fR statement
.PP
A \fIMacross\fR function may (and probably will) be called from an expression
in the traditional manner of functions throughout the annals of computer
science. In such a situation, the programmer may wish to have a function
return a value. The \fCfreturn\fR statement enables this. Its form is
.nf
\fCfreturn\fR \*[ \fIexpression\fR \*]
.fi
where \fIexpression\fR is any permissible \fIMacross\fR expression as
described below under \fBExpressions\fR. If no \fIexpression\fR is given, the
macro simply returns without having a value as its result. Any attempt to use
the (non-existent) value returned by a call to function which doesn't return a
value will result in an error message from the assembler. Function calls will
automatically return without a value upon reaching the end of the block that
forms the body of the function.
.NH 1
Miscellaneous Statements
.PP
Various useful statements don't fall into any of the above categories.
.NH 2
Include\fR statement
.PP
The \fCinclude\fR statement allows the text in other files to be included in
the source program being assembled, in the time-honored fashion. Its form is
.nf
\fCinclude\fR \fIfilename\fR
.fi
where \fIfilename\fR is a string value giving the name of the file to be
included. \fCInclude\fRd files may themselves contain \fCinclude\fR
statements to any number of levels of recursion (within reason).
.NH 2
Extern\fR statement
.PP
The \fCextern\fR statement allows you to declare symbols to be visible to the
linker outside of the file in which they are found. Its use:
.nf
\fCextern\fR \fIsymbol\fR \*[ \fC,\fR \fIsymbol\fR \*Z
.fi
.NH 2
Start\fR statement
.PP
The \fCstart\fR statement declares the starting address of a program.
.nf
\fCstart\fR \fIexpression\fR
.fi
where the \fIexpression\fR indicates the start address. There should be no
more than one \fCstart\fR statement in a program. If no start address is
specified, the object file will be produced without a start address entry.
.NH 2
Assert\fR statement
.PP
The \fCassert\fR statement provides a means of testing assembly time
conditions and generating programmer specified error messages if those
conditions are not satisfied. Its syntax is:
.nf
\fCassert (\fIcondition\fC)\fR \*[ \fItextString\fR \*]
.fi
where \fIcondition\fR is an expression such as those used in the \fCmif\fR
statement. This condition is evaluated and if \fCFALSE\fR then the message
\fItextString\fR, if given, is written to the standard output. The message is
written in the form of a conventional \fIMacross\fR error message, giving the
file and line number on which the failed assertion occured. If
\fItextString\fR is omitted then a simple error message to the effect that the
assert failed will be output. For example,
.nf
\fCassert (foo == 1) "Hey! You blew it."\fR
.fi
would check to see that the value of the symbol \fCfoo\fR is \fC1\fR, and if
it isn't would issue an error message containing the string \fC"Hey! You blew
it."\fR.
.NH 2
Org\fR statement
.PP
The \fCorg\fR statement adjusts the current location counter and tells the
assembler to start locating instructions and data in absolute memory
locations.
.nf
\fCorg\fR \fIexpression\fR
.fi
The \fIexpression\fR indicates the new current location. If this is an
absolute address, \fIMacross\fR starts assembling at the specified
absolute memory location. If, on the other hand, it is relative to a
relocatable address or to the current location counter, the current location
counter is simply adjusting accordingly.
.NH 2
Rel\fR statement
.PP
The \fCrel\fR statement restores \fIMacross\fR to assembling code in a
relocatable fashion, if it was not already doing so. The relocatable location
resumes from wherever it was left the last time an absolute \fCorg\fR stopped
relocatable code assembly. \fI((explain this better))\fR
.NH 2
Target\fR statement
.PP
The \fCtarget\fR statement
.nf
\fCtarget\fR \fIexpression\fR
.fi
tells the assembler to assemble as if it had been \fCorg\fRed to a particular
address without actually performing the \fCorg\fR. The \fIexpression\fR
indicates the location to start assembling from. This must be an absolute
address (and the \fCtarget\fR statement may only be used when assembling in
absolute mode).
.PP
The result of the \fCtarget\fR statement is that assembly proceeds from the
current location but labels will be defined as if it was proceeding from the
location specified by \fIexpression\fR and references to the current location
counter will be offset by the difference between the actual current location
counter value and the value of \fIexpression\fR. This effect will persist until the next \fCorg\fR or \fCtarget\fR statement. For example
.nf
\fCorg 0x1000
target 0x0800
foo: rts
bar: word 0x1234
word here
org someplaceElse\fR
.fi
would first set the current location counter to \fC0x1000\fR. At location
\fC0x1000\fR it would assemble an \fCrts\fR instruction while giving the label
\fCfoo\fR the value \fC0x800\fR. Then, at location \fC0x1001\fR it would
deposit the word value \fC0x1234\fR while giving the label \fCbar\fR the value
\fC0x801\fR. At location \fC0x1003\fR it would deposit the word value
\fC0x803\fR. Finally, the second \fCorg\fR would set the current location
counter to \fCsomeplaceElse\fR and the effects of the \fCtarget\fR statement
would cease.
.SH
\s+3Expressions\s-3
.PP
The expression syntax of \fIMacross\fR was chosen to be as close to that of
\fBC\fR as possible. \fIMacross\fR recognizes the same set of operators as
\fBC\fR with few exceptions, and the operators have the same precedence with
respect to each other that everyone is used to.
.PP
Another important feature of expressions in \fIMacross\fR is that expressions
by themselves on a line are valid statements. Assignment expressions, uses of
the post- and pre-increment and decrement operators (``\fC++\fR'' and
``\fC--\fR''), and calls to functions used like procedures are the most
useful applications of this. For example
.nf
\fCfoo = 5\fR
\fCbar++\fR
\fCprintf("Hello world\\n")\fR
.fi
are all valid statements.
.NH 0
\fRPrimitive expressions
.PP
The most primitive expressions are identifiers, numbers, characters, character
strings, array references, and function calls.
.PP
Identifiers have already been described, in the section \fBGeneral Form of
\fIMacross\fB Statements\fR above. The only thing to add here is that the
special identifier \fChere\fR denotes the value of the current location
counter.
.PP
Numbers may be decimal, octal, hexadecimal, binary or quarters. The form of
the first three of these is as in \fBC\fR: decimal numbers are denoted by a
sequence of decimal digits that does not begin with a ``\fC0\fR''; octal
numbers by a sequence of octal digits that \fIdoes\fR begin with a
``\fC0\fR''; and hexadecimal numbers by a sequence of hexadecimal digits
(the decimal digits plus the letters ``\fCa\fR'' through ``\fCf\fR'', in
either upper or lower case), preceded by ``\fC0x\fR'' or ``\fC0X\fR''.
Binary numbers and quarters are represented analogously. Binary numbers are
represented by a sequence of ``\fC0\fR''s and ``\fC1\fR''s preceded by
``\fC0b\fR'' or ``\fC0B\fR''. Quarters are base-four numbers (for two-bit
entities like Atari pixels) and are represented by the sequences of the digits
\fC0\fR through \fC3\fR preceded by ``\fC0q\fR'' or ``\fC0Q\fR'' (the
credit for this idea goes to Charlie Kellner).
.PP
Character constants are denoted the same as in \fBC\fR: by a single character
enclosed in apostrophes (``\fC'\fR''). The same conventions about
characters escaped with a backslash (``\fC\\\fR'') also apply. Strings may
be of varying lengths and are enclosed in quotation marks (``\fC"\fR''),
as discussed above in the explanation of instruction operands.
.PP
An array reference in \fIMacross\fR have the same syntactic form that they
have in \fBC\fR:
.nf
\fIarray \fC[ \fIindex \fC]\fR
.fi
where \fIarray\fP is an array (which is usually an identifier but can be any
expression that results in an array value or a character string \(em note that
for convenience a string can be treated as simply an array of characters) and
\fIindex\fP is an integer with a value between 0 and the length of the array
minus one, inclusive (all \fIMacross\fR arrays are zero-based).
.PP
A function call, in \fIMacross\fR, is syntactically the same as in \fBC\fR:
the name of the function being called followed by a comma-separated argument
list in parenthesis. The argument list, as in \fBC\fR, may be empty. The
arguments themselves may be arbitrary instruction operands (described above in
section 1). A number of built-in functions are provided by \fIMacross\fR to
perform a variety of useful operations. These are discussed in \fBAppendix
C\fR.
.NH 1
\fROperators
.PP
Expressions may be constructed from the primitive elements described above and
from other expressions using a variety of unary and binary operators. The
operator set is patterned after \fBC\fR's. The only \fBC\fR operators not
supported are:
.IP
[1] ``\fC?:\fR'' (arithmetic if) \(em not supported for reasons of syntactic
confusion on both the part of the parser attempting to parse it and the
programmer attempting to use it.
.IP
[2] ``\fC,\fR'' (comma) \(em used in \fIMacross\fR as an important separator
and thus not available.
.IP
[3] unary ``\fC*\fR'' and ``\fC&\fR'', \fCsizeof\fR, casts and
``\fC->\fR'' \(em not relevant here.
.LP
All of the assignment operators (``\fC+=\fR'', ``\fC-=\fR'', etc.)
\fIare\fR supported by \fIMacross\fR.
.LP
\fIMacross\fR reinterprets the \fC.\fR operator in that ``\fIexpression\fC .
\fIstructfieldname\fR'' is interpreted as adding the offset value implied by
\fIstructfieldname\fR (i.e., the distance in bytes into a \fCstruct\fR to
reach the named field) to the address that is the value of \fIexpression\fR.
.LP
\fIMacross\fR adds to the operator set the following:
.IP
[1] ``\fC?\fR'' \(em as a unary operator, takes the high order byte of the
word value that is its argument.
.IP
[2] ``\fC/\fR'' \(em as a unary operator, takes the low order byte of the
word value that is its argument.
.IP
[3] ``\fC^^\fR'' \(em a binary operator, denotes logical exclusive-OR. This
is simply an orthogonal extension for the sake of completeness.
.LP
Of course, parenthesis can be used at any point to override the normal
precedence of the various operators. A full list of all the operators that
\fIMacross\fR understands is given in \fBAppendix D\fR.
.NH 1
\fRExpression evaluation
.PP
In order to make the most effective use of expressions in the \fIMacross\fR
environment, it is helpful (and at times necessary) to understand how and when
\fIMacross\fR evaluates them.
.PP
When \fIMacross\fR evaluates an expression, it may have one of three sorts of
results. These are \fIsuccess\fR, \fIundefined\fR, and \fIfailure\fR. A
\fIsuccess\fR result means that \fIMacross\fR encountered no problems
evaluating the expression, and whatever value it evaluated to is just used as
needed. A \fIfailure\fR result indicates that there was a problem of some
sort. Usually this is a result of some user error. In any case, an
appropriate diagnostic message will be issued by the assembler and the
statement in which the expression was found will not be assembled.
.PP
An \fIundefined\fR result is where the complications, if any, arise. An
expression will evaluate to an \fIundefined\fR result if one or more of the
terms of the expression are undefined symbols. Usually these are labels which
simply haven't been encountered yet (i.e., they are forward references). In
certain contexts, such as the operand of a machine instruction, this is a
legitimate thing to do, and in certain others, such as the condition of a
\fCmif\fR statement, this is not allowed at all. In the latter case, an
\fIundefined\fR result is just like a \fIfailure\fR result. In the former
case, the assembler is forced to get fancy in order to make it all work right.
.PP
What \fIMacross\fR does is squirrel away a copy of the expression along with a
pointer as to where in the object code the value of the expression is supposed
to go. At the end of assembly, the undefined label will presumably now be
defined, and \fIMacross\fR evaluates the saved expression and pokes the result
into the appropriate location. (If, at this point, the undefined label is
still undefined, an error message to that effect is issued). Clearly, if an
expression has side effects (such as changing the value of some global
variable), this can result in some confusing behavior. The \fIMacross\fR
assembler is smart enough to not let you do anything that has overt side
effects in an expression that is being saved away for future evaluation. The
things which are disallowed in such a case are assignments and uses of the
post- and pre-increment and decrement operators (``\fC++\fR'' and
``\fC--\fR''). Functions, however, may have side effects and \fIMacross\fR
does not try to prevent you from using function calls in expressions that get
saved for later evaluation. It can, and will, detect some, but not all, side
effects during the later evaluation and give a suitable error message. This
is because it is perfectly legitimate to use a function call to a function
that doesn't have side effects in an expression containing forward references.
.PP
If you are now totally confused, the only thing you need remember is: \fBDon't
ever use a call to a function that has side effects in an expression
containing a forward reference.\fR
.bp
.CD
\s+5\fBAppendix A \(em Macross 6502 Grammar\fR\s-5
\"\s+4\fBAppendix A \(em Macross 6502 Grammar\fR\s-4
.fi
.LP
.nf
\fR\fIprogram\fR:
\*[ \fIstatement\fR Newline \*Z Endfile
\fIstatement\fR:
\*[ \fIlabel\fR \*Z \fIopcode\fR \*[ \fIoperand\fR \*[ \fC,\fR \fIoperand\fR \*Z \*]
\*[ \fIlabel\fR \*Z \fCif\fR \fC(\fR \fIcondition\fR \fC)\fR \fIblock\fR
\*[ \fCelseif\fR \fC(\fR \fIcondition\fR \fC)\fR \fIblock\fR \*Z
\*[ \fCelse\fR \fIblock\fR \*]
\*[ \fIlabel\fR \*Z \fCwhile\fR \fC(\fR \fIcondition\fR \fC)\fR \fIblock\fR
\*[ \fIlabel\fR \*Z \fCdo\fR \fIblock\fR \fCwhile\fR \fC(\fR \fIcondition\fR \fC)\fR
\*[ \fIlabel\fR \*Z \fCdo\fR \fIblock\fR \fCuntil\fR \fC(\fR \fIcondition\fR \fC)\fR
\fIdataStatement\fR
\fCdefine\fR \fIidentifier\fR \*[ \fC=\fR \fIexpression\fR \*]
\fCvariable\fR \fIidentifier\fR \*[ \fC=\fR \fIexpression\fR \*]
\fCmacro\fR \fIidentifier\fR \*[ \fIidentifier\fR \*[ \fC,\fR \fIidentifier\fR \*Z \*] \fIblock\fR
\fCfunction\fR \fIidentifier\fR \fC(\fR \*[ \fIidentifier\fR \*[ \fC,\fR \fIidentifier\fR \*Z \*] \fC)\fR \fIblock\fR
\fCundefine\fR \fIidentifier\fR \*[ \fC,\fR \fIidentifier\fR \*Z
\*[ \fIlabel\fR \*Z \fIblock\fR
\fCmdefine\fR \fIidentifier\fR \*[ \fC=\fR \fIexpression\fR \*]
\fCmif\fR \fC(\fR \fIexpression\fR \fC)\fR \fIblock\fR
\*[ \fCmelseif\fR \fC(\fR \fIexpression\fR \fC)\fR \fIblock\fR \*Z
\*[ \fCmelse\fR \fIblock\fR \*]
\fCmwhile\fR \fC(\fR \fIexpression\fR \fC)\fR \fIblock\fR
\fCmdo\fR \fIblock\fR \fCwhile\fR \fC(\fR \fIexpression\fR \fC)\fR
\fCmdo\fR \fIblock\fR \fCuntil\fR \fC(\fR \fIexpression\fR \fC)\fR
\fCfreturn\fR \*[ \fIexpression\fR \*]
\fCmfor\fR \fC(\fR \fIexpression\fR \fC,\fR \fIexpression\fR \fC,\fR \fIexpression\fR \fC)\fR \fIblock\fR
\fCmswitch ( \fIselectionExpression \fC) {\fR
\*[ \fCmcase ( \fIexpression\fR \*[ \fC,\fI expression\fR \*Z \fC)\fI block\fR \*Z
\*[ \fCmdefault\fI block\fR \*]
\fC}\fR
\fCconstrain\fR \fC(\fR \fIexpression\fR \fC)\fR \fIblock\fR
\fCassert\fR \fC(\fR \fIexpression\fR \fC)\fR \*[ \fIexpression\fR \*]
\fCinclude\fR \fItextString\fR
\fCextern\fR \fIidentifier\fR \*[ \fC,\fR \fIidentifier\fR \*Z
\fCstart\fR \fIexpression\fR
\fCorg\fR \fIexpression\fR
\fCtarget\fR \fIexpression\fR
\fIexpression\fR
\fIdataStatement\fR:
\*[ \fIlabel\fR \*Z \fCblock\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fCalign\fR \fIexpression\fR
\*[ \fIlabel\fR \*Z \fCword\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fClong\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fCdbyte\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fCbyte\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fCstring\fR \fIexpression\fR \*[ \fC,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fCstruct\fR \fC{\fR \*[ \fIdataStatement\fR \*Z \fC}\fR \fIidentifier\fR
\*[ \fIlabel\fR \*Z \fCstruct\fR \fIidentifier\fR
\fIlabel\fR: \fIidentifier\fR \fC:\fR
\fIoperand\fR:
\fIexpression\fR
\fC@\fR \fIexpression\fR
\fC#\fR \fIexpression\fR
\fCa\fR
\fCx\fR
\fCy\fR
\fCx\fR \fC\s-1[\s+1\fR \fIexpression\fR \fC\s-1]\s+1\fR
\fCx\fR \fC.\fR \fIidentifier\fR \*[ \fC.\fR \fIidentifier\fR \*Z
\fCy\fR \fC\s-1[\s+1\fR \fIexpression\fR \fC\s-1]\s+1\fR
\fCy\fR \fC.\fR \fIidentifier\fR \*[ \fC.\fR \fIidentifier\fR \*Z
\fC@\fR \fCx\fR \fC\s-1[\s+1\fR \fIexpression\fR \fC\s-1]\s+1\fR
\fC@\fR \fCx\fR \fC.\fR \fIidentifier\fR \*[ \fC.\fR \fIidentifier\fR \*Z
\fCy\fR \fC\s-1[\s+1\fR \fC@\fR \fIexpression\fR \fC\s-1]\s+1\fR
\fItextString\fR
\fIblock\fR: \fC{\fR \*[ \fIstatement\fR Newline \*Z \fC}\fR
\fItextString\fR:
\fC" \fIany string you like \fC"\fR
\fIcondition\fR:
\fIconditionCode\fR
\fC!\fR \fIconditionCode\fR
\fIexpression\fR:
\fIidentifier\fR
\fIidentifier\fR \fC(\fR \*[ \fIoperand\fR \*[ \fC,\fR \fIoperand\fR \*Z \*] \fC)\fR
\fInumber\fR
\fChere\fR
\fItextString\fR
\fC(\fR \fIexpression\fR \fC)\fR
\fC-\fR \fIexpression\fR
\fC!\fR \fIexpression\fR
\fC~\fR \fIexpression\fR
\fC?\fR \fIexpression\fR
\fC/\fR \fIexpression\fR
\fIexpression\fR \fC*\fR \fIexpression\fR
\fIexpression\fR \fC/\fR \fIexpression\fR
\fIexpression\fR \fC%\fR \fIexpression\fR
\fIexpression\fR \fC-\fR \fIexpression\fR
\fIexpression\fR \fC+\fR \fIexpression\fR
\fIexpression\fR \fC<<\fR \fIexpression\fR
\fIexpression\fR \fC>>\fR \fIexpression\fR
\fIexpression\fR \fC<\fR \fIexpression\fR
\fIexpression\fR \fC>\fR \fIexpression\fR
\fIexpression\fR \fC<=\fR \fIexpression\fR
\fIexpression\fR \fC>=\fR \fIexpression\fR
\fIexpression\fR \fC==\fR \fIexpression\fR
\fIexpression\fR \fC!=\fR \fIexpression\fR
\fIexpression\fR \fC&\fR \fIexpression\fR
\fIexpression\fR \fC|\fR \fIexpression\fR
\fIexpression\fR \fC^\fR \fIexpression\fR
\fIexpression\fR \fC&&\fR \fIexpression\fR
\fIexpression\fR \fC||\fR \fIexpression\fR
\fIexpression\fR \fC^^\fR \fIexpression\fR
\fIexpression\fR \fC.\fR \fIidentifier\fR
\fIidentifier\fR \fC=\fR \fIexpression\fR
\fIidentifier\fR \fC+=\fR \fIexpression\fR
\fIidentifier\fR \fC-=\fR \fIexpression\fR
\fIidentifier\fR \fC*=\fR \fIexpression\fR
\fIidentifier\fR \fC/=\fR \fIexpression\fR
\fIidentifier\fR \fC%=\fR \fIexpression\fR
\fIidentifier\fR \fC&=\fR \fIexpression\fR
\fIidentifier\fR \fC|=\fR \fIexpression\fR
\fIidentifier\fR \fC^=\fR \fIexpression\fR
\fIidentifier\fR \fC<<=\fR \fIexpression\fR
\fIidentifier\fR \fC>>=\fR \fIexpression\fR
\fIidentifier\fR \fC++\fR
\fIidentifier\fR \fC--\fR
\fC++\fR \fIidentifier\fR
\fC--\fR \fIidentifier\fR
\fIidentifier\fR:
\*[\fCa\fR-\fCzA\fR-\fCZ_\fR\*]\*[\fCa\fR-\fCzA\fR-\fCZ_0\fR-\fC9\fR\*Z
\fInumber\fR:
\fIdecimalNumber\fR
\fIoctalNumber\fR
\fIbinaryNumber\fR
\fIhexadecimalNumber\fR
\fIquarter\fR
\fIdecimalNumber\fR:
\*[\fC1\fR-\fC9\fR\*]\*[\fC0\fR-\fC9\fR\*Z
\fIoctalNumber\fR:
\fC0\fR\*[\fC0\fR-\fC7\fR\*Z
\fIbinaryNumber\fR:
\fC0b\fR\*[\fC01\fR\*]\*[\fC01\fR\*Z
\fIhexadecimalNumber\fR:
\fC0x\fR\*[\fC0\fR-\fC9a\fR-\fCf\fR\*]\*[\fC0\fR-\fC9a\fR-\fCf\fR\*Z
\fIquarter\fR:
\fC0q\fR\*[\fC0\fR-\fC3\fR\*]\*[\fC0\fR-\fC3\fR\*Z
.fi
.bp
.CD
\s+5\fBAppendix B \(em Condition Codes\fR\s-5
\"\s+4\fBAppendix B \(em Condition Codes\fR\s-4
.sp 1
\s+3\fB(6502 version)\fR\s-3
.fi
.PP
The \fIMacross\fR \fCif\fR, \fCwhile\fR, \fCdo-while\fR and \fCdo-until\fR
statements make use of symbols denoting the hardware condition codes of the
target processor which may be used as the conditions upon which conditional
branches are base. In the 6502 version of \fIMacross\fR, these are the
recognized condition code symbols:
.SH
\s+2\fBConditions which generate simple branches\fR\s-2
.nf
\fCcarry\fR tests carry bit
\fCequal\fR tests zero bit
\fCzero\fR
\fCneq\fR (equivalent to, e.g., \fC!equal\fR)
\fCminus\fR tests negative bit
\fCnegative\fR
\fCplus\fR (equivalent to, e.g., \fC!minus\fR)
\fCpositive\fR
\fCoverflow\fR tests overflow bit
.fi
.SH
\s+2\fBConditions which generate complex branches\fR\s-2
.NH 0
lt\fR \(em less than (valid after \fCcmp\fR or \fCsbc\fR)
.PP
For example,
.nf
\fCif (lt) {\fR
\fI...stuff...\fR
\fC}\fR
generates
\fCbcs temp\fR
\fI...stuff...\fR
\fCtemp:\fR
.fi
.NH 1
leq\fR \(em less than or equal to (valid after \fCcmp\fR or \fCsbc\fR)
.PP
For example,
.nf
\fCif (leq) {\fR
\fI...stuff...\fR
\fC}\fR
generates
\fCbeq temp1\fR
\fCbcs temp2\fR
\fCtemp1:\fR
\fI...stuff...\fR
\fCtemp2:\fR
.fi
.NH 1
geq\fR \(em greater than or equal to (valid after \fCcmp\fR or \fCsbc\fR)
.PP
For example,
.nf
\fCif (geq) {\fR
\fI...stuff...\fR
\fC}\fR
generates
\fCbcc temp\fR
\fI...stuff...\fR
\fCtemp:\fR
.fi
.NH 1
gt\fR \(em greater than (valid after \fCcmp\fR or \fCsbc\fR)
.PP
For example,
.nf
\fCif (gt) {\fR
\fI...stuff...\fR
\fC}\fR
generates
\fCbcc temp\fR
\fCbeq temp\fR
\fI...stuff...\fR
\fCtemp:\fR
.fi
.NH 1
slt\fR \(em signed less than (valid after \fCsbc\fR only)
.PP
For example,
.nf
\fCif (slt) {\fR
\fI...stuff...\fR
\fC}\fR
generates
\fCbvs temp1\fR
\fCbpl temp3\fR
\fCbmi temp2\fR
\fCtemp1: bmi temp3\fR
\fCtemp2:\fR
\fI...stuff...\fR
\fCtemp3:\fR
.fi
.NH 1
sleq\fR \(em signed less than or equal to (valid after \fCsbc\fR only)
.PP
For example,
.nf
\fCif (sleq) {\fR
\fI...stuff...\fR
\fC}\fR
generates
\fCbeq temp2\fR
\fCbvs temp1\fR
\fCbpl temp3\fR
\fCbmi temp2\fR
\fCtemp1: bmi temp3\fR
\fCtemp2:\fR
\fI...stuff...\fR
\fCtemp3:\fR
.fi
.NH 1
sgt\fR \(em signed greater than (valid after \fCsbc\fR only)
.PP
For example,
.nf
\fCif (sgt) {\fR
\fI...stuff...\fR
\fC}\fR
generates
\fCbeq temp3\fR
\fCbvs temp1\fR
\fCbmi temp3\fR
\fCbpl temp2\fR
\fCtemp1: bpl temp3\fR
\fCtemp2:\fR
\fI...stuff...\fR
\fCtemp3:\fR
.fi
.NH 1
sgeq\fR \(em signed greater than or equal to (valid after \fCsbc\fR only)
.PP
For example,
.nf
\fCif (sgeq) {\fR
\fI...stuff...\fR
\fC}\fR
generates
\fCbvs temp1\fR
\fCbmi temp3\fR
\fCbpl temp2\fR
\fCtemp1: bpl temp3\fR
\fCtemp2:\fR
\fI...stuff...\fR
\fCtemp3:\fR
.fi
.bp
.CD
\s+5\fBAppendix C \(em Built-In Functions\fR\s-5
\"\s+4\fBAppendix C \(em Built-In Functions\fR\s-4
.fi
.PP
Certain predefined built-in functions are supported by \fIMacross\fR for
reasons of convenience or syntactic or semantic irregularity. They are:
.LP
\fCaddressMode(\fIoperand\fC)\fR
.IP
Returns a number whose value indicates which addressing mode \fIoperand\fR
represents \fI((define these values))\fR.
.LP
\fCapply(\fImacname\fR \*[ \fC, \fIarg\fR \*Z \fC)\fR
.IP
Assembles the macro whose name is specified by the \fCstring\fR \fImacname\fR
with the macro arguments (if any) given by the \fIarg\fRs.
.LP
\fCarrayLength(\fIarray\fC)\fR
.IP
Returns the number of elements in the array \fIarray\fP.
.LP
\fCatascii(\fIstring\fC)\fR
.IP
Returns a string which is \fIstring\fR with each character mapped through an
ASCII to ATASCII (Atari's ASCII deviant character code) conversion table.
.LP
\fCatasciiColor(\fIstring\fC, \fIcolor\fC)\fR
.IP
Returns a string which is \fIstring\fR with each character mapped through the
ASCII to ATASCII conversion table, and then the two-bit value specified by
\fIcolor\fR OR'ed into the high order two bits of each character.
.LP
\fCisAbsoluteValue(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is an absolute (i.e.,
non-relocatable) value, otherwise \fCFALSE\fR.
.LP
\fCisARegister(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is \fCa\fR (i.e., the
accumulator), otherwise \fCFALSE\fR.
.LP
\fCisBlock(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is a block, otherwise
\fCFALSE\fR.
.LP
\fCisBuiltInFunction(\fIsymbol\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIsymbol\fR is a built-in function,
otherwise \fCFALSE\fR.
.LP
\fCisConditionCode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is a condition code,
otherwise \fCFALSE\fR.
.LP
\fCisDefined(\fIsymbol\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIsymbol\fR has been defined, otherwise
\fCFALSE\fR.
.LP
\fCisDirectMode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if the address mode of \fIoperand\fR is
\fIdirect\fR, otherwise \fCFALSE\fR.
.LP
\fCisExternal(\fIsymbol\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIsymbol\fR is external (i.e., visible
outside the file in which it is defined), otherwise \fCFALSE\fR.
.LP
\fCisField(\fIsymbol\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIsymbol\fR is a field of a struct,
otherwise \fCFALSE\fR.
.LP
\fCisFunction(\fIsymbol\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIsymbol\fR is a user defined function,
otherwise \fCFALSE\fR.
.LP
\fCisImmediateMode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if the address mode of \fIoperand\fR is
\fIimmediate\fR, otherwise \fCFALSE\fR.
.LP
\fCisIndexedMode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if the address mode of \fIoperand\fR is an
indexed mode, otherwise \fCFALSE\fR.
.LP
\fCisIndirectMode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if the address mode of \fIoperand\fR is
\fIindirect\fR, otherwise \fCFALSE\fR.
.LP
\fCisPostIndexedMode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if the address mode of \fIoperand\fR is
\fIpost-indexed\fR, otherwise \fCFALSE\fR.
.LP
\fCisPreIndexedMode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if the address mode of \fIoperand\fR is
\fIpre-indexed\fR, otherwise \fCFALSE\fR.
.LP
\fCisRelocatableValue(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is a relocatable value,
otherwise \fCFALSE\fR.
.LP
\fCisString(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is a string, otherwise
\fCFALSE\fR.
.LP
\fCisStruct(\fIsymbol\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIsymbol\fR is the name of a struct,
otherwise \fCFALSE\fR.
.LP
\fCisSymbol(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is a symbol (as opposed to an
expression or a number, for example), otherwise \fCFALSE\fR.
.LP
\fCisXIndexedMode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if the address mode of \fIoperand\fR is
\fIx-indexed\fR, otherwise \fCFALSE\fR.
.LP
\fCisXRegister(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is \fCx\fR, otherwise
\fCFALSE\fR.
.LP
\fCisYIndexedMode(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if the address mode of \fIoperand\fR is
\fIy-indexed\fR, otherwise \fCFALSE\fR.
.LP
\fCisYRegister(\fIoperand\fC)\fR
.IP
Returns \fCTRUE\fR if and only if \fIoperand\fR is \fCy\fR, otherwise
\fCFALSE\fR.
.LP
\fClistingOff()\fR
.IP
If assembly listing has been enabled using the \fC-l\fR command line flag,
turn listing off temporarily. Otherwise, no effect.
.LP
\fClistingOn()\fR
.IP
If assembly listing was turned off using the \fClistingOff()\fR function, turn
it back on again. If listings have been globally disabled by not specifying
the \fC-l\fR command line flag, this function has no effect. The
\fClistingOff()\fR and \fClistingOn()\fR functions are intended to be used to
together to control assembly listings of large programs. They can be used to
suppress listing of large and uninteresting sections such as header files full
of definitions of global values. These functions may nest: in effect
\fClistingOff()\fR increments a counter and \fClistingOn()\fR decrements it.
Only when the counter is zero (i.e., the number of \fClistingOn()\fRs matches
the number of \fClistingOff()\fRs) does listing actually occur.
.LP
\fCmakeArray(\fIlength\fR \*[ \fC, \fIelement\fR \*Z \fC)\fR
.IP
Creates an array of length \fIlength\fP and returns it. Optionally fills the
array with the values specified by the \fIelement\fP expressions. If the
number of \fIelement\fPs given is greater than \fIlength\fP, an error results.
.LP
\fCnthChar(\fIstring\fR \*[ \fC, \fIposition\fR \*] \fC)\fR
.IP
Returns the \fIposition\fPth character of the string \fIstring\fP (position
zero being the first character in the string). If \fIposition\fP is omitted
it defaults to zero. If \fIposition\fP is greater than the length of
\fIstring\fP, an error results.
.LP
\fCprintf(\fIformat\fR \*[ \fC, \fIarg\fR \*Z \fC)\fR
.IP
A formatted print routine just like the Unix system subroutine of the same
name.
.LP
\fCstrcat(\fIstring1\fC, \fIstring2\fC)\fR
.IP
Returns a string which is the concatenation of the two operands, which must
themselves be strings.
.LP
\fCstrcmp(\fIstring1\fC, \fIstring2\fC)\fR
.IP
Returns a number which is less than, equal to, or greater than 0 depending
upon whether \fIstring1\fR is lexically less than, equal to, or greater than
\fIstring2\fR. The ASCII character set is used. The two operands, of course,
must be strings.
.LP
\fCstrcmplc(\fIstring1\fC, \fIstring2\fC)\fR
.IP
Essentially the same as \fCstrcmp()\fR except that alphabetic characters are
converted to lower case before being compared. The result is a
case-independent string comparison. This is useful for comparing two
identifier name strings to see if they represent the same symbols.
.LP
\fCstrlen(\fIstring\fC)\fR
.IP
Returns a number which is the length, in characters, of \fIstring\fR.
.LP
\fCsubstr(\fIstring\fC, \fIstartPos\fR \*[\fC , \fIlength\fR \*] \fC)\fR
.IP
Returns a substring of the string \fIstring\fR starting from the character at
start position \fIstartPos\fR (counting the first character from 0) and
continuing for \fIlength\fR characters. If \fIstartPos\fR is negative, the
start position is counted from right to left (with the rightmost character
position being indicated by -1) instead of the more usual left to right. If
\fIlength\fR is negative, \fIstartPos\fR in essence denotes the end of the
desired substring and \fIlength\fR characters up to that position are
returned. If \fIlength\fR is omitted, the substring from \fIstartPos\fR to
the end of the string is returned, if \fIstartPos\fR is positive, or to the
beginning of the string, if \fIstartPos\fR is negative. If any of the indices
cause the substring bounds to go off the end of \fIstring\fR an error results.
For example,
.LP
.nf
\fCsubstr("hello there", 6, 3)\fR yields \fC"the"\fR
\fCsubstr("hello there", -8, 2)\fR yields \fC"lo"\fR
\fCsubstr("hello there", 6, -3)\fR yields \fC"o t"\fR
\fCsubstr("hello there", -8, -4)\fR yields \fC"hell"\fR
\fCsubstr("hello there", 6)\fR yields \fC"there"\fR
\fCsubstr("hello there", -7)\fR yields \fC"hello"\fR
.fi
.LP
\fCsymbolDefine(\fIstring\fR \*[ \fC, \fIvalue\fR \*] \fC)\fR
.IP
Defines the symbol named by \fIstring\fP (with optional value \fIvalue\fP) as
if it had been defined with a \fCdefine\fR statement. For example:
.LP
.nf
\fCsymbolDefine(strcat("foon", "farm"), 47)\fR
is equivalent to
\fCdefine foonfarm = 47\fR
.fi
.LP
\fCsymbolLookup(\fIstring\fC)\fR
.IP
A call to this function with a string operand is equivalent to a reference to
the symbol that the string represents. For example,
.LP
.nf
\fCand symbolLookup("foo")\fR
is equivalent to
\fCand foo\fR
.fi
.LP
\fCsymbolName(\fIsymbol\fC)\fR
.IP
Returns a string which is the name of the symbol \fIsymbol\fR. For example,
\fCsymbolName(foo)\fR would return \fC"foo"\fR. This can be used in
conjunction with the \fCsymbolLookup\fR function so that the following:
.LP
.nf
\fCand symbolLookup(strcat(symbolName(foo), "bar"))\fR
is equivalent to
\fCand foobar\fR
.fi
.LP
\fCsymbolUsage(\fIsymbol\fC)\fR
.IP
Returns a number whose value indicates what sort of symbol \fIsymbol\fR is
(i.e., label, function, struct field, etc.). \fI((define these values))\fR
.LP
\fCvalueType(\fIthing\fC)\fR
.IP
Returns a number whose value indicates the type of \fIthing\fR (i.e., symbol,
condition code, number, block, etc.). \fI((define these values))\fR
.bp
.CD
\s+5\fBAppendix D \(em Operator Set\fR\s-5
\"\s+4\fBAppendix D \(em Operator Set\fR\s-4
.fi
.PP
This appendix describes the (\fBC\fR derived) operators supported by
\fIMacross\fR.
.nf
\fC-\fR \fIexpression\fR integer negation
\fC!\fR \fIexpression\fR logical negation (0 goes to 1, all other values go to 0)
\fC~\fR \fIexpression\fR bitwise negation (ones complement)
\fC?\fR \fIexpression\fR high byte
\fC/\fR \fIexpression\fR low byte
\fIexpression\fR \fC*\fR \fIexpression\fR integer multiplication
\fIexpression\fR \fC/\fR \fIexpression\fR integer division
\fIexpression\fR \fC%\fR \fIexpression\fR integer modulus (remainder)
\fIexpression\fR \fC-\fR \fIexpression\fR integer subtraction
\fIexpression\fR \fC+\fR \fIexpression\fR integer addition
\fIexpression\fR \fC<<\fR \fIexpression\fR left shift
\fIexpression\fR \fC>>\fR \fIexpression\fR right shift
\fIexpression\fR \fC<\fR \fIexpression\fR less than
\fIexpression\fR \fC>\fR \fIexpression\fR greater than
\fIexpression\fR \fC<=\fR \fIexpression\fR less than or equal to
\fIexpression\fR \fC>=\fR \fIexpression\fR greater than or equal to
\fIexpression\fR \fC==\fR \fIexpression\fR equal to
\fIexpression\fR \fC!=\fR \fIexpression\fR not equal to
\fIexpression\fR \fC&\fR \fIexpression\fR bitwise AND
\fIexpression\fR \fC|\fR \fIexpression\fR bitwise OR
\fIexpression\fR \fC^\fR \fIexpression\fR bitwise XOR
\fIexpression\fR \fC&&\fR \fIexpression\fR logical AND
\fIexpression\fR \fC||\fR \fIexpression\fR logical OR
\fIexpression\fR \fC^^\fR \fIexpression\fR logical XOR
\fIexpression\fR \fC.\fR \fIidentifier\fR struct field selection
\fIidentifier\fR \fC=\fR \fIexpression\fR assignment
\fIidentifier\fR \fC+=\fR \fIexpression\fR assignment with addition
\fIidentifier\fR \fC-=\fR \fIexpression\fR assignment with subtraction
\fIidentifier\fR \fC*=\fR \fIexpression\fR assignment with multiplication
\fIidentifier\fR \fC/=\fR \fIexpression\fR assignment with division
\fIidentifier\fR \fC%=\fR \fIexpression\fR assignment with modulus
\fIidentifier\fR \fC&=\fR \fIexpression\fR assignment with AND
\fIidentifier\fR \fC|=\fR \fIexpression\fR assignment with OR
\fIidentifier\fR \fC^=\fR \fIexpression\fR assignment with XOR
\fIidentifier\fR \fC<<=\fR \fIexpression\fR assignment with left shift
\fIidentifier\fR \fC>>=\fR \fIexpression\fR assignment with right shift
\fIidentifier\fR \fC++\fR post-increment
\fIidentifier\fR \fC--\fR post-decrement
\fC++\fR \fIidentifier\fR pre-increment
\fC--\fR \fIidentifier\fR pre-decrement
.fi
.bp
.CD
\s+5\fB Appendix E \(em Character Escape Codes\fR\s-5
\"\s+4\fB Appendix E \(em Character Escape Codes\fR\s-4
.fi
.PP
Like \fBC\fR, \fIMacross\fR enables you to use the ``\fC\\\fR'' character as
an escape to embed quotation marks, formatting characters (such as newline)
and other non-printing characters in character strings and character
constants. The recognized codes are:
.nf
\fC\\n\fR newline
\fC\\t\fR horizontal tab
\fC\\b\fR backspace
\fC\\r\fR carriage return
\fC\\f\fR form feed
\fC\\e\fR escape
\fC\\\\\fR backslash
\fC\\'\fR apostrophe
\fC\\"\fR quote
\fC\\^\fIc\fR CONTROL-\fIc\fR (where \fIc\fR is any character).
\fC\\\fIddd\fR arbitrary byte (where \fIddd\fR is one, two or three octal digits).
.fi
.bp
.CD
\s+5\fBAppendix F \(em Recognized Opcode Mnemonics\fR\s-5
\"\s+4\fBAppendix F \(em Recognized Opcode Mnemonics\fR\s-4
.sp 1
\s+3\fB(6502 version)\fR\s-3
.fi
.PP
These are the 6502 opcode mnemonics recognized by \fIMacross\fR:
.2C
.nf
\fCadc
and
asl
bcc
bcs
beq
bit
bmi
bne
bpl
brk
bvc
bvs
clc
cld
cli
clv
cmp
cpx
cpy
dec
dex
dey
eor
inc
inx
iny
jmp
jsr
lda
ldx
ldy
lsr
nop
ora
pha
php
pla
plp
rol
ror
rti
rts
sbc
sec
sei
sta
stx
sty
tax
tay
tsx
txa
txs
tya
.fi
.1C