vcpu 0.2.0

A virtual cpu, modeled after the 6502
Documentation
use std::{
  fmt::{Debug, Formatter},
  time::Duration,
};

use enumflags2::{bitflags, BitFlag, BitFlags};

use crate::{
  core::bus::{Bus, BusItem},
  error::CpuError,
};

#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum StatusFlags {
  Carry = 1 << 0,
  Zero = 1 << 1,
  Interrupt = 1 << 2,
  Decimal = 1 << 3,
  Break = 1 << 4,
  Unused = 1 << 5,
  Overflow = 1 << 6,
  Negative = 1 << 7,
}

pub struct Cpu {
  data_bus: Bus,

  pub(crate) a: u8,
  pub(crate) x: u8,
  pub(crate) y: u8,
  pub(crate) stack_ptr: u8,
  pub(crate) program_counter: u16,
  pub(crate) status: BitFlags<StatusFlags>,

  clock_speed: f64,
  clock: u64,
  cycles: u8,
}

impl Debug for Cpu {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    write!(
      f,
      "a: {:#04X} ({}) | x: {:#04X} ({}) | y: {:#04X} ({}) | stack_ptr: {:#04X} | prog_counter: {:#06X} | cycles: {}",
      self.a, self.a, self.x, self.x, self.y, self.y, self.stack_ptr, self.program_counter, self.cycles
    )
  }
}

impl Cpu {
  pub const INTERRUPT_ADDRESS: u16 = 0xFFFE;
  pub const RESET_ADDRESS: u16 = 0xFFFC;
  pub const STACK_POINTER_BASE_ADDRESS: u16 = 0x0100;

  pub fn new(clock_speed: f64) -> Self {
    let data_bus = Bus::new();
    let status = Default::default();

    Self {
      data_bus,
      a: 0,
      x: 0,
      y: 0,
      stack_ptr: 0,
      program_counter: 0,
      status,
      clock_speed,
      clock: 0,
      cycles: 0,
    }
  }

  pub fn connect(&mut self, item: impl BusItem + 'static) {
    self.data_bus.connect(item)
  }

  pub fn clock(&mut self) -> Result<(), CpuError> {
    if self.cycles == 0 {
      let state_before = format!("{self:?}");
      let opcode = self.fetch()?;
      let instruction = self.decode(opcode)?;
      let instr_string = format!("  => {instruction:?}");
      self.cycles += self.execute(instruction)?;
      println!(
        "[{}]\nBefore: [{state_before}]\n{instr_string}\nAfter: [{self:?}]\n",
        self.clock
      );
    }

    self.clock += 1;
    self.cycles -= 1;

    Ok(())
  }

  pub fn read(&mut self, address: u16) -> u8 {
    self.data_bus.read(address, false)
  }

  pub fn write(&mut self, address: u16, value: u8) {
    self.data_bus.write(address, value)
  }

  pub fn is_flag(&self, flag: StatusFlags) -> bool {
    self.status.contains(flag)
  }

  pub fn set_flag(&mut self, flag: StatusFlags, set: bool) {
    self.status.set(flag, set);
  }

  pub fn reset(&mut self) {
    self.a = 0;
    self.x = 0;
    self.y = 0;
    self.stack_ptr = 0xFD;
    self.status = StatusFlags::empty() | StatusFlags::Unused;

    let lo = self.read(Self::RESET_ADDRESS);
    let hi = self.read(Self::RESET_ADDRESS + 1);
    self.program_counter = u16::from_le_bytes([lo, hi]);

    self.clock = 0;
    self.cycles = 8;
  }

  pub fn interrupt(&mut self) {
    if !self.is_flag(StatusFlags::Interrupt) {
      let [lo, hi] = self.program_counter.to_le_bytes();
      self.push(hi);
      self.push(lo);

      self.set_flag(StatusFlags::Break, false);
      self.set_flag(StatusFlags::Unused, true);
      self.set_flag(StatusFlags::Interrupt, true);
      self.push(self.status.bits());

      let lo = self.read(Self::INTERRUPT_ADDRESS);
      let hi = self.read(Self::INTERRUPT_ADDRESS + 1);
      self.program_counter = u16::from_le_bytes([lo, hi]);

      self.cycles = 7;
    }
  }

  pub fn non_maskable_interrupt(&mut self) {
    let [lo, hi] = self.program_counter.to_le_bytes();
    self.push(hi);
    self.push(lo);

    self.set_flag(StatusFlags::Break, false);
    self.set_flag(StatusFlags::Unused, true);
    self.set_flag(StatusFlags::Interrupt, true);
    self.push(self.status.bits());

    let lo = self.read(Self::INTERRUPT_ADDRESS);
    let hi = self.read(Self::INTERRUPT_ADDRESS + 1);
    self.program_counter = u16::from_le_bytes([lo, hi]);

    self.cycles = 8;
  }

  pub fn fetch(&mut self) -> Result<u8, CpuError> {
    let value = self.read(self.program_counter);
    self.program_counter += 1;
    Ok(value)
  }

  pub fn pop(&mut self) -> u8 {
    self.stack_ptr += 1;
    self.read(Self::STACK_POINTER_BASE_ADDRESS + self.stack_ptr as u16)
  }

  pub fn push(&mut self, value: u8) {
    self.write(Self::STACK_POINTER_BASE_ADDRESS + self.stack_ptr as u16, value);
    self.stack_ptr -= 1;
  }
}

impl Iterator for Cpu {
  type Item = ();

  fn next(&mut self) -> Option<Self::Item> {
    loop {
      if let Err(error) = self.clock() {
        eprintln!("{error}");
        return None;
      };

      std::thread::sleep(Duration::from_secs_f64(1.0 / self.clock_speed));

      if self.cycles == 0 {
        break;
      }
    }

    Some(())
  }
}