1109 lines
33 KiB
C
1109 lines
33 KiB
C
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <log.h>
|
|
|
|
#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(system, 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(System *system, Operand operand, byte value) {
|
|
switch (operand.type) {
|
|
case OPERAND_TYPE_ACCUMULATOR:
|
|
system->cpu.accumulator = value;
|
|
break;
|
|
case OPERAND_TYPE_ADDRESS:
|
|
mem_set_byte(system, 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(System *system, byte result) {
|
|
cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0);
|
|
cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, result & 0x80);
|
|
}
|
|
|
|
byte get_branch_cycle_count(System *system, bool branching, char offset) {
|
|
address target = system->cpu.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(System *system, bool branching) {
|
|
char offset = (char) cpu_get_next_byte(system);
|
|
|
|
if (branching) {
|
|
address counter = system->cpu.program_counter;
|
|
address target = counter + offset;
|
|
system->cpu.program_counter = target;
|
|
}
|
|
|
|
cpu_add_cycles(system, get_branch_cycle_count(system, branching, offset));
|
|
}
|
|
|
|
void add_with_carry(System *system, byte value) {
|
|
byte acc = system->cpu.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(system, CPU_STATUS_CARRY_MASK);
|
|
if (result < addition) {
|
|
overflow = true;
|
|
}
|
|
|
|
system->cpu.accumulator = result;
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, overflow);
|
|
cpu_set_flag(system, CPU_STATUS_OVERFLOW_MASK, is_sign_overflow(acc, value, result));
|
|
|
|
set_acl_flags(system, result);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ADC(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
|
|
byte value = read_operand(system, operand);
|
|
add_with_carry(system, value);
|
|
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_AHX(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ALR(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ANC(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_AND(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
byte result = acc & value;
|
|
system->cpu.accumulator = result;
|
|
|
|
set_acl_flags(system, result);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ARR(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ASL(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
byte result = value << 1;
|
|
write_operand(system, operand, result);
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x80);
|
|
set_acl_flags(system, result);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_AXS(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BCC(System *system, AddressingMode addr_mode) {
|
|
op_branch(system, !cpu_get_flag(system, CPU_STATUS_CARRY_MASK));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BCS(System *system, AddressingMode addr_mode) {
|
|
op_branch(system, cpu_get_flag(system, CPU_STATUS_CARRY_MASK));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BEQ(System *system, AddressingMode addr_mode) {
|
|
op_branch(system, cpu_get_flag(system, CPU_STATUS_ZERO_MASK));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BIT(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
byte result = value & acc;
|
|
|
|
cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0);
|
|
cpu_set_flag(system, CPU_STATUS_OVERFLOW_MASK, value & 0x40);
|
|
cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, value & 0x80);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BMI(System *system, AddressingMode addr_mode) {
|
|
op_branch(system, cpu_get_flag(system, CPU_STATUS_NEGATIVE_MASK));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BNE(System *system, AddressingMode addr_mode) {
|
|
op_branch(system, !cpu_get_flag(system, CPU_STATUS_ZERO_MASK));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BPL(System *system, AddressingMode addr_mode) {
|
|
op_branch(system, !cpu_get_flag(system, CPU_STATUS_NEGATIVE_MASK));
|
|
}
|
|
|
|
// Stops program execution, useful for debugging
|
|
__attribute__((unused))
|
|
void op_BRK(System *system, AddressingMode addr_mode) {
|
|
cpu_stack_push_context(system);
|
|
|
|
// TODO Load IRQ interrupt vector in PC at $FFFE/F
|
|
|
|
assert(false);
|
|
|
|
cpu_set_flag(system, CPU_STATUS_B_MASK, true);
|
|
cpu_add_cycles(system, 7);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BVC(System *system, AddressingMode addr_mode) {
|
|
op_branch(system, !cpu_get_flag(system, CPU_STATUS_OVERFLOW_MASK));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_BVS(System *system, AddressingMode addr_mode) {
|
|
op_branch(system, cpu_get_flag(system, CPU_STATUS_OVERFLOW_MASK));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_CLC(System *system, AddressingMode addr_mode) {
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, false);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_CLD(System *system, AddressingMode addr_mode) {
|
|
cpu_set_flag(system, CPU_STATUS_DECIMAL_MASK, false);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_CLI(System *system, AddressingMode addr_mode) {
|
|
cpu_set_flag(system, CPU_STATUS_INTERRUPT_DISABLE_MASK, false);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_CLV(System *system, AddressingMode addr_mode) {
|
|
cpu_set_flag(system, CPU_STATUS_OVERFLOW_MASK, false);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_CMP(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
byte result = acc - value;
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, acc >= value);
|
|
cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0);
|
|
cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, result & 0x80);
|
|
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_CPX(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte x = system->cpu.x;
|
|
|
|
byte result = x - value;
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, x >= value);
|
|
cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0);
|
|
cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, result & 0x80);
|
|
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_CPY(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte y = system->cpu.y;
|
|
|
|
byte result = y - value;
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, y >= value);
|
|
cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0);
|
|
cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, result & 0x80);
|
|
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_DCP(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
byte result = value - 1;
|
|
byte cmp_result = acc - result;
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, acc >= value);
|
|
cpu_set_flag(system, CPU_STATUS_ZERO_MASK, cmp_result == 0);
|
|
cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, cmp_result & 0x80);
|
|
|
|
write_operand(system, 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(system, cycle_count);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_DEC(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
byte result = value - 1;
|
|
|
|
set_acl_flags(system, result);
|
|
write_operand(system, operand, result);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_DEX(System *system, AddressingMode addr_mode) {
|
|
byte x = system->cpu.x;
|
|
|
|
byte result = x - 1;
|
|
system->cpu.x = result;
|
|
|
|
set_acl_flags(system, result);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_DEY(System *system, AddressingMode addr_mode) {
|
|
byte y = system->cpu.y;
|
|
|
|
byte result = y - 1;
|
|
system->cpu.y = result;
|
|
|
|
set_acl_flags(system, result);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_EOR(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
acc ^= value;
|
|
system->cpu.accumulator = acc;
|
|
|
|
set_acl_flags(system, acc);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_INC(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
value += 1;
|
|
write_operand(system, operand, value);
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_INX(System *system, AddressingMode addr_mode) {
|
|
byte x = system->cpu.x;
|
|
|
|
x += 1;
|
|
system->cpu.x = x;
|
|
|
|
set_acl_flags(system, x);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_INY(System *system, AddressingMode addr_mode) {
|
|
byte y = system->cpu.y;
|
|
|
|
y += 1;
|
|
system->cpu.y = y;
|
|
|
|
set_acl_flags(system, y);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ISC(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
value += 1;
|
|
write_operand(system, operand, value);
|
|
add_with_carry(system, ~value);
|
|
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_JMP(System *system, AddressingMode addr_mode) {
|
|
word addr = decode_operand_addr(system, addr_mode, NULL);
|
|
|
|
system->cpu.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(system, cycle_count);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_JSR(System *system, AddressingMode addr_mode) {
|
|
// Push the program counter on the stack
|
|
address program_counter = system->cpu.program_counter + 1;
|
|
cpu_stack_push(system, program_counter >> 8);
|
|
cpu_stack_push(system, program_counter & 0xff);
|
|
|
|
// Updates the program counter to the address in the operand
|
|
address addr = decode_operand_addr(system, addr_mode, NULL);
|
|
system->cpu.program_counter = addr;
|
|
|
|
cpu_add_cycles(system, 6);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_LAS(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_LAX(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
system->cpu.accumulator = value;
|
|
system->cpu.x = value;
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_LDA(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
system->cpu.accumulator = value;
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_LDX(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
system->cpu.x = value;
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_LDY(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
system->cpu.y = value;
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_LSR(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
// Put bit 0 in the carry flag
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x01);
|
|
|
|
value >>= 1;
|
|
write_operand(system, operand, value);
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_NOP(System *system, AddressingMode addr_mode) {
|
|
if (addr_mode != ADDR_MODE_IMPLICIT) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
} else {
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ORA(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
acc |= value;
|
|
system->cpu.accumulator = acc;
|
|
|
|
set_acl_flags(system, acc);
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_PHA(System *system, AddressingMode addr_mode) {
|
|
byte acc = system->cpu.accumulator;
|
|
cpu_stack_push(system, acc);
|
|
|
|
cpu_add_cycles(system, 3);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_PHP(System *system, AddressingMode addr_mode) {
|
|
byte status = system->cpu.status;
|
|
cpu_stack_push(system, status);
|
|
|
|
// cpu_set_flag(system, CPU_STATUS_B_MASK, true);
|
|
|
|
cpu_add_cycles(system, 3);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_PLA(System *system, AddressingMode addr_mode) {
|
|
byte value = cpu_stack_pop(system);
|
|
system->cpu.accumulator = value;
|
|
|
|
set_acl_flags(system, value);
|
|
|
|
cpu_add_cycles(system, 4);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_PLP(System *system, AddressingMode addr_mode) {
|
|
byte value = cpu_stack_pop(system);
|
|
|
|
value &= 0xef; // The B mask cannot be set as it is a CPU signal
|
|
value |= 0x20; // This value is always set
|
|
|
|
system->cpu.status = value;
|
|
|
|
cpu_add_cycles(system, 4);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_RLA(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte carry = cpu_get_flag(system, CPU_STATUS_CARRY_MASK);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x80);
|
|
|
|
value = (value << 1) | carry;
|
|
byte and_result = acc & value;
|
|
system->cpu.accumulator = and_result;
|
|
|
|
write_operand(system, operand, value);
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ROL(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte carry = cpu_get_flag(system, CPU_STATUS_CARRY_MASK);
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x80);
|
|
value = (value << 1) | carry;
|
|
write_operand(system, operand, value);
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_ROR(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte carry = cpu_get_flag(system, CPU_STATUS_CARRY_MASK);
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x01);
|
|
value = (value >> 1) | (carry << 7);
|
|
write_operand(system, operand, value);
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_RRA(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte carry = cpu_get_flag(system, CPU_STATUS_CARRY_MASK);
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x01);
|
|
value = (value >> 1) | (carry << 7);
|
|
add_with_carry(system, value);
|
|
write_operand(system, operand, value);
|
|
|
|
// set_acl_flags(system, value);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_RTI(System *system, AddressingMode addr_mode) {
|
|
cpu_stack_pop_context(system);
|
|
cpu_add_cycles(system, 6);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_RTS(System *system, AddressingMode addr_mode) {
|
|
byte lo = cpu_stack_pop(system);
|
|
address pc = cpu_stack_pop(system) << 8;
|
|
pc += lo;
|
|
|
|
system->cpu.program_counter = pc + 1;
|
|
cpu_add_cycles(system, 6);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SAX(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
|
|
byte x = system->cpu.x;
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
byte result = acc & x;
|
|
write_operand(system, operand, result);
|
|
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SBC(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
|
|
add_with_carry(system, ~value);
|
|
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SEC(System *system, AddressingMode addr_mode) {
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, true);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SED(System *system, AddressingMode addr_mode) {
|
|
cpu_set_flag(system, CPU_STATUS_DECIMAL_MASK, true);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SEI(System *system, AddressingMode addr_mode) {
|
|
cpu_set_flag(system, CPU_STATUS_INTERRUPT_DISABLE_MASK, true);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SHX(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SHY(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SLO(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
byte result = value << 1;
|
|
acc |= result;
|
|
system->cpu.accumulator = acc;
|
|
|
|
write_operand(system, operand, result);
|
|
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x80);
|
|
set_acl_flags(system, acc);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_SRE(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte value = read_operand(system, operand);
|
|
byte acc = system->cpu.accumulator;
|
|
|
|
// Put bit 0 in the carry flag
|
|
cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x01);
|
|
|
|
value >>= 1;
|
|
acc ^= value;
|
|
system->cpu.accumulator = acc;
|
|
write_operand(system, operand, value);
|
|
|
|
set_acl_flags(system, acc);
|
|
cpu_add_cycles(system, get_shift_cycle_count(addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_STA(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte acc = system->cpu.accumulator;
|
|
assert(operand.type == OPERAND_TYPE_ADDRESS);
|
|
|
|
mem_set_byte(system, operand.value, acc);
|
|
|
|
operand.is_page_crossing = true;
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_STP(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_STX(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte x = system->cpu.x;
|
|
assert(operand.type == OPERAND_TYPE_ADDRESS);
|
|
|
|
mem_set_byte(system, operand.value, x);
|
|
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_STY(System *system, AddressingMode addr_mode) {
|
|
Operand operand = decode_operand(system, addr_mode);
|
|
byte y = system->cpu.y;
|
|
assert(operand.type == OPERAND_TYPE_ADDRESS);
|
|
|
|
mem_set_byte(system, operand.value, y);
|
|
|
|
cpu_add_cycles(system, get_cycle_count(operand, addr_mode));
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_TAS(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_TAX(System *system, AddressingMode addr_mode) {
|
|
byte acc = system->cpu.accumulator;
|
|
system->cpu.x = acc;
|
|
|
|
set_acl_flags(system, acc);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_TAY(System *system, AddressingMode addr_mode) {
|
|
byte acc = system->cpu.accumulator;
|
|
system->cpu.y = acc;
|
|
|
|
set_acl_flags(system, acc);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_TSX(System *system, AddressingMode addr_mode) {
|
|
byte value = system->cpu.stack_pointer;
|
|
system->cpu.x = value;
|
|
|
|
set_acl_flags(system, value);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_TXA(System *system, AddressingMode addr_mode) {
|
|
byte x = system->cpu.x;
|
|
system->cpu.accumulator = x;
|
|
|
|
set_acl_flags(system, x);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_TXS(System *system, AddressingMode addr_mode) {
|
|
byte x = system->cpu.x;
|
|
system->cpu.stack_pointer = x;
|
|
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_TYA(System *system, AddressingMode addr_mode) {
|
|
byte y = system->cpu.y;
|
|
system->cpu.accumulator = y;
|
|
|
|
set_acl_flags(system, y);
|
|
cpu_add_cycles(system, 2);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
void op_XAA(System *system, AddressingMode addr_mode) {
|
|
assert(false);
|
|
}
|
|
|
|
void process_op_code(System *system, 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_JUMP)
|
|
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);
|
|
}
|
|
} |