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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
// SPDX-License-Identifier: AGPL-3.0-or-later
//! Implementation of the MOS 6502 CPU.
use bitflags::bitflags;
use crate::{
instruction::{get_operation, AddressingMode, Instruction},
memory::Memory,
};
/// Implementation of the Central Processing Unit of the 6502.
pub struct Cpu {
/// The 8 bit accumulator is used all arithmetic and logical operations
/// (with the exception of increments and decrements). The contents of the
/// accumulator can be stored and retrieved either from memory or the stack.
///
/// Most complex operations will need to use the accumulator for arithmetic
/// and efficient optimisation of its use is a key feature of time critical
/// routines.
accumulator: u8,
/// The 8 bit index register is most commonly used to hold counters or
/// offsets for accessing memory. The value of the X register can be loaded
/// and saved in memory, compared with values held in memory or incremented
/// and decremented.
///
/// The X register has one special function. It can be used to get a copy of
/// the stack pointer or change its value.
index_register_x: u8,
/// The Y register is similar to the X register in that it is available for
/// holding counter or offsets memory access and supports the same set of
/// memory load, save and compare operations as wells as increments and
/// decrements. It has no special functions.
index_register_y: u8,
/// Processor flags. See documentation for [`ProcessorStatus`].
processor_status: ProcessorStatus,
/// The program counter is a 16 bit register which points to the next
/// instruction to be executed. The value of program counter is modified
/// automatically as instructions are executed.
///
/// The value of the program counter can be modified by executing a jump, a
/// relative branch or a subroutine call to another memory address or by
/// returning from a subroutine or interrupt.
program_counter: u16,
/// The processor supports a 256 byte stack located between $0100 and $01FF.
/// The stack pointer is an 8 bit register and holds the low 8 bits of the
/// next free location on the stack. The location of the stack is fixed and
/// cannot be moved.
///
/// Pushing bytes to the stack causes the stack pointer to
/// be decremented. Conversely pulling bytes causes it to be incremented.
///
/// The CPU does not detect if the stack is overflowed by excessive pushing
/// or pulling operations and will most likely result in the program
/// crashing.
stack_pointer: u8,
}
impl Cpu {
/// Creates a new instance of the [`Cpu`].
pub const fn new() -> Self {
// TODO: Ensure these are the correct values for init
Self {
accumulator: 0,
index_register_x: 0,
index_register_y: 0,
program_counter: 0xFFFC,
stack_pointer: 0x0000,
processor_status: ProcessorStatus::empty(),
}
}
/// Fetches the next byte, pointed to by the program counter.
fn fetch_byte(&mut self, cycles: &mut u32, memory: &Memory) -> u8 {
let data = *memory
.data
.get(usize::from(self.program_counter))
.expect("program_counter indexed outside of memory");
self.program_counter = self.program_counter.wrapping_add(1);
*cycles -= 1;
data
}
/// Sets the LDA status after executing an LDA operation.
fn lda_set_status(&mut self) {
self.processor_status
.set(ProcessorStatus::ZERO, self.accumulator == 0);
self.processor_status.set(
ProcessorStatus::NEGATIVE,
(self.accumulator & 0b1000_0000) > 0,
);
}
/// Reads the next byte, pointed to by the address.
fn read_byte(cycles: &mut u32, address: u8, memory: &Memory) -> u8 {
let data = *memory
.data
.get(usize::from(address))
.expect("address indexed outside of memory");
*cycles -= 1;
data
}
/// Executes instructions on the [`Cpu`] from memory. Runs the specified
/// number of cycles.
pub fn execute(&mut self, cycles: u32, memory: &Memory) {
// Shadow cycles with a mutable copy
let mut cycles = cycles;
while cycles > 0 {
let opcode: u8 = self.fetch_byte(&mut cycles, memory);
let operation = get_operation(opcode);
if let Some(operation) = operation {
match (operation.instruction, operation.addressing_mode) {
(Instruction::Lda, AddressingMode::Immediate) => {
let value = self.fetch_byte(&mut cycles, memory);
self.accumulator = value;
self.lda_set_status();
}
(Instruction::Lda, AddressingMode::ZeroPage) => {
let zero_page_address = self.fetch_byte(&mut cycles, memory);
self.accumulator = Self::read_byte(&mut cycles, zero_page_address, memory);
self.lda_set_status();
}
(Instruction::Lda, AddressingMode::ZeroPageX) => {
let mut zero_page_address = self.fetch_byte(&mut cycles, memory);
zero_page_address += self.index_register_x;
cycles -= 1;
self.accumulator = Self::read_byte(&mut cycles, zero_page_address, memory);
self.lda_set_status();
}
}
}
}
}
/// Resets the [`Cpu`]. This will put the Cpu back into the state it was at
/// on boot.
pub fn reset(&mut self, memory: &mut Memory) {
// TODO: Incomplete. Use correct values from later on in tutorial
self.program_counter = 0xFFFC;
self.stack_pointer = 0x0000;
// Set all flags to 0
self.processor_status = ProcessorStatus::empty();
self.accumulator = 0;
self.index_register_x = 0;
self.index_register_y = 0;
// Zero out memory
memory.reset();
}
}
bitflags! {
/// As instructions are executed a set of processor flags are set or clear to
/// record the results of the operation. This flags and some additional control
/// flags are held in a special status register. Each flag has a single bit
/// within the register.
pub struct ProcessorStatus: u8 {
/// The negative flag is set if the result of the last operation had bit 7
/// set to a one.
const NEGATIVE = 0b1000_0000;
/// The overflow flag is set during arithmetic operations if the result has
/// yielded an invalid 2's complement result (e.g. adding to positive numbers
/// and ending up with a negative result: 64 + 64 => -128). It is determined by
/// looking at the carry between bits 6 and 7 and between bit 7 and the carry
/// flag.
const OVERFLOW = 0b0100_0000;
// No instruction for 0b0010_0000
/// The break command bit is set when a BRK instruction has been executed and an
/// interrupt has been generated to process it.
const BREAK_COMMAND = 0b0001_0000;
/// While the decimal mode flag is set the processor will obey the rules of
/// Binary Coded Decimal (BCD) arithmetic during addition and subtraction. The
/// flag can be explicitly set using 'Set Decimal Flag' (SED) and cleared with
/// 'Clear Decimal Flag' (CLD).
const DECIMAL_MODE = 0b0000_1000;
/// The interrupt disable flag is set if the program has executed a 'Set
/// Interrupt Disable' (SEI) instruction. While this flag is set the processor
/// will not respond to interrupts from devices until it is cleared by a 'Clear
/// Interrupt Disable' (CLI) instruction.
const INTERRUPT_DISABLE = 0b0000_0100;
/// The zero flag is set if the result of the last operation as was zero.
const ZERO = 0b0000_0010;
/// The carry flag is set if the last operation caused an overflow from bit 7 of
/// the result or an underflow from bit 0. This condition is set during
/// arithmetic, comparison and during logical shifts. It can be explicitly set
/// using the 'Set Carry Flag' (SEC) instruction and cleared with 'Clear Carry
/// Flag' (CLC).
const CARRY = 0b0000_0001;
}
}
|