Merge pull request #63 from mre/memory_trait

Tease memory handling apart from the CPU
This commit is contained in:
omarandlorraine 2023-04-05 05:23:25 +01:00 committed by GitHub
commit 99ae3d55e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 217 additions and 249 deletions

View File

@ -20,6 +20,8 @@ Source: [Wikipedia](https://en.wikipedia.org/wiki/MOS_Technology_6502)
## How to use this library
```rust
use mos6502::memory::Bus;
use mos6502::memory::Memory;
use mos6502::cpu;
fn main() {
@ -49,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);

View File

@ -1,6 +1,8 @@
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:");
@ -33,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);

View File

@ -29,10 +29,12 @@ 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

View File

@ -25,32 +25,36 @@
// 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::memory::Memory;
use crate::instruction::{self, AddressingMode, DecodedInstr, Instruction, OpInput};
use crate::memory::Bus;
use crate::registers::{Registers, StackPointer, Status, StatusArgs};
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<M>
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<M: Bus> CPU<M> {
pub fn new(memory: M) -> CPU<M> {
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<DecodedInstr> {
@ -63,8 +67,109 @@ 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 x = self.registers.index_x;
let y = self.registers.index_y;
let memory = &mut self.memory;
fn read_address<M: Bus>(mem: &mut 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(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(y.into()))
}
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(y.into()))
}
};
// Increment program counter
self.registers.program_counter =
@ -99,14 +204,13 @@ 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::<M>::shift_left_with_flags(&mut val, &mut self.registers.status);
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::<M>::shift_left_with_flags(&mut operand, &mut self.registers.status);
self.memory.set_byte(addr, operand);
}
(Instruction::BCC, OpInput::UseRelative(rel)) => {
@ -208,18 +312,17 @@ 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::<M>::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::<M>::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::<M>::decrement(&mut self.registers.index_x, &mut self.registers.status);
}
(Instruction::EOR, OpInput::UseImmediate(val)) => {
@ -231,16 +334,15 @@ 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::<M>::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::<M>::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::<M>::increment(&mut self.registers.index_x, &mut self.registers.status);
}
(Instruction::JMP, OpInput::UseAddress(addr)) => self.jump(addr),
@ -278,14 +380,13 @@ 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::<M>::shift_right_with_flags(&mut val, &mut self.registers.status);
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::<M>::shift_right_with_flags(&mut operand, &mut self.registers.status);
self.memory.set_byte(addr, operand);
}
(Instruction::ORA, OpInput::UseImmediate(val)) => {
@ -323,26 +424,24 @@ 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::<M>::rotate_left_with_flags(&mut val, &mut self.registers.status);
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::<M>::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::<M>::rotate_right_with_flags(&mut val, &mut self.registers.status);
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::<M>::rotate_right_with_flags(&mut operand, &mut self.registers.status);
self.memory.set_byte(addr, operand);
}
(Instruction::SBC, OpInput::UseImmediate(val)) => {
@ -467,7 +566,7 @@ impl CPU {
..StatusArgs::none()
}),
);
CPU::set_flags_from_i8(status, *p_val as i8);
CPU::<M>::set_flags_from_i8(status, *p_val as i8);
}
fn shift_right_with_flags(p_val: &mut u8, status: &mut Status) {
@ -481,7 +580,7 @@ impl CPU {
..StatusArgs::none()
}),
);
CPU::set_flags_from_i8(status, *p_val as i8);
CPU::<M>::set_flags_from_i8(status, *p_val as i8);
}
fn rotate_left_with_flags(p_val: &mut u8, status: &mut Status) {
@ -489,7 +588,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 {
@ -497,7 +596,7 @@ impl CPU {
..StatusArgs::none()
}),
);
CPU::set_flags_from_i8(status, *p_val as i8);
CPU::<M>::set_flags_from_i8(status, *p_val as i8);
}
fn rotate_right_with_flags(p_val: &mut u8, status: &mut Status) {
@ -513,21 +612,21 @@ impl CPU {
..StatusArgs::none()
}),
);
CPU::set_flags_from_i8(status, *p_val as i8);
CPU::<M>::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::<M>::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::<M>::set_flags_from_i8(status, value);
}
fn load_x_register(&mut self, value: u8) {
CPU::set_u8_with_flags(
CPU::<M>::set_u8_with_flags(
&mut self.registers.index_x,
&mut self.registers.status,
value,
@ -535,7 +634,7 @@ impl CPU {
}
fn load_y_register(&mut self, value: u8) {
CPU::set_u8_with_flags(
CPU::<M>::set_u8_with_flags(
&mut self.registers.index_y,
&mut self.registers.status,
value,
@ -543,7 +642,7 @@ impl CPU {
}
fn load_accumulator(&mut self, value: i8) {
CPU::set_i8_with_flags(
CPU::<M>::set_i8_with_flags(
&mut self.registers.accumulator,
&mut self.registers.status,
value,
@ -552,11 +651,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!(
@ -828,7 +923,7 @@ impl CPU {
}
}
impl core::fmt::Debug for CPU {
impl<M: Bus> core::fmt::Debug for CPU<M> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
@ -842,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();
let mut cpu = CPU::new(Ram::new());
cpu.add_with_carry(-128);
assert_eq!(cpu.registers.accumulator, -128);
cpu.add_with_carry(-128);
@ -860,7 +956,7 @@ mod tests {
#[cfg_attr(feature = "decimal_mode", test)]
fn decimal_add_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.registers.status.or(Status::PS_DECIMAL_MODE);
cpu.add_with_carry(0x09);
@ -887,7 +983,7 @@ mod tests {
#[cfg_attr(feature = "decimal_mode", test)]
fn decimal_subtract_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.registers
.status
.or(Status::PS_DECIMAL_MODE | Status::PS_CARRY);
@ -909,7 +1005,7 @@ mod tests {
#[test]
fn add_with_carry_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.add_with_carry(1);
assert_eq!(cpu.registers.accumulator, 1);
@ -932,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();
let mut cpu = CPU::new(Ram::new());
cpu.add_with_carry(127);
assert_eq!(cpu.registers.accumulator, 127);
@ -963,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();
let mut cpu = CPU::new(Ram::new());
cpu.add_with_carry(127);
assert_eq!(cpu.registers.accumulator, 127);
@ -979,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();
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);
@ -988,7 +1084,7 @@ mod tests {
#[test]
fn and_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.registers.accumulator = 0;
cpu.and(-1);
@ -1017,7 +1113,7 @@ mod tests {
#[test]
fn subtract_with_carry_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
cpu.registers.accumulator = 0;
@ -1077,7 +1173,7 @@ mod tests {
#[test]
fn decrement_memory_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
let addr: u16 = 0xA1B2;
cpu.memory.set_byte(addr, 5);
@ -1114,7 +1210,7 @@ mod tests {
#[test]
fn decrement_x_test() {
let mut cpu = CPU::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);
@ -1124,7 +1220,7 @@ mod tests {
#[test]
fn decrement_y_test() {
let mut cpu = CPU::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);
@ -1136,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();
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);
@ -1172,7 +1268,7 @@ mod tests {
#[test]
fn dec_x_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.execute_instruction((Instruction::DEX, OpInput::UseImplied));
assert_eq!(cpu.registers.index_x, 0xff);
@ -1217,7 +1313,7 @@ mod tests {
#[test]
fn jump_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
let addr: u16 = 0xA1B1;
cpu.jump(addr);
@ -1226,7 +1322,7 @@ mod tests {
#[test]
fn branch_if_carry_clear_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
cpu.branch_if_carry_clear(0xABCD);
@ -1239,7 +1335,7 @@ mod tests {
#[test]
fn branch_if_carry_set_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
cpu.branch_if_carry_set(0xABCD);
@ -1252,7 +1348,7 @@ mod tests {
#[test]
fn branch_if_equal_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.branch_if_equal(0xABCD);
assert_eq!(cpu.registers.program_counter, (0));
@ -1265,7 +1361,7 @@ mod tests {
#[test]
fn branch_if_minus_test() {
{
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
let registers_before = cpu.registers;
cpu.branch_if_minus(0xABCD);
@ -1274,7 +1370,7 @@ mod tests {
}
{
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.registers.status.or(Status::PS_NEGATIVE);
let registers_before = cpu.registers;
@ -1287,7 +1383,7 @@ mod tests {
#[test]
fn branch_if_positive_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.registers.status.insert(Status::PS_NEGATIVE);
cpu.branch_if_positive(0xABCD);
@ -1300,7 +1396,7 @@ mod tests {
#[test]
fn branch_if_overflow_clear_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.registers.status.insert(Status::PS_OVERFLOW);
cpu.branch_if_overflow_clear(0xABCD);
@ -1313,7 +1409,7 @@ mod tests {
#[test]
fn branch_across_end_of_address_space() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.registers.program_counter = 0xffff;
cpu.registers.status.insert(Status::PS_OVERFLOW);
@ -1323,7 +1419,7 @@ mod tests {
#[test]
fn branch_if_overflow_set_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.branch_if_overflow_set(0xABCD);
assert_eq!(cpu.registers.program_counter, (0));
@ -1336,9 +1432,9 @@ mod tests {
#[cfg(test)]
fn compare_test_helper<F>(compare: &mut F, load_instruction: Instruction)
where
F: FnMut(&mut CPU, u8),
F: FnMut(&mut CPU<Ram>, u8),
{
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127)));
@ -1386,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<Ram>, val: u8| {
cpu.compare_with_a_register(val);
},
Instruction::LDA,
@ -1396,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<Ram>, val: u8| {
cpu.compare_with_x_register(val);
},
Instruction::LDX,
@ -1406,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<Ram>, val: u8| {
cpu.compare_with_y_register(val);
},
Instruction::LDY,
@ -1415,7 +1511,7 @@ mod tests {
#[test]
fn exclusive_or_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
for a_before in range_inclusive(0u8, 255u8) {
for val in range_inclusive(0u8, 255u8) {
@ -1443,7 +1539,7 @@ mod tests {
#[test]
fn inclusive_or_test() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
for a_before in range_inclusive(0u8, 255u8) {
for val in range_inclusive(0u8, 255u8) {
@ -1471,7 +1567,7 @@ mod tests {
#[test]
fn stack_underflow() {
let mut cpu = CPU::new();
let mut cpu = CPU::new(Ram::new());
let _val: u8 = cpu.pull_from_stack();
}
}

View File

@ -25,8 +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;
// Abbreviations
//
// General
@ -134,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 {
@ -162,94 +150,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;
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 = memory.get_slice(arr_to_addr(arr), 2);
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 = memory.get_slice(u16::from(start), 2);
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 = memory.get_slice(u16::from(start), 2);
OpInput::UseAddress(arr_to_addr(slice).wrapping_add(xextend(y)))
}
}
}
}
pub type DecodedInstr = (Instruction, OpInput);
@ -768,26 +668,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)
));
}
}

View File

@ -58,37 +58,37 @@ impl Default for Memory {
}
}
pub trait Bus {
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]) {
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(&mut self, address: u16) -> u8 {
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;
&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 {
let old_value = self.get_byte(address);
fn set_byte(&mut self, address: u16, value: u8) {
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
@ -96,23 +96,12 @@ impl Memory {
self.bytes[start..end].copy_from_slice(values);
}
pub fn is_stack_address(address: u16) -> bool {
address > 0xff && address < 0x200
}
}
#[cfg(test)]
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() {