macross/doc/writeup_68000.itr

2263 lines
70 KiB
Plaintext

.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
.TL
\s+6Macross 68000\s-6
\"Macross
.AU
an assembler for people who hate assembly language
by
Chip Morningstar
.AI
Lucasfilm Ltd. Games Division
\\*(DY
.ds LH Macross
.ds CH \\*(DY
.ds RH 68000 Version
.ds LF Lucasfilm Ltd. Proprietary Information
.ds CF - % -
.ds RF CONFIDENTIAL
.AB
This document describes the 68000 version of \fIMacross\fR, a super-duper
cross-assembler that has but to be 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 68000 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 (\fBif\fR,
\fBwhile\fR, etc.) and the ability to define record-oriented data structures
(\fBstruct\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
\fBbyte 1, 2, 3, 4,
5, 6, 7, 8\fR
.fi
is allowed and is equivalent to
.nf
\fBbyte 1, 2, 3, 4, 5, 6, 7, 8
.fi
.PP
Comments begin with a semicolon (``\fB;\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 ``\fB/*\fR'' and ``\fB*/\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 (``\fB:\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 (``\fB_\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 \fBboldface\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 \*[ \fB,\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 B\fR at the end of this document) or a macro name. For example:
.nf
\fBandb foobar, d3\fR
\fBsomeMacro 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 address modes of the target processor. In the case
of the 68000, these are:
.NH 2
\fRRegister mode
.PP
Register operands take one of the forms
.nf
\fBd\fIn\fR
\fBa\fIn\fR
\fBsp\fR
.fi
where \fIn\fR is a digit from \fB0\fR to \fB7\fR. \fBd\fIn\fR of course
represents a data register and \fBa\fIn\fR represents an address register.
\fBsp\fR is equivalent to \fBa7\fR.
.NH 2
\fRRegister indirect mode
.PP
Register indirect mode operands take the form
.nf
\fB\s-1[\s+1\fBa\fIn\fB\s-1]\s+1\fR
.fi
where \fBa\fIn\fR is some address register.
.NH 2
\fRPostincrement mode
.PP
Postincrement mode operands take the form
.nf
\fB\s-1[\s+1\fBa\fIn\fB\s-1]\s+1+\fR
.fi
where \fBa\fIn\fR is some address register.
.NH 2
\fRPredecrement mode
.PP
Predecrement mode operands take the form
.nf
\fB-\s-1[\s+1\fBa\fIn\fB\s-1]\s+1\fR
.fi
where \fBa\fIn\fR is some address register.
.NH 2
\fRNormal mode
.PP
Normal mode addresses take the form
.nf
\fIexpression\fR
.fi
and are used in a couple of different ways. First, normal mode addresses are
used by instructions that require relative addresses (such as branch
instructions; the offset is computed automatically by \fIMacross\fR). Second,
the normal mode form of addresses is a shorthand way of refering to locations
for which \fIMacross\fR selects an appropriate address mode to use (PC
relative if it can, otherwise absolute).
.NH 2
\fRAbsolute mode
.PP
Absolute addresses take one of these two forms
.nf
\fIexpression\fB . w\fR
\fIexpression\fB . l\fR
.fi
with the \fBw\fR or \fBl\fR indicating whether to generate a short (16 bit) or
a long (32 bit) address.
.NH 2
\fRImmediate mode
.PP
Immediate mode operands take the form
.nf
\fB#\fR \fIexpression\fR
.fi
Generally speaking, in the 68000 immediate mode operands are restricted to
sixteen bit quantities. Depending upon the particular instruction involved,
the size may be even more restricted. \fIMacross\fR will give an error
message if the operand value is larger than allowed.
.NH 2
\fRDisplacement mode
.PP
Displacement mode operands take the form
.nf
\fIexpression\fR \fB\s-1[\s+1\fR \fBa\fIn\fR \fB\s-1]\s+1\fR
.fi
where \fIexpression\fR is an offset off of the address register \fBa\fIn\fR.
These displacement values are limited to 16 bit quantities in the 68000.
.PP
An alternate form of displacment mode addressing which is supported by
\fIMacross\fR allows the symbolic selection of a field of a \fBstruct\fR
pointed to by an address register
.nf
\fBa\fIn\fR \fB.\fR \fIidentifier\fR \*[ \fB.\fR \fIidentifier\fR \*Z
.fi
This is explained in greater detail in the sections on \fBstruct\fRs and
expressions below.
.NH 2
\fRIndexed mode
.PP
Indexed mode addressing is specified by operands of the form
.nf
\fIexpression\fR \fB\s-1[\s+1\fR \fBa\fIn\fR \fB,\fR \fIrn\fR \fB. w \s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBa\fIn\fR \fB,\fR \fIrn\fR \fB. l \s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBa\fIn\fR \fB,\fR \fIrn\fR \fB\s-1]\s+1\fR
.fi
where \fIexpression\fR is an offset off of address register \fBa\fIn\fR and
another register \fIrn\fR which may be any data or address register. The
Offset is restricted to an 8-bit quantity. The \fBw\fR or \fBl\fR indicates
whether the offset value in \fIrn\fR is to be taken as a word (16 bit) value
or as a long (32 bit) value (if omitted it defaults to long).
.PP
As with ordinary displacment addressing, there is a form of indexed addressing
which uses \fBstruct\fR fields
.nf
\fBa\fIn\fR \fB\s-1[\s+1\fR \fIrn\fR \fB. w \fB\s-1]\s+1\fR \fB.\fR \fIidentifier\fR \*[ \fB.\fR \fIidentifier\fR \*Z
\fBa\fIn\fR \fB\s-1[\s+1\fR \fIrn\fR \fB. l \fB\s-1]\s+1\fR \fB.\fR \fIidentifier\fR \*[ \fB.\fR \fIidentifier\fR \*Z
\fBa\fIn\fR \fB\s-1[\s+1\fR \fIrn\fR \fB\s-1]\s+1\fR \fB.\fR \fIidentifier\fR \*[ \fB.\fR \fIidentifier\fR \*Z
.fi
.NH 2
\fRProgram counter displacement mode
.PP
Program counter displacement mode operands take the form
.nf
\fIexpression\fR \fB\s-1[\s+1\fR \fBpc\fR \fB\s-1]\s+1\fR
.fi
where \fIexpression\fR is an offset off of the program counter. These
displacement values are limited to 16 bit quantities in the 68000.
.NH 2
\fRProgram counter indexed mode
.PP
Program counter indexed mode addressing is specified by operands of the form
.nf
\fIexpression\fR \fB\s-1[\s+1\fR \fBpc\fR \fB,\fR \fIrn\fR \fB. w \s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBpc\fR \fB,\fR \fIrn\fR \fB. l \s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBpc\fR \fB,\fR \fIrn\fR \fB\s-1]\s+1\fR
.fi
where \fIexpression\fR is an offset off of the program counter and another
register \fIrn\fR which may be any data or address register. The Offset is
restricted to an 8-bit quantity. The \fBw\fR or \fBl\fR indicates whether the
offset value in \fIrn\fR is to be taken as a word (16 bit) value or as a long
(32 bit) value (if omitted it defaults to long).
.NH 2
\fRControl register mode
.PP
The various control register of the 68000 are sometimes used as operands
.nf
\fBccr\fR
\fBsr\fR
\fBusp\fR
\fBsfc\fR
\fBdfc\fR
\fBvbr\fR
.fi
where \fBccr\fR represents the condition code register, \fBsr\fR represents
the status register, \fBusp\fR represents the user stack pointer, \fBsfc\fR
represents the source function code register, \fBdfc\fR represents the
destination function code register, and \fBvbr\fR represents the vector base
register.
.NH 2
\fRText operands
.PP
For the sake of macros, text strings may also be used as operands
.nf
\fB"\fIany string you like\fB"\fR
.fi
The same conventions regarding escaped characters (using ``\fB\\\fR'') that
are followed by \fBC\fR are followed by \fIMacross\fR. These are documented
in \fBAppendix F\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 \fBprintf()\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 (\fBif\fR) and three conditional
loop statements (\fBwhile\fR, \fBdo-while\fR and \fBdo-until\fR). These
statements assemble into the appropriate conditional branches and jumps to
realize the desired construct.
.NH 2
If\fR statement
.PP
The \fBif\fR statement has the following form
.nf
\*[ \fIlabel\fR \*Z \fBif\fR \fB(\fR \fIcondition\fR \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \*[ \fBelseif\fR \fB(\fR \fIcondition\fR \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \*Z \*[ \fBelse\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\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., \fBcarry\fR, \fBoverflow\fR, etc.\*- the complete list is in
\fBAppendix C\fR) or one either of these negated using the ``logical not''
operator (``\fB!\fR''). The condition is used to determine the appropriate
type of branch instruction to use. For example,
.nf
\fBif (overflow) {\fR
\fIstatements-1\fR
\fB} elseif (carry) {\fR
\fIstatements-2\fR
\fB} else {\fR
\fIstatements-3\fR
\fB}\fR
.fi
expands into this (the labels are made up for illustrative purposes only):
.nf
\fBbvc temp1
\fIstatements-1
\fBjmp temp3
temp1: bcc temp2
\fIstatements-3
\fBjmp temp3
temp2: \fIstatements-3
\fBtemp3: \fIwhatever follows\fR
.fi
The keyword \fBelseif\fR may be used as shown, or specified as two separate
keywords, \fBelse if\fR, depending on the programmer's whim.
.NH 2
While\fR statement
.PP
The \fBwhile\fR statement has the following form
.nf
\*[ \fIlabel\fR \*Z \fBwhile\fR \fB(\fR \fIcondition\fR \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR
.fi
\fIcondition\fR is as described above for the \fBif\fR statement. An example
of the \fBwhile\fR statement would be
.nf
\fBwhile (!carry) {\fR
\fIstatements\fR
\fB}\fR
.fi
which would turn into
.nf
\fBtemp2: bcs temp1
\fIstatements
\fBjmp temp2
temp1: \fIwhatever follows\fR
.fi
.NH 2
Do-while\fR statement
.PP
The \fBdo-while\fR statement is similar to the \fBwhile\fR statement except
that the condition is tested at the bottom of the loop. It has the form
.nf
\*[ \fIlabel\fR \*Z \fBdo\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \fBwhile\fR \fB(\fR \fIcondition\fR \fB)\fR
.fi
For example
.nf
\fBdo {\fR
\fIstatements\fR
\fB} while (equal)\fR
.fi
which is equivalent to
.nf
\fBtemp: \fIstatements
\fBbeq temp\fR
.fi
.NH 2
Do-until\fR statement
.PP
The \fBdo-until\fR statement is the same as the \fBdo-while\fR statement
except that the sense of the condition is negated. It has the form
.nf
\*[ \fIlabel\fR \*Z \fBdo\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \fBuntil\fR \fB(\fR \fIcondition\fR \fB)\fR
.fi
For example
.nf
\fBdo {\fR
\fIstatements\fR
\fB} until (equal)\fR
.fi
which is equivalent to
.nf
\fBtemp: \fIstatements
\fBbne 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 \fBblock\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 \fBblock\fR \fIexpression\fR \*[ \fB,\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 \fBalign\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 \fBalign\fR \fIexpression\fR
.fi
The \fIexpression\fR is the multiple to which the current location counter is
to be aligned. For example,
.nf
\fBalign 2\fR
.fi
would align the current location to a word boundary, while
.nf
\fBalign 0x100\fR
.fi
would align to a page boundary.
.NH 2
Constrain\fR statement
.PP
The \fBconstrain\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
\fBconstrain\fR \fB(\fR \fIboundary\fR \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\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 \fBconstrain\fR statement, the contents of the
constrain block itself, and the things in the assembly source immediately
after the \fBconstrain\fR statement will be located in contiguous locations in
the eventual target machine address space). For example,
.nf
\fBconstrain (0x100) {\fR
\fIstatements\fR
\fB}\fR
.fi
constrains the given statements to all fit within a page.
.NH 2
Word\fR statement
.PP
The \fBword\fR statement allocates words, i.e., two byte chunks, of memory.
It takes the form
.nf
\*[ \fIlabel\fR \*Z \fBword\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
.fi
The \fIexpression\fRs must evaluate to quantities that can be contained in 16
bits, of course. For example,
.nf
\fBword 0x1234, foobar\fR
.fi
would allocate two words, the first of which would be initialized to the
hexadecimal value 0x1234 and the second to whatever the value of \fBfoobar\fR
is.
.NH 2
Dbyte\fR statement
.PP
The \fBdbyte\fR statement is just like the \fBword\fR statement, except that
the word is byte-swapped in memory. Its form is
.nf
\*[ \fIlabel\fR \*Z \fBdbyte\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
.fi
.NH 2
Long\fR statement
.PP
The \fBlong\fR statement allocates longwords, i.e., four byte chunks, of
memory. It takes the form
.nf
\*[ \fIlabel\fR \*Z \fBlong\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
.fi
The \fIexpression\fRs must evaluate to quantities that can be contained in 32
bits, of course. For example,
.nf
\fBword 0x12345678, foobar\fR
.fi
would allocate two longwords, the first of which would be initialized to the
hexadecimal value 0x12345678 and the second to whatever the value of
\fBfoobar\fR is.
.NH 2
Byte\fR statement
.PP
The \fBbyte\fR statement is similar to the \fBword\fR and \fBdbyte\fR
statements, except that it allocates single byte chunks. Its form is
.nf
\*[ \fIlabel\fR \*Z \fBbyte\fR \fIexpression\fR \*[ \fB,\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 \fBstring\fR statement is much like the \fBbyte\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 68000 version of \fIMacross\fR but is
provided for compatability with future versions targeted at more sophisticated
processors. The form of the \fBstring\fR statement is
.nf
\*[ \fIlabel\fR \*Z \fBstring\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
.fi
.NH 2
Struct\fR statement
.PP
The \fBstruct\fR statement enables the declaration and allocation of
record-oriented data structures. There are two forms of the \fBstruct\fR
statement, the first of which declares a \fBstruct\fR record type, and the
second of which causes space to be set aside in memory for a \fBstruct\fR that
has already been declared. The form of the first type of \fBstruct\fR
statement is
.nf
\*[ \fIlabel\fR \*Z \fBstruct\fR \fB{\fR
\*[ \fIdataStatement\fR \*Z
\fB}\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 \fBstruct\fR. Any labels
inside the \fBstruct\fR become \fIfields\fR of the data structure which may be
referred to later in expressions using the ``\fB.\fR'' operator, as in
\fBC\fR. A more complete description of the semantics of \fBstruct\fRs is
given in the section below on expressions.
.PP
The first form of the \fBstruct\fR statement, called a ``\fBstruct\fR
definition'', lays out the constituent parts of a data structure and gives
those names to those parts. The second form of the \fBstruct\fR statement,
called a ``\fBstruct\fR instantiation'',
.nf
\*[ \fIlabel\fR \*Z \fBstruct\fR \fIname\fR
.fi
causes storage for the \fBstruct\fR named by \fIname\fR to be allocated. A
\fBstruct\fR definition \fImay not\fR contain another \fBstruct\fR definition,
but it \fImay\fR contain a \fBstruct\fR instantiation. For example,
.nf
\fBstruct {
pointer: block 2
class: block 1
} fooThing\fR
.fi
would create a \fBstruct\fR called \fBfooThing\fR. Then,
.nf
\fBfooLabel: struct fooThing\fR
.fi
would allocate one at the current location at the address labeled
\fBfooLabel\fR. This could then be used as follows:
.nf
\fBandb fooLabel.class, d0
jmp fooLabel.pointer\fR
.fi
which would AND \fBd0\fR with the \fBclass\fR field of the \fBstruct\fR and
then jump to wherever the \fBpointer\fR field pointed to. If the \fBx\fR
address register \fBa4\fR already contained the address of this \fBstruct\fR,
then one could say
.nf
\fBandb a4.class, d0\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 \fBdefine\fR statement enables the programmer to create symbolic names for
values. It has two forms. The first
.nf
\fBdefine\fR \fIsymbolname\fR
.fi
creates a new symbol, \fIsymbolname\fR (an identifier), and gives it the
special value \fBunassigned\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 \fBisDefined()\fR built-in function (see
\fBExpressions\fR, below) to return \fBTRUE\fR if passed as an argument. It
is also an error to \fBdefine\fR a symbol that has already been \fBdefine\fRd.
.PP
The second form of the \fBdefine\fR statement
.nf
\fBdefine\fR \fIsymbolname\fR \fB=\fR \fIexpression\fR
.fi
creates the symbol and gives it the value obtained by evaluating
\fIexpression\fR (see \fBExpressions\fR, below). Actually, what \fBdefine\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 \fBdefine\fR statements, for example:
.nf
\fBdefine foo = bar + 2
define bar = 47\fR
.fi
effectively defines \fBfoo\fR to be \fB49\fR. Beware, however, as there is no
way for the assembler to detect mutually recursive references of this sort, so
that
.nf
\fBdefine foo = bar + 2
define bar = foo + 2\fR
.fi
will be happily swallowed without complaint, until you actually try to use
\fBfoo\fR or \fBbar\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 \fBfoo\fR is and sees that it's
\fBbar + 2\fR, so it looks to see what \fBbar\fR and see that it's \fBfoo +
2\fR, so it looks to see what \fBfoo\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 \fBvariable\fR statement enables the programmer to declare symbolic
variables for future use. Similar to the \fBdefine\fR statement, it has two
forms. The first
.nf
\fBvariable\fR \fIsymbolname\fR
.fi
creates a variable named \fIsymbolname\fR and gives it the special value
\fBunassigned\fR, just like the analogous \fBdefine\fR statement.
.PP
The second form of the \fBvariable\fR statement
.nf
\fBvariable\fR \fIsymbolname\fR \fB=\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 \fBvariable\fR statement itself to the end of the assembly
(i.e., the variable is global).
.PP
The difference between the \fBdefine\fR statement and the \fBvariable\fR
statement is that the \fBdefine\fR statement creates what is in essence a
constant whereas the \fBvariable\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 \fBdefine\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 \fBvariable\fR statement is
encountered in the assembly.
.NH 2
Macro\fR statement
.PP
The \fBmacro\fR statement is used to define macros (surprise!). Its syntax is
.nf
\fBmacro\fR \fImacroname\fR \*[ \fIargumentname\fR \*[ \fB,\fR \fIargumentname\fR \*Z \*] \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\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
\fBmacro\fR statements and \fBfunction\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 (``\fB$\fR''). For
example,
.nf
\fBmacro fooMac arg {
jmp $foo
word arg
$foo: nop
}\fR
.fi
defines a macro named \fBfooMac\fR that emits a word of data that gets jumped
over. The label \fB$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 \fBjmp\fR will
refer to the location two instructions ahead, and any other macros that might
contain \fB$foo\fR will not affect this nor will they be affected by this.
.NH 2
Function\fR statement
.PP
The \fBfunction\fR statement is used to define functions. Its syntax is
.nf
\fBfunction\fR \fIfuncname\fR \fB(\fR \*[ \fIargumentname\fR \*[ \fB,\fR \fIargumentname\fR \*Z \*] \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\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 \fBmacro\fR and \fBfunction\fR
statements. A function may return a value using the \fBfreturn\fR statement,
which is described below.
.NH 2
Undefine\fR statement
.PP
The \fBundefine\fR statement allows symbol and macro definitions to be removed
from \fIMacross\fR' symbol table. It takes the form
.nf
\fBundefine\fR \fIsymbolname\fR \*[ \fB,\fR \fIsymbolname\fR \*Z
.fi
The named symbols go away as if they never were \*- they are free to be
defined again and the \fBisDefined()\fR built-in function will return
\fBFALSE\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 \fBmif\fR statement (to be described shortly) is
the means by which conditional assembly may be realized.
.NH 2
\fRBlocks
.PP
The construct
.nf
\fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\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 \fBmdefine\fR statement
.nf
\fBmdefine\fR \fIsymbolname\fR
or
\fBmdefine\fR \fIsymbolname\fR \fB=\fR \fIexpression\fR
.fi
operates like (and is syntactically congruent with) the \fBdefine\fR
statement, except that the scope of the symbol definition is restricted to the
body block of the macro or function in which the \fBmdefine\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 \fBmvariable\fR statement
.nf
\fBmvariable\fR \fIsymbolname\fR
or
\fBmvariable\fR \fIsymbolname\fR \fB=\fR \fIexpression\fR
.fi
bears exactly the same relationship to the \fBvariable\fR statement that the
\fBmdefine\fR statement does to the \fBdefine\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 \fBmif\fR statement conditionally assembles the statements contained in
the block following it:
.nf
\fBmif\fR \fB(\fR \fIcondition\fR \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \*[ \fBmelseif\fR \fB(\fR \fIcondition\fR \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \*Z \*[ \fBmelse\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \*]
.fi
unlike the \fBif\fR statement, the \fIcondition\fR may be any expression
whatsoever. \fIMacross\fR follows the \fBC\fR convention that the value 0
represents \fBFALSE\fR and any other value represents \fBTRUE\fR (in fact, the
symbols \fBTRUE\fR and \fBFALSE\fR are ``predefined'' by the assembler to
have the values 1 and 0 respectively). The meaning of the \fBmif\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 \fBmwhile\fR statement repetitively assembles a block of statements so
long as a given condition remains true. Its form is
.nf
\fBmwhile\fR \fB(\fR \fIcondition\fR \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR
.fi
As with \fBmif\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 \fBmdo-while\fR statement provides an alternative to the \fBmwhile\fR
statement by testing at the bottom of the loop instead of at the top. Its
form is
.nf
\fBmdo\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \fBwhile\fR \fB(\fR \fIcondition\fR \fB)\fR
.fi
.NH 2
Mdo-until\fR statement
.PP
The \fBmdo-until\fR statement is the same as the \fBmdo-while\fR statement
except that the sense of the condition is negated. It has the form
.nf
\fBmdo\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \fBuntil\fR \fB(\fR \fIcondition\fR \fB)\fR
.fi
.NH 2
Mfor\fR statement
.PP
The \fBmfor\fR statement provides a more general looping construct analogous
to the \fBC\fR \fBfor\fR loop. Its form is
.nf
\fBmfor\fR \fB(\fR \fIexpression-1\fR \fB,\fR \fIexpression-2\fR \fB,\fR \fIexpression-3\fR \fB)\fR \fB{\fR
\*[ \fIstatement\fR \*Z
\fB}\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 \fBmfor\fR statement is equivalent to
.nf
\fIexpression-1\fR
\fBmwhile (\fIexpression-2\fB ) {\fR
\fIstatements\fR
\fIexpression-3\fR
\fB}\fR
.fi
.NH 2
Mswitch\fR statement
.PP
The \fBmswitch\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
\fBmswitch ( \fIselectionExpression \fB) {\fR
\*[ \fBmcase ( \fIexpression\fR \*[ \fB,\fI expression\fR \*Z \fB) {\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \*Z
\*[ \fBmdefault {\fR
\*[ \fIstatement\fR \*Z
\fB}\fR \*]
\fB}\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 \fBmcase\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 \fBmcase\fR clause is immediately assembled and
execution of the \fBmswitch\fR statement is complete. If no such value
matches and there is an \fBmdefault\fR clause, the block of \fIstatement\fRs
associated with the \fBmdefault\fR clause is assembled. If no value matches
and there is no \fBmdefault\fR clause then nothing is assembled as a result of
the \fBmswitch\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
\fBmswitch (foo) {
mcase ("hello", "fnord") {
\fIstatements-1\fB
}
mcase ("ZAP!") {
\fIstatements-2\fB
}
mdefault {
\fIstatements-3\fR
}
}\fR
.fi
would switch on the value of the symbol \fBfoo\fR. If the value of \fBfoo\fR
was \fB"hello"\fR, \fIstatements-1\fR would be assembled. If the value of
\fBfoo\fR was \fB"zap!"\fR, \fIstatements-2\fR would be assembled (since the
string comparison is done independent of case). If the value of \fBfoo\fR was
\fB"cromfelter"\fR or \fB47\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 \fBfreturn\fR statement enables this. Its form is
.nf
\fBfreturn\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 \fBinclude\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
\fBinclude\fR \fIfilename\fR
.fi
where \fIfilename\fR is a string value giving the name of the file to be
included. \fBInclude\fRd files may themselves contain \fBinclude\fR
statements to any number of levels of recursion (within reason).
.NH 2
Extern\fR statement
.PP
The \fBextern\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
\fBextern\fR \fIsymbol\fR \*[ \fB,\fR \fIsymbol\fR \*Z
.fi
.NH 2
Start\fR statement
.PP
The \fBstart\fR statement declares the starting address of a program.
.nf
\fBstart\fR \fIexpression\fR
.fi
where the \fIexpression\fR indicates the start address. There should be no
more than one \fBstart\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 \fBassert\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
\fBassert (\fIcondition\fB)\fR \*[ \fItextString\fR \*]
.fi
where \fIcondition\fR is an expression such as those used in the \fBmif\fR
statement. This condition is evaluated and if \fBFALSE\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
\fBassert (foo == 1) "Hey! You blew it."\fR
.fi
would check to see that the value of the symbol \fBfoo\fR is \fB1\fR, and if
it isn't would issue an error message containing the string \fB"Hey! You blew
it."\fR.
.NH 2
Org\fR statement
.PP
The \fBorg\fR statement adjusts the current location counter and tells the
assembler to start locating instructions and data in absolute memory
locations.
.nf
\fBorg\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 \fBrel\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 \fBorg\fR stopped
relocatable code assembly. \fI((explain this better))\fR
.NH 2
Target\fR statement
.PP
The \fBtarget\fR statement
.nf
\fBtarget\fR \fIexpression\fR
.fi
tells the assembler to assemble as if it had been \fBorg\fRed to a particular
address without actually performing the \fBorg\fR. The \fIexpression\fR
indicates the location to start assembling from. This must be an absolute
address (and the \fBtarget\fR statement may only be used when assembling in
absolute mode).
.PP
The result of the \fBtarget\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 \fBorg\fR or \fBtarget\fR statement. For example
.nf
\fBorg 0x1000
target 0x0800
foo: rts
bar: word 0x1234
word here
org someplaceElse\fR
.fi
would first set the current location counter to \fB0x1000\fR. At location
\fB0x1000\fR it would assemble an \fBrts\fR instruction while giving the label
\fBfoo\fR the value \fB0x800\fR. Then, at location \fB0x1002\fR it would
deposit the word value \fB0x1234\fR while giving the label \fBbar\fR the
value \fB0x802\fR. At location \fB0x1004\fR it would deposit the word value
\fB0x804\fR. Finally, the second \fBorg\fR would set the current location
counter to \fBsomeplaceElse\fR and the effects of the \fBtarget\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 (``\fB++\fR'' and
``\fB--\fR''), and calls to functions used like procedures are the most
useful applications of this. For example
.nf
\fBfoo = 5\fR
\fBbar++\fR
\fBprintf("Hello world\\n")\fR
.fi
are all valid statements.
.NH 0
\fRPrimitive expressions
.PP
The most primitive expressions are identifiers, numbers, characters, character
strings, 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 \fBhere\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 ``\fB0\fR''; octal
numbers by a sequence of octal digits that \fIdoes\fR begin with a
``\fB0\fR''; and hexadecimal numbers by a sequence of hexadecimal digits
(the decimal digits plus the letters ``\fBa\fR'' through ``\fBf\fR'', in
either upper or lower case), preceded by ``\fB0x\fR'' or ``\fB0X\fR''.
Binary numbers and quarters are represented analogously. Binary numbers are
represented by a sequence of ``\fB0\fR''s and ``\fB1\fR''s preceded by
``\fB0b\fR'' or ``\fB0B\fR''. Quarters are base-four numbers (for two-bit
entities like Atari pixels) and are represented by the sequences of the digits
\fB0\fR through \fB3\fR preceded by ``\fB0q\fR'' or ``\fB0Q\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 (``\fB'\fR''). The same conventions about
characters escaped with a backslash (``\fB\\\fR'') also apply. Strings may
be of varying lengths and are enclosed in quotation marks (``\fB"\fR''),
as discussed above in the explanation of instruction operands.
.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
D\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] ``\fB?:\fR'' (arithmetic if) \*- 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] ``\fB,\fR'' (comma) \*- used in \fIMacross\fR as an important separator
and thus not available.
.IP
[3] unary ``\fB*\fR'' and ``\fB&\fR'', \fBsizeof\fR, casts and
``\fB->\fR'' \*- not relevant here.
.LP
All of the assignment operators (``\fB+=\fR'', ``\fB-=\fR'', etc.)
\fIare\fR supported by \fIMacross\fR.
.LP
\fIMacross\fR reinterprets the \fB.\fR operator in that ``\fIexpression\fB .
\fIstructfieldname\fR'' is interpreted as adding the offset value implied by
\fIstructfieldname\fR (i.e., the distance in bytes into a \fBstruct\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] ``\fB?\fR'' \*- as a unary operator, takes the high order byte of the
word value that is its argument.
.IP
[2] ``\fB/\fR'' \*- as a unary operator, takes the low order byte of the
word value that is its argument.
.IP
[3] ``\fB^^\fR'' \*- 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 E\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
\fBmif\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 (``\fB++\fR'' and
``\fB--\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 \*- Macross 68000 Grammar\fR\s-5
\"\s+4\fBAppendix A \*- Macross 68000 Grammar\fR\s-4
.DE
.LP
.nf
\fR\fIprogram\fR:
\*[ \fIstatement\fR Newline \*Z Endfile
\fIstatement\fR:
\*[ \fIlabel\fR \*Z \fIopcode\fR \*[ \fIoperand\fR \*[ \fB,\fR \fIoperand\fR \*Z \*]
\*[ \fIlabel\fR \*Z \fBif\fR \fB(\fR \fIcondition\fR \fB)\fR \fIblock\fR
\*[ \fBelseif\fR \fB(\fR \fIcondition\fR \fB)\fR \fIblock\fR \*Z
\*[ \fBelse\fR \fIblock\fR \*]
\*[ \fIlabel\fR \*Z \fBwhile\fR \fB(\fR \fIcondition\fR \fB)\fR \fIblock\fR
\*[ \fIlabel\fR \*Z \fBdo\fR \fIblock\fR \fBwhile\fR \fB(\fR \fIcondition\fR \fB)\fR
\*[ \fIlabel\fR \*Z \fBdo\fR \fIblock\fR \fBuntil\fR \fB(\fR \fIcondition\fR \fB)\fR
\fIdataStatement\fR
\fBdefine\fR \fIidentifier\fR \*[ \fB=\fR \fIexpression\fR \*]
\fBvariable\fR \fIidentifier\fR \*[ \fB=\fR \fIexpression\fR \*]
\fBmacro\fR \fIidentifier\fR \*[ \fIidentifier\fR \*[ \fB,\fR \fIidentifier\fR \*Z \*] \fIblock\fR
\fBfunction\fR \fIidentifier\fR \fB(\fR \*[ \fIidentifier\fR \*[ \fB,\fR \fIidentifier\fR \*Z \*] \fB)\fR \fIblock\fR
\fBundefine\fR \fIidentifier\fR \*[ \fB,\fR \fIidentifier\fR \*Z
\*[ \fIlabel\fR \*Z \fIblock\fR
\fBmdefine\fR \fIidentifier\fR \*[ \fB=\fR \fIexpression\fR \*]
\fBmif\fR \fB(\fR \fIexpression\fR \fB)\fR \fIblock\fR
\*[ \fBmelseif\fR \fB(\fR \fIexpression\fR \fB)\fR \fIblock\fR \*Z
\*[ \fBmelse\fR \fIblock\fR \*]
\fBmwhile\fR \fB(\fR \fIexpression\fR \fB)\fR \fIblock\fR
\fBmdo\fR \fIblock\fR \fBwhile\fR \fB(\fR \fIexpression\fR \fB)\fR
\fBmdo\fR \fIblock\fR \fBuntil\fR \fB(\fR \fIexpression\fR \fB)\fR
\fBfreturn\fR \*[ \fIexpression\fR \*]
\fBmfor\fR \fB(\fR \fIexpression\fR \fB,\fR \fIexpression\fR \fB,\fR \fIexpression\fR \fB)\fR \fIblock\fR
\fBmswitch ( \fIselectionExpression \fB) {\fR
\*[ \fBmcase ( \fIexpression\fR \*[ \fB,\fI expression\fR \*Z \fB)\fI block\fR \*Z
\*[ \fBmdefault\fI block\fR \*]
\fB}\fR
\fBconstrain\fR \fB(\fR \fIexpression\fR \fB)\fR \fIblock\fR
\fBassert\fR \fB(\fR \fIexpression\fR \fB)\fR \*[ \fIexpression\fR \*]
\fBinclude\fR \fItextString\fR
\fBextern\fR \fIidentifier\fR \*[ \fB,\fR \fIidentifier\fR \*Z
\fBstart\fR \fIexpression\fR
\fBorg\fR \fIexpression\fR
\fBtarget\fR \fIexpression\fR
\fIexpression\fR
\fIdataStatement\fR:
\*[ \fIlabel\fR \*Z \fBblock\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fBalign\fR \fIexpression\fR
\*[ \fIlabel\fR \*Z \fBword\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fBlong\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fBdbyte\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fBbyte\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fBstring\fR \fIexpression\fR \*[ \fB,\fR \fIexpression\fR \*Z
\*[ \fIlabel\fR \*Z \fBstruct\fR \fB{\fR \*[ \fIdataStatement\fR \*Z \fB}\fR \fIidentifier\fR
\*[ \fIlabel\fR \*Z \fBstruct\fR \fIidentifier\fR
\fIlabel\fR: \fIidentifier\fR \fB:\fR
\fIoperand\fR:
\fBd\fIn\fR
\fBa\fIn\fR
\fBsp\fR
\fB\s-1[\s+1\fR \fBa\fIn\fR \fB\s-1]\s+1\fR
\fB\s-1[\s+1\fR \fBa\fIn\fR \fB\s-1]\s+1+\fR
\fB-\s-1[\s+1\fR \fBa\fIn\fR \fB\s-1]\s+1\fR
\fIexpression\fR
\fIexpression\fB . w\fR
\fIexpression\fB . l\fR
\fB#\fR \fIexpression\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBa\fIn\fR \fB\s-1]\s+1\fR
\fBa\fIn\fR \fB.\fR \fIidentifier\fR \*[ \fB.\fR \fIidentifier\fR \*Z
\fIexpression\fR \fB\s-1[\s+1\fR \fBa\fIn\fR \fB,\fR \fIrn\fR \fB. w \s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBa\fIn\fR \fB,\fR \fIrn\fR \fB. l \s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBa\fIn\fR \fB,\fR \fIrn\fR \fB\s-1]\s+1\fR
\fBa\fIn\fR \fB\s-1[\s+1\fR \fIrn\fR \fB. w \fB\s-1]\s+1\fR \fB.\fR \fIidentifier\fR \*[ \fB.\fR \fIidentifier\fR \*Z
\fBa\fIn\fR \fB\s-1[\s+1\fR \fIrn\fR \fB. l \fB\s-1]\s+1\fR \fB.\fR \fIidentifier\fR \*[ \fB.\fR \fIidentifier\fR \*Z
\fBa\fIn\fR \fB\s-1[\s+1\fR \fIrn\fR \fB\s-1]\s+1\fR \fB.\fR \fIidentifier\fR \*[ \fB.\fR \fIidentifier\fR \*Z
\fIexpression\fR \fB\s-1[\s+1\fR \fBpc\fR \fB\s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBpc\fR \fB,\fR \fIrn\fR \fB. w \s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBpc\fR \fB,\fR \fIrn\fR \fB. l \s-1]\s+1\fR
\fIexpression\fR \fB\s-1[\s+1\fR \fBpc\fR \fB,\fR \fIrn\fR \fB\s-1]\s+1\fR
\fBccr\fR
\fBsr\fR
\fBusp\fR
\fBsfc\fR
\fBdfc\fR
\fBvbr\fR
\fItextString\fR
\fIblock\fR: \fB{\fR \*[ \fIstatement\fR Newline \*Z \fB}\fR
\fItextString\fR:
\fB" \fIany string you like \fB"\fR
\fIcondition\fR:
\fIconditionCode\fR
\fB!\fR \fIconditionCode\fR
\fIexpression\fR:
\fIidentifier\fR
\fIidentifier\fR \fB(\fR \*[ \fIoperand\fR \*[ \fB,\fR \fIoperand\fR \*Z \*] \fB)\fR
\fInumber\fR
\fBhere\fR
\fItextString\fR
\fB(\fR \fIexpression\fR \fB)\fR
\fB-\fR \fIexpression\fR
\fB!\fR \fIexpression\fR
\fB~\fR \fIexpression\fR
\fB?\fR \fIexpression\fR
\fB/\fR \fIexpression\fR
\fIexpression\fR \fB*\fR \fIexpression\fR
\fIexpression\fR \fB/\fR \fIexpression\fR
\fIexpression\fR \fB%\fR \fIexpression\fR
\fIexpression\fR \fB-\fR \fIexpression\fR
\fIexpression\fR \fB+\fR \fIexpression\fR
\fIexpression\fR \fB<<\fR \fIexpression\fR
\fIexpression\fR \fB>>\fR \fIexpression\fR
\fIexpression\fR \fB<\fR \fIexpression\fR
\fIexpression\fR \fB>\fR \fIexpression\fR
\fIexpression\fR \fB<=\fR \fIexpression\fR
\fIexpression\fR \fB>=\fR \fIexpression\fR
\fIexpression\fR \fB==\fR \fIexpression\fR
\fIexpression\fR \fB!=\fR \fIexpression\fR
\fIexpression\fR \fB&\fR \fIexpression\fR
\fIexpression\fR \fB|\fR \fIexpression\fR
\fIexpression\fR \fB^\fR \fIexpression\fR
\fIexpression\fR \fB&&\fR \fIexpression\fR
\fIexpression\fR \fB||\fR \fIexpression\fR
\fIexpression\fR \fB^^\fR \fIexpression\fR
\fIexpression\fR \fB.\fR \fIidentifier\fR
\fIidentifier\fR \fB=\fR \fIexpression\fR
\fIidentifier\fR \fB+=\fR \fIexpression\fR
\fIidentifier\fR \fB-=\fR \fIexpression\fR
\fIidentifier\fR \fB*=\fR \fIexpression\fR
\fIidentifier\fR \fB/=\fR \fIexpression\fR
\fIidentifier\fR \fB%=\fR \fIexpression\fR
\fIidentifier\fR \fB&=\fR \fIexpression\fR
\fIidentifier\fR \fB|=\fR \fIexpression\fR
\fIidentifier\fR \fB^=\fR \fIexpression\fR
\fIidentifier\fR \fB<<=\fR \fIexpression\fR
\fIidentifier\fR \fB>>=\fR \fIexpression\fR
\fIidentifier\fR \fB++\fR
\fIidentifier\fR \fB--\fR
\fB++\fR \fIidentifier\fR
\fB--\fR \fIidentifier\fR
\fIidentifier\fR:
\*[\fBa\fR-\fBzA\fR-\fBZ_\fR\*]\*[\fBa\fR-\fBzA\fR-\fBZ_0\fR-\fB9\fR\*Z
\fInumber\fR:
\fIdecimalNumber\fR
\fIoctalNumber\fR
\fIbinaryNumber\fR
\fIhexadecimalNumber\fR
\fIquarter\fR
\fIdecimalNumber\fR:
\*[\fB1\fR-\fB9\fR\*]\*[\fB0\fR-\fB9\fR\*Z
\fIoctalNumber\fR:
\fB0\fR\*[\fB0\fR-\fB7\fR\*Z
\fIbinaryNumber\fR:
\fB0b\fR\*[\fB01\fR\*]\*[\fB01\fR\*Z
\fIhexadecimalNumber\fR:
\fB0x\fR\*[\fB0\fR-\fB9a\fR-\fBf\fR\*]\*[\fB0\fR-\fB9a\fR-\fBf\fR\*Z
\fIquarter\fR:
\fB0q\fR\*[\fB0\fR-\fB3\fR\*]\*[\fB0\fR-\fB3\fR\*Z
.fi
.bp
.CD
\s+5\fBAppendix B \*- Recognized Opcode Mnemonics\fR\s-5
\"\s+4\fBAppendix B \*- Recognized Opcode Mnemonics\fR\s-4
.sp 1
\s+3\fB(68000 version)\fR\s-3
.DE
.PP
These are the 68000 opcode mnemonics recognized by \fIMacross\fR:
.2C
.nf
\fBabcd
addb
addl
addqb
addql
addqw
addw
addxb
addxl
addxw
andb
andl
andw
aslb
asll
aslw
asrb
asrl
asrw
bcc
bchg
bclr
bcs
beq
bge
bgt
bhi
ble
bls
blt
bmi
bne
bpl
bra
bset
bsr
btst
bvc
bvs
chk
clrb
clrl
clrw
cmpb
cmpl
cmpmb
cmpml
cmpmw
cmpw
dbcc
dbcs
dbeq
dbf
dbge
dbgt
dbhi
dble
dbls
dblt
dbmi
dbne
dbpl
dbra
dbt
dbvc
dbvs
divs
divu
eorb
eorl
eorw
exg
extl
extw
illegal
jmp
jsr
lea
link
lslb
lsll
lslw
lsrb
lsrl
lsrw
movb
moveb
movel
moveml
movemw
movepl
movepw
moveq
movesb
movesl
movesw
movew
movl
movml
movmw
movpl
movpw
movq
movsb
movsl
movsw
movw
muls
mulu
nbcd
negb
negl
negw
negxb
negxl
negxw
nop
notb
notl
notw
orb
orl
orw
pea
reset
rolb
roll
rolw
rorb
rorl
rorw
roxlb
roxll
roxlw
roxrb
roxrl
roxrw
rtd
rte
rtr
rts
sbcd
scc
scs
seq
sf
sge
sgt
shi
sle
sls
slt
smi
sne
spl
st
stop
subb
subl
subqb
subql
subqw
subw
subxb
subxl
subxw
svc
svs
swap
tas
trap
trapv
tstb
tstl
tstw
unlk
.fi
.1C
.CD
\s+5\fBAppendix C \*- Condition Codes\fR\s-5
\"\s+4\fBAppendix C \*- Condition Codes\fR\s-4
.sp 1
\s+3\fB(68000 version)\fR\s-3
.DE
.PP
The \fIMacross\fR \fBif\fR, \fBwhile\fR, \fBdo-while\fR and \fBdo-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 68000 version of \fIMacross\fR, these are the
recognized condition code symbols:
.LP
.nf
\fBcarry\fR
\fBequal\fR
\fBgeq\fR
\fBgreater\fR
\fBgt\fR
\fBhigh\fR
\fBhigh_same\fR
\fBhs\fR
\fBless\fR
\fBleq\fR
\fBlow\fR
\fBlow_same\fR
\fBls\fR
\fBlt\fR
\fBminus\fR
\fBnegative\fR
\fBneq\fR
\fBoverflow\fR
\fBplus\fR
\fBpositive\fR
\fBzero\fR
.fi
.bp
.CD
\s+5\fBAppendix D \*- Built-In Functions\fR\s-5
\"\s+4\fBAppendix D \*- Built-In Functions\fR\s-4
.DE
.PP
Certain predefined built-in functions are supported by \fIMacross\fR for
reasons of convenience or syntactic or semantic irregularity. They are:
.nf
\fBaddressMode(\fIoperand\fB)\fR
.fi
.RS
Returns a number whose value indicates which addressing mode \fIoperand\fR
represents \fI((define these values))\fR.
.RE
.nf
\fBapply(\fImacname\fR \*[ \fB, \fIarg\fR \*Z \fB)\fR
.fi
.RS
Assembles the macro whose name is specified by the \fBstring\fR \fImacname\fR
with the macro arguments (if any) given by the \fIarg\fRs.
.RE
.nf
\fBgetAddressRegister(\fIoperand\fB)\fR
.fi
.RS
Returns a number from 0 to 7 indicating which address register is used in
\fIoperand\fR (assuming that the address mode of \fIoperand\fR is address
register, register indirect, postincrement, predecrement, displacement or
indexed).
.RE
.nf
\fBgetDataRegister(\fIoperand\fB)\fR
.fi
.RS
Returns a number from 0 to 7 indicating which data register is used in
\fIoperand\fR (assuming that the address mode of \fIoperand\fR is data
register).
.RE
.nf
\fBgetIndexRegister(\fIoperand\fB)\fR
.fi
.RS
Returns a number from 0 to 15 indicating which register is used as the index
register in \fIoperand\fR (assuming that the address mode of operand is
indexed or program counter indexed). The values 0 through 7 indicate the
address registers \fBa0\fR through \fBa7\fR and the values 8 through 15
indicate the data registers \fBd0\fR through \fBd7\fR.
.RE
.nf
\fBgetRegister(\fIoperand\fB)\fR
.fi
.RS
Returns a number from 0 to 15 indicating which register is used in
\fIoperand\fR. The values 0 through 7 indicate the address registers \fBa0\fR
through \fBa7\fR and the values 8 through 15 indicate the data registers
\fBd0\fR through \fBd7\fR.
.RE
.nf
\fBgetWL(\fIoperand\fB)\fR
.fi
.RS
Returns a number 0 or 1 indicatng whether \fIoperand\fR is a word operand or a
long operand (assuming that the address mode of \fIoperand\fR is absolute,
indexed or program counter indexed).
.RE
.nf
\fBisAbsoluteLongMode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if the address mode of \fIoperand\fR is
absolute long, otherwise \fBFALSE\fR.
.RE
.nf
\fBisAbsoluteMode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if the address mode of \fIoperand\fR is
absolute (short or long), otherwise \fBFALSE\fR.
.RE
.nf
\fBisAbsoluteShortMode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if the address mode of \fIoperand\fR is
absolute short, otherwise \fBFALSE\fR.
.RE
.nf
\fBisAbsoluteValue(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is an absolute (i.e.,
non-relocatable) value, otherwise \fBFALSE\fR.
.RE
.bp
.nf
\fBisARegister(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is an address register,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisBlock(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is a block, otherwise
\fBFALSE\fR.
.RE
.nf
\fBisBuiltInFunction(\fIsymbol\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIsymbol\fR is a built-in function,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisCCRegister(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is the condition code
register, otherwise \fBFALSE\fR.
.RE
.nf
\fBisConditionCode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is a condition code,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisControlRegister(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is a control register
(\fBusp\fR, \fBdfc\fR, \fBsfc\fR, or \fBvbr\fR), otherwise \fBFALSE\fR.
.RE
.nf
\fBisDefined(\fIsymbol\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIsymbol\fR has been defined, otherwise
\fBFALSE\fR.
.RE
.nf
\fBisDFCRegister(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is the destination function
code register, otherwise \fBFALSE\fR.
.RE
.nf
\fBisDRegister(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is a data register, otherwise
\fBFALSE\fR.
.RE
.nf
\fBisDisplacementMode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if the address mode of \fIoperand\fR
displacment, otherwise \fBFALSE\fR.
.RE
.nf
\fBisExternal(\fIsymbol\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIsymbol\fR is external (i.e., visible
outside the file in which it is defined), otherwise \fBFALSE\fR.
.RE
.nf
\fBisField(\fIsymbol\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIsymbol\fR is a field of a struct,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisFunction(\fIsymbol\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIsymbol\fR is a user defined function,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisImmediateMode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if the address mode of \fIoperand\fR is
\fIimmediate\fR, otherwise \fBFALSE\fR.
.RE
.nf
\fBisIndexedMode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if the address mode of \fIoperand\fR is an
indexed mode, otherwise \fBFALSE\fR.
.RE
.nf
\fBisPostincrementMode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if the address mode of \fIoperand\fR is
postincrement, otherwise \fBFALSE\fR.
.RE
.nf
\fBisPredecrementMode(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if the address mode of \fIoperand\fR is
predecrement, otherwise \fBFALSE\fR.
.RE
.nf
\fBisRelocatableValue(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is a relocatable value,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisSFCRegister(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is the source function code
register, otherwise \fBFALSE\fR.
.RE
.nf
\fBisStatusRegister(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is the status register,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisString(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is a string, otherwise
\fBFALSE\fR.
.RE
.nf
\fBisStruct(\fIsymbol\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIsymbol\fR is the name of a struct,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisSymbol(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is a symbol (as opposed to an
expression or a number, for example), otherwise \fBFALSE\fR.
.RE
.nf
\fBisUSP(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is the user stack pointer,
otherwise \fBFALSE\fR.
.RE
.nf
\fBisVBRegister(\fIoperand\fB)\fR
.fi
.RS
Returns \fBTRUE\fR if and only if \fIoperand\fR is the vector base register,
otherwise \fBFALSE\fR.
.RE
.nf
\fBlistingOff()\fR
.fi
.RS
If assembly listing has been enabled using the \fB-l\fR command line flag,
turn listing off temporarily. Otherwise, no effect.
.RE
.nf
\fBlistingOn()\fR
.fi
.RS
If assembly listing was turned off using the \fBlistingOff()\fR function, turn
it back on again. If listings have been globally disabled by not specifying
the \fB-l\fR command line flag, this function has no effect. The
\fBlistingOff()\fR and \fBlistingOn()\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
\fBlistingOff()\fR increments a counter and \fBlistingOn()\fR decrements it.
Only when the counter is zero (i.e., the number of \fBlistingOn()\fRs matches
the number of \fBlistingOff()\fRs) does listing actually occur.
.RE
.nf
\fBprintf(\fIformat\fR \*[ \fB, \fIarg\fR \*Z \fB)\fR
.fi
.RS
A formatted print routine just like the Unix system subroutine of the same
name.
.RE
.nf
\fBstrcat(\fIstring1\fB, \fIstring2\fB)\fR
.fi
.RS
Returns a string which is the concatenation of the two operands, which must
themselves be strings.
.RE
.nf
\fBstrcmp(\fIstring1\fB, \fIstring2\fB)\fR
.fi
.RS
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.
.RE
.nf
\fBstrcmplc(\fIstring1\fB, \fIstring2\fB)\fR
.fi
.RS
Essentially the same as \fBstrcmp()\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.
.RE
.nf
\fBstrlen(\fIstring\fB)\fR
.fi
.RS
Returns a number which is the length, in characters, of \fIstring\fR.
.RE
.nf
\fBsubstr(\fIstring\fB, \fIstartPos\fR \*[\fB , \fIlength\fR \*] \fB)\fR
.fi
.RS
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,
.nf
\fBsubstr("hello there", 6, 3)\fR yields \fB"the"\fR
\fBsubstr("hello there", -8, 2)\fR yields \fB"lo"\fR
\fBsubstr("hello there", 6, -3)\fR yields \fB"o t"\fR
\fBsubstr("hello there", -8, -4)\fR yields \fB"hell"\fR
\fBsubstr("hello there", 6)\fR yields \fB"there"\fR
\fBsubstr("hello there", -7)\fR yields \fB"hello"\fR
.fi
.RE
.nf
\fBsymbolLookup(\fIstring\fB)\fR
.fi
.RS
A call to this function with a string operand is equivalent to a reference to
the symbol that the string represents. For example,
.nf
\fBand symbolLookup("foo")\fR
.fi
is equivalent to
.nf
\fBand foo\fR
.fi
.RE
.nf
\fBsymbolName(\fIsymbol\fB)\fR
.fi
.RS
Returns a string which is the name of the symbol \fIsymbol\fR. For example,
\fBsymbolName(foo)\fR would return \fB"foo"\fR. This can be used in
conjunction with the \fBsymbolLookup\fR function so that the following:
.nf
\fBand symbolLookup(strcat(symbolName(foo), "bar"))\fR
.fi
is equivalent to
.nf
\fBand foobar\fR
.fi
.RE
.nf
\fBsymbolUsage(\fIsymbol\fB)\fR
.fi
.RS
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
.RE
.nf
\fBvalueType(\fIthing\fB)\fR
.fi
.RS
Returns a number whose value indicates the type of \fIthing\fR (i.e., symbol,
condition code, number, block, etc.). \fI((define these values))\fR
.RE
.fi
.bp
.CD
\s+5\fBAppendix E \*- Operator Set\fR\s-5
\"\s+4\fBAppendix E \*- Operator Set\fR\s-4
.DE
.PP
This appendix describes the (\fBC\fR derived) operators supported by
\fIMacross\fR.
.nf
\fB-\fR \fIexpression\fR integer negation
\fB!\fR \fIexpression\fR logical negation (0 goes to 1, all other values go to 0)
\fB~\fR \fIexpression\fR bitwise negation (ones complement)
\fB?\fR \fIexpression\fR high byte
\fB/\fR \fIexpression\fR low byte
\fIexpression\fR \fB*\fR \fIexpression\fR integer multiplication
\fIexpression\fR \fB/\fR \fIexpression\fR integer division
\fIexpression\fR \fB%\fR \fIexpression\fR integer modulus (remainder)
\fIexpression\fR \fB-\fR \fIexpression\fR integer subtraction
\fIexpression\fR \fB+\fR \fIexpression\fR integer addition
\fIexpression\fR \fB<<\fR \fIexpression\fR left shift
\fIexpression\fR \fB>>\fR \fIexpression\fR right shift
\fIexpression\fR \fB<\fR \fIexpression\fR less than
\fIexpression\fR \fB>\fR \fIexpression\fR greater than
\fIexpression\fR \fB<=\fR \fIexpression\fR less than or equal to
\fIexpression\fR \fB>=\fR \fIexpression\fR greater than or equal to
\fIexpression\fR \fB==\fR \fIexpression\fR equal to
\fIexpression\fR \fB!=\fR \fIexpression\fR not equal to
\fIexpression\fR \fB&\fR \fIexpression\fR bitwise AND
\fIexpression\fR \fB|\fR \fIexpression\fR bitwise OR
\fIexpression\fR \fB^\fR \fIexpression\fR bitwise XOR
\fIexpression\fR \fB&&\fR \fIexpression\fR logical AND
\fIexpression\fR \fB||\fR \fIexpression\fR logical OR
\fIexpression\fR \fB^^\fR \fIexpression\fR logical XOR
\fIexpression\fR \fB.\fR \fIidentifier\fR struct field selection
\fIidentifier\fR \fB=\fR \fIexpression\fR assignment
\fIidentifier\fR \fB+=\fR \fIexpression\fR assignment with addition
\fIidentifier\fR \fB-=\fR \fIexpression\fR assignment with subtraction
\fIidentifier\fR \fB*=\fR \fIexpression\fR assignment with multiplication
\fIidentifier\fR \fB/=\fR \fIexpression\fR assignment with division
\fIidentifier\fR \fB%=\fR \fIexpression\fR assignment with modulus
\fIidentifier\fR \fB&=\fR \fIexpression\fR assignment with AND
\fIidentifier\fR \fB|=\fR \fIexpression\fR assignment with OR
\fIidentifier\fR \fB^=\fR \fIexpression\fR assignment with XOR
\fIidentifier\fR \fB<<=\fR \fIexpression\fR assignment with left shift
\fIidentifier\fR \fB>>=\fR \fIexpression\fR assignment with right shift
\fIidentifier\fR \fB++\fR post-increment
\fIidentifier\fR \fB--\fR post-decrement
\fB++\fR \fIidentifier\fR pre-increment
\fB--\fR \fIidentifier\fR pre-decrement
.fi
.bp
.CD
\s+5\fB Appendix F \*- Character Escape Codes\fR\s-5
\"\s+4\fB Appendix F \*- Character Escape Codes\fR\s-4
.DE
.PP
Like \fBC\fR, \fIMacross\fR enables you to use the ``\fB\\\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
\fB\\n\fR newline
\fB\\t\fR horizontal tab
\fB\\b\fR backspace
\fB\\r\fR carriage return
\fB\\f\fR form feed
\fB\\e\fR escape
\fB\\\\\fR backslash
\fB\\'\fR apostrophe
\fB\\"\fR quote
\fB\\^\fIc\fR CONTROL-\fIc\fR (where \fIc\fR is any character).
\fB\\\fIddd\fR arbitrary byte (where \fIddd\fR is one, two or three octal digits).
.fi