The two most basic addressing modes are called "immediate" and "direct."
Suppose we have a statement in C like
i = j + 3;
i
and j
are variables, so they are out in
memory. To execute this statement, we will need to fetch
j
from memory, and write our result to i
.
That means the instructions we generate need to have the addresses of
i
and j
, and needs to read and write those
addresses as appropriate.
The number 3
, on the other hand, is an actual value
appearing in the statement. So, our code needs to include
3
itself.
Even if we define a symbol, like
#define FRED 3
i = j + FRED;
FRED
gets replaced by 3, and immediate addressing still
ends up being used.
In assembly code, the statement described there takes three steps:
j
from memory.3
to it.i
In assembly code, this becomes:
ldaa j
adda #3
staa i
In a high level language, we don't (ordinarily) specify addressing
modes. Instead, we just use variables and constants and rely on the
language to know what we're talking about: if we use the constant
3
, the compiler generates immediate mode; if we use the
variable j
the compiler generates direct mode.
The assembler doesn't work that way. At this level, the assembler is
perfectly happy to use a constant 1
as a direct address, or to
use the address of j
as an immediate value. So we have
to tell the assembler what we want to do.
We do this by putting a #
immediately in front of the
operand for immediate addressing, and leaving it off for direct
addressing. If we forget the #
, the assembler will
be perfectly happy; it's not an error. But it's likely to be a bug!
Many small computers (including the HC11) actually have to forms of
direct addressing, so we can handle small addresses more efficiently
than large addresses. In the HC11, an address between
$0000
and $00ff
can be represented as eight
bits, while addresses from $0100
through
$ffff
require 16. Eight bit addresses are called
"direct," while 16 bit addresses are called "extended."
We don't have to worry about direct vs. extended addresses. If it's possible to use a direct address (the instruction has a direct form, and the address will fit in eight bits) the assembler will generate a direct address; otherwise, it will generate an extended address.
It would be possible to define variables in assembly code completely "by hand." We could have code that said something like
i equ 0
j equ 1
Which would set i
at address 000016 and
j
at address 000116. While that will work,
it's inconvenient and easy to make errors. It's problematic enough
for scalar variables; when we get to arrays it'll be really bad.
So, instead, we have a way to reserve space for variables (this is
analogous to declaring variables in a high level language). We do
this with the rmb
directive. If we say
org RAM
i rmb 1
j rmb 1
The assembler will:
equ
)
i
. Since the
assembler is working at location 0, i
will be at
000016
j
. Since we just
reserved a byte at 000016, this byte will be at
000116.
Here's what this looks like in an assembly listing file:
Assembling vars.asm
0001 0000 RAM equ 0
0002 0000 org RAM
0003 0000 i rmb 1
0004 0001 j rmb 1
Notice that the second column is telling us where we're putting
i
and j