LCOV - code coverage report
Current view: top level - src/arch - register_specified_shift.rs (source / functions) Hit Total Coverage
Test: lcov Lines: 0 40 0.0 %
Date: 2023-12-07 18:49:03 Functions: 0 4 0.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.16