864 lines
25 KiB
Rust
864 lines
25 KiB
Rust
use crate::cpu::op::{AddressingMode, Instruction, Operand, OperationType};
|
|
use crate::cpu::{Cpu, CpuInternals, CpuStatus};
|
|
|
|
// Unofficial ops documentation: https://www.masswerk.at/6502/6502_instruction_set.html
|
|
|
|
fn is_sign_overflow(val1: u8, val2: u8, result: u8) -> bool {
|
|
(val1 & 0x80 == val2 & 0x80) && (val1 & 0x80 != result & 0x80)
|
|
}
|
|
|
|
impl Cpu {
|
|
pub fn exec_instruction(&mut self, instr: Instruction) {
|
|
let operand = self.operand_decode(instr.addr_mode());
|
|
if operand.is_page_crossing {
|
|
self.busy_cycle_count += 1;
|
|
}
|
|
|
|
match instr.op() {
|
|
OperationType::ADC => self.op_adc(operand),
|
|
OperationType::AND => self.op_and(operand),
|
|
OperationType::ASL => self.op_asl(operand),
|
|
OperationType::BCC => self.op_bcc(operand),
|
|
OperationType::BCS => self.op_bcs(operand),
|
|
OperationType::BEQ => self.op_beq(operand),
|
|
OperationType::BIT => self.op_bit(operand),
|
|
OperationType::BMI => self.op_bmi(operand),
|
|
OperationType::BNE => self.op_bne(operand),
|
|
OperationType::BPL => self.op_bpl(operand),
|
|
OperationType::BRK => self.op_brk(operand),
|
|
OperationType::BVC => self.op_bvc(operand),
|
|
OperationType::BVS => self.op_bvs(operand),
|
|
OperationType::CLC => self.op_clc(operand),
|
|
OperationType::CLD => self.op_cld(operand),
|
|
OperationType::CLI => self.op_cli(operand),
|
|
OperationType::CLV => self.op_clv(operand),
|
|
OperationType::CMP => self.op_cmp(operand),
|
|
OperationType::CPX => self.op_cpx(operand),
|
|
OperationType::CPY => self.op_cpy(operand),
|
|
OperationType::DEC => self.op_dec(operand),
|
|
OperationType::DEX => self.op_dex(operand),
|
|
OperationType::DEY => self.op_dey(operand),
|
|
OperationType::EOR => self.op_eor(operand),
|
|
OperationType::INC => self.op_inc(operand),
|
|
OperationType::INX => self.op_inx(operand),
|
|
OperationType::INY => self.op_iny(operand),
|
|
OperationType::JMP => self.op_jmp(operand, instr.addr_mode()),
|
|
OperationType::JSR => self.op_jsr(operand),
|
|
OperationType::LDA => self.op_lda(operand),
|
|
OperationType::LDX => self.op_ldx(operand),
|
|
OperationType::LDY => self.op_ldy(operand),
|
|
OperationType::LSR => self.op_lsr(operand),
|
|
OperationType::NOP => self.op_nop(operand),
|
|
OperationType::ORA => self.op_ora(operand),
|
|
OperationType::PHA => self.op_pha(operand),
|
|
OperationType::PHP => self.op_php(operand),
|
|
OperationType::PLA => self.op_pla(operand),
|
|
OperationType::PLP => self.op_plp(operand),
|
|
OperationType::ROL => self.op_rol(operand),
|
|
OperationType::ROR => self.op_ror(operand),
|
|
OperationType::RTI => self.op_rti(operand),
|
|
OperationType::RTS => self.op_rts(operand),
|
|
OperationType::SBC => self.op_sbc(operand),
|
|
OperationType::SEC => self.op_sec(operand),
|
|
OperationType::SED => self.op_sed(operand),
|
|
OperationType::SEI => self.op_sei(operand),
|
|
OperationType::STA => self.op_sta(operand),
|
|
OperationType::STX => self.op_stx(operand),
|
|
OperationType::STY => self.op_sty(operand),
|
|
OperationType::TAX => self.op_tax(operand),
|
|
OperationType::TAY => self.op_tay(operand),
|
|
OperationType::TSX => self.op_tsx(operand),
|
|
OperationType::TXA => self.op_txa(operand),
|
|
OperationType::TXS => self.op_txs(operand),
|
|
OperationType::TYA => self.op_tya(operand),
|
|
OperationType::ISB => self.op_isc(operand),
|
|
OperationType::DCP => self.op_dcp(operand),
|
|
OperationType::SBX => self.op_sbx(operand),
|
|
OperationType::LAS => self.op_las(operand),
|
|
OperationType::LAX => self.op_lax(operand),
|
|
OperationType::AHX => self.op_ahx(operand),
|
|
OperationType::SAX => self.op_sax(operand),
|
|
OperationType::ANE => self.op_ane(operand),
|
|
OperationType::SHX => self.op_shx(operand),
|
|
OperationType::RRA => self.op_rra(operand),
|
|
OperationType::TAS => self.op_tas(operand),
|
|
OperationType::SHY => self.op_shy(operand),
|
|
OperationType::ARR => self.op_arr(operand),
|
|
OperationType::SRE => self.op_sre(operand),
|
|
OperationType::ALR => self.op_alr(operand),
|
|
OperationType::RLA => self.op_rla(operand),
|
|
OperationType::ANC => self.op_anc(operand),
|
|
OperationType::SLO => self.op_slo(operand),
|
|
OperationType::STP => self.op_stp(operand),
|
|
}
|
|
}
|
|
|
|
fn add_with_carry(&mut self, value: u8) {
|
|
let a = self.registers.a;
|
|
|
|
let addition = a.wrapping_add(value);
|
|
let mut overflow = value < a;
|
|
|
|
let result = addition.wrapping_add(self.get_status_flag_u8(CpuStatus::Carry));
|
|
if result < addition {
|
|
// The addition resulted in a smaller number, there was overflow
|
|
overflow = true;
|
|
}
|
|
|
|
self.registers.a = result;
|
|
self.set_status_flag(CpuStatus::Carry, overflow);
|
|
self.set_status_flag(CpuStatus::Overflow, is_sign_overflow(a, value, result));
|
|
}
|
|
|
|
fn sub_with_carry(&mut self, value: u8) {
|
|
let a = self.registers.a;
|
|
|
|
let subtraction = a.wrapping_sub(value);
|
|
let mut overflow = value > a;
|
|
|
|
let result = subtraction.wrapping_sub(self.get_status_flag_u8(CpuStatus::Carry));
|
|
if result > subtraction {
|
|
// The subtraction resulted in a higher number, there was overflow
|
|
overflow = true;
|
|
}
|
|
|
|
self.registers.a = result;
|
|
self.set_status_flag(CpuStatus::Carry, overflow);
|
|
self.set_status_flag(CpuStatus::Overflow, is_sign_overflow(a, value, result));
|
|
}
|
|
|
|
fn branch(&mut self, operand: Operand, condition: bool) {
|
|
let mut cycle_count = 2;
|
|
|
|
if condition {
|
|
cycle_count += 1;
|
|
|
|
let source_addr = self.registers.pc;
|
|
let target_addr = operand.value;
|
|
self.registers.pc = target_addr;
|
|
|
|
// More cycles are used if the target address is in a new page
|
|
if target_addr & 0xFF00 != source_addr & 0xFF00 {
|
|
cycle_count += 2;
|
|
}
|
|
}
|
|
|
|
self.busy_cycle_count += cycle_count;
|
|
}
|
|
|
|
fn set_common_flags(&mut self, result: u8) {
|
|
self.set_status_flag(CpuStatus::Zero, result == 0);
|
|
self.set_status_flag(CpuStatus::Negative, result & 0x80 != 0);
|
|
}
|
|
|
|
// ADC operations
|
|
/// Add with carry to accumulator
|
|
fn op_adc(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
self.add_with_carry(value);
|
|
}
|
|
|
|
/// Unofficial, stores A, X and (high byte of address + 1)
|
|
/// Unstable in the hardware
|
|
fn op_ahx(&mut self, operand: Operand) {
|
|
assert!(operand.is_address());
|
|
|
|
let addr = operand.value;
|
|
let a = self.registers.a;
|
|
let x = self.registers.x;
|
|
let h = (addr >> 8) as u8;
|
|
|
|
let result = a & x & h;
|
|
operand.write(result, self);
|
|
}
|
|
|
|
/// Unofficial, AND + LSR
|
|
fn op_alr(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = a & value;
|
|
|
|
// Sets the dropped bit in the carry register
|
|
self.set_status_flag_u8(CpuStatus::Carry, result & 0x01);
|
|
|
|
let result_shift = result >> 1;
|
|
operand.write(result_shift, self);
|
|
|
|
self.set_common_flags(result_shift);
|
|
}
|
|
|
|
/// Unofficial, bitwise AND and set carry from shift left
|
|
fn op_anc(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = a & value;
|
|
self.registers.a = result;
|
|
|
|
self.set_common_flags(result);
|
|
self.set_status_flag_u8(CpuStatus::Carry, result & 0x80);
|
|
}
|
|
|
|
/// Bitwise AND with accumulator
|
|
fn op_and(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = a & value;
|
|
self.registers.a = result;
|
|
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Unofficial, depends on analog effects and cannot be emulated easily.
|
|
fn op_ane(&self, _: Operand) {
|
|
assert!(false);
|
|
}
|
|
|
|
/// Unofficial, AND + ROR
|
|
fn op_arr(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
let carry = self.get_status_flag_u8(CpuStatus::Carry);
|
|
|
|
let result = a & value;
|
|
let result_shift = result >> 1 | (carry << 7);
|
|
self.registers.a = result_shift;
|
|
|
|
let result_added = result.wrapping_add(value);
|
|
|
|
self.set_common_flags(result_shift);
|
|
self.set_status_flag_u8(CpuStatus::Carry, result & 0x01);
|
|
self.set_status_flag(
|
|
CpuStatus::Overflow,
|
|
is_sign_overflow(result, value, result_added),
|
|
);
|
|
}
|
|
|
|
/// Arithmetic shift left
|
|
fn op_asl(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
|
|
let result = value << 1;
|
|
operand.write(result, self);
|
|
|
|
self.set_common_flags(result);
|
|
self.set_status_flag_u8(CpuStatus::Carry, value & 0x80);
|
|
}
|
|
|
|
/// Branch if carry clear
|
|
fn op_bcc(&mut self, operand: Operand) {
|
|
let condition = !self.get_status_flag(CpuStatus::Carry);
|
|
self.branch(operand, condition);
|
|
}
|
|
|
|
/// Branch if carry set
|
|
fn op_bcs(&mut self, operand: Operand) {
|
|
let condition = self.get_status_flag(CpuStatus::Carry);
|
|
self.branch(operand, condition);
|
|
}
|
|
|
|
/// Branch if equal (zero is set)
|
|
fn op_beq(&mut self, operand: Operand) {
|
|
let condition = self.get_status_flag(CpuStatus::Zero);
|
|
self.branch(operand, condition);
|
|
}
|
|
|
|
/// Bit test, only changes flags from bitwise AND with accumulator
|
|
fn op_bit(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = a & value;
|
|
|
|
self.set_status_flag(CpuStatus::Zero, result == 0);
|
|
self.set_status_flag_u8(CpuStatus::Overflow, value & 0b01000000);
|
|
self.set_status_flag_u8(CpuStatus::Negative, value & 0b10000000);
|
|
}
|
|
|
|
/// Branch if minus (negative is set)
|
|
fn op_bmi(&mut self, operand: Operand) {
|
|
let condition = self.get_status_flag(CpuStatus::Negative);
|
|
self.branch(operand, condition);
|
|
}
|
|
|
|
/// Branch if not equal (zero is not set)
|
|
fn op_bne(&mut self, operand: Operand) {
|
|
let condition = !self.get_status_flag(CpuStatus::Zero);
|
|
self.branch(operand, condition);
|
|
}
|
|
|
|
/// Branch if plus (negative not set)
|
|
fn op_bpl(&mut self, operand: Operand) {
|
|
let condition = !self.get_status_flag(CpuStatus::Negative);
|
|
self.branch(operand, condition);
|
|
}
|
|
|
|
/// Break, software interrupt
|
|
fn op_brk(&mut self, operand: Operand) {
|
|
self.set_status_flag(CpuStatus::Break, true);
|
|
self.stack_push_context();
|
|
|
|
// TODO: If an NMI is triggered at the same time, this interrupt is skipped but the break flag is still set
|
|
// Load IRQ interrupt vector in PC at $FFFE/F
|
|
// Cycle count: 7
|
|
}
|
|
|
|
/// Branch if overflow clear
|
|
fn op_bvc(&mut self, operand: Operand) {
|
|
let condition = !self.get_status_flag(CpuStatus::Overflow);
|
|
self.branch(operand, condition);
|
|
}
|
|
|
|
/// Branch if overflow set
|
|
fn op_bvs(&mut self, operand: Operand) {
|
|
let condition = self.get_status_flag(CpuStatus::Overflow);
|
|
self.branch(operand, condition);
|
|
}
|
|
|
|
/// Clear carry flag
|
|
fn op_clc(&mut self, operand: Operand) {
|
|
self.set_status_flag(CpuStatus::Carry, false);
|
|
}
|
|
|
|
/// Clear decimal flag
|
|
fn op_cld(&mut self, operand: Operand) {
|
|
self.set_status_flag(CpuStatus::Decimal, false);
|
|
}
|
|
|
|
/// Clear interrupt disable flag
|
|
fn op_cli(&mut self, operand: Operand) {
|
|
self.set_status_flag(CpuStatus::InterruptDisable, false);
|
|
}
|
|
|
|
/// Clear overflow flag
|
|
fn op_clv(&mut self, operand: Operand) {
|
|
self.set_status_flag(CpuStatus::Overflow, false);
|
|
}
|
|
|
|
/// Compare to accumulator
|
|
fn op_cmp(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = a.wrapping_sub(value);
|
|
|
|
self.set_status_flag(CpuStatus::Carry, a >= value);
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Compare to X
|
|
fn op_cpx(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let x = self.registers.x;
|
|
|
|
let result = x.wrapping_sub(value);
|
|
|
|
self.set_status_flag(CpuStatus::Carry, x >= value);
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Compare to Y
|
|
fn op_cpy(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let y = self.registers.y;
|
|
|
|
let result = y.wrapping_sub(value);
|
|
|
|
self.set_status_flag(CpuStatus::Carry, y >= value);
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Unofficial, DEC + CMP
|
|
fn op_dcp(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = value.wrapping_sub(1);
|
|
operand.write(result, self);
|
|
|
|
let cmp_result = a.wrapping_sub(result);
|
|
|
|
self.set_status_flag(CpuStatus::Carry, a >= result);
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Decrement memory
|
|
fn op_dec(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
|
|
let result = value.wrapping_sub(1);
|
|
operand.write(result, self);
|
|
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Decrement X
|
|
fn op_dex(&mut self, operand: Operand) {
|
|
let x = self.registers.x;
|
|
|
|
let result = x.wrapping_sub(1);
|
|
self.registers.x = result;
|
|
|
|
self.set_common_flags(result)
|
|
}
|
|
|
|
/// Decrement Y
|
|
fn op_dey(&mut self, operand: Operand) {
|
|
let y = self.registers.y;
|
|
|
|
let result = y.wrapping_sub(1);
|
|
self.registers.y = result;
|
|
|
|
self.set_common_flags(result)
|
|
}
|
|
|
|
/// Bitwise exclusive OR with accumulator
|
|
fn op_eor(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = a ^ value;
|
|
self.registers.a = result;
|
|
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Increment memory
|
|
fn op_inc(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
|
|
let result = value.wrapping_add(1);
|
|
operand.write(result, self);
|
|
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Increment X
|
|
fn op_inx(&mut self, operand: Operand) {
|
|
let x = self.registers.x;
|
|
|
|
let result = x.wrapping_add(1);
|
|
self.registers.x = result;
|
|
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Increment Y
|
|
fn op_iny(&mut self, operand: Operand) {
|
|
let y = self.registers.y;
|
|
|
|
let result = y.wrapping_add(1);
|
|
self.registers.y = result;
|
|
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Unofficial, INC + SBC
|
|
fn op_isc(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
let c = self.get_status_flag_u8(CpuStatus::Carry);
|
|
|
|
let result = value.wrapping_add(1);
|
|
operand.write(result, self);
|
|
|
|
self.sub_with_carry(!result);
|
|
}
|
|
|
|
/// Jump
|
|
fn op_jmp(&mut self, operand: Operand, addr_mode: AddressingMode) {
|
|
let value = operand.value;
|
|
|
|
let target_addr = if addr_mode == AddressingMode::ABS {
|
|
value
|
|
} else {
|
|
assert_eq!(addr_mode, AddressingMode::IND);
|
|
self.memory_bus.borrow().get_word(value)
|
|
};
|
|
|
|
self.registers.pc = target_addr;
|
|
}
|
|
|
|
/// Jump to subroutine
|
|
fn op_jsr(&mut self, operand: Operand) {
|
|
let target_addr = operand.value;
|
|
let pc = self.registers.pc;
|
|
|
|
self.stack_push_word(pc);
|
|
self.registers.pc = target_addr;
|
|
}
|
|
|
|
/// Unofficial, LDA and TSX
|
|
fn op_las(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let sp = self.registers.sp;
|
|
|
|
let result = value & sp;
|
|
self.registers.a = result;
|
|
self.registers.x = result;
|
|
self.registers.sp = result;
|
|
|
|
self.set_common_flags(result)
|
|
}
|
|
|
|
/// Unofficial, LDA + LDX
|
|
fn op_lax(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
|
|
self.registers.a = value;
|
|
self.registers.x = value;
|
|
|
|
self.set_common_flags(value);
|
|
}
|
|
|
|
/// Unofficial and highly unstable, involve analog effects and won't be emulated
|
|
fn op_lxa(&mut self, operand: Operand) {
|
|
assert!(false);
|
|
}
|
|
|
|
/// Load to accumulator
|
|
fn op_lda(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
|
|
self.registers.a = value;
|
|
|
|
self.set_common_flags(value);
|
|
}
|
|
|
|
/// Load to X
|
|
fn op_ldx(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
|
|
self.registers.x = value;
|
|
|
|
self.set_common_flags(value);
|
|
}
|
|
|
|
/// Load to Y
|
|
fn op_ldy(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
|
|
self.registers.y = value;
|
|
|
|
self.set_common_flags(value);
|
|
}
|
|
|
|
/// Logical shift right
|
|
fn op_lsr(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
|
|
let result = value >> 1;
|
|
operand.write(result, self);
|
|
|
|
self.set_common_flags(result);
|
|
self.set_status_flag_u8(CpuStatus::Carry, value & 0x1);
|
|
}
|
|
|
|
/// No operation
|
|
fn op_nop(&mut self, operand: Operand) {
|
|
}
|
|
|
|
/// Bitwise OR with accumulator
|
|
fn op_ora(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = a | value;
|
|
self.registers.a = result;
|
|
}
|
|
|
|
/// Push A to stack
|
|
fn op_pha(&mut self, operand: Operand) {
|
|
let a = self.registers.a;
|
|
|
|
self.stack_push(a);
|
|
}
|
|
|
|
/// Push status to stack
|
|
fn op_php(&mut self, operand: Operand) {
|
|
let mut status = self.registers.status;
|
|
status |= 0b00110000;
|
|
|
|
self.stack_push(status);
|
|
}
|
|
|
|
/// Pull A from stack
|
|
fn op_pla(&mut self, operand: Operand) {
|
|
let a = self.stack_pop();
|
|
|
|
self.registers.a = a;
|
|
|
|
self.set_common_flags(a);
|
|
}
|
|
|
|
/// Pull status from stack
|
|
fn op_plp(&mut self, operand: Operand) {
|
|
let current_status = self.registers.status & 0b00110000;
|
|
let status = self.stack_pop() & 0b11001111; // bits 4 and 5 are ignored
|
|
|
|
let result = status | current_status;
|
|
|
|
self.registers.status = result;
|
|
}
|
|
|
|
/// Unofficial, ROL + AND
|
|
fn op_rla(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
let carry = self.get_status_flag_u8(CpuStatus::Carry);
|
|
|
|
let result = (value << 1) | carry;
|
|
operand.write(result, self);
|
|
|
|
let and_result = a & result;
|
|
self.registers.a = and_result;
|
|
|
|
self.set_common_flags(and_result);
|
|
self.set_status_flag_u8(CpuStatus::Carry, value & 0x80);
|
|
}
|
|
|
|
/// Rotate left
|
|
fn op_rol(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let carry = self.get_status_flag_u8(CpuStatus::Carry);
|
|
|
|
let result = (value << 1) | carry;
|
|
operand.write(result, self);
|
|
|
|
self.set_common_flags(result);
|
|
self.set_status_flag_u8(CpuStatus::Carry, value & 0x80);
|
|
}
|
|
|
|
/// Rotate right
|
|
fn op_ror(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let carry = self.get_status_flag_u8(CpuStatus::Carry);
|
|
|
|
let result = (value >> 1) | (carry << 7);
|
|
operand.write(result, self);
|
|
|
|
self.set_common_flags(result);
|
|
self.set_status_flag_u8(CpuStatus::Carry, value & 0x01);
|
|
}
|
|
|
|
// Unofficial, ROR + ADC
|
|
fn op_rra(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let carry = self.get_status_flag_u8(CpuStatus::Carry);
|
|
|
|
let result = (value >> 1) | (carry << 7);
|
|
operand.write(result, self);
|
|
|
|
self.set_status_flag_u8(CpuStatus::Carry, value & 0x01);
|
|
self.add_with_carry(result);
|
|
}
|
|
|
|
/// Return from interrupt
|
|
fn op_rti(&mut self, operand: Operand) {
|
|
let current_status = self.registers.status & 0b00110000;
|
|
let status = self.stack_pop() & 0b11001111; // bits 4 and 5 are ignored
|
|
|
|
let result_status = status | current_status;
|
|
let pc = self.stack_pop_word();
|
|
|
|
self.registers.status = result_status;
|
|
self.registers.pc = pc;
|
|
}
|
|
|
|
/// Return from subroutine
|
|
fn op_rts(&mut self, operand: Operand) {
|
|
let pc = self.stack_pop_word();
|
|
|
|
let target_pc = pc.wrapping_add(1);
|
|
self.registers.pc = target_pc;
|
|
}
|
|
|
|
/// Unofficial, M = A & X
|
|
fn op_sax(&mut self, operand: Operand) {
|
|
let a = self.registers.a;
|
|
let x = self.registers.x;
|
|
|
|
let result = a & x;
|
|
operand.write(result, self);
|
|
}
|
|
|
|
/// Subtract with carry from accumulator
|
|
fn op_sbc(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
self.sub_with_carry(!value);
|
|
}
|
|
|
|
/// Unofficial, CMP + DEX, flags like CMP
|
|
fn op_sbx(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
let x = self.registers.x;
|
|
|
|
let result = (a & x).wrapping_sub(value);
|
|
self.registers.x = result;
|
|
|
|
self.set_status_flag(CpuStatus::Carry, a >= value);
|
|
self.set_common_flags(result);
|
|
}
|
|
|
|
/// Set carry flag
|
|
fn op_sec(&mut self, operand: Operand) {
|
|
self.set_status_flag(CpuStatus::Carry, true);
|
|
}
|
|
|
|
/// Set decimal
|
|
fn op_sed(&mut self, operand: Operand) {
|
|
self.set_status_flag(CpuStatus::Decimal, true);
|
|
}
|
|
|
|
/// Set interrupt disable
|
|
fn op_sei(&mut self, operand: Operand) {
|
|
self.set_status_flag(CpuStatus::InterruptDisable, true);
|
|
}
|
|
|
|
/// Unofficial and unstable, M = X & (high byte + 1)
|
|
fn op_shx(&mut self, operand: Operand) {
|
|
let value = ((operand.value & 0xFF) >> 8) as u8;
|
|
let x = self.registers.x;
|
|
|
|
let result = x | value;
|
|
operand.write(result, self);
|
|
}
|
|
|
|
/// Unofficial and unstable, M = Y & (high byte + 1)
|
|
fn op_shy(&mut self, operand: Operand) {
|
|
let value = ((operand.value & 0xFF) >> 8) as u8;
|
|
let y = self.registers.y;
|
|
|
|
let result = y | value;
|
|
operand.write(result, self);
|
|
}
|
|
|
|
/// Unofficial, ASL + ORA
|
|
fn op_slo(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = value << 1;
|
|
operand.write(result, self);
|
|
|
|
let ora_result = a | result;
|
|
self.registers.a = ora_result;
|
|
|
|
self.set_common_flags(ora_result);
|
|
self.set_status_flag_u8(CpuStatus::Carry, value & 0x80);
|
|
}
|
|
|
|
/// Unofficial, LSR + EOR
|
|
fn op_sre(&mut self, operand: Operand) {
|
|
let value = operand.read(self);
|
|
let a = self.registers.a;
|
|
|
|
let result = value >> 1;
|
|
operand.write(result, self);
|
|
|
|
let or_result = a ^ result;
|
|
self.registers.a = or_result;
|
|
|
|
self.set_common_flags(or_result);
|
|
self.set_status_flag_u8(CpuStatus::Carry, value & 0x1);
|
|
}
|
|
|
|
/// Store accumulator
|
|
fn op_sta(&mut self, operand: Operand) {
|
|
assert!(operand.is_address());
|
|
|
|
let a = self.registers.a;
|
|
operand.write(a, self);
|
|
// TODO: C code enabled page crossing in the operand, but I didn't find it in the docs
|
|
}
|
|
|
|
/// Stop
|
|
fn op_stp(&mut self, operand: Operand) {
|
|
// TODO: Freeze the CPU and requires reset
|
|
}
|
|
|
|
/// Store X
|
|
fn op_stx(&mut self, operand: Operand) {
|
|
assert!(operand.is_address());
|
|
|
|
let x = self.registers.x;
|
|
operand.write(x, self);
|
|
}
|
|
|
|
/// Store Y
|
|
fn op_sty(&mut self, operand: Operand) {
|
|
assert!(operand.is_address());
|
|
|
|
let y = self.registers.y;
|
|
operand.write(y, self);
|
|
}
|
|
|
|
/// Unofficial and unstable, SP = A & X, M = A & X & (H + 1)
|
|
fn op_tas(&mut self, operand: Operand) {
|
|
let h = (operand.value >> 8) as u8;
|
|
let a = self.registers.a;
|
|
let x = self.registers.x;
|
|
|
|
let sp = a & x;
|
|
let result = a & x & (h.wrapping_add(1));
|
|
|
|
self.registers.sp = sp;
|
|
operand.write(result, self);
|
|
}
|
|
|
|
/// Transfer A to X
|
|
fn op_tax(&mut self, operand: Operand) {
|
|
let a = self.registers.a;
|
|
|
|
self.registers.x = a;
|
|
|
|
self.set_common_flags(a);
|
|
}
|
|
|
|
/// Transfer A to Y
|
|
fn op_tay(&mut self, operand: Operand) {
|
|
let a = self.registers.a;
|
|
|
|
self.registers.y = a;
|
|
|
|
self.set_common_flags(a);
|
|
}
|
|
|
|
/// Transfer stack pointer to X
|
|
fn op_tsx(&mut self, operand: Operand) {
|
|
let sp = self.registers.sp;
|
|
|
|
self.registers.x = sp;
|
|
|
|
self.set_common_flags(sp);
|
|
}
|
|
|
|
/// Transfer X to A
|
|
fn op_txa(&mut self, operand: Operand) {
|
|
let x = self.registers.x;
|
|
|
|
self.registers.a = x;
|
|
|
|
self.set_common_flags(x);
|
|
}
|
|
|
|
/// Transfer X to stack pointer
|
|
fn op_txs(&mut self, operand: Operand) {
|
|
let x = self.registers.x;
|
|
|
|
self.registers.sp = x;
|
|
}
|
|
|
|
/// Transfer Y to A
|
|
fn op_tya(&mut self, operand: Operand) {
|
|
let y = self.registers.y;
|
|
|
|
self.registers.a = y;
|
|
|
|
self.set_common_flags(y);
|
|
}
|
|
}
|