#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);
#define fp(name) &MOS6502::algorithm##name
#define addr(name) &MOS6502::address##name

auto MOS6502::interrupt() -> void {
  idle();
  idle();
  push(PCH);
  push(PCL);
  n16 vector = 0xfffe;
  nmi(vector);
  push(P | 0x20);
  I = 1;
  PCL = read(vector++);
L PCH = read(vector++);
}

auto MOS6502::reset() -> void {
  idle();
  idle();
  read(0x0100 | S--); // Dummy push (PCH)
  read(0x0100 | S--); // Dummy push (PCL)
  n16 vector = 0xfffc;
  read(0x0100 | S--); // Dummy push (P)
  I = 1;
  PCL = read(vector++);
L PCH = read(vector++);
}

auto MOS6502::instruction() -> void {
  switch(opcode()) {
  op(0x00, None,   addr(Implied),        fp(BRK))
  op(0x01, Load,   addr(IndirectX),      fp(ORA))
  op(0x02, None,   addr(Implied),        fp(JAM))
  op(0x03, Modify, addr(IndirectX),      fp(SLO))
  op(0x04, Load,   addr(ZeroPage),       fp(NOP))
  op(0x05, Load,   addr(ZeroPage),       fp(ORA))
  op(0x06, Modify, addr(ZeroPage),       fp(ASLM))
  op(0x07, Modify, addr(ZeroPage),       fp(SLO))
  op(0x08, None,   addr(Implied),        fp(PHP))
  op(0x09, Load,   addr(Immediate),      fp(ORA))
  op(0x0a, Load,   addr(Implied),        fp(ASLA))
  op(0x0b, Load,   addr(Immediate),      fp(AAC))
  op(0x0c, Load,   addr(Absolute),       fp(NOP))
  op(0x0d, Load,   addr(Absolute),       fp(ORA))
  op(0x0e, Modify, addr(Absolute),       fp(ASLM))
  op(0x0f, Modify, addr(Absolute),       fp(SLO))
  op(0x10, None,   addr(Relative),       fp(BPL))
  op(0x11, Load,   addr(IndirectYRead),  fp(ORA))
  op(0x12, None,   addr(Implied),        fp(JAM))
  op(0x13, Modify, addr(IndirectYWrite), fp(SLO))
  op(0x14, Load,   addr(ZeroPageX),      fp(NOP))
  op(0x15, Load,   addr(ZeroPageX),      fp(ORA))
  op(0x16, Modify, addr(ZeroPageX),      fp(ASLM))
  op(0x17, Modify, addr(ZeroPageX),      fp(SLO))
  op(0x18, Load,   addr(Implied),        fp(CLC))
  op(0x19, Load,   addr(AbsoluteYRead),  fp(ORA))
  op(0x1a, Load,   addr(Implied),        fp(NOP))
  op(0x1b, Modify, addr(AbsoluteYWrite), fp(SLO))
  op(0x1c, Load,   addr(AbsoluteXRead),  fp(NOP))
  op(0x1d, Load,   addr(AbsoluteXRead),  fp(ORA))
  op(0x1e, Modify, addr(AbsoluteXWrite), fp(ASLM))
  op(0x1f, Modify, addr(AbsoluteXWrite), fp(SLO))
  op(0x20, None,   addr(Implied),        fp(JSR))
  op(0x21, Load,   addr(IndirectX),      fp(AND))
  op(0x22, None,   addr(Implied),        fp(JAM))
  op(0x23, Modify, addr(IndirectX),      fp(RLA))
  op(0x24, Load,   addr(ZeroPage),       fp(BIT))
  op(0x25, Load,   addr(ZeroPage),       fp(AND))
  op(0x26, Modify, addr(ZeroPage),       fp(ROLM))
  op(0x27, Modify, addr(ZeroPage),       fp(RLA))
  op(0x28, None,   addr(Implied),        fp(PLP))
  op(0x29, Load,   addr(Immediate),      fp(AND))
  op(0x2a, Load,   addr(Implied),        fp(ROLA))
  op(0x2b, Load,   addr(Immediate),      fp(AAC))
  op(0x2c, Load,   addr(Absolute),       fp(BIT))
  op(0x2d, Load,   addr(Absolute),       fp(AND))
  op(0x2e, Modify, addr(Absolute),       fp(ROLM))
  op(0x2f, Modify, addr(Absolute),       fp(RLA))
  op(0x30, None,   addr(Relative),       fp(BMI))
  op(0x31, Load,   addr(IndirectYRead),  fp(AND))
  op(0x32, None,   addr(Implied),        fp(JAM))
  op(0x33, Modify, addr(IndirectYWrite), fp(RLA))
  op(0x34, Load,   addr(ZeroPageX),      fp(NOP))
  op(0x35, Load,   addr(ZeroPageX),      fp(AND))
  op(0x36, Modify, addr(ZeroPageX),      fp(ROLM))
  op(0x37, Modify, addr(ZeroPageX),      fp(RLA))
  op(0x38, Load,   addr(Implied),        fp(SEC))
  op(0x39, Load,   addr(AbsoluteYRead),  fp(AND))
  op(0x3a, Load,   addr(Implied),        fp(NOP))
  op(0x3b, Modify, addr(AbsoluteYWrite), fp(RLA))
  op(0x3c, Load,   addr(AbsoluteXRead),  fp(NOP))
  op(0x3d, Load,   addr(AbsoluteXRead),  fp(AND))
  op(0x3e, Modify, addr(AbsoluteXWrite), fp(ROLM))
  op(0x3f, Modify, addr(AbsoluteXWrite), fp(RLA))
  op(0x40, None,   addr(Implied),        fp(RTI))
  op(0x41, Load,   addr(IndirectX),      fp(EOR))
  op(0x42, None,   addr(Implied),        fp(JAM))
  op(0x43, Modify, addr(IndirectX),      fp(SRE))
  op(0x44, Load,   addr(ZeroPage),       fp(NOP))
  op(0x45, Load,   addr(ZeroPage),       fp(EOR))
  op(0x46, Modify, addr(ZeroPage),       fp(LSRM))
  op(0x47, Modify, addr(ZeroPage),       fp(SRE))
  op(0x48, None,   addr(Implied),        fp(PHA))
  op(0x49, Load,   addr(Immediate),      fp(EOR))
  op(0x4a, Load,   addr(Implied),        fp(LSRA))
  op(0x4b, Load,   addr(Immediate),      fp(ASR))
  op(0x4c, None,   addr(AbsoluteJMP),    fp(JMP))
  op(0x4d, Load,   addr(Absolute),       fp(EOR))
  op(0x4e, Modify, addr(Absolute),       fp(LSRM))
  op(0x4f, Modify, addr(Absolute),       fp(SRE))
  op(0x50, None,   addr(Relative),       fp(BVC))
  op(0x51, Load,   addr(IndirectYRead),  fp(EOR))
  op(0x52, None,   addr(Implied),        fp(JAM))
  op(0x53, Modify, addr(IndirectYWrite), fp(SRE))
  op(0x54, Load,   addr(ZeroPageX),      fp(NOP))
  op(0x55, Load,   addr(ZeroPageX),      fp(EOR))
  op(0x56, Modify, addr(ZeroPageX),      fp(LSRM))
  op(0x57, Modify, addr(ZeroPageX),      fp(SRE))
  op(0x58, Load,   addr(Implied),        fp(CLI))
  op(0x59, Load,   addr(AbsoluteYRead),  fp(EOR))
  op(0x5a, Load,   addr(Implied),        fp(NOP))
  op(0x5b, Modify, addr(AbsoluteYWrite), fp(SRE))
  op(0x5c, Load,   addr(AbsoluteXRead),  fp(NOP))
  op(0x5d, Load,   addr(AbsoluteXRead),  fp(EOR))
  op(0x5e, Modify, addr(AbsoluteXWrite), fp(LSRM))
  op(0x5f, Modify, addr(AbsoluteXWrite), fp(SRE))
  op(0x60, None,   addr(Implied),        fp(RTS))
  op(0x61, Load,   addr(IndirectX),      fp(ADC))
  op(0x62, None,   addr(Implied),        fp(JAM))
  op(0x63, Modify, addr(IndirectX),      fp(RRA))
  op(0x64, Load,   addr(ZeroPage),       fp(NOP))
  op(0x65, Load,   addr(ZeroPage),       fp(ADC))
  op(0x66, Modify, addr(ZeroPage),       fp(RORM))
  op(0x67, Modify, addr(ZeroPage),       fp(RRA))
  op(0x68, None,   addr(Implied),        fp(PLA))
  op(0x69, Load,   addr(Immediate),      fp(ADC))
  op(0x6a, Load,   addr(Implied),        fp(RORA))
  op(0x6b, Load,   addr(Immediate),      fp(ARR))
  op(0x6c, None,   addr(Indirect),       fp(JMP))
  op(0x6d, Load,   addr(Absolute),       fp(ADC))
  op(0x6e, Modify, addr(Absolute),       fp(RORM))
  op(0x6f, Modify, addr(Absolute),       fp(RRA))
  op(0x70, None,   addr(Relative),       fp(BVS))
  op(0x71, Load,   addr(IndirectYRead),  fp(ADC))
  op(0x72, None,   addr(Implied),        fp(JAM))
  op(0x73, Modify, addr(IndirectYWrite), fp(RRA))
  op(0x74, Load,   addr(ZeroPageX),      fp(NOP))
  op(0x75, Load,   addr(ZeroPageX),      fp(ADC))
  op(0x76, Modify, addr(ZeroPageX),      fp(RORM))
  op(0x77, Modify, addr(ZeroPageX),      fp(RRA))
  op(0x78, Load,   addr(Implied),        fp(SEI))
  op(0x79, Load,   addr(AbsoluteYRead),  fp(ADC))
  op(0x7a, Load,   addr(Implied),        fp(NOP))
  op(0x7b, Modify, addr(AbsoluteYWrite), fp(RRA))
  op(0x7c, Load,   addr(AbsoluteXRead),  fp(NOP))
  op(0x7d, Load,   addr(AbsoluteXRead),  fp(ADC))
  op(0x7e, Modify, addr(AbsoluteXWrite), fp(RORM))
  op(0x7f, Modify, addr(AbsoluteXWrite), fp(RRA))
  op(0x80, Load,   addr(Immediate),      fp(NOP))
  op(0x81, Store,  addr(IndirectX),      fp(STA))
  op(0x82, Load,   addr(Immediate),      fp(NOP))
  op(0x83, Store,  addr(IndirectX),      fp(AAX))
  op(0x84, Store,  addr(ZeroPage),       fp(STY))
  op(0x85, Store,  addr(ZeroPage),       fp(STA))
  op(0x86, Store,  addr(ZeroPage),       fp(STX))
  op(0x87, Store,  addr(ZeroPage),       fp(AAX))
  op(0x88, Load,   addr(Implied),        fp(DEY))
  op(0x89, Load,   addr(Immediate),      fp(NOP))
  op(0x8a, Load,   addr(Implied),        fp(TXA))
  op(0x8b, Load,   addr(Immediate),      fp(ANE))
  op(0x8c, Store,  addr(Absolute),       fp(STY))
  op(0x8d, Store,  addr(Absolute),       fp(STA))
  op(0x8e, Store,  addr(Absolute),       fp(STX))
  op(0x8f, Store,  addr(Absolute),       fp(AAX))
  op(0x90, None,   addr(Relative),       fp(BCC))
  op(0x91, Store,  addr(IndirectYWrite), fp(STA))
  op(0x92, None,   addr(Implied),        fp(JAM))
  op(0x93, Store,  addr(IndirectYWrite), fp(AAX))
  op(0x94, Store,  addr(ZeroPageX),      fp(STY))
  op(0x95, Store,  addr(ZeroPageX),      fp(STA))
  op(0x96, Store,  addr(ZeroPageY),      fp(STX))
  op(0x97, Store,  addr(ZeroPageY),      fp(AAX))
  op(0x98, Load,   addr(Implied),        fp(TYA))
  op(0x99, Store,  addr(AbsoluteYWrite), fp(STA))
  op(0x9a, Load,   addr(Implied),        fp(TXS))
  op(0x9b, Store,  addr(AbsoluteYWrite), fp(TAS))
  op(0x9c, Store,  addr(AbsoluteXWrite), fp(SYA))
  op(0x9d, Store,  addr(AbsoluteXWrite), fp(STA))
  op(0x9e, Store,  addr(AbsoluteYWrite), fp(SXA))
  op(0x9f, Store,  addr(AbsoluteYWrite), fp(AAX))
  op(0xa0, Load,   addr(Immediate),      fp(LDY))
  op(0xa1, Load,   addr(IndirectX),      fp(LDA))
  op(0xa2, Load,   addr(Immediate),      fp(LDX))
  op(0xa3, Load,   addr(IndirectX),      fp(LAX))
  op(0xa4, Load,   addr(ZeroPage),       fp(LDY))
  op(0xa5, Load,   addr(ZeroPage),       fp(LDA))
  op(0xa6, Load,   addr(ZeroPage),       fp(LDX))
  op(0xa7, Load,   addr(ZeroPage),       fp(LAX))
  op(0xa8, Load,   addr(Implied),        fp(TAY))
  op(0xa9, Load,   addr(Immediate),      fp(LDA))
  op(0xaa, Load,   addr(Implied),        fp(TAX))
  op(0xab, Load,   addr(Immediate),      fp(ATX))
  op(0xac, Load,   addr(Absolute),       fp(LDY))
  op(0xad, Load,   addr(Absolute),       fp(LDA))
  op(0xae, Load,   addr(Absolute),       fp(LDX))
  op(0xaf, Load,   addr(Absolute),       fp(LAX))
  op(0xb0, None,   addr(Relative),       fp(BCS))
  op(0xb1, Load,   addr(IndirectYRead),  fp(LDA))
  op(0xb2, None,   addr(Implied),        fp(JAM))
  op(0xb3, Load,   addr(IndirectYRead),  fp(LAX))
  op(0xb4, Load,   addr(ZeroPageX),      fp(LDY))
  op(0xb5, Load,   addr(ZeroPageX),      fp(LDA))
  op(0xb6, Load,   addr(ZeroPageY),      fp(LDX))
  op(0xb7, Load,   addr(ZeroPageY),      fp(LAX))
  op(0xb8, Load,   addr(Implied),        fp(CLV))
  op(0xb9, Load,   addr(AbsoluteYRead),  fp(LDA))
  op(0xba, Load,   addr(Implied),        fp(TSX))
  op(0xbb, Load,   addr(AbsoluteYRead),  fp(LAS))
  op(0xbc, Load,   addr(AbsoluteXRead),  fp(LDY))
  op(0xbd, Load,   addr(AbsoluteXRead),  fp(LDA))
  op(0xbe, Load,   addr(AbsoluteYRead),  fp(LDX))
  op(0xbf, Load,   addr(AbsoluteYRead),  fp(LAX))
  op(0xc0, Load,   addr(Immediate),      fp(CPY))
  op(0xc1, Load,   addr(IndirectX),      fp(CMP))
  op(0xc2, Load,   addr(Immediate),      fp(NOP))
  op(0xc3, Modify, addr(IndirectX),      fp(DCP))
  op(0xc4, Load,   addr(ZeroPage),       fp(CPY))
  op(0xc5, Load,   addr(ZeroPage),       fp(CMP))
  op(0xc6, Modify, addr(ZeroPage),       fp(DEC))
  op(0xc7, Modify, addr(ZeroPage),       fp(DCP))
  op(0xc8, Load,   addr(Implied),        fp(INY))
  op(0xc9, Load,   addr(Immediate),      fp(CMP))
  op(0xca, Load,   addr(Implied),        fp(DEX))
  op(0xcb, Load,   addr(Immediate),      fp(AXS))
  op(0xcc, Load,   addr(Absolute),       fp(CPY))
  op(0xcd, Load,   addr(Absolute),       fp(CMP))
  op(0xce, Modify, addr(Absolute),       fp(DEC))
  op(0xcf, Modify, addr(Absolute),       fp(DCP))
  op(0xd0, None,   addr(Relative),       fp(BNE))
  op(0xd1, Load,   addr(IndirectYRead),  fp(CMP))
  op(0xd2, None,   addr(Implied),        fp(JAM))
  op(0xd3, Modify, addr(IndirectYWrite), fp(DCP))
  op(0xd4, Load,   addr(ZeroPageX),      fp(NOP))
  op(0xd5, Load,   addr(ZeroPageX),      fp(CMP))
  op(0xd6, Modify, addr(ZeroPageX),      fp(DEC))
  op(0xd7, Modify, addr(ZeroPageX),      fp(DCP))
  op(0xd8, Load,   addr(Implied),        fp(CLD))
  op(0xd9, Load,   addr(AbsoluteYRead),  fp(CMP))
  op(0xda, Load,   addr(Implied),        fp(NOP))
  op(0xdb, Modify, addr(AbsoluteYWrite), fp(DCP))
  op(0xdc, Load,   addr(AbsoluteXRead),  fp(NOP))
  op(0xdd, Load,   addr(AbsoluteXRead),  fp(CMP))
  op(0xde, Modify, addr(AbsoluteXWrite), fp(DEC))
  op(0xdf, Modify, addr(AbsoluteXWrite), fp(DCP))
  op(0xe0, Load,   addr(Immediate),      fp(CPX))
  op(0xe1, Load,   addr(IndirectX),      fp(SBC))
  op(0xe2, Load,   addr(Immediate),      fp(NOP))
  op(0xe3, Modify, addr(IndirectX),      fp(ISC))
  op(0xe4, Load,   addr(ZeroPage),       fp(CPX))
  op(0xe5, Load,   addr(ZeroPage),       fp(SBC))
  op(0xe6, Modify, addr(ZeroPage),       fp(INC))
  op(0xe7, Modify, addr(ZeroPage),       fp(ISC))
  op(0xe8, Load,   addr(Implied),        fp(INX))
  op(0xe9, Load,   addr(Immediate),      fp(SBC))
  op(0xea, Load,   addr(Implied),        fp(NOP))
  op(0xeb, Load,   addr(Immediate),      fp(SBC))
  op(0xec, Load,   addr(Absolute),       fp(CPX))
  op(0xed, Load,   addr(Absolute),       fp(SBC))
  op(0xee, Modify, addr(Absolute),       fp(INC))
  op(0xef, Modify, addr(Absolute),       fp(ISC))
  op(0xf0, None,   addr(Relative),       fp(BEQ))
  op(0xf1, Load,   addr(IndirectYRead),  fp(SBC))
  op(0xf2, None,   addr(Implied),        fp(JAM))
  op(0xf3, Modify, addr(IndirectYWrite), fp(ISC))
  op(0xf4, Load,   addr(ZeroPageX),      fp(NOP))
  op(0xf5, Load,   addr(ZeroPageX),      fp(SBC))
  op(0xf6, Modify, addr(ZeroPageX),      fp(INC))
  op(0xf7, Modify, addr(ZeroPageX),      fp(ISC))
  op(0xf8, Load,   addr(Implied),        fp(SED))
  op(0xf9, Load,   addr(AbsoluteYRead),  fp(SBC))
  op(0xfa, Load,   addr(Implied),        fp(NOP))
  op(0xfb, Modify, addr(AbsoluteYWrite), fp(ISC))
  op(0xfc, Load,   addr(AbsoluteXRead),  fp(NOP))
  op(0xfd, Load,   addr(AbsoluteXRead),  fp(SBC))
  op(0xfe, Modify, addr(AbsoluteXWrite), fp(INC))
  op(0xff, Modify, addr(AbsoluteXWrite), fp(ISC))
  }
}

#undef op
#undef fp
#undef addr
