gameboy wormboy manboy gameworm gameman wormgame mangame manworm

Dependencies:   mbed SDFileSystem2

cpu.cpp

Committer:
dicarloj
Date:
2019-01-13
Revision:
17:c9afe1a7b423

File content as of revision 17:c9afe1a7b423:

// Implementation of OPCODES and cpu step function

#define NDEBUG
#include <assert.h>

#include "cpu.h"
#include "mem.h"

// cpu registers, halted state, and timers
CpuState globalState;

typedef void OpcodeHandler(u8 opcode);

// the GB has several invalid opcodes
void invHandler(u8 opcode) {
  printf("got invalid opcode 0x%x\n", opcode);
  assert(false);
}

////////////////////////////////
// CB Opcode Helper Functions //
////////////////////////////////

u8 rlcReg(u8 value, bool isA) {
  u8 result = value;
  clearAllFlags();
  if((result & 0x80) != 0) {
    setCarryFlag();
    result <<= 1;
    result |= 0x1;
  } else {
    result <<= 1;
  }
  if(!isA) {
    if((u8)result == 0) {
      setZeroFlag();
    }
  }
  return result;
}

u8 rlReg(u8 value, bool isA) {
  u8 carry = getCarryFlag() ? (u8)1 : (u8)0;
  u8 result = value;
  clearAllFlags();
  if((result & 0x80) != 0) {
    setCarryFlag();
  }
  result <<= 1;
  result |= carry;
  if(!isA) {
    if((u8)result == 0) {
      setZeroFlag();
    }
  }
  return result;
}

u8 rrc(u8 value, bool isA) {
  u8 result = value;
  clearAllFlags();
  if((result & 1) != 0) {
    setCarryFlag();
    result >>= 1;
    result |= 0x80;
  } else {
    result >>= 1;
  }
  if(!isA) {
    if((u8)result == 0) {
      setZeroFlag();
    }
  }
  return result;
}

u8 rr(u8 value, bool isA) {
  u8 carry = getCarryFlag() ? (u8)0x80 : (u8)0x00;
  u8 result = value;
  clearAllFlags();
  if((result & 1) != 0) {
    setCarryFlag();
  }
  result >>= 1;
  result |= carry;
  if(!isA) {
    if((u8)result == 0) {
      setZeroFlag();
    }
  }
  return result;
}

u8 srl(u8 value) {
  u8 result = value;
  clearAllFlags();
  if(result & 1) {
    setCarryFlag();
  }
  result >>= 1;
  if(result == 0) {
    setZeroFlag();
  }
  return result;
}

u8 sla(u8 value) {
  clearAllFlags();
  if(value & 0x80) {
    setCarryFlag();
  }
  u8 result = value << 1;
  if(result == 0) {
    setZeroFlag();
  }
  return result;
}

u8 sra(u8 value) {
  u8 result = value;
  clearAllFlags();
  if(result & 1) {
    setCarryFlag();
  }
  if((result & 0x80)) {
    result >>= 1;
    result |= 0x80;
  } else {
    result >>= 1;
  }
  if(result == 0) {
    setZeroFlag();
  }
  return result;
}

u8 swapRegister(u8 value) {
  u8 low = value & 0xf;
  u8 hi = (value >> 4) & 0xf;
  u8 result = (low << 4) + hi;
  clearAllFlags();
  if((u8)result == 0) {
    setZeroFlag();
  }
  return result;
}


////////////////////////////////
// CB Opcodes                 //
////////////////////////////////

void SLA_A(u8 opcode) { // 0x27
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = sla(globalState.a);
}

void SLA_B(u8 opcode) { // 0x20
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = sla(globalState.bc.hi);
}

void SLA_C(u8 opcode) { // 0x21
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = sla(globalState.bc.lo);
}

void SLA_D(u8 opcode) { // 0x22
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = sla(globalState.de.hi);
}

void SLA_E(u8 opcode) { // 0x23
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = sla(globalState.de.lo);
}

void SLA_H(u8 opcode) { // 0x24
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = sla(globalState.hl.hi);
}

void SLA_L(u8 opcode) { // 0x25
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = sla(globalState.hl.lo);
}

void SLA_DHL(u8 opcode) { // 0x26
  globalState.pc++;
  globalState.cycleCount += 16;
  writeByte(sla(readByte(globalState.hl.v)), globalState.hl.v);
}

void SRA_A(u8 opcode) { // 0x2f
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = sra(globalState.a);
}

void SRA_B(u8 opcode) { // 0x28
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = sra(globalState.bc.hi);
}

void SRA_C(u8 opcode) { // 0x29
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = sra(globalState.bc.lo);
}

void SRA_D(u8 opcode) { // 0x2a
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = sra(globalState.de.hi);
}

void SRA_E(u8 opcode) { // 0x2b
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = sra(globalState.de.lo);
}

void SRA_H(u8 opcode) { // 0x2c
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = sra(globalState.hl.hi);
}

void SRA_L(u8 opcode) { // 0x2d
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = sra(globalState.hl.lo);
}

void SRA_DHL(u8 opcode) { // 0x2e
  globalState.pc++;
  globalState.cycleCount += 16;
  writeByte(sra(readByte(globalState.hl.v)), globalState.hl.v);
}


void SRL_A(u8 opcode) { // 0x3f
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = srl(globalState.a);
}

void SRL_B(u8 opcode) { // 0x38
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = srl(globalState.bc.hi);
}

void SRL_C(u8 opcode) { // 0x39
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = srl(globalState.bc.lo);
}

void SRL_D(u8 opcode) { // 0x3a
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = srl(globalState.de.hi);
}

void SRL_E(u8 opcode) { // 0x3b
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = srl(globalState.de.lo);
}

void SRL_H(u8 opcode) { // 0x3c
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = srl(globalState.hl.hi);
}

void SRL_L(u8 opcode) { // 0x3d
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = srl(globalState.hl.lo);
}

void SRL_DHL(u8 opcode) { // 0x3e
  globalState.pc++;
  globalState.cycleCount += 16;
  writeByte(srl(readByte(globalState.hl.v)), globalState.hl.v);
}

void RR_A(u8 opcode) { // 0x1f
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = rr(globalState.a, false);
}

void RR_B(u8 opcode) { // 0x18
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = rr(globalState.bc.hi, false);
}

void RR_C(u8 opcode) { // 0x19
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = rr(globalState.bc.lo, false);
}

void RR_D(u8 opcode) { // 0x1a
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = rr(globalState.de.hi, false);
}

void RR_E(u8 opcode) { // 0x1b
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = rr(globalState.de.lo, false);
}

void RR_H(u8 opcode) { // 0x1c
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = rr(globalState.hl.hi, false);
}

void RR_L(u8 opcode) { // 0x1d
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = rr(globalState.hl.lo, false);
}

void RR_DHL(u8 opcode) { // 0x1e
  globalState.pc++;
  globalState.cycleCount += 16;
  writeByte(rr(readByte(globalState.hl.v), false), globalState.hl.v);
}

void RL_A(u8 opcode) { // 0x17
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = rlReg(globalState.a, false);
}

void RL_B(u8 opcode) { // 0x10
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = rlReg(globalState.bc.hi, false);
}

void RL_C(u8 opcode) { // 0x11
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = rlReg(globalState.bc.lo, false);
}

void RL_D(u8 opcode) { // 0x12
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = rlReg(globalState.de.hi, false);
}

void RL_E(u8 opcode) { // 0x13
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = rlReg(globalState.de.lo, false);
}

void RL_H(u8 opcode) { // 0x14
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = rlReg(globalState.hl.hi, false);
}

void RL_L(u8 opcode) { // 0x15
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = rlReg(globalState.hl.lo, false);
}

void RL_DHL(u8 opcode) { // 0x16
  globalState.pc++;
  globalState.cycleCount += 16;
  writeByte(rlReg(readByte(globalState.hl.v), false), globalState.hl.v);
}

void SWAP_A(u8 opcode) { // 37
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = swapRegister(globalState.a);
}

void SWAP_B(u8 opcode) { // 30
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = swapRegister(globalState.bc.hi);
}

void SWAP_C(u8 opcode) { // 31
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = swapRegister(globalState.bc.lo);
}

void SWAP_D(u8 opcode) { // 32
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = swapRegister(globalState.de.hi);
}

void SWAP_E(u8 opcode) { // 33
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = swapRegister(globalState.de.lo);
}

void SWAP_H(u8 opcode) { // 34
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = swapRegister(globalState.hl.hi);
}

void SWAP_L(u8 opcode) { // 35
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = swapRegister(globalState.hl.lo);
}

void SWAP_DHL(u8 opcode) { // 36
  globalState.pc++;
  globalState.cycleCount += 16;
  writeByte(swapRegister(readByte(globalState.hl.v)), globalState.hl.v);
}

void RLC_A(u8 opcode) { // 07
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = rlcReg(globalState.a, false);
}

void RLC_B(u8 opcode) { // 00
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = rlcReg(globalState.bc.hi, false);
}

void RLC_C(u8 opcode) { // 01
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = rlcReg(globalState.bc.lo, false);
}

void RLC_D(u8 opcode) { // 02
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = rlcReg(globalState.de.hi, false);
}

void RLC_E(u8 opcode) { // 03
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = rlcReg(globalState.de.lo, false);
}

void RLC_H(u8 opcode) { // 04
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = rlcReg(globalState.hl.hi, false);
}

void RLC_L(u8 opcode) { // 05
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = rlcReg(globalState.hl.lo, false);
}

void RLC_DHL(u8 opcode) { // 06
  globalState.pc++;
  globalState.cycleCount += 16;
  u8 result = rlcReg(readByte(globalState.hl.v), false);
  writeByte(result, globalState.hl.v);
}


void RRC_A(u8 opcode) { // 0f
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = rrc(globalState.a, false);
}

void RRC_B(u8 opcode) { // 08
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = rrc(globalState.bc.hi, false);
}

void RRC_C(u8 opcode) { // 09
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = rrc(globalState.bc.lo, false);
}

void RRC_D(u8 opcode) { // 0a
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = rrc(globalState.de.hi, false);
}

void RRC_E(u8 opcode) { // 0b
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = rrc(globalState.de.lo, false);
}

void RRC_H(u8 opcode) { // 0c
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = rrc(globalState.hl.hi, false);
}

void RRC_L(u8 opcode) { // 0d
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = rrc(globalState.hl.lo, false);
}

void RRC_DHL(u8 opcode) { // 0e
  globalState.pc++;
  globalState.cycleCount += 16;
  u8 result = rrc(readByte(globalState.hl.v), false);
  writeByte(result, globalState.hl.v);
}


void bit_B_set(u8 opcode) {
  u8 bitID = (opcode - (u8)0xC0) >> 3;
  assert(bitID < 8);
  globalState.bc.hi |= (1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_C_set(u8 opcode) {
  u8 bitID = (opcode - (u8)0xC1) >> 3;
  assert(bitID < 8);
  globalState.bc.lo |= (1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_D_set(u8 opcode) {
  u8 bitID = (opcode - (u8)0xC2) >> 3;
  assert(bitID < 8);
  globalState.de.hi |= (1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_E_set(u8 opcode) {
  u8 bitID = (opcode - (u8)0xC3) >> 3;
  assert(bitID < 8);
  globalState.de.lo |= (1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_H_set(u8 opcode) {
  u8 bitID = (opcode - (u8)0xC4) >> 3;
  assert(bitID < 8);
  globalState.hl.hi |= (1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_L_set(u8 opcode) {
  u8 bitID = (opcode - (u8)0xC5) >> 3;
  assert(bitID < 8);
  globalState.hl.lo |= (1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_DHL_set(u8 opcode) {
  u8 bitID = (opcode - (u8)0xC6) >> 3;
  assert(bitID < 8);
  u8 value = readByte(globalState.hl.v);
  value |= (1 << bitID);
  writeByte(value, globalState.hl.v);
  globalState.pc += 1;
  globalState.cycleCount += 16;
}

void bit_A_set(u8 opcode) {
  u8 bitID = (opcode - (u8)0xC7) >> 3;
  assert(bitID < 8);
  globalState.a |= (1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_B_res(u8 opcode) {
  u8 bitID = (opcode - (u8)0x80) >> 3;
  assert(bitID < 8);
  globalState.bc.hi &= ~(1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_C_res(u8 opcode) {
  u8 bitID = (opcode - (u8)0x81) >> 3;
  assert(bitID < 8);
  globalState.bc.lo &= ~(1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_D_res(u8 opcode) {
  u8 bitID = (opcode - (u8)0x82) >> 3;
  assert(bitID < 8);
  globalState.de.hi &= ~(1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_E_res(u8 opcode) {
  u8 bitID = (opcode - (u8)0x83) >> 3;
  assert(bitID < 8);
  globalState.de.lo &= ~(1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_H_res(u8 opcode) {
  u8 bitID = (opcode - (u8)0x84) >> 3;
  assert(bitID < 8);
  globalState.hl.hi &= ~(1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_L_res(u8 opcode) {
  u8 bitID = (opcode - (u8)0x85) >> 3;
  assert(bitID < 8);
  globalState.hl.lo &= ~(1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_DHL_res(u8 opcode) {
  u8 bitID = (opcode - (u8)0x86) >> 3;
  assert(bitID < 8);
  u8 value = readByte(globalState.hl.v);
  value &= ~(1 << bitID);
  writeByte(value, globalState.hl.v);
  globalState.pc += 1;
  globalState.cycleCount += 16;
}

void bit_A_res(u8 opcode) {
  u8 bitID = (opcode - (u8)0x87) >> 3;
  assert(bitID < 8);
  globalState.a &= ~(1 << bitID);
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_A_test(u8 opcode) {
  u8 bitID = (opcode - (u8)0x47) >> 3;
  assert(bitID < 8);
  //printf("check bit %d of A\n", bitID);
  u8 val = globalState.a;
  if(((val >> bitID) & 1) == 0) {
    setZeroFlag();
  } else {
    clearZeroFlag();
  }
  setHalfCarryFlag();
  clearSubtractFlag();
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_B_test(u8 opcode) {
  u8 bitID = (opcode - (u8)0x40) >> 3;
  //printf("B opcode 0x%x bitId %d\n", opcode, bitID);
  assert(bitID < 8);
  //printf("check bit %d of B\n", bitID);
  u8 val = globalState.bc.hi;
  if(((val >> bitID) & 1) == 0) {
    setZeroFlag();
  } else {
    clearZeroFlag();
  }
  setHalfCarryFlag();
  clearSubtractFlag();
  globalState.pc += 1;
  globalState.cycleCount += 8;
}


void bit_C_test(u8 opcode) {
  u8 bitID = (opcode - (u8)0x41) >> 3;
  assert(bitID < 8);
  //printf("check bit %d of C\n", bitID);
  u8 val = globalState.bc.lo;
  if(((val >> bitID) & 1) == 0) {
    setZeroFlag();
  } else {
    clearZeroFlag();
  }
  setHalfCarryFlag();
  clearSubtractFlag();
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_D_test(u8 opcode) {
  u8 bitID = (opcode - (u8)0x42) >> 3;
  assert(bitID < 8);
  //printf("check bit %d of D\n", bitID);
  u8 val = globalState.de.hi;
  if(((val >> bitID) & 1) == 0) {
    setZeroFlag();
  } else {
    clearZeroFlag();
  }
  setHalfCarryFlag();
  clearSubtractFlag();
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_E_test(u8 opcode) {
  u8 bitID = (opcode - (u8)0x43) >> 3;
  assert(bitID < 8);
  //printf("check bit %d of E\n", bitID);
  u8 val = globalState.de.lo;
  if(((val >> bitID) & 1) == 0) {
    setZeroFlag();
  } else {
    clearZeroFlag();
  }
  setHalfCarryFlag();
  clearSubtractFlag();
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_H_test(u8 opcode) {
  u8 bitID = (opcode - (u8)0x44) >> 3;
  assert(bitID < 8);
  //printf("check bit %d of H\n", bitID);
  u8 val = globalState.hl.hi;
  if(((val >> bitID) & 1) == 0) {
    setZeroFlag();
  } else {
    clearZeroFlag();
  }
  setHalfCarryFlag();
  clearSubtractFlag();
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_L_test(u8 opcode) {
  u8 bitID = (opcode - (u8)0x45) >> 3;
  assert(bitID < 8);
  //printf("check bit %d of L\n", bitID);
  u8 val = globalState.hl.lo;
  if(((val >> bitID) & 1) == 0) {
    setZeroFlag();
  } else {
    clearZeroFlag();
  }
  setHalfCarryFlag();
  clearSubtractFlag();
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

void bit_DHL_test(u8 opcode) {
  u8 bitID = (opcode - (u8)0x45) >> 3;
  assert(bitID < 8);
  //printf("check bit %d of L\n", bitID);
  u8 val = readByte(globalState.hl.v);
  if(((val >> bitID) & 1) == 0) {
    setZeroFlag();
  } else {
    clearZeroFlag();
  }
  setHalfCarryFlag();
  clearSubtractFlag();
  globalState.pc += 1;
  globalState.cycleCount += 16;
}

// CB-prefixed opcode handler table
static OpcodeHandler* opcode_cbs[256] =
        {RLC_B,          RLC_C,          RLC_D,          RLC_E,          RLC_H,          RLC_L,          RLC_DHL,        RLC_A,          // 0x0 - 0x7
         RRC_B,          RRC_C,          RRC_D,          RRC_E,          RRC_H,          RRC_L,          RRC_DHL,        RRC_A,          // 0x8 - 0xf
         RL_B,           RL_C,           RL_D,           RL_E,           RL_H,           RL_L,           RL_DHL,         RL_A,           // 0x10 - 0x17
         RR_B,           RR_C,           RR_D,           RR_E,           RR_H,           RR_L,           RR_DHL,         RR_A,           // 0x18 - 0x1f
         SLA_B,          SLA_C,          SLA_D,          SLA_E,          SLA_H,          SLA_L,          SLA_DHL,        SLA_A,          // 0x20 - 0x27
         SRA_B,          SRA_C,          SRA_D,          SRA_E,          SRA_H,          SRA_L,          SRA_DHL,        SRA_A,          // 0x28 - 0x2f
         SWAP_B,         SWAP_C,         SWAP_D,         SWAP_E,         SWAP_H,         SWAP_L,         SWAP_DHL,       SWAP_A,         // 0x30 - 0x37
         SRL_B,          SRL_C,          SRL_D,          SRL_E,          SRL_H,          SRL_L,          SRL_DHL,        SRL_A,          // 0x38 - 0x3f
         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x40 - 0x47
         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x48 - 0x4f
         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x50 - 0x57
         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x58 - 0x5f
         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x60 - 0x67
         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x68 - 0x6f
         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x70 - 0x77
         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x78 - 0x7f
         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0x80 - 0x8
         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0x88 - 0x8f
         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0x90 - 0x97
         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0x98 - 0x9f
         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0xa0 - 0xa7
         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0xa8 - 0xaf
         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0xb0 - 0xb7
         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0xb8 - 0xbf
         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xc0 - 0xc7
         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xc8 - 0xcf
         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xd0 - 0xd7
         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xd8 - 0xdf
         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xe0 - 0xe7
         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xe8 - 0xef
         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xf0 - 0xf7
         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     }; // 0xf8 - 0xff


////////////////////////////////
// Opcodes                    //
////////////////////////////////
void LD_B_n(u8 opocde) { // 06
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.bc.hi = imm;
  globalState.cycleCount += 8;
}

void LD_C_n(u8 opocde) { // 0E
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.bc.lo = imm;
  globalState.cycleCount += 8;
}

void LD_D_n(u8 opocde) { // 16
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.de.hi = imm;
  globalState.cycleCount += 8;
}

void LD_E_n(u8 opocde) { // 1e
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.de.lo = imm;
  globalState.cycleCount += 8;
}

void LD_H_n(u8 opocde) { // 26
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.hl.hi = imm;
  globalState.cycleCount += 8;
}

void LD_L_n(u8 opocde) { // 2e
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.hl.lo = imm;
  globalState.cycleCount += 8;
}

// REGISTER-REGISTER LOADS (REGISTER A)
void LD_A_A(u8 opcode) { // 0x7f
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = globalState.a;
}

void LD_A_B(u8 opcode) { // 0x78
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = globalState.bc.hi;
}

void LD_A_C(u8 opcode) { // 0x79
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = globalState.bc.lo;
}

void LD_A_D(u8 opcode) { // 0x7a
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = globalState.de.hi;
}

void LD_A_E(u8 opcode) { // 0x7b
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = globalState.de.lo;
}

void LD_A_H(u8 opcode) { // 0x7c
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = globalState.hl.hi;
}

void LD_A_L(u8 opcode) { // 0x7d
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = globalState.hl.lo;
}

void LD_A_DHL(u8 opcode) { // 0x7e
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = readByte(globalState.hl.v);
}



// REGISTER-REGISTER LOADS (REGISTER B)
void LD_B_B(u8 opcode) { // 0x40
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.hi = globalState.bc.hi;
}

void LD_B_C(u8 opcode) { // 0x41
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.hi = globalState.bc.lo;
}

void LD_B_D(u8 opcode) { // 0x42
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.hi = globalState.de.hi;
}

void LD_B_E(u8 opcode) { // 0x43
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.hi = globalState.de.lo;
}

void LD_B_H(u8 opcode) { // 0x44
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.hi = globalState.hl.hi;
}

void LD_B_L(u8 opcode) { // 0x45
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.hi = globalState.hl.lo;
}

void LD_B_DHL(u8 opcode) { // 0x46
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.hi = readByte(globalState.hl.v);
}



// REGISTER-REGISTER LOADS (REGISTER C)
void LD_C_B(u8 opcode) { // 0x48
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.lo = globalState.bc.hi;
}

void LD_C_C(u8 opcode) { // 0x49
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.lo = globalState.bc.lo;
}

void LD_C_D(u8 opcode) { // 0x4a
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.lo = globalState.de.hi;
}

void LD_C_E(u8 opcode) { // 0x4b
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.lo = globalState.de.lo;
}

void LD_C_H(u8 opcode) { // 0x4c
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.lo = globalState.hl.hi;
}

void LD_C_L(u8 opcode) { // 0x4d
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.lo = globalState.hl.lo;
}

void LD_C_DHL(u8 opcode) { // 0x4e
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.lo = readByte(globalState.hl.v);
}



// REGISTER-REGISTER LOADS (REGISTER D)
void LD_D_B(u8 opcode) { // 0x50
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.hi = globalState.bc.hi;
}

void LD_D_C(u8 opcode) { // 0x51
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.hi = globalState.bc.lo;
}

void LD_D_D(u8 opcode) { // 0x52
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.hi = globalState.de.hi;
}

void LD_D_E(u8 opcode) { // 0x53
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.hi = globalState.de.lo;
}

void LD_D_H(u8 opcode) { // 0x54
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.hi = globalState.hl.hi;
}

void LD_D_L(u8 opcode) { // 0x55
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.hi = globalState.hl.lo;
}

void LD_D_DHL(u8 opcode) { // 0x56
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.hi = readByte(globalState.hl.v);
}


// REGISTER-REGISTER LOADS (REGISTER E)
void LD_E_B(u8 opcode) { // 0x58
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.lo = globalState.bc.hi;
}

void LD_E_C(u8 opcode) { // 0x59
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.lo = globalState.bc.lo;
}

void LD_E_D(u8 opcode) { // 0x5a
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.lo = globalState.de.hi;
}

void LD_E_E(u8 opcode) { // 0x5b
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.lo = globalState.de.lo;
}

void LD_E_H(u8 opcode) { // 0x5c
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.lo = globalState.hl.hi;
}

void LD_E_L(u8 opcode) { // 0x5d
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.lo = globalState.hl.lo;
}

void LD_E_DHL(u8 opcode) { // 0x5e
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.lo = readByte(globalState.hl.v);
}

// REGISTER-REGISTER LOADS (REGISTER H)
void LD_H_B(u8 opcode) { // 0x60
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.hi = globalState.bc.hi;
}

void LD_H_C(u8 opcode) { // 0x61
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.hi = globalState.bc.lo;
}

void LD_H_D(u8 opcode) { // 0x62
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.hi = globalState.de.hi;
}

void LD_H_E(u8 opcode) { // 0x63
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.hi = globalState.de.lo;
}

void LD_H_H(u8 opcode) { // 0x64
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.hi = globalState.hl.hi;
}

void LD_H_L(u8 opcode) { // 0x65
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.hi = globalState.hl.lo;
}

void LD_H_DHL(u8 opcode) { // 0x66
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.hi = readByte(globalState.hl.v);
}

// REGISTER-REGISTER LOADS (REGISTER L)
void LD_L_B(u8 opcode) { // 0x60
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.lo = globalState.bc.hi;
}

void LD_L_C(u8 opcode) { // 0x61
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.lo = globalState.bc.lo;
}

void LD_L_D(u8 opcode) { // 0x62
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.lo = globalState.de.hi;
}

void LD_L_E(u8 opcode) { // 0x63
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.lo = globalState.de.lo;
}

void LD_L_H(u8 opcode) { // 0x64
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.lo = globalState.hl.hi;
}

void LD_L_L(u8 opcode) { // 0x65
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.lo = globalState.hl.lo;
}

void LD_L_DHL(u8 opcode) { // 0x66
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.lo = readByte(globalState.hl.v);
}

// REGISTER-REGISTER LOADS (REGISTER (HL))
void LD_DHL_B(u8 opcode) { // 0x70
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.bc.hi, globalState.hl.v);
}

void LD_DHL_C(u8 opcode) { // 0x71
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.bc.lo, globalState.hl.v);
}

void LD_DHL_D(u8 opcode) { // 0x72
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.de.hi, globalState.hl.v);
}

void LD_DHL_E(u8 opcode) { // 0x73
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.de.lo, globalState.hl.v);
}

void LD_DHL_H(u8 opcode) { // 0x74
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.hl.hi, globalState.hl.v);
}

void LD_DHL_L(u8 opcode) { // 0x75
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.hl.lo, globalState.hl.v);
}

// Random
void LD_DHL_n(u8 opcode) { // 0x36
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 12;
  writeByte(imm, globalState.hl.v);
}

// Load Into Register A Specials
void LD_A_DBC(u8 opcode) { // 0A
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = readByte(globalState.bc.v);
}

void LD_A_DDE(u8 opcode) { // 1A
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = readByte(globalState.de.v);
}


void LD_A_Dnn(u8 opcode) { // FA
  globalState.pc++;
  globalState.cycleCount += 16;
  globalState.a = readByte(readU16(globalState.pc));
  globalState.pc += 2;
}

void LD_A_n(u8 opocde) { // 0x3e
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.a = imm;
  globalState.cycleCount += 8;
}

// LOAD FROM REGISTER A
void LD_B_A(u8 opcode) { // 0x47
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.hi = globalState.a;
}

void LD_C_A(u8 opcode) { // 0x4f
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.bc.lo = globalState.a;
}

void LD_D_A(u8 opcode) { // 0x57
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.hi = globalState.a;
}

void LD_E_A(u8 opcode) { // 0x5f
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.de.lo = globalState.a;
}

void LD_H_A(u8 opcode) { // 0x67
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.hi = globalState.a;
}

void LD_L_A(u8 opcode) { // 0x6f
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.hl.lo = globalState.a;
}

void LD_DBC_A(u8 opcode) { // 0x02
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.a, globalState.bc.v);
}

void LD_DDE_A(u8 opcode) { // 0x12
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.a, globalState.de.v);
}

void LD_DHL_A(u8 opcode) { // 0x77
  globalState.pc++;
  globalState.cycleCount += 8;
  writeByte(globalState.a, globalState.hl.v);
}

void LD_Dnn_A(u8 opcode) { // 0xea
  globalState.pc++;
  globalState.cycleCount += 16;
  writeByte(globalState.a, readU16(globalState.pc));
  globalState.pc += 2;
}

// Load A with memory offset from 0xff00
void LD_A_FF00_C(u8 opcode) { //0xf2
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = readByte((u16)0xff00 + globalState.bc.lo);
}

// Store A with memory offset from 0xff00
void LD_FF00_C_A(u8 opcode) { // 0xe2
  globalState.pc++;
  writeByte(globalState.a, (u16)0xff00 + globalState.bc.lo);
  globalState.cycleCount += 8;
}

// Load (HL) into A, decrement HL
void LDD_A_DHL(u8 opcode) { // 0x3a
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.a = readByte(globalState.hl.v);
  globalState.hl.v--;
}

// Load A into (HL), decrement HL
void LDD_DHL_A(u8 opcode) { // 0x32
  writeByte(globalState.a, globalState.hl.v);
  globalState.hl.v--;
  globalState.pc += 1;
  globalState.cycleCount += 8;
}

// Load (HL) into A, increment HL
void LDI_A_DHL(u8 opcode){ // 0x2a
  globalState.pc += 1;
  globalState.cycleCount += 8;
  globalState.a = readByte(globalState.hl.v);
  globalState.hl.v++;
}

// Load A into (HL), increment HL
void LDI_DHL_A(u8 opcode) { // 0x22
  writeByte(globalState.a, globalState.hl.v);
  globalState.hl.v++;
  globalState.pc += 1;
  globalState.cycleCount += 8;
}
// page 75

// Store A into FF00 + n
void LD_FF00_n_A(u8 opcode) { //0xe0
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  writeByte(globalState.a, (u16)0xff00 + imm);
  globalState.cycleCount += 12;
}

// Load A with FF00 + n
void LD_A_FF00_n(u8 opcode) { // 0xf0
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.a = readByte((u16)0xff00 + imm);
  globalState.cycleCount += 12;
}

// 16-bit loads
void LD_BC_nn(u8 opcode) { // 0x01
  u16 imm = readU16(globalState.pc + (u16)1);
  globalState.bc.v = imm;
  globalState.pc += 3;
  globalState.cycleCount += 12;
}

void LD_DE_nn(u8 opcode) { // 0x11
  u16 imm = readU16(globalState.pc + (u16)1);
  globalState.de.v = imm;
  globalState.pc += 3;
  globalState.cycleCount += 12;
}

void LD_HL_nn(u8 opcode) { // 0x21
  u16 imm = readU16(globalState.pc + (u16)1);
  globalState.hl.v = imm;
  globalState.pc += 3;
  globalState.cycleCount += 12;
}

void LD_SP_nn(u8 opcode) { // 0x31
  u16 imm = readU16(globalState.pc + (u16)1);
  globalState.sp = imm;
  globalState.pc += 3;
  globalState.cycleCount += 12;
}

void LD_SP_HL(u8 opcode) { // 0xf9
  globalState.sp = globalState.hl.v;
  globalState.pc++;
  globalState.cycleCount += 8;
}

// load effective address relative to stack pointer
void LD_HL_SP_n(u8 opcode) { //0xf8
  globalState.pc++;
  globalState.cycleCount += 12;
  s8 imm = readByte(globalState.pc);
  globalState.pc++;
  clearAllFlags();
  u16 result = globalState.sp + imm;
  if(((globalState.sp ^ imm ^ result) & 0x100) == 0x100) {
    setCarryFlag();
  }
  if(((globalState.sp ^ imm ^ result) & 0x10) == 0x10) {
    setHalfCarryFlag();
  }
  globalState.hl.v = result;
}

// store stack pointer
void LD_Dnn_SP(u8 opcode) { // 08
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc+=2;
  globalState.cycleCount += 20;
  writeU16(globalState.sp, addr);
}

// push U16 register onto stack
void PUSH_AF(u8 opcode) { // 0xf5
  globalState.pc++;
  globalState.cycleCount += 16;
  writeU16(globalState.f + (((u16)globalState.a) << 8), globalState.sp - (u16)2);
  globalState.sp -= 2;
}

void PUSH_BC(u8 opcode) { // 0xc5
  globalState.pc++;
  globalState.cycleCount += 16;
  writeU16(globalState.bc.v, globalState.sp - (u16)2);
  globalState.sp -= 2;
}

void PUSH_DE(u8 opcode) { // 0xd5
  globalState.pc++;
  globalState.cycleCount += 16;
  writeU16(globalState.de.v, globalState.sp - (u16)2);
  globalState.sp -= 2;
}

void PUSH_HL(u8 opcode) { // 0xe5
  globalState.pc++;
  globalState.cycleCount += 16;
  writeU16(globalState.hl.v, globalState.sp - (u16)2);
  globalState.sp -= 2;
}

// POP U16 register from stack
void POP_AF(u8 opcode) { // 0xf1
  globalState.pc++;
  globalState.cycleCount += 12;
  u16 v = readU16(globalState.sp);
  globalState.sp += 2;
  globalState.a = (u8)(v >> 8);
  globalState.f = (u8)(v & 0xf0);
}

void POP_BC(u8 opcode) { // 0xc1
  globalState.pc++;
  globalState.cycleCount += 12;
  u16 v = readU16(globalState.sp);
  globalState.sp += 2;
  globalState.bc.v = v;
}

void POP_DE(u8 opcode) { // 0xd1
  globalState.pc++;
  globalState.cycleCount += 12;
  u16 v = readU16(globalState.sp);
  globalState.sp += 2;
  globalState.de.v = v;
}

void POP_HL(u8 opcode) { // 0xe1
  globalState.pc++;
  globalState.cycleCount += 12;
  u16 v = readU16(globalState.sp);
  globalState.sp += 2;
  globalState.hl.v = v;
}

void addToA(u8 number) {
  int result = globalState.a + number;
  int carryBits = globalState.a ^ number ^ result;
  globalState.a = (u8)result;
  clearAllFlags();

  if((u8)result == 0){
    setZeroFlag();
  }

  if((carryBits & 0x100) != 0) {
    setCarryFlag();
  }
  if((carryBits & 0x10) != 0) {
    setHalfCarryFlag();
  }
}

// ADD Opcodes
void ADD_A(u8 opcode) { // 0x87
  globalState.pc++;
  globalState.cycleCount += 4;
  addToA(globalState.a);
}

void ADD_B(u8 opcode) { // 0x80
  globalState.pc++;
  globalState.cycleCount += 4;
  addToA(globalState.bc.hi);
}

void ADD_C(u8 opcode) { // 0x81
  globalState.pc++;
  globalState.cycleCount += 4;
  addToA(globalState.bc.lo);
}

void ADD_D(u8 opcode) { // 0x82
  globalState.pc++;
  globalState.cycleCount += 4;
  addToA(globalState.de.hi);
}

void ADD_E(u8 opcode) { // 0x83
  globalState.pc++;
  globalState.cycleCount += 4;
  addToA(globalState.de.lo);
}

void ADD_H(u8 opcode) { // 0x84
  globalState.pc++;
  globalState.cycleCount += 4;
  addToA(globalState.hl.hi);
}

void ADD_L(u8 opcode) { // 0x85
  globalState.pc++;
  globalState.cycleCount += 4;
  addToA(globalState.hl.lo);
}

void ADD_DHL(u8 opcode) { // 0x86
  globalState.pc++;
  globalState.cycleCount += 8;
  addToA(readByte(globalState.hl.v));
}

void ADD_n(u8 opcode) { // 0xC6
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 8;
  addToA(imm);
}

void adcToA(u8 number) {
  int carry = getCarryFlag() ? 1 : 0;
  int result = globalState.a + number + carry;
  clearAllFlags();
  if((u8)result == 0) {
    setZeroFlag();
  }
  if(result > 0xff) {
    setCarryFlag();
  }
  if(((globalState.a & 0xf) + (number & 0xf) + carry) > 0xf) {
    setHalfCarryFlag();
  }
  globalState.a = (u8) result;
}

// ADC Opcodes
void ADC_A(u8 opcode) { // 0x8f
  globalState.pc++;
  globalState.cycleCount += 4;
  adcToA(globalState.a);
}

void ADC_B(u8 opcode) { // 0x88
  globalState.pc++;
  globalState.cycleCount += 4;
  adcToA(globalState.bc.hi);
}

void ADC_C(u8 opcode) { // 0x89
  globalState.pc++;
  globalState.cycleCount += 4;
  adcToA(globalState.bc.lo);
}

void ADC_D(u8 opcode) { // 0x8a
  globalState.pc++;
  globalState.cycleCount += 4;
  adcToA(globalState.de.hi);
}

void ADC_E(u8 opcode) { // 0x8b
  globalState.pc++;
  globalState.cycleCount += 4;
  adcToA(globalState.de.lo);
}

void ADC_H(u8 opcode) { // 0x8c
  globalState.pc++;
  globalState.cycleCount += 4;
  adcToA(globalState.hl.hi);
}

void ADC_L(u8 opcode) { // 0x8d
  globalState.pc++;
  globalState.cycleCount += 4;
  adcToA(globalState.hl.lo);
}

void ADC_DHL(u8 opcode) { // 0x8e
  globalState.pc++;
  globalState.cycleCount += 8;
  adcToA(readByte(globalState.hl.v));
}

void ADC_n(u8 opcode) { // 0xCe
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 8;
  adcToA(imm);
}

void subToA(u8 number) {
  int result = globalState.a - number;
  int carryBits = globalState.a ^ number ^ result;
  globalState.a = (u8)result;
  clearAllFlags();
  setSubtractFlag();
  if((u8)result == 0) {
    setZeroFlag();
  }
  if((carryBits & 0x100) != 0) {
    setCarryFlag();
  }
  if((carryBits & 0x10) != 0) {
    setHalfCarryFlag();
  }
}

// SUB Opcodes
void SUB_A(u8 opcode) { // 0x97
  globalState.pc++;
  globalState.cycleCount += 4;
  subToA(globalState.a);
}

void SUB_B(u8 opcode) { // 0x90
  globalState.pc++;
  globalState.cycleCount += 4;
  subToA(globalState.bc.hi);
}

void SUB_C(u8 opcode) { // 0x91
  globalState.pc++;
  globalState.cycleCount += 4;
  subToA(globalState.bc.lo);
}

void SUB_D(u8 opcode) { // 0x92
  globalState.pc++;
  globalState.cycleCount += 4;
  subToA(globalState.de.hi);
}

void SUB_E(u8 opcode) { // 0x93
  globalState.pc++;
  globalState.cycleCount += 4;
  subToA(globalState.de.lo);
}

void SUB_H(u8 opcode) { // 0x94
  globalState.pc++;
  globalState.cycleCount += 4;
  subToA(globalState.hl.hi);
}

void SUB_L(u8 opcode) { // 0x95
  globalState.pc++;
  globalState.cycleCount += 4;
  subToA(globalState.hl.lo);
}

void SUB_DHL(u8 opcode) { // 0x96
  globalState.pc++;
  globalState.cycleCount += 8;
  subToA(readByte(globalState.hl.v));
}

void SUB_n(u8 opcode) { // 0xD6
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 8;
  subToA(imm);
}

void sbcToA(u8 number) {
  int carry = getCarryFlag() ? 1 : 0;
  int result = globalState.a - number - carry;
  clearAllFlags();
  setSubtractFlag();
  if((u8)result == 0) {
    setZeroFlag();
  }
  if(result < 0) {
    setCarryFlag();
  }
  if(((globalState.a & 0xf) - (number & 0xf) - carry) < 0) {
    setHalfCarryFlag();
  }
  globalState.a = (u8)result;
}

// SBC Opcodes
void SBC_A(u8 opcode) { // 0x9f
  globalState.pc++;
  globalState.cycleCount += 4;
  sbcToA(globalState.a);
}

void SBC_B(u8 opcode) { // 0x98
  globalState.pc++;
  globalState.cycleCount += 4;
  sbcToA(globalState.bc.hi);
}

void SBC_C(u8 opcode) { // 0x99
  globalState.pc++;
  globalState.cycleCount += 4;
  sbcToA(globalState.bc.lo);
}

void SBC_D(u8 opcode) { // 0x9a
  globalState.pc++;
  globalState.cycleCount += 4;
  sbcToA(globalState.de.hi);
}

void SBC_E(u8 opcode) { // 0x9b
  globalState.pc++;
  globalState.cycleCount += 4;
  sbcToA(globalState.de.lo);
}

void SBC_H(u8 opcode) { // 0x9c
  globalState.pc++;
  globalState.cycleCount += 4;
  sbcToA(globalState.hl.hi);
}

void SBC_L(u8 opcode) { // 0x9d
  globalState.pc++;
  globalState.cycleCount += 4;
  sbcToA(globalState.hl.lo);
}

void SBC_DHL(u8 opcode) { // 0x9e
  globalState.pc++;
  globalState.cycleCount += 8;
  sbcToA(readByte(globalState.hl.v));
}

void SBC_n(u8 opcode) { // 0xDe
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 8;
  sbcToA(imm);
}

void andtoA(u8 number) {
  u8 result = globalState.a & number;
  globalState.a = result;
  clearAllFlags();
  setHalfCarryFlag();
  if((u8)result == 0) {
    setZeroFlag();
  }
}

// AND Opcodes
void AND_A(u8 opcode) { // 0xa7
  globalState.pc++;
  globalState.cycleCount += 4;
  andtoA(globalState.a);
}

void AND_B(u8 opcode) { // 0xa0
  globalState.pc++;
  globalState.cycleCount += 4;
  andtoA(globalState.bc.hi);
}

void AND_C(u8 opcode) { // 0xa1
  globalState.pc++;
  globalState.cycleCount += 4;
  andtoA(globalState.bc.lo);
}

void AND_D(u8 opcode) { // 0xa2
  globalState.pc++;
  globalState.cycleCount += 4;
  andtoA(globalState.de.hi);
}

void AND_E(u8 opcode) { // 0xa3
  globalState.pc++;
  globalState.cycleCount += 4;
  andtoA(globalState.de.lo);
}

void AND_H(u8 opcode) { // 0xa4
  globalState.pc++;
  globalState.cycleCount += 4;
  andtoA(globalState.hl.hi);
}

void AND_L(u8 opcode) { // 0xa5
  globalState.pc++;
  globalState.cycleCount += 4;
  andtoA(globalState.hl.lo);
}

void AND_DHL(u8 opcode) { // 0xa6
  globalState.pc++;
  globalState.cycleCount += 8;
  andtoA(readByte(globalState.hl.v));
}

void AND_n(u8 opcode) { // 0xe6
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 8;
  andtoA(imm);
}

void xorToA(u8 number) {
  u8 result = globalState.a ^ number;
  globalState.a = result;
  clearAllFlags();
  if((u8)result == 0) {
    setZeroFlag();
  }
}

// XOR Opcodes
void XOR_A(u8 opcode) { // 0xaf
  globalState.pc++;
  globalState.cycleCount += 4;
  xorToA(globalState.a);
}

void XOR_B(u8 opcode) { // 0xa8
  globalState.pc++;
  globalState.cycleCount += 4;
  xorToA(globalState.bc.hi);
}

void XOR_C(u8 opcode) { // 0xa9
  globalState.pc++;
  globalState.cycleCount += 4;
  xorToA(globalState.bc.lo);
}

void XOR_D(u8 opcode) { // 0xaa
  globalState.pc++;
  globalState.cycleCount += 4;
  xorToA(globalState.de.hi);
}

void XOR_E(u8 opcode) { // 0xab
  globalState.pc++;
  globalState.cycleCount += 4;
  xorToA(globalState.de.lo);
}

void XOR_H(u8 opcode) { // 0xac
  globalState.pc++;
  globalState.cycleCount += 4;
  xorToA(globalState.hl.hi);
}

void XOR_L(u8 opcode) { // 0xad
  globalState.pc++;
  globalState.cycleCount += 4;
  xorToA(globalState.hl.lo);
}

void XOR_DHL(u8 opcode) { // 0xae
  globalState.pc++;
  globalState.cycleCount += 8;
  xorToA(readByte(globalState.hl.v));
}

void XOR_n(u8 opcode) { // 0xee
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 8;
  xorToA(imm);
}

void orToA(u8 number) {
  u8 result = globalState.a | number;
  globalState.a = result;
  clearAllFlags();
  if((u8)result == 0) {
    setZeroFlag();
  }
}

// OR Opcodes
void OR_A(u8 opcode) { // 0xb7
  globalState.pc++;
  globalState.cycleCount += 4;
  orToA(globalState.a);
}

void OR_B(u8 opcode) { // 0xb0
  globalState.pc++;
  globalState.cycleCount += 4;
  orToA(globalState.bc.hi);
}

void OR_C(u8 opcode) { // 0xb1
  globalState.pc++;
  globalState.cycleCount += 4;
  orToA(globalState.bc.lo);
}

void OR_D(u8 opcode) { // 0xb2
  globalState.pc++;
  globalState.cycleCount += 4;
  orToA(globalState.de.hi);
}

void OR_E(u8 opcode) { // 0xb3
  globalState.pc++;
  globalState.cycleCount += 4;
  orToA(globalState.de.lo);
}

void OR_H(u8 opcode) { // 0xb4
  globalState.pc++;
  globalState.cycleCount += 4;
  orToA(globalState.hl.hi);
}

void OR_L(u8 opcode) { // 0xb5
  globalState.pc++;
  globalState.cycleCount += 4;
  orToA(globalState.hl.lo);
}

void OR_DHL(u8 opcode) { // 0xb6
  globalState.pc++;
  globalState.cycleCount += 8;
  orToA(readByte(globalState.hl.v));
}

void OR_n(u8 opcode) { // 0xf6
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 8;
  orToA(imm);
}

void cpToA(u8 number) {
  clearAllFlags();
  setSubtractFlag();
  if(globalState.a < number) {
    setCarryFlag();
  }
  if(globalState.a == number) {
    setZeroFlag();
  }
  if(((globalState.a - number) & 0xf) > (globalState.a & 0xf)) {
    setHalfCarryFlag();
  }
}

// CP Opcodes
void CP_A(u8 opcode) { // 0xbf
  globalState.pc++;
  globalState.cycleCount += 4;
  cpToA(globalState.a);
}

void CP_B(u8 opcode) { // 0xb8
  globalState.pc++;
  globalState.cycleCount += 4;
  cpToA(globalState.bc.hi);
}

void CP_C(u8 opcode) { // 0xb9
  globalState.pc++;
  globalState.cycleCount += 4;
  cpToA(globalState.bc.lo);
}

void CP_D(u8 opcode) { // 0xba
  globalState.pc++;
  globalState.cycleCount += 4;
  cpToA(globalState.de.hi);
}

void CP_E(u8 opcode) { // 0xbb
  globalState.pc++;
  globalState.cycleCount += 4;
  cpToA(globalState.de.lo);
}

void CP_H(u8 opcode) { // 0xbc
  globalState.pc++;
  globalState.cycleCount += 4;
  cpToA(globalState.hl.hi);
}

void CP_L(u8 opcode) { // 0xbd
  globalState.pc++;
  globalState.cycleCount += 4;
  cpToA(globalState.hl.lo);
}

void CP_DHL(u8 opcode) { // 0xbe
  globalState.pc++;
  globalState.cycleCount += 8;
  cpToA(readByte(globalState.hl.v));
}

void CP_n(u8 opcode) { // 0xfe
  u8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 8;
  cpToA(imm);
}

void DecodeCB(u8 opcode) {
  globalState.pc += 1;
  u8 newOpcode = readByte(globalState.pc);
  opcode_cbs[newOpcode](newOpcode);
}

u8 increment(u8 in) {
  u8 result = in + (u8)1;
  if(getCarryFlag()) {
    clearAllFlags();
    setCarryFlag();
  } else {
    clearAllFlags();
  }
  if(result == 0) {
    setZeroFlag();
  }
  if((result & 0x0f) == 0x00) {
    setHalfCarryFlag();
  }
  return result;
}

void INC_A(u8 opcode) { // 0x3c
  globalState.a = increment(globalState.a);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void INC_B(u8 opcode) { // 0x04
  globalState.bc.hi = increment(globalState.bc.hi);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void INC_C(u8 opcode) { // 0x0c
  globalState.bc.lo = increment(globalState.bc.lo);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void INC_D(u8 opcode) { // 0x14
  globalState.de.hi = increment(globalState.de.hi);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void INC_E(u8 opcode) { // 0x1c
  globalState.de.lo = increment(globalState.de.lo);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void INC_H(u8 opcode) { // 0x24
  globalState.hl.hi = increment(globalState.hl.hi);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void INC_L(u8 opcode) { // 0x2c
  globalState.hl.lo = increment(globalState.hl.lo);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void INC_DHL(u8 opcode) { // 0x34
  // todo this one is hard.
  u8 v = readByte(globalState.hl.v);
  writeByte(increment(v), globalState.hl.v);
  globalState.pc++;
  globalState.cycleCount += 12;
}

u8 decrement(u8 in) {
  u8 result = in - (u8)1;
  if(getCarryFlag()) {
    clearAllFlags();
    setCarryFlag();
  } else {
    clearAllFlags();
  }
  setSubtractFlag();
  if(result == 0) {
    setZeroFlag();
  }
  if((result & 0xf) == 0xf) {
    setHalfCarryFlag();
  }
  return result;
}

void DEC_A(u8 opcode) { // 0x3d
  globalState.a = decrement(globalState.a);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void DEC_B(u8 opcode) { // 0x05
  globalState.bc.hi = decrement(globalState.bc.hi);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void DEC_C(u8 opcode) { // 0x0d
  globalState.bc.lo = decrement(globalState.bc.lo);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void DEC_D(u8 opcode) { // 0x15
  globalState.de.hi = decrement(globalState.de.hi);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void DEC_E(u8 opcode) { // 0x1d
  globalState.de.lo = decrement(globalState.de.lo);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void DEC_H(u8 opcode) { // 0x25
  globalState.hl.hi = decrement(globalState.hl.hi);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void DEC_L(u8 opcode) { // 0x2d
  globalState.hl.lo = decrement(globalState.hl.lo);
  globalState.pc++;
  globalState.cycleCount += 4;
}

void DEC_DHL(u8 opcode) { // 0x35
  // todo this one is hard.
  u8 v = readByte(globalState.hl.v);
  writeByte(decrement(v), globalState.hl.v);
  globalState.pc++;
  globalState.cycleCount += 12;
}

void addToHl(u16 number) {
  int result = globalState.hl.v + number;
  if(getZeroFlag()) {
    clearAllFlags();
    setZeroFlag();
  } else {
    clearAllFlags();
  }
  if(result & 0x10000) {
    setCarryFlag();
  }
  if((globalState.hl.v ^ number ^ (result & 0xffff)) & 0x1000) {
    setHalfCarryFlag();
  }
  globalState.hl.v = (u16)result;
}

void ADD_HL_BC(u8 opcode) { // 09
  globalState.pc++;
  globalState.cycleCount += 8;
  addToHl(globalState.bc.v);
}

void ADD_HL_DE(u8 opcode) { // 19
  globalState.pc++;
  globalState.cycleCount += 8;
  addToHl(globalState.de.v);
}

void ADD_HL_HL(u8 opcode) { // 29
  globalState.pc++;
  globalState.cycleCount += 8;
  addToHl(globalState.hl.v);
}

void ADD_HL_SP(u8 opcode) { // 39
  globalState.pc++;
  globalState.cycleCount += 8;
  addToHl(globalState.sp);
}

void ADD_SP_n(u8 opcode) { // E8
  s8 number = readByte(++globalState.pc);
  globalState.pc++;
  globalState.cycleCount += 16;
  int result = globalState.sp + number;
  clearAllFlags();
  if(((globalState.sp ^ number ^ (result & 0xffff)) & 0x100) == 0x100) {
    setCarryFlag();
  }
  if(((globalState.sp ^ number ^ (result & 0xffff)) & 0x10) == 0x10) {
    setHalfCarryFlag();
  }
  globalState.sp = (u16)result;
}

// 16 bit incs
void INC_BC(u8 opcode) { // 03
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.v++;
}

void INC_DE(u8 opcode) { // 13
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.v++;
}

void INC_HL(u8 opcode) { // 23
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.v++;
}

void INC_SP(u8 opcode) { // 33
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.sp++;
}

// 16 bit decs
void DEC_BC(u8 opcode) { // 0B
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.bc.v--;
}

void DEC_DE(u8 opcode) { // 1B
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.de.v--;
}

void DEC_HL(u8 opcode) { // 2B
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.hl.v--;
}

void DEC_SP(u8 opcode) { // 3B
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.sp--;
}

void DAA(u8 opcode) { // 0x27
  globalState.pc++;
  globalState.cycleCount += 4;
  u8 a = globalState.a;

  if(!getSubtractFlag()) {
    if(getCarryFlag() || a > 0x99) {
      a += 0x60;
      setCarryFlag();
    }
    if(getHalfCarryFlag() || (a & 0x0f) > 0x09) {
      a += 0x6;
    }
  } else {
    if(getCarryFlag()) {
      a -= 0x60;
    }
    if(getHalfCarryFlag()) {
      a -= 0x6;
    }
  }
  clearZeroFlag();
  clearHalfCarryFlag();
  if(a == 0) {
    setZeroFlag();
  }
  globalState.a = (u8)a;
}

void CPL(u8 opcode) { // 0x2f
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = ~globalState.a;
  setHalfCarryFlag();
  setSubtractFlag();
}

void CCF(u8 opcode) { // 0x3f
  globalState.pc++;
  globalState.cycleCount += 4;
  if(getCarryFlag()) {
    clearCarryFlag();
  } else {
    setCarryFlag();
  }
  clearHalfCarryFlag();
  clearSubtractFlag();
}

void SCF(u8 opcode) { // 0x37
  globalState.pc++;
  globalState.cycleCount += 4;
  clearHalfCarryFlag();
  clearSubtractFlag();
  setCarryFlag();
}

void NOP(u8 opcode) { // 00
  globalState.pc++;
  globalState.cycleCount += 4;
}


void HALT(u8 opcode) { // 76
  globalState.cycleCount += 4;
  globalState.pc++;
  //if(globalState.ime) {
    globalState.halt = true;
  //}

}

void STOP(u8 opcode) { // 10 (00)
  printf("stop not yet implemented\n");
  assert(false);
}

void DI(u8 opcode) { // F3
  // todo instruction after bs
  globalState.ime = 0;
  globalState.cycleCount += 4;
  globalState.pc++;
//  printf("di not yet implemented\n");
//  assert(false);
}

void EI(u8 opcode) { // FB
  globalState.ime = 1;
  globalState.cycleCount += 4;
  globalState.pc++;
}

void RLCA(u8 opcode) { // 07
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = rlcReg(globalState.a, true);
}

void RLA(u8 opcode) { // 17
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = rlReg(globalState.a, true);
}

void RRCA(u8 opcode) { // 0x0f
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = rrc(globalState.a, true);
}

void RRA(u8 opcode) { // 0x1f
  globalState.pc++;
  globalState.cycleCount += 4;
  globalState.a = rr(globalState.a, true);
}

void JP_nn(u8 opcodes) { // 0xc3
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  globalState.cycleCount += 12;
  globalState.pc = addr;
}

void JP_NZ_nn(u8 opcodes) { // 0xc2
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  if(!getZeroFlag()) {
    globalState.pc = addr;
  }
  globalState.cycleCount += 12;
}

void JP_Z_nn(u8 opcodes) { // 0xca
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  if(getZeroFlag()) {
    globalState.pc = addr;
  }
  globalState.cycleCount += 12;
}

void JP_NC_nn(u8 opcodes) { // 0xd2
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  if(!getCarryFlag()) {
    globalState.pc = addr;
  }
  globalState.cycleCount += 12;
}

void JP_C_nn(u8 opcodes) { // 0xda
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  if(getCarryFlag()) {
    globalState.pc = addr;
  }
  globalState.cycleCount += 12;
}

void JP_HL(u8 opcodes) { // 0xe9
  globalState.pc = globalState.hl.v;
  globalState.cycleCount += 4;
}

void JR_n(u8 opcode) { // 18
  s8 imm = readByte(++globalState.pc);
  globalState.pc++;
  globalState.pc += imm;
  globalState.cycleCount += 8;
}

void JR_NZ(u8 opcode) { // 20
  s8 imm = readByte(++globalState.pc);
  globalState.pc++;
  if(!getZeroFlag()){
    globalState.pc += imm;
  }
  globalState.cycleCount += 8;
}

void JR_Z(u8 opcode) { // 28
  s8 imm = readByte(++globalState.pc);
  globalState.pc++;
  if(getZeroFlag()){
    globalState.pc += imm;
  }
  globalState.cycleCount += 8;
}

void JR_NC(u8 opcode) {  // 30
  s8 imm = readByte(++globalState.pc);
  globalState.pc++;
  if(!getCarryFlag()){
    globalState.pc += imm;
  }
  globalState.cycleCount += 8;
}

void JR_C(u8 opcode) { // 38
  s8 imm = readByte(++globalState.pc);
  globalState.pc++;
  if(getCarryFlag()){
    globalState.pc += imm;
  }
  globalState.cycleCount += 8;
}


void CALL_nn(u8 opcode) { // 0xCD
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  //printf("call address 0x%x\n", addr);
  globalState.pc += 2;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = addr;
  globalState.cycleCount += 12;
}

void CALL_NZ(u8 opcode) { // 0xC4
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  if(!getZeroFlag()) {
    writeU16(globalState.pc, globalState.sp - (u16)2);
    globalState.sp -= 2;
    globalState.pc = addr;
  }
  globalState.cycleCount += 12;
}

void CALL_Z(u8 opcode) { // 0xCc
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  if(getZeroFlag()) {
    writeU16(globalState.pc, globalState.sp - (u16)2);
    globalState.sp -= 2;
    globalState.pc = addr;
  }
  globalState.cycleCount += 12;
}


void CALL_NC(u8 opcode) { // 0d4
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  if(!getCarryFlag()) {
    writeU16(globalState.pc, globalState.sp - (u16)2);
    globalState.sp -= 2;
    globalState.pc = addr;
  }
  globalState.cycleCount += 12;
}

void CALL_C(u8 opcode) { // 0xdc
  globalState.pc++;
  u16 addr = readU16(globalState.pc);
  globalState.pc += 2;
  if(getCarryFlag()) {
    writeU16(globalState.pc, globalState.sp - (u16)2);
    globalState.sp -= 2;
    globalState.pc = addr;
  }
  globalState.cycleCount += 12;
}

void REST_00(u8 opcode) { // 0xc7
  globalState.pc++;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = 0x0000;
  globalState.cycleCount += 32;
}

void REST_08(u8 opcode) { // 0xcf
  globalState.pc++;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = 0x0008;
  globalState.cycleCount += 32;
}

void REST_10(u8 opcode) { // 0xd7
  globalState.pc++;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = 0x0010;
  globalState.cycleCount += 32;
}

void REST_18(u8 opcode) { // 0xdf
  globalState.pc++;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = 0x0018;
  globalState.cycleCount += 32;
}

void REST_20(u8 opcode) { // 0xe7
  globalState.pc++;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = 0x0020;
  globalState.cycleCount += 32;
}

void REST_28(u8 opcode) { // 0xef
  globalState.pc++;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = 0x0028;
  globalState.cycleCount += 32;
}

void REST_30(u8 opcode) { // 0xf7
  globalState.pc++;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = 0x0030;
  globalState.cycleCount += 32;
}

void REST_38(u8 opcode) { // 0xff
  globalState.pc++;
  writeU16(globalState.pc, globalState.sp - (u16)2);
  globalState.sp -= 2;
  globalState.pc = 0x0038;
  globalState.cycleCount += 32;
}

void RET(u8 opcode) { // 0xc9
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.pc = readU16(globalState.sp);
  globalState.sp += 2;
}

void RET_NZ(u8 opcode) { // 0xc0
  globalState.pc++;
  globalState.cycleCount += 8;
  if(!getZeroFlag()) {
    globalState.pc = readU16(globalState.sp);
    globalState.sp += 2;
  }
}

void RET_Z(u8 opcode) { // 0xc8
  globalState.pc++;
  globalState.cycleCount += 8;
  if(getZeroFlag()) {
    globalState.pc = readU16(globalState.sp);
    globalState.sp += 2;
  }
}

void RET_NC(u8 opcode) { // 0xd0
  globalState.pc++;
  globalState.cycleCount += 8;
  if(!getCarryFlag()) {
    globalState.pc = readU16(globalState.sp);
    globalState.sp += 2;
  }
}

void RET_C(u8 opcode) { // 0xd8
  globalState.pc++;
  globalState.cycleCount += 8;
  if(getCarryFlag()) {
    globalState.pc = readU16(globalState.sp);
    globalState.sp += 2;
  }
}

void RETI(u8 opcode) { // 0xd9
  globalState.pc++;
  globalState.cycleCount += 8;
  globalState.pc = readU16(globalState.sp);
  globalState.sp += 2;
  globalState.ime = 1;
}

// normal opcode handler table
static OpcodeHandler* opcodes[256] =
        {NOP,          LD_BC_nn,     LD_DBC_A,     INC_BC,       INC_B,        DEC_B,        LD_B_n,       RLCA,         // 0x0 - 0x7
         LD_Dnn_SP,    ADD_HL_BC,    LD_A_DBC,     DEC_BC,       INC_C,        DEC_C,        LD_C_n,       RRCA,         // 0x8 - 0xf
         STOP,         LD_DE_nn,     LD_DDE_A,     INC_DE,       INC_D,        DEC_D,        LD_D_n,       RLA,          // 0x10 - 0x17
         JR_n,         ADD_HL_DE,    LD_A_DDE,     DEC_DE,       INC_E,        DEC_E,        LD_E_n,       RRA,          // 0x18 - 0x1f
         JR_NZ,        LD_HL_nn,     LDI_DHL_A,    INC_HL,       INC_H,        DEC_H,        LD_H_n,       DAA,          // 0x20 - 0x27
         JR_Z,         ADD_HL_HL,    LDI_A_DHL,    DEC_HL,       INC_L,        DEC_L,        LD_L_n,       CPL,          // 0x28 - 0x2f
         JR_NC,        LD_SP_nn,     LDD_DHL_A,    INC_SP,       INC_DHL,      DEC_DHL,      LD_DHL_n,     SCF,          // 0x30 - 0x37
         JR_C,         ADD_HL_SP,    LDD_A_DHL,    DEC_SP,       INC_A,        DEC_A,        LD_A_n,       CCF,          // 0x38 - 0x3f
         LD_B_B,       LD_B_C,       LD_B_D,       LD_B_E,       LD_B_H,       LD_B_L,       LD_B_DHL,     LD_B_A,       // 0x40 - 0x47
         LD_C_B,       LD_C_C,       LD_C_D,       LD_C_E,       LD_C_H,       LD_C_L,       LD_C_DHL,     LD_C_A,       // 0x48 - 0x4f
         LD_D_B,       LD_D_C,       LD_D_D,       LD_D_E,       LD_D_H,       LD_D_L,       LD_D_DHL,     LD_D_A,       // 0x50 - 0x57
         LD_E_B,       LD_E_C,       LD_E_D,       LD_E_E,       LD_E_H,       LD_E_L,       LD_E_DHL,     LD_E_A,       // 0x58 - 0x5f
         LD_H_B,       LD_H_C,       LD_H_D,       LD_H_E,       LD_H_H,       LD_H_L,       LD_H_DHL,     LD_H_A,       // 0x60 - 0x67
         LD_L_B,       LD_L_C,       LD_L_D,       LD_L_E,       LD_L_H,       LD_L_L,       LD_L_DHL,     LD_L_A,       // 0x68 - 0x6f
         LD_DHL_B,     LD_DHL_C,     LD_DHL_D,     LD_DHL_E,     LD_DHL_H,     LD_DHL_L,     HALT,         LD_DHL_A,     // 0x70 - 0x77
         LD_A_B,       LD_A_C,       LD_A_D,       LD_A_E,       LD_A_H,       LD_A_L,       LD_A_DHL,     LD_A_A      , // 0x78 - 0x7f
         ADD_B,        ADD_C,        ADD_D,        ADD_E,        ADD_H,        ADD_L,        ADD_DHL,      ADD_A,        // 0x80 - 0x87
         ADC_B,        ADC_C,        ADC_D,        ADC_E,        ADC_H,        ADC_L,        ADC_DHL,      ADC_A,        // 0x88 - 0x8f
         SUB_B,        SUB_C,        SUB_D,        SUB_E,        SUB_H,        SUB_L,        SUB_DHL,      SUB_A,        // 0x90 - 0x97
         SBC_B,        SBC_C,        SBC_D,        SBC_E,        SBC_H,        SBC_L,        SBC_DHL,      SBC_A,        // 0x98 - 0x9f
         AND_B,        AND_C,        AND_D,        AND_E,        AND_H,        AND_L,        AND_DHL,      AND_A,        // 0xa0 - 0xa7
         XOR_B,        XOR_C,        XOR_D,        XOR_E,        XOR_H,        XOR_L,        XOR_DHL,      XOR_A,        // 0xa8 - 0xaf
         OR_B,         OR_C,         OR_D,         OR_E,         OR_H,         OR_L,         OR_DHL,       OR_A,         // 0xb0 - 0xb7
         CP_B,         CP_C,         CP_D,         CP_E,         CP_H,         CP_L,         CP_DHL,       CP_A,         // 0xb8 - 0xbf
         RET_NZ,       POP_BC,       JP_NZ_nn,     JP_nn,        CALL_NZ,      PUSH_BC,      ADD_n,        REST_00,      // 0xc0 - 0xc7
         RET_Z,        RET,          JP_Z_nn,      DecodeCB,     CALL_Z,       CALL_nn,      ADC_n,        REST_08,      // 0xc8 - 0xcf
         RET_NC,       POP_DE,       JP_NC_nn,     invHandler,   CALL_NC,      PUSH_DE,      SUB_n,        REST_10,      // 0xd0 - 0xd7
         RET_C,        RETI,         JP_C_nn,      invHandler,   CALL_C,       invHandler,   SBC_n,        REST_18,      // 0xd8 - 0xdf
         LD_FF00_n_A,  POP_HL,       LD_FF00_C_A,  invHandler,   invHandler,   PUSH_HL,      AND_n,        REST_20,      // 0xe0 - 0xe7
         ADD_SP_n,     JP_HL,        LD_Dnn_A,     invHandler,   invHandler,   invHandler,   XOR_n,        REST_28,      // 0xe8 - 0xef
         LD_A_FF00_n,  POP_AF,       LD_A_FF00_C,  DI,           invHandler,   PUSH_AF,      OR_n,         REST_30,      // 0xf0 - 0xf7
         LD_HL_SP_n,   LD_SP_HL,     LD_A_Dnn,     EI,           invHandler,   invHandler,   CP_n,         REST_38};     // 0xf8 - 0xff


// reset the globalState structure, including registers and timers
void resetCpu() {
  globalState.f = 0x1; // or 0x11? (
  globalState.a = 0xb0; // maybe f, a swap??
  globalState.bc.v = 0x0013;
  globalState.de.v = 0x00d8;
  globalState.hl.v = 0x014d;
  globalState.sp = 0xfffe;
  globalState.pc = 0x0;
  globalState.halt = false;
  globalState.cycleCount = 0;
  globalState.divOffset = 0;
  globalState.timSubcount = 0;
}

// set the globalState so the next call to step() will run an ISR
void interrupt(u16 addr) {
  globalState.ime = 0;                                // disable interrupts
  writeU16(globalState.pc, globalState.sp - (u16)2);  // push pc to stack
  globalState.sp -= 2;                                // push pc to stack
  globalState.cycleCount += 12;                       // timing
  globalState.pc = addr;                              // jump to ISR
}

// timer speeds: once this number of clock cycles has elapsed, the timer ticks.
static u32 timReset[4] = {(1 << 10), (1 << 4), (1 << 6), (1 << 8)};

// step the CPU 1 instruction
// returns number of clock cycles
u32 cpuStep() {
  uint64_t oldCycleCount = globalState.cycleCount;

  // update div register
  globalMemState.ioRegs[IO_DIV] = (u8)((globalState.cycleCount - globalState.divOffset) >> 8);

  // execute, if we aren't halted.
  if(!globalState.halt) {
    // fetch opcode
    u8 opcode = readByte(globalState.pc);
    // execute opcode
    opcodes[opcode](opcode);
  }

    assert(false);
  // interrupts
  if(globalState.ime && globalMemState.upperRam[0x7f] && globalMemState.ioRegs[IO_IF]) {

    // mask interrupts with the interrupt enable register at 0xffff.
    u8 interrupts = globalMemState.upperRam[0x7f] & globalMemState.ioRegs[IO_IF];

    if(interrupts & 0x01) {
      globalMemState.ioRegs[IO_IF] &= ~1;
      interrupt(VBLANK_INTERRUPT);
      globalState.halt = false;
    } else if(interrupts & 0x02) {
      globalMemState.ioRegs[IO_IF] &= ~2;
      interrupt(LCDC_INTERRUPT);
      globalState.halt = false;
    } else if(interrupts & 0x04) {
      globalMemState.ioRegs[IO_IF] &= ~4;
      interrupt(TIMER_INTERRUPT);
      globalState.halt = false;
    } else if(interrupts & 0x08) {
      globalMemState.ioRegs[IO_IF] &= ~8;
      interrupt(SERIAL_INTERRUPT);
      globalState.halt = false;
    } else if(interrupts & 0x10) {
      globalMemState.ioRegs[IO_IF] &= ~0x10;
      interrupt(HIGH_TO_LOW_P10_P13);
      globalState.halt = false;
    }
  }

  // even if we have IME off, and we're halted, we're supposed to check IF and IE register
  // this won't fire an interrupt, but will get us out of halt
  // this behavior isn't well documented, but was required to pass cpu_instr.gb
  // (though none of the games seem to need it...)
  if(globalState.halt) {
    globalState.cycleCount += 800; // just to keep ticking the timer...
    u8 interrupts = globalMemState.upperRam[0x7f] & globalMemState.ioRegs[IO_IF];
    if(interrupts & 0x01) {
      globalState.halt = false;
    } else if(interrupts & 0x02) {
      globalState.halt = false;
    } else if(interrupts & 0x04) {
      globalState.halt = false;
    } else if(interrupts & 0x08) {
      globalState.halt = false;
    } else if(interrupts & 0x10) {
      globalState.halt = false;
    }
  }


  // cycle count
  uint64_t cyclesThisIteration = globalState.cycleCount - oldCycleCount;

  // update timer
  u8 tac = globalMemState.ioRegs[IO_TAC];
  bool ten = ((tac >> 2) & 1) != 0; // timer enable?
  if(ten) {
    u8 tclk = (tac & (u8)3);     // timer speed
    globalState.timSubcount += cyclesThisIteration;
    if(globalState.timSubcount >= timReset[tclk]) { // timer tick
      globalState.timSubcount = 0;
      u8 timv = globalMemState.ioRegs[IO_TIMA]; // check for overflow
      if(timv == 255) {
        globalMemState.ioRegs[IO_IF] |= 4; // set interrupt
        globalMemState.ioRegs[IO_TIMA] = globalMemState.ioRegs[IO_TMA]; // reset
      } else {
        globalMemState.ioRegs[IO_TIMA] = timv + (u8)1; // increment.
      }
    }
  }

  return (u32)cyclesThisIteration;
}

bool getZeroFlag() {
  return (globalState.f & 0x80) != 0;
}

bool getSubtractFlag() {
  return (globalState.f & 0x40) != 0;
}

bool getHalfCarryFlag() {
  return (globalState.f & 0x20) != 0;
}

bool getCarryFlag() {
  return (globalState.f & 0x10) != 0;
}

void setZeroFlag() {
  globalState.f = globalState.f | (u8)0x80;
}

void setSubtractFlag() {
  globalState.f = globalState.f | (u8)0x40;
}

void setHalfCarryFlag() {
  globalState.f = globalState.f | (u8)0x20;
}

void setCarryFlag() {
  globalState.f = globalState.f | (u8)0x10;
}

void clearZeroFlag(){
  globalState.f = globalState.f & ~((u8)0x80);
}

void clearSubtractFlag(){
  globalState.f = globalState.f & ~((u8)0x40);
}

void clearHalfCarryFlag(){
  globalState.f = globalState.f & ~((u8)0x20);
}

void clearCarryFlag(){
  globalState.f = globalState.f & ~((u8)0x10);
}

void clearAllFlags() {
  clearZeroFlag();
  clearSubtractFlag();
  clearHalfCarryFlag();
  clearCarryFlag();
}