9

I've looked at a few different websites and they all agree that a JP nnnn instruction (where nnnn is a 16-bit address) always takes 10 states to execute.

According to what I've read, all of the following instructions take 10 states, even the conditional ones, and even if the jump is not taken:

  • JP nnnn
  • JP c, nnnn
  • JP nc, nnnn
  • JP z, nnnn
  • JP nz, nnnn
  • JP po, nnnn
  • JP pe, nnnn
  • JP p, nnnn
  • JP m, nnnn

These sources also agree that this is not the case for JR,CALL, and RET: those instructions are faster when unconditional, and are also faster if the condition is false and no jump/call/return occurred. I would have expected the same to be true for JP but apparently not. Why is this?

EDIT: JP and CALL are not faster when unconditional. That was a mistake I made when looking at my chart. I was only looking at RET when I made that statement.

EDIT 2: The above edit should have said "JR and CALL."

Source 1 Source 2

6
  • JR and CALL are not faster when unconditional. Unconditional JR takes 12 t-states, just the same as JR cc with the condition being true. Unconditional CALL takes 17 t-states, just the same as CALL cc with the condition being true.
    – introspec
    Oct 18, 2021 at 15:11
  • In fact, unconditional JR is slower than unconditional JP, so it is not even clear if these commands can be compared fairly.
    – introspec
    Oct 18, 2021 at 15:12
  • You're right, that was my mistake. JR and CALL are not faster when unconditional. They're only faster when the condition is false. Oct 18, 2021 at 15:14
  • @puppydrum64 - your recent edit says 'JP and CALL', I assume that's a typo. Oct 18, 2021 at 15:23
  • 2
    The above edit should have said — couldn’t you have just put it there instead of stringing together ‘disregard what I said, I actually meant something else entirely’? Oct 21, 2021 at 9:38

1 Answer 1

11

The JR instructions are slow when the branch is taken because they use a four-bit(!) ALU to compute the new address. This calculation can't start until after the target address is fetched (which it always will be). If the branch is skipped, the Z80 will skip those additional calculations.

When processing a JP instruction, the Z80 fetches the next two bytes and increments the program counter while doing so. It might have been possible to add circuitry to suppress the memory fetch if the branch will be skipped, which would have saved one cycle for each of those bytes (each increment of a 16-bit register would require two cycles regardless). Since the Z80 isn't performing any arithmetic with the target-address bytes, making use of them once they're fetched is no more expensive than discarding them.

The JR instruction as well as the IX+d and IY+d addressing modes could probably have been made a couple of cycles more efficient if the Z80 had included logic to allow the 16-bit increment/decrement unit to support +256 and -256 functions and logic to output the high byte only. If that had been done, the increment/decrement unit could have used the high bit of the displacement to select +256 or -256 mode, and started computing a possible new high byte for the address while the ALU computed the low half. If a carry or borrow occurred, the new address would be formed using the high byte from the increment/decrement unit and the low byte from the ALU. If none occurred, the new address would be formed using the original address high byte and the ALU. I wouldn't be surprised if there may have been plans to do such a thing, but space or time constraints prevented it from being implemented. The design of the instruction set would make a lot more sense if some of the slow instructions were a couple of cycles faster.

10
  • 1
    That's good to know, thank you. Interestingly, I found that JP (HL) only takes 4 cycles. Right now I'm doing the math to see at what point it's quicker to use JP (HL) than JP abs for a loop that's too long for a DJNZ. Oct 18, 2021 at 15:20
  • 4
    If the target address is in HL, JP(HL) will be faster than JP because it requires nothing more than forwarding HL into the addressing unit for the following fetch rather than PC. No arithmetic required. Using a combination of RET NZ and JP (HL) will increase the "setup time" for a loop, but reduce the iteration time since the combined execution time for RET NZ that is not taken and JP (HL) which is always taken is only eight cycles.
    – supercat
    Oct 18, 2021 at 15:28
  • 2
    Wouldn't it be 9 cycles (5 for the failed RET NZ and 4 for the JP (HL)?) Oct 18, 2021 at 15:30
  • 1
    @puppydrum64: Is a failed NZ five rather than four? I didn't have my chart handy, so I was quoting cycle counts from my imperfect memory. Still a cycle faster than using a normal JP, at least if the loop executes enough times to make it worthwhile. BTW, when copying a chunk of memory, using a four LDI instructions and DJNZ will be faster than using LDIR. That latter instruction would have performed well as designed if the Z80 had an 8-bit ALU, but checking if BC is zero takes two cycles longer than would just checking B, and it wastes another five cycles subtracting two from the PC.
    – supercat
    Oct 18, 2021 at 16:04
  • 3
    I've sometimes thought it would be interesting to have a contest for people to design machines with architectures similar to the 6502, Z80, or CDP1802, with similar space and internal-timing constraints, but no need for binary compatibility, to see how the architectures might have compared were it not for the need to get both of them to market quickly. All three processors do well for some kinds of operations, but end up being really clunky for some others, in ways that a few small tweaks could really improve without adding much circuitry (or in some cases, removing circuitry).
    – supercat
    Oct 18, 2021 at 16:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .