 
 def init_cycletable() {
   cycletable = malloc(0x1_00) as *uint8;
+  memset(cycletable as *uint8, 0, 0x1_00);
 
   *(cycletable + 0x00) = 4;
   *(cycletable + 0x01) = 12;

  *(optable + 0x04) = op_04;
  *(optable + 0x05) = op_05;
  *(optable + 0x06) = op_06;
  *(optable + 0x07) = op_07;
  *(optable + 0x08) = op_08;
  *(optable + 0x09) = op_09;
  *(optable + 0x0A) = op_0A;

  *(cycletable + 0x0D) = 4;
  *(cycletable + 0x0E) = 8;
  *(cycletable + 0x0F) = 4;

  *(cycletable + 0x20) = 8;  //  +4 IFF
  *(cycletable + 0x21) = 12;
  *(cycletable + 0x22) = 8;
  *(cycletable + 0x23) = 8;
  *(cycletable + 0x24) = 4;
  *(cycletable + 0x25) = 4;
  *(cycletable + 0x26) = 8;
  *(cycletable + 0x27) = 4;
  *(cycletable + 0x28) = 8;  //  +4 IFF
  *(cycletable + 0x29) = 8;
  *(cycletable + 0x2A) = 8;
  *(cycletable + 0x2B) = 8;
  *(cycletable + 0x2C) = 4;
  *(cycletable + 0x2D) = 4;
  *(cycletable + 0x2E) = 8;
  *(cycletable + 0x2F) = 4;

  *(cycletable + 0x30) = 8;  //  +4 IFF
  *(cycletable + 0x31) = 12;
  *(cycletable + 0x32) = 8;
  *(cycletable + 0x33) = 8;
  *(cycletable + 0x34) = 12;
  *(cycletable + 0x35) = 12;
  *(cycletable + 0x36) = 12;
  *(cycletable + 0x37) = 4;
  *(cycletable + 0x38) = 8;  //  +4 IFF
  *(cycletable + 0x39) = 8;
  *(cycletable + 0x3A) = 8;
  *(cycletable + 0x3B) = 8;
  *(cycletable + 0x3C) = 4;
  *(cycletable + 0x3D) = 4;
  *(cycletable + 0x3E) = 8;
  *(cycletable + 0x3F) = 4;

  *(cycletable + 0xC0) = 8;   // +12 IFF
  *(cycletable + 0xC1) = 12;
  *(cycletable + 0xC2) = 12;  //  +4 IFF
  *(cycletable + 0xC3) = 16;
  *(cycletable + 0xC4) = 12;  // +12 IFF
  *(cycletable + 0xC5) = 16;
  *(cycletable + 0xC6) = 8;
  *(cycletable + 0xC7) = 16;
  *(cycletable + 0xC8) = 8;   // +12 IFF
  *(cycletable + 0xC9) = 16;
  *(cycletable + 0xCA) = 12;  //  +4 IFF
  *(cycletable + 0xCC) = 12;  // +12 IFF
  *(cycletable + 0xCD) = 24;
  *(cycletable + 0xCE) = 8;
  *(cycletable + 0xCF) = 16;

  *(cycletable + 0xE0) = 12;
  *(cycletable + 0xE1) = 12;
  *(cycletable + 0xE2) = 8;
  // *(cycletable + 0xE3) = _
  // *(cycletable + 0xE4) = _
  *(cycletable + 0xE5) = 16;
  *(cycletable + 0xE6) = 8;
  *(cycletable + 0xE7) = 16;
  *(cycletable + 0xE8) = 16;
  *(cycletable + 0xE9) = 4;
  *(cycletable + 0xEA) = 16;
  // *(cycletable + 0xEB) = _
  // *(cycletable + 0xEC) = _
  // *(cycletable + 0xED) = _
  *(cycletable + 0xEE) = 8;
  *(cycletable + 0xEF) = 16;

  *(cycletable + 0xF0) = 12;
  *(cycletable + 0xF1) = 12;
  *(cycletable + 0xF2) = 8;
  *(cycletable + 0xF3) = 4;
  // *(cycletable + 0xF4) = _
  *(cycletable + 0xF5) = 16;
  *(cycletable + 0xF6) = 8;
  *(cycletable + 0xF7) = 16;
  *(cycletable + 0xF8) = 12;
  *(cycletable + 0xF9) = 8;
  *(cycletable + 0xFA) = 16;
  *(cycletable + 0xFB) = 4;
  // *(cycletable + 0xFC) = _
  // *(cycletable + 0xFD) = _
  *(cycletable + 0xFE) = 8;
  *(cycletable + 0xFF) = 16;
}

def fini_cycletable() {
  free(cycletable as *uint8);
}

// =============================================================================
// [FL] Flag
// =============================================================================
type Flag = uint8;

let FLAG_Z: Flag = 0b1000_0000;
let FLAG_N: Flag = 0b0100_0000;
let FLAG_H: Flag = 0b0010_0000;
let FLAG_C: Flag = 0b0001_0000;

def flag_set(flag: Flag, value: bool) {
  if value { *F |=  uint8(flag); }
  else     { *F &= ~uint8(flag); }
}

def flag_get(flag: Flag): bool {
  return *F & uint8(flag) != 0;
}

def flag_geti(flag: Flag): uint8 {
  return if flag_get(flag) { 1; } else { 0; };
}

// =============================================================================
// [OM] Operation Mnemonics
// =============================================================================

// Increment 8-bit Register
def om_inc8(r: *uint8) {
  flag_set(FLAG_H, (((*r & 0xF) + (1 & 0xF)) & 0x10) > 0);

  *r += 1;

  flag_set(FLAG_Z, *r == 0);
  flag_set(FLAG_N, false);
}

// Decrement 8-bit Register
def om_dec8(r: *uint8) {
  flag_set(FLAG_H, (((*r & 0xF) - (1 & 0xF)) & 0x10) < 0);

  *r -= 1;

  flag_set(FLAG_Z, *r == 0);
  flag_set(FLAG_N, true);
}

// Increment 16-bit Register
def om_inc16(r: *uint16) {
  *r += 1;
}

// Decrement 16-bit Register
def om_dec16(r: *uint16) {
  *r -= 1;
}

// And 8-bit value
def om_and8(a: uint8, b: uint8): uint8 {
  let r = a & b;

  flag_set(FLAG_Z, r == 0);
  flag_set(FLAG_N, false);
  flag_set(FLAG_H, true);
  flag_set(FLAG_C, false);

  return r;
}

// Or 8-bit value
def om_or8(a: uint8, b: uint8): uint8 {
  let r = a & b;

  flag_set(FLAG_Z, r == 0);
  flag_set(FLAG_N, false);
  flag_set(FLAG_H, false);
  flag_set(FLAG_C, false);

  return r;
}

// Xor 8-bit value
def om_xor8(a: uint8, b: uint8): uint8 {
  let r = a ^ b;

  flag_set(FLAG_Z, r == 0);
  flag_set(FLAG_N, false);
  flag_set(FLAG_H, false);
  flag_set(FLAG_C, false);

  return r;
}

// Add 8-bit value
def om_add8(a: uint8, b: uint8): uint8 {
  flag_set(FLAG_H, (((a & 0xF) + (b & 0xF)) & 0x10) > 0);

  let r = uint16(a) + uint16(b);

  flag_set(FLAG_C, r > 0xFFFF);
  flag_set(FLAG_Z, r == 0);
  flag_set(FLAG_N, false);

  return uint8(r & 0xFF);
}

// Add 8-bit value w/carry
def om_adc8(a: uint8, b: uint8): uint8 {
  return om_add8(a, b + uint8(flag_geti(FLAG_C)));
}

// Subtract 8-bit value
def om_sub8(a: uint8, b: uint8): uint8 {
  let r = om_add8(a, ~b);

  flag_set(FLAG_N, true);

  return r;
}

// Subtract 8-bit value w/carry
def om_sbc8(a: uint8, b: uint8): uint8 {
  let r = om_adc8(a, ~b);

  flag_set(FLAG_N, true);

  return r;
}

// Add 16-bit value
def om_add16(a: uint16, b: uint16): uint16 {
  flag_set(FLAG_H, (((a & 0xFF) + (b & 0xFF)) & 0x100) > 0);

  let r = uint32(a) + uint32(b);

  flag_set(FLAG_C, r > 0xFFFF);
  flag_set(FLAG_N, false);

  return uint16(r & 0xFFFF);
}

// Add 16-bit value w/carry
def om_adc16(a: uint16, b: uint16): uint16 {
  return om_add16(a, b + uint16(flag_geti(FLAG_C)));
}

// Subtract 16-bit value
def om_sub16(a: uint16, b: uint16): uint16 {
  let r = om_add16(a, ~b);

  flag_set(FLAG_N, true);

  return r;
}

// Subtract 16-bit value w/carry
def om_sbc16(a: uint16, b: uint16): uint16 {
  let r = om_adc16(a, ~b);

  flag_set(FLAG_N, true);

  return r;
}

// Push 16-bit value
def om_push16(r: *uint16) {
  SP -= 2;
  mmu_write16(SP, *r);
}

// Pop 16-bit value
def om_pop16(r: *uint16) {
  *r = mmu_read16(SP);
  SP += 2;
}

// Jump
def om_jp(address: uint16) {
  PC = address;
}

// Call
def om_call(address: uint16) {
  om_push16(&PC);
  om_jp(address);
}

// Return
def om_ret() {
  om_pop16(&PC);
}

// Return (and enable interrupts)
def om_reti() {
  IME = true;
  om_ret();
}

// =============================================================================
