ft CW
ta 8n +8n +8n +8n +8n +8n +8n
ft
TL
A Manual for the Plan 9 assembler
AU
Rob Pike
[email protected]
SH
Machines
PP
There is an assembler for each of the MIPS, SPARC, Intel 386,
Intel 960, AT&T DSP3210, and Motorola 68020.
The 68020 assembler,
CW 2a ,
is the oldest and in many ways the prototype.
The assemblers are really just variations of a single program:
they share many properties such as left-to-right assignment order for
instruction operands and the synthesis of macro instructions
such as
CW MOVE
to hide the peculiarities of the load and store structure of the machines.
To keep things concrete, the first part of this manual is
specifically about the 68020.
At the end is a description of the differences among
the other assemblers.
PP
The document, ``How to Use the Plan 9 C Compiler'', by Rob Pike,
is a prerequisite for this manual.
SH
Registers
PP
All pre-defined symbols in the assembler are upper-case.
Data registers are
CW R0
through
CW R7 ;
address registers are
CW A0
through
CW A7 ;
floating-point registers are
CW F0
through
CW F7 .
PP
A pointer in
CW A6
is used by the C compiler to point to data, enabling short addresses to
be used more often.
The value of
CW A6
is constant and must be set during C program initialization
to the address of the externally-defined symbol
CW a6base .
PP
The following hardware registers are defined in the assembler; their
meaning should be obvious given a 68020 manual:
CW CAAR ,
CW CACR ,
CW CCR ,
CW DFC ,
CW ISP ,
CW MSP ,
CW SFC ,
CW SR ,
CW USP ,
and
CW VBR .
PP
The assembler also defines several pseudo-registers that
manipulate the stack:
CW FP ,
CW SP ,
and
CW TOS .
CW FP
is the frame pointer, so
CW 0(FP)
is the first argument,
CW 4(FP)
is the second, and so on.
CW SP
is the local stack pointer, where automatic variables are held
(SP is a pseudo-register only on the 68020);
CW 0(SP)
is the first automatic, and so on as with
CW FP .
Finally,
CW TOS
is the top-of-stack register, used for pushing parameters to procedures,
saving temporary values, and so on.
PP
The assembler and loader track these pseudo-registers so
the above statements are true regardless of what has been
pushed on the hardware stack, pointed to by
CW A7 .
The name
CW A7
refers to the hardware stack pointer, but beware of mixed use of
CW A7
and the above stack-related pseudo-registers, which will cause trouble.
Note, too, that the
CW PEA
instruction is observed by the loader to
alter SP and thus will insert a corresponding pop before all returns.
The assembler accepts a label-like name to be attached to
CW FP
and
CW SP
uses, such as
CW p+0(FP) ,
to help document that
CW p
is the first argument to a routine.
The name goes in the symbol table but has no significance to the result
of the program.
SH
Referring to data
PP
All external references must be made relative to some pseudo-register,
either
CW PC
(the virtual program counter) or
CW SB
(the ``static base'' register).
CW PC
counts instructions, not bytes of data.
For example, to branch to the second following instruction, that is,
to skip one instruction, one may write
P1
       BRA     2(PC)
P2
Labels are also allowed, as in
P1
       BRA     return
       NOP
return:
       RTS
P2
When using labels, there is no
CW (PC)
annotation.
PP
The pseudo-register
CW SB
refers to the beginning of the address space of the program.
Thus, references to global data and procedures are written as
offsets to
CW SB ,
as in
P1
       MOVL    $array(SB), TOS
P2
to push the address of a global array on the stack, or
P1
       MOVL    array+4(SB), TOS
P2
to push the second (4-byte) element of the array.
Note the use of an offset; the complete list of addressing modes is given below.
Similarly, subroutine calls must use
CW SB :
P1
       BSR     exit(SB)
P2
File-static variables have syntax
P1
       local<>+4(SB)
P2
The
CW <>
will be filled in at load time by a unique integer.
PP
When a program starts, it must execute
P1
       MOVL    $a6base(SB), A6
P2
before accessing any global data.
(On machines such as the MIPS and SPARC that cannot load a register
in a single instruction, constants are loaded through the static base
register.  The loader recognizes code that initializes the static
base register and treats it specially.  You must be careful, however,
not to load large constants on such machines when the static base
register is not set up, such as early in interrupt routines.)
SH
Expressions
PP
Expressions are mostly what one might expect.
Where an offset or a constant is expected,
a primary expression with unary operators is allowed.
A general C constant expression is allowed in parentheses.
PP
Source files are preprocessed exactly as in the C compiler, so
CW #define
and
CW #include
work.
SH
Addressing modes
PP
The simple addressing modes are shared by all the assemblers.
Here, for completeness, follows a table of all the 68020 addressing modes,
since that machine has the richest set.
In the table,
CW o
is an offset, which if zero may be elided.
Many of the modes listed have the same name;
scrutiny of the format will show what default is being applied.
For instance, indexed mode with no address register supplied operates
as though a zero-valued register were used.
For "offset" read "displacement."
For "\f(CW.s\fP" read one of
CW .L ,
or
CW .W
followed by
CW *1 ,
CW *2 ,
CW *4 ,
or
CW *8
to indicate the size and scaling of the data.
IP
TS
l lfCW.
data register   R0
address register        A0
floating-point register F0
special names   CAAR, CACR, etc.
constant        $con
floating point constant $fcon
external symbol name+o(SB)
local symbol    name<>+o(SB)
automatic symbol        name+o(SP)
argument        name+o(FP)
address of external     $name+o(SB)
address of local        $name<>+o(SB)
indirect post-increment (A0)+
indirect pre-decrement  -(A0)
indirect with offset    o(A0)
indexed with offset     o()(R0.s)
indexed with offset     o(A0)(R0.s)
external indexed        name+o(SB)(R0.s)
local indexed   name<>+o(SB)(R0.s)
automatic indexed       name+o(SP)(R0.s)
parameter indexed       name+o(FP)(R0.s)
offset indirect post-indexed    d(o())(R0.s)
offset indirect post-indexed    d(o(A0))(R0.s)
external indirect post-indexed  d(name+o(SB))(R0.s)
local indirect post-indexed     d(name<>+o(SB))(R0.s)
automatic indirect post-indexed d(name+o(SP))(R0.s)
parameter indirect post-indexed d(name+o(FP))(R0.s)
offset indirect pre-indexed     d(o()(R0.s))
offset indirect pre-indexed     d(o(A0))
offset indirect pre-indexed     d(o(A0)(R0.s))
external indirect pre-indexed   d(name+o(SB))
external indirect pre-indexed   d(name+o(SB)(R0.s))
local indirect pre-indexed      d(name<>+o(SB))
local indirect pre-indexed      d(name<>+o(SB)(R0.s))
automatic indirect pre-indexed  d(name+o(SP))
automatic indirect pre-indexed  d(name+o(SP)(R0.s))
parameter indirect pre-indexed  d(name+o(FP))
parameter indirect pre-indexed  d(name+o(FP)(R0.s))
TE
in
SH
Laying down data
PP
Placing data in the instruction stream, say for interrupt vectors, is easy:
the pseudo-instructions
CW LONG
and
CW WORD
(but not
CW BYTE )
lay down the value of their single argument, of the appropriate size,
as if it were an instruction:
P1
       LONG    $12345
P2
places the long 12345 (base 10)
in the instruction stream.
(On all machines except the 68020, the only such operator is
CW WORD
and it lays down 32-bit quantities.)
PP
Placing information in the data section is more painful.
The pseudo-instruction
CW DATA
does the work, given two arguments: an address at which to place the item,
including its size,
and the value to place there.  For example, to define a character array
CW array
containing the characters
CW abc
and a terminating null:
P1
       DATA    array+0(SB)/1, $'a'
       DATA    array+1(SB)/1, $'b'
       DATA    array+2(SB)/1, $'c'
       GLOBL   array(SB), $4
P2
or
P1
       DATA    array+0(SB)/4, $"abc\ez"
       GLOBL   array(SB), $4
P2
The
CW /1
defines the number of bytes to define,
CW GLOBL
makes the symbol global, and the
CW $4
says how many bytes the symbol occupies.
Uninitialized data is zeroed automatically.
The character
CW \ez
is equivalent to the C
CW \e0.
The string in a
CW DATA
statement may contain a maximum of eight bytes;
build larger strings piecewise.
Two pseudo-instructions,
CW DYNT
and
CW INIT ,
allow the Alef compilers to build dynamic type information during the load
phase.
The
CW DYNT
pseudo-instruction has two forms:
P1
       DYNT    , ALEF_SI_5+0(SB)
       DYNT    ALEF_AS+0(SB), ALEF_SI_5+0(SB)
P2
In the first form,
CW DYNT
defines the symbol to be a small unique integer constant, chosen by the loader,
which is some multiple of the word size.  In the second form,
CW DYNT
defines the second symbol in the same way,
places the address of the most recently
defined text symbol in the array specified by the first symbol at the
index defined by the value of the second symbol,
and then adjusts the size of the array accordingly.
PP
The
CW INIT
pseudo-instruction takes the same parameters as a
CW DATA
statement.  Its symbol is used as the base of an array and the
data item is installed in the array at the offset specified by the most recent
CW DYNT
pseudo-instruction.
The size of the array is adjusted accordingly.
The
CW DYNT
and
CW INIT
pseudo-instructions are not implemented on the 68020.
SH
Defining a procedure
PP
Entry points are defined by the pseudo-operation
CW TEXT ,
which takes as arguments the name of the procedure (including the ubiquitous
CW (SB) )
and the number of bytes of automatic storage to pre-allocate on the stack,
which will usually be zero when writing assembly language programs.
On the MIPS and SPARC, the special value -4 instructs the loader to generate no PC save
and restore instructions, even if the function is not a leaf.
Here is a complete procedure that returns the sum
of its two arguments:
P1
TEXT    sum(SB), $0
       MOVL    arg1+0(FP), R0
       ADDL    arg2+4(FP), R0
       RTS
P2
An optional middle argument
to the
CW TEXT
pseudo-op is a bit field of options to the loader.
Setting the 1 bit suspends profiling the function when profiling is enabled for the rest of
the program.
For example,
P1
TEXT    sum(SB), 1, $0
       MOVL    arg1+0(FP), R0
       ADDL    arg2+4(FP), R0
       RTS
P2
will not be profiled; the first version above would be.
Subroutines with peculiar state, such as system call routines,
should not be profiled.
PP
Setting the 2 bit allows multiple definitions of the same
CW TEXT
symbol in a program; the loader will place only one such function in the image.
It is emitted only by the Alef compilers.
PP
Subroutines to be called from C should place their result in
CW R0 ,
even if it is an address.
Floating point values are returned in
CW F0 .
Functions that return a structure to a C program
receive as their first argument the address of the location to
store the result;
CW R0
is unused in the calling protocol for such procedures.
A subroutine is responsible for saving its own registers,
and therefore is free to use any registers without saving them (``caller saves'').
CW A6
and
CW A7
are the exceptions as described above.
SH
When in doubt
PP
If you get confused, try using the
CW -S
option to
CW 2c
and compiling a sample program.
The standard output is valid input to the assembler.
SH
Instructions
PP
The instruction set of the assembler is not identical to that
of the machine.
It is chosen to match what the compiler generates, augmented
slightly by specific needs of the operating system.
For example,
CW 2a
does not distinguish between the various forms of
CW MOVE
instruction: move quick, move address, etc.  Instead the context
does the job.  For example,
P1
       MOVL    $1, R1
       MOVL    A0, R2
       MOVW    SR, R3
P2
generates official
CW MOVEQ ,
CW MOVEA ,
and
CW MOVESR
instructions.
A number of instructions do not have the syntax necessary to specify
their entire capabilities.  Notable examples are the bitfield
instructions, the
multiply and divide instructions, etc.
For a complete set of generated instruction names (in
CW 2a
notation, not Motorola's) see the file
CW /sys/src/cmd/2c/2.out.h .
Despite its name, this file contains an enumeration of the
instructions that appear in the intermediate files generated
by the compiler, which correspond exactly to lines of assembly language.
SH
Laying down instructions
PP
The loader modifies the code produced by the assembler and compiler.
It folds branches,
copies short sequences of code to eliminate branches,
and discards unreachable code.
The first instruction of every function is assumed to be reachable.
The pseudo-instruction
CW NOP ,
which you may see in compiler output,
means no instruction at all, rather than an instruction that does nothing.
The loader discards all
CW NOP 's.
PP
To generate a true
CW NOP
instruction, or any other instruction not known to the assembler, use a
CW WORD
pseudo-instruction.
Such instructions on RISCs are not scheduled by the loader and must have
their delay slots filled manually.
SH
MIPS
PP
The registers are only addressed by number:
CW R0
through
CW R31 .
CW R29
is the stack pointer;
CW R30
is used as the static base pointer, the analogue of
CW A6
on the 68020.
Its value is the address of the global symbol
CW setR30(SB) .
The register holding returned values from subroutines is
CW R1 .
When a function is called, space for the first argument
is reserved at
CW 0(FP)
but in C (not Alef) the value is passed in
CW R1
instead.
PP
The loader uses
CW R28
as a temporary.  The system uses
CW R26
and
CW R27
as interrupt-time temporaries.  Therefore none of these registers
should be used in user code.
PP
The control registers are not known to the assembler.
Instead they are numbered registers
CW M0 ,
CW M1 ,
etc.
Use this trick to access, say,
CW STATUS :
P1
#define STATUS  12
       MOVW    M(STATUS), R1
P2
PP
Floating point registers are called
CW F0
through
CW F31 .
By convention,
CW F24
must be initialized to the value 0.0,
CW F26
to 0.5,
CW F28
to 1.0, and
CW F30
to 2.0;
this is done by the operating system.
PP
The instructions and their syntax are different from those of the manufacturer's
manual.
There are no
CW lui
and kin; instead there are
CW MOVW
(move word),
CW MOVH
(move halfword),
and
CW MOVB
(move byte) pseudo-instructions.  If the operand is unsigned, the instructions
are
CW MOVHU
and
CW MOVBU .
The order of operands is from left to right in dataflow order, just as
on the 68020 but not as in MIPS documentation.
This means that the
CW Bcond
instructions are reversed with respect to the book; for example, a
CW va
CW BGTZ
generates a MIPS
CW bltz
instruction.
PP
The assembler is for the R2000, R3000, and most of the R4000 and R6000 architectures.
It understands the 64-bit instructions
CW MOVV ,
CW MOVVL ,
CW ADDV ,
CW ADDVU ,
CW SUBV ,
CW SUBVU ,
CW MULV ,
CW MULVU ,
CW DIVV ,
CW DIVVU ,
CW SLLV ,
CW SRLV ,
and
CW SRAV .
The assembler does not have any cache, load-linked, or store-conditional instructions.
PP
Some assembler instructions are expanded into multiple instructions by the loader.
For example the loader may convert the load of a 32 bit constant into an
CW lui
followed by an
CW ori .
PP
Assembler instructions should be laid out as if there
were no load, branch, or floating point compare delay slots;
the loader will rearrange\(em\f2schedule\f1\(emthe instructions
to guarantee correctness and improve performance.
The only exception is that the correct scheduling of instructions
that use control registers varies from model to model of machine
(and is often undocumented) so you should schedule such instructions
by hand to guarantee correct behavior.
The loader generates
P1
       NOR     R0, R0, R0
P2
when it needs a true no-op instruction.
Use exactly this instruction when scheduling code manually;
the loader recognizes it and schedules the code before it and after it independently.  Also,
CW WORD
pseudo-ops are scheduled like no-ops.
PP
The
CW NOSCHED
pseudo-op disables instruction scheduling
(scheduling is enabled by default);
CW SCHED
re-enables it.
Branch folding, code copying, and dead code elimination are
disabled for instructions that are not scheduled.
SH
SPARC
PP
Once you understand the Plan 9 model for the MIPS, the SPARC is familiar.
Registers have numerical names only:
CW R0
through
CW R31 .
Forget about register windows: Plan 9 doesn't use them at all.
The machine has 32 global registers, period.
CW R1
[sic] is the stack pointer.
CW R2
is the static base register, with value the address of
CW setSB(SB) .
CW R7
is the return register and also the register holding the first
argument to a C (not Alef) function, again with space reserved at
CW 0(FP) .
CW R14
is the loader temporary.
PP
Floating-point registers are exactly as on the MIPS.
PP
The control registers are known by names such as
CW FSR .
The instructions to access these registers are
CW MOVW
instructions, for example
P1
       MOVW    Y, R8
P2
for the SPARC instruction
P1
       rdy     %r8
P2
PP
Move instructions are similar to those on the MIPS: pseudo-operations
that turn into appropriate sequences of
CW sethi
instructions, adds, etc.
Instructions read from left to right.  Because the arguments are
flipped to
CW SUBCC ,
the condition codes are not inverted as on the MIPS.
PP
The syntax for the ASI stuff is, for example to move a word from ASI 2:
P1
       MOVW    (R7, 2), R8
P2
The syntax for double indexing is
P1
       MOVW    (R7+R8), R9
P2
PP
The SPARC's instruction scheduling is similar to the MIPS's.
The official no-op instruction is:
P1
       ORN     R0, R0, R0
P2
SH
i960
PP
Registers are numbered
CW R0
through
CW R31 .
Stack pointer is
CW R29 ;
return register is
CW R4 ;
static base is
CW R28 ;
it is initialized to the address of
CW setSB(SB) .
CW R3
must be zero; this should be done manually early in execution by
P1
       SUBO    R3, R3
P2
CW R27
is the loader temporary.
PP
There is no support for floating point.
PP
The Intel calling convention is not supported and cannot be used; use
CW BAL
instead.
Instructions are mostly as in the book.  The major change is that
CW LOAD
and
CW STORE
are both called
CW MOV .
The extension character for
CW MOV
is as in the manual:
CW O
for ordinal,
CW W
for signed, etc.
SH
i386
PP
The assembler assumes 32-bit protected mode.
The register names are
CW SP ,
CW AX ,
CW BX ,
CW CX ,
CW DX ,
CW BP ,
CW DI ,
and
CW SI .
The stack pointer (not a pseudo-register) is
CW SP
and the return register is
CW AX .
There is no physical frame pointer but, as for the MIPS,
CW FP
is a pseudo-register that acts as
a frame pointer.
PP
Opcode names are mostly the same as those listed in the Intel manual
with an
CW L ,
CW W ,
or
CW B
appended to identify 32-bit,
16-bit, and 8-bit operations.
The exceptions are loads, stores, and conditionals.
All load and store opcodes to and from general registers, special registers
(such as
CW CR0,
CW CR3,
CW GDTR,
CW IDTR,
CW SS,
CW CS,
CW DS,
CW ES,
CW FS,
and
CW GS )
or memory are written
as
P1
       MOV\f2x\fP      src,dst
P2
where
I x
is
CW L ,
CW W ,
or
CW B .
Thus to get
CW AL
use a
CW MOVB
instruction.  If you need to access
CW AH ,
you must mention it explicitly in a
CW MOVB :
P1
       MOVB    AH, BX
P2
There are many examples of illegal moves, for example,
P1
       MOVB    BP, DI
P2
that the loader actually implements as pseudo-operations.
PP
The names of conditions in all conditional instructions
CW J , (
CW SET )
follow the conventions of the 68020 instead of those of the Intel
assembler:
CW JOS ,
CW JOC ,
CW JCS ,
CW JCC ,
CW JEQ ,
CW JNE ,
CW JLS ,
CW JHI ,
CW JMI ,
CW JPL ,
CW JPS ,
CW JPC ,
CW JLT ,
CW JGE ,
CW JLE ,
and
CW JGT
instead of
CW JO ,
CW JNO ,
CW JB ,
CW JNB ,
CW JZ ,
CW JNZ ,
CW JBE ,
CW JNBE ,
CW JS ,
CW JNS ,
CW JP ,
CW JNP ,
CW JL ,
CW JNL ,
CW JLE ,
and
CW JNLE .
PP
The addressing modes have syntax like
CW AX ,
CW (AX) ,
CW (AX)(BX*4) ,
CW 10(AX) ,
and
CW 10(AX)(BX*4) .
The offsets from
CW AX
can be replaced by offsets from
CW FP
or
CW SB
to access names, for example
CW extern+5(SB)(AX*2) .
PP
Other notes: Non-relative
CW JMP
and
CW CALL
have a
CW *
added to the syntax.
Only
CW LOOP ,
CW LOOPEQ ,
and
CW LOOPNE
are legal loop instructions.  Only
CW REP
and
CW REPN
are recognized repeaters.  These are not prefixes, but rather
stand-alone opcodes that precede the strings, for example
P1
       CLD; REP; MOVSL
P2
Segment override prefixes in
CW MOD/RM
fields are not supported.
SH
3210
PP
Opcode names for integer instructions follow the conventions used for the MIPS,
with an appended
CW H
indicating a 16-bit operation.
The unusual instructions are
CW ADDCR ,
CW ADDN ,
CW BIT ,
CW CALL ,
CW DBRA ,
CW IRET ,
CW ROL ,
CW ROR ,
CW SUBR ,
CW SFTRST ,
and
CW WAITI .
The condition to test is specified as the first parameter to conditional opcodes.
PP
The basic floating arithmetic opcodes are
CW FMUL ,
CW FADD ,
CW FSUB ,
CW FMADD ,
and
CW FMSUB .
A
CW T
suffix specifies the tap versions of the instructions,
while a
CW N
suffix negates the accumulator argument.
The parameters are in the reverse order from the 3210 manual.
An
CW FDIV
pseudo-op is provided.
Conversions are specified as moves; the remaining floating instructions are
CW FIEEE ,
CW FDSP ,
CW FROUND ,
CW FSEED ,
CW FIFEQ ,
CW FIFEQ ,
CW FIFGT ,
and
CW FIFLT .
PP
CW DO
instructions take as a second parameter the number of instructions in the loop
or a label that points to a
CW DOEND
instruction.
CW DOEND
instructions have one parameter, a label pointing to the corresponding
CW DO
instruction.
All instructions between the
CW DO
and
CW DOEND
instructions are in the loop.
CW DOLOCK
instructions have the same syntax as
CW DO
instructions.
CW BMOVW
is a block move instruction macro.
PP
Instructions are scheduled like MIPS instructions.
Floating point instructions are scheduled.
PP
The condition codes are capitalized versions of the standard 3210 names,
with the exception that an F is prefixed for floating point and an O is used
for overflows.