mirror of
https://github.com/FranLMSP/snes.git
synced 2026-08-03 08:30:01 -04:00
3122 lines
116 KiB
Rust
3122 lines
116 KiB
Rust
use super::cpu::CPU;
|
|
use crate::cpu::bus::Bus;
|
|
use crate::cpu::dma;
|
|
use crate::cpu::cycles;
|
|
use crate::utils::addressing::{AddressingMode, IndexRegister};
|
|
use crate::utils::alu;
|
|
use crate::utils::num_trait::SnesNum;
|
|
use crate::common::flags::Flags;
|
|
|
|
/// TODO: separate this into different files, for example
|
|
/// arithmetic instructions in one file, transfers in another file, etc
|
|
|
|
impl CPU {
|
|
fn get_effective_address(&self, bus: &mut Bus, addressing_mode: AddressingMode) -> u32 {
|
|
addressing_mode.effective_address(
|
|
bus,
|
|
self.registers.get_pc_address(),
|
|
self.registers.d,
|
|
self.registers.sp,
|
|
self.registers.x, self.registers.y
|
|
)
|
|
}
|
|
|
|
fn get_8bit_from_address(&self, bus: &mut Bus, addressing_mode: AddressingMode) -> u8 {
|
|
match addressing_mode {
|
|
AddressingMode::Accumulator => self.registers.a as u8,
|
|
_ => addressing_mode.value_8bit(
|
|
bus,
|
|
self.registers.get_pc_address(),
|
|
self.registers.d,
|
|
self.registers.sp,
|
|
self.registers.x, self.registers.y
|
|
)
|
|
}
|
|
}
|
|
|
|
fn get_16bit_from_address(&self, bus: &mut Bus, addressing_mode: AddressingMode) -> u16 {
|
|
match addressing_mode {
|
|
AddressingMode::Accumulator => self.registers.a,
|
|
_ => addressing_mode.value_16bit(
|
|
bus,
|
|
self.registers.get_pc_address(),
|
|
self.registers.d,
|
|
self.registers.sp,
|
|
self.registers.x, self.registers.y
|
|
)
|
|
}
|
|
}
|
|
|
|
fn set_8bit_to_address(&mut self, bus: &mut Bus, addressing_mode: AddressingMode, value: u8) {
|
|
match addressing_mode {
|
|
AddressingMode::Accumulator => self.registers.set_low_a(value),
|
|
_ => addressing_mode.store_8bit(
|
|
bus,
|
|
self.registers.get_pc_address(),
|
|
self.registers.d,
|
|
self.registers.sp,
|
|
self.registers.x, self.registers.y,
|
|
value,
|
|
),
|
|
};
|
|
}
|
|
|
|
fn set_16bit_to_address(&mut self, bus: &mut Bus, addressing_mode: AddressingMode, value: u16) {
|
|
match addressing_mode {
|
|
AddressingMode::Accumulator => self.registers.a = value,
|
|
_ => addressing_mode.store_16bit(
|
|
bus,
|
|
self.registers.get_pc_address(),
|
|
self.registers.d,
|
|
self.registers.sp,
|
|
self.registers.x, self.registers.y,
|
|
value,
|
|
),
|
|
};
|
|
}
|
|
|
|
fn adc(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let carry_flag = self.registers.get_carry_flag();
|
|
let is_decimal_mode = self.registers.get_decimal_mode_flag();
|
|
let is_16bit = self.registers.is_16bit_mode();
|
|
let target = self.registers.a;
|
|
if is_16bit {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = match is_decimal_mode {
|
|
true => alu::adc_bcd(target, value, carry_flag),
|
|
false => alu::adc_bin(target, value, carry_flag),
|
|
};
|
|
self.registers.a = result;
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = match is_decimal_mode {
|
|
true => alu::adc_bcd(target as u8, value, carry_flag),
|
|
false => alu::adc_bin(target as u8, value, carry_flag),
|
|
};
|
|
self.registers.set_low_a(result as u8);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_arithmetic(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn sbc(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let carry_flag = self.registers.get_carry_flag();
|
|
let is_decimal_mode = self.registers.get_decimal_mode_flag();
|
|
let is_16bit = self.registers.is_16bit_mode();
|
|
let target = self.registers.a;
|
|
if is_16bit {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = match is_decimal_mode {
|
|
true => alu::sbc_bcd(target, value, carry_flag),
|
|
false => alu::sbc_bin(target, value, carry_flag),
|
|
};
|
|
self.registers.a = result;
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = match is_decimal_mode {
|
|
true => alu::sbc_bcd(target as u8, value, carry_flag),
|
|
false => alu::sbc_bin(target as u8, value, carry_flag),
|
|
};
|
|
self.registers.set_low_a(result as u8);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_arithmetic(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn do_dec<T: SnesNum>(&mut self, target: T) -> T {
|
|
let (result, affected_flags) = alu::sbc_bin(target, T::from_u32(1), false);
|
|
for flag in affected_flags {
|
|
match flag {
|
|
Flags::Negative(_) | Flags::Zero(_) => self.registers.set_flags(&[flag]),
|
|
_ => {},
|
|
}
|
|
}
|
|
result
|
|
}
|
|
|
|
fn dec(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let result = self.do_dec(value).to_u32() as u16;
|
|
self.set_16bit_to_address(bus, addressing_mode, result);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let result = self.do_dec(value).to_u32() as u8;
|
|
self.set_8bit_to_address(bus, addressing_mode, result);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_inc_dec(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn dex(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.x = self.do_dec(self.registers.x);
|
|
} else {
|
|
let result = self.do_dec(self.registers.x).to_u32() as u8;
|
|
self.registers.set_low_x(result);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn dey(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.y = self.do_dec(self.registers.y);
|
|
} else {
|
|
let result = self.do_dec(self.registers.y).to_u32() as u8;
|
|
self.registers.set_low_y(result);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn do_inc<T: SnesNum>(&mut self, target: T) -> T {
|
|
let (result, affected_flags) = alu::adc_bin(target, T::from_u32(1), false);
|
|
for flag in affected_flags {
|
|
match flag {
|
|
Flags::Negative(_) | Flags::Zero(_) => self.registers.set_flags(&[flag]),
|
|
_ => {},
|
|
}
|
|
}
|
|
result
|
|
}
|
|
|
|
fn inc(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let result = self.do_inc(value).to_u32() as u16;
|
|
self.set_16bit_to_address(bus, addressing_mode, result);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let result = self.do_inc(value).to_u32() as u8;
|
|
self.set_8bit_to_address(bus, addressing_mode, result);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_inc_dec(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn inx(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.x = self.do_inc(self.registers.x);
|
|
} else {
|
|
let result = self.do_inc(self.registers.x).to_u32() as u8;
|
|
self.registers.set_low_x(result);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn iny(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.y = self.do_inc(self.registers.y);
|
|
} else {
|
|
let result = self.do_inc(self.registers.y).to_u32() as u8;
|
|
self.registers.set_low_y(result);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_inc_dec_index();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn do_comp<T: SnesNum>(&mut self, target: T, value: T) {
|
|
let (_, affected_flags) = alu::sbc_bin(target, value, false);
|
|
for flag in affected_flags {
|
|
match flag {
|
|
Flags::Overflow(_) => {},
|
|
_ => self.registers.set_flags(&[flag]),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn cmp(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let is_16bit = self.registers.is_16bit_mode();
|
|
let target = self.registers.a;
|
|
if is_16bit {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
self.do_comp(target, value);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
self.do_comp(target as u8, value);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_arithmetic(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn cpx(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let is_16bit = self.registers.is_16bit_index();
|
|
let target = self.registers.x;
|
|
if is_16bit {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
self.do_comp(target, value);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
self.do_comp(target as u8, value);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_comp_index(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn cpy(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let is_16bit = self.registers.is_16bit_index();
|
|
let target = self.registers.y;
|
|
if is_16bit {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
self.do_comp(target, value);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
self.do_comp(target as u8, value);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_comp_index(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn and(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let target = self.registers.a;
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = alu::and(target, value);
|
|
self.registers.a = result;
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = alu::and(target as u8, value);
|
|
self.registers.set_low_a(result);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_bitwise(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn ora(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let target = self.registers.a;
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = alu::ora(target, value);
|
|
self.registers.a = result;
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = alu::ora(target as u8, value);
|
|
self.registers.set_low_a(result);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_bitwise(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn eor(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let target = self.registers.a;
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = alu::eor(target, value);
|
|
self.registers.a = result;
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let (result, affected_flags) = alu::eor(target as u8, value);
|
|
self.registers.set_low_a(result);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_bitwise(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn asl(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let target = match addressing_mode {
|
|
AddressingMode::Accumulator => self.registers.a,
|
|
_ => match self.registers.is_16bit_mode() {
|
|
true => self.get_16bit_from_address(bus, addressing_mode),
|
|
false => self.get_8bit_from_address(bus, addressing_mode) as u16,
|
|
}
|
|
};
|
|
if self.registers.is_16bit_mode() {
|
|
let (result, affected_flags) = alu::asl(target);
|
|
self.set_16bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let (result, affected_flags) = alu::asl(target as u8);
|
|
self.set_8bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_shift(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn lsr(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let target = match addressing_mode {
|
|
AddressingMode::Accumulator => self.registers.a,
|
|
_ => match self.registers.is_16bit_mode() {
|
|
true => self.get_16bit_from_address(bus, addressing_mode),
|
|
false => self.get_8bit_from_address(bus, addressing_mode) as u16,
|
|
}
|
|
};
|
|
if self.registers.is_16bit_mode() {
|
|
let (result, affected_flags) = alu::lsr(target);
|
|
self.set_16bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let (result, affected_flags) = alu::lsr(target as u8);
|
|
self.set_8bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_shift(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn do_bit<T: SnesNum>(&mut self, accumulator: T, value: T, addressing_mode: AddressingMode) {
|
|
let (result, _) = alu::and(accumulator, value);
|
|
// Immediate addressing affects only the zero flag
|
|
match addressing_mode {
|
|
AddressingMode::Immediate => self.registers.set_zero_flag(result.is_zero()),
|
|
_ => {
|
|
self.registers.set_zero_flag(result.is_zero());
|
|
self.registers.set_negative_flag(value.is_negative());
|
|
self.registers.set_overflow_flag(value.next_to_highest_bit());
|
|
}
|
|
};
|
|
}
|
|
|
|
fn bit(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
self.do_bit(self.registers.a, value, addressing_mode);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
self.do_bit(self.registers.a as u8, value, addressing_mode);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_bit(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn do_branch(&mut self, nearlabel: u8) -> bool {
|
|
let is_negative = (nearlabel >> 7) != 0;
|
|
let old_pc = self.registers.get_pc_address();
|
|
if is_negative {
|
|
let nearlabel = !nearlabel + 1;
|
|
self.registers.decrement_pc(nearlabel as u16);
|
|
} else {
|
|
self.registers.increment_pc(nearlabel as u16);
|
|
}
|
|
let new_pc = self.registers.get_pc_address();
|
|
let page_boundary_crossed = (old_pc & 0xFF00) != (new_pc & 0xFF00);
|
|
return page_boundary_crossed
|
|
}
|
|
|
|
fn bcc(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
if !self.registers.get_carry_flag() {
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
}
|
|
|
|
fn bcs(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
if self.registers.get_carry_flag() {
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
}
|
|
|
|
fn beq(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
if self.registers.get_zero_flag() {
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
}
|
|
|
|
fn bne(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
if !self.registers.get_zero_flag() {
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
}
|
|
|
|
fn bmi(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
if self.registers.get_negative_flag() {
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
}
|
|
|
|
fn bpl(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
if !self.registers.get_negative_flag() {
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
}
|
|
|
|
fn bra(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn brl(&mut self, bus: &mut Bus) {
|
|
let label = bus.read(self.registers.get_pc_address()) as u16 |
|
|
((bus.read(self.registers.get_pc_address() + 1) as u16) << 8);
|
|
let is_negative = (label >> 15) != 0;
|
|
if is_negative {
|
|
let label = !label + 1;
|
|
self.registers.decrement_pc(label);
|
|
} else {
|
|
self.registers.increment_pc(label);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_long();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn bvc(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
if !self.registers.get_overflow_flag() {
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
}
|
|
|
|
fn bvs(&mut self, bus: &mut Bus) {
|
|
let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1));
|
|
let (bytes, cycles) = cycles::increment_cycles_branch();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
if self.registers.get_overflow_flag() {
|
|
let page_boundary_crossed = self.do_branch(nearlabel);
|
|
let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
}
|
|
|
|
fn clc(&mut self) {
|
|
self.registers.set_carry_flag(false);
|
|
let (bytes, cycles) = cycles::increment_cycles_clear();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn cld(&mut self) {
|
|
self.registers.set_decimal_mode_flag(false);
|
|
let (bytes, cycles) = cycles::increment_cycles_clear();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn cli(&mut self) {
|
|
self.registers.set_irq_disable_flag(false);
|
|
let (bytes, cycles) = cycles::increment_cycles_clear();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn clv(&mut self) {
|
|
self.registers.set_overflow_flag(false);
|
|
let (bytes, cycles) = cycles::increment_cycles_clear();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn nop(&mut self) {
|
|
let (bytes, cycles) = cycles::increment_cycles_nop();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn jmp(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let effective_address = self.get_effective_address(bus, addressing_mode);
|
|
let is_long = match addressing_mode {
|
|
AddressingMode::AbsoluteLong |
|
|
AddressingMode::AbsoluteIndirectLong => true,
|
|
_ => false,
|
|
};
|
|
self.registers.pc = effective_address as u16;
|
|
if is_long {
|
|
self.registers.pbr = (effective_address >> 16) as u8;
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_jmp(addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
pub fn do_push(&mut self, bus: &mut Bus, bytes: &[u8]) {
|
|
for byte in bytes {
|
|
let address = self.registers.sp as u32;
|
|
bus.write(address, *byte);
|
|
self.registers.decrement_sp(1);
|
|
}
|
|
}
|
|
|
|
fn brk(&mut self, bus: &mut Bus) {
|
|
self.do_push(bus, &[self.registers.pbr]);
|
|
self.do_push(bus, &[(self.registers.pc >> 8) as u8, self.registers.pc as u8]);
|
|
self.do_push(bus, &[self.registers.p]);
|
|
self.registers.set_decimal_mode_flag(false);
|
|
self.registers.set_irq_disable_flag(true);
|
|
let (bytes, cycles) = cycles::increment_cycles_brk(self.registers.emulation_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn cop(&mut self, bus: &mut Bus) {
|
|
self.do_push(bus, &[self.registers.pbr]);
|
|
self.do_push(bus, &[(self.registers.pc >> 8) as u8, self.registers.pc as u8]);
|
|
self.do_push(bus, &[self.registers.p]);
|
|
self.registers.set_decimal_mode_flag(false);
|
|
self.registers.set_irq_disable_flag(true);
|
|
let (bytes, cycles) = cycles::increment_cycles_brk(self.registers.emulation_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn pea(&mut self, bus: &mut Bus) {
|
|
let address = self.get_effective_address(bus, AddressingMode::Absolute);
|
|
self.do_push(bus, &[(address >> 8) as u8, address as u8]);
|
|
let (bytes, cycles) = cycles::increment_cycles_pea();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn pei(&mut self, bus: &mut Bus) {
|
|
let address = self.get_effective_address(bus, AddressingMode::DirectPageIndirect);
|
|
self.do_push(bus, &[(address >> 8) as u8, address as u8]);
|
|
let (bytes, cycles) = cycles::increment_cycles_pei(&self.registers);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn per(&mut self, bus: &mut Bus) {
|
|
let label = self.get_effective_address(bus, AddressingMode::Absolute) as u16;
|
|
let is_negative = (label>> 15) == 1;
|
|
let (bytes, cycles) = cycles::increment_cycles_per();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
let address = match is_negative {
|
|
true => self.registers.pc.wrapping_sub(!label + 1),
|
|
false=> self.registers.pc.wrapping_add(label),
|
|
};
|
|
self.do_push(bus, &[(address >> 8) as u8, address as u8]);
|
|
}
|
|
|
|
fn pha(&mut self, bus: &mut Bus) {
|
|
let value = self.registers.a;
|
|
if self.registers.is_16bit_mode() {
|
|
self.do_push(bus, &[(value >> 8) as u8, value as u8]);
|
|
} else {
|
|
self.do_push(bus, &[value as u8]);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_pha(self.registers.is_16bit_mode());
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn phb(&mut self, bus: &mut Bus) {
|
|
self.do_push(bus, &[self.registers.dbr]);
|
|
let (bytes, cycles) = cycles::increment_cycles_phb();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn phd(&mut self, bus: &mut Bus) {
|
|
let value = self.registers.d;
|
|
self.do_push(bus, &[(value >> 8) as u8, value as u8]);
|
|
let (bytes, cycles) = cycles::increment_cycles_phd();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
pub fn phk(&mut self, bus: &mut Bus) {
|
|
self.do_push(bus, &[self.registers.pbr]);
|
|
let (bytes, cycles) = cycles::increment_cycles_phk();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
pub fn php(&mut self, bus: &mut Bus) {
|
|
self.do_push(bus, &[self.registers.p]);
|
|
let (bytes, cycles) = cycles::increment_cycles_php();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn phx(&mut self, bus: &mut Bus) {
|
|
let value = self.registers.x;
|
|
if self.registers.is_16bit_index() {
|
|
self.do_push(bus, &[(value >> 8) as u8, value as u8]);
|
|
} else {
|
|
self.do_push(bus, &[value as u8]);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_push_index(self.registers.is_16bit_index());
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn phy(&mut self, bus: &mut Bus) {
|
|
let value = self.registers.y;
|
|
if self.registers.is_16bit_index() {
|
|
self.do_push(bus, &[(value >> 8) as u8, value as u8]);
|
|
} else {
|
|
self.do_push(bus, &[value as u8]);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_push_index(self.registers.is_16bit_index());
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn jsr(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let effective_address = self.get_effective_address(bus, addressing_mode);
|
|
let is_long = match addressing_mode {
|
|
AddressingMode::AbsoluteLong |
|
|
AddressingMode::AbsoluteIndirectLong => true,
|
|
_ => false,
|
|
};
|
|
// We need to push the *next* instruction onto the stack
|
|
let (bytes, cycles) = cycles::increment_cycles_jsr(addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
let value = self.registers.get_pc_address();
|
|
if is_long {
|
|
self.do_push(bus, &[
|
|
(value >> 16) as u8,
|
|
(value >> 8) as u8,
|
|
value as u8,
|
|
]);
|
|
} else {
|
|
self.do_push(bus, &[
|
|
(value >> 8) as u8,
|
|
value as u8,
|
|
]);
|
|
}
|
|
self.registers.pc = effective_address as u16;
|
|
if is_long {
|
|
self.registers.pbr = (effective_address >> 16) as u8;
|
|
}
|
|
}
|
|
|
|
fn lda(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
self.registers.a = value;
|
|
self.registers.set_flags(&[
|
|
Flags::Negative(value >> 15 == 1),
|
|
Flags::Zero(value == 0),
|
|
]);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
self.registers.set_flags(&[
|
|
Flags::Negative(value >> 7 == 1),
|
|
Flags::Zero(value == 0),
|
|
]);
|
|
self.registers.set_low_a(value);
|
|
self.do_bit(self.registers.a as u8, value, addressing_mode);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_lda(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn do_ld_index(&mut self, bus: &mut Bus, index: IndexRegister, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_index() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
match index {
|
|
IndexRegister::X => self.registers.x = value,
|
|
IndexRegister::Y => self.registers.y = value,
|
|
}
|
|
self.registers.set_flags(&[
|
|
Flags::Negative(value >> 15 == 1),
|
|
Flags::Zero(value == 0),
|
|
]);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
match index {
|
|
IndexRegister::X => self.registers.set_low_x(value),
|
|
IndexRegister::Y => self.registers.set_low_y(value),
|
|
}
|
|
self.registers.set_flags(&[
|
|
Flags::Negative(value >> 7 == 1),
|
|
Flags::Zero(value == 0),
|
|
]);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_ld_index(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn ldx(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
self.do_ld_index(bus, IndexRegister::X, addressing_mode);
|
|
}
|
|
|
|
fn ldy(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
self.do_ld_index(bus, IndexRegister::Y, addressing_mode);
|
|
}
|
|
|
|
fn do_pull(&mut self, bus: &mut Bus, count: usize) -> Vec<u8> {
|
|
let mut bytes = vec![];
|
|
let mut is_zero = true;
|
|
for _ in 0..count {
|
|
self.registers.increment_sp(1);
|
|
let byte = bus.read(self.registers.sp as u32);
|
|
if byte != 0 {
|
|
is_zero = false;
|
|
}
|
|
bytes.push(byte);
|
|
}
|
|
self.registers.set_zero_flag(is_zero);
|
|
if bytes.len() > 0 {
|
|
// Low byte is pulled first, so we need to check
|
|
// for the last byte that we pull
|
|
self.registers.set_negative_flag((bytes[bytes.len() - 1] >> 7) == 1);
|
|
}
|
|
bytes
|
|
}
|
|
|
|
fn pla(&mut self, bus: &mut Bus) {
|
|
if self.registers.is_16bit_mode() {
|
|
let bytes = self.do_pull(bus, 2);
|
|
self.registers.a = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
|
} else {
|
|
let bytes = self.do_pull(bus, 1);
|
|
self.registers.set_low_a(bytes[0]);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_pla(self.registers.is_16bit_mode());
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn plb(&mut self, bus: &mut Bus) {
|
|
self.registers.dbr = self.do_pull(bus, 1)[0];
|
|
let (bytes, cycles) = cycles::increment_cycles_plb();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn pld(&mut self, bus: &mut Bus) {
|
|
let bytes = self.do_pull(bus, 2);
|
|
self.registers.d = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
|
let (bytes, cycles) = cycles::increment_cycles_pld();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn plp(&mut self, bus: &mut Bus) {
|
|
let bytes = self.do_pull(bus, 1);
|
|
self.registers.p = bytes[0];
|
|
let (bytes, cycles) = cycles::increment_cycles_plp();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn plx(&mut self, bus: &mut Bus) {
|
|
if self.registers.is_16bit_index() {
|
|
let bytes = self.do_pull(bus, 2);
|
|
self.registers.x = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
|
} else {
|
|
let bytes = self.do_pull(bus, 1);
|
|
self.registers.set_low_x(bytes[0]);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_pl_index(self.registers.is_16bit_index());
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn ply(&mut self, bus: &mut Bus) {
|
|
if self.registers.is_16bit_index() {
|
|
let bytes = self.do_pull(bus, 2);
|
|
self.registers.y = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
|
} else {
|
|
let bytes = self.do_pull(bus, 1);
|
|
self.registers.set_low_y(bytes[0]);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_pl_index(self.registers.is_16bit_index());
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn rep(&mut self, bus: &mut Bus) {
|
|
let byte = self.get_8bit_from_address(bus, AddressingMode::Immediate);
|
|
self.registers.reset_rep_byte(byte);
|
|
let (bytes, cycles) = cycles::increment_cycles_rep();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn rol(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let target = match addressing_mode {
|
|
AddressingMode::Accumulator => self.registers.a,
|
|
_ => match self.registers.is_16bit_mode() {
|
|
true => self.get_16bit_from_address(bus, addressing_mode),
|
|
false => self.get_8bit_from_address(bus, addressing_mode) as u16,
|
|
}
|
|
};
|
|
if self.registers.is_16bit_mode() {
|
|
let (result, affected_flags) = alu::rol(target, self.registers.get_carry_flag());
|
|
self.set_16bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let (result, affected_flags) = alu::rol(target as u8, self.registers.get_carry_flag());
|
|
self.set_8bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_shift(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn ror(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
let target = match addressing_mode {
|
|
AddressingMode::Accumulator => self.registers.a,
|
|
_ => match self.registers.is_16bit_mode() {
|
|
true => self.get_16bit_from_address(bus, addressing_mode),
|
|
false => self.get_8bit_from_address(bus, addressing_mode) as u16,
|
|
}
|
|
};
|
|
if self.registers.is_16bit_mode() {
|
|
let (result, affected_flags) = alu::ror(target, self.registers.get_carry_flag());
|
|
self.set_16bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_flags(&affected_flags);
|
|
} else {
|
|
let (result, affected_flags) = alu::ror(target as u8, self.registers.get_carry_flag());
|
|
self.set_8bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_flags(&affected_flags);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_shift(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn rtl(&mut self, bus: &mut Bus) {
|
|
let bytes = self.do_pull(bus, 3);
|
|
// Low byte of PC is pulled first, then high byte and then PBR
|
|
self.registers.pc = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
|
self.registers.pbr = bytes[2];
|
|
let (bytes, cycles) = cycles::increment_cycles_return_subroutine();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn rts(&mut self, bus: &mut Bus) {
|
|
let bytes = self.do_pull(bus, 2);
|
|
// Low byte of PC is pulled first, then high byte
|
|
self.registers.pc = (bytes[0] as u16) | ((bytes[1] as u16) << 8);
|
|
let (bytes, cycles) = cycles::increment_cycles_return_subroutine();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn sec(&mut self) {
|
|
self.registers.set_carry_flag(true);
|
|
let (bytes, cycles) = cycles::increment_cycles_set_flag();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn sed(&mut self) {
|
|
self.registers.set_decimal_mode_flag(true);
|
|
let (bytes, cycles) = cycles::increment_cycles_set_flag();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn sei(&mut self) {
|
|
self.registers.set_irq_disable_flag(true);
|
|
let (bytes, cycles) = cycles::increment_cycles_set_flag();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn sep(&mut self, bus: &mut Bus) {
|
|
let byte = self.get_8bit_from_address(bus, AddressingMode::Immediate);
|
|
self.registers.set_sep_byte(byte);
|
|
let (bytes, cycles) = cycles::increment_cycles_sep();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn sta(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_mode() {
|
|
self.set_16bit_to_address(bus, addressing_mode, self.registers.a);
|
|
} else {
|
|
self.set_8bit_to_address(bus, addressing_mode, self.registers.a as u8);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_sta(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn stx(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_index() {
|
|
self.set_16bit_to_address(bus, addressing_mode, self.registers.x);
|
|
} else {
|
|
self.set_8bit_to_address(bus, addressing_mode, self.registers.x as u8);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_st_index(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn sty(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_index() {
|
|
self.set_16bit_to_address(bus, addressing_mode, self.registers.y);
|
|
} else {
|
|
self.set_8bit_to_address(bus, addressing_mode, self.registers.y as u8);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_st_index(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn stz(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_mode() {
|
|
self.set_16bit_to_address(bus, addressing_mode, 0);
|
|
} else {
|
|
self.set_8bit_to_address(bus, addressing_mode, 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_st_index(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn stp(&mut self) {
|
|
self.is_stopped = true;
|
|
let (bytes, cycles) = cycles::increment_cycles_stp();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tax(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.x = self.registers.a;
|
|
self.registers.set_negative_flag((self.registers.x >> 15) == 1);
|
|
self.registers.set_zero_flag(self.registers.x == 0);
|
|
} else {
|
|
let result = self.registers.a as u8;
|
|
self.registers.set_low_x(result);
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tay(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.y = self.registers.a;
|
|
self.registers.set_negative_flag((self.registers.y >> 15) == 1);
|
|
self.registers.set_zero_flag(self.registers.y == 0);
|
|
} else {
|
|
let result = self.registers.a as u8;
|
|
self.registers.set_low_y(result);
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tcd(&mut self) {
|
|
let result = self.registers.a;
|
|
self.registers.d = result;
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tcs(&mut self) {
|
|
let result = self.registers.a;
|
|
self.registers.sp = result;
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tdc(&mut self) {
|
|
let result = self.registers.d;
|
|
self.registers.a = result;
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tsc(&mut self) {
|
|
let result = self.registers.sp;
|
|
self.registers.a = result;
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tsx(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
let result = self.registers.sp;
|
|
self.registers.x = result;
|
|
self.registers.set_negative_flag((result >> 15) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
} else {
|
|
let result = self.registers.sp as u8;
|
|
self.registers.set_low_x(result);
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn txa(&mut self) {
|
|
if self.registers.is_16bit_mode() {
|
|
self.registers.a = self.registers.x;
|
|
self.registers.set_negative_flag((self.registers.a >> 15) == 1);
|
|
self.registers.set_zero_flag(self.registers.a == 0);
|
|
} else {
|
|
let result = self.registers.x as u8;
|
|
self.registers.set_low_a(result);
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn txs(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.sp = self.registers.x;
|
|
self.registers.set_negative_flag((self.registers.a >> 15) == 1);
|
|
self.registers.set_zero_flag(self.registers.a == 0);
|
|
} else {
|
|
let result = self.registers.x as u8;
|
|
self.registers.set_low_sp(result);
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn txy(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.y = self.registers.x;
|
|
self.registers.set_negative_flag((self.registers.x >> 15) == 1);
|
|
self.registers.set_zero_flag(self.registers.x == 0);
|
|
} else {
|
|
let result = self.registers.x as u8;
|
|
self.registers.set_low_y(result);
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tya(&mut self) {
|
|
if self.registers.is_16bit_mode() {
|
|
self.registers.a = self.registers.y;
|
|
self.registers.set_negative_flag((self.registers.a >> 15) == 1);
|
|
self.registers.set_zero_flag(self.registers.a == 0);
|
|
} else {
|
|
let result = self.registers.y as u8;
|
|
self.registers.set_low_a(result);
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tyx(&mut self) {
|
|
if self.registers.is_16bit_index() {
|
|
self.registers.x = self.registers.y;
|
|
self.registers.set_negative_flag((self.registers.y >> 15) == 1);
|
|
self.registers.set_zero_flag(self.registers.y == 0);
|
|
} else {
|
|
let result = self.registers.y as u8;
|
|
self.registers.set_low_x(result);
|
|
self.registers.set_negative_flag((result >> 7) == 1);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_transfer();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn xba(&mut self) {
|
|
self.registers.a = (self.registers.a << 8) | (self.registers.a >> 8);
|
|
self.registers.set_negative_flag(((self.registers.a as u8) >> 7) == 1);
|
|
self.registers.set_zero_flag((self.registers.a as u8) == 0);
|
|
let (bytes, cycles) = cycles::increment_cycles_xba();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn do_move(&mut self, bus: &mut Bus, is_next: bool) {
|
|
let pc = self.registers.get_pc_address();
|
|
let source_bank = bus.read(pc + 2);
|
|
let dest_bank = bus.read(pc + 1);
|
|
let mut count = 0;
|
|
while self.registers.a != 0xFFFF {
|
|
let (x, y) = match self.registers.is_16bit_index() {
|
|
true => (self.registers.x, self.registers.y),
|
|
false => (self.registers.x & 0x00FF, self.registers.y & 0x00FF),
|
|
};
|
|
let source_address = ((source_bank as u32) << 16) | (x as u32);
|
|
let dest_address = ((dest_bank as u32) << 16) | (y as u32);
|
|
let byte = bus.read(source_address);
|
|
bus.write(dest_address, byte);
|
|
self.registers.a = self.registers.a.wrapping_sub(1);
|
|
if is_next {
|
|
self.registers.x = self.registers.x.wrapping_add(1);
|
|
self.registers.y = self.registers.y.wrapping_add(1);
|
|
} else {
|
|
self.registers.x = self.registers.x.wrapping_sub(1);
|
|
self.registers.y = self.registers.y.wrapping_sub(1);
|
|
}
|
|
count += 1;
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_move(count);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn mvn(&mut self, bus: &mut Bus) {
|
|
self.do_move(bus, true);
|
|
}
|
|
|
|
fn mvp(&mut self, bus: &mut Bus) {
|
|
self.do_move(bus, true);
|
|
}
|
|
|
|
fn rti(&mut self, bus: &mut Bus) {
|
|
self.registers.p = self.do_pull(bus, 1)[0];
|
|
let pc_bytes = self.do_pull(bus, 2);
|
|
self.registers.pc = (pc_bytes[0] as u16) | ((pc_bytes[1] as u16) << 8);
|
|
if !self.registers.emulation_mode {
|
|
self.registers.pbr = self.do_pull(bus, 1)[0];
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_return_interrupt(self.registers.emulation_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn trb(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let result = self.registers.a & value;
|
|
self.set_16bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_zero_flag(result == 0);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let result = (self.registers.a as u8) & value;
|
|
self.set_8bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_test(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn tsb(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
|
if self.registers.is_16bit_mode() {
|
|
let value = self.get_16bit_from_address(bus, addressing_mode);
|
|
let result = self.registers.a | value;
|
|
self.set_16bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_zero_flag(result == 0);
|
|
} else {
|
|
let value = self.get_8bit_from_address(bus, addressing_mode);
|
|
let result = (self.registers.a as u8) | value;
|
|
self.set_8bit_to_address(bus, addressing_mode, result);
|
|
self.registers.set_zero_flag(result == 0);
|
|
}
|
|
let (bytes, cycles) = cycles::increment_cycles_test(&self.registers, addressing_mode);
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn wai(&mut self) {
|
|
self.is_waiting_interrupt = true;
|
|
let (bytes, cycles) = cycles::increment_cycles_stp();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
|
|
fn wdm(&mut self) {
|
|
let (bytes, cycles) = cycles::increment_cycles_wdm();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
|
|
fn xce(&mut self) {
|
|
self.registers.exchange_carry_and_emulation();
|
|
let (bytes, cycles) = cycles::increment_cycles_exchange();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
|
|
fn check_running_state(&mut self, bus: &mut Bus) -> bool {
|
|
// Each byte in a DMA transfer takes 8 master cycles.
|
|
// And each CPU can take either 6, 8 or 12 master cycles depending
|
|
// on what's being read from memory. So this won't be accurate.
|
|
if bus.dma.is_active() {
|
|
let pending_bus_writes = bus.dma.tick();
|
|
for (src, dst) in pending_bus_writes {
|
|
let byte = bus.read(src);
|
|
bus.write(dst, byte);
|
|
let (bytes, cycles) = cycles::increment_cycles_while_stopped();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
}
|
|
if !bus.dma.is_active() {
|
|
bus.write(dma::MDMAEN as u32, 0x00)
|
|
}
|
|
return false;
|
|
}
|
|
if self.is_stopped {
|
|
let (bytes, cycles) = cycles::increment_cycles_while_stopped();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
return false;
|
|
}
|
|
if self.is_waiting_interrupt {
|
|
// TODO: check for interrupts here
|
|
let (bytes, cycles) = cycles::increment_cycles_while_stopped();
|
|
self.registers.increment_pc(bytes); self.cycles += cycles;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
pub fn tick(&mut self, bus: &mut Bus) {
|
|
if !self.check_running_state(bus) {
|
|
return;
|
|
}
|
|
let opcode = bus.read(self.registers.get_pc_address());
|
|
self.execute_opcode(opcode, bus);
|
|
}
|
|
|
|
pub fn execute_opcode(&mut self, opcode: u8, bus: &mut Bus) {
|
|
type A = AddressingMode;
|
|
type I = IndexRegister;
|
|
match opcode {
|
|
// ADC
|
|
0x69 => self.adc(bus, A::Immediate),
|
|
0x6D => self.adc(bus, A::Absolute),
|
|
0x6F => self.adc(bus, A::AbsoluteLong),
|
|
0x65 => self.adc(bus, A::DirectPage),
|
|
0x72 => self.adc(bus, A::DirectPageIndirect),
|
|
0x67 => self.adc(bus, A::DirectPageIndirectLong),
|
|
0x7D => self.adc(bus, A::AbsoluteIndexed(I::X)),
|
|
0x7F => self.adc(bus, A::AbsoluteLongIndexed(I::X)),
|
|
0x79 => self.adc(bus, A::AbsoluteIndexed(I::Y)),
|
|
0x75 => self.adc(bus, A::DirectPageIndexed(I::X)),
|
|
0x61 => self.adc(bus, A::DirectPageIndexedIndirect(I::X)),
|
|
0x71 => self.adc(bus, A::DirectPageIndirectIndexed(I::Y)),
|
|
0x77 => self.adc(bus, A::DirectPageIndirectLongIndexed(I::Y)),
|
|
0x63 => self.adc(bus, A::StackRelative),
|
|
0x73 => self.adc(bus, A::StackRelativeIndirectIndexed(I::Y)),
|
|
// AND
|
|
0x29 => self.and(bus, A::Immediate),
|
|
0x2D => self.and(bus, A::Absolute),
|
|
0x2F => self.and(bus, A::AbsoluteLong),
|
|
0x25 => self.and(bus, A::DirectPage),
|
|
0x32 => self.and(bus, A::DirectPageIndirect),
|
|
0x27 => self.and(bus, A::DirectPageIndirectLong),
|
|
0x3D => self.and(bus, A::AbsoluteIndexed(I::X)),
|
|
0x3F => self.and(bus, A::AbsoluteLongIndexed(I::X)),
|
|
0x39 => self.and(bus, A::AbsoluteIndexed(I::Y)),
|
|
0x35 => self.and(bus, A::DirectPageIndexed(I::X)),
|
|
0x21 => self.and(bus, A::DirectPageIndexedIndirect(I::X)),
|
|
0x31 => self.and(bus, A::DirectPageIndirectIndexed(I::Y)),
|
|
0x37 => self.and(bus, A::DirectPageIndirectLongIndexed(I::Y)),
|
|
0x23 => self.and(bus, A::StackRelative),
|
|
0x33 => self.and(bus, A::StackRelativeIndirectIndexed(I::Y)),
|
|
// ASL
|
|
0x0A => self.asl(bus, A::Accumulator),
|
|
0x0E => self.asl(bus, A::Absolute),
|
|
0x06 => self.asl(bus, A::DirectPage),
|
|
0x1E => self.asl(bus, A::AbsoluteIndexed(I::X)),
|
|
0x16 => self.asl(bus, A::DirectPageIndexed(I::X)),
|
|
// BCC
|
|
0x90 => self.bcc(bus),
|
|
// BCS
|
|
0xB0 => self.bcs(bus),
|
|
// BEQ
|
|
0xF0 => self.beq(bus),
|
|
// BNE
|
|
0xD0 => self.bne(bus),
|
|
// BMI
|
|
0x30 => self.bmi(bus),
|
|
// BPL
|
|
0x10 => self.bpl(bus),
|
|
// BRA
|
|
0x80 => self.bra(bus),
|
|
// BRK
|
|
0x00 => self.brk(bus),
|
|
// BRL
|
|
0x82 => self.brl(bus),
|
|
// BVC
|
|
0x50 => self.bvc(bus),
|
|
// BVS
|
|
0x70 => self.bvs(bus),
|
|
// BIT
|
|
0x89 => self.bit(bus, A::Immediate),
|
|
0x2C => self.bit(bus, A::Absolute),
|
|
0x24 => self.bit(bus, A::DirectPage),
|
|
0x3C => self.bit(bus, A::AbsoluteIndexed(I::X)),
|
|
0x34 => self.bit(bus, A::DirectPageIndexed(I::X)),
|
|
// CLC
|
|
0x18 => self.clc(),
|
|
// CLD
|
|
0xD8 => self.cld(),
|
|
// CLI
|
|
0x58 => self.cli(),
|
|
// CLV
|
|
0xB8 => self.clv(),
|
|
// CMP
|
|
0xC9 => self.cmp(bus, A::Immediate),
|
|
0xCD => self.cmp(bus, A::Absolute),
|
|
0xCF => self.cmp(bus, A::AbsoluteLong),
|
|
0xC5 => self.cmp(bus, A::DirectPage),
|
|
0xD2 => self.cmp(bus, A::DirectPageIndirect),
|
|
0xC7 => self.cmp(bus, A::DirectPageIndirectLong),
|
|
0xDD => self.cmp(bus, A::AbsoluteIndexed(I::X)),
|
|
0xDF => self.cmp(bus, A::AbsoluteLongIndexed(I::X)),
|
|
0xD9 => self.cmp(bus, A::AbsoluteIndexed(I::Y)),
|
|
0xD5 => self.cmp(bus, A::DirectPageIndexed(I::X)),
|
|
0xC1 => self.cmp(bus, A::DirectPageIndexedIndirect(I::X)),
|
|
0xD1 => self.cmp(bus, A::DirectPageIndirectIndexed(I::Y)),
|
|
0xD7 => self.cmp(bus, A::DirectPageIndirectLongIndexed(I::Y)),
|
|
0xC3 => self.cmp(bus, A::StackRelative),
|
|
0xD3 => self.cmp(bus, A::StackRelativeIndirectIndexed(I::Y)),
|
|
// COP
|
|
0x02 => self.cop(bus),
|
|
// CPX
|
|
0xE0 => self.cpx(bus, A::Immediate),
|
|
0xEC => self.cpx(bus, A::Absolute),
|
|
0xE4 => self.cpx(bus, A::DirectPage),
|
|
// CPY
|
|
0xC0 => self.cpy(bus, A::Immediate),
|
|
0xCC => self.cpy(bus, A::Absolute),
|
|
0xC4 => self.cpy(bus, A::DirectPage),
|
|
// DEC
|
|
0x3A => self.dec(bus, A::Accumulator),
|
|
0xCE => self.dec(bus, A::Absolute),
|
|
0xC6 => self.dec(bus, A::DirectPage),
|
|
0xDE => self.dec(bus, A::AbsoluteIndexed(I::X)),
|
|
0xD6 => self.dec(bus, A::DirectPageIndexed(I::X)),
|
|
// DEX
|
|
0xCA => self.dex(),
|
|
// DEY
|
|
0x88 => self.dey(),
|
|
// EOR
|
|
0x49 => self.eor(bus, A::Immediate),
|
|
0x4D => self.eor(bus, A::Absolute),
|
|
0x4F => self.eor(bus, A::AbsoluteLong),
|
|
0x45 => self.eor(bus, A::DirectPage),
|
|
0x52 => self.eor(bus, A::DirectPageIndirect),
|
|
0x47 => self.eor(bus, A::DirectPageIndirectLong),
|
|
0x5D => self.eor(bus, A::AbsoluteIndexed(I::X)),
|
|
0x5F => self.eor(bus, A::AbsoluteLongIndexed(I::X)),
|
|
0x59 => self.eor(bus, A::AbsoluteIndexed(I::Y)),
|
|
0x55 => self.eor(bus, A::DirectPageIndexed(I::X)),
|
|
0x41 => self.eor(bus, A::DirectPageIndexedIndirect(I::X)),
|
|
0x51 => self.eor(bus, A::DirectPageIndirectIndexed(I::Y)),
|
|
0x57 => self.eor(bus, A::DirectPageIndirectLongIndexed(I::Y)),
|
|
0x43 => self.eor(bus, A::StackRelative),
|
|
0x53 => self.eor(bus, A::StackRelativeIndirectIndexed(I::Y)),
|
|
// INC
|
|
0x1A => self.inc(bus, A::Accumulator),
|
|
0xEE => self.inc(bus, A::Absolute),
|
|
0xE6 => self.inc(bus, A::DirectPage),
|
|
0xFE => self.inc(bus, A::AbsoluteIndexed(I::X)),
|
|
0xF6 => self.inc(bus, A::DirectPageIndexed(I::X)),
|
|
// INX
|
|
0xE8 => self.inx(),
|
|
// INY
|
|
0xC8 => self.iny(),
|
|
// JMP
|
|
0x4C => self.jmp(bus, A::Absolute),
|
|
0x6C => self.jmp(bus, A::AbsoluteIndirect),
|
|
0x7C => self.jmp(bus, A::AbsoluteIndexedIndirect(I::X)),
|
|
0x5C => self.jmp(bus, A::AbsoluteLong),
|
|
0xDC => self.jmp(bus, A::AbsoluteIndirectLong),
|
|
// JSR
|
|
0x20 => self.jsr(bus, A::Absolute),
|
|
0xFC => self.jsr(bus, A::AbsoluteIndexedIndirect(I::X)),
|
|
0x22 => self.jsr(bus, A::AbsoluteLong), // same as JSL
|
|
// LDA
|
|
0xA9 => self.lda(bus, A::Immediate),
|
|
0xAD => self.lda(bus, A::Absolute),
|
|
0xAF => self.lda(bus, A::AbsoluteLong),
|
|
0xA5 => self.lda(bus, A::DirectPage),
|
|
0xB2 => self.lda(bus, A::DirectPageIndirect),
|
|
0xA7 => self.lda(bus, A::DirectPageIndirectLong),
|
|
0xBD => self.lda(bus, A::AbsoluteIndexed(I::X)),
|
|
0xBF => self.lda(bus, A::AbsoluteLongIndexed(I::X)),
|
|
0xB9 => self.lda(bus, A::AbsoluteIndexed(I::Y)),
|
|
0xB5 => self.lda(bus, A::DirectPageIndexed(I::X)),
|
|
0xA1 => self.lda(bus, A::DirectPageIndexedIndirect(I::X)),
|
|
0xB1 => self.lda(bus, A::DirectPageIndirectIndexed(I::Y)),
|
|
0xB7 => self.lda(bus, A::DirectPageIndirectLongIndexed(I::Y)),
|
|
0xA3 => self.lda(bus, A::StackRelative),
|
|
0xB3 => self.lda(bus, A::StackRelativeIndirectIndexed(I::Y)),
|
|
// LDX
|
|
0xA2 => self.ldx(bus, A::Immediate),
|
|
0xAE => self.ldx(bus, A::Absolute),
|
|
0xA6 => self.ldx(bus, A::DirectPage),
|
|
0xBE => self.ldx(bus, A::AbsoluteIndexed(I::Y)),
|
|
0xB6 => self.ldx(bus, A::DirectPageIndexed(I::Y)),
|
|
// LDY
|
|
0xA0 => self.ldy(bus, A::Immediate),
|
|
0xAC => self.ldy(bus, A::Absolute),
|
|
0xA4 => self.ldy(bus, A::DirectPage),
|
|
0xB4 => self.ldy(bus, A::AbsoluteIndexed(I::Y)),
|
|
0xBC => self.ldy(bus, A::DirectPageIndexed(I::Y)),
|
|
// LSR
|
|
0x4A => self.lsr(bus, A::Accumulator),
|
|
0x4E => self.lsr(bus, A::Absolute),
|
|
0x46 => self.lsr(bus, A::DirectPage),
|
|
0x5E => self.lsr(bus, A::AbsoluteIndexed(I::X)),
|
|
0x56 => self.lsr(bus, A::DirectPageIndexed(I::X)),
|
|
// MVN
|
|
0x54 => self.mvn(bus),
|
|
// MVP
|
|
0x44 => self.mvp(bus),
|
|
// NOP
|
|
0xEA => self.nop(),
|
|
// ORA
|
|
0x09 => self.ora(bus, A::Immediate),
|
|
0x0D => self.ora(bus, A::Absolute),
|
|
0x0F => self.ora(bus, A::AbsoluteLong),
|
|
0x05 => self.ora(bus, A::DirectPage),
|
|
0x12 => self.ora(bus, A::DirectPageIndirect),
|
|
0x07 => self.ora(bus, A::DirectPageIndirectLong),
|
|
0x1D => self.ora(bus, A::AbsoluteIndexed(I::X)),
|
|
0x1F => self.ora(bus, A::AbsoluteLongIndexed(I::X)),
|
|
0x19 => self.ora(bus, A::AbsoluteIndexed(I::Y)),
|
|
0x15 => self.ora(bus, A::DirectPageIndexed(I::X)),
|
|
0x01 => self.ora(bus, A::DirectPageIndexedIndirect(I::X)),
|
|
0x11 => self.ora(bus, A::DirectPageIndirectIndexed(I::Y)),
|
|
0x17 => self.ora(bus, A::DirectPageIndirectLongIndexed(I::Y)),
|
|
0x03 => self.ora(bus, A::StackRelative),
|
|
0x13 => self.ora(bus, A::StackRelativeIndirectIndexed(I::Y)),
|
|
// PEA
|
|
0xF4 => self.pea(bus),
|
|
// PEI
|
|
0xD4 => self.pei(bus),
|
|
// PER
|
|
0x62 => self.per(bus),
|
|
// PHA
|
|
0x48 => self.pha(bus),
|
|
// PHB
|
|
0x8B => self.phb(bus),
|
|
// PHD
|
|
0x0B => self.phd(bus),
|
|
// PHK
|
|
0x4B => self.phk(bus),
|
|
// PHP
|
|
0x08 => self.php(bus),
|
|
// PHX
|
|
0xDA => self.phx(bus),
|
|
// PHY
|
|
0x5A => self.phy(bus),
|
|
// PLA
|
|
0x68 => self.pla(bus),
|
|
// PLB
|
|
0xAB => self.plb(bus),
|
|
// PLD
|
|
0x2B => self.pld(bus),
|
|
// PLP
|
|
0x28 => self.plp(bus),
|
|
// PLX
|
|
0xFA => self.plx(bus),
|
|
// PLY
|
|
0x7A => self.ply(bus),
|
|
// REP
|
|
0xC2 => self.rep(bus),
|
|
// ROL
|
|
0x2A => self.rol(bus, AddressingMode::Accumulator),
|
|
0x2E => self.rol(bus, AddressingMode::Absolute),
|
|
0x26 => self.rol(bus, AddressingMode::DirectPage),
|
|
0x3E => self.rol(bus, AddressingMode::AbsoluteIndexed(I::X)),
|
|
0x36 => self.rol(bus, AddressingMode::DirectPageIndexed(I::X)),
|
|
// ROR
|
|
0x6A => self.ror(bus, AddressingMode::Accumulator),
|
|
0x6E => self.ror(bus, AddressingMode::Absolute),
|
|
0x66 => self.ror(bus, AddressingMode::DirectPage),
|
|
0x7E => self.ror(bus, AddressingMode::AbsoluteIndexed(I::X)),
|
|
0x76 => self.ror(bus, AddressingMode::DirectPageIndexed(I::X)),
|
|
// RTI
|
|
0x40 => self.rti(bus),
|
|
// RTL
|
|
0x6B => self.rtl(bus),
|
|
// RTS
|
|
0x60 => self.rts(bus),
|
|
// SBC
|
|
0xE9 => self.sbc(bus, A::Immediate),
|
|
0xED => self.sbc(bus, A::Absolute),
|
|
0xEF => self.sbc(bus, A::AbsoluteLong),
|
|
0xE5 => self.sbc(bus, A::DirectPage),
|
|
0xF2 => self.sbc(bus, A::DirectPageIndirect),
|
|
0xE7 => self.sbc(bus, A::DirectPageIndirectLong),
|
|
0xFD => self.sbc(bus, A::AbsoluteIndexed(I::X)),
|
|
0xFF => self.sbc(bus, A::AbsoluteLongIndexed(I::X)),
|
|
0xF9 => self.sbc(bus, A::AbsoluteIndexed(I::Y)),
|
|
0xF5 => self.sbc(bus, A::DirectPageIndexed(I::X)),
|
|
0xE1 => self.sbc(bus, A::DirectPageIndexedIndirect(I::X)),
|
|
0xF1 => self.sbc(bus, A::DirectPageIndirectIndexed(I::Y)),
|
|
0xF7 => self.sbc(bus, A::DirectPageIndirectLongIndexed(I::Y)),
|
|
0xE3 => self.sbc(bus, A::StackRelative),
|
|
0xF3 => self.sbc(bus, A::StackRelativeIndirectIndexed(I::Y)),
|
|
// SEC
|
|
0x38 => self.sec(),
|
|
// SED
|
|
0xF8 => self.sed(),
|
|
// SEI
|
|
0x78 => self.sei(),
|
|
// SEP
|
|
0xE2 => self.sep(bus),
|
|
// STA
|
|
0x8D => self.sta(bus, A::Absolute),
|
|
0x8F => self.sta(bus, A::AbsoluteLong),
|
|
0x85 => self.sta(bus, A::DirectPage),
|
|
0x92 => self.sta(bus, A::DirectPageIndirect),
|
|
0x87 => self.sta(bus, A::DirectPageIndirectLong),
|
|
0x9D => self.sta(bus, A::AbsoluteIndexed(I::X)),
|
|
0x9F => self.sta(bus, A::AbsoluteLongIndexed(I::X)),
|
|
0x99 => self.sta(bus, A::AbsoluteIndexed(I::Y)),
|
|
0x95 => self.sta(bus, A::DirectPageIndexed(I::X)),
|
|
0x81 => self.sta(bus, A::DirectPageIndexedIndirect(I::X)),
|
|
0x91 => self.sta(bus, A::DirectPageIndirectIndexed(I::Y)),
|
|
0x97 => self.sta(bus, A::DirectPageIndirectLongIndexed(I::Y)),
|
|
0x83 => self.sta(bus, A::StackRelative),
|
|
0x93 => self.sta(bus, A::StackRelativeIndirectIndexed(I::Y)),
|
|
// STP
|
|
0xDB => self.stp(),
|
|
// STX
|
|
0x8E => self.stx(bus, A::Absolute),
|
|
0x86 => self.stx(bus, A::DirectPage),
|
|
0x96 => self.stx(bus, A::DirectPageIndexed(I::Y)),
|
|
// STY
|
|
0x8C => self.sty(bus, A::Absolute),
|
|
0x84 => self.sty(bus, A::DirectPage),
|
|
0x94 => self.sty(bus, A::DirectPageIndexed(I::X)),
|
|
// STZ
|
|
0x9C => self.stz(bus, A::Absolute),
|
|
0x64 => self.stz(bus, A::DirectPage),
|
|
0x9E => self.stz(bus, A::AbsoluteIndexed(I::X)),
|
|
0x74 => self.stz(bus, A::DirectPageIndexed(I::X)),
|
|
// TAX
|
|
0xAA => self.tax(),
|
|
// TAY
|
|
0xA8 => self.tay(),
|
|
// TCD
|
|
0x5B => self.tcd(),
|
|
// TCS
|
|
0x1B => self.tcs(),
|
|
// TCD
|
|
0x7B => self.tdc(),
|
|
// TRB
|
|
0x1C => self.trb(bus, A::Absolute),
|
|
0x14 => self.trb(bus, A::DirectPage),
|
|
// TSB
|
|
0x0C => self.tsb(bus, A::Absolute),
|
|
0x04 => self.tsb(bus, A::DirectPage),
|
|
// TSC
|
|
0x3B => self.tsc(),
|
|
// TSX
|
|
0xBA => self.tsx(),
|
|
// TXA
|
|
0x8A => self.txa(),
|
|
// TXS
|
|
0x9A => self.txs(),
|
|
// TXY
|
|
0x9B => self.txy(),
|
|
// TYA
|
|
0x98 => self.tya(),
|
|
// TYX
|
|
0xBB => self.tyx(),
|
|
// WAI
|
|
0xCB => self.wai(),
|
|
// WDM
|
|
0x42 => self.wdm(),
|
|
// XBA
|
|
0xEB => self.xba(),
|
|
// XCE
|
|
0xFB => self.xce(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod cpu_instructions_tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_adc() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.a = 0x0000;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(true);
|
|
bus.write(0x000001, 0x40);
|
|
cpu.adc(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.a, 0x40);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_carry_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_eor() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.a = 0x0F;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_16bit_mode(false);
|
|
bus.write(0x000001, 0xF0);
|
|
cpu.eor(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.a, 0xFF);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(cpu.registers.get_negative_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_sbc() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.a = 0x0001;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(true);
|
|
bus.write(0x000001, 1);
|
|
cpu.sbc(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.a, 0);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_carry_flag());
|
|
assert!(cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_and() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.a = 0x0101;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(true);
|
|
bus.write(0x000001, 0x01);
|
|
bus.write(0x000002, 0x01);
|
|
cpu.and(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.a, 0x0101);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_carry_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_ora() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.a = 0x0F;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(true);
|
|
bus.write(0x000001, 0xF0);
|
|
cpu.ora(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.a, 0xFF);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
assert!(cpu.registers.get_negative_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_asl() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.a = 0b01010000_00000000;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(false);
|
|
cpu.asl(&mut bus, AddressingMode::Accumulator);
|
|
assert_eq!(cpu.registers.a, 0b10100000_00000000);
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 4);
|
|
assert!(!cpu.registers.get_carry_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
assert!(cpu.registers.get_negative_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_lsr() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.a = 0b00000000_00000011;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(false);
|
|
cpu.registers.set_negative_flag(true);
|
|
cpu.registers.set_carry_flag(false);
|
|
cpu.lsr(&mut bus, AddressingMode::Accumulator);
|
|
assert_eq!(cpu.registers.a, 0b00000000_00000001);
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 4);
|
|
assert!(cpu.registers.get_carry_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
assert!(!cpu.registers.get_negative_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_bit() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.a = 0b1111_0000;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.p = 0x00;
|
|
bus.write(0x000001, 0b0000_1111);
|
|
cpu.registers.set_16bit_mode(false);
|
|
cpu.bit(&mut bus, AddressingMode::Immediate);
|
|
// Check that it only affects the zero flag on immediate mode
|
|
assert_eq!(cpu.registers.a, 0b1111_0000); // Check that A is not altered
|
|
assert_eq!(cpu.registers.p, 0b0010_0010); // Only zero flag was altered (bit 6 is memory select mode)
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(cpu.registers.get_zero_flag());
|
|
|
|
cpu.registers.a = 0b00110000_00000000;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.p = 0x00;
|
|
cpu.cycles = 0;
|
|
// Write absolute address
|
|
bus.write(0x000001, 0x04);
|
|
bus.write(0x000002, 0x00);
|
|
// Write effective value of address
|
|
bus.write(0x000004, 0x00);
|
|
bus.write(0x000005, 0b1100_0000);
|
|
cpu.registers.set_16bit_mode(true);
|
|
cpu.bit(&mut bus, AddressingMode::Absolute);
|
|
// Check that it only affects the zero flag on immediate mode
|
|
assert_eq!(cpu.registers.a, 0b00110000_00000000); // Check that A is not altered
|
|
assert_eq!(cpu.registers.p, 0b1100_0010);
|
|
assert_eq!(cpu.registers.pc, 0x03);
|
|
assert_eq!(cpu.cycles, 5);
|
|
assert!(cpu.registers.get_zero_flag());
|
|
assert!(cpu.registers.get_negative_flag());
|
|
assert!(cpu.registers.get_overflow_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_clc() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.set_carry_flag(true);
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.clc();
|
|
assert!(!cpu.registers.get_carry_flag());
|
|
assert_eq!(cpu.registers.pc, 1);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_cld() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.set_decimal_mode_flag(true);
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cld();
|
|
assert!(!cpu.registers.get_decimal_mode_flag());
|
|
assert_eq!(cpu.registers.pc, 1);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_cli() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.set_irq_disable_flag(true);
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cli();
|
|
assert!(!cpu.registers.get_irq_disable_flag());
|
|
assert_eq!(cpu.registers.pc, 1);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_clv() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.set_overflow_flag(true);
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.clv();
|
|
assert!(!cpu.registers.get_overflow_flag());
|
|
assert_eq!(cpu.registers.pc, 1);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bcc() {
|
|
// test with positive nearlabel
|
|
// branch not taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_carry_flag(true);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.bcc(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
// branch taken
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_carry_flag(false);
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.bcc(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_carry_flag(false);
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.bcc(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bcs() {
|
|
// test with positive nearlabel
|
|
// branch not taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_carry_flag(false);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.bcs(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
// branch taken
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_carry_flag(true);
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.bcs(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_carry_flag(true);
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.bcs(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_beq() {
|
|
// test with positive nearlabel
|
|
// branch not taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_zero_flag(false);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.beq(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
// branch taken
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_zero_flag(true);
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.beq(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_zero_flag(true);
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.beq(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bne() {
|
|
// test with positive nearlabel
|
|
// branch not taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_zero_flag(true);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.bne(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
// branch taken
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_zero_flag(false);
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.bne(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_zero_flag(false);
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.bne(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bmi() {
|
|
// test with positive nearlabel
|
|
// branch not taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_negative_flag(false);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.bmi(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
// branch taken
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_negative_flag(true);
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.bmi(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_negative_flag(true);
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.bmi(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bpl() {
|
|
// test with positive nearlabel
|
|
// branch not taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_negative_flag(true);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.bpl(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
// branch taken
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_negative_flag(false);
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.bpl(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_negative_flag(false);
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.bpl(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bra() {
|
|
// test with positive nearlabel
|
|
// branch always taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.bra(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.bra(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_brl() {
|
|
// test with positive nearlabel
|
|
// branch always taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0001;
|
|
cpu.cycles = 0;
|
|
bus.write(0x01, 0b00000000);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.brl(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x04 + 0b00001111_00000000);
|
|
assert_eq!(cpu.cycles, 4);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x00FD;
|
|
cpu.cycles = 0;
|
|
bus.write(0xFD, 0xFF); // write -1
|
|
bus.write(0xFE, 0xFF); // write -1
|
|
cpu.brl(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFF);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bvc() {
|
|
// test with positive nearlabel
|
|
// branch not taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_overflow_flag(true);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.bvc(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
// branch taken
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_overflow_flag(false);
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.bvc(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_overflow_flag(false);
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.bvc(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bvs() {
|
|
// test with positive nearlabel
|
|
// branch not taken
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_overflow_flag(false);
|
|
bus.write(0x02, 0b00001111);
|
|
cpu.bvs(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
// branch taken
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_overflow_flag(true);
|
|
bus.write(0x01, 0b00001111);
|
|
cpu.bvs(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0x02 + 0b00001111);
|
|
assert_eq!(cpu.cycles, 3);
|
|
// test with negative nearlabel and boundary cross
|
|
cpu.registers.pc = 0x0100;
|
|
cpu.cycles = 0;
|
|
cpu.registers.set_overflow_flag(true);
|
|
bus.write(0x101, 0xFB); // write -5
|
|
cpu.bvs(&mut bus);
|
|
assert_eq!(cpu.registers.pc, 0xFD);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_cmp() {
|
|
// CMP is basically an SBC instruction but it doesn't
|
|
// store the result nor it affects the overflow flag
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.a = 0x0001;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(true);
|
|
bus.write(0x000001, 1);
|
|
cpu.cmp(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.a, 0x0001); // check A is not affected
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_carry_flag());
|
|
assert!(cpu.registers.get_zero_flag());
|
|
|
|
// check overflow flag is not affected
|
|
cpu.cycles = 0;
|
|
cpu.registers.a = 0x0050;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_16bit_mode(false);
|
|
cpu.registers.set_overflow_flag(false);
|
|
bus.write(0x000001, 0xB0);
|
|
cpu.cmp(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.a, 0x0050); // check A is not affected
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(cpu.registers.get_carry_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
assert!(!cpu.registers.get_overflow_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_cpx() {
|
|
// CMP is basically an SBC instruction but it doesn't
|
|
// store the result nor it affects the overflow flag
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.x = 0x01;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_16bit_index(false);
|
|
bus.write(0x000001, 1);
|
|
cpu.cpx(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.x, 0x01); // check A is not affected
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_carry_flag());
|
|
assert!(cpu.registers.get_zero_flag());
|
|
|
|
// check overflow flag is not affected
|
|
cpu.cycles = 0;
|
|
cpu.registers.x = 0x50;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.registers.set_overflow_flag(false);
|
|
bus.write(0x000002, 0xB0);
|
|
bus.write(0x000001, 0x00);
|
|
cpu.cpx(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.x, 0x50); // check X is not affected
|
|
assert_eq!(cpu.registers.pc, 0x03);
|
|
assert_eq!(cpu.cycles, 3);
|
|
assert!(cpu.registers.get_carry_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
assert!(!cpu.registers.get_overflow_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_cpy() {
|
|
// CMP is basically an SBC instruction but it doesn't
|
|
// store the result nor it affects the overflow flag
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.y = 0x01;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_16bit_index(false);
|
|
bus.write(0x000001, 1);
|
|
cpu.cpy(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.y, 0x01); // check A is not affected
|
|
assert_eq!(cpu.registers.pc, 0x02);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_carry_flag());
|
|
assert!(cpu.registers.get_zero_flag());
|
|
|
|
// check overflow flag is not affected
|
|
cpu.cycles = 0;
|
|
cpu.registers.y = 0x50;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.registers.set_overflow_flag(false);
|
|
bus.write(0x000002, 0xB0);
|
|
bus.write(0x000001, 0x00);
|
|
cpu.cpy(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.y, 0x50); // check X is not affected
|
|
assert_eq!(cpu.registers.pc, 0x03);
|
|
assert_eq!(cpu.cycles, 3);
|
|
assert!(cpu.registers.get_carry_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
assert!(!cpu.registers.get_overflow_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dec() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.a = 0x0001;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(true);
|
|
cpu.dec(&mut bus, AddressingMode::Accumulator);
|
|
assert_eq!(cpu.registers.a, 0);
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_negative_flag());
|
|
assert!(cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dex() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.x = 0x0001;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.dex();
|
|
assert_eq!(cpu.registers.x, 0);
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_negative_flag());
|
|
assert!(cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dey() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.y = 0x0001;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.dey();
|
|
assert_eq!(cpu.registers.y, 0);
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_negative_flag());
|
|
assert!(cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_inc() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.a = 0x0001;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_memory_select_flag(true);
|
|
cpu.inc(&mut bus, AddressingMode::Accumulator);
|
|
assert_eq!(cpu.registers.a, 2);
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_negative_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_inx() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.x = 0x0001;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.inx();
|
|
assert_eq!(cpu.registers.x, 2);
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_negative_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_iny() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.y = 0x0001;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.iny();
|
|
assert_eq!(cpu.registers.y, 2);
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(!cpu.registers.get_negative_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_nop() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.nop();
|
|
assert_eq!(cpu.registers.pc, 0x01);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_jmp() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
bus.write(0x000002, 0xAA);
|
|
bus.write(0x000001, 0xBB);
|
|
cpu.jmp(&mut bus, AddressingMode::Absolute);
|
|
assert_eq!(cpu.registers.pc, 0xAABB);
|
|
assert_eq!(cpu.cycles, 3);
|
|
|
|
// Test a long address
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.pbr = 0x00;
|
|
bus.write(0x000003, 0xAA);
|
|
bus.write(0x000002, 0xBB);
|
|
bus.write(0x000001, 0xCC);
|
|
cpu.jmp(&mut bus, AddressingMode::AbsoluteLong);
|
|
assert_eq!(cpu.registers.pbr, 0xAA);
|
|
assert_eq!(cpu.registers.pc, 0xBBCC);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_jsr() {
|
|
// Test a long address
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x1234;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.sp = 0x1FC;
|
|
bus.write(cpu.registers.get_pc_address() + 3, 0xAA);
|
|
bus.write(cpu.registers.get_pc_address() + 2, 0xBB);
|
|
bus.write(cpu.registers.get_pc_address() + 1, 0xCC);
|
|
// write next instruction
|
|
cpu.jsr(&mut bus, AddressingMode::AbsoluteLong);
|
|
assert_eq!(bus.read(0x1FC), 0x00);
|
|
assert_eq!(bus.read(0x1FB), 0x12);
|
|
assert_eq!(bus.read(0x1FA), 0x38); // we should store the NEXT instruction
|
|
assert_eq!(cpu.registers.pbr, 0xAA);
|
|
assert_eq!(cpu.registers.pc, 0xBBCC);
|
|
assert_eq!(cpu.registers.sp, 0x1F9);
|
|
assert_eq!(cpu.cycles, 8);
|
|
}
|
|
|
|
#[test]
|
|
fn test_lda() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.a = 0x0000;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.set_negative_flag(false);
|
|
cpu.registers.set_zero_flag(true);
|
|
cpu.registers.set_16bit_mode(false);
|
|
bus.write(0x0001, 0xFF);
|
|
cpu.lda(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.pc, 0x0002);
|
|
assert_eq!(cpu.registers.a, 0x00FF);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(cpu.registers.get_negative_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_ldx() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.x = 0x0000;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.set_negative_flag(false);
|
|
cpu.registers.set_zero_flag(true);
|
|
cpu.registers.set_16bit_index(false);
|
|
bus.write(0x0001, 0xFF);
|
|
cpu.ldx(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.pc, 0x0002);
|
|
assert_eq!(cpu.registers.x, 0x00FF);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(cpu.registers.get_negative_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_ldy() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.y = 0x0000;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.set_negative_flag(false);
|
|
cpu.registers.set_zero_flag(true);
|
|
cpu.registers.set_16bit_index(false);
|
|
bus.write(0x0001, 0xFF);
|
|
cpu.ldy(&mut bus, AddressingMode::Immediate);
|
|
assert_eq!(cpu.registers.pc, 0x0002);
|
|
assert_eq!(cpu.registers.y, 0x00FF);
|
|
assert_eq!(cpu.cycles, 2);
|
|
assert!(cpu.registers.get_negative_flag());
|
|
assert!(!cpu.registers.get_zero_flag());
|
|
}
|
|
|
|
#[test]
|
|
fn test_pea() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x1FC;
|
|
bus.write(0x000002, 0xAA);
|
|
bus.write(0x000001, 0xBB);
|
|
cpu.pea(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0xAA);
|
|
assert_eq!(bus.read(0x1FB), 0xBB);
|
|
assert_eq!(cpu.registers.pc, 0x0003);
|
|
assert_eq!(cpu.cycles, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_pei() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x1FC;
|
|
cpu.registers.d = 0x00;
|
|
bus.write(0x000001, 0x02); // Direct page address
|
|
bus.write(0x000002, 0xAA);
|
|
bus.write(0x000003, 0xBB);
|
|
cpu.pei(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0xAA);
|
|
assert_eq!(bus.read(0x1FB), 0xBB);
|
|
assert_eq!(cpu.registers.pc, 0x0002);
|
|
assert_eq!(cpu.cycles, 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_per() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x1FC;
|
|
bus.write(0x000002, 0x00);
|
|
bus.write(0x000001, 0x01);
|
|
cpu.per(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0x00);
|
|
assert_eq!(bus.read(0x1FB), 0x04);
|
|
assert_eq!(cpu.registers.pc, 0x0003);
|
|
assert_eq!(cpu.cycles, 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_pha() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x1FC;
|
|
cpu.registers.a = 0x1234;
|
|
cpu.registers.set_16bit_mode(false);
|
|
cpu.pha(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0x34);
|
|
assert_eq!(cpu.registers.sp, 0x1FB);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_phb() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x1FC;
|
|
cpu.registers.dbr = 0x12;
|
|
cpu.phb(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0x12);
|
|
assert_eq!(cpu.registers.sp, 0x1FB);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_phd() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x1FC;
|
|
cpu.registers.d = 0x1234;
|
|
cpu.phd(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0x12);
|
|
assert_eq!(bus.read(0x1FB), 0x34);
|
|
assert_eq!(cpu.registers.sp, 0x1FA);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_phk() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.sp = 0x1FC;
|
|
bus.write(0x1FC, 0xFF);
|
|
cpu.phk(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0x00);
|
|
assert_eq!(cpu.registers.sp, 0x1FB);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_php() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.p = 0x12;
|
|
cpu.registers.sp = 0x1FC;
|
|
cpu.php(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0x12);
|
|
assert_eq!(cpu.registers.sp, 0x1FB);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_phx() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.x = 0x1234;
|
|
cpu.registers.sp = 0x1FC;
|
|
cpu.phx(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0x12);
|
|
assert_eq!(bus.read(0x1FB), 0x34);
|
|
assert_eq!(cpu.registers.sp, 0x1FA);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_phy() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.y = 0x1234;
|
|
cpu.registers.sp = 0x1FC;
|
|
cpu.phy(&mut bus);
|
|
assert_eq!(bus.read(0x1FC), 0x12);
|
|
assert_eq!(bus.read(0x1FB), 0x34);
|
|
assert_eq!(cpu.registers.sp, 0x1FA);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_pla() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.y = 0x1234;
|
|
cpu.registers.set_16bit_mode(true);
|
|
cpu.registers.set_negative_flag(true);
|
|
cpu.registers.set_zero_flag(true);
|
|
bus.write(0x1FB, 0x34);
|
|
bus.write(0x1FC, 0x12);
|
|
cpu.registers.sp = 0x1FA;
|
|
cpu.pla(&mut bus);
|
|
assert_eq!(cpu.registers.a, 0x1234);
|
|
assert_eq!(cpu.registers.sp, 0x1FC);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.registers.get_negative_flag(), false);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.cycles, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_plb() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.dbr = 0x00;
|
|
cpu.registers.set_negative_flag(true);
|
|
cpu.registers.set_zero_flag(true);
|
|
bus.write(0x1FC, 0x12);
|
|
cpu.registers.sp = 0x1FB;
|
|
cpu.plb(&mut bus);
|
|
assert_eq!(cpu.registers.dbr, 0x12);
|
|
assert_eq!(cpu.registers.sp, 0x1FC);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.registers.get_negative_flag(), false);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_pld() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.d = 0x1234;
|
|
cpu.registers.set_negative_flag(true);
|
|
cpu.registers.set_zero_flag(true);
|
|
bus.write(0x1FB, 0x34);
|
|
bus.write(0x1FC, 0x12);
|
|
cpu.registers.sp = 0x1FA;
|
|
cpu.pld(&mut bus);
|
|
assert_eq!(cpu.registers.d, 0x1234);
|
|
assert_eq!(cpu.registers.sp, 0x1FC);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.registers.get_negative_flag(), false);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.cycles, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_plp() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.p = 0x00;
|
|
bus.write(0x1FC, 0xFF);
|
|
cpu.registers.sp = 0x1FB;
|
|
cpu.plp(&mut bus);
|
|
assert_eq!(cpu.registers.p, 0xFF);
|
|
assert_eq!(cpu.registers.sp, 0x1FC);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_plx() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.x = 0x1234;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.registers.set_negative_flag(true);
|
|
cpu.registers.set_zero_flag(true);
|
|
bus.write(0x1FB, 0x34);
|
|
bus.write(0x1FC, 0x12);
|
|
cpu.registers.sp = 0x1FA;
|
|
cpu.plx(&mut bus);
|
|
assert_eq!(cpu.registers.x, 0x1234);
|
|
assert_eq!(cpu.registers.sp, 0x1FC);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.registers.get_negative_flag(), false);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.cycles, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_ply() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.y = 0x1234;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.registers.set_negative_flag(true);
|
|
cpu.registers.set_zero_flag(true);
|
|
bus.write(0x1FB, 0x34);
|
|
bus.write(0x1FC, 0x12);
|
|
cpu.registers.sp = 0x1FA;
|
|
cpu.ply(&mut bus);
|
|
assert_eq!(cpu.registers.y, 0x1234);
|
|
assert_eq!(cpu.registers.sp, 0x1FC);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.registers.get_negative_flag(), false);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.cycles, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rep() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.p = 0xFF;
|
|
bus.write(0x0001, 0xFF);
|
|
cpu.rep(&mut bus);
|
|
assert_eq!(cpu.registers.p, 0x00);
|
|
assert_eq!(cpu.registers.pc, 0x0002);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rol() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.set_16bit_mode(false);
|
|
cpu.registers.a = 0b0100_0000;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.rol(&mut bus, AddressingMode::Accumulator);
|
|
assert_eq!(cpu.registers.get_negative_flag(), true);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.registers.a, 0b1000_0000);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_ror() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.set_16bit_mode(false);
|
|
cpu.registers.set_carry_flag(true);
|
|
cpu.registers.a = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.ror(&mut bus, AddressingMode::Accumulator);
|
|
assert_eq!(cpu.registers.get_carry_flag(), false);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.registers.a, 0b1000_0000);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rtl() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x1F9;
|
|
bus.write(0x1FC, 0x12);
|
|
bus.write(0x1FB, 0x34);
|
|
bus.write(0x1FA, 0x56);
|
|
cpu.rtl(&mut bus);
|
|
assert_eq!(cpu.registers.pbr, 0x12);
|
|
assert_eq!(cpu.registers.pc, 0x3456);
|
|
assert_eq!(cpu.cycles, 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rts() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pbr = 0x00;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x1FA;
|
|
bus.write(0x1FC, 0x12);
|
|
bus.write(0x1FB, 0x34);
|
|
cpu.rts(&mut bus);
|
|
assert_eq!(cpu.registers.pbr, 0x00);
|
|
assert_eq!(cpu.registers.pc, 0x1234);
|
|
assert_eq!(cpu.cycles, 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sec() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_carry_flag(false);
|
|
cpu.sec();
|
|
assert_eq!(cpu.registers.get_carry_flag(), true);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sed() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_decimal_mode_flag(false);
|
|
cpu.sed();
|
|
assert_eq!(cpu.registers.get_decimal_mode_flag(), true);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sei() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_irq_disable_flag(false);
|
|
cpu.sei();
|
|
assert_eq!(cpu.registers.get_irq_disable_flag(), true);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sep() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.p = 0x00;
|
|
bus.write(0x0001, 0xFF);
|
|
cpu.sep(&mut bus);
|
|
assert_eq!(cpu.registers.p, 0xFF);
|
|
assert_eq!(cpu.registers.pc, 0x0002);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sta() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0x12;
|
|
cpu.registers.set_16bit_mode(false);
|
|
bus.write(0x0002, 0x00);
|
|
bus.write(0x0001, 0x03);
|
|
cpu.sta(&mut bus, AddressingMode::Absolute);
|
|
assert_eq!(bus.read(0x0003), 0x12);
|
|
assert_eq!(cpu.registers.pc, 0x0003);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_stp() {
|
|
let mut cpu = CPU::new();
|
|
cpu.is_stopped = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.stp();
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.is_stopped, true);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_stx() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.x = 0x12;
|
|
cpu.registers.set_16bit_index(false);
|
|
bus.write(0x0002, 0x00);
|
|
bus.write(0x0001, 0x03);
|
|
cpu.stx(&mut bus, AddressingMode::Absolute);
|
|
assert_eq!(bus.read(0x0003), 0x12);
|
|
assert_eq!(cpu.registers.pc, 0x0003);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sty() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.y = 0x12;
|
|
cpu.registers.set_16bit_index(false);
|
|
bus.write(0x0002, 0x00);
|
|
bus.write(0x0001, 0x03);
|
|
cpu.sty(&mut bus, AddressingMode::Absolute);
|
|
assert_eq!(bus.read(0x0003), 0x12);
|
|
assert_eq!(cpu.registers.pc, 0x0003);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_stz() {
|
|
let mut cpu = CPU::new();
|
|
let mut bus = Bus::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.set_16bit_index(false);
|
|
bus.write(0x0002, 0x00);
|
|
bus.write(0x0001, 0x03);
|
|
bus.write(0x0003, 0xFF);
|
|
cpu.stz(&mut bus, AddressingMode::Absolute);
|
|
assert_eq!(bus.read(0x0003), 0x00);
|
|
assert_eq!(cpu.registers.pc, 0x0003);
|
|
assert_eq!(cpu.cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tax() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0xF0F0;
|
|
cpu.registers.x = 0x0000;
|
|
cpu.registers.set_16bit_mode(true);
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.tax();
|
|
assert_eq!(cpu.registers.get_negative_flag(), true);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.registers.x, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tay() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0xF0F0;
|
|
cpu.registers.y = 0x0000;
|
|
cpu.registers.set_16bit_mode(true);
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.tay();
|
|
assert_eq!(cpu.registers.get_negative_flag(), true);
|
|
assert_eq!(cpu.registers.get_zero_flag(), false);
|
|
assert_eq!(cpu.registers.y, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tcd() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0xF0F0;
|
|
cpu.registers.d = 0x0000;
|
|
cpu.tcd();
|
|
assert_eq!(cpu.registers.d, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tcs() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0xF0F0;
|
|
cpu.registers.sp = 0x0000;
|
|
cpu.tcs();
|
|
assert_eq!(cpu.registers.sp, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tdc() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0x0000;
|
|
cpu.registers.d = 0xF0F0;
|
|
cpu.tdc();
|
|
assert_eq!(cpu.registers.a, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tsc() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0x0000;
|
|
cpu.registers.sp = 0xF0F0;
|
|
cpu.tsc();
|
|
assert_eq!(cpu.registers.a, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tsx() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.x = 0x0000;
|
|
cpu.registers.sp = 0xF0F0;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.tsx();
|
|
assert_eq!(cpu.registers.x, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_txa() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0x0000;
|
|
cpu.registers.x = 0xF0F0;
|
|
cpu.registers.set_16bit_mode(true);
|
|
cpu.txa();
|
|
assert_eq!(cpu.registers.a, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_txs() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.sp = 0x0000;
|
|
cpu.registers.x = 0xF0F0;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.txs();
|
|
assert_eq!(cpu.registers.sp, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_txy() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.y = 0x0000;
|
|
cpu.registers.x = 0xF0F0;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.txy();
|
|
assert_eq!(cpu.registers.y, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tya() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0x0000;
|
|
cpu.registers.y = 0xF0F0;
|
|
cpu.registers.set_16bit_mode(true);
|
|
cpu.tya();
|
|
assert_eq!(cpu.registers.a, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tyx() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.emulation_mode = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.x = 0x0000;
|
|
cpu.registers.y = 0xF0F0;
|
|
cpu.registers.set_16bit_index(true);
|
|
cpu.tyx();
|
|
assert_eq!(cpu.registers.x, 0xF0F0);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_wai() {
|
|
let mut cpu = CPU::new();
|
|
cpu.is_waiting_interrupt = false;
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.wai();
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.is_waiting_interrupt, true);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_wdm() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.wdm();
|
|
assert_eq!(cpu.registers.pc, 0x0002);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_xba() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.registers.a = 0x11FF;
|
|
cpu.xba();
|
|
assert_eq!(cpu.registers.a, 0xFF11);
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_xce() {
|
|
let mut cpu = CPU::new();
|
|
cpu.registers.pc = 0x0000;
|
|
cpu.xce();
|
|
assert_eq!(cpu.registers.pc, 0x0001);
|
|
assert_eq!(cpu.cycles, 2);
|
|
}
|
|
}
|