use crate::memory::Memory;
use crate::peripheral::BusSignals;
use crate::registers::Registers;
mod opcodes;
pub use opcodes::instruction_cycles;
pub const VEC_RESET: u16 = 0xFFFE;
pub const VEC_NMI: u16 = 0xFFFC;
pub const VEC_SWI: u16 = 0xFFFA;
pub const VEC_IRQ: u16 = 0xFFF8;
pub const VEC_FIRQ: u16 = 0xFFF6;
pub const VEC_SWI2: u16 = 0xFFF4;
pub const VEC_SWI3: u16 = 0xFFF2;
pub struct Cpu {
reg: Registers,
cycles: u64,
halted: bool,
illegal: bool,
nmi_armed: bool,
int_lines: BusSignals,
cwai: bool,
sync: bool,
}
impl Cpu {
pub fn new() -> Self {
Self {
reg: Registers::new(),
cycles: 0,
halted: false,
illegal: false,
nmi_armed: false,
int_lines: BusSignals::default(),
cwai: false,
sync: false,
}
}
pub fn reset(&mut self, mem: &mut impl Memory) {
self.reg = Registers::new();
self.reg.cc.set_irq_inhibit(true);
self.reg.cc.set_firq_inhibit(true);
self.reg.pc = mem.read_word(VEC_RESET);
self.cycles = 0;
self.halted = false;
self.illegal = false;
self.nmi_armed = false;
self.int_lines = BusSignals::default();
self.cwai = false;
self.sync = false;
}
pub fn registers(&self) -> &Registers {
&self.reg
}
pub fn registers_mut(&mut self) -> RegistersMut<'_> {
let prev_s = self.reg.s;
RegistersMut { cpu: self, prev_s }
}
pub fn cycles(&self) -> u64 {
self.cycles
}
pub fn halted(&self) -> bool {
self.halted
}
pub fn set_halted(&mut self, active: bool) {
self.halted = active;
}
pub fn illegal(&self) -> bool {
self.illegal
}
pub fn clear_illegal(&mut self) {
self.illegal = false;
}
pub fn set_irq(&mut self, active: bool) {
if active {
self.int_lines.insert(BusSignals::IRQ);
} else {
self.int_lines.remove(BusSignals::IRQ);
}
}
pub fn set_firq(&mut self, active: bool) {
if active {
self.int_lines.insert(BusSignals::FIRQ);
} else {
self.int_lines.remove(BusSignals::FIRQ);
}
}
pub fn trigger_nmi(&mut self) {
if self.nmi_armed {
self.int_lines.insert(BusSignals::NMI);
}
}
pub fn apply_signals(&mut self, signals: BusSignals, prev: BusSignals) {
if signals.contains(BusSignals::NMI) && !prev.contains(BusSignals::NMI) {
self.trigger_nmi();
}
if signals.contains(BusSignals::FIRQ) {
self.int_lines.insert(BusSignals::FIRQ);
} else {
self.int_lines.remove(BusSignals::FIRQ);
}
if signals.contains(BusSignals::IRQ) {
self.int_lines.insert(BusSignals::IRQ);
} else {
self.int_lines.remove(BusSignals::IRQ);
}
}
pub fn step(&mut self, mem: &mut impl Memory) -> u64 {
if self.halted {
return 1;
}
let start_cycles = self.cycles;
if self.sync {
if !self.int_lines.is_empty() {
self.sync = false;
} else {
self.cycles += 1;
return 1;
}
}
if self.cwai {
let serviceable = self.int_lines.contains(BusSignals::NMI)
|| (self.int_lines.contains(BusSignals::FIRQ) && !self.reg.cc.firq_inhibit())
|| (self.int_lines.contains(BusSignals::IRQ) && !self.reg.cc.irq_inhibit());
if !serviceable {
self.cycles += 1;
return 1;
}
}
if self.check_interrupts(mem) {
return self.cycles - start_cycles;
}
let opcode = self.fetch_byte(mem);
self.execute(mem, opcode);
self.cycles - start_cycles
}
pub fn run(&mut self, mem: &mut impl Memory, cycle_budget: u64) -> u64 {
let start_cycles = self.cycles;
let target = self.cycles + cycle_budget;
while self.cycles < target && !self.halted {
self.step(mem);
}
self.cycles - start_cycles
}
fn check_interrupts(&mut self, mem: &mut impl Memory) -> bool {
if self.int_lines.is_empty() {
return false;
}
if self.int_lines.contains(BusSignals::NMI) {
self.int_lines.remove(BusSignals::NMI);
if !self.cwai {
self.reg.cc.set_entire(true);
self.push_entire_state(mem);
}
self.cwai = false;
self.reg.cc.set_irq_inhibit(true);
self.reg.cc.set_firq_inhibit(true);
self.reg.pc = mem.read_word(VEC_NMI);
self.cycles += 19;
return true;
}
if self.int_lines.contains(BusSignals::FIRQ) && !self.reg.cc.firq_inhibit() {
if !self.cwai {
self.reg.cc.set_entire(false);
self.push_word_s(mem, self.reg.pc);
self.push_byte_s(mem, self.reg.cc.to_byte());
}
self.cwai = false;
self.reg.cc.set_irq_inhibit(true);
self.reg.cc.set_firq_inhibit(true);
self.reg.pc = mem.read_word(VEC_FIRQ);
self.cycles += 10;
return true;
}
if self.int_lines.contains(BusSignals::IRQ) && !self.reg.cc.irq_inhibit() {
if !self.cwai {
self.reg.cc.set_entire(true);
self.push_entire_state(mem);
}
self.cwai = false;
self.reg.cc.set_irq_inhibit(true);
self.reg.pc = mem.read_word(VEC_IRQ);
self.cycles += 19;
return true;
}
false
}
pub(super) fn push_byte_s(&mut self, mem: &mut impl Memory, val: u8) {
self.reg.s = self.reg.s.wrapping_sub(1);
mem.write(self.reg.s, val);
}
pub(super) fn push_word_s(&mut self, mem: &mut impl Memory, val: u16) {
self.reg.s = self.reg.s.wrapping_sub(2);
mem.write_word(self.reg.s, val);
}
pub(super) fn pull_byte_s(&mut self, mem: &mut impl Memory) -> u8 {
let val = mem.read(self.reg.s);
self.reg.s = self.reg.s.wrapping_add(1);
val
}
pub(super) fn pull_word_s(&mut self, mem: &mut impl Memory) -> u16 {
let val = mem.read_word(self.reg.s);
self.reg.s = self.reg.s.wrapping_add(2);
val
}
pub(super) fn push_byte_u(&mut self, mem: &mut impl Memory, val: u8) {
self.reg.u = self.reg.u.wrapping_sub(1);
mem.write(self.reg.u, val);
}
pub(super) fn push_word_u(&mut self, mem: &mut impl Memory, val: u16) {
self.reg.u = self.reg.u.wrapping_sub(2);
mem.write_word(self.reg.u, val);
}
pub(super) fn pull_byte_u(&mut self, mem: &mut impl Memory) -> u8 {
let val = mem.read(self.reg.u);
self.reg.u = self.reg.u.wrapping_add(1);
val
}
pub(super) fn pull_word_u(&mut self, mem: &mut impl Memory) -> u16 {
let val = mem.read_word(self.reg.u);
self.reg.u = self.reg.u.wrapping_add(2);
val
}
pub(super) fn push_entire_state(&mut self, mem: &mut impl Memory) {
self.push_word_s(mem, self.reg.pc);
self.push_word_s(mem, self.reg.u);
self.push_word_s(mem, self.reg.y);
self.push_word_s(mem, self.reg.x);
self.push_byte_s(mem, self.reg.dp);
self.push_byte_s(mem, self.reg.b());
self.push_byte_s(mem, self.reg.a());
self.push_byte_s(mem, self.reg.cc.to_byte());
}
pub(super) fn fetch_byte(&mut self, mem: &mut impl Memory) -> u8 {
let val = mem.read(self.reg.pc);
self.reg.pc = self.reg.pc.wrapping_add(1);
val
}
pub(super) fn fetch_word(&mut self, mem: &mut impl Memory) -> u16 {
let val = mem.read_word(self.reg.pc);
self.reg.pc = self.reg.pc.wrapping_add(2);
val
}
pub(super) fn addr_direct(&mut self, mem: &mut impl Memory) -> u16 {
let lo = self.fetch_byte(mem) as u16;
((self.reg.dp as u16) << 8) | lo
}
pub(super) fn addr_extended(&mut self, mem: &mut impl Memory) -> u16 {
self.fetch_word(mem)
}
pub(super) fn addr_indexed(&mut self, mem: &mut impl Memory) -> (u16, u8) {
crate::addressing::indexed(self, mem)
}
pub(super) fn addr_relative8(&mut self, mem: &mut impl Memory) -> u16 {
let offset = self.fetch_byte(mem) as i8 as i16 as u16;
self.reg.pc.wrapping_add(offset)
}
pub(super) fn addr_relative16(&mut self, mem: &mut impl Memory) -> u16 {
let offset = self.fetch_word(mem);
self.reg.pc.wrapping_add(offset)
}
pub(super) fn arm_nmi(&mut self) {
self.nmi_armed = true;
}
}
impl Default for Cpu {
fn default() -> Self {
Self::new()
}
}
pub struct RegistersMut<'a> {
cpu: &'a mut Cpu,
prev_s: u16,
}
impl std::ops::Deref for RegistersMut<'_> {
type Target = Registers;
fn deref(&self) -> &Registers {
&self.cpu.reg
}
}
impl std::ops::DerefMut for RegistersMut<'_> {
fn deref_mut(&mut self) -> &mut Registers {
&mut self.cpu.reg
}
}
impl Drop for RegistersMut<'_> {
fn drop(&mut self) {
if self.cpu.reg.s != self.prev_s {
self.cpu.nmi_armed = true;
}
}
}
impl fmt::Debug for Cpu {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} cyc={}", self.reg, self.cycles)
}
}
use std::fmt;