csx64/exec/
registers.rs

1//! Various types of emulated hardware registers.
2
3use rug::float::Round;
4
5/// A 64-bit general-purpose CPU register with standard partitioning.
6#[derive(Default, Clone, Copy)]
7pub struct CPURegister(pub u64);
8impl CPURegister {
9    /// Gets the full 64-bit value.
10    pub const fn get_x64(self) -> u64 {
11        self.0
12    }
13    /// Sets the full 64-bit value.
14    pub fn set_x64(&mut self, val: u64) {
15        self.0 = val;
16    }
17
18    /// Gets the low 32-bits.
19    pub const fn get_x32(self) -> u32 {
20        self.0 as u32
21    }
22    /// Sets the low 32-bits to `val` and zeros the high 32-bits.
23    pub fn set_x32(&mut self, val: u32) {
24        self.0 = val as u64;
25    }
26
27    /// Gets the low 16-bits.
28    pub const fn get_x16(self) -> u16 {
29        self.0 as u16
30    }
31    /// Sets the low 16-bits (without modifying any other bits).
32    pub fn set_x16(&mut self, val: u16) {
33        self.0 = (self.0 & 0xffffffffffff0000) | (val as u64);
34    }
35
36    /// Gets the low 8-bits.
37    pub const fn get_x8(self) -> u8 {
38        self.0 as u8
39    }
40    /// Sets the low 8-bits (without modifying any other bits).
41    pub fn set_x8(&mut self, val: u8) {
42        self.0 = (self.0 & 0xffffffffffffff00) | (val as u64);
43    }
44
45    /// Gets bits 8-15.
46    pub const fn get_x8h(self) -> u8 {
47        (self.0 >> 8) as u8
48    }
49    /// Sets bits 8-15 (without modifying any other bits).
50    pub fn set_x8h(&mut self, val: u8) {
51        self.0 = (self.0 & 0xffffffffffff00ff) | ((val as u64) << 8);
52    }
53
54    /// Gets the value with the given size, zero extended to 64-bit.
55    /// Panics if sizecode is invalid.
56    pub(super) fn get_raw(self, sizecode: u8) -> u64 {
57        match sizecode {
58            0 => self.get_x8() as u64,
59            1 => self.get_x16() as u64,
60            2 => self.get_x32() as u64,
61            3 => self.get_x64(),
62            _ => panic!(),
63        }
64    }
65    /// Writes the value with the given size, truncating it if too large.
66    /// Panics if sizecode is invalid.
67    pub(super) fn set_raw(&mut self, sizecode: u8, value: u64) {
68        match sizecode {
69            0 => self.set_x8(value as u8),
70            1 => self.set_x16(value as u16),
71            2 => self.set_x32(value as u32),
72            3 => self.set_x64(value),
73            _ => panic!(),
74        }
75    }
76}
77
78#[test]
79fn test_cpu_register() {
80    let mut r = CPURegister::default();
81    assert_eq!(r.get_x64(), 0);
82
83    r.set_x64(0x2049381758392734);
84    assert_eq!(r.get_x64(), 0x2049381758392734);
85    assert_eq!(r.get_x32(), 0x58392734);
86    assert_eq!(r.get_x16(), 0x2734);
87    assert_eq!(r.get_x8(), 0x34);
88    assert_eq!(r.get_x8h(), 0x27);
89
90    r.set_x16(0x8692);
91    assert_eq!(r.get_x64(), 0x2049381758398692);
92    assert_eq!(r.get_x32(), 0x58398692);
93    assert_eq!(r.get_x16(), 0x8692);
94    assert_eq!(r.get_x8(), 0x92);
95    assert_eq!(r.get_x8h(), 0x86);
96
97    r.set_x8(0xf5);
98    assert_eq!(r.get_x64(), 0x20493817583986f5);
99    assert_eq!(r.get_x32(), 0x583986f5);
100    assert_eq!(r.get_x16(), 0x86f5);
101    assert_eq!(r.get_x8(), 0xf5);
102    assert_eq!(r.get_x8h(), 0x86);
103
104    r.set_x8h(0x12);
105    assert_eq!(r.get_x64(), 0x20493817583912f5);
106    assert_eq!(r.get_x32(), 0x583912f5);
107    assert_eq!(r.get_x16(), 0x12f5);
108    assert_eq!(r.get_x8(), 0xf5);
109    assert_eq!(r.get_x8h(), 0x12);
110
111    r.set_x32(0x59288643);
112    assert_eq!(r.get_x64(), 0x59288643);
113    assert_eq!(r.get_x32(), 0x59288643);
114    assert_eq!(r.get_x16(), 0x8643);
115    assert_eq!(r.get_x8(), 0x43);
116    assert_eq!(r.get_x8h(), 0x86);
117}
118
119/// Represents a 512-bit ZMM (vector) register.
120/// 
121/// Values are held as an array of 64 bytes, which is suitably aligned for use in any simd operations.
122/// Multi-byte values must be stored little-endian to facilitate the correct index behavior.
123/// The provided element accessors automatically do this, but care should be taken if accessing the data directly (e.g. for simd operations).
124#[derive(Clone, Copy)]
125#[repr(align(64))]
126pub struct ZMMRegister(pub [u8; 64]);
127impl Default for ZMMRegister {
128    fn default() -> Self {
129        Self([0; 64]) // arrays this large don't impl Default on their own - new releases of rust might fix this
130    }
131}
132macro_rules! zmm_impl {
133    ($get:ident : $set:ident => $t:ident) => {
134        pub fn $get(&self, index: usize) -> $t {
135            $t::from_le(bytemuck::cast_slice(&self.0)[index])
136        }
137        pub fn $set(&mut self, index: usize, value: $t) {
138            bytemuck::cast_slice_mut(&mut self.0)[index] = value.to_le()
139        }
140    };
141    (signed $get:ident : $set:ident => $i:ident : $u:ident : $get_raw:ident : $set_raw:ident) => {
142        pub fn $get(&self, index: usize) -> $i {
143            self.$get_raw(index) as $i
144        }
145        pub fn $set(&mut self, index: usize, value: $i) {
146            self.$set_raw(index, value as $u)
147        }
148    };
149    (float $get:ident : $set:ident => $t:ident : $get_raw:ident : $set_raw:ident) => {
150        pub fn $get(&self, index: usize) -> $t {
151            $t::from_bits(self.$get_raw(index))
152        }
153        pub fn $set(&mut self, index: usize, value: $t) {
154            self.$set_raw(index, value.to_bits())
155        }
156    };
157}
158impl ZMMRegister {
159    pub fn get_u8(&self, index: usize) -> u8 {
160        self.0[index]
161    }
162    pub fn set_u8(&mut self, index: usize, value: u8) {
163        self.0[index] = value
164    }
165
166    zmm_impl! { get_u16 : set_u16 => u16 }
167    zmm_impl! { get_u32 : set_u32 => u32 }
168    zmm_impl! { get_u64 : set_u64 => u64 }
169
170    zmm_impl! { signed get_i64 : set_i64 => i64 : u64 : get_u64 : set_u64 }
171    zmm_impl! { signed get_i32 : set_i32 => i32 : u32 : get_u32 : set_u32 }
172    zmm_impl! { signed get_i16 : set_i16 => i16 : u16 : get_u16 : set_u16 }
173    zmm_impl! { signed get_i8 : set_i8 => i8 : u8 : get_u8 : set_u8 }
174
175    zmm_impl! { float get_f64 : set_f64 => f64 : get_u64 : set_u64 }
176    zmm_impl! { float get_f32 : set_f32 => f32 : get_u32 : set_u32 }
177
178    pub(crate) fn get(&self, index: usize, size: u8) -> u64 {
179        match size {
180            0 => self.get_u8(index) as u64,
181            1 => self.get_u16(index) as u64,
182            2 => self.get_u32(index) as u64,
183            3 => self.get_u64(index),
184            _ => panic!(),
185        }
186    }
187    pub(crate) fn set(&mut self, index: usize, size: u8, value: u64) {
188        match size {
189            0 => self.set_u8(index, value as u8),
190            1 => self.set_u16(index, value as u16),
191            2 => self.set_u32(index, value as u32),
192            3 => self.set_u64(index, value),
193            _ => panic!(),
194        }
195    }
196}
197
198#[test]
199fn test_zmm_register() {
200    assert_eq!(std::mem::align_of::<ZMMRegister>(), 64);
201    assert_eq!(std::mem::size_of::<ZMMRegister>(), 64);
202    
203    let mut r = ZMMRegister::default();
204
205    for i in 0..64 {
206        r.set_u8(i, i as u8);
207    }
208    for i in 0..32 {
209        assert_eq!(r.get_u16(i), u16::from_le_bytes([i as u8 * 2, i as u8 * 2 + 1]));
210    }
211}
212
213macro_rules! impl_flag {
214    ($mask_name:ident, $set:ident, $clear:ident, $flip:ident, $get:ident, $assign:ident => $from:ty [ $mask:literal ]) => {
215        pub const $mask_name: $from = $mask;
216        pub fn $set(&mut self) { self.0 |= $mask }
217        pub fn $clear(&mut self) { self.0 &= !$mask }
218        pub fn $flip(&mut self) { self.0 ^= $mask }
219        pub const fn $get(self) -> bool { (self.0 & $mask) != 0 }
220        pub fn $assign(&mut self, value: bool) {
221            if value { self.$set() } else { self.$clear() }
222        }
223    }
224}
225macro_rules! impl_field {
226    ($mask_name:ident, $get:ident, $assign:ident => $from:ty [ $shift:literal => $mask:literal ] $to:ty) => {
227        pub const $mask_name: $from = $mask << $shift;
228        pub const fn $get(self) -> $to {
229            ((self.0 >> $shift) & $mask) as $to
230        }
231        pub fn $assign(&mut self, val: $to) {
232            assert_eq!(val, val & $mask);
233            self.0 = (self.0 & !($mask << $shift)) | ((val as $from) << $shift);
234        }
235    }
236}
237
238/// The CPU flags register.
239#[derive(Default, Clone, Copy)]
240pub struct Flags(pub u64);
241impl Flags {
242    impl_flag! { MASK_CF, set_cf, clear_cf, flip_cf, get_cf, assign_cf       => u64 [0x0000000000000001] }
243    impl_flag! { MASK_PF, set_pf, clear_pf, flip_pf, get_pf, assign_pf       => u64 [0x0000000000000004] }
244    impl_flag! { MASK_AF, set_af, clear_af, flip_af, get_af, assign_af       => u64 [0x0000000000000010] }
245    impl_flag! { MASK_ZF, set_zf, clear_zf, flip_zf, get_zf, assign_zf       => u64 [0x0000000000000040] }
246    impl_flag! { MASK_SF, set_sf, clear_sf, flip_sf, get_sf, assign_sf       => u64 [0x0000000000000080] }
247    impl_flag! { MASK_TF, set_tf, clear_tf, flip_tf, get_tf, assign_tf       => u64 [0x0000000000000100] }
248    impl_flag! { MASK_IF, set_if, clear_if, flip_if, get_if, assign_if       => u64 [0x0000000000000200] }
249    impl_flag! { MASK_DF, set_df, clear_df, flip_df, get_df, assign_df       => u64 [0x0000000000000400] }
250    impl_flag! { MASK_OF, set_of, clear_of, flip_of, get_of, assign_of       => u64 [0x0000000000000800] }
251    impl_flag! { MASK_NT, set_nt, clear_nt, flip_nt, get_nt, assign_nt       => u64 [0x0000000000004000] }
252    impl_flag! { MASK_RF, set_rf, clear_rf, flip_rf, get_rf, assign_rf       => u64 [0x0000000000010000] }
253    impl_flag! { MASK_VM, set_vm, clear_vm, flip_vm, get_vm, assign_vm       => u64 [0x0000000000020000] }
254    impl_flag! { MASK_AC, set_ac, clear_ac, flip_ac, get_ac, assign_ac       => u64 [0x0000000000040000] }
255    impl_flag! { MASK_VIF, set_vif, clear_vif, flip_vif, get_vif, assign_vif => u64 [0x0000000000080000] }
256    impl_flag! { MASK_VIP, set_vip, clear_vip, flip_vip, get_vip, assign_vip => u64 [0x0000000000100000] }
257    impl_flag! { MASK_ID, set_id, clear_id, flip_id, get_id, assign_id       => u64 [0x0000000000200000] }
258
259    impl_field! { MASK_IOPL, get_iopl, assign_iopl => u64 [ 12 => 0b11 ] u8 }
260
261    // -------------------------------------------------------------------------------------
262
263    impl_flag! { MASK_OTS, set_ots, clear_ots, flip_ots, get_ots, assign_ots => u64 [0x0000000100000000] }
264
265    // -------------------------------------------------------------------------------------
266
267    /// Checks the "below" condition.
268    pub const fn condition_b(self) -> bool { self.get_cf() }
269    /// Checks the "below or equal" condition.
270    pub const fn condition_be(self) -> bool { self.get_cf() || self.get_zf() }
271    /// Checks the "above" condition.
272    pub const fn condition_a(self) -> bool { !self.condition_be() }
273    /// Checks the "above or equal" condition.
274    pub const fn condition_ae(self) -> bool { !self.condition_b() }
275
276    // -------------------------------------------------------------------------------------
277
278    /// Checks the "less than" condition.
279    pub const fn condition_l(self) -> bool { self.get_sf() != self.get_of() }
280    /// Checks the "less or equal" condition.
281    pub const fn condition_le(self) -> bool { self.get_zf() || (self.get_sf() != self.get_of()) }
282    /// Checks the "greater than" condition.
283    pub const fn condition_g(self) -> bool { !self.condition_le() }
284    /// Checks the "greater or equal" condition.
285    pub const fn condition_ge(self) -> bool { !self.condition_l() }
286}
287
288/// The MXCSR register.
289#[derive(Default, Clone, Copy)]
290pub struct MXCSR(pub u16);
291impl MXCSR {
292
293}
294
295/// The FPU control word.
296#[derive(Clone, Copy)]
297pub struct Control(pub u16);
298impl Control {
299    impl_flag! { MASK_IM, set_im, clear_im, flip_im, get_im, assign_im       => u16 [0x0001] }
300    impl_flag! { MASK_DM, set_dm, clear_dm, flip_dm, get_dm, assign_dm       => u16 [0x0002] }
301    impl_flag! { MASK_ZM, set_zm, clear_zm, flip_zm, get_zm, assign_zm       => u16 [0x0004] }
302    impl_flag! { MASK_OM, set_om, clear_om, flip_om, get_om, assign_om       => u16 [0x0008] }
303    impl_flag! { MASK_UM, set_um, clear_um, flip_um, get_um, assign_um       => u16 [0x0010] }
304    impl_flag! { MASK_PM, set_pm, clear_pm, flip_pm, get_pm, assign_pm       => u16 [0x0020] }
305    impl_flag! { MASK_IEM, set_iem, clear_iem, flip_iem, get_iem, assign_iem => u16 [0x0080] }
306    impl_flag! { MASK_IC, set_ic, clear_ic, flip_ic, get_ic, assign_ic       => u16 [0x1000] }
307
308    impl_field! { MASK_PC, get_pc, assign_pc => u16 [ 8 => 0b11 ] u8 }
309    impl_field! { MASK_RC, get_rc, assign_rc => u16 [ 10 => 0b11 ] u8 }
310
311    /// Gets the rounding control field and interprets it as a rounding mode enum for program use.
312    pub fn get_rc_enum(self) -> Round {
313        match self.get_rc() {
314            0 => Round::Nearest,
315            1 => Round::Down,
316            2 => Round::Up,
317            3 => Round::Zero,
318            _ => unreachable!(),
319        }
320    }
321    /// Gets the precision control field value in terms of the number of bits.
322    /// Fails only for `0b01`, which is reserved.
323    pub fn get_pc_val(self) -> Result<u32, ()> {
324        match self.get_pc() {
325            0 => Ok(24),
326            1 => Err(()),
327            2 => Ok(53),
328            3 => Ok(64),
329            _ => unreachable!(),
330        }
331    }
332}
333
334/// The FPU status word.
335#[derive(Clone, Copy)]
336pub struct Status(pub u16);
337impl Status {
338    impl_flag! { MASK_I, set_i, clear_i, flip_i, get_i, assign_i       => u16 [0x0001] }
339    impl_flag! { MASK_D, set_d, clear_d, flip_d, get_d, assign_d       => u16 [0x0002] }
340    impl_flag! { MASK_Z, set_z, clear_z, flip_z, get_z, assign_z       => u16 [0x0004] }
341    impl_flag! { MASK_O, set_o, clear_o, flip_o, get_o, assign_o       => u16 [0x0008] }
342    impl_flag! { MASK_U, set_u, clear_u, flip_u, get_u, assign_u       => u16 [0x0010] }
343    impl_flag! { MASK_P, set_p, clear_p, flip_p, get_p, assign_p       => u16 [0x0020] }
344    impl_flag! { MASK_SF, set_sf, clear_sf, flip_sf, get_sf, assign_sf => u16 [0x0040] }
345    impl_flag! { MASK_IR, set_ir, clear_ir, flip_ir, get_ir, assign_ir => u16 [0x0080] }
346    impl_flag! { MASK_C0, set_c0, clear_c0, flip_c0, get_c0, assign_c0 => u16 [0x0100] }
347    impl_flag! { MASK_C1, set_c1, clear_c1, flip_c1, get_c1, assign_c1 => u16 [0x0200] }
348    impl_flag! { MASK_C2, set_c2, clear_c2, flip_c2, get_c2, assign_c2 => u16 [0x0400] }
349    impl_flag! { MASK_C3, set_c3, clear_c3, flip_c3, get_c3, assign_c3 => u16 [0x4000] }
350    impl_flag! { MASK_B, set_b, clear_b, flip_b, get_b, assign_b       => u16 [0x8000] }
351
352    impl_field! { MASK_TOP, get_top, assign_top => u16 [ 11 => 0b111 ] u8 }
353}
354
355/// The FPU tag word.
356#[derive(Clone, Copy)]
357pub struct Tag(pub u16);
358impl Tag {
359    pub fn get_physical(self, index: u8) -> u8 {
360        ((self.0 >> (2 * index)) & 3) as u8
361    }
362    pub fn set_physical(&mut self, index: u8, value: TagValue) {
363        assert!(index < 8);
364        let s = 2 * index;
365        self.0 = (self.0 & !(3 << s)) | ((value as u16) << s);
366    }
367}
368
369/// The valid values for an fpu register tag.
370#[repr(u8)]
371pub enum TagValue {
372    NonZero = 0,
373    Zero = 1,
374    Special = 2,
375    Empty = 3,
376}