x86emu/
registers.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Structs to hold register state for the x86 instruction emulator.
5
6use x86defs::SegmentRegister;
7
8#[repr(usize)]
9#[derive(Debug, Copy, Clone, PartialEq)]
10#[expect(clippy::upper_case_acronyms)]
11pub enum Gp {
12    RAX = 0,
13    RCX = 1,
14    RDX = 2,
15    RBX = 3,
16    RSP = 4,
17    RBP = 5,
18    RSI = 6,
19    RDI = 7,
20    R8 = 8,
21    R9 = 9,
22    R10 = 10,
23    R11 = 11,
24    R12 = 12,
25    R13 = 13,
26    R14 = 14,
27    R15 = 15,
28}
29
30#[derive(Debug, Copy, Clone)]
31#[expect(clippy::upper_case_acronyms)]
32pub enum GpSize {
33    /// 8-bit registers have a shift value, depending on if we're capturing the high/low bits
34    BYTE(usize),
35    WORD,
36    DWORD,
37    QWORD,
38}
39
40#[repr(usize)]
41#[derive(Debug, Copy, Clone)]
42pub enum Segment {
43    ES = 0,
44    CS = 1,
45    SS = 2,
46    DS = 3,
47    FS = 4,
48    GS = 5,
49}
50
51#[derive(Debug, Copy, Clone)]
52pub struct RegisterIndex {
53    /// Index of the full register size. E.g. this would be the index of RAX for the register EAX.
54    pub extended_index: Gp,
55    /// The size of the register, including a shift for 8-bit registers
56    pub size: GpSize,
57}
58
59impl RegisterIndex {
60    /// Converts the internal emulator representation of a register into a u64
61    /// e.g. for AL, this consumes the value of RAX and outputs the low 8 bits
62    pub fn apply_sizing(&self, v: u64) -> u64 {
63        match self.size {
64            GpSize::BYTE(shift) => ((v >> shift) as u8).into(),
65            GpSize::WORD => (v as u16).into(),
66            GpSize::DWORD => (v as u32).into(),
67            GpSize::QWORD => v,
68        }
69    }
70
71    pub fn apply_sizing_signed(&self, v: u64) -> i64 {
72        match self.size {
73            GpSize::BYTE(shift) => ((v >> shift) as i8).into(),
74            GpSize::WORD => (v as i16).into(),
75            GpSize::DWORD => (v as i32).into(),
76            GpSize::QWORD => v as i64,
77        }
78    }
79
80    pub fn apply_update(&self, extended_register: u64, v: u64) -> u64 {
81        match self.size {
82            GpSize::BYTE(shift) => {
83                let mask = !(0xff << shift);
84                (extended_register & mask) | (((v as u8) as u64) << shift)
85            }
86            GpSize::WORD => (extended_register & !0xffff) | (v as u16) as u64,
87            // N.B. setting a 32-bit register zero extends the result to the 64-bit
88            //      register. This is different from 16-bit and 8-bit registers.
89            GpSize::DWORD => (v as u32) as u64,
90            GpSize::QWORD => v,
91        }
92    }
93}
94
95impl From<Gp> for RegisterIndex {
96    fn from(val: Gp) -> Self {
97        RegisterIndex {
98            extended_index: val,
99            size: GpSize::QWORD,
100        }
101    }
102}
103
104pub(crate) fn bitness(cr0: u64, efer: u64, cs: SegmentRegister) -> Bitness {
105    if cr0 & x86defs::X64_CR0_PE != 0 {
106        if efer & x86defs::X64_EFER_LMA != 0 {
107            if cs.attributes.long() {
108                Bitness::Bit64
109            } else {
110                Bitness::Bit32
111            }
112        } else {
113            if cs.attributes.default() {
114                Bitness::Bit32
115            } else {
116                Bitness::Bit16
117            }
118        }
119    } else {
120        Bitness::Bit16
121    }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq)]
125pub(crate) enum Bitness {
126    Bit64,
127    Bit32,
128    Bit16,
129}
130
131impl From<Bitness> for u32 {
132    fn from(bitness: Bitness) -> u32 {
133        match bitness {
134            Bitness::Bit64 => 64,
135            Bitness::Bit32 => 32,
136            Bitness::Bit16 => 16,
137        }
138    }
139}