BIT instruction

This commit is contained in:
2022-11-24 23:10:02 -05:00
parent e9f3cd24c7
commit 0ea8fdca2c
4 changed files with 97 additions and 6 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ impl Bus {
self.wram[(address & 0xFFFF) as usize] = value; self.wram[(address & 0xFFFF) as usize] = value;
} }
pub fn map_address(address: u32) -> MemoryMap { fn map_address(address: u32) -> MemoryMap {
let (bank, sub_address) = { let (bank, sub_address) = {
let bank = (address >> 16) as u8; let bank = (address >> 16) as u8;
let sub_address = address as u16; let sub_address = address as u16;
+19 -5
View File
@@ -119,6 +119,17 @@ impl CPU {
} }
} }
fn common_bytes_cycles_bit(addressing_mode: AddressingMode) -> (u16, usize) {
match addressing_mode {
A::Immediate => (2, 2),
A::Absolute => (3, 4),
A::DirectPage => (2, 3),
A::AbsoluteIndexed(_) => (3, 4),
A::DirectPageIndexed(_) => (2, 4),
_ => unreachable!(),
}
}
pub fn increment_cycles_arithmetic(&mut self, addressing_mode: AddressingMode) { pub fn increment_cycles_arithmetic(&mut self, addressing_mode: AddressingMode) {
let (bytes, cycles) = CPU::common_bytes_cycles_arithmetic(addressing_mode); let (bytes, cycles) = CPU::common_bytes_cycles_arithmetic(addressing_mode);
self.registers.increment_pc(bytes); self.cycles += cycles; self.registers.increment_pc(bytes); self.cycles += cycles;
@@ -139,11 +150,14 @@ impl CPU {
// Add 2 cycles if m = 1 // Add 2 cycles if m = 1
let (_, cycles) = self.common_conditions(addressing_mode, &[Condition::MemorySelectFlag]); let (_, cycles) = self.common_conditions(addressing_mode, &[Condition::MemorySelectFlag]);
self.cycles += cycles; self.cycles += cycles;
let (bytes, cycles) = self.common_conditions(addressing_mode, &[ let (bytes, cycles) = self.common_conditions(addressing_mode, &BITWISE_CONDITIONS);
Condition::MemorySelectFlag, self.registers.increment_pc(bytes); self.cycles += cycles;
Condition::DirectPageZero, }
Condition::IndexCrossesPageBoundary,
]); pub fn increment_cycles_bit(&mut self, addressing_mode: AddressingMode) {
let (bytes, cycles) = CPU::common_bytes_cycles_bit(addressing_mode);
self.registers.increment_pc(bytes); self.cycles += cycles;
let (bytes, cycles) = self.common_conditions(addressing_mode, &BITWISE_CONDITIONS);
self.registers.increment_pc(bytes); self.cycles += cycles; self.registers.increment_pc(bytes); self.cycles += cycles;
} }
} }
+72
View File
@@ -2,6 +2,7 @@ use super::cpu::CPU;
use crate::bus::Bus; use crate::bus::Bus;
use crate::utils::addressing::{AddressingMode, IndexRegister}; use crate::utils::addressing::{AddressingMode, IndexRegister};
use crate::utils::alu; use crate::utils::alu;
use crate::utils::num_trait::SnesNum;
impl CPU { impl CPU {
fn get_8bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u8 { fn get_8bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u8 {
@@ -119,6 +120,30 @@ impl CPU {
self.increment_cycles_shift(addressing_mode); self.increment_cycles_shift(addressing_mode);
} }
fn do_bit<T: SnesNum>(&mut self, accumulator: T, value: T, addressing_mode: AddressingMode) {
let (result, _) = alu::and(accumulator, value);
// Immediate addressing affects only the zero flag
match addressing_mode {
AddressingMode::Immediate => self.registers.set_zero_flag(result.is_zero()),
_ => {
self.registers.set_zero_flag(result.is_zero());
self.registers.set_negative_flag(value.is_negative());
self.registers.set_overflow_flag(value.next_to_highest_bit());
}
};
}
fn bit(&mut self, bus: &Bus, addressing_mode: AddressingMode) {
if self.registers.is_16bit_mode() {
let value = self.get_16bit_from_address(bus, addressing_mode);
self.do_bit(self.registers.a, value, addressing_mode);
} else {
let value = self.get_8bit_from_address(bus, addressing_mode);
self.do_bit(self.registers.a as u8, value, addressing_mode);
}
self.increment_cycles_bit(addressing_mode);
}
pub fn execute_opcode(&mut self, opcode: u8, bus: &Bus) { pub fn execute_opcode(&mut self, opcode: u8, bus: &Bus) {
type A = AddressingMode; type A = AddressingMode;
type I = IndexRegister; type I = IndexRegister;
@@ -177,6 +202,12 @@ impl CPU {
0x06 => self.asl(bus, A::DirectPage), 0x06 => self.asl(bus, A::DirectPage),
0x1E => self.asl(bus, A::AbsoluteIndexed(I::X)), 0x1E => self.asl(bus, A::AbsoluteIndexed(I::X)),
0x16 => self.asl(bus, A::DirectPageIndexed(I::X)), 0x16 => self.asl(bus, A::DirectPageIndexed(I::X)),
// BIT
0x89 => self.bit(bus, A::Immediate),
0x2C => self.bit(bus, A::Absolute),
0x24 => self.bit(bus, A::DirectPage),
0x3C => self.bit(bus, A::AbsoluteIndexed(I::X)),
0x34 => self.bit(bus, A::DirectPageIndexed(I::X)),
_ => println!("Invalid opcode: {:02X}", opcode), _ => println!("Invalid opcode: {:02X}", opcode),
} }
} }
@@ -253,4 +284,45 @@ mod cpu_instructions_tests {
assert!(!cpu.registers.get_zero_flag()); assert!(!cpu.registers.get_zero_flag());
assert!(cpu.registers.get_negative_flag()); assert!(cpu.registers.get_negative_flag());
} }
#[test]
fn test_bit() {
let mut cpu = CPU::new();
let mut bus = Bus::new();
cpu.registers.a = 0b1111_0000;
cpu.registers.pbr = 0x00;
cpu.registers.pc = 0x0000;
cpu.registers.p = 0x00;
bus.write(0x000001, 0b0000_1111);
cpu.registers.set_16bit_mode(false);
cpu.bit(&bus, AddressingMode::Immediate);
// Check that it only affects the zero flag on immediate mode
assert_eq!(cpu.registers.a, 0b1111_0000); // Check that A is not altered
assert_eq!(cpu.registers.p, 0b0010_0010); // Only zero flag was altered (bit 6 is memory select mode)
assert_eq!(cpu.registers.pc, 0x02);
assert_eq!(cpu.cycles, 2);
assert!(cpu.registers.get_zero_flag());
cpu.registers.a = 0b00110000_00000000;
cpu.registers.pbr = 0x00;
cpu.registers.pc = 0x0000;
cpu.registers.p = 0x00;
cpu.cycles = 0;
// Write absolute address
bus.write(0x000001, 0x04);
bus.write(0x000002, 0x00);
// Write effective value of address
bus.write(0x000004, 0x00);
bus.write(0x000005, 0b1100_0000);
cpu.registers.set_16bit_mode(true);
cpu.bit(&bus, AddressingMode::Absolute);
// Check that it only affects the zero flag on immediate mode
assert_eq!(cpu.registers.a, 0b00110000_00000000); // Check that A is not altered
assert_eq!(cpu.registers.p, 0b1100_0010);
assert_eq!(cpu.registers.pc, 0x03);
assert_eq!(cpu.cycles, 5);
assert!(cpu.registers.get_zero_flag());
assert!(cpu.registers.get_negative_flag());
assert!(cpu.registers.get_overflow_flag());
}
} }
+5
View File
@@ -8,6 +8,7 @@ pub trait SnesNum: Copy + Clone + Sized + Eq + PartialEq {
fn asl(&self) -> Self; fn asl(&self) -> Self;
fn is_negative(&self) -> bool; fn is_negative(&self) -> bool;
fn is_zero(&self) -> bool; fn is_zero(&self) -> bool;
fn next_to_highest_bit(&self) -> bool;
fn to_u32(&self) -> u32; fn to_u32(&self) -> u32;
fn from_u32(v: u32) -> Self; fn from_u32(v: u32) -> Self;
fn invert(&self) -> Self; fn invert(&self) -> Self;
@@ -74,6 +75,10 @@ macro_rules! define_impl {
(*self) == 0 (*self) == 0
} }
fn next_to_highest_bit(&self) -> bool {
((*self) << 1) & !(<$t>::MAX >> 1) != 0
}
fn to_u32(&self) -> u32 { fn to_u32(&self) -> u32 {
(* self) as u32 (* self) as u32
} }