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
132
133
134
135
136
137
138
139
140
141
142
143
144
use std::fmt::Display;

use super::Cpu;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum AddressingMode {
    None,
    Absolute,
    AbsoluteX,
    AbsoluteY,
    Immediate,
    Implied,
    Indirect,
    IndexedIndirect,
    IndirectIndexed,
    Relative,
    ZeroPage,
    ZeroPageX,
    ZeroPageY,
}

impl Display for AddressingMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AddressingMode::None => write!(f, "None"),
            AddressingMode::Absolute => write!(f, "Absolute"),
            AddressingMode::AbsoluteX => write!(f, "AbsoluteX"),
            AddressingMode::AbsoluteY => write!(f, "AbsoluteY"),
            AddressingMode::Immediate => write!(f, "Immediate"),
            AddressingMode::Implied => write!(f, "Implied"),
            AddressingMode::Indirect => write!(f, "Indirect"),
            AddressingMode::IndexedIndirect => write!(f, "IndexedIndirect"),
            AddressingMode::IndirectIndexed => write!(f, "IndirectIndexed"),
            AddressingMode::Relative => write!(f, "Relative"),
            AddressingMode::ZeroPage => write!(f, "ZeroPage"),
            AddressingMode::ZeroPageX => write!(f, "ZeroPageX"),
            AddressingMode::ZeroPageY => write!(f, "ZeroPageY"),
        }
    }
}

impl AddressingMode {
    /// Execute an addressing mode, returns true if an extra cycle is needed
    pub fn execute(&self, cpu: &mut Cpu) -> bool {
        match self {
            AddressingMode::None => false,
            AddressingMode::Absolute => {
                let address = cpu.read_u16(cpu.pc);
                cpu.addr_abs = address;
                cpu.pc += 2;
                false
            }
            AddressingMode::AbsoluteX => {
                let address = cpu.read_u16(cpu.pc);
                cpu.addr_abs = address + cpu.x as u16;
                cpu.pc += 2;

                // If page boundary is crossed, we need an extra cycle
                if (cpu.addr_abs & 0xFF00) != (address & 0xFF00) {
                    return true;
                }
                false
            }
            AddressingMode::AbsoluteY => {
                let address = cpu.read_u16(cpu.pc);
                cpu.addr_abs = address + cpu.y as u16;
                cpu.pc += 2;

                // If page boundary is crossed, we need an extra cycle
                if (cpu.addr_abs & 0xFF00) != (address & 0xFF00) {
                    return true;
                }
                false
            }
            AddressingMode::Immediate => {
                cpu.addr_abs = cpu.pc;
                cpu.pc += 1;
                false
            }
            AddressingMode::Implied => {
                cpu.fetched_data = cpu.a;
                false
            }
            AddressingMode::Indirect => {
                let addr_lo = cpu.read(cpu.pc);
                let addr_hi = cpu.read(cpu.pc + 1);
                let addr = (addr_hi as u16) << 8 | (addr_lo as u16);

                if addr_lo == 0x00FF {
                    // We crossed a page boundary, so we need to simulate the hardware bug
                    cpu.addr_abs = (cpu.read(addr & 0xFF00) as u16) << 8 | cpu.read(addr) as u16;
                } else {
                    cpu.addr_abs = (cpu.read(addr + 1) as u16) << 8 | cpu.read(addr) as u16;
                }
                cpu.pc += 2;
                false
            }
            AddressingMode::IndexedIndirect => {
                let temp = cpu.read(cpu.pc);
                let lo = cpu.read(temp as u16 + cpu.x as u16);
                let hi = cpu.read(temp as u16 + cpu.x as u16 + 1);
                cpu.addr_abs = (hi as u16) << 8 | (lo as u16);
                cpu.pc += 1;
                false
            }
            AddressingMode::IndirectIndexed => {
                let temp = cpu.read(cpu.pc);
                let lo = cpu.read(temp as u16);
                let hi = cpu.read(temp as u16 + 1);
                cpu.addr_abs = (hi as u16) << 8 | (lo as u16);
                cpu.pc += 1;

                // If page boundary is crossed, we need an extra cycle
                if (cpu.addr_abs & 0xFF00) != ((hi as u16) << 8) {
                    return true;
                }
                false
            }
            AddressingMode::Relative => {
                cpu.addr_rel = cpu.read(cpu.pc) as u16;
                cpu.pc += 1;
                if cpu.addr_rel & 0x80 > 0 {
                    cpu.addr_rel |= 0xFF00;
                }
                false
            }
            AddressingMode::ZeroPage => {
                cpu.addr_abs = (cpu.read(cpu.pc) as u16) & 0x00FF;
                cpu.pc += 1;
                false
            }
            AddressingMode::ZeroPageX => {
                cpu.addr_abs = (cpu.read(cpu.pc) as u16 + cpu.x as u16) & 0x00FF;
                cpu.pc += 1;
                false
            }
            AddressingMode::ZeroPageY => {
                cpu.addr_abs = (cpu.read(cpu.pc) as u16 + cpu.y as u16) & 0x00FF;
                cpu.pc += 1;
                false
            }
        }
    }
}