use crate::asm::{Instruction, AddressingMode, decode, cycle_count};
use crate::common::{IoAccess, Clockable, Interrupt};
use super::memorymap;
use std::num::Wrapping;
#[cfg(feature="events")]
use std::sync::mpsc::Sender;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum AddressingModeResult {
Byte(u8), Address(u16), Offset(u8), Implied
}
impl AddressingModeResult {
pub fn to_address(&self) -> Option<u16> {
match *self {
AddressingModeResult::Address(a) => Some(a),
_ => None,
}
}
pub fn to_byte<A2B>(&self, mut a2b: A2B) -> Option<u8> where A2B: FnMut(u16) -> u8 {
match *self {
AddressingModeResult::Byte(b) => Some(b),
AddressingModeResult::Address(a) => {
let b = a2b(a);
Some(b)
},
_ => None,
}
}
pub fn to_offset(&self) -> Option<u8> {
match *self {
AddressingModeResult::Offset(o) => Some(o),
_ => None,
}
}
}
#[derive(Copy, Clone)]
pub enum State {
Reset,
Fetch,
Execute(Instruction, AddressingMode, [u8; 3], usize),
}
enum Flags {
Carry = 1 << 0,
Zero = 1 << 1,
InterruptDisable = 1 << 2,
Decimal = 1 << 3,
Overflow = 1 << 6,
Negative = 1 << 7,
}
#[cfg(feature="events")]
pub mod events {
use super::{Instruction, AddressingMode};
#[derive(Debug)]
pub struct InstructionData {
pub instr: Instruction,
pub mode: AddressingMode,
pub opcode_data: [u8; 3],
pub addr: u16,
pub a: u8,
pub x: u8,
pub y: u8,
pub p: u8,
pub pc: u16,
pub sp: u8,
}
pub enum CpuEvent {
Instruction(InstructionData),
}
}
const STACK_PAGE_OFFSET: u16 = 0x100;
pub struct Cpu<Io: IoAccess> {
a: u8, x: u8, y: u8, pc: u16, sp: u8, p: u8,
bus: Option<Io>,
state: State,
interrupted: Option<Interrupt>,
debug: bool, is_holding: bool,
#[cfg(feature="events")]
logger: Option<Sender<events::CpuEvent>>,
}
impl<Io: IoAccess> Default for Cpu<Io> {
fn default() -> Self {
Cpu {
a: 0,
x: 0,
y: 0,
pc: 0,
sp: 0xFD,
p: 0x24,
bus: None,
state: State::Reset,
interrupted: None,
debug: false,
is_holding: false,
#[cfg(feature="events")]
logger: None,
}
}
}
impl<Io: IoAccess> Cpu<Io> {
pub fn load_bus(&mut self, bus: Io) {
self.bus = Some(bus);
}
pub fn set_pc(&mut self, pc: u16) {
self.pc = pc;
self.state = State::Fetch;
}
pub fn set_debug(&mut self, debug: bool) {
self.debug = debug;
}
#[cfg(feature="events")]
pub fn set_event_sender(&mut self, sender: Sender<events::CpuEvent>) {
self.logger = Some(sender);
}
pub fn get_pc(&self) -> u16 {
self.pc
}
pub fn is_holding(&self) -> bool {
self.is_holding
}
pub fn read_ram(&self, addr: u16) -> u8 {
self.read_u8(addr)
}
fn run_cycle(&mut self, state: State) -> State {
match state {
State::Reset => {
self.pc = self.read_u16(memorymap::RESET_VECTOR);
State::Fetch
},
State::Fetch => {
let masked = self.get_flag_bit(Flags::InterruptDisable);
self.interrupted = self.interrupted.filter(
|int_type| (*int_type == Interrupt::Irq && !masked) || *int_type == Interrupt::Nmi);
if let Some(int_type) = self.interrupted {
self.interrupt(int_type);
State::Fetch
}
else {
let opcode = self.fetch();
self.get_execute_state(opcode)
}
},
State::Execute(ref instr, ref mode, ref opcode_data, ref cycle) => {
let total_cycles = cycle_count(*instr, *mode);
if *cycle < total_cycles-1 {
return State::Execute(*instr, *mode, *opcode_data, *cycle + 1);
}
let operand_data = &opcode_data[1..];
if self.debug {
#[cfg(feature = "events")]
{
let data = events::InstructionData {
instr: *instr,
mode: *mode,
opcode_data: *opcode_data,
addr: self.pc - (mode.operand_len() + 1) as u16,
a: self.a,
x: self.x,
y: self.y,
p: self.p,
pc: self.pc,
sp: self.sp,
};
if let Some(ref logger) = self.logger {
match logger.send(events::CpuEvent::Instruction(data)) {
Ok(_) => {},
Err(_) => self.logger = None,
}
}
}
}
let addressing_result = match mode {
AddressingMode::Immediate => self.immediate(operand_data),
AddressingMode::ZeroPage => self.zeropage(operand_data),
AddressingMode::ZeroPageX => self.zeropage_x(operand_data),
AddressingMode::ZeroPageY => self.zeropage_y(operand_data),
AddressingMode::Absolute => self.absolute(operand_data),
AddressingMode::AbsoluteX => self.absolute_x(operand_data),
AddressingMode::AbsoluteY => self.absolute_y(operand_data),
AddressingMode::IndexedIndirect => self.indexed_indirect(operand_data),
AddressingMode::IndirectIndexed => self.indirect_indexed(operand_data),
AddressingMode::Indirect => self.indirect(operand_data),
AddressingMode::Relative => self.relative(operand_data),
AddressingMode::Accumulator => self.accumulator(),
AddressingMode::Implied => AddressingModeResult::Implied,
};
let read_mem = |addr: u16| -> u8 {
self.read_u8(addr)
};
match instr {
Instruction::NOP => {},
Instruction::CLC => self.clc(),
Instruction::CLD => self.cld(),
Instruction::CLI => self.cli(),
Instruction::CLV => self.clv(),
Instruction::DEX => self.dex(),
Instruction::DEY => self.dey(),
Instruction::INX => self.inx(),
Instruction::INY => self.iny(),
Instruction::PHA => self.pha(),
Instruction::PHP => self.php(),
Instruction::PLA => self.pla(),
Instruction::PLP => self.plp(),
Instruction::RTI => self.rti(),
Instruction::RTS => self.rts(),
Instruction::SEC => self.sec(),
Instruction::SED => self.sed(),
Instruction::SEI => self.sei(),
Instruction::TAX => self.tax(),
Instruction::TAY => self.tay(),
Instruction::TSX => self.tsx(),
Instruction::TXA => self.txa(),
Instruction::TXS => self.txs(),
Instruction::TYA => self.tya(),
Instruction::BRK => self.brk(),
Instruction::ALR => {
let byte = addressing_result.to_byte(read_mem);
self.alr(byte.unwrap());
},
Instruction::ARR => {
let byte = addressing_result.to_byte(read_mem);
self.arr(byte.unwrap());
},
Instruction::AXS => {
let byte = addressing_result.to_byte(read_mem);
self.axs(byte.unwrap());
},
Instruction::LDA => {
let byte = addressing_result.to_byte(read_mem);
self.lda(byte.unwrap())
},
Instruction::LAX => {
let byte = addressing_result.to_byte(read_mem);
self.lax(byte.unwrap())
},
Instruction::JMP => {
let addr = addressing_result.to_address();
self.jmp(addr.unwrap())
},
Instruction::ADC => {
let byte = addressing_result.to_byte(read_mem);
self.adc(byte.unwrap())
},
Instruction::AND => {
let byte = addressing_result.to_byte(read_mem);
self.and(byte.unwrap())
},
Instruction::ANC => {
let byte = addressing_result.to_byte(read_mem);
self.anc(byte.unwrap())
}
Instruction::BCC => {
let offset = addressing_result.to_offset();
self.bcc(offset.unwrap())
},
Instruction::BCS => {
let offset = addressing_result.to_offset();
self.bcs(offset.unwrap())
},
Instruction::BEQ => {
let offset = addressing_result.to_offset();
self.beq(offset.unwrap())
},
Instruction::BNE => {
let offset = addressing_result.to_offset();
self.bne(offset.unwrap())
},
Instruction::BMI => {
let offset = addressing_result.to_offset();
self.bmi(offset.unwrap())
},
Instruction::BPL => {
let offset = addressing_result.to_offset();
self.bpl(offset.unwrap())
},
Instruction::BVC => {
let offset = addressing_result.to_offset();
self.bvc(offset.unwrap())
},
Instruction::BVS => {
let offset = addressing_result.to_offset();
self.bvs(offset.unwrap())
},
Instruction::BIT => {
let byte = addressing_result.to_byte(read_mem);
self.bit(byte.unwrap())
},
Instruction::CMP => {
let byte = addressing_result.to_byte(read_mem);
self.cmp(byte.unwrap())
},
Instruction::CPX => {
let byte = addressing_result.to_byte(read_mem);
self.cpx(byte.unwrap())
},
Instruction::CPY => {
let byte = addressing_result.to_byte(read_mem);
self.cpy(byte.unwrap())
},
Instruction::EOR => {
let byte = addressing_result.to_byte(read_mem);
self.eor(byte.unwrap())
},
Instruction::LDX => {
let byte = addressing_result.to_byte(read_mem);
self.ldx(byte.unwrap())
},
Instruction::LDY => {
let byte = addressing_result.to_byte(read_mem);
self.ldy(byte.unwrap())
},
Instruction::SBC => {
let byte = addressing_result.to_byte(read_mem);
self.sbc(byte.unwrap())
},
Instruction::ORA => {
let byte = addressing_result.to_byte(read_mem);
self.ora(byte.unwrap())
},
Instruction::JSR => {
let addr = addressing_result.to_address();
self.jsr(addr.unwrap())
},
Instruction::STA => {
let v = self.sta();
self.write_result(addressing_result, v);
},
Instruction::STX => {
let v = self.stx();
self.write_result(addressing_result, v);
},
Instruction::STY => {
let v = self.sty();
self.write_result(addressing_result, v);
},
Instruction::SAX => {
let v = self.sax();
self.write_result(addressing_result, v);
},
Instruction::SHY => {
let byte = high_byte!(addressing_result.to_address().unwrap()) as u8;
let v = self.shy(byte.wrapping_add(1));
self.write_result(addressing_result, v);
},
Instruction::SHX => {
let byte = high_byte!(addressing_result.to_address().unwrap()) as u8;
let v = self.shx(byte.wrapping_add(1));
self.write_result(addressing_result, v);
},
Instruction::ASL => {
let byte = addressing_result.to_byte(read_mem);
let v = self.asl(byte.unwrap());
self.write_result(addressing_result, v);
},
Instruction::ROR => {
let byte = addressing_result.to_byte(read_mem);
let v = self.ror(byte.unwrap());
self.write_result(addressing_result, v);
},
Instruction::ROL => {
let byte = addressing_result.to_byte(read_mem);
let v = self.rol(byte.unwrap());
self.write_result(addressing_result, v);
},
Instruction::LSR => {
let byte = addressing_result.to_byte(read_mem);
let v = self.lsr(byte.unwrap());
self.write_result(addressing_result, v);
},
Instruction::INC => {
let byte = addressing_result.to_byte(read_mem);
let v = self.inc(byte.unwrap());
self.write_result(addressing_result, v);
},
Instruction::DEC => {
let byte = addressing_result.to_byte(read_mem);
let v = self.dec(byte.unwrap());
self.write_result(addressing_result, v);
},
Instruction::DCP => {
let byte = addressing_result.to_byte(read_mem);
let v = self.dcp(byte.unwrap());
self.write_result(addressing_result, v);
},
Instruction::ISB => {
let byte = addressing_result.to_byte(read_mem);
let m = self.isb(byte.unwrap());
self.write_result(addressing_result, m);
},
Instruction::SLO => {
let byte = addressing_result.to_byte(read_mem);
let m = self.slo(byte.unwrap());
self.write_result(addressing_result, m);
},
Instruction::RLA => {
let byte = addressing_result.to_byte(read_mem);
let m = self.rla(byte.unwrap());
self.write_result(addressing_result, m);
},
Instruction::SRE => {
let byte = addressing_result.to_byte(read_mem);
let m = self.sre(byte.unwrap());
self.write_result(addressing_result, m);
},
Instruction::RRA => {
let byte = addressing_result.to_byte(read_mem);
let m = self.rra(byte.unwrap());
self.write_result(addressing_result, m);
}
}
State::Fetch
},
}
}
fn get_execute_state(&mut self, opcode: u8) -> State {
let (instr, mode) = decode(opcode);
let operand_data = self.fetch_operand_data(mode.operand_len());
let opcode_data: [u8; 3] = [opcode, operand_data[0], operand_data[1]];
State::Execute(instr, mode, opcode_data, 0)
}
fn fetch_operand_data(&mut self, num_bytes: usize) -> [u8; 2] {
let mut operand_data = [0u8; 2];
for item in operand_data.iter_mut().take(num_bytes) {
*item = self.read_next_u8();
}
operand_data
}
fn lda(&mut self, a: u8) {
self.a = a;
self.update_flags(self.a);
}
fn lax(&mut self, m: u8) {
self.a = m;
self.x = m;
self.update_flags(self.a);
}
fn sax(&self) -> u8 {
self.a & self.x
}
fn dcp(&mut self, m: u8) -> u8 {
let m = m.wrapping_sub(1);
self.cmp(m);
m
}
fn jmp(&mut self, addr: u16) {
self.pc = addr;
}
fn adc(&mut self, m: u8) {
let a = self.a as u16;
let m = m as u16;
let c = self.get_carry() as u16;
let r = a + m + c;
let is_carry = r > 0xFF;
let sign_bit = bit_is_set!(r, 7);
let v = bit_is_set!(a, 7) != sign_bit && bit_is_set!(m, 7) != sign_bit;
self.a = (r & 0x0FF) as u8;
self.update_flags_with_carry(self.a, is_carry);
self.set_flag_bit(Flags::Overflow, v);
}
fn and(&mut self, m: u8) {
self.a &= m;
self.update_flags(self.a);
}
fn anc(&mut self, m: u8) {
self.and(m);
self.set_flag_bit(Flags::Carry, bit_is_set!(self.a, 7));
}
fn asl(&mut self, m: u8) -> u8 {
let c = bit_is_set!(m, 7);
let r = m << 1;
self.set_zero_flag(r);
self.set_negative_flag(r);
self.set_flag_bit(Flags::Carry, c);
r
}
fn alr(&mut self, m: u8) {
self.and(m);
self.a = self.lsr(self.a);
}
fn arr(&mut self, m: u8) {
self.and(m);
let b6 = bit_as_value!(self.a, 6);
let b7 = bit_as_value!(self.a, 7);
let v = b6 ^ b7;
let c = self.get_carry();
self.a >>= 1;
self.a |= c << 7;
self.update_flags(self.a);
self.set_flag_bit(Flags::Overflow, v != 0);
}
fn axs(&mut self, m: u8) {
let r = self.a & self.x;
let r = r.wrapping_sub(m);
self.x = r;
self.set_zero_flag(r);
}
fn sta(&mut self) -> u8 {
self.a
}
fn bcc(&mut self, offset: u8) {
self.branch(!self.get_flag_bit(Flags::Carry), offset);
}
fn bcs(&mut self, offset: u8) {
self.branch(self.get_flag_bit(Flags::Carry), offset);
}
fn beq(&mut self, offset: u8) {
self.branch(self.get_flag_bit(Flags::Zero), offset);
}
fn bne(&mut self, offset: u8) {
self.branch(!self.get_flag_bit(Flags::Zero), offset);
}
fn bmi(&mut self, offset: u8) {
self.branch(self.get_flag_bit(Flags::Negative), offset);
}
fn bpl(&mut self, offset: u8) {
self.branch(!self.get_flag_bit(Flags::Negative), offset);
}
fn bvc(&mut self, offset: u8) {
self.branch(!self.get_flag_bit(Flags::Overflow), offset);
}
fn bvs(&mut self, offset: u8) {
self.branch(self.get_flag_bit(Flags::Overflow), offset);
}
fn bit(&mut self, m: u8) {
let r = self.a & m;
self.set_flag_bit(Flags::Overflow, bit_is_set!(m, 6));
self.set_flag_bit(Flags::Negative, bit_is_set!(m, 7));
self.set_flag_bit(Flags::Zero, r == 0);
}
fn clc(&mut self) {
self.set_flag_bit(Flags::Carry, false);
}
fn cld(&mut self) {
self.set_flag_bit(Flags::Decimal, false);
}
fn cli(&mut self) {
self.set_flag_bit(Flags::InterruptDisable, false);
}
fn clv(&mut self) {
self.set_flag_bit(Flags::Overflow, false);
}
fn cmp(&mut self, m: u8) {
self.compare(self.a, m);
}
fn cpx(&mut self, m: u8) {
self.compare(self.x, m);
}
fn cpy(&mut self, m: u8) {
self.compare(self.y, m);
}
fn dec(&mut self, m: u8) -> u8 {
self.decrement(m)
}
fn dex(&mut self) {
self.x = self.decrement(self.x);
}
fn dey(&mut self) {
self.y = self.decrement(self.y);
}
fn inc(&mut self, m: u8) -> u8 {
self.increment(m)
}
fn inx(&mut self) {
self.x = self.increment(self.x);
}
fn iny(&mut self) {
self.y = self.increment(self.y);
}
fn eor(&mut self, m: u8) {
self.a ^= m;
self.set_zero_flag(self.a);
self.set_negative_flag(self.a);
}
fn ldx(&mut self, m: u8) {
self.x = m;
self.set_zero_flag(self.x);
self.set_negative_flag(self.x);
}
fn ldy(&mut self, m: u8) {
self.y = m;
self.set_zero_flag(self.y);
self.set_negative_flag(self.y);
}
fn pha(&mut self) {
self.push(self.a);
}
fn php(&mut self) {
self.push(self.p | bv!(4) | bv!(5));
}
fn pla(&mut self) {
self.a = self.pull();
self.update_flags(self.a);
}
fn plp(&mut self) {
self.p = self.pull() & !bv!(4);
}
fn lsr(&mut self, m: u8) -> u8 {
let c = bit_is_set!(m, 0);
let r = m >> 1;
self.set_zero_flag(r);
self.set_negative_flag(r);
self.set_flag_bit(Flags::Carry, c);
r
}
fn ora(&mut self, m: u8) {
self.a |= m;
self.set_zero_flag(self.a);
self.set_negative_flag(self.a);
}
fn ror(&mut self, m: u8) -> u8 {
let current_carry = self.get_flag_bit(Flags::Carry);
let new_carry = bit_is_set!(m, 0);
let mut r = m >> 1;
if current_carry {
bit_set!(r, 7);
}
self.set_flag_bit(Flags::Carry, new_carry);
self.set_zero_flag(r);
self.set_negative_flag(r);
r
}
fn rol(&mut self, m: u8) -> u8 {
let current_carry = self.get_flag_bit(Flags::Carry);
let new_carry = bit_is_set!(m, 7);
let mut r = m << 1;
if current_carry {
bit_set!(r, 0);
}
self.set_flag_bit(Flags::Carry, new_carry);
self.set_zero_flag(r);
self.set_negative_flag(r);
r
}
fn rla(&mut self, m: u8) -> u8 {
let m = self.rol(m);
self.a &= m;
self.update_flags(self.a);
m
}
fn rra(&mut self, m: u8) -> u8 {
let m = self.ror(m);
self.adc(m);
m
}
fn sre(&mut self, m: u8) -> u8 {
let m = self.lsr(m);
self.eor(m);
m
}
fn rti(&mut self) {
self.plp();
self.pc = self.pull16();
self.set_flag_bit(Flags::InterruptDisable, false);
}
fn jsr(&mut self, addr: u16) {
self.push16(self.pc-1);
self.pc = addr;
}
fn rts(&mut self) {
self.pc = self.pull16();
self.pc = self.pc.wrapping_add(1);
}
fn sbc(&mut self, m: u8) {
let m = Wrapping(m as u16);
let c = Wrapping(1u16) - Wrapping(self.get_carry() as u16);
let a = Wrapping(self.a as u16);
let r = a - m - c;
let c = bit_is_clear!(r.0, 8);
let sign_bit = bit_is_set!(r.0, 7);
let v = bit_is_set!(a.0, 7) != sign_bit && bit_is_set!(!m.0, 7) != sign_bit;
self.a = (r.0 & 0xFF) as u8;
self.set_flag_bit(Flags::Carry, c);
self.set_flag_bit(Flags::Overflow, v);
self.update_flags(self.a);
}
fn isb(&mut self, m: u8) -> u8 {
let m = m.wrapping_add(1);
self.sbc(m);
m
}
fn slo(&mut self, m: u8) -> u8 {
let m = self.asl(m);
self.ora(m);
m
}
fn sec(&mut self) {
self.set_flag_bit(Flags::Carry, true);
}
fn sed(&mut self) {
self.set_flag_bit(Flags::Decimal, true);
}
fn sei(&mut self) {
self.set_flag_bit(Flags::InterruptDisable, true);
}
fn shy(&self, m: u8) -> u8 {
self.y & m
}
fn shx(&self, m: u8) -> u8 {
self.x & m
}
fn stx(&mut self) -> u8 {
self.x
}
fn sty(&mut self) -> u8 {
self.y
}
fn tax(&mut self) {
self.x = self.a;
self.set_zero_flag(self.x);
self.set_negative_flag(self.x);
}
fn tay(&mut self) {
self.y = self.a;
self.set_zero_flag(self.y);
self.set_negative_flag(self.y);
}
fn tsx(&mut self) {
self.x = self.sp as u8;
self.set_zero_flag(self.x);
self.set_negative_flag(self.x);
}
fn txa(&mut self) {
self.a = self.x;
self.set_negative_flag(self.a);
self.set_zero_flag(self.a);
}
fn txs(&mut self) {
self.sp = self.x
}
fn tya(&mut self) {
self.a = self.y;
self.set_negative_flag(self.a);
self.set_zero_flag(self.a);
}
fn brk(&mut self) {
self.push16(self.pc);
self.push(self.p | bv!(4) | bv!(5));
self.pc = self.read_u16(memorymap::IRQ_VECTOR);
self.set_flag_bit(Flags::InterruptDisable, true);
}
fn immediate(&self, data: &[u8]) -> AddressingModeResult {
AddressingModeResult::Byte(data[0])
}
fn accumulator(&self) -> AddressingModeResult {
AddressingModeResult::Byte(self.a)
}
fn absolute(&mut self, data: &[u8]) -> AddressingModeResult {
AddressingModeResult::Address(((data[1] as u16) << 8) | data[0] as u16)
}
fn absolute_x(&mut self, data: &[u8]) -> AddressingModeResult {
self.absolute_i(data, self.x)
}
fn absolute_y(&mut self, data: &[u8]) -> AddressingModeResult {
self.absolute_i(data, self.y)
}
fn absolute_i(&mut self, data: &[u8], i: u8) -> AddressingModeResult {
let addr = ((data[1] as u16) << 8) | data[0] as u16;
let addr = (Wrapping(addr) + Wrapping(i as u16)).0;
AddressingModeResult::Address(addr)
}
fn zeropage(&mut self, data: &[u8]) -> AddressingModeResult {
AddressingModeResult::Address(data[0] as u16)
}
fn zeropage_x(&mut self, data: &[u8]) -> AddressingModeResult {
self.zeropage_i(data, self.x)
}
fn zeropage_y(&mut self, data: &[u8]) -> AddressingModeResult {
self.zeropage_i(data, self.y)
}
fn zeropage_i(&mut self, data: &[u8], i: u8) -> AddressingModeResult {
AddressingModeResult::Address(((data[0] as u16) + i as u16) & 0xFF)
}
fn indexed_indirect(&mut self, data: &[u8]) -> AddressingModeResult {
let ptr = ((data[0] as u16) + (self.x as u16)) & 0xFF;
let addr = self.indirect_read(ptr);
AddressingModeResult::Address(addr)
}
fn indirect_indexed(&mut self, data: &[u8]) -> AddressingModeResult {
let ptr = data[0] as u16;
let addr = self.indirect_read(ptr);
let addr = addr.wrapping_add(self.y as u16);
AddressingModeResult::Address(addr)
}
fn indirect(&self, data: &[u8]) -> AddressingModeResult {
let ptr = ((data[1] as u16) << 8) | data[0] as u16;
let addr = self.indirect_read(ptr);
AddressingModeResult::Address(addr)
}
fn indirect_read(&self, ptr: u16) -> u16 {
let page = ptr & 0xFF00;
let addr_lo = ptr;
let addr_hi = page | ((ptr + 0x01) & 0x00FF);
let lo = self.read_u8(addr_lo) as u16;
let hi = self.read_u8(addr_hi) as u16;
(hi << 8) | lo
}
fn relative(&mut self, data: &[u8]) -> AddressingModeResult {
AddressingModeResult::Offset(data[0])
}
fn update_flags_with_carry(&mut self, a: u8, c: bool) {
self.update_flags(a);
self.set_flag_bit(Flags::Carry, c);
}
fn update_flags(&mut self, a: u8) {
self.set_zero_flag(a);
self.set_negative_flag(a);
}
fn set_zero_flag(&mut self, a: u8) {
self.set_flag_bit(Flags::Zero, a == 0);
}
fn set_negative_flag(&mut self, a: u8) {
self.set_flag_bit(Flags::Negative, bit_is_set!(a, 7));
}
fn get_carry(&self) -> u8 {
if self.get_flag_bit(Flags::Carry) { 1 } else { 0 }
}
fn get_flag_bit(&self, f: Flags) -> bool {
mask_is_set!(self.p, f as u8)
}
fn set_flag_bit(&mut self, f: Flags, v: bool) {
if v {
mask_set!(self.p, f as u8);
}
else {
mask_clear!(self.p, f as u8);
}
}
fn branch(&mut self, cond_met: bool, offset: u8) {
if cond_met {
let offset = offset as i8;
let offset = offset as i16;
let base_addr = self.pc as i16;
self.pc = (Wrapping(base_addr) + Wrapping(offset)).0 as u16;
}
}
fn compare(&mut self, a: u8, m: u8) {
let r = (Wrapping(a) - Wrapping(m)).0;
self.set_flag_bit(Flags::Carry, a >= m);
self.set_zero_flag(r);
self.set_negative_flag(r);
}
fn decrement(&mut self, a: u8) -> u8 {
let new_a = (Wrapping(a) - Wrapping(1u8)).0;
self.set_zero_flag(new_a);
self.set_negative_flag(new_a);
new_a
}
fn increment(&mut self, a: u8) -> u8 {
let new_a = (Wrapping(a) + Wrapping(1u8)).0;
self.set_zero_flag(new_a);
self.set_negative_flag(new_a);
new_a
}
fn push(&mut self, data: u8) {
self.write_u8((self.sp as u16) + STACK_PAGE_OFFSET, data);
self.sp = self.sp.wrapping_sub(1);
}
fn pull(&mut self) -> u8 {
self.sp = self.sp.wrapping_add(1);
self.read_u8((self.sp as u16) + STACK_PAGE_OFFSET)
}
fn push16(&mut self, data: u16) {
let hi = high_byte!(data) as u8;
let lo = low_byte!(data) as u8;
self.push(hi);
self.push(lo);
}
fn pull16(&mut self) -> u16 {
let lo = self.pull() as u16;
let hi = self.pull() as u16;
(hi << 8) | lo
}
fn fetch(&mut self) -> u8 {
self.read_next_u8()
}
fn write_result(&mut self, addressing_result: AddressingModeResult, value: u8) {
let mode = match self.state {
State::Execute(_, mode, _, _) => mode,
_ => panic!("Must be in execution state!"),
};
if mode == AddressingMode::Accumulator {
self.a = value;
}
else {
self.write_u8(addressing_result.to_address().unwrap(), value);
}
}
fn read_next_u8(&mut self) -> u8 {
let byte = self.read_u8(self.pc);
self.pc = self.pc.wrapping_add(1);
byte
}
fn read_u16(&mut self, addr: u16) -> u16 {
let lo = self.read_u8(addr) as u16;
let hi = self.read_u8(addr + 1) as u16;
(hi << 8) | lo
}
fn read_u8(&self, addr: u16) -> u8 {
if let Some(ref bus) = self.bus {
bus.read_byte(addr)
}
else {
panic!("Attempt to read while bus is not loaded");
}
}
fn write_u8(&mut self, addr: u16, value: u8) {
if let Some(ref mut bus) = self.bus {
bus.write_byte(addr, value);
}
}
fn interrupt(&mut self, int_type: Interrupt) {
self.interrupted = None;
self.push16(self.pc);
self.push(self.p);
self.pc = match int_type {
Interrupt::Nmi => self.read_u16(memorymap::NMI_VECTOR),
Interrupt::Irq => self.read_u16(memorymap::IRQ_VECTOR),
};
self.set_flag_bit(Flags::InterruptDisable, true);
}
}
impl<Io: IoAccess> IoAccess for Cpu<Io> {
fn raise_interrupt(&mut self, interrupt_type: Interrupt) {
if self.interrupted.is_none() {
self.interrupted = Some(interrupt_type);
}
}
}
impl<Io: IoAccess> Clockable for Cpu<Io> {
fn tick(&mut self) {
let prev_pc = self.pc;
self.state = self.run_cycle(self.state);
self.is_holding = prev_pc == self.pc;
}
}
#[cfg(test)]
mod tests {
use super::*;
use helper::*;
#[test]
fn pc_after_reset() {
let mut cpu = init_cpu(vec![]);
cpu.pc = 0x0001;
simple_test_base(&mut cpu, 0);
assert_eq!(cpu.pc, 0x4020);
}
#[test]
fn nop() {
let prg = vec![0xEA];
let cpu = simple_test(prg, 2);
assert_eq!(cpu.pc, 0x4021);
}
#[test]
fn lda_immediate() {
let prg = vec![
0xA9, 0xA5 ];
let cpu = simple_test(prg, 2);
assert_eq!(cpu.a, 0xA5);
}
#[test]
fn lda_absolute() {
let prg = vec![
0xAD, 0x23, 0x40, 0xDE, ];
let cpu = simple_test(prg, 4);
assert_eq!(cpu.a, 0xDE);
}
#[test]
fn lda_zeropage() {
let prg = vec![
0xA5, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x02, 0xDE);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0xDE);
}
#[test]
fn lda_zeropage_x() {
let prg = vec![
0xB5, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x03, 0xDE);
cpu.x = 0x0001;
simple_test_base(&mut cpu, 4);
assert_eq!(cpu.a, 0xDE);
}
#[test]
fn lda_absolute_x() {
let prg = vec![
0xBD, 0x23, 0x40, 0x00, 0xDE, ];
let mut cpu = init_cpu(prg);
cpu.x = 0x0001;
simple_test_base(&mut cpu, 5);
assert_eq!(cpu.a, 0xDE);
}
#[test]
fn lda_absolute_y() {
let prg = vec![
0xB9, 0x23, 0x40, 0x00, 0xDE, ];
let mut cpu = init_cpu(prg);
cpu.y = 0x0001;
simple_test_base(&mut cpu, 5);
assert_eq!(cpu.a, 0xDE);
}
#[test]
fn lda_indexed_indirect() {
let prg = vec![
0xA1, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x03, 0x05);
cpu.write_u8(0x04, 0x00);
cpu.write_u8(0x05, 0xDE);
cpu.x = 0x0001;
simple_test_base(&mut cpu, 6);
assert_eq!(cpu.a, 0xDE);
}
#[test]
fn lda_indirect_indexed() {
let prg = vec![
0xB1, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x02, 0x05);
cpu.write_u8(0x03, 0x00);
cpu.write_u8(0x06, 0xDE);
cpu.y = 0x0001;
simple_test_base(&mut cpu, 6);
assert_eq!(cpu.a, 0xDE);
}
#[test]
fn lda_flags_zero() {
let prg = vec![
0xA9, 0x00 ];
let cpu = simple_test(prg, 2);
assert_eq!(cpu.a, 0x00);
assert_eq!(cpu.p, Flags::Zero as u8);
}
#[test]
fn lda_flags_negative() {
let prg = vec![
0xA9, 0x80 ];
let cpu = simple_test(prg, 2);
assert_eq!(cpu.a, 0x80);
assert_eq!(cpu.p, Flags::Negative as u8);
}
#[test]
fn lax() {
let prg = vec![
0xA7, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x02, 0xDE);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0xDE);
assert_eq!(cpu.x, 0xDE);
}
#[test]
fn sax() {
let prg = vec![
0x87, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xFF;
cpu.x = 0x00;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.read_u8(0x02), 0x00);
}
#[test]
fn dcp() {
let prg = vec![
0xC7, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x02, 0x01);
simple_test_base(&mut cpu, 5);
assert_eq!(cpu.read_u8(0x02), 0x00);
}
#[test]
fn jmp_absolute() {
let prg = vec![
0x4C, 0x00, 0x10 ];
let cpu = simple_test(prg, 3);
assert_eq!(cpu.pc, 0x1000);
}
#[test]
fn jmp_indirect() {
let prg = vec![
0x6C, 0x23, 0x40, 0x00, 0x10, ];
let cpu = simple_test(prg, 5);
assert_eq!(cpu.pc, 0x1000);
}
#[test]
fn jmp_indirect_page_cross() {
let prg = vec![
0x6C, 0xFF, 0x00, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x0FF, 0xAD);
cpu.write_u8(0x00, 0xDE);
simple_test_base(&mut cpu, 5);
assert_eq!(cpu.pc, 0xDEAD);
}
#[test]
fn adc_immediate_no_carry() {
let prg = vec![
0x69, 0x05, ];
let cpu = simple_test(prg, 3);
assert_eq!(cpu.a, 0x05);
assert_eq!(mask_is_clear!(cpu.p, Flags::Carry as u8), true);
}
#[test]
fn adc_immediate_carry() {
let prg = vec![
0x69, 0x01, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xFF;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0x00);
assert_eq!(mask_is_set!(cpu.p, Flags::Carry as u8), true);
}
#[test]
fn adc_immediate_with_carry_set() {
let prg = vec![
0x69, 0xFF, 0x69, 0x01, 0x69, 0x00, ];
let mut cpu = init_cpu(prg);
simple_test_base(&mut cpu, 6);
assert_eq!(cpu.a, 0x01);
assert_eq!(mask_is_clear!(cpu.p, Flags::Carry as u8), true);
}
#[test]
fn adc_overflow_1() {
let prg = vec![
0x69, 0x01, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0x02);
assert_eq!(cpu.get_flag_bit(Flags::Overflow), false);
}
#[test]
fn adc_overflow_2() {
let prg = vec![
0x69, 0xFF, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0x00);
assert_eq!(cpu.get_flag_bit(Flags::Overflow), false);
}
#[test]
fn adc_overflow_3() {
let prg = vec![
0x69, 0x01, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x7F;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0x80);
assert_eq!(cpu.get_flag_bit(Flags::Overflow), true);
}
#[test]
fn adc_overflow_4() {
let prg = vec![
0x69, 0xFF, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x80;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0x7F);
assert_eq!(cpu.get_flag_bit(Flags::Overflow), true);
}
#[test]
fn and_immediate() {
let prg = vec![
0x29, 0xF0, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xFF;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0xF0);
assert_eq!(mask_is_set!(cpu.p, Flags::Negative as u8), true);
}
#[test]
fn and_immediate_zero_set() {
let prg = vec![
0x29, 0x00, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xFF;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0x00);
assert_eq!(mask_is_set!(cpu.p, Flags::Negative as u8), false);
assert_eq!(mask_is_set!(cpu.p, Flags::Zero as u8), true);
}
#[test]
fn zero_flag_cleared() {
let prg = vec![
0xA9, 0x00, 0xA9, 0x01, ];
let cpu = simple_test(prg, 4);
assert_eq!(cpu.a, 0x01);
assert_eq!(mask_is_clear!(cpu.p, Flags::Zero as u8), true);
}
#[test]
fn asl_accumulator() {
let prg = vec![
0x0A, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0x02);
assert_eq!(cpu.get_flag_bit(Flags::Carry), false);
assert_eq!(cpu.get_flag_bit(Flags::Zero), false);
assert_eq!(cpu.get_flag_bit(Flags::Negative), false);
}
#[test]
fn asl_accumulator_is_zero() {
let prg = vec![
0x0A, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x80;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0x00);
assert_eq!(cpu.get_flag_bit(Flags::Carry), true);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
assert_eq!(cpu.get_flag_bit(Flags::Negative), false);
}
#[test]
fn asl_accumulator_carry_and_negative_set() {
let prg = vec![
0x0A, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xC0;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0x80);
assert_eq!(cpu.get_flag_bit(Flags::Carry), true);
assert_eq!(cpu.get_flag_bit(Flags::Zero), false);
assert_eq!(cpu.get_flag_bit(Flags::Negative), true);
}
#[test]
fn sta_zeropage() {
let prg = vec![
0x85, 0x05, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xDE;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.read_u8(0x05), 0xDE);
}
#[test]
fn bcc_carry_not_set() {
let prg = vec![
0x90, 0x02, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Carry as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4024);
}
#[test]
fn bcc_carry_set() {
let prg = vec![
0x90, 0x02, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Carry as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4022);
}
#[test]
fn bcs_carry_not_set() {
let prg = vec![
0xB0, 0x02, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Carry as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4022);
}
#[test]
fn bcs_carry_set() {
let prg = vec![
0xB0, 0x02, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Carry as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4024);
}
#[test]
fn beq_zero_not_set() {
let prg = vec![
0xF0, 0x02, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Zero as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4022);
}
#[test]
fn beq_zero_set() {
let prg = vec![
0xF0, 0x02, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Zero as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4024);
}
#[test]
fn bne_zero_not_set() {
let prg = vec![
0xD0, 0x02, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Zero as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4024);
}
#[test]
fn bne_zero_not_set_negative_offset() {
let prg = vec![
0xD0, 0xFE, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Zero as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4020);
}
#[test]
fn bne_zero_set() {
let prg = vec![
0xD0, 0x02, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Zero as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4022);
}
#[test]
fn bmi_negative_not_set() {
let prg = vec![
0x30, 0x02, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Negative as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4022);
}
#[test]
fn bmi_negative_set() {
let prg = vec![
0x30, 0x02, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Negative as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4024);
}
#[test]
fn bpl_negative_not_set() {
let prg = vec![
0x10, 0x02, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Negative as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4024);
}
#[test]
fn bpl_negative_set() {
let prg = vec![
0x10, 0x02, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Negative as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4022);
}
#[test]
fn bvc_overflow_not_set() {
let prg = vec![
0x50, 0x02, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Overflow as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4024);
}
#[test]
fn bvc_overflow_set() {
let prg = vec![
0x50, 0x02, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Overflow as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4022);
}
#[test]
fn bvs_overflow_not_set() {
let prg = vec![
0x70, 0x02, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Overflow as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4022);
}
#[test]
fn bvs_overflow_set() {
let prg = vec![
0x70, 0x02, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Overflow as u8);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.pc, 0x4024);
}
#[test]
fn bit_check_mask() {
let prg = vec![
0x24, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
cpu.write_u8(0x02, 0x01);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.get_flag_bit(Flags::Zero), false);
}
#[test]
fn bit_check_mask_not_set() {
let prg = vec![
0x24, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
cpu.write_u8(0x02, 0x00);
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
}
#[test]
fn bit_check_mask_absolute() {
let prg = vec![
0x2C, 0x02, 0x00, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
cpu.write_u8(0x02, 0x01);
simple_test_base(&mut cpu, 4);
assert_eq!(cpu.get_flag_bit(Flags::Zero), false);
}
#[test]
fn clear_flags_instructions() {
let prg = vec![
0x18, 0xD8, 0x58, 0xB8, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Carry as u8);
mask_set!(cpu.p, Flags::Decimal as u8);
mask_set!(cpu.p, Flags::InterruptDisable as u8);
mask_set!(cpu.p, Flags::Overflow as u8);
simple_test_base(&mut cpu, 8);
assert_eq!(cpu.get_flag_bit(Flags::Carry), false);
assert_eq!(cpu.get_flag_bit(Flags::Decimal), false);
assert_eq!(cpu.get_flag_bit(Flags::InterruptDisable), false);
assert_eq!(cpu.get_flag_bit(Flags::Overflow), false);
}
#[test]
fn set_flags_instructions() {
let prg = vec![
0x38, 0xF8, 0x78, ];
let mut cpu = init_cpu(prg);
mask_clear!(cpu.p, Flags::Carry as u8);
mask_clear!(cpu.p, Flags::Decimal as u8);
mask_clear!(cpu.p, Flags::InterruptDisable as u8);
mask_clear!(cpu.p, Flags::Overflow as u8);
simple_test_base(&mut cpu, 6);
assert_eq!(cpu.get_flag_bit(Flags::Carry), true);
assert_eq!(cpu.get_flag_bit(Flags::Decimal), true);
assert_eq!(cpu.get_flag_bit(Flags::InterruptDisable), true);
}
#[test]
fn cmp_zero_set() {
let prg = vec![
0xC9, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x02;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
}
#[test]
fn cmp_negative_set() {
let prg = vec![
0xC9, 0x01, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x00;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.get_flag_bit(Flags::Negative), true);
}
#[test]
fn dec_mem() {
let prg = vec![
0xC6, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x02, 0x01);
simple_test_base(&mut cpu, 5);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
assert_eq!(cpu.read_u8(0x02), 0x00);
}
#[test]
fn dex() {
let prg = vec![
0xCA, ];
let mut cpu = init_cpu(prg);
cpu.x = 0x01;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.x, 0x00);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
}
#[test]
fn dey() {
let prg = vec![
0x88, ];
let mut cpu = init_cpu(prg);
cpu.y = 0x01;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.y, 0x00);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
}
#[test]
fn inc_mem() {
let prg = vec![
0xE6, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x02, 0xFF);
simple_test_base(&mut cpu, 5);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
assert_eq!(cpu.read_u8(0x02), 0x00);
}
#[test]
fn inx() {
let prg = vec![
0xE8, ];
let mut cpu = init_cpu(prg);
cpu.x = 0xFF;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.x, 0x00);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
}
#[test]
fn iny() {
let prg = vec![
0xC8, ];
let mut cpu = init_cpu(prg);
cpu.y = 0xFF;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.y, 0x00);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
}
#[test]
fn eor() {
let prg = vec![
0x49, 0xFF, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xFF;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.a, 0x00);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
}
#[test]
fn ldx_immediate() {
let prg = vec![
0xA2, 0xA5 ];
let cpu = simple_test(prg, 2);
assert_eq!(cpu.x, 0xA5);
}
#[test]
fn ldx_absolute() {
let prg = vec![
0xAE, 0xFF, 0x07, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x07FF, 0xA5);
simple_test_base(&mut cpu, 4);
assert_eq!(cpu.x, 0xA5);
}
#[test]
fn ldy_immediate() {
let prg = vec![
0xA0, 0xA5 ];
let cpu = simple_test(prg, 2);
assert_eq!(cpu.y, 0xA5);
}
#[test]
fn pha() {
let prg = vec![
0x48, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xFF;
cpu.sp = 0x0A;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.sp, 0x09);
assert_eq!(cpu.read_u8(0x10A), 0xFF);
}
#[test]
fn pla() {
let prg = vec![
0x68, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x10A, 0xFF);
cpu.sp = 0x09;
simple_test_base(&mut cpu, 4);
assert_eq!(cpu.a, 0xFF);
assert_eq!(cpu.sp, 0x0A);
}
#[test]
fn php() {
let prg = vec![
0x08, ];
let mut cpu = init_cpu(prg);
cpu.p = 0xFF;
cpu.sp = 0x0A;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.sp, 0x09);
assert_eq!(cpu.read_u8(0x10A), 0xFF);
}
#[test]
fn plp() {
let prg = vec![
0x28, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x10A, 0xFF);
cpu.p = 0;
cpu.sp = 0x09;
simple_test_base(&mut cpu, 4);
assert_eq!(cpu.p, 0xEF);
assert_eq!(cpu.sp, 0x0A);
}
#[test]
fn lsr_zero_set() {
let prg = vec![
0x4A, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0x00);
assert_eq!(cpu.get_flag_bit(Flags::Zero), true);
assert_eq!(cpu.get_flag_bit(Flags::Carry), true);
}
#[test]
fn ora() {
let prg = vec![
0x09, 0x0F, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xF0;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0xFF);
}
#[test]
fn ror_carry() {
let prg = vec![
0x6A, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Carry as u8);
cpu.a = 0x01;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0x80);
assert_eq!(cpu.get_flag_bit(Flags::Carry), true);
assert_eq!(cpu.get_flag_bit(Flags::Negative), true);
}
#[test]
fn rol_carry() {
let prg = vec![
0x2A, ];
let mut cpu = init_cpu(prg);
mask_set!(cpu.p, Flags::Carry as u8);
cpu.a = 0x81;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0x03);
assert_eq!(cpu.get_flag_bit(Flags::Carry), true);
}
#[test]
fn rti() {
let prg = vec![
0x40, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x10A, 0xDE);
cpu.write_u8(0x109, 0xAD);
cpu.write_u8(0x108, 0xA1);
cpu.sp = 0x0007;
simple_test_base(&mut cpu, 6);
assert_eq!(cpu.p, 0xA1);
assert_eq!(cpu.sp, 0x0A);
assert_eq!(cpu.pc, 0xDEAD);
}
#[test]
fn jsr() {
let prg = vec![
0x20, 0xAD, 0xDE, ];
let mut cpu = init_cpu(prg);
cpu.sp = 0x000A;
simple_test_base(&mut cpu, 6);
assert_eq!(cpu.pc, 0xDEAD);
assert_eq!(cpu.read_u8(0x010A), 0x40);
assert_eq!(cpu.read_u8(0x0109), 0x22);
}
#[test]
fn sbc() {
let prg = vec![
0xE9, 0x01, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0xFF);
}
#[test]
fn sbc2() {
let prg = vec![
0xE9, 0x40, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x40;
mask_set!(cpu.p, Flags::Carry as u8);
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0x00);
assert_eq!(mask_is_set!(cpu.p, Flags::Carry as u8), true);
assert_eq!(mask_is_set!(cpu.p, Flags::Zero as u8), true);
assert_eq!(mask_is_set!(cpu.p, Flags::Overflow as u8), false);
assert_eq!(mask_is_set!(cpu.p, Flags::Negative as u8), false);
}
#[test]
fn sbc_overflow_1() {
let prg = vec![
0xE9, 0x01, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x00;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0xFE);
assert_eq!(mask_is_set!(cpu.p, Flags::Overflow as u8), false);
}
#[test]
fn sbc_overflow_2() {
let prg = vec![
0xE9, 0x01, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x80;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0x7E);
assert_eq!(mask_is_set!(cpu.p, Flags::Overflow as u8), true);
}
#[test]
fn isb() {
let prg = vec![
0xE7, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.a = 0x01;
cpu.write_u8(0x02, 0x00);
simple_test_base(&mut cpu, 5);
assert_eq!(cpu.a, 0xFF);
assert_eq!(cpu.read_u8(0x02), 0x01);
}
#[test]
fn rts() {
let prg = vec![
0x60, ];
let mut cpu = init_cpu(prg);
cpu.write_u8(0x10A, 0xDE);
cpu.write_u8(0x109, 0xAC);
cpu.sp = 0x0008;
simple_test_base(&mut cpu, 6);
assert_eq!(cpu.sp, 0x0A);
assert_eq!(cpu.pc, 0xDEAD);
}
#[test]
fn stx() {
let prg = vec![
0x86, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.x = 0xA5;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.read_u8(0x02), 0xA5);
}
#[test]
fn tax() {
let prg = vec![
0xAA, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xA5;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.x, 0xA5);
}
#[test]
fn tay() {
let prg = vec![
0xA8, ];
let mut cpu = init_cpu(prg);
cpu.a = 0xA5;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.y, 0xA5);
}
#[test]
fn tsx() {
let prg = vec![
0xBA, ];
let mut cpu = init_cpu(prg);
cpu.sp = 0xA5;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.x, 0xA5);
}
#[test]
fn txa() {
let prg = vec![
0x8A, ];
let mut cpu = init_cpu(prg);
cpu.x = 0xA5;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0xA5);
}
#[test]
fn txs() {
let prg = vec![
0x9A, ];
let mut cpu = init_cpu(prg);
cpu.x = 0xA5;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.sp, 0xA5);
}
#[test]
fn tya() {
let prg = vec![
0x98, ];
let mut cpu = init_cpu(prg);
cpu.y = 0xA5;
simple_test_base(&mut cpu, 2);
assert_eq!(cpu.a, 0xA5);
}
#[test]
fn sty() {
let prg = vec![
0x84, 0x02, ];
let mut cpu = init_cpu(prg);
cpu.y = 0xA5;
simple_test_base(&mut cpu, 3);
assert_eq!(cpu.read_u8(0x02), 0xA5);
}
#[test]
fn brk() {
}
#[test]
fn is_holding() {
let prg = vec![
0x4C, 0x20, 0x40, ];
let cpu = simple_test(prg, 2);
assert_eq!(cpu.is_holding(), true);
}
#[test]
fn irq_interrupt_not_masked() {
let prg = vec![
0xEA ];
let mut cpu = init_cpu(prg);
cpu.set_flag_bit(Flags::InterruptDisable, false);
cpu.raise_interrupt(Interrupt::Irq);
cpu.tick();
cpu.tick();
assert_eq!(cpu.pc, 0x4030);
}
#[test]
fn irq_interrupt_masked() {
let prg = vec![
0xEA ];
let mut cpu = init_cpu(prg);
cpu.set_flag_bit(Flags::InterruptDisable, true);
cpu.raise_interrupt(Interrupt::Irq);
cpu.tick();
cpu.tick();
assert_eq!(cpu.pc, 0x4021);
}
#[test]
fn b_flag() {
let prg = vec![
0xA9, 0xFF, 0x48, 0x28, ];
let cpu = simple_test(prg, 9);
assert_eq!(cpu.p, 0xEF);
}
mod helper {
use super::*;
pub struct FakeBus {
memmap: Vec<u8>, }
impl Default for FakeBus {
fn default() -> Self {
FakeBus {
memmap: vec![],
}
}
}
impl FakeBus {
pub fn from(prg_rom: Vec<u8>) -> Self {
let mut rom = vec![0x00; 0x10000];
for (i, byte) in prg_rom.iter().enumerate() {
rom[0x4020 + i] = *byte;
}
rom[0xFFFC] = 0x20;
rom[0xFFFD] = 0x40;
rom[0xFFFA] = 0x20;
rom[0xFFFB] = 0x40;
rom[0xFFFE] = 0x30;
rom[0xFFFF] = 0x40;
FakeBus {
memmap: rom,
}
}
}
impl IoAccess for FakeBus {
fn read_byte(&self, addr: u16) -> u8 {
self.memmap[addr as usize]
}
fn write_byte(&mut self, addr: u16, data: u8) {
self.memmap[addr as usize] = data;
}
}
pub fn simple_test(prg: Vec<u8>, ticks: usize) -> Cpu<FakeBus> {
let mut cpu = init_cpu(prg);
cpu.p = 0x00;
simple_test_base(&mut cpu, ticks);
cpu
}
pub fn simple_test_base(cpu: &mut Cpu<FakeBus>, ticks: usize) {
run_cpu(cpu, ticks);
}
pub fn init_cpu(prg: Vec<u8>) -> Cpu<FakeBus> {
let bus = FakeBus::from(prg);
let mut cpu: Cpu<FakeBus> = Cpu::default();
cpu.set_debug(true);
cpu.load_bus(bus);
cpu
}
pub fn run_cpu(cpu: &mut Cpu<FakeBus>, ticks: usize) {
cpu.tick();
for _ in 0..ticks {
cpu.tick();
}
}
}
}