use crate::bus::Memory;
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 {
pub reg: Registers,
pub cycles: u64,
pub halted: bool,
pub illegal: bool,
nmi_armed: bool,
nmi_pending: bool,
firq_line: bool,
irq_line: bool,
cwai: bool,
sync: bool,
}
impl Cpu {
pub fn new() -> Self {
Self {
reg: Registers::new(),
cycles: 0,
halted: false,
illegal: false,
nmi_armed: false,
nmi_pending: false,
firq_line: false,
irq_line: false,
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.nmi_pending = false;
self.firq_line = false;
self.irq_line = false;
self.cwai = false;
self.sync = false;
}
pub fn set_irq(&mut self, active: bool) {
self.irq_line = active;
}
pub fn set_firq(&mut self, active: bool) {
self.firq_line = active;
}
pub fn trigger_nmi(&mut self) {
if self.nmi_armed {
self.nmi_pending = true;
}
}
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.nmi_pending || self.firq_line || self.irq_line {
self.sync = false;
} else {
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.nmi_pending {
self.nmi_pending = false;
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.firq_line && !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.irq_line && !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.push_byte_s(mem, val as u8); self.push_byte_s(mem, (val >> 8) as u8);
}
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 hi = self.pull_byte_s(mem) as u16;
let lo = self.pull_byte_s(mem) as u16;
(hi << 8) | lo
}
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.push_byte_u(mem, val as u8);
self.push_byte_u(mem, (val >> 8) as u8);
}
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 hi = self.pull_byte_u(mem) as u16;
let lo = self.pull_byte_u(mem) as u16;
(hi << 8) | lo
}
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 hi = self.fetch_byte(mem) as u16;
let lo = self.fetch_byte(mem) as u16;
(hi << 8) | lo
}
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()
}
}
impl fmt::Debug for Cpu {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} cyc={}", self.reg, self.cycles)
}
}
use std::fmt;