#include #include #include #include "op.h" #include "cpu.h" // Reference: https://www.nesdev.org/wiki/CPU_unofficial_opcodes // https://www.middle-engine.com/blog/posts/2020/06/23/programming-the-nes-the-6502-in-detail #define IS_OP_CODE_MODE(op, op_code, addr_mode) \ case op_code: \ log_debug("OP: %s", #op); \ op_ ## op(ADDR_MODE_ ## addr_mode); \ break; #define IS_OP_CODE(op, op_code) \ IS_OP_CODE_MODE(op, op_code, IMPLICIT) #define IS_ALU_OP_CODE_(op, offset, addr_mode) \ IS_OP_CODE_MODE(op, OP_CODE_BASE_ ## op + offset, addr_mode) #define IS_ALU_OP_CODE(op) \ IS_ALU_OP_CODE_(op, 0x01, INDIRECT_X) \ IS_ALU_OP_CODE_(op, 0x05, ZERO_PAGE) \ IS_ALU_OP_CODE_(op, 0x09, IMMEDIATE) \ IS_ALU_OP_CODE_(op, 0x0d, ABSOLUTE) \ IS_ALU_OP_CODE_(op, 0x11, INDIRECT_Y) \ IS_ALU_OP_CODE_(op, 0x15, ZERO_PAGE_INDEXED_X) \ IS_ALU_OP_CODE_(op, 0x19, ABSOLUTE_INDEXED_Y) \ IS_ALU_OP_CODE_(op, 0x1d, ABSOLUTE_INDEXED_X) #define IS_ALU_OP_CODE_NO_IMMEDIATE(op) \ IS_ALU_OP_CODE_(op, 0x01, INDIRECT_X) \ IS_ALU_OP_CODE_(op, 0x05, ZERO_PAGE) \ IS_ALU_OP_CODE_(op, 0x0d, ABSOLUTE) \ IS_ALU_OP_CODE_(op, 0x11, INDIRECT_Y) \ IS_ALU_OP_CODE_(op, 0x15, ZERO_PAGE_INDEXED_X) \ IS_ALU_OP_CODE_(op, 0x19, ABSOLUTE_INDEXED_Y) \ IS_ALU_OP_CODE_(op, 0x1d, ABSOLUTE_INDEXED_X) #define IS_RMW_OP_CODE_(op, line, offset, addr_mode) \ IS_OP_CODE_MODE(op, OP_CODE_BASE_ ## line + offset, addr_mode) #define IS_RMW_OP_CODE(op, line) \ IS_RMW_OP_CODE_(op, line, 0x06, ZERO_PAGE) \ IS_RMW_OP_CODE_(op, line, 0x0a, IMPLICIT) \ IS_RMW_OP_CODE_(op, line, 0x0e, ABSOLUTE) \ IS_RMW_OP_CODE_(op, line, 0x16, ZERO_PAGE_INDEXED_X) \ IS_RMW_OP_CODE_(op, line, 0x1e, ABSOLUTE_INDEXED_X) #define IS_UNOFFICIAL_OP_CODE_(op, line, offset, addr_mode) \ IS_OP_CODE_MODE(op, OP_CODE_BASE_ ## line + offset, addr_mode) #define IS_UNOFFICIAL_OP_CODE(op, line) \ IS_UNOFFICIAL_OP_CODE_(op, line, 0x03, INDIRECT_X) \ IS_UNOFFICIAL_OP_CODE_(op, line, 0x07, ZERO_PAGE) \ IS_UNOFFICIAL_OP_CODE_(op, line, 0x0f, ABSOLUTE) \ IS_UNOFFICIAL_OP_CODE_(op, line, 0x13, INDIRECT_Y) \ IS_UNOFFICIAL_OP_CODE_(op, line, 0x17, ZERO_PAGE_INDEXED_X) \ IS_UNOFFICIAL_OP_CODE_(op, line, 0x1b, ABSOLUTE_INDEXED_Y) \ IS_UNOFFICIAL_OP_CODE_(op, line, 0x1f, ABSOLUTE_INDEXED_X) address decode_operand_addr(AddressingMode addr_mode, bool *page_crossing) { CpuRegisters *registers = cpu_get_registers(); if (addr_mode == ADDR_MODE_ZERO_PAGE) { return cpu_get_next_byte(); } else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_X) { return (cpu_get_next_byte() + registers->x) & 0xff; } else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_Y) { return (cpu_get_next_byte() + registers->y) & 0xff; } else if (addr_mode == ADDR_MODE_ABSOLUTE || addr_mode == ADDR_MODE_ABSOLUTE_JUMP) { return cpu_get_next_word(); } else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) { word addr = cpu_get_next_word(); word new_addr = addr + registers->x; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); return new_addr; } else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) { word addr = cpu_get_next_word(); word new_addr = addr + registers->y; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); return new_addr; } else if (addr_mode == ADDR_MODE_INDIRECT_JUMP) { word addr = cpu_get_next_word(); if ((addr & 0xff) == 0xff) { // Error in NES CPU for JMP op word result = cpu_peek_byte(addr); result += cpu_peek_byte(addr & 0xff00) << 8; return result; } return cpu_peek_word(addr); } else if (addr_mode == ADDR_MODE_INDIRECT_X) { byte addr = cpu_get_next_byte(); word result = cpu_peek_byte((addr + registers->x) & 0xff); result += cpu_peek_byte((addr + registers->x + 1) & 0xff) << 8; return result; } else if (addr_mode == ADDR_MODE_INDIRECT_Y) { byte arg_addr = cpu_get_next_byte(); word addr = cpu_peek_byte(arg_addr) + (cpu_peek_byte((arg_addr + 1) & 0xff) << 8); word new_addr = addr + registers->y; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); return new_addr; } else { assert(false); } } Operand decode_operand(AddressingMode addr_mode) { Operand operand; if (addr_mode == ADDR_MODE_ACCUMULATOR) { operand.type = OPERAND_TYPE_ACCUMULATOR; operand.value = 0; operand.is_page_crossing = false; } else if (addr_mode == ADDR_MODE_IMMEDIATE) { operand.type = OPERAND_TYPE_IMMEDIATE; operand.value = cpu_get_next_byte(); operand.is_page_crossing = false; } else { operand.type = OPERAND_TYPE_ADDRESS; operand.value = decode_operand_addr(addr_mode, &operand.is_page_crossing); } return operand; } byte read_operand(Operand operand) { switch (operand.type) { case OPERAND_TYPE_ACCUMULATOR: return cpu_get_registers()->accumulator; case OPERAND_TYPE_IMMEDIATE: return (byte) operand.value; case OPERAND_TYPE_ADDRESS: return cpu_peek_byte(operand.value); default: assert(false); } } void write_operand(Operand operand, byte value) { switch (operand.type) { case OPERAND_TYPE_ACCUMULATOR: cpu_get_registers()->accumulator = value; break; case OPERAND_TYPE_ADDRESS: cpu_push_byte(operand.value, value); break; default: assert(false); } } bool is_sign_overflow(byte val1, byte val2, byte result) { return ((val1 & 0x80) == (val2 & 0x80)) && ((val1 & 0x80) != (result & 0x80)); } byte get_cycle_count(Operand operand, AddressingMode addr_mode) { switch (addr_mode) { case ADDR_MODE_ACCUMULATOR: case ADDR_MODE_IMPLICIT: return 2; case ADDR_MODE_ZERO_PAGE: return 3; case ADDR_MODE_ZERO_PAGE_INDEXED_X: case ADDR_MODE_ZERO_PAGE_INDEXED_Y: case ADDR_MODE_ABSOLUTE: return 4; case ADDR_MODE_ABSOLUTE_INDEXED_X: case ADDR_MODE_ABSOLUTE_INDEXED_Y: return operand.is_page_crossing ? 5 : 4; case ADDR_MODE_INDIRECT_X: return 6; case ADDR_MODE_INDIRECT_Y: return operand.is_page_crossing ? 6 : 5; default: assert(false); } } byte get_shift_cycle_count(AddressingMode addr_mode) { switch (addr_mode) { case ADDR_MODE_ACCUMULATOR: return 2; case ADDR_MODE_ZERO_PAGE: return 5; case ADDR_MODE_ZERO_PAGE_INDEXED_X: case ADDR_MODE_ABSOLUTE: return 6; case ADDR_MODE_ABSOLUTE_INDEXED_X: return 7; default: assert(false); } } void set_acl_flags(byte result) { cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); } byte get_branch_cycle_count(bool branching, char offset) { address target = cpu_get_registers()->program_counter; byte cycle_count = 2; if (branching) { cycle_count += 1; if ((target & 0xff00) != ((target - offset) & 0xff00)) { cycle_count += 2; } } return cycle_count; } void op_branch(bool branching) { char offset = (char) cpu_get_next_byte(); if (branching) { address counter = cpu_get_registers()->program_counter; address target = counter + offset; cpu_get_registers()->program_counter = target; } cpu_add_cycles(get_branch_cycle_count(branching, offset)); } void add_with_carry(byte value) { byte acc = cpu_get_registers()->accumulator; byte addition = acc + value; bool overflow = false; // Check for overflow if (addition < acc) { overflow = true; } // Add carry flag and check for overflow again byte result = addition + cpu_get_flag(CPU_STATUS_CARRY_MASK); if (result < addition) { overflow = true; } cpu_get_registers()->accumulator = acc; cpu_set_flag(overflow, CPU_STATUS_CARRY_MASK); cpu_set_flag(is_sign_overflow(acc, value, result), CPU_STATUS_OVERFLOW_MASK); set_acl_flags(result); } void op_ADC(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); add_with_carry(value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_AHX(AddressingMode addr_mode) { assert(false); } void op_ALR(AddressingMode addr_mode) { assert(false); } void op_ANC(AddressingMode addr_mode) { assert(false); } void op_AND(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_registers()->accumulator; byte result = acc & value; cpu_get_registers()->accumulator = result; set_acl_flags(result); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_ARR(AddressingMode addr_mode) { assert(false); } void op_ASL(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte result = value << 1; write_operand(operand, result); cpu_set_flag(value & 0x80, CPU_STATUS_CARRY_MASK); set_acl_flags(result); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } void op_AXS(AddressingMode addr_mode) { assert(false); } void op_BCC(AddressingMode addr_mode) { op_branch(!cpu_get_flag(CPU_STATUS_CARRY_MASK)); } void op_BCS(AddressingMode addr_mode) { op_branch(cpu_get_flag(CPU_STATUS_CARRY_MASK)); } void op_BEQ(AddressingMode addr_mode) { op_branch(cpu_get_flag(CPU_STATUS_ZERO_MASK)); } void op_BIT(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_registers()->accumulator; byte result = value & acc; cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); cpu_set_flag(result & 0x40, CPU_STATUS_OVERFLOW_MASK); cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); } void op_BMI(AddressingMode addr_mode) { op_branch(cpu_get_flag(CPU_STATUS_NEGATIVE_MASK)); } void op_BNE(AddressingMode addr_mode) { op_branch(!cpu_get_flag(CPU_STATUS_ZERO_MASK)); } void op_BPL(AddressingMode addr_mode) { op_branch(!cpu_get_flag(CPU_STATUS_NEGATIVE_MASK)); } // Stops program execution, useful for debugging void op_BRK(AddressingMode addr_mode) { cpu_stack_push_context(); // TODO Load IRQ interrupt vector in PC at $FFFE/F cpu_set_flag(true, CPU_STATUS_B_MASK); cpu_add_cycles(7); } void op_BVC(AddressingMode addr_mode) { op_branch(!cpu_get_flag(CPU_STATUS_OVERFLOW_MASK)); } void op_BVS(AddressingMode addr_mode) { op_branch(cpu_get_flag(CPU_STATUS_OVERFLOW_MASK)); } void op_CLC(AddressingMode addr_mode) { cpu_set_flag(false, CPU_STATUS_CARRY_MASK); cpu_add_cycles(2); } void op_CLD(AddressingMode addr_mode) { cpu_set_flag(false, CPU_STATUS_DECIMAL_MASK); cpu_add_cycles(2); } void op_CLI(AddressingMode addr_mode) { cpu_set_flag(false, CPU_STATUS_INTERRUPT_DISABLE_MASK); cpu_add_cycles(2); } void op_CLV(AddressingMode addr_mode) { cpu_set_flag(false, CPU_STATUS_OVERFLOW_MASK); cpu_add_cycles(2); } void op_CMP(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_registers()->accumulator; byte result = acc - value; cpu_set_flag(acc >= value, CPU_STATUS_CARRY_MASK); cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_CPX(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte x = cpu_get_registers()->x; byte result = x - value; cpu_set_flag(x >= value, CPU_STATUS_CARRY_MASK); cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_CPY(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte y = cpu_get_registers()->y; byte result = y - value; cpu_set_flag(y >= value, CPU_STATUS_CARRY_MASK); cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_DCP(AddressingMode addr_mode) { assert(false); } void op_DEC(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte result = value - 1; set_acl_flags(result); write_operand(operand, result); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_DEX(AddressingMode addr_mode) { byte x = cpu_get_registers()->x; byte result = x - 1; cpu_get_registers()->x = result; set_acl_flags(result); cpu_add_cycles(2); } void op_DEY(AddressingMode addr_mode) { byte y = cpu_get_registers()->y; byte result = y - 1; cpu_get_registers()->y = result; set_acl_flags(result); cpu_add_cycles(2); } void op_EOR(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_registers()->accumulator; acc ^= value; cpu_get_registers()->accumulator = acc; set_acl_flags(acc); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_INC(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); value += 1; write_operand(operand, value); set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } void op_INX(AddressingMode addr_mode) { byte x = cpu_get_registers()->x; x += 1; cpu_get_registers()->x = x; set_acl_flags(x); cpu_add_cycles(2); } void op_INY(AddressingMode addr_mode) { byte y = cpu_get_registers()->y; y += 1; cpu_get_registers()->y = y; set_acl_flags(y); cpu_add_cycles(2); } void op_ISC(AddressingMode addr_mode) { assert(false); } void op_JMP(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte addr = read_operand(operand); cpu_get_registers()->program_counter = addr; // TODO WN: Handle CPU bug? // > An original 6502 has does not correctly fetch the target address if the indirect vector falls on a page boundary (e.g. $xxFF where xx is any value from $00 to $FF). // > In this case fetches the LSB from $xxFF as expected but takes the MSB from $xx00. // > This is fixed in some later chips like the 65SC02 so for compatibility always ensure the indirect vector is not at the end of the page. } void op_JSR(AddressingMode addr_mode) { // Push the program counter on the stack address program_counter = cpu_get_registers()->program_counter - 1; cpu_stack_push(program_counter >> 8); cpu_stack_push(program_counter & 0xff); // Updates the program counter to the address in the operand address addr = decode_operand_addr(addr_mode, NULL); cpu_get_registers()->program_counter = addr; cpu_add_cycles(6); } void op_LAX(AddressingMode addr_mode) { assert(false); } void op_LDA(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); cpu_get_registers()->accumulator = value; set_acl_flags(value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_LDX(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); cpu_get_registers()->x = value; set_acl_flags(value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_LDY(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); cpu_get_registers()->y = value; set_acl_flags(value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_LSR(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); // Put bit 0 in the carry flag cpu_set_flag(value & 0x01, CPU_STATUS_CARRY_MASK); value >>= 1; write_operand(operand, value); set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } void op_NOP(AddressingMode addr_mode) { cpu_add_cycles(2); } void op_ORA(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_registers()->accumulator; acc |= value; cpu_get_registers()->accumulator = acc; set_acl_flags(acc); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_PHA(AddressingMode addr_mode) { byte acc = cpu_get_registers()->accumulator; cpu_stack_push(acc); cpu_add_cycles(3); } void op_PHP(AddressingMode addr_mode) { byte status = cpu_get_registers()->status; cpu_stack_push(status); cpu_add_cycles(3); } void op_PLA(AddressingMode addr_mode) { byte value = cpu_stack_pop(); cpu_get_registers()->accumulator = value; cpu_add_cycles(4); } void op_PLP(AddressingMode addr_mode) { byte value = cpu_stack_pop(); cpu_get_registers()->status = value; cpu_add_cycles(4); } void op_RLA(AddressingMode addr_mode) { assert(false); } void op_ROL(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte carry = cpu_get_flag(CPU_STATUS_CARRY_MASK); cpu_set_flag(value & 0x80, CPU_STATUS_CARRY_MASK); value = (value << 1) | carry; write_operand(operand, value); set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } void op_ROR(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte carry = cpu_get_flag(CPU_STATUS_CARRY_MASK); cpu_set_flag(value & 0x01, CPU_STATUS_CARRY_MASK); value = (value >> 1) | (carry << 7); write_operand(operand, value); set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } void op_RRA(AddressingMode addr_mode) { assert(false); } void op_RTI(AddressingMode addr_mode) { cpu_stack_pop_context(); cpu_add_cycles(6); } void op_RTS(AddressingMode addr_mode) { byte lo = cpu_stack_pop(); address pc = cpu_stack_pop() << 8; pc += lo; cpu_get_registers()->program_counter = pc - 1; cpu_add_cycles(6); } void op_SAX(AddressingMode addr_mode) { assert(false); } void op_SBC(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); add_with_carry(~value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_SEC(AddressingMode addr_mode) { cpu_set_flag(1, CPU_STATUS_CARRY_MASK); cpu_add_cycles(2); } void op_SED(AddressingMode addr_mode) { cpu_set_flag(1, CPU_STATUS_DECIMAL_MASK); cpu_add_cycles(2); } void op_SEI(AddressingMode addr_mode) { cpu_set_flag(1, CPU_STATUS_INTERRUPT_DISABLE_MASK); cpu_add_cycles(2); } void op_SHX(AddressingMode addr_mode) { assert(false); } void op_SHY(AddressingMode addr_mode) { assert(false); } void op_SLO(AddressingMode addr_mode) { assert(false); } void op_SRE(AddressingMode addr_mode) { assert(false); } void op_STA(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte acc = cpu_get_registers()->accumulator; assert(operand.type == OPERAND_TYPE_ADDRESS); cpu_push_byte(acc, operand.value); operand.is_page_crossing = true; cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_STP(AddressingMode addr_mode) { assert(false); } void op_STX(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte x = cpu_get_registers()->x; assert(operand.type == OPERAND_TYPE_ADDRESS); cpu_push_byte(x, operand.value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_STY(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte y = cpu_get_registers()->y; assert(operand.type == OPERAND_TYPE_ADDRESS); cpu_push_byte(y, operand.value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } void op_TAS(AddressingMode addr_mode) { assert(false); } void op_TAX(AddressingMode addr_mode) { byte acc = cpu_get_registers()->accumulator; cpu_get_registers()->x = acc; set_acl_flags(acc); cpu_add_cycles(2); } void op_TAY(AddressingMode addr_mode) { byte acc = cpu_get_registers()->accumulator; cpu_get_registers()->y = acc; set_acl_flags(acc); cpu_add_cycles(2); } void op_TSX(AddressingMode addr_mode) { byte value = cpu_stack_pop(); cpu_get_registers()->x = value; set_acl_flags(value); cpu_add_cycles(2); } void op_TXA(AddressingMode addr_mode) { byte x = cpu_get_registers()->x; cpu_get_registers()->accumulator = x; set_acl_flags(x); cpu_add_cycles(2); } void op_TXS(AddressingMode addr_mode) { byte x = cpu_get_registers()->x; cpu_stack_push(x); cpu_add_cycles(2); } void op_TYA(AddressingMode addr_mode) { byte y = cpu_get_registers()->y; cpu_get_registers()->accumulator = y; set_acl_flags(y); cpu_add_cycles(2); } void op_XAA(AddressingMode addr_mode) { assert(false); } void process_op_code(byte op) { switch (op) { // CTRL IS_OP_CODE(BRK, 0x00) IS_OP_CODE(PHP, 0x08) IS_OP_CODE(CLC, 0x18) IS_OP_CODE(PLP, 0x28) IS_OP_CODE(SEC, 0x38) IS_OP_CODE(RTI, 0x40) IS_OP_CODE(PHA, 0x48) IS_OP_CODE(CLI, 0x58) IS_OP_CODE(RTS, 0x60) IS_OP_CODE(PLA, 0x68) IS_OP_CODE(SEI, 0x78) IS_OP_CODE(DEY, 0x88) IS_OP_CODE(TYA, 0x98) IS_OP_CODE(TAY, 0xa8) IS_OP_CODE(CLV, 0xb8) IS_OP_CODE(INY, 0xc8) IS_OP_CODE(CLD, 0xd8) IS_OP_CODE(INX, 0xe8) IS_OP_CODE(SED, 0xf8) IS_OP_CODE_MODE(JSR, 0x20, ABSOLUTE) IS_OP_CODE_MODE(BIT, 0x24, ZERO_PAGE) IS_OP_CODE_MODE(BIT, 0x2c, ABSOLUTE) IS_OP_CODE_MODE(JMP, 0x4c, ABSOLUTE) IS_OP_CODE_MODE(JMP, 0x6c, ABSOLUTE_JUMP) IS_OP_CODE_MODE(STY, 0x84, ZERO_PAGE) IS_OP_CODE_MODE(STY, 0x8c, ABSOLUTE) IS_OP_CODE_MODE(STY, 0x94, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(SHY, 0x9c, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(LDY, 0xa0, IMMEDIATE) IS_OP_CODE_MODE(LDY, 0xa4, ZERO_PAGE) IS_OP_CODE_MODE(LDY, 0xac, ABSOLUTE) IS_OP_CODE_MODE(LDY, 0xb4, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(LDY, 0xbc, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(CPY, 0xc0, IMMEDIATE) IS_OP_CODE_MODE(CPY, 0xc4, ZERO_PAGE) IS_OP_CODE_MODE(CPY, 0xcc, ABSOLUTE) IS_OP_CODE_MODE(CPX, 0xe0, IMMEDIATE) IS_OP_CODE_MODE(CPX, 0xe4, ZERO_PAGE) IS_OP_CODE_MODE(CPX, 0xec, ABSOLUTE) IS_OP_CODE_MODE(BPL, 0x10, RELATIVE) IS_OP_CODE_MODE(BMI, 0x30, RELATIVE) IS_OP_CODE_MODE(BVC, 0x50, RELATIVE) IS_OP_CODE_MODE(BVS, 0x70, RELATIVE) IS_OP_CODE_MODE(BCC, 0x90, RELATIVE) IS_OP_CODE_MODE(BCS, 0xb0, RELATIVE) IS_OP_CODE_MODE(BNE, 0xd0, RELATIVE) IS_OP_CODE_MODE(BEQ, 0xf0, RELATIVE) IS_OP_CODE_MODE(NOP, 0x04, ZERO_PAGE) IS_OP_CODE_MODE(NOP, 0x0c, ABSOLUTE) IS_OP_CODE_MODE(NOP, 0x14, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x1c, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x34, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x3c, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x44, ZERO_PAGE) IS_OP_CODE_MODE(NOP, 0x54, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x5c, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x64, ZERO_PAGE) IS_OP_CODE_MODE(NOP, 0x74, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x7c, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x80, IMMEDIATE) IS_OP_CODE_MODE(NOP, 0xd4, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0xdc, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0xf4, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0xfc, ABSOLUTE_INDEXED_X) // ALU IS_ALU_OP_CODE(ORA) IS_ALU_OP_CODE(AND) IS_ALU_OP_CODE(EOR) IS_ALU_OP_CODE(ADC) IS_ALU_OP_CODE_NO_IMMEDIATE(STA) IS_ALU_OP_CODE(LDA) IS_ALU_OP_CODE(CMP) IS_ALU_OP_CODE(SBC) // RMW IS_RMW_OP_CODE(ASL, ORA) IS_RMW_OP_CODE(ROL, AND) IS_RMW_OP_CODE(LSR, EOR) IS_RMW_OP_CODE(ROR, ADC) IS_OP_CODE(STP, 0x02) IS_OP_CODE(STP, 0x12) IS_OP_CODE(NOP, 0x1a) IS_OP_CODE(STP, 0x22) IS_OP_CODE(STP, 0x32) IS_OP_CODE(NOP, 0x3a) IS_OP_CODE(STP, 0x42) IS_OP_CODE(STP, 0x52) IS_OP_CODE(NOP, 0x5a) IS_OP_CODE(STP, 0x62) IS_OP_CODE(STP, 0x72) IS_OP_CODE(NOP, 0x7a) IS_OP_CODE_MODE(NOP, 0x82, IMMEDIATE) IS_OP_CODE_MODE(STX, 0x86, ZERO_PAGE) IS_OP_CODE(TXA, 0x8a) IS_OP_CODE_MODE(STX, 0x8e, ABSOLUTE) IS_OP_CODE(STP, 0x92) IS_OP_CODE_MODE(STX, 0x96, ZERO_PAGE_INDEXED_Y) IS_OP_CODE(TSX, 0x9a) IS_OP_CODE_MODE(SHX, 0x9e, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(LDX, 0xa2, IMMEDIATE) IS_OP_CODE_MODE(LDX, 0xa6, ZERO_PAGE) IS_OP_CODE(TAX, 0xaa) IS_OP_CODE_MODE(LDX, 0xae, ABSOLUTE) IS_OP_CODE(STP, 0xb2) IS_OP_CODE_MODE(LDX, 0xb6, ZERO_PAGE_INDEXED_Y) IS_OP_CODE(TSX, 0xba) IS_OP_CODE_MODE(LDX, 0xbe, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(NOP, 0xc2, IMMEDIATE) IS_OP_CODE_MODE(DEC, 0xc6, ZERO_PAGE) IS_OP_CODE(DEX, 0xca) IS_OP_CODE_MODE(DEC, 0xce, ABSOLUTE) IS_OP_CODE(STP, 0xd2) IS_OP_CODE_MODE(DEC, 0xd6, ZERO_PAGE_INDEXED_Y) IS_OP_CODE(NOP, 0xda) IS_OP_CODE_MODE(DEC, 0xde, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(NOP, 0xe2, IMMEDIATE) IS_OP_CODE_MODE(INC, 0xe6, ZERO_PAGE) IS_OP_CODE(NOP, 0xea) // The official NOP IS_OP_CODE_MODE(INC, 0xee, ABSOLUTE) IS_OP_CODE(STP, 0xf2) IS_OP_CODE_MODE(INC, 0xf6, ZERO_PAGE_INDEXED_Y) IS_OP_CODE(NOP, 0xfa) IS_OP_CODE_MODE(INC, 0xfe, ABSOLUTE_INDEXED_X) // Unofficial IS_UNOFFICIAL_OP_CODE(SLO, ORA) IS_UNOFFICIAL_OP_CODE(RLA, AND) IS_UNOFFICIAL_OP_CODE(SRE, EOR) IS_UNOFFICIAL_OP_CODE(RRA, ADC) IS_UNOFFICIAL_OP_CODE(DCP, CMP) IS_UNOFFICIAL_OP_CODE(ISC, SBC) IS_OP_CODE_MODE(ANC, 0x0b, IMMEDIATE) IS_OP_CODE_MODE(ANC, 0x2b, IMMEDIATE) IS_OP_CODE_MODE(ALR, 0x4b, IMMEDIATE) IS_OP_CODE_MODE(ARR, 0x6b, IMMEDIATE) IS_OP_CODE_MODE(AXS, 0xcb, IMMEDIATE) IS_OP_CODE_MODE(SBC, 0xeb, IMMEDIATE) IS_OP_CODE_MODE(SAX, 0x83, INDIRECT_X) IS_OP_CODE_MODE(SAX, 0x87, ZERO_PAGE) IS_OP_CODE_MODE(XAA, 0x8b, IMMEDIATE) IS_OP_CODE_MODE(SAX, 0x8f, ABSOLUTE) IS_OP_CODE_MODE(AHX, 0x93, INDIRECT_Y) IS_OP_CODE_MODE(SAX, 0x97, ZERO_PAGE_INDEXED_Y) IS_OP_CODE_MODE(TAS, 0x9b, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(AHX, 0x9f, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(LAX, 0xa3, INDIRECT_X) IS_OP_CODE_MODE(LAX, 0xa7, ZERO_PAGE) IS_OP_CODE_MODE(LAX, 0xab, IMMEDIATE) IS_OP_CODE_MODE(LAX, 0xaf, ABSOLUTE) IS_OP_CODE_MODE(LAX, 0xb3, INDIRECT_Y) IS_OP_CODE_MODE(LAX, 0xb7, ZERO_PAGE_INDEXED_Y) IS_OP_CODE_MODE(LAX, 0xbb, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(LAX, 0xbf, ABSOLUTE_INDEXED_Y) } }