nesrust/core/src/cpu/mod.rs
2025-01-14 16:10:37 -05:00

261 lines
6.4 KiB
Rust

mod disassembler;
mod op;
mod operations;
use crate::memory::MemoryBus;
use crate::Clock;
use bitflags::bitflags;
const STACK_ADDR: u16 = 0x0100;
/// Represents a 6502 CPU
struct Cpu<'a> {
/// The registers of the CPU
registers: CpuRegisters,
/// The memory bus accessible by the CPU
memory_bus: &'a MemoryBus,
/// The number of cycles ran on the CPU
cycle: u32,
/// The amount of cycles the CPU will be busy for (won't execute any instruction)
busy_cycle_count: u16,
/// Whether an OAM DMA was triggered (data transfer to the PPU OAM memory)
oam_dma_triggered: bool,
/// Whether an interrupt was requested
nmi_requested: bool,
}
/// Represents the registers of the 6502 CPU
#[derive(Copy, Clone)]
struct CpuRegisters {
/// The program counter
pc: u16,
/// The stack pointer
sp: u8,
/// The accumulator
a: u8,
/// The X general purpose register
x: u8,
/// The Y general purpose register
y: u8,
/// The status flags
status: u8,
}
bitflags! {
pub struct CpuStatus: u8 {
const Carry = 0b00000001;
const Zero = 0b00000010;
const InterruptDisable = 0b00000100;
const Decimal = 0b00001000;
const Break = 0b00010000;
const Overflow = 0b01000000;
const Negative = 0b10000000;
}
}
pub trait CpuInternals {
/// Gets a status flag
///
/// # Arguments
/// * `flag` - The status flag to get
fn get_status_flag(&self, flag: CpuStatus) -> bool;
/// Gets a status flag as a byte
///
/// # Arguments
/// * `flag` - The status flag to get
///
/// # Returns
/// `1` if the flag is `true`, `0` if `false`
fn get_status_flag_u8(&self, flag: CpuStatus) -> u8;
/// Gets the next byte in the program
fn program_get_next_byte(&mut self) -> u8;
/// Gets the next word in the program
fn program_get_next_word(&mut self) -> u16;
/// Sets a status flag
///
/// # Arguments
/// * `flag` - The status flag to set
/// * `value` - Whether the flag is set
fn set_status_flag(&mut self, flag: CpuStatus, set: bool);
/// Sets a status flag from a byte.
/// The value `0` will become `false`, and any other value will be `true`.
///
/// # Arguments
/// * `flag` - The status flag to set
/// * `value` - The value of the flag
fn set_status_flag_u8(&mut self, flag: CpuStatus, value: u8);
/// Pushes a value to the top of the stack
///
/// # Arguments
/// * `value` - The value to push
fn stack_push(&mut self, value: u8);
/// Pushes a word value to the top of the stack
///
/// # Arguments
/// * `value` - The word to push
fn stack_push_word(&mut self, value: u16);
/// Pops the byte from the top of the stack
///
/// # Returns
/// The byte on the top of the stack
fn stack_pop(&mut self) -> u8;
/// Pops a word from the top of the stack
///
/// # Returns
/// The word on the top of the stack
fn stack_pop_word(&mut self) -> u16;
/// Pushes the context to the top of the stack
/// The context consists of the program counter and the status register.
fn stack_push_context(&mut self);
/// Pops the context from the top of the stack
fn stack_pop_context(&mut self);
}
impl Cpu {
pub fn new<'a>(memory_bus: &mut MemoryBus) -> Self {
Cpu {
registers: CpuRegisters {
pc: 0x8000,
sp: 0xFD,
a: 0,
x: 0,
y: 0,
status: 0x04,
},
memory_bus,
cycle: 0,
busy_cycle_count: 0,
oam_dma_triggered: false,
nmi_requested: false,
}
}
}
impl CpuInternals for Cpu {
fn get_status_flag(&self, flag: CpuStatus) -> bool {
*self.registers.status & flag
}
fn get_status_flag_u8(&self, flag: CpuStatus) -> u8 {
let status = self.get_status_flag(flag);
if status {
1
} else {
0
}
}
fn program_get_next_byte(&mut self) -> u8 {
let byte = self.memory_bus.get_byte(self.registers.pc);
self.registers.pc += 1;
byte
}
fn program_get_next_word(&mut self) -> u16 {
let word = self.memory_bus.get_word(self.registers.pc);
self.registers.pc += 2;
word
}
fn set_status_flag(&mut self, flag: CpuStatus, set: bool) {
if set {
*self.registers.status |= flag;
} else {
*self.registers.status &= !flag;
}
}
fn set_status_flag_u8(&mut self, flag: CpuStatus, value: u8) {
let set = if value == 0 { false } else { true };
self.set_status_flag(flag, set);
}
fn stack_push(&mut self, value: u8) {
assert!(self.registers.sp > 0);
let addr = STACK_ADDR | self.registers.sp as u16;
self.memory_bus.set_byte(addr, value);
self.registers.sp -= 1;
}
fn stack_push_word(&mut self, value: u16) {
self.stack_push((value >> 8) as u8);
self.stack_push((value & 0xFF) as u8);
}
fn stack_pop(&mut self) -> u8 {
assert!(self.registers.sp < 0xff);
self.registers.sp += 1;
let addr = STACK_ADDR | self.registers.sp as u16;
self.memory_bus.get_byte(addr)
}
fn stack_pop_word(&mut self) -> u16 {
let low = self.stack_pop() as u16;
let high = self.stack_pop() as u16;
low | (high << 8)
}
fn stack_push_context(&mut self) {
self.stack_push((self.registers.pc >> 8) as u8);
self.stack_push((self.registers.pc & 0xff) as u8);
self.stack_push(self.registers.status);
}
fn stack_pop_context(&mut self) {
let mut status = self.stack_pop();
status &= 0xEF;
status |= 0x20;
let mut pc = self.stack_pop() as u16;
pc += (self.stack_pop() as u16) << 8;
self.registers.status = status;
self.registers.pc = pc;
}
}
impl Clock for Cpu {
fn cycle(&mut self) {
self.cycle += 1;
if self.busy_cycle_count > 0 {
self.busy_cycle_count -= 1;
return;
}
self.disassemble_next_instr();
let op_code = self.program_get_next_byte();
let instr = Self::INSTRUCTIONS[op_code];
self.busy_cycle_count = instr.cycles();
self.exec_instruction(instr);
}
}