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