#include #include #include "op.h" #include "cpu.h" #include "decoding.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: \ 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, ACCUMULATOR) \ 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) void write_operand(Operand operand, byte value) { switch (operand.type) { case OPERAND_TYPE_ACCUMULATOR: cpu_get_state()->accumulator = value; break; case OPERAND_TYPE_ADDRESS: mem_set_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_IMMEDIATE: 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: case ADDR_MODE_ABSOLUTE_INDEXED_Y: return 7; case ADDR_MODE_INDIRECT_X: case ADDR_MODE_INDIRECT_Y: return 8; default: assert(false); } } void set_acl_flags(byte result) { cpu_set_flag(CPU_STATUS_ZERO_MASK, result == 0); cpu_set_flag(CPU_STATUS_NEGATIVE_MASK, result & 0x80); } byte get_branch_cycle_count(bool branching, char offset) { address target = cpu_get_state()->program_counter; byte cycle_count = 2; if (branching) { cycle_count += 1; if ((target & 0xff00) != ((target - offset) & 0xff00)) { cycle_count += 2; } } return cycle_count; } __attribute__((unused)) void op_branch(bool branching) { char offset = (char) cpu_get_next_byte(); if (branching) { address counter = cpu_get_state()->program_counter; address target = counter + offset; cpu_get_state()->program_counter = target; } cpu_add_cycles(get_branch_cycle_count(branching, offset)); } void add_with_carry(byte value) { byte acc = cpu_get_state()->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_state()->accumulator = result; cpu_set_flag(CPU_STATUS_CARRY_MASK, overflow); cpu_set_flag(CPU_STATUS_OVERFLOW_MASK, is_sign_overflow(acc, value, result)); set_acl_flags(result); } __attribute__((unused)) 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)); } __attribute__((unused)) void op_AHX(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_ALR(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_ANC(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_AND(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_state()->accumulator; byte result = acc & value; cpu_get_state()->accumulator = result; set_acl_flags(result); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_ARR(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) 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(CPU_STATUS_CARRY_MASK, value & 0x80); set_acl_flags(result); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) void op_AXS(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_BCC(AddressingMode addr_mode) { op_branch(!cpu_get_flag(CPU_STATUS_CARRY_MASK)); } __attribute__((unused)) void op_BCS(AddressingMode addr_mode) { op_branch(cpu_get_flag(CPU_STATUS_CARRY_MASK)); } __attribute__((unused)) void op_BEQ(AddressingMode addr_mode) { op_branch(cpu_get_flag(CPU_STATUS_ZERO_MASK)); } __attribute__((unused)) void op_BIT(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_state()->accumulator; byte result = value & acc; cpu_set_flag(CPU_STATUS_ZERO_MASK, result == 0); cpu_set_flag(CPU_STATUS_OVERFLOW_MASK, value & 0x40); cpu_set_flag(CPU_STATUS_NEGATIVE_MASK, value & 0x80); } __attribute__((unused)) void op_BMI(AddressingMode addr_mode) { op_branch(cpu_get_flag(CPU_STATUS_NEGATIVE_MASK)); } __attribute__((unused)) void op_BNE(AddressingMode addr_mode) { op_branch(!cpu_get_flag(CPU_STATUS_ZERO_MASK)); } __attribute__((unused)) void op_BPL(AddressingMode addr_mode) { op_branch(!cpu_get_flag(CPU_STATUS_NEGATIVE_MASK)); } // Stops program execution, useful for debugging __attribute__((unused)) void op_BRK(AddressingMode addr_mode) { cpu_stack_push_context(); // TODO Load IRQ interrupt vector in PC at $FFFE/F assert(false); cpu_set_flag(CPU_STATUS_B_MASK, true); cpu_add_cycles(7); } __attribute__((unused)) void op_BVC(AddressingMode addr_mode) { op_branch(!cpu_get_flag(CPU_STATUS_OVERFLOW_MASK)); } __attribute__((unused)) void op_BVS(AddressingMode addr_mode) { op_branch(cpu_get_flag(CPU_STATUS_OVERFLOW_MASK)); } __attribute__((unused)) void op_CLC(AddressingMode addr_mode) { cpu_set_flag(CPU_STATUS_CARRY_MASK, false); cpu_add_cycles(2); } __attribute__((unused)) void op_CLD(AddressingMode addr_mode) { cpu_set_flag(CPU_STATUS_DECIMAL_MASK, false); cpu_add_cycles(2); } __attribute__((unused)) void op_CLI(AddressingMode addr_mode) { cpu_set_flag(CPU_STATUS_INTERRUPT_DISABLE_MASK, false); cpu_add_cycles(2); } __attribute__((unused)) void op_CLV(AddressingMode addr_mode) { cpu_set_flag(CPU_STATUS_OVERFLOW_MASK, false); cpu_add_cycles(2); } __attribute__((unused)) void op_CMP(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_state()->accumulator; byte result = acc - value; cpu_set_flag(CPU_STATUS_CARRY_MASK, acc >= value); cpu_set_flag(CPU_STATUS_ZERO_MASK, result == 0); cpu_set_flag(CPU_STATUS_NEGATIVE_MASK, result & 0x80); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_CPX(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte x = cpu_get_state()->x; byte result = x - value; cpu_set_flag(CPU_STATUS_CARRY_MASK, x >= value); cpu_set_flag(CPU_STATUS_ZERO_MASK, result == 0); cpu_set_flag(CPU_STATUS_NEGATIVE_MASK, result & 0x80); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_CPY(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte y = cpu_get_state()->y; byte result = y - value; cpu_set_flag(CPU_STATUS_CARRY_MASK, y >= value); cpu_set_flag(CPU_STATUS_ZERO_MASK, result == 0); cpu_set_flag(CPU_STATUS_NEGATIVE_MASK, result & 0x80); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_DCP(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_state()->accumulator; byte result = value - 1; byte cmp_result = acc - result; cpu_set_flag(CPU_STATUS_CARRY_MASK, acc >= value); cpu_set_flag(CPU_STATUS_ZERO_MASK, cmp_result == 0); cpu_set_flag(CPU_STATUS_NEGATIVE_MASK, cmp_result & 0x80); write_operand(operand, result); byte cycle_count; switch (addr_mode) { case ADDR_MODE_ZERO_PAGE: cycle_count = 5; break; case ADDR_MODE_ZERO_PAGE_INDEXED_X: case ADDR_MODE_ABSOLUTE: cycle_count = 6; break; case ADDR_MODE_ABSOLUTE_INDEXED_X: case ADDR_MODE_ABSOLUTE_INDEXED_Y: cycle_count = 7; break; case ADDR_MODE_INDIRECT_X: case ADDR_MODE_INDIRECT_Y: cycle_count = 8; break; default: assert(false); } cpu_add_cycles(cycle_count); } __attribute__((unused)) 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)); } __attribute__((unused)) void op_DEX(AddressingMode addr_mode) { byte x = cpu_get_state()->x; byte result = x - 1; cpu_get_state()->x = result; set_acl_flags(result); cpu_add_cycles(2); } __attribute__((unused)) void op_DEY(AddressingMode addr_mode) { byte y = cpu_get_state()->y; byte result = y - 1; cpu_get_state()->y = result; set_acl_flags(result); cpu_add_cycles(2); } __attribute__((unused)) void op_EOR(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_state()->accumulator; acc ^= value; cpu_get_state()->accumulator = acc; set_acl_flags(acc); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) 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)); } __attribute__((unused)) void op_INX(AddressingMode addr_mode) { byte x = cpu_get_state()->x; x += 1; cpu_get_state()->x = x; set_acl_flags(x); cpu_add_cycles(2); } __attribute__((unused)) void op_INY(AddressingMode addr_mode) { byte y = cpu_get_state()->y; y += 1; cpu_get_state()->y = y; set_acl_flags(y); cpu_add_cycles(2); } __attribute__((unused)) void op_ISC(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); value += 1; write_operand(operand, value); add_with_carry(~value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) void op_JMP(AddressingMode addr_mode) { word addr = decode_operand_addr(addr_mode, NULL); cpu_get_state()->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. int cycle_count = 3; if (addr_mode == ADDR_MODE_INDIRECT_JUMP) { cycle_count = 5; } cpu_add_cycles(cycle_count); } __attribute__((unused)) void op_JSR(AddressingMode addr_mode) { // Push the program counter on the stack address program_counter = cpu_get_state()->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_state()->program_counter = addr; cpu_add_cycles(6); } __attribute__((unused)) void op_LAS(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_LAX(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); cpu_get_state()->accumulator = value; cpu_get_state()->x = value; set_acl_flags(value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_LDA(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); cpu_get_state()->accumulator = value; set_acl_flags(value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_LDX(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); cpu_get_state()->x = value; set_acl_flags(value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_LDY(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); cpu_get_state()->y = value; set_acl_flags(value); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) 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(CPU_STATUS_CARRY_MASK, value & 0x01); value >>= 1; write_operand(operand, value); set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) void op_NOP(AddressingMode addr_mode) { if (addr_mode != ADDR_MODE_IMPLICIT) { Operand operand = decode_operand(addr_mode); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } else { cpu_add_cycles(2); } } __attribute__((unused)) void op_ORA(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_state()->accumulator; acc |= value; cpu_get_state()->accumulator = acc; set_acl_flags(acc); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_PHA(AddressingMode addr_mode) { byte acc = cpu_get_state()->accumulator; cpu_stack_push(acc); cpu_add_cycles(3); } __attribute__((unused)) void op_PHP(AddressingMode addr_mode) { byte status = cpu_get_state()->status; cpu_stack_push(status); // cpu_set_flag(CPU_STATUS_B_MASK, true); cpu_add_cycles(3); } __attribute__((unused)) void op_PLA(AddressingMode addr_mode) { byte value = cpu_stack_pop(); cpu_get_state()->accumulator = value; set_acl_flags(value); cpu_add_cycles(4); } __attribute__((unused)) void op_PLP(AddressingMode addr_mode) { byte value = cpu_stack_pop(); value &= 0xef; // The B mask cannot be set as it is a CPU signal value |= 0x20; // This value is always set cpu_get_state()->status = value; cpu_add_cycles(4); } __attribute__((unused)) void op_RLA(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte carry = cpu_get_flag(CPU_STATUS_CARRY_MASK); byte acc = cpu_get_state()->accumulator; cpu_set_flag(CPU_STATUS_CARRY_MASK, value & 0x80); value = (value << 1) | carry; byte and_result = acc & value; cpu_get_state()->accumulator = and_result; write_operand(operand, value); set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) 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(CPU_STATUS_CARRY_MASK, value & 0x80); value = (value << 1) | carry; write_operand(operand, value); set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) 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(CPU_STATUS_CARRY_MASK, value & 0x01); value = (value >> 1) | (carry << 7); write_operand(operand, value); set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) void op_RRA(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(CPU_STATUS_CARRY_MASK, value & 0x01); value = (value >> 1) | (carry << 7); add_with_carry(value); write_operand(operand, value); // set_acl_flags(value); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) void op_RTI(AddressingMode addr_mode) { cpu_stack_pop_context(); cpu_add_cycles(6); } __attribute__((unused)) void op_RTS(AddressingMode addr_mode) { byte lo = cpu_stack_pop(); address pc = cpu_stack_pop() << 8; pc += lo; cpu_get_state()->program_counter = pc + 1; cpu_add_cycles(6); } __attribute__((unused)) void op_SAX(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte x = cpu_get_state()->x; byte acc = cpu_get_state()->accumulator; byte result = acc & x; write_operand(operand, result); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) 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)); } __attribute__((unused)) void op_SEC(AddressingMode addr_mode) { cpu_set_flag(CPU_STATUS_CARRY_MASK, true); cpu_add_cycles(2); } __attribute__((unused)) void op_SED(AddressingMode addr_mode) { cpu_set_flag(CPU_STATUS_DECIMAL_MASK, true); cpu_add_cycles(2); } __attribute__((unused)) void op_SEI(AddressingMode addr_mode) { cpu_set_flag(CPU_STATUS_INTERRUPT_DISABLE_MASK, true); cpu_add_cycles(2); } __attribute__((unused)) void op_SHX(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_SHY(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_SLO(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_state()->accumulator; byte result = value << 1; acc |= result; cpu_get_state()->accumulator = acc; write_operand(operand, result); cpu_set_flag(CPU_STATUS_CARRY_MASK, value & 0x80); set_acl_flags(acc); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) void op_SRE(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte value = read_operand(operand); byte acc = cpu_get_state()->accumulator; // Put bit 0 in the carry flag cpu_set_flag(CPU_STATUS_CARRY_MASK, value & 0x01); value >>= 1; acc ^= value; cpu_get_state()->accumulator = acc; write_operand(operand, value); set_acl_flags(acc); cpu_add_cycles(get_shift_cycle_count(addr_mode)); } __attribute__((unused)) void op_STA(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte acc = cpu_get_state()->accumulator; assert(operand.type == OPERAND_TYPE_ADDRESS); mem_set_byte(operand.value, acc); operand.is_page_crossing = true; cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_STP(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_STX(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte x = cpu_get_state()->x; assert(operand.type == OPERAND_TYPE_ADDRESS); mem_set_byte(operand.value, x); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_STY(AddressingMode addr_mode) { Operand operand = decode_operand(addr_mode); byte y = cpu_get_state()->y; assert(operand.type == OPERAND_TYPE_ADDRESS); mem_set_byte(operand.value, y); cpu_add_cycles(get_cycle_count(operand, addr_mode)); } __attribute__((unused)) void op_TAS(AddressingMode addr_mode) { assert(false); } __attribute__((unused)) void op_TAX(AddressingMode addr_mode) { byte acc = cpu_get_state()->accumulator; cpu_get_state()->x = acc; set_acl_flags(acc); cpu_add_cycles(2); } __attribute__((unused)) void op_TAY(AddressingMode addr_mode) { byte acc = cpu_get_state()->accumulator; cpu_get_state()->y = acc; set_acl_flags(acc); cpu_add_cycles(2); } __attribute__((unused)) void op_TSX(AddressingMode addr_mode) { byte value = cpu_get_state()->stack_pointer; cpu_get_state()->x = value; set_acl_flags(value); cpu_add_cycles(2); } __attribute__((unused)) void op_TXA(AddressingMode addr_mode) { byte x = cpu_get_state()->x; cpu_get_state()->accumulator = x; set_acl_flags(x); cpu_add_cycles(2); } __attribute__((unused)) void op_TXS(AddressingMode addr_mode) { byte x = cpu_get_state()->x; cpu_get_state()->stack_pointer = x; cpu_add_cycles(2); } __attribute__((unused)) void op_TYA(AddressingMode addr_mode) { byte y = cpu_get_state()->y; cpu_get_state()->accumulator = y; set_acl_flags(y); cpu_add_cycles(2); } __attribute__((unused)) 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, INDIRECT_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, ABSOLUTE_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(TXS, 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_X) IS_OP_CODE(NOP, 0xda) IS_OP_CODE_MODE(DEC, 0xde, ABSOLUTE_INDEXED_X) 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_X) 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(LAS, 0xbb, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(LAX, 0xbf, ABSOLUTE_INDEXED_Y) default: assert(false); } } AddressingMode get_op_addr_mode(byte op_code) { switch (op_code) { case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0x20: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x4c: case 0x4d: case 0x4e: case 0x4f: case 0x6d: case 0x6e: case 0x6f: case 0x8c: case 0x8d: case 0x8e: case 0x8f: case 0xac: case 0xad: case 0xae: case 0xaf: case 0xcc: case 0xcd: case 0xce: case 0xcf: case 0xec: case 0xed: case 0xee: case 0xef: return ADDR_MODE_ABSOLUTE; case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x3c: case 0x3d: case 0x3e: case 0x3f: case 0x5c: case 0x5d: case 0x5e: case 0x5f: case 0x7c: case 0x7d: case 0x7e: case 0x7f: case 0x9c: case 0x9d: case 0xbc: case 0xbd: case 0xdc: case 0xdd: case 0xde: case 0xdf: case 0xfc: case 0xfd: case 0xfe: case 0xff: return ADDR_MODE_ABSOLUTE_INDEXED_X; case 0x19: case 0x1b: case 0x39: case 0x3b: case 0x59: case 0x5b: case 0x79: case 0x7b: case 0x99: case 0x9b: case 0x9e: case 0x9f: case 0xb9: case 0xbb: case 0xbe: case 0xbf: case 0xd9: case 0xdb: case 0xf9: case 0xfb: return ADDR_MODE_ABSOLUTE_INDEXED_Y; case 0x0a: case 0x2a: case 0x4a: case 0x6a: return ADDR_MODE_ACCUMULATOR; case 0x09: case 0x0b: case 0x29: case 0x2b: case 0x49: case 0x4b: case 0x69: case 0x6b: case 0x80: case 0x82: case 0x89: case 0x8b: case 0xa0: case 0xa2: case 0xa9: case 0xab: case 0xc0: case 0xc2: case 0xc9: case 0xcb: case 0xe0: case 0xe2: case 0xe9: case 0xeb: return ADDR_MODE_IMMEDIATE; case 0x00: case 0x02: case 0x08: case 0x12: case 0x18: case 0x1a: case 0x22: case 0x28: case 0x32: case 0x38: case 0x3a: case 0x40: case 0x42: case 0x48: case 0x52: case 0x58: case 0x5a: case 0x60: case 0x62: case 0x68: case 0x72: case 0x78: case 0x7a: case 0x88: case 0x8a: case 0x92: case 0x98: case 0x9a: case 0xa8: case 0xaa: case 0xb2: case 0xb8: case 0xba: case 0xc8: case 0xca: case 0xd2: case 0xd8: case 0xda: case 0xe8: case 0xea: case 0xf2: case 0xf8: case 0xfa: return ADDR_MODE_IMPLICIT; case 0x01: case 0x03: case 0x21: case 0x23: case 0x41: case 0x43: case 0x61: case 0x63: case 0x81: case 0x83: case 0xa1: case 0xa3: case 0xc1: case 0xc3: case 0xe1: case 0xe3: return ADDR_MODE_INDIRECT_X; case 0x6c: return ADDR_MODE_INDIRECT_JUMP; case 0x11: case 0x13: case 0x31: case 0x33: case 0x51: case 0x53: case 0x71: case 0x73: case 0x91: case 0x93: case 0xb1: case 0xb3: case 0xd1: case 0xd3: case 0xf1: case 0xf3: return ADDR_MODE_INDIRECT_Y; case 0x10: case 0x30: case 0x50: case 0x70: case 0x90: case 0xb0: case 0xd0: case 0xf0: return ADDR_MODE_RELATIVE; case 0x04: case 0x05: case 0x06: case 0x07: case 0x24: case 0x25: case 0x26: case 0x27: case 0x44: case 0x45: case 0x46: case 0x47: case 0x64: case 0x65: case 0x66: case 0x67: case 0x84: case 0x85: case 0x86: case 0x87: case 0xa4: case 0xa5: case 0xa6: case 0xa7: case 0xc4: case 0xc5: case 0xc6: case 0xc7: case 0xe4: case 0xe5: case 0xe6: case 0xe7: return ADDR_MODE_ZERO_PAGE; case 0x14: case 0x15: case 0x16: case 0x17: case 0x34: case 0x35: case 0x36: case 0x37: case 0x54: case 0x55: case 0x56: case 0x57: case 0x74: case 0x75: case 0x76: case 0x77: case 0x94: case 0x95: case 0xb4: case 0xb5: case 0xd4: case 0xd5: case 0xd6: case 0xd7: case 0xf4: case 0xf5: case 0xf6: case 0xf7: return ADDR_MODE_ZERO_PAGE_INDEXED_X; case 0x96: case 0x97: case 0xb6: case 0xb7: return ADDR_MODE_ZERO_PAGE_INDEXED_Y; default: assert(false); } }