use crate::cpu::*;
#[derive(Clone, Debug, Copy)]
#[allow(non_camel_case_types)]
pub enum AddressingModes {
Implicit,
Accumulator,
Relative,
Immediate,
ZeroPage,
ZeroPageX,
ZeroPageY,
Absolute,
AbsoluteX,
AbsoluteY,
Indirect,
IndexedIndirectX,
IndirectIndexedY,
}
impl AddressingModes {
pub fn get_operand_address<M: Memory>(self, cpu: &mut CPU<M>) -> u16 {
match self {
AddressingModes::Immediate => cpu.program_counter,
AddressingModes::ZeroPage => cpu.mapper.read_u8(cpu.program_counter) as u16,
AddressingModes::ZeroPageX => cpu
.mapper
.read_u8(cpu.program_counter)
.wrapping_add(cpu.register_x) as u16,
AddressingModes::ZeroPageY => cpu
.mapper
.read_u8(cpu.program_counter)
.wrapping_add(cpu.register_y) as u16,
AddressingModes::Absolute => cpu.mapper.read_u16(cpu.program_counter),
AddressingModes::AbsoluteX => cpu
.mapper
.read_u16(cpu.program_counter)
.wrapping_add(cpu.register_x as u16),
AddressingModes::AbsoluteY => cpu
.mapper
.read_u16(cpu.program_counter)
.wrapping_add(cpu.register_y as u16),
AddressingModes::Indirect => {
let indirect_adress = cpu.mapper.read_u16(cpu.program_counter);
let low_order_address = indirect_adress;
let high_order_address = match indirect_adress & 0x00FF {
0xFF => indirect_adress & 0b1111_1111_0000_0000,
_ => indirect_adress.wrapping_add(1),
};
let low_order_byte = cpu.mapper.read_u8(low_order_address);
let high_order_byte = cpu.mapper.read_u8(high_order_address);
u16::from_le_bytes([low_order_byte, high_order_byte])
}
AddressingModes::IndexedIndirectX => {
let indirect_address = cpu.mapper.read_u8(cpu.program_counter);
let inc_address = indirect_address.wrapping_add(cpu.register_x);
let address = cpu.mapper.zero_page_read_u16(inc_address);
return address;
}
AddressingModes::IndirectIndexedY => {
let indirect_address = cpu.mapper.read_u8(cpu.program_counter);
cpu.mapper
.zero_page_read_u16(indirect_address)
.wrapping_add(cpu.register_y as u16)
}
_ => {
panic!(
"Cannot get operand address when the Addressing Mode is {:?}",
self
);
}
}
}
pub fn get_operand<M: Memory>(self, cpu: &mut CPU<M>) -> u8 {
match self {
AddressingModes::Accumulator => cpu.register_a,
_ => {
let index = self.get_operand_address(cpu);
cpu.mapper.read_u8(index)
}
}
}
pub fn is_page_crossed<M: Memory>(&self, cpu: &mut CPU<M>) -> bool {
match self {
AddressingModes::Implicit => false,
AddressingModes::Accumulator => false,
AddressingModes::Relative => false,
AddressingModes::Immediate => false,
AddressingModes::ZeroPage => false,
AddressingModes::ZeroPageX => false,
AddressingModes::ZeroPageY => false,
AddressingModes::Absolute => false,
AddressingModes::AbsoluteX => {
let low = cpu.mapper.read_u8(cpu.program_counter);
let (_, page_crossed) = low.overflowing_add(cpu.register_x);
return page_crossed;
}
AddressingModes::AbsoluteY => {
let low = cpu.mapper.read_u8(cpu.program_counter);
let (_, page_crossed) = low.overflowing_add(cpu.register_y);
return page_crossed;
}
AddressingModes::Indirect => false,
AddressingModes::IndexedIndirectX => false,
AddressingModes::IndirectIndexedY => {
let indirect_address = cpu.mapper.read_u8(cpu.program_counter);
let (_, page_crossed) = cpu
.mapper
.read_u8(indirect_address as u16)
.overflowing_add(cpu.register_y);
return page_crossed;
}
}
}
}
#[cfg(test)]
mod test_addressing_modes {
use super::*;
use crate::cpu::mappers::test_mapper::TestMapper;
#[test]
fn test_addressing_mode_immediate() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0x8000;
let result = AddressingModes::Immediate.get_operand_address(&mut cpu);
assert_eq!(result, 0x8000);
}
#[test]
fn test_addressing_mode_zero_page() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0xAAAA;
cpu.mapper.write_u8(0xAAAA, 0xAA);
let result = AddressingModes::ZeroPage.get_operand_address(&mut cpu);
assert_eq!(result, 0xAA);
}
#[test]
fn test_addressing_mode_zero_page_x() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0xAAAA;
cpu.mapper.write_u8(0xAAAA, 0x80);
cpu.register_x = 0xFF;
let result = AddressingModes::ZeroPageX.get_operand_address(&mut cpu);
assert_eq!(result, 0x7F);
}
#[test]
fn test_addressing_mode_zero_page_y() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0xAAAA;
cpu.mapper.write_u8(0xAAAA, 0x80);
cpu.register_y = 0xFF;
let result = AddressingModes::ZeroPageY.get_operand_address(&mut cpu);
assert_eq!(result, 0x7F);
}
#[test]
fn test_addressing_mode_absolute() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0x0;
cpu.mapper.write_u8(0x0, 0x9e);
cpu.mapper.write_u8(0x1, 0x5e);
let result = AddressingModes::Absolute.get_operand_address(&mut cpu);
assert_eq!(result, 0x5e9e);
}
#[test]
fn test_addressing_mode_absolute_x() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0x0;
cpu.mapper.write_u16(0x00, 2000);
cpu.register_x = 82;
let result = AddressingModes::AbsoluteX.get_operand_address(&mut cpu);
assert_eq!(result, 2082);
}
#[test]
fn test_addressing_mode_absolute_y() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0x0;
cpu.mapper.write_u16(0x00, 2000);
cpu.register_y = 82;
let result = AddressingModes::AbsoluteY.get_operand_address(&mut cpu);
assert_eq!(result, 2082);
}
#[test]
fn test_addressing_mode_indexed_indirect_x() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0x8000;
cpu.mapper.write_u8(0x8000, 0x20);
cpu.mapper.write_u16(0x0021, 0xBAFC);
cpu.register_x = 0x01;
let result = AddressingModes::IndexedIndirectX.get_operand_address(&mut cpu);
assert_eq!(result, 0xBAFC);
}
#[test]
fn test_addressing_mode_indirect_indexed_y() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.program_counter = 0x8000;
cpu.mapper.write_u8(0x8000, 0x52);
cpu.mapper.write_u16(0x0052, 0xEF05);
cpu.register_y = 0x03;
let result = AddressingModes::IndirectIndexedY.get_operand_address(&mut cpu);
assert_eq!(result, 0xEF08);
}
#[test]
fn test_get_operand() {
let mapper = TestMapper::new();
let mut cpu = CPU::new(mapper);
cpu.register_a = 0x80;
let result = AddressingModes::Accumulator.get_operand(&mut cpu);
assert_eq!(result, 0x80);
}
}