1mod instructions; 2 3use crate::traits::BusInterface; 4use bincode::{Decode, Encode}; 5use jgenesis_common::num::GetBit; 6 7#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 8enum SizeBits { 9 Eight, 10 Sixteen, 11} 12 13impl SizeBits { 14 fn from_bit(bit: bool) -> Self { 15 if bit { Self::Eight } else { Self::Sixteen } 16 } 17 18 fn to_bit(self) -> bool { 19 self == Self::Eight 20 } 21} 22 23#[derive(Debug, Clone, Copy, Encode, Decode)] 24pub struct StatusRegister { 25 carry: bool, 26 zero: bool, 27 irq_disabled: bool, 28 decimal_mode: bool, 29 index_size: SizeBits, 30 accumulator_size: SizeBits, 31 overflow: bool, 32 negative: bool, 33} 34 35impl Default for StatusRegister { 36 fn default() -> Self { 37 Self { 38 carry: false, 39 zero: false, 40 irq_disabled: true, 41 decimal_mode: false, 42 index_size: SizeBits::Eight, 43 accumulator_size: SizeBits::Eight, 44 overflow: false, 45 negative: false, 46 } 47 } 48} 49 50impl From<StatusRegister> for u8 { 51 fn from(value: StatusRegister) -> Self { 52 (u8::from(value.negative) << 7) 53 | (u8::from(value.overflow) << 6) 54 | (u8::from(value.accumulator_size.to_bit()) << 5) 55 | (u8::from(value.index_size.to_bit()) << 4) 56 | (u8::from(value.decimal_mode) << 3) 57 | (u8::from(value.irq_disabled) << 2) 58 | (u8::from(value.zero) << 1) 59 | u8::from(value.carry) 60 } 61} 62 63impl From<u8> for StatusRegister { 64 fn from(value: u8) -> Self { 65 Self { 66 carry: value.bit(0), 67 zero: value.bit(1), 68 irq_disabled: value.bit(2), 69 decimal_mode: value.bit(3), 70 index_size: SizeBits::from_bit(value.bit(4)), 71 accumulator_size: SizeBits::from_bit(value.bit(5)), 72 overflow: value.bit(6), 73 negative: value.bit(7), 74 } 75 } 76} 77 78#[derive(Debug, Clone, Encode, Decode)] 79pub struct Registers { 80 // Accumulator 81 pub a: u16, 82 // Index registers 83 pub x: u16, 84 pub y: u16, 85 // Stack pointer 86 pub s: u16, 87 // Direct page pointer 88 pub d: u16, 89 // Program bank register 90 pub pbr: u8, 91 // Program counter 92 pub pc: u16, 93 // Data bank register 94 pub dbr: u8, 95 // Status register 96 pub p: StatusRegister, 97 // Emulation mode flag 98 pub emulation_mode: bool, 99} 100 101impl Registers { 102 fn new() -> Self { 103 Self { 104 a: 0, 105 x: 0, 106 y: 0, 107 s: 0x01FF, 108 d: 0, 109 pbr: 0, 110 pc: 0, 111 dbr: 0, 112 p: StatusRegister::default(), 113 // 65816 always powers on in emulation mode 114 emulation_mode: true, 115 } 116 } 117} 118 119#[derive(Debug, Clone, Default, Encode, Decode)] 120struct State { 121 cycle: u8, 122 opcode: u8, 123 handling_interrupt: Option<InterruptType>, 124 waiting: bool, 125 stopped: bool, 126 last_nmi: bool, 127 nmi_triggered: bool, 128 last_irq: bool, 129 irq_triggered: bool, 130 reset_pending: bool, 131 // Temp values to store internal instruction state between cycles 132 t0: u8, 133 t1: u8, 134 t2: u8, 135 t3: u8, 136 t4: u8, 137} 138 139#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 140pub(crate) enum InterruptType { 141 Breakpoint, 142 Coprocessor, 143 Nmi, 144 Irq, 145 Reset, 146} 147 148impl InterruptType { 149 const fn is_software(self) -> bool { 150 matches!(self, Self::Breakpoint | Self::Coprocessor) 151 } 152 153 const fn emulation_vector(self) -> u16 { 154 match self { 155 Self::Coprocessor => 0xFFF4, 156 Self::Nmi => 0xFFFA, 157 Self::Reset => 0xFFFC, 158 Self::Breakpoint | Self::Irq => 0xFFFE, 159 } 160 } 161 162 const fn native_vector(self) -> u16 { 163 match self { 164 Self::Coprocessor => 0xFFE4, 165 Self::Breakpoint => 0xFFE6, 166 Self::Nmi => 0xFFEA, 167 Self::Irq => 0xFFEE, 168 Self::Reset => 0xFFFC, 169 } 170 } 171} 172 173#[derive(Debug, Clone, Encode, Decode)] 174pub struct Wdc65816 { 175 registers: Registers, 176 state: State, 177} 178 179impl Wdc65816 { 180 #[must_use] 181 pub fn new() -> Self { 182 Self { registers: Registers::new(), state: State::default() } 183 } 184 185 pub fn reset<B: BusInterface>(&mut self, bus: &mut B) { 186 let reset_vector: u32 = InterruptType::Reset.emulation_vector().into(); 187 let address_lsb = bus.read(reset_vector); 188 let address_msb = bus.read(reset_vector + 1); 189 self.registers.pc = u16::from_le_bytes([address_lsb, address_msb]); 190 self.registers.pbr = 0; 191 192 self.registers.p.irq_disabled = true; 193 self.registers.p.decimal_mode = false; 194 self.registers.emulation_mode = true; 195 196 // Emulation mode forces on m and x flags and forces the stack to page 1 197 self.registers.p.accumulator_size = SizeBits::Eight; 198 self.registers.p.index_size = SizeBits::Eight; 199 self.registers.x &= 0x00FF; 200 self.registers.y &= 0x00FF; 201 self.registers.s = 0x0100 | (self.registers.s & 0x00FF); 202 203 self.state = State::default(); 204 } 205 206 #[inline] 207 pub fn tick<B: BusInterface>(&mut self, bus: &mut B) { 208 if bus.reset() { 209 self.state.reset_pending = true; 210 bus.idle(); 211 return; 212 } 213 214 if self.state.reset_pending { 215 // TODO timing? this does 2 bus reads and probably takes more internal cycles 216 self.reset(bus); 217 self.state.reset_pending = false; 218 return; 219 } 220 221 if bus.halt() { 222 bus.idle(); 223 return; 224 } 225 226 instructions::execute(self, bus); 227 } 228 229 #[inline] 230 #[must_use] 231 pub fn is_mid_instruction(&self) -> bool { 232 self.state.cycle != 0 233 } 234 235 #[inline] 236 #[must_use] 237 pub fn registers(&self) -> &Registers { 238 &self.registers 239 } 240 241 #[inline] 242 pub fn set_registers(&mut self, registers: Registers) { 243 self.registers = registers; 244 245 if self.registers.emulation_mode { 246 // Stack is forced to page 1 in emulation mode 247 self.registers.s = 0x0100 | (self.registers.s & 0x00FF); 248 } 249 } 250} 251 252impl Default for Wdc65816 { 253 fn default() -> Self { 254 Self::new() 255 } 256}