Finally got the barrel shifter right?

The barrel shifter sure has lots of edge cases, need to implement tests ASAP


Former-commit-id: 989fc4477f610603154404d0ed4335619ebd6345
This commit is contained in:
Michel Heily 2019-07-08 21:58:30 +03:00
parent 2817db4e1c
commit 6c0df33597

View file

@ -98,7 +98,13 @@ impl BarrelShifterValue {
impl Core { impl Core {
/// Performs a generic barrel shifter operation /// Performs a generic barrel shifter operation
fn barrel_shift(&mut self, val: i32, amount: u32, shift: BarrelShiftOpCode) -> i32 { fn barrel_shift(
&mut self,
val: i32,
amount: u32,
shift: BarrelShiftOpCode,
immediate: bool,
) -> i32 {
// //
// From GBATEK: // From GBATEK:
// Zero Shift Amount (Shift Register by Immediate, with Immediate=0) // Zero Shift Amount (Shift Register by Immediate, with Immediate=0)
@ -135,24 +141,42 @@ impl Core {
} }
}, },
BarrelShiftOpCode::LSR => match amount { BarrelShiftOpCode::LSR => match amount {
x if x > 0 && x < 32 => {
self.cpsr.set_C(val >> (amount - 1) & 1 == 1);
((val as u32) >> amount) as i32
}
0 | 32 => { 0 | 32 => {
if immediate {
self.cpsr.set_C((val as u32).bit(31)); self.cpsr.set_C((val as u32).bit(31));
0 0
} else {
val
}
}
x if x < 32 => {
self.cpsr.set_C(val >> (amount - 1) & 1 == 1);
((val as u32) >> amount) as i32
} }
_ => { _ => {
self.cpsr.set_C(false); self.cpsr.set_C(false);
0 0
} }
}, },
BarrelShiftOpCode::ASR => { BarrelShiftOpCode::ASR => match amount {
if 0 < amount && amount < 32 { 0 => {
if immediate {
let bit31 = (val as u32).bit(31);
self.cpsr.set_C(bit31);
if bit31 {
-1
} else {
0
}
} else {
val
}
}
x if x < 32 => {
self.cpsr.set_C(val.wrapping_shr(amount - 1) & 1 == 1); self.cpsr.set_C(val.wrapping_shr(amount - 1) & 1 == 1);
val.wrapping_shr(amount) val.wrapping_shr(amount)
} else { }
_ => {
let bit31 = (val as u32).bit(31); let bit31 = (val as u32).bit(31);
self.cpsr.set_C(bit31); self.cpsr.set_C(bit31);
if bit31 { if bit31 {
@ -161,24 +185,28 @@ impl Core {
0 0
} }
} }
} },
BarrelShiftOpCode::ROR => { BarrelShiftOpCode::ROR => {
match amount { match amount {
0 => 0 => {
if immediate {
/* RRX */ /* RRX */
{ self.cpsr.set_C(val & 0b1 != 0);
let val = val as u32; let old_c = self.cpsr.C() as i32;
let old_c = self.cpsr.C() as u32; ((val as u32) >> 1) as i32 | (old_c << 31)
(val.rotate_right(1) | (old_c << 31)) as i32 } else {
}
32 => {
self.cpsr.set_C((val as u32).bit(31));
val val
} }
}
_ => { _ => {
let amount = amount % 32; let amount = amount % 32;
self.cpsr.set_C(((val as u32) >> (amount - 1)) & 1 == 1); let val = if amount != 0 {
val.rotate_right(amount) val.rotate_right(amount)
} else {
val
};
self.cpsr.set_C((val as u32).bit(31));
val
} }
} }
} }
@ -188,10 +216,12 @@ impl Core {
pub fn register_shift(&mut self, reg: usize, shift: ShiftedRegister) -> CpuResult<i32> { pub fn register_shift(&mut self, reg: usize, shift: ShiftedRegister) -> CpuResult<i32> {
let val = self.get_reg(reg) as i32; let val = self.get_reg(reg) as i32;
match shift { match shift {
ShiftedRegister::ByAmount(amount, shift) => Ok(self.barrel_shift(val, amount, shift)), ShiftedRegister::ByAmount(amount, shift) => {
Ok(self.barrel_shift(val, amount, shift, true))
}
ShiftedRegister::ByRegister(reg, shift) => { ShiftedRegister::ByRegister(reg, shift) => {
if reg != REG_PC { if reg != REG_PC {
Ok(self.barrel_shift(val, self.get_reg(reg) & 0xff, shift)) Ok(self.barrel_shift(val, self.get_reg(reg) & 0xff, shift, false))
} else { } else {
Err(CpuError::IllegalInstruction) Err(CpuError::IllegalInstruction)
} }