So far, we've had to determine every address we use in a program at assembly-time: the variables, the addresses we branch to, and everything. It turns out that many addresses used in a program aren't determined until run-time. Some examples of addresses that are determined at run-time include:
The figure shows an array with five elements. In order to access the elements, their addresses have to be determined at run-time.
Processors in general may include any of a number of major mechanisms to deal with run-time calculation of variable addresses:
The HC11 follows what is becoming pretty much standard practice in modern architectures of only providing what Motorola calls indexed addressing (though it actually meets the ``standard'' definition of based addressing).
How do you use indexed addressing? First, concepts relating the ideas to a high-level langauge.
Suppose you have a record, such as a C struct or data
in a Java class.
You put a pointer to it in the index register, and use an offset to
the field you want in the offset field. Notice that you never iterate
through the fields of a struct (the language syntax doesn't even
support it); you always know the offset at compile-time.
Something that makes this more convenient is the equ
assembler directive. You can define symbols for all the offsets and
for the size of the object, and refer to the symbols later.
$ff, see
later.
,x or
offset,y. For instance
ldaa 7,x
will load the A accumulator from address 7+x: so if
IX contains 42, you'll load from address 49.
You say
ldx #label
ldy #label
The index registers are capable of very little arithmetic. You can
increment them, you can decrement them, you can add the B accumulator
to either one, you can load them, you can store them, you can exchange
them with D, you can transfer them back and forth to the stack pointer.
That's pretty much it.
Here's an example of a program which will copy an array from a source array to a destination array. The source array is at the beginning of EEPROM, while the destination array is at the beginning of RAM. Hopefully, the comments will make what's happening clear.
RAM equ 0
EEPROM equ $f800
BUFSIZ equ 8
org RAM
dst rmb bufsiz * space for destination array
org EEPROM
src fcc 5, 7, 9, 13, 15, 37, $12, %010101
esrc
bufsiz equ esrc-src
start ldab #bufsiz * guard so we don't overwrite
ldx #src * pointer to source
ldy #dst * pointer to destination
loop ldaa 0,x * get a char from source string
staa 0,y * copy to destination string
inx * increment pointers
iny
decb
bne loop * if we aren't done, continue
done
eloop bra eloop
end start
Here's another example: this one isn't a complete program, it just shows how to compare a value in an array against the next value.
ldaa 0,x * get the number IX points to
cmpa 1,x * compare it against the next number
Once that's been done, the result of the comparison is in the condition codes (as usual).
If we have an array in RAM, we can use IX to hold its index. So, if we have an array that we would have declared in C as
char awry[10];
then in HC11 assembler we would reserve space for this same array with
awry rmb 10
Now, the way we would exectute the C statement
a = awry[ix];
(note the use of a as the destination of the assignment
and IX as the array index) would be to use the IX
register to hold the array index and use the instruction
ldaa awry,x
One last thing we can do is to put some arithmetic in the offset. So,
for instance, suppose we have an array called awry, and
we want to compare a element of the array against its neighbor at
index+1. We'll use IX to contain the array index, and
the code can look like
ldaa awry,x
cmpa awry+1,x
Note that there are some very severe restrictions on the arithmetic we
can do in the offset: we can only have constants in there (so an
offset of awry+a would not use the a
accumulator), and we can't try to save the result of the expression.
In other words, the expression is evaluated at assembly time, and
can't change during execution.
Using the y index register also gives us an opportunity
to learn about a standard hack that manufacturers apply to their
instruction sets to expand them beyond the limitations of their
original design: a prefix byte.
Early members of the Motorola 6800 family only had a single index
register: the x register. When they wanted to add a
second index register, they had a problem: their were too many
instructions using indexed addressing to just add that many new op
codes to the instruction set. This left them with two choices: they
could either cripple the new y index register, leaving it
capable of doing less than the x index register, or they
could put a prefix byte into the instruction set. A prefix
byte is a byte that ``adjusts'' what the instruction does. So,
ldaa 3,x assembles to a6 3, while
ldaa 3,y assembles to
18 a6 3.
It turns out the HC11 has three prefix bytes: $18,
$1a, and $cd, corresponding to code pages 2,
3, and 4 (on pages 9, 10, and 11 of the reference guide).
Notice that this means that any instruction using the y
index register will be a byte larger and a cycle slower than the
corresponding x index register instruction. Which means
that if you're only going to need one index register use
x, if you're going to need both then try to do more with
x than with y.
One very important use of indexed addressing on the HC11 is the case
where you want to use the various bit-twiddling instructions (like
bset, brset, and their friends) to examine
or control devices. Unfortunately, the devices are in a block
starting at $1000, and those instructions don't support
extended addressing. So, you can set one of the index registers to
point to $1000, and use a one-byte offset to specify the
particular device control register.
Here's an example of the use of indexed addressing to copy a character
string from one location to another - effectively, the C
strcpy function. We'll let the source string be in
EEPROM, and the destination string be in RAM.
RAM equ 0
EEPROM equ $f800
org RAM
dst rmb 10
org EEPROM
src fcc _The string_
fcb 0
start ldx #src
ldy #dst
strcpy ldaa 0,x
stab 0,y
inx
iny
tsta
bne strcpy
done bra done
end start