From 61b8dfe1f222605f0358e5389219be0f1d0bdb21 Mon Sep 17 00:00:00 2001 From: omarandlorraine <64254276+omarandlorraine@users.noreply.github.com> Date: Sun, 26 Mar 2023 23:27:44 +0100 Subject: [PATCH 01/15] add single_step function (#62) Co-authored-by: Sam M W --- src/cpu.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cpu.rs b/src/cpu.rs index 0721a50..ff81865 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -415,6 +415,12 @@ impl CPU { }; } + pub fn single_step(&mut self) { + if let Some(decoded_instr) = self.fetch_next_and_decode() { + self.execute_instruction(decoded_instr); + } + } + pub fn run(&mut self) { while let Some(decoded_instr) = self.fetch_next_and_decode() { self.execute_instruction(decoded_instr); From 766143d52f007b22d31e4d7fff370ffb3f1554b4 Mon Sep 17 00:00:00 2001 From: Sam M W Date: Sun, 2 Apr 2023 21:21:46 +0100 Subject: [PATCH 02/15] remove is_stack_address; it's never used --- src/memory.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 9b7bc42..1929365 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -96,10 +96,6 @@ impl Memory { self.bytes[start..end].copy_from_slice(values); } - - pub fn is_stack_address(address: u16) -> bool { - address > 0xff && address < 0x200 - } } #[cfg(test)] From 131b0f312db3e9676aa9eb617ee1da28820653dd Mon Sep 17 00:00:00 2001 From: Sam M W Date: Sun, 2 Apr 2023 21:35:18 +0100 Subject: [PATCH 03/15] remove get_byte_mut_ref method from Memory This is the right thing to do, because the actual hardware will perform a read and then perform a write. This could have observable side effects on a real system --- src/cpu.rs | 42 ++++++++++++++++++------------------------ src/memory.rs | 4 ---- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index ff81865..c4208bc 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -103,10 +103,9 @@ impl CPU { self.registers.accumulator = val as i8; } (Instruction::ASL, OpInput::UseAddress(addr)) => { - CPU::shift_left_with_flags( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); + let mut operand: u8 = self.memory.get_byte(addr); + CPU::shift_left_with_flags(&mut operand, &mut self.registers.status); + self.memory.set_byte(addr, operand); } (Instruction::BCC, OpInput::UseRelative(rel)) => { @@ -208,10 +207,9 @@ impl CPU { } (Instruction::DEC, OpInput::UseAddress(addr)) => { - CPU::decrement( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); + let mut operand: u8 = self.memory.get_byte(addr); + CPU::decrement(&mut operand, &mut self.registers.status); + self.memory.set_byte(addr, operand); } (Instruction::DEY, OpInput::UseImplied) => { @@ -231,10 +229,9 @@ impl CPU { } (Instruction::INC, OpInput::UseAddress(addr)) => { - CPU::increment( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); + let mut operand: u8 = self.memory.get_byte(addr); + CPU::increment(&mut operand, &mut self.registers.status); + self.memory.set_byte(addr, operand); } (Instruction::INX, OpInput::UseImplied) => { CPU::increment(&mut self.registers.index_x, &mut self.registers.status); @@ -282,10 +279,9 @@ impl CPU { self.registers.accumulator = val as i8; } (Instruction::LSR, OpInput::UseAddress(addr)) => { - CPU::shift_right_with_flags( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); + let mut operand: u8 = self.memory.get_byte(addr); + CPU::shift_right_with_flags(&mut operand, &mut self.registers.status); + self.memory.set_byte(addr, operand); } (Instruction::ORA, OpInput::UseImmediate(val)) => { @@ -327,10 +323,9 @@ impl CPU { self.registers.accumulator = val as i8; } (Instruction::ROL, OpInput::UseAddress(addr)) => { - CPU::rotate_left_with_flags( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); + let mut operand: u8 = self.memory.get_byte(addr); + CPU::rotate_left_with_flags(&mut operand, &mut self.registers.status); + self.memory.set_byte(addr, operand); } (Instruction::ROR, OpInput::UseImplied) => { // Accumulator mode @@ -339,10 +334,9 @@ impl CPU { self.registers.accumulator = val as i8; } (Instruction::ROR, OpInput::UseAddress(addr)) => { - CPU::rotate_right_with_flags( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); + let mut operand: u8 = self.memory.get_byte(addr); + CPU::rotate_right_with_flags(&mut operand, &mut self.registers.status); + self.memory.set_byte(addr, operand); } (Instruction::SBC, OpInput::UseImmediate(val)) => { diff --git a/src/memory.rs b/src/memory.rs index 1929365..097c110 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -69,10 +69,6 @@ impl Memory { self.bytes[address as usize] } - pub fn get_byte_mut_ref(&mut self, address: u16) -> &mut u8 { - &mut self.bytes[address as usize] - } - pub fn get_slice(&self, start: u16, diff: u16) -> &[u8] { let orig: usize = start.into(); let end = orig + diff as usize; From 4254c02f550f91d9403ae95b73ad10969b09b6cc Mon Sep 17 00:00:00 2001 From: Sam M W Date: Sun, 2 Apr 2023 21:53:36 +0100 Subject: [PATCH 04/15] Remove unit test test_memory_set_bytes This is the right thing to do because its pass depends on zero-initilization. Real memory will not behave this way --- src/memory.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 097c110..4428978 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -98,13 +98,6 @@ impl Memory { mod tests { use super::*; - #[test] - fn test_memory_set_bytes() { - let mut memory = Memory::new(); - memory.set_bytes(0x0100, &[1, 2, 3, 4, 5]); - assert_eq!(memory.get_slice(0x00FF, 7), &[0, 1, 2, 3, 4, 5, 0]); - } - #[test] #[should_panic] fn test_memory_overflow_panic() { From f3f15de8cc8c7d39fe47efe9385a7d3909a82e80 Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 05:47:29 +0100 Subject: [PATCH 05/15] remove memory.get_slice real hardware won't have the ability to dive in and grab a slice from memory --- src/cpu.rs | 16 ++++++++++++++-- src/instruction.rs | 19 +++++++++++++------ src/memory.rs | 7 ------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index c4208bc..6de3598 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -63,8 +63,20 @@ impl CPU { let data_start = self.registers.program_counter.wrapping_add(1); - let slice = self.memory.get_slice(data_start, extra_bytes); - let am_out = am.process(self, slice); + let slice = if extra_bytes == 0 { + [0, 0] + } else if extra_bytes == 1 { + [self.memory.get_byte(data_start), 0] + } else if extra_bytes == 2 { + [ + self.memory.get_byte(data_start), + self.memory.get_byte(data_start.wrapping_add(1)), + ] + } else { + panic!() + }; + + let am_out = am.process(self, &slice[..extra_bytes as usize]); // Increment program counter self.registers.program_counter = diff --git a/src/instruction.rs b/src/instruction.rs index 7ba88d1..b755d05 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -26,6 +26,7 @@ // POSSIBILITY OF SUCH DAMAGE. use crate::cpu::CPU; +use crate::memory::Memory; // Abbreviations // @@ -171,6 +172,12 @@ impl AddressingMode { let memory = &cpu.memory; + fn read_address(mem: &Memory, addr: u16) -> [u8; 2] { + let lo = mem.get_byte(addr); + let hi = mem.get_byte(addr.wrapping_add(1)); + [lo, hi] + } + match self { AddressingMode::Accumulator | AddressingMode::Implied => { // Always the same -- no input @@ -227,8 +234,8 @@ impl AddressingMode { // Use [u8, ..2] from instruction as an address. Interpret the // two bytes starting at that address as an address. // (Output: a 16-bit address) - let slice = memory.get_slice(arr_to_addr(arr), 2); - OpInput::UseAddress(arr_to_addr(slice)) + let slice = read_address(memory, arr_to_addr(arr)); + OpInput::UseAddress(arr_to_addr(&slice)) } AddressingMode::IndexedIndirectX => { // Use [u8, ..1] from instruction @@ -236,8 +243,8 @@ impl AddressingMode { // This is where the absolute (16-bit) target address is stored. // (Output: a 16-bit address) let start = arr[0].wrapping_add(x); - let slice = memory.get_slice(u16::from(start), 2); - OpInput::UseAddress(arr_to_addr(slice)) + let slice = read_address(memory, u16::from(start)); + OpInput::UseAddress(arr_to_addr(&slice)) } AddressingMode::IndirectIndexedY => { // Use [u8, ..1] from instruction @@ -245,8 +252,8 @@ impl AddressingMode { // Add Y register to this address to get the final address // (Output: a 16-bit address) let start = arr[0]; - let slice = memory.get_slice(u16::from(start), 2); - OpInput::UseAddress(arr_to_addr(slice).wrapping_add(xextend(y))) + let slice = read_address(memory, u16::from(start)); + OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(xextend(y))) } } } diff --git a/src/memory.rs b/src/memory.rs index 4428978..c4f9535 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -69,13 +69,6 @@ impl Memory { self.bytes[address as usize] } - pub fn get_slice(&self, start: u16, diff: u16) -> &[u8] { - let orig: usize = start.into(); - let end = orig + diff as usize; - - &self.bytes[orig..end] - } - // Sets the byte at the given address to the given value and returns the // previous value at the address. pub fn set_byte(&mut self, address: u16, value: u8) -> u8 { From a8dac6e8051fb8f918f45431c4192148f99e1399 Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 06:15:07 +0100 Subject: [PATCH 06/15] extract memory functionality into a trait --- README.md | 1 + examples/euclidean_algo.rs | 1 + examples/mos6502.rs | 1 + src/cpu.rs | 1 + src/instruction.rs | 1 + src/memory.rs | 19 ++++++++++++++++--- 6 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7f7303c..bcd6c4d 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Source: [Wikipedia](https://en.wikipedia.org/wiki/MOS_Technology_6502) ## How to use this library ```rust +use mos6502::memory::Bus; use mos6502::cpu; fn main() { diff --git a/examples/euclidean_algo.rs b/examples/euclidean_algo.rs index 0177362..446f5d2 100644 --- a/examples/euclidean_algo.rs +++ b/examples/euclidean_algo.rs @@ -1,6 +1,7 @@ extern crate mos6502; use mos6502::cpu; +use mos6502::memory::Bus; fn main() { println!("Enter two numbers (< 128) to know their GCD:"); diff --git a/examples/mos6502.rs b/examples/mos6502.rs index c71080c..26fa717 100644 --- a/examples/mos6502.rs +++ b/examples/mos6502.rs @@ -29,6 +29,7 @@ extern crate mos6502; #[cfg(not(test))] use mos6502::cpu; +use mos6502::memory::Bus; #[cfg(not(test))] fn main() { diff --git a/src/cpu.rs b/src/cpu.rs index 6de3598..43d05be 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -26,6 +26,7 @@ // POSSIBILITY OF SUCH DAMAGE. use crate::instruction::{self, DecodedInstr, Instruction, OpInput}; +use crate::memory::Bus; use crate::memory::Memory; use crate::registers::{Registers, StackPointer, Status, StatusArgs}; diff --git a/src/instruction.rs b/src/instruction.rs index b755d05..a2e6508 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -26,6 +26,7 @@ // POSSIBILITY OF SUCH DAMAGE. use crate::cpu::CPU; +use crate::memory::Bus; use crate::memory::Memory; // Abbreviations diff --git a/src/memory.rs b/src/memory.rs index c4f9535..7194de7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -58,26 +58,39 @@ impl Default for Memory { } } +pub trait Bus { + fn get_byte(&self, address: u16) -> u8; + fn set_byte(&mut self, address: u16, value: u8) -> u8; + + fn set_bytes(&mut self, start: u16, values: &[u8]) { + for i in 0..values.len() as u16 { + self.set_byte(start + i, values[i as usize]); + } + } +} + impl Memory { pub fn new() -> Memory { Memory { bytes: [0; MEMORY_SIZE], } } +} - pub fn get_byte(&self, address: u16) -> u8 { +impl Bus for Memory { + fn get_byte(&self, address: u16) -> u8 { self.bytes[address as usize] } // Sets the byte at the given address to the given value and returns the // previous value at the address. - pub fn set_byte(&mut self, address: u16, value: u8) -> u8 { + fn set_byte(&mut self, address: u16, value: u8) -> u8 { let old_value = self.get_byte(address); self.bytes[address as usize] = value; old_value } - pub fn set_bytes(&mut self, start: u16, values: &[u8]) { + fn set_bytes(&mut self, start: u16, values: &[u8]) { let start = start as usize; // This panics if the range is invalid From 12c901e8aaca59f7b7f413554f7c1d3670a519fb Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 06:59:12 +0100 Subject: [PATCH 07/15] generic argument for CPU represents memory --- src/cpu.rs | 178 ++++++++++++++++++++++++++++++++++----------- src/instruction.rs | 121 ------------------------------ 2 files changed, 137 insertions(+), 162 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index 43d05be..81d017f 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -25,33 +25,40 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -use crate::instruction::{self, DecodedInstr, Instruction, OpInput}; +use crate::instruction::{self, AddressingMode, DecodedInstr, Instruction, OpInput}; use crate::memory::Bus; -use crate::memory::Memory; + use crate::registers::{Registers, StackPointer, Status, StatusArgs}; +fn xextend(x: u8) -> u16 { + u16::from(x) +} + +fn arr_to_addr(arr: &[u8]) -> u16 { + debug_assert!(arr.len() == 2); + + u16::from(arr[0]) + (u16::from(arr[1]) << 8usize) +} + #[derive(Clone)] -pub struct CPU { +pub struct CPU +where + M: Bus, +{ pub registers: Registers, - pub memory: Memory, + pub memory: M, } -impl Default for CPU { - fn default() -> Self { - Self::new() - } -} - -impl CPU { - pub fn new() -> CPU { +impl CPU { + pub fn new(memory: M) -> CPU { CPU { registers: Registers::new(), - memory: Memory::new(), + memory, } } pub fn reset(&mut self) { - *self = CPU::new(); + //TODO: // should read some bytes from the stack and also get the PC from the reset vector } pub fn fetch_next_and_decode(&mut self) -> Option { @@ -77,7 +84,96 @@ impl CPU { panic!() }; - let am_out = am.process(self, &slice[..extra_bytes as usize]); + let x = self.registers.index_x; + let y = self.registers.index_y; + + let memory = &self.memory; + + fn read_address(mem: &M, addr: u16) -> [u8; 2] { + let lo = mem.get_byte(addr); + let hi = mem.get_byte(addr.wrapping_add(1)); + [lo, hi] + } + + let am_out = match am { + AddressingMode::Accumulator | AddressingMode::Implied => { + // Always the same -- no input + OpInput::UseImplied + } + AddressingMode::Immediate => { + // Use [u8, ..1] specified in instruction as input + OpInput::UseImmediate(slice[0]) + } + AddressingMode::ZeroPage => { + // Use [u8, ..1] from instruction + // Interpret as zero page address + // (Output: an 8-bit zero-page address) + OpInput::UseAddress(u16::from(slice[0])) + } + AddressingMode::ZeroPageX => { + // Use [u8, ..1] from instruction + // Add to X register (as u8 -- the final address is in 0-page) + // (Output: an 8-bit zero-page address) + OpInput::UseAddress(u16::from(slice[0].wrapping_add(x))) + } + AddressingMode::ZeroPageY => { + // Use [u8, ..1] from instruction + // Add to Y register (as u8 -- the final address is in 0-page) + // (Output: an 8-bit zero-page address) + OpInput::UseAddress(u16::from(slice[0].wrapping_add(y))) + } + + AddressingMode::Relative => { + // Use [u8, ..1] from instruction + // (interpret as relative...) + // (This is sign extended to a 16-but data type, but an unsigned one: u16. It's a + // little weird, but it's so we can add the PC and the offset easily) + let offset = slice[0]; + let sign_extend = if offset & 0x80 == 0x80 { 0xffu8 } else { 0x0 }; + let rel = u16::from_le_bytes([offset, sign_extend]); + OpInput::UseRelative(rel) + } + AddressingMode::Absolute => { + // Use [u8, ..2] from instruction as address + // (Output: a 16-bit address) + OpInput::UseAddress(arr_to_addr(&slice)) + } + AddressingMode::AbsoluteX => { + // Use [u8, ..2] from instruction as address, add X + // (Output: a 16-bit address) + OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(xextend(x))) + } + AddressingMode::AbsoluteY => { + // Use [u8, ..2] from instruction as address, add Y + // (Output: a 16-bit address) + OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(xextend(y))) + } + AddressingMode::Indirect => { + // Use [u8, ..2] from instruction as an address. Interpret the + // two bytes starting at that address as an address. + // (Output: a 16-bit address) + let slice = read_address(memory, arr_to_addr(&slice)); + OpInput::UseAddress(arr_to_addr(&slice)) + } + AddressingMode::IndexedIndirectX => { + // Use [u8, ..1] from instruction + // Add to X register with 0-page wraparound, like ZeroPageX. + // This is where the absolute (16-bit) target address is stored. + // (Output: a 16-bit address) + let start = slice[0].wrapping_add(x); + let slice = read_address(memory, u16::from(start)); + OpInput::UseAddress(arr_to_addr(&slice)) + } + AddressingMode::IndirectIndexedY => { + // Use [u8, ..1] from instruction + // This is where the absolute (16-bit) target address is stored. + // Add Y register to this address to get the final address + // (Output: a 16-bit address) + let start = slice[0]; + let slice = read_address(memory, u16::from(start)); + OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(xextend(y))) + } + }; // Increment program counter self.registers.program_counter = @@ -112,12 +208,12 @@ impl CPU { (Instruction::ASL, OpInput::UseImplied) => { // Accumulator mode let mut val = self.registers.accumulator as u8; - CPU::shift_left_with_flags(&mut val, &mut self.registers.status); + CPU::::shift_left_with_flags(&mut val, &mut self.registers.status); self.registers.accumulator = val as i8; } (Instruction::ASL, OpInput::UseAddress(addr)) => { let mut operand: u8 = self.memory.get_byte(addr); - CPU::shift_left_with_flags(&mut operand, &mut self.registers.status); + CPU::::shift_left_with_flags(&mut operand, &mut self.registers.status); self.memory.set_byte(addr, operand); } @@ -221,16 +317,16 @@ impl CPU { (Instruction::DEC, OpInput::UseAddress(addr)) => { let mut operand: u8 = self.memory.get_byte(addr); - CPU::decrement(&mut operand, &mut self.registers.status); + CPU::::decrement(&mut operand, &mut self.registers.status); self.memory.set_byte(addr, operand); } (Instruction::DEY, OpInput::UseImplied) => { - CPU::decrement(&mut self.registers.index_y, &mut self.registers.status); + CPU::::decrement(&mut self.registers.index_y, &mut self.registers.status); } (Instruction::DEX, OpInput::UseImplied) => { - CPU::decrement(&mut self.registers.index_x, &mut self.registers.status); + CPU::::decrement(&mut self.registers.index_x, &mut self.registers.status); } (Instruction::EOR, OpInput::UseImmediate(val)) => { @@ -243,14 +339,14 @@ impl CPU { (Instruction::INC, OpInput::UseAddress(addr)) => { let mut operand: u8 = self.memory.get_byte(addr); - CPU::increment(&mut operand, &mut self.registers.status); + CPU::::increment(&mut operand, &mut self.registers.status); self.memory.set_byte(addr, operand); } (Instruction::INX, OpInput::UseImplied) => { - CPU::increment(&mut self.registers.index_x, &mut self.registers.status); + CPU::::increment(&mut self.registers.index_x, &mut self.registers.status); } (Instruction::INY, OpInput::UseImplied) => { - CPU::increment(&mut self.registers.index_x, &mut self.registers.status); + CPU::::increment(&mut self.registers.index_x, &mut self.registers.status); } (Instruction::JMP, OpInput::UseAddress(addr)) => self.jump(addr), @@ -288,12 +384,12 @@ impl CPU { (Instruction::LSR, OpInput::UseImplied) => { // Accumulator mode let mut val = self.registers.accumulator as u8; - CPU::shift_right_with_flags(&mut val, &mut self.registers.status); + CPU::::shift_right_with_flags(&mut val, &mut self.registers.status); self.registers.accumulator = val as i8; } (Instruction::LSR, OpInput::UseAddress(addr)) => { let mut operand: u8 = self.memory.get_byte(addr); - CPU::shift_right_with_flags(&mut operand, &mut self.registers.status); + CPU::::shift_right_with_flags(&mut operand, &mut self.registers.status); self.memory.set_byte(addr, operand); } @@ -332,23 +428,23 @@ impl CPU { (Instruction::ROL, OpInput::UseImplied) => { // Accumulator mode let mut val = self.registers.accumulator as u8; - CPU::rotate_left_with_flags(&mut val, &mut self.registers.status); + CPU::::rotate_left_with_flags(&mut val, &mut self.registers.status); self.registers.accumulator = val as i8; } (Instruction::ROL, OpInput::UseAddress(addr)) => { let mut operand: u8 = self.memory.get_byte(addr); - CPU::rotate_left_with_flags(&mut operand, &mut self.registers.status); + CPU::::rotate_left_with_flags(&mut operand, &mut self.registers.status); self.memory.set_byte(addr, operand); } (Instruction::ROR, OpInput::UseImplied) => { // Accumulator mode let mut val = self.registers.accumulator as u8; - CPU::rotate_right_with_flags(&mut val, &mut self.registers.status); + CPU::::rotate_right_with_flags(&mut val, &mut self.registers.status); self.registers.accumulator = val as i8; } (Instruction::ROR, OpInput::UseAddress(addr)) => { let mut operand: u8 = self.memory.get_byte(addr); - CPU::rotate_right_with_flags(&mut operand, &mut self.registers.status); + CPU::::rotate_right_with_flags(&mut operand, &mut self.registers.status); self.memory.set_byte(addr, operand); } @@ -474,7 +570,7 @@ impl CPU { ..StatusArgs::none() }), ); - CPU::set_flags_from_i8(status, *p_val as i8); + CPU::::set_flags_from_i8(status, *p_val as i8); } fn shift_right_with_flags(p_val: &mut u8, status: &mut Status) { @@ -488,7 +584,7 @@ impl CPU { ..StatusArgs::none() }), ); - CPU::set_flags_from_i8(status, *p_val as i8); + CPU::::set_flags_from_i8(status, *p_val as i8); } fn rotate_left_with_flags(p_val: &mut u8, status: &mut Status) { @@ -504,7 +600,7 @@ impl CPU { ..StatusArgs::none() }), ); - CPU::set_flags_from_i8(status, *p_val as i8); + CPU::::set_flags_from_i8(status, *p_val as i8); } fn rotate_right_with_flags(p_val: &mut u8, status: &mut Status) { @@ -520,21 +616,21 @@ impl CPU { ..StatusArgs::none() }), ); - CPU::set_flags_from_i8(status, *p_val as i8); + CPU::::set_flags_from_i8(status, *p_val as i8); } fn set_u8_with_flags(mem: &mut u8, status: &mut Status, value: u8) { *mem = value; - CPU::set_flags_from_u8(status, value); + CPU::::set_flags_from_u8(status, value); } fn set_i8_with_flags(mem: &mut i8, status: &mut Status, value: i8) { *mem = value; - CPU::set_flags_from_i8(status, value); + CPU::::set_flags_from_i8(status, value); } fn load_x_register(&mut self, value: u8) { - CPU::set_u8_with_flags( + CPU::::set_u8_with_flags( &mut self.registers.index_x, &mut self.registers.status, value, @@ -542,7 +638,7 @@ impl CPU { } fn load_y_register(&mut self, value: u8) { - CPU::set_u8_with_flags( + CPU::::set_u8_with_flags( &mut self.registers.index_y, &mut self.registers.status, value, @@ -550,7 +646,7 @@ impl CPU { } fn load_accumulator(&mut self, value: i8) { - CPU::set_i8_with_flags( + CPU::::set_i8_with_flags( &mut self.registers.accumulator, &mut self.registers.status, value, @@ -835,7 +931,7 @@ impl CPU { } } -impl core::fmt::Debug for CPU { +impl core::fmt::Debug for CPU { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!( f, @@ -1341,9 +1437,9 @@ mod tests { } #[cfg(test)] - fn compare_test_helper(compare: &mut F, load_instruction: Instruction) + fn compare_test_helper(compare: &mut F, load_instruction: Instruction) where - F: FnMut(&mut CPU, u8), + F: FnMut(&mut CPU, u8), { let mut cpu = CPU::new(); diff --git a/src/instruction.rs b/src/instruction.rs index a2e6508..e3a98f5 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -25,10 +25,6 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -use crate::cpu::CPU; -use crate::memory::Bus; -use crate::memory::Memory; - // Abbreviations // // General @@ -164,100 +160,6 @@ impl AddressingMode { AddressingMode::IndirectIndexedY => 1, } } - - pub fn process(self, cpu: &CPU, arr: &[u8]) -> OpInput { - debug_assert!(arr.len() == self.extra_bytes() as usize); - - let x = cpu.registers.index_x; - let y = cpu.registers.index_y; - - let memory = &cpu.memory; - - fn read_address(mem: &Memory, addr: u16) -> [u8; 2] { - let lo = mem.get_byte(addr); - let hi = mem.get_byte(addr.wrapping_add(1)); - [lo, hi] - } - - match self { - AddressingMode::Accumulator | AddressingMode::Implied => { - // Always the same -- no input - OpInput::UseImplied - } - AddressingMode::Immediate => { - // Use [u8, ..1] specified in instruction as input - OpInput::UseImmediate(arr[0]) - } - AddressingMode::ZeroPage => { - // Use [u8, ..1] from instruction - // Interpret as zero page address - // (Output: an 8-bit zero-page address) - OpInput::UseAddress(u16::from(arr[0])) - } - AddressingMode::ZeroPageX => { - // Use [u8, ..1] from instruction - // Add to X register (as u8 -- the final address is in 0-page) - // (Output: an 8-bit zero-page address) - OpInput::UseAddress(u16::from(arr[0].wrapping_add(x))) - } - AddressingMode::ZeroPageY => { - // Use [u8, ..1] from instruction - // Add to Y register (as u8 -- the final address is in 0-page) - // (Output: an 8-bit zero-page address) - OpInput::UseAddress(u16::from(arr[0].wrapping_add(y))) - } - AddressingMode::Relative => { - // Use [u8, ..1] from instruction - // (interpret as relative...) - // (This is sign extended to a 16-but data type, but an unsigned one: u16. It's a - // little weird, but it's so we can add the PC and the offset easily) - let offset = arr[0]; - let sign_extend = if offset & 0x80 == 0x80 { 0xffu8 } else { 0x0 }; - let rel = u16::from_le_bytes([offset, sign_extend]); - OpInput::UseRelative(rel) - } - AddressingMode::Absolute => { - // Use [u8, ..2] from instruction as address - // (Output: a 16-bit address) - OpInput::UseAddress(arr_to_addr(arr)) - } - AddressingMode::AbsoluteX => { - // Use [u8, ..2] from instruction as address, add X - // (Output: a 16-bit address) - OpInput::UseAddress(arr_to_addr(arr).wrapping_add(xextend(x))) - } - AddressingMode::AbsoluteY => { - // Use [u8, ..2] from instruction as address, add Y - // (Output: a 16-bit address) - OpInput::UseAddress(arr_to_addr(arr).wrapping_add(xextend(y))) - } - AddressingMode::Indirect => { - // Use [u8, ..2] from instruction as an address. Interpret the - // two bytes starting at that address as an address. - // (Output: a 16-bit address) - let slice = read_address(memory, arr_to_addr(arr)); - OpInput::UseAddress(arr_to_addr(&slice)) - } - AddressingMode::IndexedIndirectX => { - // Use [u8, ..1] from instruction - // Add to X register with 0-page wraparound, like ZeroPageX. - // This is where the absolute (16-bit) target address is stored. - // (Output: a 16-bit address) - let start = arr[0].wrapping_add(x); - let slice = read_address(memory, u16::from(start)); - OpInput::UseAddress(arr_to_addr(&slice)) - } - AddressingMode::IndirectIndexedY => { - // Use [u8, ..1] from instruction - // This is where the absolute (16-bit) target address is stored. - // Add Y register to this address to get the final address - // (Output: a 16-bit address) - let start = arr[0]; - let slice = read_address(memory, u16::from(start)); - OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(xextend(y))) - } - } - } } pub type DecodedInstr = (Instruction, OpInput); @@ -776,26 +678,3 @@ pub static OPCODES: [Option<(Instruction, AddressingMode)>; 256] = [ /*0xFF*/ None, ]; - -#[cfg(test)] -mod tests { - - #[test] - fn zeropage_wrap_around() { - use crate::instruction::AddressingMode; - use crate::instruction::OpInput; - use crate::instruction::CPU; - - let mut cpu = CPU::new(); - cpu.registers.index_x = 9; - - assert!(matches!( - AddressingMode::ZeroPageX.process(&cpu, &[10]), - OpInput::UseAddress(19) - )); - assert!(matches!( - AddressingMode::ZeroPageX.process(&cpu, &[250]), - OpInput::UseAddress(3) - )); - } -} From 0708a1084c12078ee16529e5458b672b2e73b5f1 Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 07:31:49 +0100 Subject: [PATCH 08/15] fix the tests back up --- README.md | 3 +- examples/euclidean_algo.rs | 3 +- examples/mos6502.rs | 3 +- src/cpu.rs | 69 ++++++++++++++++++++------------------ 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index bcd6c4d..057ea7a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Source: [Wikipedia](https://en.wikipedia.org/wiki/MOS_Technology_6502) ```rust use mos6502::memory::Bus; +use mos6502::memory::Memory; use mos6502::cpu; fn main() { @@ -50,7 +51,7 @@ fn main() { 0x4c, 0x10, 0x00, // Jump to .algo ]; - let mut cpu = cpu::CPU::new(); + let mut cpu = cpu::CPU::new(Memory::new()); cpu.memory.set_bytes(0x00, &zero_page_data); cpu.memory.set_bytes(0x10, &program); diff --git a/examples/euclidean_algo.rs b/examples/euclidean_algo.rs index 446f5d2..04fc087 100644 --- a/examples/euclidean_algo.rs +++ b/examples/euclidean_algo.rs @@ -2,6 +2,7 @@ extern crate mos6502; use mos6502::cpu; use mos6502::memory::Bus; +use mos6502::memory::Memory; fn main() { println!("Enter two numbers (< 128) to know their GCD:"); @@ -34,7 +35,7 @@ fn main() { 0x4c, 0x10, 0x00, // Jump to .algo ]; - let mut cpu = cpu::CPU::new(); + let mut cpu = cpu::CPU::new(Memory::new()); cpu.memory.set_bytes(0x00, &zero_page_data); cpu.memory.set_bytes(0x10, &program); diff --git a/examples/mos6502.rs b/examples/mos6502.rs index 26fa717..2d0a788 100644 --- a/examples/mos6502.rs +++ b/examples/mos6502.rs @@ -30,10 +30,11 @@ extern crate mos6502; #[cfg(not(test))] use mos6502::cpu; use mos6502::memory::Bus; +use mos6502::memory::Memory; #[cfg(not(test))] fn main() { - let mut cpu = cpu::CPU::new(); + let mut cpu = cpu::CPU::new(Memory::new()); // "Load" a program diff --git a/src/cpu.rs b/src/cpu.rs index 81d017f..371e3ff 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -949,7 +949,7 @@ mod tests { #[test] fn dont_panic_for_overflow() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.add_with_carry(-128); assert_eq!(cpu.registers.accumulator, -128); cpu.add_with_carry(-128); @@ -963,7 +963,7 @@ mod tests { #[cfg_attr(feature = "decimal_mode", test)] fn decimal_add_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.status.or(Status::PS_DECIMAL_MODE); cpu.add_with_carry(0x09); @@ -990,7 +990,7 @@ mod tests { #[cfg_attr(feature = "decimal_mode", test)] fn decimal_subtract_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers .status .or(Status::PS_DECIMAL_MODE | Status::PS_CARRY); @@ -1012,7 +1012,7 @@ mod tests { #[test] fn add_with_carry_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.add_with_carry(1); assert_eq!(cpu.registers.accumulator, 1); @@ -1035,7 +1035,7 @@ mod tests { assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE)); assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW)); - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.add_with_carry(127); assert_eq!(cpu.registers.accumulator, 127); @@ -1066,7 +1066,7 @@ mod tests { assert!(cpu.registers.status.contains(Status::PS_NEGATIVE)); assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW)); - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.add_with_carry(127); assert_eq!(cpu.registers.accumulator, 127); @@ -1082,7 +1082,7 @@ mod tests { assert!(cpu.registers.status.contains(Status::PS_NEGATIVE)); assert!(cpu.registers.status.contains(Status::PS_OVERFLOW)); - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.status.or(Status::PS_CARRY); cpu.add_with_carry(-1); assert_eq!(cpu.registers.accumulator, 0); @@ -1091,7 +1091,7 @@ mod tests { #[test] fn and_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.accumulator = 0; cpu.and(-1); @@ -1120,7 +1120,7 @@ mod tests { #[test] fn subtract_with_carry_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied)); cpu.registers.accumulator = 0; @@ -1180,7 +1180,7 @@ mod tests { #[test] fn decrement_memory_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); let addr: u16 = 0xA1B2; cpu.memory.set_byte(addr, 5); @@ -1217,7 +1217,7 @@ mod tests { #[test] fn decrement_x_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.index_x = 0x80; cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied)); assert_eq!(cpu.registers.index_x, 127); @@ -1227,7 +1227,7 @@ mod tests { #[test] fn decrement_y_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.index_y = 0x80; cpu.execute_instruction((Instruction::DEY, OpInput::UseImplied)); assert_eq!(cpu.registers.index_y, 127); @@ -1239,7 +1239,7 @@ mod tests { fn logical_shift_right_test() { // Testing UseImplied version (which targets the accumulator) only, for now - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(0))); cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied)); assert_eq!(cpu.registers.accumulator, 0); @@ -1275,7 +1275,7 @@ mod tests { #[test] fn dec_x_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied)); assert_eq!(cpu.registers.index_x, 0xff); @@ -1320,7 +1320,7 @@ mod tests { #[test] fn jump_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); let addr: u16 = 0xA1B1; cpu.jump(addr); @@ -1329,7 +1329,7 @@ mod tests { #[test] fn branch_if_carry_clear_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied)); cpu.branch_if_carry_clear(0xABCD); @@ -1342,7 +1342,7 @@ mod tests { #[test] fn branch_if_carry_set_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied)); cpu.branch_if_carry_set(0xABCD); @@ -1355,7 +1355,7 @@ mod tests { #[test] fn branch_if_equal_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.branch_if_equal(0xABCD); assert_eq!(cpu.registers.program_counter, (0)); @@ -1368,7 +1368,7 @@ mod tests { #[test] fn branch_if_minus_test() { { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); let registers_before = cpu.registers; cpu.branch_if_minus(0xABCD); @@ -1377,7 +1377,7 @@ mod tests { } { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.status.or(Status::PS_NEGATIVE); let registers_before = cpu.registers; @@ -1390,7 +1390,7 @@ mod tests { #[test] fn branch_if_positive_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.status.insert(Status::PS_NEGATIVE); cpu.branch_if_positive(0xABCD); @@ -1403,7 +1403,7 @@ mod tests { #[test] fn branch_if_overflow_clear_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.status.insert(Status::PS_OVERFLOW); cpu.branch_if_overflow_clear(0xABCD); @@ -1416,7 +1416,7 @@ mod tests { #[test] fn branch_across_end_of_address_space() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.registers.program_counter = 0xffff; cpu.registers.status.insert(Status::PS_OVERFLOW); @@ -1426,7 +1426,7 @@ mod tests { #[test] fn branch_if_overflow_set_test() { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.branch_if_overflow_set(0xABCD); assert_eq!(cpu.registers.program_counter, (0)); @@ -1437,11 +1437,11 @@ mod tests { } #[cfg(test)] - fn compare_test_helper(compare: &mut F, load_instruction: Instruction) + fn compare_test_helper(compare: &mut F, load_instruction: Instruction) where - F: FnMut(&mut CPU, u8), + F: FnMut(&mut CPU, u8), { - let mut cpu = CPU::new(); + let mut cpu = CPU::new(crate::memory::Memory::new()); cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127))); @@ -1489,7 +1489,7 @@ mod tests { #[test] fn compare_with_a_register_test() { compare_test_helper( - &mut |cpu: &mut CPU, val: u8| { + &mut |cpu: &mut CPU, val: u8| { cpu.compare_with_a_register(val); }, Instruction::LDA, @@ -1499,7 +1499,7 @@ mod tests { #[test] fn compare_with_x_register_test() { compare_test_helper( - &mut |cpu: &mut CPU, val: u8| { + &mut |cpu: &mut CPU, val: u8| { cpu.compare_with_x_register(val); }, Instruction::LDX, @@ -1509,7 +1509,7 @@ mod tests { #[test] fn compare_with_y_register_test() { compare_test_helper( - &mut |cpu: &mut CPU, val: u8| { + &mut |cpu: &mut CPU, val: u8| { cpu.compare_with_y_register(val); }, Instruction::LDY, @@ -1518,7 +1518,8 @@ mod tests { #[test] fn exclusive_or_test() { - let mut cpu = CPU::new(); + use crate::memory::Memory; + let mut cpu = CPU::new(Memory::new()); for a_before in range_inclusive(0u8, 255u8) { for val in range_inclusive(0u8, 255u8) { @@ -1546,7 +1547,8 @@ mod tests { #[test] fn inclusive_or_test() { - let mut cpu = CPU::new(); + use crate::memory::Memory; + let mut cpu = CPU::new(Memory::new()); for a_before in range_inclusive(0u8, 255u8) { for val in range_inclusive(0u8, 255u8) { @@ -1574,7 +1576,8 @@ mod tests { #[test] fn stack_underflow() { - let mut cpu = CPU::new(); + use crate::memory::Memory; + let mut cpu = CPU::new(Memory::new()); let _val: u8 = cpu.pull_from_stack(); } } From 672defd817b5fdfbf196ac2fa33f41a114afc8dc Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 07:36:50 +0100 Subject: [PATCH 09/15] remove now dead code --- src/instruction.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/instruction.rs b/src/instruction.rs index e3a98f5..885452e 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -132,16 +132,6 @@ pub enum AddressingMode { // zero page address) plus Y register } -fn xextend(x: u8) -> u16 { - u16::from(x) -} - -fn arr_to_addr(arr: &[u8]) -> u16 { - debug_assert!(arr.len() == 2); - - u16::from(arr[0]) + (u16::from(arr[1]) << 8usize) -} - impl AddressingMode { pub fn extra_bytes(self) -> u16 { match self { From 3accd8ce40c8e3a42b64330190544729edcfc80f Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 07:38:38 +0100 Subject: [PATCH 10/15] as clippy suggests, convert from bool to u8/i8 --- src/cpu.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index 371e3ff..eac754f 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -592,7 +592,7 @@ impl CPU { let mask = 1 << 7; let is_bit_7_set = (*p_val & mask) == mask; let shifted = (*p_val & !(1 << 7)) << 1; - *p_val = shifted + if is_carry_set { 1 } else { 0 }; + *p_val = shifted + u8::from(is_carry_set); status.set_with_mask( Status::PS_CARRY, Status::new(StatusArgs { @@ -655,11 +655,7 @@ impl CPU { fn add_with_carry(&mut self, value: i8) { let a_before: i8 = self.registers.accumulator; - let c_before: i8 = if self.registers.status.contains(Status::PS_CARRY) { - 1 - } else { - 0 - }; + let c_before: i8 = i8::from(self.registers.status.contains(Status::PS_CARRY)); let a_after: i8 = a_before.wrapping_add(c_before).wrapping_add(value); debug_assert_eq!( From 7222388c5e4e16d29df0224fae6e0fa3a648ee0f Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 21:00:17 +0100 Subject: [PATCH 11/15] get rid of silly xextend function; just use the .into() --- src/cpu.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index eac754f..f8a32b3 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -30,10 +30,6 @@ use crate::memory::Bus; use crate::registers::{Registers, StackPointer, Status, StatusArgs}; -fn xextend(x: u8) -> u16 { - u16::from(x) -} - fn arr_to_addr(arr: &[u8]) -> u16 { debug_assert!(arr.len() == 2); @@ -141,12 +137,12 @@ impl CPU { AddressingMode::AbsoluteX => { // Use [u8, ..2] from instruction as address, add X // (Output: a 16-bit address) - OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(xextend(x))) + OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(x.into())) } AddressingMode::AbsoluteY => { // Use [u8, ..2] from instruction as address, add Y // (Output: a 16-bit address) - OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(xextend(y))) + OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(y.into())) } AddressingMode::Indirect => { // Use [u8, ..2] from instruction as an address. Interpret the @@ -171,7 +167,7 @@ impl CPU { // (Output: a 16-bit address) let start = slice[0]; let slice = read_address(memory, u16::from(start)); - OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(xextend(y))) + OpInput::UseAddress(arr_to_addr(&slice).wrapping_add(y.into())) } }; From d2043dc6fbcbccf2ab41c90334961b89662b775e Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 21:04:34 +0100 Subject: [PATCH 12/15] less verbosity, more expliciteness --- src/cpu.rs | 68 ++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index f8a32b3..04a1a10 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -937,11 +937,12 @@ impl core::fmt::Debug for CPU { mod tests { use super::*; + use crate::memory::Memory as Ram; use num::range_inclusive; #[test] fn dont_panic_for_overflow() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.add_with_carry(-128); assert_eq!(cpu.registers.accumulator, -128); cpu.add_with_carry(-128); @@ -955,7 +956,7 @@ mod tests { #[cfg_attr(feature = "decimal_mode", test)] fn decimal_add_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.status.or(Status::PS_DECIMAL_MODE); cpu.add_with_carry(0x09); @@ -982,7 +983,7 @@ mod tests { #[cfg_attr(feature = "decimal_mode", test)] fn decimal_subtract_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers .status .or(Status::PS_DECIMAL_MODE | Status::PS_CARRY); @@ -1004,7 +1005,7 @@ mod tests { #[test] fn add_with_carry_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.add_with_carry(1); assert_eq!(cpu.registers.accumulator, 1); @@ -1027,7 +1028,7 @@ mod tests { assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE)); assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW)); - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.add_with_carry(127); assert_eq!(cpu.registers.accumulator, 127); @@ -1058,7 +1059,7 @@ mod tests { assert!(cpu.registers.status.contains(Status::PS_NEGATIVE)); assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW)); - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.add_with_carry(127); assert_eq!(cpu.registers.accumulator, 127); @@ -1074,7 +1075,7 @@ mod tests { assert!(cpu.registers.status.contains(Status::PS_NEGATIVE)); assert!(cpu.registers.status.contains(Status::PS_OVERFLOW)); - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.status.or(Status::PS_CARRY); cpu.add_with_carry(-1); assert_eq!(cpu.registers.accumulator, 0); @@ -1083,7 +1084,7 @@ mod tests { #[test] fn and_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.accumulator = 0; cpu.and(-1); @@ -1112,7 +1113,7 @@ mod tests { #[test] fn subtract_with_carry_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied)); cpu.registers.accumulator = 0; @@ -1172,7 +1173,7 @@ mod tests { #[test] fn decrement_memory_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); let addr: u16 = 0xA1B2; cpu.memory.set_byte(addr, 5); @@ -1209,7 +1210,7 @@ mod tests { #[test] fn decrement_x_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.index_x = 0x80; cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied)); assert_eq!(cpu.registers.index_x, 127); @@ -1219,7 +1220,7 @@ mod tests { #[test] fn decrement_y_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.index_y = 0x80; cpu.execute_instruction((Instruction::DEY, OpInput::UseImplied)); assert_eq!(cpu.registers.index_y, 127); @@ -1231,7 +1232,7 @@ mod tests { fn logical_shift_right_test() { // Testing UseImplied version (which targets the accumulator) only, for now - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(0))); cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied)); assert_eq!(cpu.registers.accumulator, 0); @@ -1267,7 +1268,7 @@ mod tests { #[test] fn dec_x_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied)); assert_eq!(cpu.registers.index_x, 0xff); @@ -1312,7 +1313,7 @@ mod tests { #[test] fn jump_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); let addr: u16 = 0xA1B1; cpu.jump(addr); @@ -1321,7 +1322,7 @@ mod tests { #[test] fn branch_if_carry_clear_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied)); cpu.branch_if_carry_clear(0xABCD); @@ -1334,7 +1335,7 @@ mod tests { #[test] fn branch_if_carry_set_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied)); cpu.branch_if_carry_set(0xABCD); @@ -1347,7 +1348,7 @@ mod tests { #[test] fn branch_if_equal_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.branch_if_equal(0xABCD); assert_eq!(cpu.registers.program_counter, (0)); @@ -1360,7 +1361,7 @@ mod tests { #[test] fn branch_if_minus_test() { { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); let registers_before = cpu.registers; cpu.branch_if_minus(0xABCD); @@ -1369,7 +1370,7 @@ mod tests { } { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.status.or(Status::PS_NEGATIVE); let registers_before = cpu.registers; @@ -1382,7 +1383,7 @@ mod tests { #[test] fn branch_if_positive_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.status.insert(Status::PS_NEGATIVE); cpu.branch_if_positive(0xABCD); @@ -1395,7 +1396,7 @@ mod tests { #[test] fn branch_if_overflow_clear_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.status.insert(Status::PS_OVERFLOW); cpu.branch_if_overflow_clear(0xABCD); @@ -1408,7 +1409,7 @@ mod tests { #[test] fn branch_across_end_of_address_space() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.registers.program_counter = 0xffff; cpu.registers.status.insert(Status::PS_OVERFLOW); @@ -1418,7 +1419,7 @@ mod tests { #[test] fn branch_if_overflow_set_test() { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.branch_if_overflow_set(0xABCD); assert_eq!(cpu.registers.program_counter, (0)); @@ -1431,9 +1432,9 @@ mod tests { #[cfg(test)] fn compare_test_helper(compare: &mut F, load_instruction: Instruction) where - F: FnMut(&mut CPU, u8), + F: FnMut(&mut CPU, u8), { - let mut cpu = CPU::new(crate::memory::Memory::new()); + let mut cpu = CPU::new(Ram::new()); cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127))); @@ -1481,7 +1482,7 @@ mod tests { #[test] fn compare_with_a_register_test() { compare_test_helper( - &mut |cpu: &mut CPU, val: u8| { + &mut |cpu: &mut CPU, val: u8| { cpu.compare_with_a_register(val); }, Instruction::LDA, @@ -1491,7 +1492,7 @@ mod tests { #[test] fn compare_with_x_register_test() { compare_test_helper( - &mut |cpu: &mut CPU, val: u8| { + &mut |cpu: &mut CPU, val: u8| { cpu.compare_with_x_register(val); }, Instruction::LDX, @@ -1501,7 +1502,7 @@ mod tests { #[test] fn compare_with_y_register_test() { compare_test_helper( - &mut |cpu: &mut CPU, val: u8| { + &mut |cpu: &mut CPU, val: u8| { cpu.compare_with_y_register(val); }, Instruction::LDY, @@ -1510,8 +1511,7 @@ mod tests { #[test] fn exclusive_or_test() { - use crate::memory::Memory; - let mut cpu = CPU::new(Memory::new()); + let mut cpu = CPU::new(Ram::new()); for a_before in range_inclusive(0u8, 255u8) { for val in range_inclusive(0u8, 255u8) { @@ -1539,8 +1539,7 @@ mod tests { #[test] fn inclusive_or_test() { - use crate::memory::Memory; - let mut cpu = CPU::new(Memory::new()); + let mut cpu = CPU::new(Ram::new()); for a_before in range_inclusive(0u8, 255u8) { for val in range_inclusive(0u8, 255u8) { @@ -1568,8 +1567,7 @@ mod tests { #[test] fn stack_underflow() { - use crate::memory::Memory; - let mut cpu = CPU::new(Memory::new()); + let mut cpu = CPU::new(Ram::new()); let _val: u8 = cpu.pull_from_stack(); } } From 26926a9feb588e3d0da18961dd34e6be7fb79b95 Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 22:44:02 +0100 Subject: [PATCH 13/15] set_byte method does not need to return old_value --- src/memory.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 7194de7..cf987f2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -60,7 +60,7 @@ impl Default for Memory { pub trait Bus { fn get_byte(&self, address: u16) -> u8; - fn set_byte(&mut self, address: u16, value: u8) -> u8; + fn set_byte(&mut self, address: u16, value: u8); fn set_bytes(&mut self, start: u16, values: &[u8]) { for i in 0..values.len() as u16 { @@ -84,10 +84,8 @@ impl Bus for Memory { // Sets the byte at the given address to the given value and returns the // previous value at the address. - fn set_byte(&mut self, address: u16, value: u8) -> u8 { - let old_value = self.get_byte(address); + fn set_byte(&mut self, address: u16, value: u8) { self.bytes[address as usize] = value; - old_value } fn set_bytes(&mut self, start: u16, values: &[u8]) { From 9f75d1a6b57e9c9262674de048ea52f1b433d30b Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 3 Apr 2023 22:46:46 +0100 Subject: [PATCH 14/15] the get_byte method needs to take mutable reference to self the reson for this is it's possible for external hardware to have some side effect on a read. --- src/cpu.rs | 4 ++-- src/memory.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index 04a1a10..a2a0d0c 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -83,9 +83,9 @@ impl CPU { let x = self.registers.index_x; let y = self.registers.index_y; - let memory = &self.memory; + let memory = &mut self.memory; - fn read_address(mem: &M, addr: u16) -> [u8; 2] { + fn read_address(mem: &mut M, addr: u16) -> [u8; 2] { let lo = mem.get_byte(addr); let hi = mem.get_byte(addr.wrapping_add(1)); [lo, hi] diff --git a/src/memory.rs b/src/memory.rs index cf987f2..dbe46b5 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -59,7 +59,7 @@ impl Default for Memory { } pub trait Bus { - fn get_byte(&self, address: u16) -> u8; + fn get_byte(&mut self, address: u16) -> u8; fn set_byte(&mut self, address: u16, value: u8); fn set_bytes(&mut self, start: u16, values: &[u8]) { @@ -78,7 +78,7 @@ impl Memory { } impl Bus for Memory { - fn get_byte(&self, address: u16) -> u8 { + fn get_byte(&mut self, address: u16) -> u8 { self.bytes[address as usize] } From f031c080a00afbce76bbd30d1690a66b45ca18fc Mon Sep 17 00:00:00 2001 From: Sam M W Date: Tue, 4 Apr 2023 14:48:13 +0100 Subject: [PATCH 15/15] bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 53bb3be..575d9be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ name = "mos6502" description = "A MOS 6502 Emulator" license = "BSD-3-Clause" -version = "0.3.0" +version = "0.4.0" authors = ["The 6502-rs Developers"] build = "build.rs" exclude = ["examples/**"]