use crate::core::space::{AddressSpace, MemAttrs};
use crate::core::value::Width;
use super::isa::{Access, Insn, Mode, Op, decode};
use super::{Config, Interrupt, Lines, Regs, flags};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Source {
Brk,
Irq,
Nmi,
}
const NMI_VECTOR: u16 = 0xfffa;
const RESET_VECTOR: u16 = 0xfffc;
const IRQ_VECTOR: u16 = 0xfffe;
const JAM_TAIL: u32 = 9;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct State {
pub regs: Regs,
pub cycles: u64,
pub halted: bool,
pub reset_pending: bool,
pub pending: Option<Interrupt>,
pub open_bus: u8,
pub faults: u64,
pub last_fault: u16,
}
impl State {
pub(super) const fn new() -> State {
State {
regs: Regs::new(),
cycles: 0,
halted: false,
reset_pending: true,
pending: None,
open_bus: 0,
faults: 0,
last_fault: 0,
}
}
}
pub(super) struct Exec<'a> {
state: &'a mut State,
space: &'a AddressSpace,
cfg: &'a Config,
lines: &'a Lines,
attrs: MemAttrs,
icycle: u32,
skip_poll: bool,
polling: bool,
used: u64,
}
impl<'a> Exec<'a> {
pub(super) fn new(
state: &'a mut State,
space: &'a AddressSpace,
cfg: &'a Config,
lines: &'a Lines,
) -> Exec<'a> {
let attrs = MemAttrs::DEFAULT.with_requester(cfg.requester);
Exec {
state,
space,
cfg,
lines,
attrs,
icycle: 0,
skip_poll: false,
polling: true,
used: 0,
}
}
pub(super) fn step(&mut self) -> u64 {
if self.state.reset_pending {
self.reset_sequence();
} else if self.state.halted {
return 0;
} else if let Some(kind) = self.state.pending.take() {
self.polling = false;
let source = match kind {
Interrupt::Nmi => Source::Nmi,
Interrupt::Irq => Source::Irq,
};
let pc = self.state.regs.pc;
self.read(pc);
self.read(pc);
self.sequence(source);
} else {
self.instruction();
}
self.used
}
fn begin_cycle(&mut self) {
if self.icycle > 0 {
if self.skip_poll {
self.skip_poll = false;
} else {
self.poll();
}
}
self.icycle += 1;
self.used += 1;
self.state.cycles = self.state.cycles.wrapping_add(1);
}
fn poll(&mut self) {
if !self.polling {
return;
}
self.state.pending = if self.lines.nmi_pending() {
Some(Interrupt::Nmi)
} else if self.lines.irq_asserted() && !self.flag(flags::I) {
Some(Interrupt::Irq)
} else {
None
};
}
fn read(&mut self, addr: u16) -> u8 {
self.begin_cycle();
match self.space.read(u64::from(addr), Width::U8, self.attrs) {
Ok(v) => {
let byte = v as u8;
self.state.open_bus = byte;
byte
}
Err(_) => {
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
self.state.open_bus
}
}
}
fn write(&mut self, addr: u16, value: u8) {
self.begin_cycle();
self.state.open_bus = value;
if self
.space
.write(u64::from(addr), Width::U8, u64::from(value), self.attrs)
.is_err()
{
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
}
}
fn fetch(&mut self) -> u8 {
let pc = self.state.regs.pc;
let byte = self.read(pc);
self.state.regs.pc = pc.wrapping_add(1);
byte
}
fn push(&mut self, value: u8) {
let s = self.state.regs.s;
self.write(0x0100 | u16::from(s), value);
self.state.regs.s = s.wrapping_sub(1);
}
fn pull(&mut self) -> u8 {
let s = self.state.regs.s.wrapping_add(1);
self.state.regs.s = s;
self.read(0x0100 | u16::from(s))
}
fn peek_stack(&mut self) {
let addr = 0x0100 | u16::from(self.state.regs.s);
self.read(addr);
}
fn flag(&self, mask: u8) -> bool {
self.state.regs.p & mask != 0
}
fn set_flag(&mut self, mask: u8, on: bool) {
if on {
self.state.regs.p |= mask;
} else {
self.state.regs.p &= !mask;
}
}
fn set_nz(&mut self, value: u8) {
self.set_flag(flags::Z, value == 0);
self.set_flag(flags::N, value & 0x80 != 0);
}
fn sequence(&mut self, source: Source) {
let pc = self.state.regs.pc;
self.push((pc >> 8) as u8);
self.push(pc as u8);
let stolen = self.lines.take_nmi_pending();
let vector = if stolen || source == Source::Nmi {
NMI_VECTOR
} else {
IRQ_VECTOR
};
let pushed = match source {
Source::Brk => self.state.regs.p | flags::B | flags::U,
Source::Irq | Source::Nmi => (self.state.regs.p | flags::U) & !flags::B,
};
self.push(pushed);
self.set_flag(flags::I, true);
let lo = self.read(vector);
let hi = self.read(vector.wrapping_add(1));
self.state.regs.pc = u16::from(lo) | (u16::from(hi) << 8);
}
fn reset_sequence(&mut self) {
self.polling = false;
self.state.reset_pending = false;
self.state.halted = false;
self.state.pending = None;
let pc = self.state.regs.pc;
self.read(pc);
self.read(pc);
for _ in 0..3 {
self.peek_stack();
self.state.regs.s = self.state.regs.s.wrapping_sub(1);
}
self.state.regs.p |= flags::U | flags::I;
let lo = self.read(RESET_VECTOR);
let hi = self.read(RESET_VECTOR.wrapping_add(1));
self.state.regs.pc = u16::from(lo) | (u16::from(hi) << 8);
}
fn instruction(&mut self) {
let opcode = self.fetch();
let insn = decode(opcode);
match insn.op {
Op::BRK => {
self.polling = false;
self.fetch();
self.sequence(Source::Brk);
}
Op::JSR => self.jsr(),
Op::RTS => self.rts(),
Op::RTI => self.rti(),
Op::PHA | Op::PHP => self.push_insn(insn.op),
Op::PLA | Op::PLP => self.pull_insn(insn.op),
Op::JMP => self.jmp(insn.mode),
Op::JAM => self.jam(),
Op::BPL | Op::BMI | Op::BVC | Op::BVS | Op::BCC | Op::BCS | Op::BNE | Op::BEQ => {
self.branch(insn.op);
}
_ => self.operate(insn),
}
}
fn operate(&mut self, insn: Insn) {
let loc = self.resolve(insn.mode, insn.access);
match insn.access {
Access::None => self.implied(insn.op, insn.mode),
Access::Read => {
let value = if insn.mode == Mode::Immediate {
loc.immediate
} else {
self.read(loc.addr)
};
self.read_op(insn.op, value);
}
Access::Write => {
let (addr, value) = self.write_op(insn.op, loc);
self.write(addr, value);
}
Access::Modify => {
let old = self.read(loc.addr);
self.write(loc.addr, old);
let new = self.modify_op(insn.op, old);
self.write(loc.addr, new);
}
}
}
fn resolve(&mut self, mode: Mode, access: Access) -> Located {
let mut out = Located::default();
match mode {
Mode::Implied | Mode::Accumulator => {
let pc = self.state.regs.pc;
self.read(pc);
}
Mode::Immediate => out.immediate = self.fetch(),
Mode::ZeroPage => out.addr = u16::from(self.fetch()),
Mode::ZeroPageX | Mode::ZeroPageY => {
let base = self.fetch();
self.read(u16::from(base));
let index = if mode == Mode::ZeroPageX {
self.state.regs.x
} else {
self.state.regs.y
};
out.addr = u16::from(base.wrapping_add(index));
}
Mode::Absolute => {
let lo = self.fetch();
let hi = self.fetch();
out.base_hi = hi;
out.addr = u16::from(lo) | (u16::from(hi) << 8);
}
Mode::AbsoluteX | Mode::AbsoluteY => {
let lo = self.fetch();
let hi = self.fetch();
let base = u16::from(lo) | (u16::from(hi) << 8);
let index = if mode == Mode::AbsoluteX {
self.state.regs.x
} else {
self.state.regs.y
};
out.base_hi = hi;
out.addr = self.index(base, index, access, &mut out.crossed);
}
Mode::IndirectX => {
let ptr = self.fetch();
self.read(u16::from(ptr));
let at = ptr.wrapping_add(self.state.regs.x);
let lo = self.read(u16::from(at));
let hi = self.read(u16::from(at.wrapping_add(1)));
out.base_hi = hi;
out.addr = u16::from(lo) | (u16::from(hi) << 8);
}
Mode::IndirectY => {
let ptr = self.fetch();
let lo = self.read(u16::from(ptr));
let hi = self.read(u16::from(ptr.wrapping_add(1)));
let base = u16::from(lo) | (u16::from(hi) << 8);
out.base_hi = hi;
out.addr = self.index(base, self.state.regs.y, access, &mut out.crossed);
}
Mode::Relative | Mode::Indirect | Mode::Break => {
debug_assert!(false, "{mode:?} is resolved by its own handler");
}
}
out
}
fn index(&mut self, base: u16, index: u8, access: Access, crossed: &mut bool) -> u16 {
let addr = base.wrapping_add(u16::from(index));
*crossed = (addr & 0xff00) != (base & 0xff00);
if *crossed || access != Access::Read {
let unfixed = (base & 0xff00) | (addr & 0x00ff);
self.read(unfixed);
}
addr
}
fn implied(&mut self, op: Op, mode: Mode) {
let regs = self.state.regs;
match op {
Op::CLC => self.set_flag(flags::C, false),
Op::SEC => self.set_flag(flags::C, true),
Op::CLD => self.set_flag(flags::D, false),
Op::SED => self.set_flag(flags::D, true),
Op::CLV => self.set_flag(flags::V, false),
Op::CLI => self.set_flag(flags::I, false),
Op::SEI => self.set_flag(flags::I, true),
Op::INX => {
let v = regs.x.wrapping_add(1);
self.state.regs.x = v;
self.set_nz(v);
}
Op::INY => {
let v = regs.y.wrapping_add(1);
self.state.regs.y = v;
self.set_nz(v);
}
Op::DEX => {
let v = regs.x.wrapping_sub(1);
self.state.regs.x = v;
self.set_nz(v);
}
Op::DEY => {
let v = regs.y.wrapping_sub(1);
self.state.regs.y = v;
self.set_nz(v);
}
Op::TAX => {
self.state.regs.x = regs.a;
self.set_nz(regs.a);
}
Op::TAY => {
self.state.regs.y = regs.a;
self.set_nz(regs.a);
}
Op::TXA => {
self.state.regs.a = regs.x;
self.set_nz(regs.x);
}
Op::TYA => {
self.state.regs.a = regs.y;
self.set_nz(regs.y);
}
Op::TSX => {
self.state.regs.x = regs.s;
self.set_nz(regs.s);
}
Op::TXS => self.state.regs.s = regs.x,
Op::NOP => {}
Op::ASL | Op::LSR | Op::ROL | Op::ROR => {
debug_assert_eq!(mode, Mode::Accumulator);
let v = self.shift(op, regs.a);
self.state.regs.a = v;
}
other => debug_assert!(false, "{other:?} is not an implied instruction"),
}
}
fn read_op(&mut self, op: Op, value: u8) {
let a = self.state.regs.a;
match op {
Op::LDA => {
self.state.regs.a = value;
self.set_nz(value);
}
Op::LDX => {
self.state.regs.x = value;
self.set_nz(value);
}
Op::LDY => {
self.state.regs.y = value;
self.set_nz(value);
}
Op::LAX => {
self.state.regs.a = value;
self.state.regs.x = value;
self.set_nz(value);
}
Op::ORA => {
let v = a | value;
self.state.regs.a = v;
self.set_nz(v);
}
Op::AND => {
let v = a & value;
self.state.regs.a = v;
self.set_nz(v);
}
Op::EOR => {
let v = a ^ value;
self.state.regs.a = v;
self.set_nz(v);
}
Op::ADC => self.adc(value),
Op::SBC | Op::USBC => self.sbc(value),
Op::CMP => self.compare(a, value),
Op::CPX => {
let x = self.state.regs.x;
self.compare(x, value);
}
Op::CPY => {
let y = self.state.regs.y;
self.compare(y, value);
}
Op::BIT => {
self.set_flag(flags::Z, a & value == 0);
self.set_flag(flags::N, value & 0x80 != 0);
self.set_flag(flags::V, value & 0x40 != 0);
}
Op::NOP => {}
Op::ANC => {
let v = a & value;
self.state.regs.a = v;
self.set_nz(v);
self.set_flag(flags::C, v & 0x80 != 0);
}
Op::ALR => {
let t = a & value;
self.set_flag(flags::C, t & 0x01 != 0);
let v = t >> 1;
self.state.regs.a = v;
self.set_nz(v);
}
Op::ARR => self.arr(value),
Op::ANE => {
let v = (a | self.cfg.magic) & self.state.regs.x & value;
self.state.regs.a = v;
self.set_nz(v);
}
Op::LXA => {
let v = (a | self.cfg.magic) & value;
self.state.regs.a = v;
self.state.regs.x = v;
self.set_nz(v);
}
Op::SBX => {
let t = a & self.state.regs.x;
self.set_flag(flags::C, t >= value);
let v = t.wrapping_sub(value);
self.state.regs.x = v;
self.set_nz(v);
}
Op::LAS => {
let v = value & self.state.regs.s;
self.state.regs.a = v;
self.state.regs.x = v;
self.state.regs.s = v;
self.set_nz(v);
}
other => debug_assert!(false, "{other:?} is not a read instruction"),
}
}
fn write_op(&mut self, op: Op, loc: Located) -> (u16, u8) {
let regs = self.state.regs;
match op {
Op::STA => (loc.addr, regs.a),
Op::STX => (loc.addr, regs.x),
Op::STY => (loc.addr, regs.y),
Op::SAX => (loc.addr, regs.a & regs.x),
Op::SHA => self.unstable_store(regs.a & regs.x, loc),
Op::SHX => self.unstable_store(regs.x, loc),
Op::SHY => self.unstable_store(regs.y, loc),
Op::TAS => {
self.state.regs.s = regs.a & regs.x;
self.unstable_store(regs.a & regs.x, loc)
}
other => {
debug_assert!(false, "{other:?} is not a write instruction");
(loc.addr, regs.a)
}
}
}
fn unstable_store(&mut self, reg: u8, loc: Located) -> (u16, u8) {
let value = reg & loc.base_hi.wrapping_add(1);
let addr = if loc.crossed {
(u16::from(value) << 8) | (loc.addr & 0x00ff)
} else {
loc.addr
};
(addr, value)
}
fn modify_op(&mut self, op: Op, value: u8) -> u8 {
match op {
Op::ASL | Op::LSR | Op::ROL | Op::ROR => self.shift(op, value),
Op::INC => {
let v = value.wrapping_add(1);
self.set_nz(v);
v
}
Op::DEC => {
let v = value.wrapping_sub(1);
self.set_nz(v);
v
}
Op::SLO => {
let v = self.shift(Op::ASL, value);
let a = self.state.regs.a | v;
self.state.regs.a = a;
self.set_nz(a);
v
}
Op::SRE => {
let v = self.shift(Op::LSR, value);
let a = self.state.regs.a ^ v;
self.state.regs.a = a;
self.set_nz(a);
v
}
Op::RLA => {
let v = self.shift(Op::ROL, value);
let a = self.state.regs.a & v;
self.state.regs.a = a;
self.set_nz(a);
v
}
Op::RRA => {
let v = self.shift(Op::ROR, value);
self.adc(v);
v
}
Op::ISC => {
let v = value.wrapping_add(1);
self.sbc(v);
v
}
Op::DCP => {
let v = value.wrapping_sub(1);
let a = self.state.regs.a;
self.compare(a, v);
v
}
other => {
debug_assert!(false, "{other:?} is not a read-modify-write instruction");
value
}
}
}
fn shift(&mut self, op: Op, value: u8) -> u8 {
let carry_in = u8::from(self.flag(flags::C));
let (result, carry_out) = match op {
Op::ASL => (value << 1, value & 0x80 != 0),
Op::LSR => (value >> 1, value & 0x01 != 0),
Op::ROL => ((value << 1) | carry_in, value & 0x80 != 0),
Op::ROR => ((value >> 1) | (carry_in << 7), value & 0x01 != 0),
other => {
debug_assert!(false, "{other:?} is not a shift");
(value, false)
}
};
self.set_flag(flags::C, carry_out);
self.set_nz(result);
result
}
fn adc(&mut self, m: u8) {
let a = self.state.regs.a;
let c = u16::from(self.flag(flags::C));
let binary = u16::from(a) + u16::from(m) + c;
if self.decimal() {
let mut low = u16::from(a & 0x0f) + u16::from(m & 0x0f) + c;
if low >= 0x0a {
low = ((low + 0x06) & 0x0f) + 0x10;
}
let mut sum = u16::from(a & 0xf0) + u16::from(m & 0xf0) + low;
let intermediate = sum as u8;
self.set_flag(flags::N, intermediate & 0x80 != 0);
self.set_flag(flags::V, (!(a ^ m) & (a ^ intermediate) & 0x80) != 0);
self.set_flag(flags::Z, (binary as u8) == 0);
if sum >= 0xa0 {
sum += 0x60;
}
self.set_flag(flags::C, sum >= 0x100);
self.state.regs.a = sum as u8;
} else {
let result = binary as u8;
self.set_flag(flags::C, binary > 0xff);
self.set_flag(flags::V, (!(a ^ m) & (a ^ result) & 0x80) != 0);
self.state.regs.a = result;
self.set_nz(result);
}
}
fn sbc(&mut self, m: u8) {
let a = self.state.regs.a;
let borrow = i32::from(!self.flag(flags::C));
let binary = i32::from(a) - i32::from(m) - borrow;
let result = binary as u8;
self.set_flag(flags::C, binary >= 0);
self.set_flag(flags::V, ((a ^ m) & (a ^ result) & 0x80) != 0);
self.set_nz(result);
self.state.regs.a = if self.decimal() {
let mut low = i32::from(a & 0x0f) - i32::from(m & 0x0f) - borrow;
if low < 0 {
low = ((low - 0x06) & 0x0f) - 0x10;
}
let mut sum = i32::from(a & 0xf0) - i32::from(m & 0xf0) + low;
if sum < 0 {
sum -= 0x60;
}
sum as u8
} else {
result
};
}
fn compare(&mut self, reg: u8, m: u8) {
self.set_flag(flags::C, reg >= m);
let result = reg.wrapping_sub(m);
self.set_nz(result);
}
fn arr(&mut self, m: u8) {
let t = self.state.regs.a & m;
let carry_in = self.flag(flags::C);
let rotated = (t >> 1) | (u8::from(carry_in) << 7);
if self.decimal() {
self.set_flag(flags::N, carry_in);
self.set_flag(flags::Z, rotated == 0);
self.set_flag(flags::V, (t ^ rotated) & 0x40 != 0);
let mut out = rotated;
if u16::from(t & 0x0f) + u16::from(t & 0x01) > 0x05 {
out = (out & 0xf0) | (out.wrapping_add(0x06) & 0x0f);
}
if u16::from(t & 0xf0) + u16::from(t & 0x10) > 0x50 {
out = out.wrapping_add(0x60);
self.set_flag(flags::C, true);
} else {
self.set_flag(flags::C, false);
}
self.state.regs.a = out;
} else {
self.state.regs.a = rotated;
self.set_nz(rotated);
self.set_flag(flags::C, t & 0x80 != 0);
self.set_flag(flags::V, (t ^ (t << 1)) & 0x80 != 0);
}
}
fn decimal(&self) -> bool {
self.cfg.decimal && self.flag(flags::D)
}
fn branch(&mut self, op: Op) {
let offset = self.fetch();
let taken = match op {
Op::BPL => !self.flag(flags::N),
Op::BMI => self.flag(flags::N),
Op::BVC => !self.flag(flags::V),
Op::BVS => self.flag(flags::V),
Op::BCC => !self.flag(flags::C),
Op::BCS => self.flag(flags::C),
Op::BNE => !self.flag(flags::Z),
Op::BEQ => self.flag(flags::Z),
other => {
debug_assert!(false, "{other:?} is not a branch");
false
}
};
if !taken {
return;
}
self.skip_poll = true;
let pc = self.state.regs.pc;
self.read(pc);
let target = pc.wrapping_add(offset as i8 as u16);
if (target & 0xff00) != (pc & 0xff00) {
self.read((pc & 0xff00) | (target & 0x00ff));
}
self.state.regs.pc = target;
}
fn jmp(&mut self, mode: Mode) {
let lo = self.fetch();
let hi = self.fetch();
let ptr = u16::from(lo) | (u16::from(hi) << 8);
self.state.regs.pc = match mode {
Mode::Absolute => ptr,
Mode::Indirect => {
let target_lo = self.read(ptr);
let wrapped = (ptr & 0xff00) | u16::from((ptr as u8).wrapping_add(1));
let target_hi = self.read(wrapped);
u16::from(target_lo) | (u16::from(target_hi) << 8)
}
other => {
debug_assert!(false, "JMP cannot use {other:?}");
ptr
}
};
}
fn jsr(&mut self) {
let lo = self.fetch();
self.peek_stack();
let ret = self.state.regs.pc;
self.push((ret >> 8) as u8);
self.push(ret as u8);
let hi = self.read(ret);
self.state.regs.pc = u16::from(lo) | (u16::from(hi) << 8);
}
fn rts(&mut self) {
let pc = self.state.regs.pc;
self.read(pc);
self.peek_stack();
let lo = self.pull();
let hi = self.pull();
let target = u16::from(lo) | (u16::from(hi) << 8);
self.read(target);
self.state.regs.pc = target.wrapping_add(1);
}
fn rti(&mut self) {
let pc = self.state.regs.pc;
self.read(pc);
self.peek_stack();
let p = self.pull();
self.state.regs.p = (p | flags::U) & !flags::B;
let lo = self.pull();
let hi = self.pull();
self.state.regs.pc = u16::from(lo) | (u16::from(hi) << 8);
}
fn push_insn(&mut self, op: Op) {
let pc = self.state.regs.pc;
self.read(pc);
let value = match op {
Op::PHA => self.state.regs.a,
_ => self.state.regs.p | flags::B | flags::U,
};
self.push(value);
}
fn pull_insn(&mut self, op: Op) {
let pc = self.state.regs.pc;
self.read(pc);
self.peek_stack();
let value = self.pull();
match op {
Op::PLA => {
self.state.regs.a = value;
self.set_nz(value);
}
_ => self.state.regs.p = (value | flags::U) & !flags::B,
}
}
fn jam(&mut self) {
let pc = self.state.regs.pc;
self.read(pc);
for cycle in 0..JAM_TAIL {
let addr = if (1..=2).contains(&cycle) {
0xfffe
} else {
0xffff
};
self.read(addr);
}
self.state.halted = true;
}
}
#[derive(Debug, Clone, Copy, Default)]
struct Located {
addr: u16,
immediate: u8,
base_hi: u8,
crossed: bool,
}