ASL instruction

This commit is contained in:
2022-11-20 23:19:39 -05:00
parent f78883257a
commit 20747f5fad
4 changed files with 125 additions and 0 deletions
+30
View File
@@ -100,6 +100,23 @@ impl CPU {
A::DirectPageIndirectLongIndexed(_) => (2, 6),
A::StackRelative => (2, 4),
A::StackRelativeIndirectIndexed(_) => (2, 7),
_ => unreachable!(),
}
}
fn common_bytes_cycles_shift(addressing_mode: AddressingMode) -> (u16, usize) {
match addressing_mode {
A::Accumulator => (1, 2),
A::Absolute => (3, 6),
A::DirectPage => (2, 5),
// Note: in some documentations you will find that this addressing mode has
// 7 cycles for shift instructions, and then it says to substract
// 1 cycles if no page boundary is crossed.
// But to make it simpler, we are assigning 6 cycles here and then incrementing
// it by 1 later if a page boundary is crossed.
A::AbsoluteIndexed(_) => (3, 6),
A::DirectPageIndexed(_) => (2, 6),
_ => unreachable!(),
}
}
@@ -116,6 +133,19 @@ impl CPU {
self.cycles += cycles;
self.common_conditions(addressing_mode, &BITWISE_CONDITIONS);
}
pub fn increment_cycles_shift(&mut self, addressing_mode: AddressingMode) {
let (bytes, cycles) = CPU::common_bytes_cycles_shift(addressing_mode);
self.registers.increment_pc(bytes);
self.cycles += cycles;
// Add 2 cycles if m = 1
self.common_conditions(addressing_mode, &[Condition::MemorySelectFlag]);
self.common_conditions(addressing_mode, &[
Condition::MemorySelectFlag,
Condition::DirectPageZero,
Condition::IndexCrossesPageBoundary,
]);
}
}
#[cfg(test)]
+49
View File
@@ -113,6 +113,32 @@ impl CPU {
self.increment_cycles_bitwise(addressing_mode);
}
fn asl(&mut self, bus: &Bus, addressing_mode: AddressingMode) {
// if the M flag is set, perform 8 bit addition.
// Otherwise, 16 bit addition
let target = match addressing_mode {
AddressingMode::Accumulator => self.registers.a,
_ => match self.registers.is_16bit_mode() {
true => self.get_16bit_from_address(bus, addressing_mode),
false => self.get_8bit_from_address(bus, addressing_mode) as u16,
}
};
if self.registers.is_16bit_mode() {
let (result, is_negative, is_zero, is_carry) = alu::asl16bit(target);
self.registers.a = result;
self.registers.set_negative_flag(is_negative);
self.registers.set_zero_flag(is_zero);
self.registers.set_carry_flag(is_carry);
} else {
let (result, is_negative, is_zero, is_carry) = alu::asl8bit(target as u8);
self.registers.set_low_a(result);
self.registers.set_negative_flag(is_negative);
self.registers.set_zero_flag(is_zero);
self.registers.set_carry_flag(is_carry);
}
self.increment_cycles_shift(addressing_mode);
}
pub fn execute_opcode(&mut self, opcode: u8, bus: &Bus) {
type A = AddressingMode;
type I = IndexRegister;
@@ -165,6 +191,12 @@ impl CPU {
0x37 => self.and(bus, A::DirectPageIndirectLongIndexed(I::Y)),
0x23 => self.and(bus, A::StackRelative),
0x33 => self.and(bus, A::StackRelativeIndirectIndexed(I::Y)),
// ASL
0x0A => self.asl(bus, A::Accumulator),
0x0E => self.asl(bus, A::Absolute),
0x06 => self.asl(bus, A::DirectPage),
0x1E => self.asl(bus, A::AbsoluteIndexed(I::X)),
0x16 => self.asl(bus, A::DirectPageIndexed(I::X)),
_ => println!("Invalid opcode: {:02X}", opcode),
}
}
@@ -224,4 +256,21 @@ mod cpu_instructions_tests {
assert!(!cpu.registers.get_carry_flag());
assert!(!cpu.registers.get_zero_flag());
}
#[test]
fn test_asl() {
let mut cpu = CPU::new();
let bus = Bus::new();
cpu.registers.a = 0b01010000_00000000;
cpu.registers.pbr = 0x00;
cpu.registers.pc = 0x0000;
cpu.registers.set_memory_select_flag(false);
cpu.asl(&bus, AddressingMode::Accumulator);
assert_eq!(cpu.registers.a, 0b10100000_00000000);
assert_eq!(cpu.registers.pc, 0x01);
assert_eq!(cpu.cycles, 4);
assert!(!cpu.registers.get_carry_flag());
assert!(!cpu.registers.get_zero_flag());
assert!(cpu.registers.get_negative_flag());
}
}
+2
View File
@@ -93,6 +93,7 @@ pub enum IndexRegister {
#[derive(Copy, Clone)]
pub enum AddressingMode {
Accumulator,
Immediate,
Absolute,
AbsoluteLong,
@@ -114,6 +115,7 @@ impl AddressingMode {
use IndexRegister::X as X;
// TODO: maybe use impl Immediate {pub fn effective_address} to prevent this match statement?
match self {
Self::Accumulator => pc_addr,
Self::Immediate => immediate(pc_addr),
Self::Absolute => absolute(bus, pc_addr),
Self::AbsoluteLong => absolute_long(bus, pc_addr),
+44
View File
@@ -165,6 +165,22 @@ pub fn and16bit(target: u16, value: u16) -> (u16, bool, bool) {
(result, is_negative, is_zero)
}
pub fn asl8bit(target: u8) -> (u8, bool, bool, bool) {
let result = target << 1;
let is_negative = (result >> 7) == 1;
let is_zero = result == 0;
let is_carry = (target >> 7) == 1;
(result, is_negative, is_zero, is_carry)
}
pub fn asl16bit(target: u16) -> (u16, bool, bool, bool) {
let result = target << 1;
let is_negative = (result >> 15) == 1;
let is_zero = result == 0;
let is_carry = (target >> 15) == 1;
(result, is_negative, is_zero, is_carry)
}
#[cfg(test)]
mod alu_tests {
use super::*;
@@ -406,4 +422,32 @@ mod alu_tests {
assert_eq!(is_negative, false);
assert_eq!(is_zero, false);
}
#[test]
fn test_asl8bit() {
let (result, is_negative, is_zero, is_carry) = asl8bit(0b0101_0101);
assert_eq!(result, 0b1010_1010);
assert_eq!(is_negative, true);
assert_eq!(is_zero, false);
assert_eq!(is_carry, false);
let (result, is_negative, is_zero, is_carry) = asl8bit(0b1000_0000);
assert_eq!(result, 0b0000_0000);
assert_eq!(is_negative, false);
assert_eq!(is_zero, true);
assert_eq!(is_carry, true);
}
#[test]
fn test_asl16bit() {
let (result, is_negative, is_zero, is_carry) = asl16bit(0b01000000_00000000);
assert_eq!(result, 0b10000000_00000000);
assert_eq!(is_negative, true);
assert_eq!(is_zero, false);
assert_eq!(is_carry, false);
let (result, is_negative, is_zero, is_carry) = asl16bit(0b10000000_00000000);
assert_eq!(result, 0b00000000_00000000);
assert_eq!(is_negative, false);
assert_eq!(is_zero, true);
assert_eq!(is_carry, true);
}
}