mirror of
https://github.com/FranLMSP/snes.git
synced 2026-08-03 08:30:01 -04:00
ASL instruction
This commit is contained in:
@@ -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)]
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user