#![warn(missing_docs)]
mod addresses;
mod addressing;
pub mod bus;
mod instructions;
use crate::addresses::RESET_VECTOR;
use crate::addressing::AddressingMode;
use crate::bus::Bus;
use crate::instructions::INSTRUCTION_LIST;
use bitflags::bitflags;
use std::fmt::Display;
pub struct Cpu {
pub a: u8,
pub x: u8,
pub y: u8,
pub sp: u8,
pub pc: u16,
pub status: u8,
pub cycles: u8,
addr_abs: u16,
addr_rel: u16,
addr_mode: AddressingMode,
opcode: u8,
fetched_data: u8,
pub bus: Box<dyn Bus>,
pub variant: Variant,
pub enable_illegal_opcodes: bool,
pub current_instruction_string: String,
pub debug: usize,
}
bitflags! {
pub struct StatusFlags: u8 {
const None = 0b0000_0000;
const Carry = 0b0000_0001;
const Zero = 0b0000_0010;
const InterruptDisable = 0b0000_0100;
const Decimal = 0b0000_1000;
const Break = 0b0001_0000;
const Unused = 0b0010_0000;
const Overflow = 0b0100_0000;
const Negative = 0b1000_0000;
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum Variant {
CMOS,
NES,
NMOS,
}
impl Display for Variant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::CMOS => write!(f, "CMOS"),
Self::NES => write!(f, "NES"),
Self::NMOS => write!(f, "NMOS"),
}
}
}
impl Cpu {
pub fn new(bus: Box<dyn Bus>, debug: usize) -> Cpu {
Cpu {
a: 0,
x: 0,
y: 0,
sp: 0,
pc: 0,
status: StatusFlags::None.bits(),
cycles: 0,
addr_abs: 0,
addr_rel: 0,
addr_mode: AddressingMode::None,
opcode: 0,
fetched_data: 0,
bus,
variant: Variant::CMOS,
enable_illegal_opcodes: false,
current_instruction_string: String::new(),
debug,
}
}
pub fn reset(&mut self) {
if self.debug > 0 {
println!("CPU: Reset");
}
self.a = 0;
self.x = 0;
self.y = 0;
self.sp = 0xfd;
self.pc = self.read_u16(RESET_VECTOR);
self.status = StatusFlags::None.bits()
| StatusFlags::Unused.bits()
| StatusFlags::InterruptDisable.bits();
self.cycles = 8;
self.current_instruction_string = "RESET".to_string();
}
pub fn read(&self, address: u16) -> u8 {
self.bus.read(address)
}
pub fn read_u16(&self, address: u16) -> u16 {
let lo = self.read(address) as u16;
let hi = self.read(address + 1) as u16;
(hi << 8) | lo
}
pub fn write(&mut self, address: u16, value: u8) {
self.bus.write(address, value)
}
pub fn write_u16(&mut self, address: u16, value: u16) {
self.write(address, (value & 0xff) as u8);
self.write(address + 1, ((value >> 8) & 0xff) as u8);
}
fn set_flag(&mut self, flag: StatusFlags, value: bool) {
if value {
self.status |= flag.bits();
} else {
self.status &= !flag.bits();
}
}
fn get_flag(&self, flag: StatusFlags) -> bool {
(self.status & flag.bits()) != 0
}
fn increment_sp(&mut self) {
self.sp = self.sp.wrapping_add(1);
}
fn decrement_sp(&mut self) {
self.sp = self.sp.wrapping_sub(1);
}
pub fn change_variant(&mut self, variant: Variant) {
self.variant = variant;
}
pub fn set_illegal_opcodes(&mut self, value: bool) {
self.enable_illegal_opcodes = value;
}
pub fn get_status_string(&self) -> String {
let mut status = String::new();
status.push_str("STATUS: ");
status.push_str(if self.get_flag(StatusFlags::Negative) {
"N"
} else {
"n"
});
status.push_str(if self.get_flag(StatusFlags::Overflow) {
"V"
} else {
"v"
});
status.push('-');
status.push_str(if self.get_flag(StatusFlags::Break) {
"B"
} else {
"b"
});
status.push_str(if self.get_flag(StatusFlags::Decimal) {
"D"
} else {
"d"
});
status.push_str(if self.get_flag(StatusFlags::InterruptDisable) {
"I"
} else {
"i"
});
status.push_str(if self.get_flag(StatusFlags::Zero) {
"Z"
} else {
"z"
});
status.push_str(if self.get_flag(StatusFlags::Carry) {
"C"
} else {
"c"
});
status
}
fn fetch(&mut self) -> u8 {
if self.addr_mode != AddressingMode::Implied {
self.fetched_data = self.read(self.addr_abs);
}
self.fetched_data
}
fn push(&mut self, value: u8) {
self.write(0x100 + self.sp as u16, value);
self.decrement_sp();
}
fn push_word(&mut self, value: u16) {
self.push(((value >> 8) & 0xff) as u8);
self.push((value & 0xff) as u8);
}
fn pop(&mut self) -> u8 {
self.increment_sp();
self.read(0x100 + self.sp as u16)
}
fn pop_word(&mut self) -> u16 {
let lo = self.pop() as u16;
let hi = self.pop() as u16;
(hi << 8) | lo
}
fn execute_instruction(&mut self, opcode: u8) -> u8 {
let instruction = &INSTRUCTION_LIST[opcode as usize];
(instruction.function)(self)
}
fn get_operand_string(&mut self, mode: AddressingMode, address: u16) -> String {
match mode {
AddressingMode::None => String::from(""),
AddressingMode::Implied => String::from(""),
AddressingMode::Immediate => format!("#${:02X}", self.read(address)),
AddressingMode::ZeroPage => format!("${:02X}", self.read(address)),
AddressingMode::ZeroPageX => format!("${:02X},X", self.read(address)),
AddressingMode::ZeroPageY => format!("${:02X},Y", self.read(address)),
AddressingMode::Relative => format!("${:02X}", self.read(address)),
AddressingMode::Absolute => format!("${:04X}", self.read_u16(address)),
AddressingMode::AbsoluteX => format!("${:04X},X", self.read_u16(address)),
AddressingMode::AbsoluteY => format!("${:04X},Y", self.read_u16(address)),
AddressingMode::Indirect => format!("(${:04X})", self.read_u16(address)),
AddressingMode::IndexedIndirect => format!("(${:02X},X)", self.read(address)),
AddressingMode::IndirectIndexed => format!("(${:02X}),Y", self.read(address)),
}
}
fn disassemble_instruction_at(&mut self, from_pc: u16) -> String {
let opcode = self.read(from_pc);
let instruction = &INSTRUCTION_LIST[opcode as usize];
let addr_mode = instructions::get_addr_mode(opcode);
let addr_str = self.get_operand_string(addr_mode, from_pc + 1);
format!("{} {}", instruction.name, addr_str)
}
fn execute_addr_mode(&mut self, mode: AddressingMode) -> u8 {
self.addr_mode = mode;
let extra_cycle = mode.execute(self);
if extra_cycle {
return 1;
}
0
}
pub fn get_cycles(&self, opcode: u8) -> u8 {
instructions::get_cycles(opcode)
}
fn do_interrupt(&mut self, vector: u16) {
self.push_word(self.pc);
self.set_flag(StatusFlags::Break, false);
self.set_flag(StatusFlags::Unused, true);
self.set_flag(StatusFlags::Break, true);
self.set_flag(StatusFlags::InterruptDisable, true);
self.push(self.status);
self.set_flag(StatusFlags::InterruptDisable, false);
self.pc = self.read_u16(vector);
self.cycles = 7;
}
pub fn irq(&mut self) {
if !self.get_flag(StatusFlags::InterruptDisable) {
self.do_interrupt(addresses::IRQ_VECTOR);
}
}
pub fn nmi(&mut self) {
self.do_interrupt(addresses::NMI_VECTOR);
}
pub fn get_register(&self, register: &str) -> u8 {
match register {
"A" => self.a,
"X" => self.x,
"Y" => self.y,
"SP" => self.sp,
_ => panic!("Invalid register: {}", register),
}
}
fn set_zn_flags(&mut self, value: u8) {
self.set_flag(StatusFlags::Zero, value == 0);
self.set_flag(StatusFlags::Negative, value & 0x80 != 0);
}
pub fn clock(&mut self) {
if self.cycles == 0 {
self.current_instruction_string = self.disassemble_instruction_at(self.pc);
match self.debug {
0 => (),
1 => println!("{}", self.current_instruction_string),
2 => {
println!("{}", self.current_instruction_string);
println!("CPU pre-execute state: {}", self.get_state());
}
_ => panic!("Invalid debug mode: {}", self.debug),
}
self.opcode = self.read(self.pc);
self.pc += 1;
self.cycles = self.get_cycles(self.opcode);
self.addr_mode = instructions::get_addr_mode(self.opcode);
let cycles_addr = self.execute_addr_mode(self.addr_mode);
let cycles_instruction = self.execute_instruction(self.opcode);
self.cycles += cycles_addr + cycles_instruction;
if self.debug > 1 {
println!(
"CPU post-execute state: {}",
self.get_state(),
);
}
}
self.cycles -= 1;
}
pub fn get_state(&self) -> String {
format!(
"A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} PC:{:04X}",
self.a, self.x, self.y, self.status, self.sp, self.pc
)
}
}
#[cfg(test)]
mod cpu_tests {
struct TestBus {
ram: Vec<u8>,
}
impl TestBus {
fn new() -> TestBus {
TestBus {
ram: vec![0; 0x10000],
}
}
}
impl Bus for TestBus {
fn read(&self, address: u16) -> u8 {
self.ram[address as usize]
}
fn write(&mut self, address: u16, value: u8) {
self.ram[address as usize] = value;
}
fn load(&mut self, path: &str, address: u16) {
let mut file = std::fs::File::open(path).unwrap();
file.read(&mut self.ram[address as usize..]).unwrap();
}
}
use super::*;
use std::io::Read;
#[test]
fn test_reset() {
let bus = Box::new(TestBus::new());
let mut cpu = Cpu::new(bus, 0);
cpu.write(RESET_VECTOR, 0x00);
cpu.write(RESET_VECTOR + 1, 0x80);
cpu.reset();
assert_eq!(cpu.pc, 0x8000);
assert_eq!(
cpu.status,
StatusFlags::None.bits()
| StatusFlags::Unused.bits()
| StatusFlags::InterruptDisable.bits()
);
assert_eq!(cpu.sp, 0xfd);
assert_eq!(cpu.a, 0);
assert_eq!(cpu.x, 0);
assert_eq!(cpu.y, 0);
}
#[test]
fn test_read_u16() {
let bus = Box::new(TestBus::new());
let mut cpu = Cpu::new(bus, 0);
cpu.write(0x10, 0x20);
cpu.write(0x11, 0x30);
assert_eq!(cpu.read_u16(0x10), 0x3020);
}
#[test]
fn test_write_u16() {
let bus = Box::new(TestBus::new());
let mut cpu = Cpu::new(bus, 0);
cpu.write_u16(0x10, 0x3020);
assert_eq!(cpu.read(0x10), 0x20);
assert_eq!(cpu.read(0x11), 0x30);
}
#[test]
fn test_read_write() {
let bus = Box::new(TestBus::new());
let mut cpu = Cpu::new(bus, 0);
cpu.write(0x10, 0x20);
assert_eq!(cpu.read(0x10), 0x20);
}
}