Jumping around

So far, all programs we have seen executed from top to bottom, and there weren't any conditions. It's time to change that!

Some context

Let's quickly talk about how the CPU processes instructions. The CPU has another 16-bit register I didn't talk about yet, which is called pc, the program counter. It's simply the memory address where the CPU is reading instructions from. When the CPU executes an instruction, the following happens:

  1. The byte [pc] is read (this means that if PC = $1234, the byte at $1234 is read)
  2. pc is incremented
  3. That byte is decoded so the CPU knows which instruction it should execute
  4. If the instruction has operand byte(s), it is/they are read as well, one by one, and pc is incremented after each read
  5. The instruction is executed proper

So far, execution has been linear - from $0000 to $FFFF. However, some instructions modify pc! There are five such instructions: jp, jr, call, rst and ret. The latter three make use of the stack, which is a concept we will see later. For now, we will focus on jp and jr.

A CPU on a pogo stick

jp lets execution "jump" (that's how it's called) to another place in memory. jp $1234 simply sets pc to $1234 -- if you want, it's ld pc, $1234, although that syntax is invalid :3

This essentially enables execution to flow in any order, which resolves the linearity problem. But not the conditional problem. Fear not! For jp is an instruction that can be executed conditionally! Depending on what, you may ask? Depending on our trusty flags.

Conditions

There are four conditions available, which act depending on two flags: C and Z.

ConditionMnemonicTrue when...
Non-zero nz Zero reset
Zero z Zero set
No carry nc Carry reset
Carry c Carry set

Remember that labels are references to memory locations, which make them very common operands for jp. Here's a small code snippet that uses several things we've seen so far:

    ld a, [wPlayerHP]
    and a ; Is it 0?
    jp z, GameOver ; If HP hit 0, play Game Over animation
    cp 10 ; Is it under 10 HP?
    jr nc, .noLowHealthBeep ; If above 10 HP, don't beep

    ; We're going to see just below that jr is essentially the same as jp
    ...

.noLowHealthBeep

Short, but fast

Now, we saw how jp works. But what about jr? It also performs jumps, but they're relative. Relative to what? To pc! Instead of specifying the destination address, jr takes one byte as its operand, the offset, which is added to pc to determine the target destination.

Pro: smaller and faster
Con: can only go 128 bytes backwards or 127 bytes forwards

As for the syntax... it's the same as jp! The nice part about the assembler is that it computes the offset for you, based on where the instruction and the target will be located. If the offset cannot be encoded as a jr, then it will simply complain and error out. I recommend using jr by default, and jp when it can't be used.

And by the way, jr supports the same conditions as jp. Neat!


Does it all make sense so far? We are very close to making our Hello World, don't worry. All that's left for us to figure out is how to draw things to the screen...