Skip to main content

rysk_core/
variant.rs

1use crate::register::Register;
2
3/// Decode an instruction encoding variant into its significant parts
4/// ```rust
5/// use rysk_core::variant::{ self, Variant };
6/// let instruction = [0x13, 0, 0, 0];
7/// let variant::R { destination, source1, source2 } = Variant::decode(instruction);
8/// ```
9pub trait Variant {
10    fn decode(instruction: [u8; 4]) -> Self;
11}
12
13/// Extract the destination register index from an instruction
14#[inline(always)]
15fn destination(instruction: [u8; 4]) -> usize {
16    (((instruction[0] & 0x80) >> 7) | ((instruction[1] & 0x0F) << 1)) as _
17}
18/// Extract the first source register index from an instruction
19#[inline(always)]
20fn source1(instruction: [u8; 4]) -> usize {
21    (((instruction[1] & 0x80) >> 7) | ((instruction[2] & 0x0F) << 1)) as _
22}
23/// Extract the second source register index from an instruction
24#[inline(always)]
25fn source2(instruction: [u8; 4]) -> usize {
26    (((instruction[2] & 0xF0) >> 4) | ((instruction[3] & 0x01) << 4)) as _
27}
28
29/// The R instruction type, encoding a destination and 2 source registers.
30pub struct R {
31    pub destination: usize,
32    pub source1: usize,
33    pub source2: usize
34}
35impl Variant for R {
36    fn decode(instruction: [u8; 4]) -> Self {
37        Self {
38            destination: destination(instruction),
39            source1: source1(instruction),
40            source2: source2(instruction),
41        }
42    }
43}
44
45/// The I instruction type, encoding a destination and source register as well as an immediate value.
46/// The immediate value is a sign extended 12-bit integer.
47pub struct I<R: Register> {
48    pub destination: usize,
49    pub source: usize,
50    pub immediate: R
51}
52impl<R: Register> Variant for I<R> {
53    fn decode(instruction: [u8; 4]) -> Self {
54        let signed = instruction[3] & 0x80 != 0;
55        Self {
56            destination: destination(instruction),
57            source: source1(instruction),
58            immediate: R::sign_extended_half([((instruction[2] & 0xF0) >> 4) | ((instruction[3] & 0x0F) << 4), ((instruction[3] & 0xF0) >> 4) | if signed { 0xF0 } else { 0 }])
59        }
60    }
61}
62
63/// A variation of the I type where the immediate encodes a 12-bit unsigned integer index.
64pub struct C {
65    pub destination: usize,
66    pub source: usize,
67    pub csr: usize
68}
69impl Variant for C {
70    fn decode(instruction: [u8; 4]) -> Self {
71        Self {
72            destination: destination(instruction),
73            source: source1(instruction),
74            csr: ((instruction[2] & 0xF0) >> 4) as usize | ((instruction[3] & 0x0F) << 4) as usize | ((instruction[3] & 0xF0) << 4) as usize
75        }
76    }
77}
78
79/// The S instruction type, encoding 2 source registers and a 12-bit sign extended immediate value.
80pub struct S<R: Register> {
81    pub source1: usize,
82    pub source2: usize,
83    pub immediate: R
84}
85impl<R: Register> Variant for S<R> {
86    fn decode(instruction: [u8; 4]) -> Self {
87        let signed = instruction[3] & 0x80 != 0;
88        Self {
89            source1: source1(instruction),
90            source2: source2(instruction),
91            immediate: R::sign_extended_half([((instruction[0] & 0x80) >> 7) | ((instruction[1] & 0x0F) << 1) | ((instruction[3] & 0x0E) << 4), ((instruction[3] & 0xF0) >> 4) | if signed { 0xF0 } else { 0 }])
92        }
93    }
94}
95
96/// A variation of the S type where the immediate is a 13-bit branch offset.
97/// The branch offset's least significant bit is not set as it must always be aligned, thereby allowing for larger offsets.
98pub struct B<R: Register> {
99    pub source1: usize,
100    pub source2: usize,
101    pub immediate: R
102}
103impl<R: Register> Variant for B<R> {
104    fn decode(instruction: [u8; 4]) -> Self {
105        let signed = instruction[3] & 0x80 != 0;
106        Self {
107            source1: source1(instruction),
108            source2: source2(instruction),
109            immediate: R::sign_extended_half([
110                ((instruction[1] & 0xF) << 1) | ((instruction[3] & 0x0E) << 4),
111                ((instruction[3] & 0x70) >> 4) | ((instruction[0] & 0x80) >> 4) | ((instruction[3] & 0x80) >> 3) | if signed { 0xE0 } else { 0 },
112            ])
113        }
114    }
115}
116
117/// The U instruction variant, encoding a destination and a 32-bit immediate value with the lower 12 bits zeroed.
118pub struct U<R: Register> {
119    pub destination: usize,
120    pub immediate: R
121}
122impl<R: Register> Variant for U<R> {
123    fn decode(instruction: [u8; 4]) -> Self {
124        Self {
125            destination: destination(instruction),
126            immediate: R::sign_extended_word([0, instruction[1] & 0xF0, instruction[2], instruction[3]])
127        }
128    }
129}
130
131/// A variation of the U instruction type where the immediate encodes a 21-bit jump offset.
132/// The least significant bit of the offset is zeroed as it must be aligned, thereby allowing a greater offset range.
133pub struct J<R: Register> {
134    pub destination: usize,
135    pub immediate: R
136}
137impl<R: Register> Variant for J<R> {
138    fn decode(instruction: [u8; 4]) -> Self {
139        let signed = instruction[3] & 0x80 != 0;
140        Self {
141            destination: destination(instruction),
142            immediate: R::sign_extended_word([
143                ((instruction[2] & 0xE0) >> 4) // 1-3
144                    | ((instruction[3] & 0x0F) << 4), // 4-7
145                ((instruction[3] & 0x70) >> 4) // 8-10
146                    | ((instruction[2] & 0x10) >> 1) // 11
147                    | (instruction[1] & 0xF0), // 12-15
148                (instruction[2] & 0x0F) // 16-19
149                    | ((instruction[3] & 0x80) >> 3) // 20
150                    | if signed {0xE0} else {0},
151                    if signed {0xFF} else {0}
152            ])
153        }
154    }
155}