diff --git a/src/arm7tdmi/alu.rs b/src/arm7tdmi/alu.rs index 7ab9ead..ce2a2a1 100644 --- a/src/arm7tdmi/alu.rs +++ b/src/arm7tdmi/alu.rs @@ -98,7 +98,13 @@ impl BarrelShifterValue { impl Core { /// 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: // Zero Shift Amount (Shift Register by Immediate, with Immediate=0) @@ -135,24 +141,42 @@ impl Core { } }, BarrelShiftOpCode::LSR => match amount { - x if x > 0 && x < 32 => { + 0 | 32 => { + if immediate { + self.cpsr.set_C((val as u32).bit(31)); + 0 + } else { + val + } + } + x if x < 32 => { self.cpsr.set_C(val >> (amount - 1) & 1 == 1); ((val as u32) >> amount) as i32 } - 0 | 32 => { - self.cpsr.set_C((val as u32).bit(31)); - 0 - } _ => { self.cpsr.set_C(false); 0 } }, - BarrelShiftOpCode::ASR => { - if 0 < amount && amount < 32 { + BarrelShiftOpCode::ASR => match amount { + 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); val.wrapping_shr(amount) - } else { + } + _ => { let bit31 = (val as u32).bit(31); self.cpsr.set_C(bit31); if bit31 { @@ -161,24 +185,28 @@ impl Core { 0 } } - } + }, BarrelShiftOpCode::ROR => { match amount { - 0 => - /* RRX */ - { - let val = val as u32; - let old_c = self.cpsr.C() as u32; - (val.rotate_right(1) | (old_c << 31)) as i32 - } - 32 => { - self.cpsr.set_C((val as u32).bit(31)); - val + 0 => { + if immediate { + /* RRX */ + self.cpsr.set_C(val & 0b1 != 0); + let old_c = self.cpsr.C() as i32; + ((val as u32) >> 1) as i32 | (old_c << 31) + } else { + val + } } _ => { let amount = amount % 32; - self.cpsr.set_C(((val as u32) >> (amount - 1)) & 1 == 1); - val.rotate_right(amount) + let val = if amount != 0 { + 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 { let val = self.get_reg(reg) as i32; 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) => { 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 { Err(CpuError::IllegalInstruction) }