1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::cpu::mos6502::{MemoryIO, Mos6502};

use super::Mos6502Variant;

/// Fetch values or addresses from memory, optionally dependent on the current
/// opcode.
pub trait Fetch {
  /// Fetch an immediate 1-byte value at the current program counter, and
  /// increment the program counter.
  fn fetch(&mut self) -> u8;

  /// Fetch an immediate 2-byte value (little-endian).
  fn fetch_word(&mut self) -> u16;

  /// Fetch the next operand value, based on the current opcode.
  /// Panics if the opcode is not valid for this operation.
  fn fetch_operand_value(&mut self, opcode: u8) -> (u8, u8);

  /// Fetch the next operand address, based on the current opcode.
  /// Panics if the opcode is not valid for this operation.
  fn fetch_operand_address(&mut self, opcode: u8) -> (u16, u8);
}

impl Fetch for Mos6502 {
  fn fetch(&mut self) -> u8 {
    let result = self.read(self.registers.pc.address());
    self.registers.pc.increment();
    result
  }

  fn fetch_word(&mut self) -> u16 {
    let lo = self.fetch();
    let hi = self.fetch();
    (hi as u16) << 8 | lo as u16
  }

  fn fetch_operand_value(&mut self, opcode: u8) -> (u8, u8) {
    match opcode & 0x1F {
      0x00 | 0x02 | 0x09 | 0x0B => (self.fetch(), 2), // Immediate
      0x08 | 0x18 | 0x1A => panic!("Implied operand has no value"),
      0x0A => (self.registers.a, 0),
      _ => {
        let (address, cycles) = self.fetch_operand_address(opcode);
        (self.read(address), cycles)
      }
    }
  }

  #[allow(clippy::manual_range_patterns)]
  fn fetch_operand_address(&mut self, opcode: u8) -> (u16, u8) {
    match opcode & 0x1F {
      0x00 | 0x02 | 0x09 | 0x0B => panic!("Immediate operand has no address"),
      0x01 | 0x03 => {
        // (Indirect,X)
        let base = self.fetch();
        let pointer = (base as u16 + self.registers.x as u16) & 0xFF;
        (self.read_word(pointer), 6)
      }
      0x04 | 0x05 | 0x06 | 0x07 => (self.fetch() as u16, 3), // Zero page
      0x08 | 0x0A | 0x18 | 0x1A => panic!("Implied operand has no address"),
      0x0C | 0x0D | 0x0E | 0x0F => (self.fetch_word(), 4), // Absolute
      0x10 => (self.fetch() as i8 as u16, 2),              // Relative
      0x11 | 0x13 => {
        // (Indirect),Y
        let base = self.fetch();
        let pointer = self.read_word(base as u16);
        (pointer + self.registers.y as u16, 5)
      }
      0x12 => match self.variant {
        Mos6502Variant::NMOS => {
          // These all halt the processor on an NMOS chip
          panic!("Invalid opcode");
        }
        Mos6502Variant::CMOS => {
          // (Indirect)
          let base = self.fetch();
          let pointer = self.read_word(base as u16);
          (pointer, 5)
        }
      },
      0x14 | 0x15 => {
        // Zero page,X
        let base = self.fetch();
        ((base as u16 + self.registers.x as u16) & 0xFF, 4)
      }
      0x16 | 0x17 => {
        // Zero page,X or Zero page,Y
        let base = self.fetch();
        if opcode & 0xC0 == 0x80 {
          ((base as u16 + self.registers.y as u16) & 0xFF, 5)
        } else {
          ((base as u16 + self.registers.x as u16) & 0xFF, 5)
        }
      }
      0x19 | 0x1B => {
        // Absolute,Y
        let base = self.fetch_word();
        (base + self.registers.y as u16, 4)
      }
      0x1C | 0x1D => {
        // Absolute,X
        let base = self.fetch_word();
        let indexed = base + self.registers.x as u16;

        if self.variant == Mos6502Variant::NMOS && base & 0xFF00 != indexed & 0xFF00 {
          self.read(base & 0xFF00 | indexed & 0x00FF);
          (indexed, 5)
        } else {
          (indexed, 4)
        }
      }
      0x1E | 0x1F => {
        // Absolute,X or Absolute,Y
        let base = self.fetch_word();
        let indexed = if opcode & 0xC0 == 0x80 {
          base + self.registers.y as u16
        } else {
          base + self.registers.x as u16
        };

        if self.variant == Mos6502Variant::NMOS && base & 0xFF00 != indexed & 0xFF00 {
          self.read(base & 0xFF00 | indexed & 0x00FF);
          (indexed, 5)
        } else {
          (indexed, 4)
        }
      }
      _ => unreachable!(),
    }
  }
}