dustbox/cpu/
instruction.rs

1use std::fmt;
2
3use crate::cpu::Segment;
4use crate::cpu::Op;
5use crate::cpu::{Parameter, ParameterSet};
6use crate::cpu::{OperandSize, AddressSize};
7use crate::hex::hex_bytes;
8use crate::string::right_pad;
9
10#[derive(Clone, Debug, PartialEq)]
11pub struct Instruction {
12    pub command: Op,
13    pub params: ParameterSet,
14    pub length: u8,
15    // op prefixes
16    pub segment_prefix: Segment,    // segment prefix opcode
17    pub repeat: RepeatMode,         // REPcc prefix
18    pub lock: bool,                 // LOCK prefix
19    pub op_size: OperandSize,       // 0x66 prefix
20    pub address_size: AddressSize,  // 0x67 prefix
21}
22
23impl fmt::Display for Instruction {
24    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25        let instr = self.describe_instruction();
26        if self.segment_prefix == Segment::Default || self.hide_segment_prefix() {
27            write!(f, "{}", instr)
28        } else {
29            write!(f, "{} {}", self.segment_prefix.as_str(), instr)
30        }
31    }
32}
33
34impl Instruction {
35    pub fn new(op: Op) -> Self {
36        Instruction::new3(op, Parameter::None, Parameter::None, Parameter::None)
37    }
38
39    pub fn new1(op: Op, dst: Parameter) -> Self {
40        Instruction::new3(op, dst, Parameter::None, Parameter::None)
41    }
42
43    pub fn new2(op: Op, dst: Parameter, src: Parameter) -> Self {
44        Instruction::new3(op, dst, src, Parameter::None)
45    }
46
47    pub fn new3(op: Op, dst: Parameter, src: Parameter, src2: Parameter) -> Self {
48        let op_size = Instruction::op_size_from_op(&op);
49        Instruction {
50            command: op,
51            segment_prefix: Segment::Default,
52            params: ParameterSet {dst, src, src2},
53            lock: false,
54            repeat: RepeatMode::None,
55            op_size,
56            address_size: AddressSize::_16bit,
57            length: 0,
58        }
59    }
60
61    // used to decorate tracer
62    pub fn is_ret(&self) -> bool {
63        self.command == Op::Retn || self.command == Op::Retf || self.command == Op::RetImm16
64    }
65
66    // used to decorate tracer
67    pub fn is_loop(&self) -> bool {
68        self.command == Op::Loop || self.command == Op::Loope || self.command == Op::Loopne
69    }
70
71    // used to decorate tracer
72    pub fn is_unconditional_jmp(&self) -> bool {
73        self.command == Op::JmpShort || self.command == Op::JmpNear || self.command == Op::JmpFar
74    }
75
76    fn op_size_from_op(op: &Op) -> OperandSize {
77        match *op {
78            Op::Mov32 | Op::Inc32 | Op::Dec32 => OperandSize::_32bit,
79            _ => OperandSize::_16bit,
80        }
81    }
82
83    fn hide_segment_prefix(&self) -> bool {
84        self.command == Op::Add8 || self.command == Op::Add16 || self.command == Op::Add32 ||
85        self.command == Op::Adc8 || self.command == Op::Adc16 || self.command == Op::Adc32 ||
86        self.command == Op::Sub8 || self.command == Op::Sub16 || self.command == Op::Sub32 ||
87        self.command == Op::Sbb8 || self.command == Op::Sbb16 || self.command == Op::Sbb32 ||
88        self.command == Op::Inc8 || self.command == Op::Inc16 || self.command == Op::Inc32 ||
89        self.command == Op::Dec8 || self.command == Op::Dec16 || self.command == Op::Dec32 ||
90        self.command == Op::Mul8 || self.command == Op::Mul16 || self.command == Op::Mul32 ||
91        self.command == Op::Div8 || self.command == Op::Div16 || self.command == Op::Div32 ||
92        self.command == Op::Imul8 || self.command == Op::Imul16 || self.command == Op::Imul32 ||
93        self.command == Op::Idiv8 || self.command == Op::Idiv16 || self.command == Op::Idiv32 ||
94        self.command == Op::And8 || self.command == Op::And16 || self.command == Op::And32 ||
95        self.command == Op::Or8 || self.command == Op::Or16 || self.command == Op::Or32 ||
96        self.command == Op::Xor8 || self.command == Op::Xor16 || self.command == Op::Xor32 ||
97        self.command == Op::Cmp8 || self.command == Op::Cmp16 || self.command == Op::Cmp32 ||
98        self.command == Op::Test8 || self.command == Op::Test16 || self.command == Op::Test32 ||
99        self.command == Op::Xchg8 || self.command == Op::Xchg16 || self.command == Op::Xchg32 ||
100        self.command == Op::Mov8 || self.command == Op::Mov16 || self.command == Op::Mov32 ||
101        self.command == Op::Movsx16 || self.command == Op::Movsx32 || self.command == Op::Movzx16
102    }
103
104    fn describe_instruction(&self) -> String {
105        let op_space = 9;
106        let mut prefix = self.repeat.as_str().to_owned();
107        if prefix != "" {
108            prefix = right_pad(&prefix, op_space);
109        }
110
111        match self.params.dst {
112            Parameter::None => format!("{}{}", prefix, self.command),
113            _ => {
114                let cmd = right_pad(&format!("{}{}", prefix, self.command), op_space);
115
116                match self.params.src2 {
117                    Parameter::None => match self.params.src {
118                        Parameter::None => format!("{}{}", cmd, self.params.dst),
119                        _ => format!("{}{}, {}", cmd, self.params.dst, self.params.src),
120                    },
121                    _ => format!(
122                        "{}{}, {}, {}",
123                        cmd,
124                        self.params.dst,
125                        self.params.src,
126                        self.params.src2
127                    ),
128                }
129            }
130        }
131    }
132}
133
134#[derive(Debug, PartialEq)]
135pub struct InstructionInfo {
136    pub segment: usize,
137    pub offset: usize,
138    pub bytes: Vec<u8>,
139    pub instruction: Instruction,
140}
141
142impl fmt::Display for InstructionInfo {
143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        write!(
145            f,
146            "[{:04X}:{:04X}] {} {}",
147            self.segment,
148            self.offset,
149            right_pad(&hex_bytes(&self.bytes), 16),
150            format!("{}", self.instruction),
151        )
152    }
153}
154
155#[derive(Copy, Clone, Debug, PartialEq)]
156pub enum RepeatMode {
157    None,
158    Rep,
159    Repe, // alias repz
160    Repne, // alias repnz
161}
162
163impl RepeatMode {
164    fn as_str(&self) -> &str {
165        match *self {
166            RepeatMode::None => "",
167            RepeatMode::Rep => "Rep",
168            RepeatMode::Repe => "Repe",
169            RepeatMode::Repne => "Repne",
170        }
171    }
172}
173
174#[derive(Debug)]
175pub struct ModRegRm {
176    pub md: u8,  /// "mod" is correct name, but is reserved keyword
177    pub reg: u8,
178    pub rm: u8,
179}
180
181impl ModRegRm {
182    pub fn u8(&self) -> u8 {
183        (self.md << 6) |  // high 2 bits
184        (self.reg << 3) | // mid 3 bits
185        self.rm           // low 3 bits
186    }
187
188    pub fn rm_reg(rm: u8, reg: u8) -> u8 {
189        // md 3 = register adressing
190        // XXX ModRegRm.rm really should use enum AMode, not like AMode is now. naming there is wrong
191        ModRegRm{md: 3, rm, reg}.u8()
192    }
193}