You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
4.8 KiB
125 lines
4.8 KiB
// This is all new to me so it's heavily commented so we can understand what is happening. |
|
|
|
pub struct CPU { |
|
// the accumulator register is a specific register used for arithmetic and logic operations |
|
// the cpu instruction loads a value into the accumulator register and then updates certain |
|
// flags in the processor status register to relect the operation of the result |
|
pub register_a: u8, |
|
// THe Process Status Register is a collection of individual bits (flags) that represent the |
|
// current state of the CPU. Each bit has purpose such as if a calculation resulted in zero or |
|
// if the result is negative |
|
// |
|
// Zero flag: |
|
// - Bit 1 of the status register |
|
// - Set if register_a == 0, cleared otherwise |
|
// |
|
// Negative flag (N) - indicates whether the result of the most recent operation is negative |
|
// - Bit 7 of the status register (most significant bit is Bit 7 because it's zero based) |
|
// - Set if the most significant bit of register_a is 1, cleared otherwise |
|
pub status: u8, |
|
// track our current position in the program |
|
pub program_counter: u16, |
|
// most commonly used to hold counters or offsets for accessing memory. The value can be loaded |
|
// and saved in memory, compared with values held in memory or incremented and decremented. |
|
// |
|
// X register has one special function. It can be used to copy a stack pointer or change it's |
|
// value. |
|
pub register_x: u8, |
|
} |
|
|
|
impl CPU { |
|
pub fn new() -> Self { |
|
CPU { |
|
register_a: 0, |
|
status: 0, |
|
program_counter: 0, |
|
register_x: 0, |
|
} |
|
} |
|
|
|
// The interpret method takes in mutalbe reference to self as we know we will need to modify |
|
// register_a during execution |
|
// |
|
// - Fetch next instruction from instruction memory |
|
// - Decode instruction |
|
// - Execute the instruction |
|
// - Repeat |
|
pub fn interpret(&mut self, program: Vec<u8>) { |
|
self.program_counter = 0; |
|
|
|
// We need an infinite loop to continuously fetch instructions from the program array. We |
|
// use the program_counter to keep track fo the current instruction. |
|
loop { |
|
// set the opscode to the current byte in the program at the address indicated by |
|
// program counter |
|
let opscode = program[self.program_counter as usize]; |
|
// we increment program counterto point to the next byte |
|
self.program_counter += 1; |
|
|
|
match opscode { |
|
// this will implement LDA (0xA9) opcode. 0xA9 is the LDA Immediate instruction in |
|
// the 6502 CPU. |
|
0xA9 => { |
|
// fetch the next byte in program. This byte is the immediate value to load |
|
// into the accumulator (register_a) |
|
let param = program[self.program_counter as usize]; |
|
// Increment program counter to point to the next instruction after the |
|
// parameter |
|
self.program_counter += 1; |
|
// store the fetch paramenter in register_a |
|
self.register_a = param; |
|
|
|
// now we wil update the status register |
|
if self.register_a == 0 { |
|
// 0b000_0010 represents a number where only the second bit (bit 1) is set |
|
// to 1 and all other bits are 0 |
|
self.status = self.status | 0b000_0010; |
|
} else { |
|
// 0b1111_1101 represents a number where only bit 1 is 0 and the rest are 1 |
|
self.status = self.status & 0b1111_1101; |
|
} |
|
} |
|
0xAA => { |
|
self.register_x = self.register_a; |
|
|
|
if self.register_x == 0 { |
|
self.status = self.status | 0b000_0010; |
|
} else { |
|
self.status = self.status & 0b1111_1101; |
|
} |
|
|
|
if self.register_x & 0b1000_0000 != 0 { |
|
self.status = self.status | 0b1000_0000; |
|
} else { |
|
self.status = self.status & 0b0111_1111; |
|
} |
|
} |
|
0x00 => { |
|
return; |
|
} |
|
_ => todo!(), |
|
} |
|
} |
|
} |
|
} |
|
|
|
#[cfg(test)] |
|
mod tests { |
|
use super::*; |
|
|
|
#[test] |
|
fn test_lda_sets_register_a() { |
|
let mut cpu = CPU::new(); |
|
cpu.interpret(vec![0xa9, 0x05, 0x00]); |
|
assert_eq!(cpu.register_a, 0x05); |
|
assert!(cpu.status & 0b0000_0010 == 0b00); |
|
assert!(cpu.status & 0b1000_0000 == 0); |
|
} |
|
|
|
#[test] |
|
fn test_0xa9_lda_zero_flag() { |
|
let mut cpu = CPU::new(); |
|
cpu.interpret(vec![0xa9, 0x00, 0x00]); |
|
assert!(cpu.status & 0b0000_0010 == 0b10); |
|
} |
|
}
|
|
|