|
|
|
|
@ -37,6 +37,41 @@ impl CPU {
@@ -37,6 +37,41 @@ impl CPU {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// LDA stands for Load Accumulator. It loads a value into the accumulator register. It affects
|
|
|
|
|
// the Zero Flag and Negative Flag.
|
|
|
|
|
fn lda(&mut self, value: u8) { |
|
|
|
|
self.register_a = value; |
|
|
|
|
self.update_zero_and_negative_flags(self.register_a); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn tax(&mut self) { |
|
|
|
|
self.register_x = self.register_a; |
|
|
|
|
self.update_zero_and_negative_flags(self.register_x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn update_zero_and_negative_flags(&mut self, result: u8) { |
|
|
|
|
// update the status register
|
|
|
|
|
if result == 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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if result & 0b1000_000 != 0 { |
|
|
|
|
self.status = self.status | 0b100_0000; |
|
|
|
|
} else { |
|
|
|
|
self.status = self.status & 0b0111_1111; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn inx(&mut self) { |
|
|
|
|
self.register_x = self.register_x.wrapping_add(1); |
|
|
|
|
self.update_zero_and_negative_flags(self.register_x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// The interpret method takes in mutalbe reference to self as we know we will need to modify
|
|
|
|
|
// register_a during execution
|
|
|
|
|
//
|
|
|
|
|
@ -59,44 +94,31 @@ impl CPU {
@@ -59,44 +94,31 @@ impl CPU {
|
|
|
|
|
match opscode { |
|
|
|
|
// this will implement LDA (0xA9) opcode. 0xA9 is the LDA Immediate instruction in
|
|
|
|
|
// the 6502 CPU.
|
|
|
|
|
//
|
|
|
|
|
// 0x42 tells the CPU to execute a specific operation: LDA Immediate. An opscode is
|
|
|
|
|
// a command for the CPU, instructing it what to do next. Essentially, this means
|
|
|
|
|
// "Load the immediate value from the next memory location into the accumulator"
|
|
|
|
|
//
|
|
|
|
|
// Immediate value refers to the constant valuethat is directly embedded in the
|
|
|
|
|
// instruction itself, rather than being fetched from memory or calculated
|
|
|
|
|
// directly.
|
|
|
|
|
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
|
|
|
|
|
// parameter. The program counter must always point to the next instruction to
|
|
|
|
|
// be executed.
|
|
|
|
|
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; |
|
|
|
|
// store the fetch parameter in register_a - handling the actual loading of the
|
|
|
|
|
// value into the accumulator and updates the CPU flags.
|
|
|
|
|
self.lda(param); |
|
|
|
|
} |
|
|
|
|
// implement the 0xAA opcode which corresponds to the TAX (transfer acculumater to
|
|
|
|
|
// X register)
|
|
|
|
|
0xAA => self.tax(), |
|
|
|
|
0xe8 => self.inx(), |
|
|
|
|
0x00 => return, |
|
|
|
|
_ => todo!(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -122,4 +144,30 @@ mod tests {
@@ -122,4 +144,30 @@ mod tests {
|
|
|
|
|
cpu.interpret(vec![0xa9, 0x00, 0x00]); |
|
|
|
|
assert!(cpu.status & 0b0000_0010 == 0b10); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_0xaa_tax_move_to_a_to_x() { |
|
|
|
|
let mut cpu = CPU::new(); |
|
|
|
|
cpu.register_a = 10; |
|
|
|
|
cpu.interpret(vec![0xaa, 0x00]); |
|
|
|
|
|
|
|
|
|
assert_eq!(cpu.register_x, 10) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_5_ops_working_together() { |
|
|
|
|
let mut cpu = CPU::new(); |
|
|
|
|
cpu.interpret(vec![0xa9, 0xc0, 0xaa, 0xe8, 0x00]); |
|
|
|
|
|
|
|
|
|
assert_eq!(cpu.register_x, 0xc1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_inx_overflow() { |
|
|
|
|
let mut cpu = CPU::new(); |
|
|
|
|
cpu.register_x = 0xff; |
|
|
|
|
cpu.interpret(vec![0xe8, 0xe8, 0x00]); |
|
|
|
|
|
|
|
|
|
assert_eq!(cpu.register_x, 1) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|