Once you understand condition codes, the Branch Group is pretty straightforward. There are 2 types of branches, jumps (JMP) and calls (CALL). JMP simply sets the PC to the target address of the jump. CALL is for subroutines, it puts the return address on the stack, then sets the PC to the target address. RET returns from a CALL by getting the address off the stack and putting it in the PC.
JMP and CALL both only jump to absolute addresses which are encoded in the bytes after the opcode.
The JMP instruction unconditionally branches to the target address. There are also conditional jump instructions for all the condition codes (except AC):
JNZ and JZ for Zero
JNC and JC for Carry
JPO and JPE for Parity
JP (plus) and JM (minus) for Sign
Here are a few of them implemented:
case 0xc2: //JNZ address if (0 == state->cc.z) state->pc = (opcode[2] << 8) | opcode[1]; else // branch not taken state->pc += 2; break; case 0xc3: //JMP address state->pc = (opcode[2] << 8) | opcode[1]; break;
CALL will push the address of the instruction after the call onto the stack, then jumps to the target address. RET gets an address off the stack and stores it to the PC. There are conditional versions of CALL and RET for all conditions.
CZ, CNZ, RZ, RNZ for Zero
CNC, CC, RNC, RC for Carry
CPO, CPE, RPO, RPE for Parity
CP, CM, RP, RM for Sign
case 0xcd: //CALL address { uint16_t ret = state->pc+2; state->memory[state->sp-1] = (ret >> 8) & 0xff; state->memory[state->sp-2] = (ret & 0xff); state->sp = state->sp - 2; state->pc = (opcode[2] << 8) | opcode[1]; } break; case 0xc9: //RET state->pc = state->memory[state->sp] | (state->memory[state->sp+1] << 8); state->sp += 2; break;
The PCHL instruction will do a unconditional jump to the address in the HL register pair.
The previously-discussed RST is included in this group. It pushes the return address on the stack then jumps to a predetermined low-memory address.
Post questions or comments on Twitter @realemulator101, or if you find issues in the code, file them on the github repository.