What happens with variables whose locations are determined at run-time? There are three categories of variables like this:
How do you use indexed addressing? First, concepts relating the ideas to a high-level langauge.
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 a string from a source
array to a destination array, while making sure it doesn't overflow
the destination buffer. 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. This
example actually accomplishes the same function as the C
strncpy
function.
RAM equ 0
EEPROM equ $f800
BUFSIZ equ 16
org RAM
dst rmb BUFSIZ * space for destination array
org EEPROM
src fcc 'This is a too-long source string'
fcb 0 * terminate the string with a null, just like C does
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
beq done * if that was the null, we just finished
inx * increment pointers
iny
decb
bne loop * if we aren't out of room, 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.
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
.
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