A jump table
The same program in M68K, MIPS, Z80, x86.
An operation number can choose a piece of code through a table of addresses. This program uses
t2 = 2, so it selects entry 2 — the third entry, because indexes start at 0 — and leaves
6 * 3, or 18, in t6.
The table is useful when there are several choices. The index still has to be checked before it is
used as an address. Here the accepted operation numbers are 0 through 3. bgeu treats its inputs
as unsigned, so a negative 32-bit value also counts as out of range: its unsigned form is a very
large number.
.eqv CASE_COUNT, 4
.data
table: .word add_op, sub_op, mul_op, div_op
.text
main:
li t0, 6 # a = 6
li t1, 3 # b = 3
li t2, 2 # op = 2: entry 2, the third table entry
li t3, CASE_COUNT
bgeu t2, t3, out_of_range
la t3, table # base address of the table
slli t4, t2, 2 # byte offset = op * 4
add t4, t3, t4 # address of table[op]
lw t5, 0(t4) # address stored in table[op]
jr t5 # jump to that address
out_of_range:
li t6, -1 # no operation was selected
j done
add_op:
add t6, t0, t1 # a + b
j done
sub_op:
sub t6, t0, t1 # a - b
j done
mul_op:
mul t6, t0, t1 # a * b
j done
div_op:
div t6, t0, t1 # a / b
done: # every path finishes here; t6 holds the result
.word add_op, sub_op, mul_op, div_op puts four words in memory. Each word is the text-section
address of the label named after it. The exact numbers depend on where this copy of the program is
assembled. Open the memory panel at 10010000 to inspect the four addresses your assembly
produced; the important fact is their order: entry 0 leads to add_op, entry 1 to sub_op, and
so on.
After the check, the lookup has three steps. slli turns a word index into a byte offset by
multiplying it by 4. Adding that offset to the table base finds the selected word. lw reads the
address in that word, and jr t5 continues execution at the address held in t5. With t2 = 2,
the offset is 8, so lw reads the address of mul_op.
The check comes first because an invalid index would point outside the four-word table. Try t2
values 0, 1, and 3 to select the other operations. Then try 4 and -1: both take the
out_of_range path and leave -1 in t6, without loading an address from outside the table.
Each operation ends with j done. The handlers sit one after another in memory, so without that
jump, finishing add_op would continue into sub_op and overwrite the answer. Step through the
program once and watch t6: for the starting values it finishes at 18.
Try it: add a fifth operation
Complete this version to add a remainder operation selected by t2 = 4. Change CASE_COUNT to 5,
add rem_op as the fifth address in table, and write its handler. It must leave the remainder of
t0 / t1 in t6, then jump to done. Keep the range check.
.eqv CASE_COUNT, 4 # change to 5
.data
table: .word add_op, sub_op, mul_op, div_op # add rem_op
.text
main:
li t0, 6
li t1, 3
li t2, 4 # select the new operation
li t3, CASE_COUNT
bgeu t2, t3, out_of_range
la t3, table
slli t4, t2, 2
add t4, t3, t4
lw t5, 0(t4)
jr t5
out_of_range:
li t6, -1
j done
add_op:
add t6, t0, t1
j done
sub_op:
sub t6, t0, t1
j done
mul_op:
mul t6, t0, t1
j done
div_op:
div t6, t0, t1
j done
# Put rem_op here, before done.
done:
After it passes, change t2 to 5 and then -1. Both values should still leave -1 in t6.
Show one solution
.eqv CASE_COUNT, 5
.data
table: .word add_op, sub_op, mul_op, div_op, rem_op
.text
main:
li t0, 6
li t1, 3
li t2, 4
li t3, CASE_COUNT
bgeu t2, t3, out_of_range
la t3, table
slli t4, t2, 2
add t4, t3, t4
lw t5, 0(t4)
jr t5
out_of_range:
li t6, -1
j done
add_op:
add t6, t0, t1
j done
sub_op:
sub t6, t0, t1
j done
mul_op:
mul t6, t0, t1
j done
div_op:
div t6, t0, t1
j done
rem_op:
rem t6, t0, t1
j done
done: