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;
}
pub fn map_address(address: u32) -> MemoryMap {
fn map_address(address: u32) -> MemoryMap {
let (bank, sub_address) = {
let bank = (address >> 16) as u8;
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) {
let (bytes, cycles) = CPU::common_bytes_cycles_arithmetic(addressing_mode);
self.registers.increment_pc(bytes); self.cycles += cycles;
@@ -139,11 +150,14 @@ impl CPU {
// Add 2 cycles if m = 1
let (_, cycles) = self.common_conditions(addressing_mode, &[Condition::MemorySelectFlag]);
self.cycles += cycles;
let (bytes, cycles) = self.common_conditions(addressing_mode, &[
Condition::MemorySelectFlag,
Condition::DirectPageZero,
Condition::IndexCrossesPageBoundary,
]);
let (bytes, cycles) = self.common_conditions(addressing_mode, &BITWISE_CONDITIONS);
self.registers.increment_pc(bytes); self.cycles += cycles;
}
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;
}
}
+72
View File
@@ -2,6 +2,7 @@ use super::cpu::CPU;
use crate::bus::Bus;
use crate::utils::addressing::{AddressingMode, IndexRegister};
use crate::utils::alu;
use crate::utils::num_trait::SnesNum;
impl CPU {
fn get_8bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u8 {
@@ -119,6 +120,30 @@ impl CPU {
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) {
type A = AddressingMode;
type I = IndexRegister;
@@ -177,6 +202,12 @@ impl CPU {
0x06 => self.asl(bus, A::DirectPage),
0x1E => self.asl(bus, A::AbsoluteIndexed(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),
}
}
@@ -253,4 +284,45 @@ mod cpu_instructions_tests {
assert!(!cpu.registers.get_zero_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 is_negative(&self) -> bool;
fn is_zero(&self) -> bool;
fn next_to_highest_bit(&self) -> bool;
fn to_u32(&self) -> u32;
fn from_u32(v: u32) -> Self;
fn invert(&self) -> Self;
@@ -74,6 +75,10 @@ macro_rules! define_impl {
(*self) == 0
}
fn next_to_highest_bit(&self) -> bool {
((*self) << 1) & !(<$t>::MAX >> 1) != 0
}
fn to_u32(&self) -> u32 {
(* self) as u32
}