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