Line data Source code
1 : //! Utils for register specified shift operations, register specified meaning a 2 : //! shift amount that comes from a Risc CPU register. See: [ARM7TDMI Datasheet 3 : //! Page.36-38] 4 : //! 5 : //! Register specified shift special effects list: 6 : //! 7 : //! 1. If the shift amount is 0, the carry-out is the old carry flag and val is 8 : //! untouched. 9 : //! 10 : //! 2. If the shift amount is between 1 and 31, it will operate the same as its 11 : //! [`instruction_specified_shift`] counter part. 12 : //! 13 : //! 3. If the shift amount is 32 or more, there are side effects depending on 14 : //! the shift: 15 : //! 16 : //! - LSL by 32 has result zero, carry out equal to bit 0 of the val. 17 : //! 18 : //! - LSL by more than 32 has result zero, carry out zero. 19 : //! 20 : //! - LSR by 32 has result zero, carry out equal to bit 31 of the val. 21 : //! 22 : //! - LSR by more than 32 has result zero, carry out zero. 23 : //! 24 : //! - ASR by 32 or more has the shift results val filled with and carry out 25 : //! equal to bit 31 of the val. 26 : //! 27 : //! - ROR by 32 has result equal to Rm, carry out equal to bit 31 of the val. 28 : //! 29 : //! - ROR by n where n is greater than 32 will give the same result and carry 30 : //! out as ROR by n-32; therefore repeatedly subtract 32 from n until the 31 : //! amount is in the of range 1 to 32. 32 : //! 33 : //! [ARM7TDMI Datasheet Page.36-38]:https://github.com/gregorygaines/gopherboyadvance/blob/main/docs/references/arm7tdmi-datasheet.pdf 34 : 35 : use crate::arch::instruction_specified_shift; 36 : use crate::arch::ShiftResults; 37 : use crate::util::bit_manipulation::is_bit_set_32; 38 : 39 : /// Shifts bits left by a register specified amount. 40 : /// 41 : /// Bits are moved out of the left-hand end and zeros are filled into the 42 : /// right-hand end. If the shift amount is zero, the carry-out is the old carry 43 : /// flag and val is untouched. If the shift amount is less than 32 and greater 44 : /// than 0, the shift functions identically as 45 : /// [`instruction_specified_shift::shift_left`]. If the shift amount is 32, the 46 : /// result val is zero, and the carry-out is equal to bit 0 of original val. If 47 : /// the shift amount is more than 32, the result is zero and the carry-out is 48 : /// false. 49 0 : pub fn shift_left(val: u32, shift_amount: u32, carry_flag: bool) -> ShiftResults { 50 0 : let shift_amount = shift_amount & 0xFF; 51 0 : if shift_amount == 0 { 52 0 : ShiftResults { val, carry_out: carry_flag } 53 0 : } else if shift_amount < 32 { 54 0 : instruction_specified_shift::shift_left(val, shift_amount, carry_flag) 55 0 : } else if shift_amount == 32 { 56 0 : ShiftResults { val: 0, carry_out: is_bit_set_32(val, /* bit_idx= */ 0) } 57 : } else { 58 : // Shift by more than 32 59 0 : ShiftResults { val: 0, carry_out: false } 60 : } 61 0 : } 62 : 63 : /// Shifts bits right by a register specified amount. 64 : /// 65 : /// Bits are moved out of the right-hand end and zeros are filled into the 66 : /// left-hand end. If the shift amount is zero, the carry-out is the old carry 67 : /// flag and val is untouched. If the shift amount is less than 32 and greater 68 : /// than 0, the shift functions identically as 69 : /// [`instruction_specified_shift::shift_right`]. If the shift amount is 32, the 70 : /// result val is zero, and the carry-out is equal to bit 31 of original val. If 71 : /// the shift amount is more than 32, the result is zero and the carry-out is 72 : /// false. 73 0 : pub fn shift_right(val: u32, shift_amount: u32, carry_flag: bool) -> ShiftResults { 74 0 : let shift_amount = shift_amount & 0xFF; 75 0 : if shift_amount == 0 { 76 0 : ShiftResults { val, carry_out: carry_flag } 77 0 : } else if shift_amount < 32 { 78 0 : instruction_specified_shift::shift_right(val, shift_amount) 79 0 : } else if shift_amount == 32 { 80 0 : ShiftResults { val: 0, carry_out: is_bit_set_32(val, /* bit_idx= */ 31) } 81 : } else { 82 : // Shift by more than 32 83 0 : ShiftResults { val: 0, carry_out: false } 84 : } 85 0 : } 86 : 87 : /// Rotates bits right by a register specified amount. 88 : /// 89 : /// Bits moved out of the right-hand end are rotated back into the left-hand 90 : /// end. If the shift amount is specified is zero, the carry-out is the old 91 : /// carry flag and val is untouched. Otherwise, the shift amount is repeatedly 92 : /// subtracted by 32 until its in the range of 1..=32. Once the shift amount is 93 : /// in range, if the shift amount is zero, this means the original shift amount 94 : /// was outside the range of an integer so the val is untouched and the carry 95 : /// out is bit 31 of val. If the shift amount is greater than zero, the shift 96 : /// functions identically as [`instruction_specified_shift::rotate_right`] using 97 : /// the newly in range shift amount. 98 0 : pub fn rotate_right(val: u32, shift_amount: u32, carry_flag: bool) -> ShiftResults { 99 0 : let shift_amount = shift_amount & 0xFF; 100 0 : if shift_amount == 0 { 101 0 : ShiftResults { val, carry_out: carry_flag } 102 : } else { 103 : // Force shift amount into the range of 1..=32 104 0 : let in_range_shift_amount = shift_amount & 0x1F; 105 0 : if in_range_shift_amount == 0 { 106 : // Original shift amount was outside the integer range. 107 0 : ShiftResults { val, carry_out: is_bit_set_32(val, /* bit_idx= */ 31) } 108 : } else { 109 0 : instruction_specified_shift::rotate_right(val, in_range_shift_amount, carry_flag) 110 : } 111 : } 112 0 : } 113 : 114 : /// Shifts bits right by a register specified amount while preserving the sign 115 : /// of the val. 116 : /// 117 : /// Bits are moved out of the right-hand end and bit 31 of the val are filled in 118 : /// the left-hand end to preserves the sign in 2's complement notation. If the 119 : /// shift amount is zero, the carry-out is the old carry flag and val is 120 : /// untouched. If the shift amount is greater than zero and less than 32, the 121 : /// shift functions identically as 122 : /// [`instruction_specified_shift::arithmetic_shift_right`]. If the shift amount 123 : /// is greater than 32, the carry-out is bit 32 of the original val the result 124 : /// val is filled with the carry out; either 0xFFFF_FFFF or 0x0. 125 0 : pub fn arithmetic_shift_right(val: u32, shift_amount: u32, carry_flag: bool) -> ShiftResults { 126 0 : let shift_amount = shift_amount & 0xFF; 127 0 : if shift_amount == 0 { 128 0 : ShiftResults { val, carry_out: carry_flag } 129 0 : } else if shift_amount < 32 { 130 0 : instruction_specified_shift::arithmetic_shift_right(val, shift_amount) 131 : } else { 132 : // Shift by more than 32 133 0 : let carry_out = is_bit_set_32(val, /* bit_idx= */ 31); 134 0 : if carry_out { 135 0 : ShiftResults { val: 0xFFFF_FFFF, carry_out } 136 : } else { 137 0 : ShiftResults { val: 0, carry_out } 138 : } 139 : } 140 0 : }