ADDRESSING MODES
Back when a full 3-hour course on
assembly language programming was a part of the curriculum, approximately
one-third of the course was devoted to the intricacies of addressing modes. In this
class, we will spend considerably less time on them. An addressing
mode is simply a way of specifying an operand.
Absolute
addressing. The
address that is specified in the instruction is the actual address of the
operand. Absolute addressing was the mode used by MARIE. Although this may seem
like the obvious way to specify an operand, it has several shortcomings:
Assume that the op code for a
"move byte" instruction is 0000 0010 (02 in hex). Assume that we
want to move data from location 5 to location 6 using absolute addressing on a
machine with 32-bit addresses. The 5 is 0000 0000 0000 0000 0000 0000 0000 0101
(00 00 00 05 in hex) and the 6 is
0000 0000 0000 0000 0000 0000 0000 0110
(00 00 00 06 in hex). It would seem that the complete instruction (in hex)
would be 02 00 00 00 05 00 00 00 06. And this would work if there were only one addressing
mode. However, since there is more than one addressing mode (more than one way
to interpret the operand bits in the instruction), we need to also tell which
addressing mode is being used (for each operand). Assume that one byte is used
to specify the addressing mode for each operand. And assume that absolute
addressing mode is mode number 1 (01 in hex). Our instruction now becomes: 02 01 00 00 00 05 01 00 00 00 06, where the bold underlined part of each operand is used to
specify the addressing mode.
Because of the problems with absolute
addressing, computer designers have developed other addressing modes.
Since data must be loaded into
registers before any processing can be done on it, we must have a way of specifying
which register we wish to use. Three bits in an instruction can specify one of
eight registers. Four bits can specify one of 16 registers. "n" bits can specify one of 2n registers.
This is the reason that the number of registers in a CPU is always a power of
two.
Assume that we have a CPU with 16
registers. Also assume that register addressing mode is mode number 2. To move
a byte (op code is 02 in hex) from register 9 (09 in hex) to register 10 (0A in
hex), our instruction would look like this: 02 02 09 02 0A, where the blue number is the op code, the green number is
the first operand (register 09), and the red number is the second operand
(register 0A).
These two addressing modes are similar in that they
both specify an offset that is to be added to a
register. When the offset is added to the address in the register, the result
is the address of the operand. The only difference between the two methods is that
with base register addressing, the base register must be specified as part of
the operand, and with relative addressing, the "base register" is the
program counter.
Assume that the contents of the base register
(we'll use register 8) is 00 00 01 00 (hex). And assume that we want to move a byte (op code is 02 in hex)
from location 00 00 01 05 (an offset of 5 bytes from the address
in the base register) to 00 00 01 06 (an offset of 6 bytes from
the address in the base register). Also assume that base register addressing
mode is mode number 3. With base register addressing, it is not enough to know
the addressing mode. The base register must also be specified. We will use one
byte to specify the addressing mode (03) and another byte to specify the base
register (08) for each operand. Our final assumption is that the offset is
stored as a 16-bit number. Our instruction will look like this: 02 03 08 00 05 03 08 00 06 where the blue number is the op code, the green number is the
first operand (the bold underlined part is the addressing mode specifier) and
the red number is the second operand.
Assume that the byte we want to move is 100 (hex) bytes beyond the current instruction. This amount can be added to the PC to get the correct address. In this case, we are using the PC as a base register. This may be a separate mode. Assume mode 04. Since the PC is implied in relative addressing mode, we do not need to specify a register – only the mode -- relative addressing. Again we will assume that our offset can be stored in a 16-bit number. Let's assume that we want to move a byte from 100 bytes past the PC to register 5. Our instruction will look like this: 02 04 01 00 02 05
Sometimes, it would be convenient for
an operand to be part of the instruction itself. This means that the operand
would have to be a constant. When the operand is a part of the instruction
itself, the addressing mode is called "immediate" mode (because the
operand is available immediately -- it's part of the
instruction -- you don't have to go back to memory to fetch it).
Assume that we want to move (op code is
02) a 3 into register 7. And assume that immediate addressing mode is mode
number 5 (05). And assume that immediate operands occupy one byte. Our
instruction will look like this: 02 05 03 02 07
where the blue number is the op code,
the green number is the first operand (an immediate 3), and the red number is
the second operand (register 7).
Direct addressing is simple: the
address of the operand is specified in the instruction. With indirect
addressing, the address of the address is specified in the instruction.
Assume that we want to move (op code
02) the number at location 00 00 02 00 into register 7. And
assume that indirect addressing mode is mode number 6 (06). Also, assume that
the address 00 00 02 00 is stored in location 00 00 12 34.
Using direct addressing, the instruction would look like this: 02 01 00 00 02 00 02 07 However, with indirect addressing, the instruction would
look like this: 02 06 00 00 12 34 02 07
where the blue number is the op code, the green number is the first operand (06
= indirect, so go to location 00 00 12 34, not for the data as
with direct addressing, but for the address of the data: 00 00 02 00),
and the red number is the second operand (register 7).
This is exactly the same as indirect
addressing, except that the address of the operand is in a register rather than
in a memory location.
Assume that, like the previous example,
we want to move (op code 02) the number at location 00 00 02 00
into register 7. And assume that register indirect addressing mode is mode
number 7 (07). Also assume that the address 00 00 02 00 is
stored in register 12 (0C). Our instruction will look like this: 02 07 0C 02 07 where the blue number is the op code, the green number is
the first operand (07 = register indirect, and 0C is the register number), and
the red number is the second operand (register 7).
You are already familiar with the idea of indexing,
except that you probably called it subscripting. In a high-level
language, indexing looks like this:
for i= 1 to 10 dowriteln (list[i]);
Because actual subscripts are impossible to do on punched
cards or computer terminals (and before the advent of the bit-mapped graphics
displays that are common today), instead of writing listi, we wrote list[i],
instead. The code above will print out the items numbered 1 through 10 of an
array named "list". Because an array is such a common data structure,
many CPU's have an indexed addressing mode that makes it easier for the
machine/assembly language programmer to step through an array. Such an
addressing mode will use an index register. The value in the
index register is added to another base address to determine the
actual address of the data.
Assume that indexed addressing mode is mode 8 (08). Assume
that we have an array starting at location 00 00 01 00. Also
assume that the index register is register 10 (0A), and that it currently holds
the value 3 (note that because the 3 is an offset, it will actually refer to the fourth
item in the array). To move the fourth byte in the array into register 7, our instruction
would look like this: 02 08 0A 01 00 00 01 00 02 07. Note that, to do indexed
addressing, two addressing modes must be combined. In addition to the indexed
addressing mode (the light green part of the first operand), a base address
must be specified (the dark green part of the first operand). This instruction
is executed as follows: the 02 means "move byte"; the 07 0A means use
register 10 (0A) as an index into the array whose base address follows; the
base address is 00 00 01 00 (direct addressing); and the
destination is register 7.
Autoincrement and autodecrement are identical to indexed addressing mode
except that the index gets automatically incremented (or decremented) each time
you use it in a loop (just like the "for" loop in most high-level
languages).
This sounds a little too hairy to go into much detail. It
can be used to step through an array of pointers (addresses) such as a
parameter list.