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); } }