lib.rsannotatedlib.rssource237 lines · 6.3 KB · raw
1pub mod bus;
2mod instructions;
3
4use crate::bus::BusInterface;
5use crate::instructions::InstructionState;
6use bincode::{Decode, Encode};
7use jgenesis_common::num::GetBit;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
10pub enum StatusReadContext {
11    HardwareInterruptHandler,
12    Brk,
13    PushStack,
14}
15
16#[derive(Debug, Clone, Copy, Encode, Decode)]
17pub struct StatusFlags {
18    negative: bool,
19    overflow: bool,
20    decimal: bool,
21    interrupt_disable: bool,
22    zero: bool,
23    carry: bool,
24}
25
26impl StatusFlags {
27    #[must_use]
28    pub fn new() -> Self {
29        // I flag defaults to 1, others default to 0
30        Self {
31            negative: false,
32            overflow: false,
33            decimal: false,
34            interrupt_disable: true,
35            zero: false,
36            carry: false,
37        }
38    }
39
40    pub fn set_negative(&mut self, negative: bool) -> &mut Self {
41        self.negative = negative;
42        self
43    }
44
45    pub fn set_overflow(&mut self, overflow: bool) -> &mut Self {
46        self.overflow = overflow;
47        self
48    }
49
50    pub fn set_zero(&mut self, zero: bool) -> &mut Self {
51        self.zero = zero;
52        self
53    }
54
55    pub fn set_carry(&mut self, carry: bool) -> &mut Self {
56        self.carry = carry;
57        self
58    }
59
60    #[must_use]
61    pub fn to_byte(self, read_ctx: StatusReadContext) -> u8 {
62        // B flag is set during BRK and PHA/PHP, cleared during NMI & IRQ handlers
63        let b_flag = match read_ctx {
64            StatusReadContext::Brk | StatusReadContext::PushStack => 0x10,
65            StatusReadContext::HardwareInterruptHandler => 0x00,
66        };
67
68        // Bit 5 is unused, always reads as 1
69        (u8::from(self.negative) << 7)
70            | (u8::from(self.overflow) << 6)
71            | 0x20
72            | b_flag
73            | (u8::from(self.decimal) << 3)
74            | (u8::from(self.interrupt_disable) << 2)
75            | (u8::from(self.zero) << 1)
76            | u8::from(self.carry)
77    }
78
79    #[must_use]
80    pub fn from_byte(byte: u8) -> Self {
81        Self {
82            negative: byte.bit(7),
83            overflow: byte.bit(6),
84            decimal: byte.bit(3),
85            interrupt_disable: byte.bit(2),
86            zero: byte.bit(1),
87            carry: byte.bit(0),
88        }
89    }
90}
91
92impl Default for StatusFlags {
93    fn default() -> Self {
94        Self::new()
95    }
96}
97
98#[derive(Debug, Clone, Encode, Decode)]
99pub struct CpuRegisters {
100    pub accumulator: u8,
101    pub x: u8,
102    pub y: u8,
103    pub status: StatusFlags,
104    pub pc: u16,
105    pub sp: u8,
106    pub enable_decimal_mode: bool,
107}
108
109impl CpuRegisters {
110    fn new(reset_vector: u16, enable_decimal_mode: bool) -> Self {
111        Self {
112            accumulator: 0,
113            x: 0,
114            y: 0,
115            status: StatusFlags::new(),
116            pc: reset_vector,
117            sp: 0xFD,
118            enable_decimal_mode,
119        }
120    }
121
122    fn in_decimal_mode(&self) -> bool {
123        self.enable_decimal_mode && self.status.decimal
124    }
125}
126
127#[derive(Debug, Clone, Encode, Decode)]
128pub struct Mos6502 {
129    registers: CpuRegisters,
130    state: InstructionState,
131    frozen: bool,
132}
133
134const NMI_VECTOR: u16 = 0xFFFA;
135const RESET_VECTOR: u16 = 0xFFFC;
136const IRQ_VECTOR: u16 = 0xFFFE;
137
138impl Mos6502 {
139    /// Create a new standard 6502 with the PC pointing to the RESET vector, read from $FFFC.
140    ///
141    /// In the standard 6502, the decimal mode flag works as intended and toggles BCD arithmetic.
142    pub fn new_standard<B: BusInterface>(bus: &mut B) -> Self {
143        Self::new(bus, true)
144    }
145
146    /// Create a new NES 6502 with the PC pointing to the RESET vector, read from $FFFC.
147    ///
148    /// In the NES 6502, the decimal mode flag does nothing.
149    pub fn new_nes<B: BusInterface>(bus: &mut B) -> Self {
150        Self::new(bus, false)
151    }
152
153    fn new<B: BusInterface>(bus: &mut B, enable_decimal_mode: bool) -> Self {
154        let reset_vector_lsb = bus.read(RESET_VECTOR);
155        let reset_vector_msb = bus.read(RESET_VECTOR + 1);
156        let reset_vector = u16::from_le_bytes([reset_vector_lsb, reset_vector_msb]);
157
158        Self {
159            registers: CpuRegisters::new(reset_vector, enable_decimal_mode),
160            state: InstructionState::default(),
161            frozen: false,
162        }
163    }
164
165    /// Reset the 6502, which does the following:
166    /// * Immediately update PC to point to the RESET vector, abandoning any in-progress instruction
167    /// * Subtract 3 from the stack pointer
168    /// * Disable IRQs
169    /// * If the CPU was frozen by an illegal KIL opcode, unfreeze it
170    pub fn reset<B: BusInterface>(&mut self, bus: &mut B) {
171        let reset_vector_lsb = bus.read(RESET_VECTOR);
172        let reset_vector_msb = bus.read(RESET_VECTOR + 1);
173        self.registers.pc = u16::from_le_bytes([reset_vector_lsb, reset_vector_msb]);
174        self.state = InstructionState::default();
175
176        self.registers.sp = self.registers.sp.wrapping_sub(3);
177
178        self.registers.status.interrupt_disable = true;
179
180        self.frozen = false;
181    }
182
183    /// Run the CPU for 1 cycle.
184    #[inline]
185    pub fn tick<B: BusInterface>(&mut self, bus: &mut B) {
186        if self.frozen {
187            // CPU was frozen by an illegal KIL opcode; do nothing
188            return;
189        }
190
191        if self.state.instruction_complete {
192            // Opcode is always read, even if handling an interrupt
193            let opcode = bus.read(self.registers.pc);
194
195            if self.state.pending_interrupt {
196                self.state.pending_interrupt = false;
197                self.state.executing_interrupt = true;
198            } else {
199                self.registers.pc = self.registers.pc.wrapping_add(1);
200                self.state.opcode = opcode;
201            }
202
203            self.state.instruction_complete = false;
204            self.state.cycle = 0;
205            return;
206        }
207
208        instructions::execute_cycle(self, bus);
209    }
210
211    #[inline]
212    #[must_use]
213    pub fn pc(&self) -> u16 {
214        self.registers.pc
215    }
216
217    #[must_use]
218    pub fn is_mid_instruction(&self) -> bool {
219        !self.state.instruction_complete
220    }
221
222    #[must_use]
223    pub fn registers(&self) -> &CpuRegisters {
224        &self.registers
225    }
226
227    pub fn set_registers(&mut self, registers: CpuRegisters) {
228        self.registers = registers;
229    }
230
231    /// Return whether the CPU has frozen from a KIL instruction.
232    #[inline]
233    #[must_use]
234    pub fn frozen(&self) -> bool {
235        self.frozen
236    }
237}