A jump table

Four addresses in memory, an index that picks one of them, and a jp (hl) instead of a chain of comparisons.

A number picks which of four pieces of code runs. a holds 2, the program reads the third address out of a table in memory and jumps to it, and the multiplication is what happens. Changing that one number changes the answer without changing a comparison anywhere.

The bigger of two numbers chose between two paths with a cp and a jump. A chain of those works for three or four cases and gets slower with every one you add, since a value at the bottom of the chain is compared against everything above it first. A table is looked up once whatever the value is.

You need to know: the "jp, jr and the conditions" lecture and the "Addressing on the Z80" lecture. What is new here is a jump to an address the program worked out, jp (hl) goes to whatever hl holds, which nothing in the source names.

.dw add_op, sub_op, mul_op, div_op writes four words, and each one is the address the assembler gave that label. Put the memory panel on 9000 and the eight bytes read 13 80 17 80 1B 80 22 80, which little endian is 0x8013, 0x8017, 0x801B and 0x8022, four addresses inside your own code. A label is nothing but an address, and this is what that sentence is for.

The five instructions from ld l, a down to add hl, bc are C's table[op]. The index is widened into a pair, doubled with add hl, hl because an address is two bytes, and added to the base, which is the shape every indexed read takes here: the Z80 has no base plus index mode, so (hl) is the only thing that reads what came out.

Reading the address out of the table takes four more instructions and a spare register. ld a, (hl) takes the low byte first, inc hl and ld h, (hl) take the high byte, and the low byte cannot go into l until h has been read, because writing l first would move the pointer the second read is using. That is the whole reason a is borrowed in the middle.

jp (hl) then jumps without pushing anything, which is what makes this a switch and not four calls. The parentheses are Zilog's spelling and nothing is read from memory: the jump goes to the address in hl.

a and c both come out at 12, which is 18, and hl at 801B, the address of mul_op. Each arm ends with jr done for the same reason the two halves of an if do: the arms are laid out one after another and nothing stops the program running into the next one.

Try changing ld a, 2 to ld a, 3 and a comes out at 02, which is 6 divided by 3. With ld a, 0 it comes out at 09. Nothing else in the program moves, and there is no comparison anywhere in it to change.