Skip to main content

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