1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
//! Utils for instruction specified shift operations, instruction specified
//! meaning a shift amount that's encoded in an Risc instruction. See: [ARM7TDMI
//! Datasheet Page.36-38]
//!
//! [ARM7TDMI Datasheet Page.36-38]:https://github.com/gregorygaines/gopherboyadvance/blob/main/docs/references/arm7tdmi-datasheet.pdf
use crate::arch::ShiftResults;
use crate::util::bit_manipulation::is_bit_set_32;
/// Shifts bits left by an instruction specified amount.
///
/// Bits are moved out of the left-hand end and zeros are filled into the
/// right-hand end. If the shift amount is zero, the carry out is the old carry
/// flag and val is unchanged.
pub fn shift_left(val: u32, shift_amount: u32, carry_flag: bool) -> ShiftResults {
if shift_amount == 0 {
ShiftResults { val, carry_out: carry_flag }
} else {
ShiftResults {
val: val << shift_amount,
carry_out: is_bit_set_32(val, (32 - shift_amount) as i32),
}
}
}
/// Shifts bits right by an instruction specified amount.
///
/// Bits are moved out of the right-hand end and zeros are filled in the left
/// hand end. If the shift amount is zero, the shift is interpreted as LSR#32 or
/// shift right by 32 which produces a result val of zero, and the carry out
/// being bit 31 of val of the original val.
pub fn shift_right(val: u32, shift_amount: u32) -> ShiftResults {
if shift_amount == 0 {
ShiftResults { val: 0, carry_out: is_bit_set_32(val, /* bit_idx= */ 31) }
} else {
ShiftResults {
val: val >> shift_amount,
carry_out: is_bit_set_32(val, (shift_amount - 1) as i32),
}
}
}
/// Rotates bits right by an instruction specified amount.
///
/// Bits moved out of the right-hand end are rotated back into the left-hand
/// end. If the shift amount is zero, the instruction becomes a RRX or rotate
/// right extended. A RRX is a shift right by 1 bit, with bit 0 being the carry
/// out and bit 31 being the carry flag.
pub fn rotate_right(val: u32, shift_amount: u32, carry_flag: bool) -> ShiftResults {
if shift_amount == 0 {
ShiftResults {
val: ((carry_flag as u32) << 31) | (val >> 1),
carry_out: is_bit_set_32(val, /* bit_idx= */ 0),
}
} else {
ShiftResults {
val: (val >> shift_amount) | (val << (32 - shift_amount)),
carry_out: is_bit_set_32(val, (shift_amount - 1) as i32),
}
}
}
/// Shifts bits right by an instruction specified amount while preserving the
/// sign of the val.
///
/// Bits are moved out of the right-hand end and bit 31 of the val are filled in
/// the left-hand end to preserves the sign in 2's complement notation. If the
/// shift amount is zero, the shift is interpreted as ASR#32 or arithmetic shift
/// right by 32. The bits of the result val becomes all ones or zeros, according
/// to bit 31 in val; either 0xFFFF_FFFF or 0x0 and the carry out is bit 31 of
/// original val.
pub fn arithmetic_shift_right(val: u32, shift_amount: u32) -> ShiftResults {
if shift_amount == 0 {
let carry_out = is_bit_set_32(val, /* bit_idx= */ 31);
if carry_out {
ShiftResults { val: 0xFFFF_FFFF, carry_out }
} else {
ShiftResults { val: 0, carry_out }
}
} else {
// Preserve the most significant bit (msb) of the val as a integer.
let preserved_msb = val & 0x8000_0000;
let mut res = val;
for _ in 0..shift_amount {
// Shift right and set the msb to the preserved msb.
res = (res >> 1) | preserved_msb
}
ShiftResults { val: res, carry_out: is_bit_set_32(val, (shift_amount - 1) as i32) }
}
}