auto I8080::instruction() -> void {
  EI = 0;
  P = 0;

  if(HALT) {
    return wait(1);
  }

  n8 code;
  while(true) {
    code = opcode();
    if(code == 0xdd) { prefix = Prefix::ix; continue; }
    if(code == 0xfd) { prefix = Prefix::iy; continue; }
    break;
  }

  instruction(code);

  prefix = Prefix::hl;
}

#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);

auto I8080::instruction(n8 code) -> void {
  switch(code) {
  op(0x00, NOP)
  op(0x01, LD_rr_nn, BC)
  op(0x02, LD_irr_a, BC)
  op(0x03, INC_rr, BC)
  op(0x04, INC_r, B)
  op(0x05, DEC_r, B)
  op(0x06, LD_r_n, B)
  op(0x07, RLCA)
  op(0x09, ADD_hl_rr, BC)
  op(0x0a, LD_a_irr, BC)
  op(0x0b, DEC_rr, BC)
  op(0x0c, INC_r, C)
  op(0x0d, DEC_r, C)
  op(0x0e, LD_r_n, C)
  op(0x0f, RRCA)
  op(0x11, LD_rr_nn, DE)
  op(0x12, LD_irr_a, DE)
  op(0x13, INC_rr, DE)
  op(0x14, INC_r, D)
  op(0x15, DEC_r, D)
  op(0x16, LD_r_n, D)
  op(0x17, RLA)
  op(0x19, ADD_hl_rr, DE)
  op(0x1a, LD_a_irr, DE)
  op(0x1b, DEC_rr, DE)
  op(0x1c, INC_r, E)
  op(0x1d, DEC_r, E)
  op(0x1e, LD_r_n, E)
  op(0x1f, RRA)
  op(0x21, LD_rr_nn, HL)
  op(0x22, LD_inn_rr, HL)
  op(0x23, INC_rr, HL)
  op(0x24, INC_r, H)
  op(0x25, DEC_r, H)
  op(0x26, LD_r_n, H)
  op(0x27, DAA)
  op(0x29, ADD_hl_rr, HL)
  op(0x2a, LD_rr_inn, HL)
  op(0x2b, DEC_rr, HL)
  op(0x2c, INC_r, L)
  op(0x2d, DEC_r, L)
  op(0x2e, LD_r_n, L)
  op(0x2f, CPL)
  op(0x31, LD_rr_nn, SP)
  op(0x32, LD_inn_a)
  op(0x33, INC_rr, SP)
  op(0x34, INC_irr, HL)
  op(0x35, DEC_irr, HL)
  op(0x36, LD_irr_n, HL)
  op(0x37, SCF)
  op(0x39, ADD_hl_rr, SP)
  op(0x3a, LD_a_inn)
  op(0x3b, DEC_rr, SP)
  op(0x3c, INC_r, A)
  op(0x3d, DEC_r, A)
  op(0x3e, LD_r_n, A)
  op(0x3f, CCF)
  op(0x40, LD_r_r, B, B)
  op(0x41, LD_r_r, B, C)
  op(0x42, LD_r_r, B, D)
  op(0x43, LD_r_r, B, E)
  op(0x44, LD_r_r, B, H)
  op(0x45, LD_r_r, B, L)
  op(0x46, LD_r_irr, B, HL)
  op(0x47, LD_r_r, B, A)
  op(0x48, LD_r_r, C, B)
  op(0x49, LD_r_r, C, C)
  op(0x4a, LD_r_r, C, D)
  op(0x4b, LD_r_r, C, E)
  op(0x4c, LD_r_r, C, H)
  op(0x4d, LD_r_r, C, L)
  op(0x4e, LD_r_irr, C, HL)
  op(0x4f, LD_r_r, C, A)
  op(0x50, LD_r_r, D, B)
  op(0x51, LD_r_r, D, C)
  op(0x52, LD_r_r, D, D)
  op(0x53, LD_r_r, D, E)
  op(0x54, LD_r_r, D, H)
  op(0x55, LD_r_r, D, L)
  op(0x56, LD_r_irr, D, HL)
  op(0x57, LD_r_r, D, A)
  op(0x58, LD_r_r, E, B)
  op(0x59, LD_r_r, E, C)
  op(0x5a, LD_r_r, E, D)
  op(0x5b, LD_r_r, E, E)
  op(0x5c, LD_r_r, E, H)
  op(0x5d, LD_r_r, E, L)
  op(0x5e, LD_r_irr, E, HL)
  op(0x5f, LD_r_r, E, A)
  op(0x60, LD_r_r, H, B)
  op(0x61, LD_r_r, H, C)
  op(0x62, LD_r_r, H, D)
  op(0x63, LD_r_r, H, E)
  op(0x64, LD_r_r, H, H)
  op(0x65, LD_r_r, H, L)
  op(0x66, LD_r_irr,  H, HL)
  op(0x67, LD_r_r, H, A)
  op(0x68, LD_r_r, L, B)
  op(0x69, LD_r_r, L, C)
  op(0x6a, LD_r_r, L, D)
  op(0x6b, LD_r_r, L, E)
  op(0x6c, LD_r_r, L, H)
  op(0x6d, LD_r_r, L, L)
  op(0x6e, LD_r_irr, L, HL)
  op(0x6f, LD_r_r, L, A)
  op(0x70, LD_irr_r, HL, B)
  op(0x71, LD_irr_r, HL, C)
  op(0x72, LD_irr_r, HL, D)
  op(0x73, LD_irr_r, HL, E)
  op(0x74, LD_irr_r, HL, H)
  op(0x75, LD_irr_r, HL, L)
  op(0x76, HALT)
  op(0x77, LD_irr_r, HL, A)
  op(0x78, LD_r_r, A, B)
  op(0x79, LD_r_r, A, C)
  op(0x7a, LD_r_r, A, D)
  op(0x7b, LD_r_r, A, E)
  op(0x7c, LD_r_r, A, H)
  op(0x7d, LD_r_r, A, L)
  op(0x7e, LD_r_irr, A, HL)
  op(0x7f, LD_r_r, A, A)
  op(0x80, ADD_a_r, B)
  op(0x81, ADD_a_r, C)
  op(0x82, ADD_a_r, D)
  op(0x83, ADD_a_r, E)
  op(0x84, ADD_a_r, H)
  op(0x85, ADD_a_r, L)
  op(0x86, ADD_a_irr, HL)
  op(0x87, ADD_a_r, A)
  op(0x88, ADC_a_r, B)
  op(0x89, ADC_a_r, C)
  op(0x8a, ADC_a_r, D)
  op(0x8b, ADC_a_r, E)
  op(0x8c, ADC_a_r, H)
  op(0x8d, ADC_a_r, L)
  op(0x8e, ADC_a_irr, HL)
  op(0x8f, ADC_a_r, A)
  op(0x90, SUB_a_r, B)
  op(0x91, SUB_a_r, C)
  op(0x92, SUB_a_r, D)
  op(0x93, SUB_a_r, E)
  op(0x94, SUB_a_r, H)
  op(0x95, SUB_a_r, L)
  op(0x96, SUB_a_irr, HL)
  op(0x97, SUB_a_r, A)
  op(0x98, SBC_a_r, B)
  op(0x99, SBC_a_r, C)
  op(0x9a, SBC_a_r, D)
  op(0x9b, SBC_a_r, E)
  op(0x9c, SBC_a_r, H)
  op(0x9d, SBC_a_r, L)
  op(0x9e, SBC_a_irr, HL)
  op(0x9f, SBC_a_r, A)
  op(0xa0, AND_a_r, B)
  op(0xa1, AND_a_r, C)
  op(0xa2, AND_a_r, D)
  op(0xa3, AND_a_r, E)
  op(0xa4, AND_a_r, H)
  op(0xa5, AND_a_r, L)
  op(0xa6, AND_a_irr, HL)
  op(0xa7, AND_a_r, A)
  op(0xa8, XOR_a_r, B)
  op(0xa9, XOR_a_r, C)
  op(0xaa, XOR_a_r, D)
  op(0xab, XOR_a_r, E)
  op(0xac, XOR_a_r, H)
  op(0xad, XOR_a_r, L)
  op(0xae, XOR_a_irr, HL)
  op(0xaf, XOR_a_r, A)
  op(0xb0, OR_a_r, B)
  op(0xb1, OR_a_r, C)
  op(0xb2, OR_a_r, D)
  op(0xb3, OR_a_r, E)
  op(0xb4, OR_a_r, H)
  op(0xb5, OR_a_r, L)
  op(0xb6, OR_a_irr, HL)
  op(0xb7, OR_a_r, A)
  op(0xb8, CP_a_r, B)
  op(0xb9, CP_a_r, C)
  op(0xba, CP_a_r, D)
  op(0xbb, CP_a_r, E)
  op(0xbc, CP_a_r, H)
  op(0xbd, CP_a_r, L)
  op(0xbe, CP_a_irr, HL)
  op(0xbf, CP_a_r, A)
  op(0xc0, RET_c, ZF == 0)
  op(0xc1, POP_rr, BC)
  op(0xc2, JP_c_nn, ZF == 0)
  op(0xc3, JP_c_nn, 1)
  op(0xc4, CALL_c_nn, ZF == 0)
  op(0xc5, PUSH_rr, BC)
  op(0xc6, ADD_a_n)
  op(0xc7, RST_o, 0)
  op(0xc8, RET_c, ZF == 1)
  op(0xc9, RET)
  op(0xca, JP_c_nn, ZF == 1)
  op(0xcc, CALL_c_nn, ZF == 1)
  op(0xcd, CALL_c_nn, true)
  op(0xce, ADC_a_n)
  op(0xcf, RST_o, 1)
  op(0xd0, RET_c, CF == 0)
  op(0xd1, POP_rr, DE)
  op(0xd2, JP_c_nn, CF == 0)
  op(0xd3, OUT_in_a)
  op(0xd4, CALL_c_nn, CF == 0)
  op(0xd5, PUSH_rr, DE)
  op(0xd6, SUB_a_n)
  op(0xd7, RST_o, 2)
  op(0xd8, RET_c, CF == 1)
  op(0xda, JP_c_nn, CF == 1)
  op(0xdb, IN_a_in)
  op(0xdc, CALL_c_nn, CF == 1)
  op(0xde, SBC_a_n)
  op(0xdf, RST_o, 3)
  op(0xe0, RET_c, PF == 0)
  op(0xe1, POP_rr, HL)
  op(0xe2, JP_c_nn, PF == 0)
  op(0xe3, EX_irr_rr, SP, HL)
  op(0xe4, CALL_c_nn, PF == 0)
  op(0xe5, PUSH_rr, HL)
  op(0xe6, AND_a_n)
  op(0xe7, RST_o, 4)
  op(0xe8, RET_c, PF == 1)
  op(0xe9, JP_rr, HL)
  op(0xea, JP_c_nn, PF == 1)
  op(0xeb, EX_rr_rr, DE, HL)
  op(0xec, CALL_c_nn, PF == 1)
  op(0xee, XOR_a_n)
  op(0xef, RST_o, 5)
  op(0xf0, RET_c, SF == 0)
  op(0xf1, POP_rr, AF)
  op(0xf2, JP_c_nn, SF == 0)
  op(0xf3, DI)
  op(0xf4, CALL_c_nn, SF == 0)
  op(0xf5, PUSH_rr, AF)
  op(0xf6, OR_a_n)
  op(0xf7, RST_o, 6)
  op(0xf8, RET_c, SF == 1)
  op(0xf9, LD_sp_rr, HL)
  op(0xfa, JP_c_nn, SF == 1)
  op(0xfb, EI)
  op(0xfc, CALL_c_nn, SF == 1)
  op(0xfe, CP_a_n)
  op(0xff, RST_o, 7)
  }
}

#undef op
