1use core::num::NonZero;
2
3use osom_encoders_common::osom_debug_assert;
4
5use super::Size;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10#[must_use]
11pub enum GPRKind {
12 Bit8High = 1, Bit8,
17
18 Bit16,
20
21 Bit32,
23
24 Bit64,
26}
27
28impl GPRKind {
29 #[inline(always)]
30 #[must_use]
31 pub const fn as_u8(self) -> u8 {
32 let result = self as u8;
33 unsafe { core::hint::assert_unchecked(result > 0 && result < 8) };
34 result
35 }
36
37 #[inline(always)]
43 pub const unsafe fn from_u8_unchecked(value: u8) -> Self {
44 osom_debug_assert!(value > 0 && value < 8);
45 unsafe { core::mem::transmute(value) }
46 }
47
48 #[must_use]
55 pub const fn from_u8(value: u8) -> Option<Self> {
56 if value == 0 || value >= 8 {
57 return None;
58 }
59
60 Some(unsafe { Self::from_u8_unchecked(value) })
61 }
62
63 #[inline(always)]
65 #[must_use]
66 pub const fn equals(self, other: Self) -> bool {
67 self.as_u8() == other.as_u8()
68 }
69
70 #[inline(always)]
71 pub const fn size(self) -> Size {
72 match self {
73 GPRKind::Bit8 | GPRKind::Bit8High => Size::Bit8,
74 GPRKind::Bit16 => Size::Bit16,
75 GPRKind::Bit32 => Size::Bit32,
76 GPRKind::Bit64 => Size::Bit64,
77 }
78 }
79}
80
81impl core::hash::Hash for GPRKind {
82 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
83 self.as_u8().hash(state);
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91#[repr(transparent)]
92#[must_use]
93pub struct GPR {
94 value: NonZero<u8>,
95}
96
97const GPR_KIND_SHIFT: u8 = 5;
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
101#[repr(u8)]
102pub enum NewGPRError {
103 InvalidBit8HighIndex,
105
106 IndexOutOfRange,
108}
109
110macro_rules! gpr {
111 ($name: ident, $kind: ident, $index: literal) => {
112 pub const $name: Self = unsafe { Self::new_unchecked(GPRKind::$kind, $index) };
113 };
114}
115
116impl GPR {
117 gpr!(RAX, Bit64, 0);
118 gpr!(RCX, Bit64, 1);
119 gpr!(RDX, Bit64, 2);
120 gpr!(RBX, Bit64, 3);
121 gpr!(RSP, Bit64, 4);
122 gpr!(RBP, Bit64, 5);
123 gpr!(RSI, Bit64, 6);
124 gpr!(RDI, Bit64, 7);
125 gpr!(R8, Bit64, 8);
126 gpr!(R9, Bit64, 9);
127 gpr!(R10, Bit64, 10);
128 gpr!(R11, Bit64, 11);
129 gpr!(R12, Bit64, 12);
130 gpr!(R13, Bit64, 13);
131 gpr!(R14, Bit64, 14);
132 gpr!(R15, Bit64, 15);
133
134 gpr!(EAX, Bit32, 0);
135 gpr!(ECX, Bit32, 1);
136 gpr!(EDX, Bit32, 2);
137 gpr!(EBX, Bit32, 3);
138 gpr!(ESP, Bit32, 4);
139 gpr!(EBP, Bit32, 5);
140 gpr!(ESI, Bit32, 6);
141 gpr!(EDI, Bit32, 7);
142 gpr!(R8D, Bit32, 8);
143 gpr!(R9D, Bit32, 9);
144 gpr!(R10D, Bit32, 10);
145 gpr!(R11D, Bit32, 11);
146 gpr!(R12D, Bit32, 12);
147 gpr!(R13D, Bit32, 13);
148 gpr!(R14D, Bit32, 14);
149 gpr!(R15D, Bit32, 15);
150
151 gpr!(AX, Bit16, 0);
152 gpr!(CX, Bit16, 1);
153 gpr!(DX, Bit16, 2);
154 gpr!(BX, Bit16, 3);
155 gpr!(SP, Bit16, 4);
156 gpr!(BP, Bit16, 5);
157 gpr!(SI, Bit16, 6);
158 gpr!(DI, Bit16, 7);
159 gpr!(R8W, Bit16, 8);
160 gpr!(R9W, Bit16, 9);
161 gpr!(R10W, Bit16, 10);
162 gpr!(R11W, Bit16, 11);
163 gpr!(R12W, Bit16, 12);
164 gpr!(R13W, Bit16, 13);
165 gpr!(R14W, Bit16, 14);
166 gpr!(R15W, Bit16, 15);
167
168 gpr!(AL, Bit8, 0);
169 gpr!(CL, Bit8, 1);
170 gpr!(DL, Bit8, 2);
171 gpr!(BL, Bit8, 3);
172 gpr!(SPL, Bit8, 4);
173 gpr!(BPL, Bit8, 5);
174 gpr!(SIL, Bit8, 6);
175 gpr!(DIL, Bit8, 7);
176 gpr!(R8B, Bit8, 8);
177 gpr!(R9B, Bit8, 9);
178 gpr!(R10B, Bit8, 10);
179 gpr!(R11B, Bit8, 11);
180 gpr!(R12B, Bit8, 12);
181 gpr!(R13B, Bit8, 13);
182 gpr!(R14B, Bit8, 14);
183 gpr!(R15B, Bit8, 15);
184
185 gpr!(AH, Bit8High, 4);
186 gpr!(CH, Bit8High, 5);
187 gpr!(DH, Bit8High, 6);
188 gpr!(BH, Bit8High, 7);
189
190 #[inline(always)]
198 pub const unsafe fn new_unchecked(kind: GPRKind, index: u8) -> Self {
199 osom_debug_assert!(!kind.equals(GPRKind::Bit8High) || (index >= 4 && index <= 7));
200 osom_debug_assert!(index < 32);
201
202 let kind_u8 = kind.as_u8();
203
204 let value = (kind_u8 << GPR_KIND_SHIFT) | index;
205
206 unsafe {
207 Self {
208 value: NonZero::new_unchecked(value),
209 }
210 }
211 }
212
213 #[inline(always)]
221 pub const fn new(kind: GPRKind, index: u8) -> Result<Self, NewGPRError> {
222 if kind.equals(GPRKind::Bit8High) && (index < 4 || index > 7) {
223 return Err(NewGPRError::InvalidBit8HighIndex);
224 }
225
226 if index >= 32 {
227 return Err(NewGPRError::IndexOutOfRange);
228 }
229
230 Ok(unsafe { Self::new_unchecked(kind, index) })
231 }
232
233 #[inline(always)]
234 pub const fn kind(self) -> GPRKind {
235 let kind_u8 = self.value.get() >> GPR_KIND_SHIFT;
236 unsafe { GPRKind::from_u8_unchecked(kind_u8) }
237 }
238
239 #[inline(always)]
240 pub const fn size(self) -> Size {
241 self.kind().size()
242 }
243
244 #[inline(always)]
245 #[must_use]
246 pub(crate) const fn index(self) -> u8 {
247 self.value.get() & 0b11111
248 }
249
250 #[inline(always)]
252 #[must_use]
253 pub(crate) const fn is_extended(self) -> bool {
254 self.index() > 0b111
255 }
256
257 #[allow(dead_code)] #[inline(always)]
261 #[must_use]
262 pub(crate) const fn is_apx(self) -> bool {
263 self.index() > 0b1111
264 }
265
266 #[inline(always)]
267 #[must_use]
268 pub(crate) const fn lower_3_bits_index(self) -> u8 {
269 self.index() & 0b111
270 }
271
272 #[inline(always)]
279 #[must_use]
280 pub(crate) const fn index_matches_bit8_high(self) -> bool {
281 let idx = self.index();
282 idx >= 4 && idx <= 7
283 }
284
285 #[inline(always)]
286 #[must_use]
287 pub const fn equals(&self, other: &Self) -> bool {
288 self.value.get() == other.value.get()
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use rstest::rstest;
295
296 use super::*;
297
298 #[rstest]
299 #[case(GPR::RAX, Size::Bit64)]
300 #[case(GPR::EAX, Size::Bit32)]
301 #[case(GPR::AX, Size::Bit16)]
302 #[case(GPR::AL, Size::Bit8)]
303 #[case(GPR::AH, Size::Bit8)]
304 #[case(GPR::R8, Size::Bit64)]
305 #[case(GPR::R8D, Size::Bit32)]
306 #[case(GPR::R8W, Size::Bit16)]
307 #[case(GPR::R8B, Size::Bit8)]
308 fn test_gpr_size(#[case] gpr: GPR, #[case] size: Size) {
309 assert_eq!(gpr.size(), size);
310 }
311}