1use crate::bus::Bus;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum AddressingMode {
13 Implied,
17
18 Accumulator,
22
23 Immediate,
27
28 ZeroPage,
32
33 ZeroPageX,
37
38 ZeroPageY,
42
43 Absolute,
47
48 AbsoluteX,
52
53 AbsoluteY,
57
58 Indirect,
62
63 IndexedIndirectX,
70
71 IndirectIndexedY,
78
79 Relative,
83}
84
85impl AddressingMode {
86 #[inline]
94 #[must_use]
95 pub const fn operand_bytes(self) -> u8 {
96 match self {
97 Self::Implied | Self::Accumulator => 0,
98 Self::Immediate
99 | Self::ZeroPage
100 | Self::ZeroPageX
101 | Self::ZeroPageY
102 | Self::IndexedIndirectX
103 | Self::IndirectIndexedY
104 | Self::Relative => 1,
105 Self::Absolute | Self::AbsoluteX | Self::AbsoluteY | Self::Indirect => 2,
106 }
107 }
108
109 #[inline]
119 #[must_use]
120 pub const fn can_page_cross(self) -> bool {
121 matches!(
122 self,
123 Self::AbsoluteX | Self::AbsoluteY | Self::IndirectIndexedY | Self::Relative
124 )
125 }
126}
127
128#[derive(Debug, Clone, Copy)]
132pub struct AddressResult {
133 pub addr: u16,
135
136 pub page_crossed: bool,
140
141 pub base_addr: u16,
146}
147
148impl AddressResult {
149 #[inline]
151 #[must_use]
152 pub const fn new(addr: u16) -> Self {
153 Self {
154 addr,
155 page_crossed: false,
156 base_addr: 0,
157 }
158 }
159
160 #[inline]
162 #[must_use]
163 pub const fn with_page_cross(addr: u16, page_crossed: bool) -> Self {
164 Self {
165 addr,
166 page_crossed,
167 base_addr: 0,
168 }
169 }
170
171 #[inline]
173 #[must_use]
174 pub const fn with_base_and_cross(addr: u16, base_addr: u16, page_crossed: bool) -> Self {
175 Self {
176 addr,
177 page_crossed,
178 base_addr,
179 }
180 }
181}
182
183#[inline]
187#[must_use]
188pub const fn page_crossed(addr1: u16, addr2: u16) -> bool {
189 (addr1 & 0xFF00) != (addr2 & 0xFF00)
190}
191
192impl AddressingMode {
196 pub fn resolve(self, pc: u16, x: u8, y: u8, bus: &mut impl Bus) -> AddressResult {
215 match self {
216 Self::Implied | Self::Accumulator => AddressResult::new(0),
217
218 Self::Immediate => AddressResult::new(pc),
219
220 Self::ZeroPage => {
221 let addr = bus.read(pc) as u16;
222 AddressResult::new(addr)
223 }
224
225 Self::ZeroPageX => {
226 let base = bus.read(pc);
227 let addr = base.wrapping_add(x) as u16;
229 AddressResult::new(addr)
230 }
231
232 Self::ZeroPageY => {
233 let base = bus.read(pc);
234 let addr = base.wrapping_add(y) as u16;
236 AddressResult::new(addr)
237 }
238
239 Self::Absolute => {
240 let addr = bus.read_u16(pc);
241 AddressResult::new(addr)
242 }
243
244 Self::AbsoluteX => {
245 let base = bus.read_u16(pc);
246 let addr = base.wrapping_add(x as u16);
247 let crossed = page_crossed(base, addr);
248 AddressResult::with_base_and_cross(addr, base, crossed)
249 }
250
251 Self::AbsoluteY => {
252 let base = bus.read_u16(pc);
253 let addr = base.wrapping_add(y as u16);
254 let crossed = page_crossed(base, addr);
255 AddressResult::with_base_and_cross(addr, base, crossed)
256 }
257
258 Self::Indirect => {
259 let ptr = bus.read_u16(pc);
260 let addr = bus.read_u16_wrap(ptr);
262 AddressResult::new(addr)
263 }
264
265 Self::IndexedIndirectX => {
266 let base = bus.read(pc);
267 let ptr = base.wrapping_add(x);
269
270 let lo = bus.read(ptr as u16) as u16;
272 let hi = bus.read(ptr.wrapping_add(1) as u16) as u16;
273 let addr = (hi << 8) | lo;
274
275 AddressResult::new(addr)
276 }
277
278 Self::IndirectIndexedY => {
279 let ptr = bus.read(pc);
280
281 let lo = bus.read(ptr as u16) as u16;
283 let hi = bus.read(ptr.wrapping_add(1) as u16) as u16;
284 let base = (hi << 8) | lo;
285
286 let addr = base.wrapping_add(y as u16);
288 let crossed = page_crossed(base, addr);
289
290 AddressResult::with_base_and_cross(addr, base, crossed)
291 }
292
293 Self::Relative => {
294 let offset = bus.read(pc) as i8;
296 let base = pc.wrapping_add(1);
298 let addr = base.wrapping_add(offset as u16);
299
300 let crossed = page_crossed(base, addr);
301 AddressResult::with_page_cross(addr, crossed)
302 }
303 }
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311 struct TestBus {
312 ram: [u8; 0x10000],
313 }
314
315 impl Bus for TestBus {
316 fn read(&mut self, addr: u16) -> u8 {
317 self.ram[addr as usize]
318 }
319
320 fn write(&mut self, addr: u16, value: u8) {
321 self.ram[addr as usize] = value;
322 }
323 }
324
325 #[test]
326 fn test_page_crossed_same_page() {
327 assert!(!page_crossed(0x1234, 0x1256));
328 }
329
330 #[test]
331 fn test_page_crossed_different_page() {
332 assert!(page_crossed(0x12FF, 0x1300));
333 }
334
335 #[test]
336 fn test_immediate() {
337 let mut bus = TestBus { ram: [0; 0x10000] };
338 let result = AddressingMode::Immediate.resolve(0x1000, 0, 0, &mut bus);
339 assert_eq!(result.addr, 0x1000);
340 assert!(!result.page_crossed);
341 }
342
343 #[test]
344 fn test_zero_page() {
345 let mut bus = TestBus { ram: [0; 0x10000] };
346 bus.write(0x1000, 0x42);
347
348 let result = AddressingMode::ZeroPage.resolve(0x1000, 0, 0, &mut bus);
349 assert_eq!(result.addr, 0x0042);
350 }
351
352 #[test]
353 fn test_zero_page_x_wrap() {
354 let mut bus = TestBus { ram: [0; 0x10000] };
355 bus.write(0x1000, 0xFF);
356
357 let result = AddressingMode::ZeroPageX.resolve(0x1000, 0x05, 0, &mut bus);
359 assert_eq!(result.addr, 0x0004);
360 }
361
362 #[test]
363 fn test_absolute() {
364 let mut bus = TestBus { ram: [0; 0x10000] };
365 bus.write(0x1000, 0x34);
366 bus.write(0x1001, 0x12);
367
368 let result = AddressingMode::Absolute.resolve(0x1000, 0, 0, &mut bus);
369 assert_eq!(result.addr, 0x1234);
370 }
371
372 #[test]
373 fn test_absolute_x_no_page_cross() {
374 let mut bus = TestBus { ram: [0; 0x10000] };
375 bus.write(0x1000, 0x00);
376 bus.write(0x1001, 0x12);
377
378 let result = AddressingMode::AbsoluteX.resolve(0x1000, 0x10, 0, &mut bus);
379 assert_eq!(result.addr, 0x1210);
380 assert!(!result.page_crossed);
381 }
382
383 #[test]
384 fn test_absolute_x_page_cross() {
385 let mut bus = TestBus { ram: [0; 0x10000] };
386 bus.write(0x1000, 0xFF);
387 bus.write(0x1001, 0x12);
388
389 let result = AddressingMode::AbsoluteX.resolve(0x1000, 0x01, 0, &mut bus);
390 assert_eq!(result.addr, 0x1300);
391 assert!(result.page_crossed);
392 }
393
394 #[test]
395 fn test_indexed_indirect_x() {
396 let mut bus = TestBus { ram: [0; 0x10000] };
397
398 bus.write(0x1000, 0x80);
400
401 bus.write(0x0085, 0x20);
403 bus.write(0x0086, 0x30);
404
405 let result = AddressingMode::IndexedIndirectX.resolve(0x1000, 0x05, 0, &mut bus);
406 assert_eq!(result.addr, 0x3020);
407 }
408
409 #[test]
410 fn test_indirect_indexed_y_no_page_cross() {
411 let mut bus = TestBus { ram: [0; 0x10000] };
412
413 bus.write(0x1000, 0x80);
415
416 bus.write(0x0080, 0x00);
418 bus.write(0x0081, 0x30);
419
420 let result = AddressingMode::IndirectIndexedY.resolve(0x1000, 0, 0x10, &mut bus);
421 assert_eq!(result.addr, 0x3010);
422 assert!(!result.page_crossed);
423 }
424
425 #[test]
426 fn test_indirect_indexed_y_page_cross() {
427 let mut bus = TestBus { ram: [0; 0x10000] };
428
429 bus.write(0x1000, 0x80);
431
432 bus.write(0x0080, 0xFF);
434 bus.write(0x0081, 0x30);
435
436 let result = AddressingMode::IndirectIndexedY.resolve(0x1000, 0, 0x01, &mut bus);
437 assert_eq!(result.addr, 0x3100);
438 assert!(result.page_crossed);
439 }
440
441 #[test]
442 fn test_relative_forward_same_page() {
443 let mut bus = TestBus { ram: [0; 0x10000] };
444
445 bus.write(0x1000, 0x10);
447
448 let result = AddressingMode::Relative.resolve(0x1000, 0, 0, &mut bus);
449 assert_eq!(result.addr, 0x1011);
451 assert!(!result.page_crossed);
452 }
453
454 #[test]
455 fn test_relative_forward_page_cross() {
456 let mut bus = TestBus { ram: [0; 0x10000] };
457
458 bus.write(0x10F0, 0x20);
460
461 let result = AddressingMode::Relative.resolve(0x10F0, 0, 0, &mut bus);
462 assert_eq!(result.addr, 0x1111);
464 assert!(result.page_crossed);
465 }
466
467 #[test]
468 fn test_relative_backward() {
469 let mut bus = TestBus { ram: [0; 0x10000] };
470
471 bus.write(0x1050, 0xF0);
473
474 let result = AddressingMode::Relative.resolve(0x1050, 0, 0, &mut bus);
475 assert_eq!(result.addr, 0x1041);
477 assert!(!result.page_crossed);
478 }
479}