From 8f857a2fcbdfc7315cd09c0a4ac372b3ec92b538 Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 9 Aug 2020 20:43:42 -0700 Subject: adjust public interface: public items should all be stable `OperandCode` (obviously) wildly varies depending on how i feel on a given week, so it's now hidden to avoid people depending on numerical values of its discriminants. `RegisterBank` got a similar treatment with a new `RegisterClass` struct that's suitable for public use. --- CHANGELOG | 11 ++ README.md | 15 ++- src/long_mode/mod.rs | 230 +++++++++++++++++++++++++++++------------ src/protected_mode/mod.rs | 193 ++++++++++++++++++++++++---------- test/long_mode/mod.rs | 2 +- test/long_mode/regspec.rs | 19 ++++ test/protected_mode/mod.rs | 2 +- test/protected_mode/regspec.rs | 15 +++ 8 files changed, 359 insertions(+), 128 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 34f9c64..c846cdb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,14 @@ +## 0.1.0 +* port `long_mode` improvements to `protected_mode` decoder + - real mode will wait until another day +* support undocumented instruction `salc` +* fix segment registers being numbered wrong + - this is relevant only for mov to/from segments +* support x86_32 `push ``/`pop ` +* support x86_32 `pusha`/`popa` +* support x86_32 BCD instructions + - for `aam`/`aad`, the undocumented `amx` and `adx` forms are used in all cases, including when the base is 10 + ## 0.0.15 * the `x87` instruction set is now fully supported diff --git a/README.md b/README.md index b9e24da..0e15050 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ x86 decoders implemented as part of the yaxpeax project. * `#[no_std]` * configurable choice of permitted instruction set extensions -* seems to be pretty fast +* very fast * pretty small? ### `#[no_std]` @@ -18,7 +18,9 @@ the decoders provided by `yaxpeax-x86` are designed to be usable in a `no_std` s `yaxpeax-x86` decoders provide the option to specify what [instruction set extensions](http://git.iximeow.net/yaxpeax-x86/tree/src/long_mode/mod.rs#n1297) are eligible when decoding, to support decoding x86 instructions as understood by a particular microarchitecture. the default impls of decoders in `yaxpeax_x86` take an optimistsic approach to decoding and assumes all feature sets are available, as well as accepting both intel-specific and amd-specific quirks around undefined encodings. ### pretty fast -by the in-repo benchmark, `yaxpeax_x86::long_mode` decodes `x86_64` instructions at anywhere between 60 million instructions per second to just shy of 100 million instructions per second, depending on hardware and distribution of instructions being decoded. when hooked up to `disasm-bench`, `yaxpeax_x86::long_mode` has shown roughly 150mb/s decode throughput. +by the in-repo benchmark, `yaxpeax_x86::long_mode` decodes `x86_64` instructions at anywhere between 60 million instructions per second to just shy of 100 million instructions per second, depending on hardware and distribution of instructions being decoded. + +when hooked up to [`disas-bench`](https://github.com/iximeow/disas-bench#results), `yaxpeax_x86::long_mode` has shown roughly 175mb/s decode throughput and on most hardware is the fastest software x86 decoder available. ### pretty small? `yaxpeax_x86::long_mode` is expected to be around 20kb of code and data. currently a stripped static build of `ffi/` takes a bit more space - around 130kb. instruction rendering is currently non-optional, and is a significant amount of `.text` size. data tables are larger than anticipated, and it's currently an open question if they can be reduced down, or the size target of `yaxpeax_x86::long_mode` should be raised. @@ -32,20 +34,17 @@ the canonical copy of `yaxpeax-x86` is at [https://git.iximeow.net/yaxpeax-x86/] `yaxpeax-x86` is also mirrored on GitHub at [https://www.github.com/iximeow/yaxpeax-x86](https://www.github.com/iximeow/yaxpeax-x86). ### ! user beware ! -* while the decoder has the option to select instruction set extensions, it is entirely likely that some extensions are not yet properly rejected when the corresponding flag is disabled. user beware! -* `yaxpeax-x86` likely has many corners where it does not reject instructions it should. particularly likely is accepting a register operand where the corresponding instruction specifically only allows memory encodings - `lea` is a good example of this. user beware! * `yaxpeax-x86` will, but does not yet, have a decoder for real-mode `x86`. it is strongly recommended to use `::Instruction` and similar type aliases, rather than using struct and operand types directly. user beware! * avx512 is not yet supported. user beware! * avx256 support is questionable. user beware! -* x87 is not yet supported. user beware! -* ring-0 instructions have questionable support. user beware! -* the only unsafe code in `yaxpeax_x86` is instances of `unsafe { unreachable_unchecked(); }`. while these are, currently, all unreachable, this code should default to a fail-safe assertion fail, with `unreachable_unchecked` being an opt-in feature. user beware! +* `ffi/` exists, and is enough to get a bare minimum decoding and string representation of an instruction, but is not as fully populated as a non-rust caller might like. user beware! +* `yaxpeax_x86` makes regular use of `unsafe { unreachable_unchecked(); }` and occasional use of `unsafe { _.get_unchecked() }`. while these are, to the author's knowledge, unreachable, this is audited and updated on a best-effort basis. eventually, `yaxpeax-x86` should grow a fuzzing suite that build with these cases checked and panicking. ### changelog a changelog across crate versions is maintained in the `CHANGELOG` file located in the repo, as well as [online](https://git.iximeow.net/yaxpeax-x86/tree/CHANGELOG). ### see also -[`iced`](https://github.com/0xd4d/iced) looks to be another very good `x86_64` decoder, with an in-progress [translation to rust](https://github.com/0xd4d/iced/tree/rust) +[`iced`](https://github.com/0xd4d/iced) is another very good `x86_64` decoder, also written in rust. it provides additional information about instruction semantics as part of the crate, as well as the ability to re-encode instructions. [`disas-bench`](https://github.com/athre0z/disas-bench), a handy benchmark of several `x86_64` decoders, easily extended to compare with `yaxpeax-x86` as well. diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index 3d6dc2b..23213ec 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -9,14 +9,24 @@ use yaxpeax_arch::{AddressDiff, Decoder, LengthedInstruction}; #[cfg(feature="use-serde")] #[derive(Copy, Clone, Debug, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)] pub struct RegSpec { - pub num: u8, - pub bank: RegisterBank + num: u8, + bank: RegisterBank } #[cfg(not(feature="use-serde"))] #[derive(Copy, Clone, Debug, PartialOrd, Ord, Eq, PartialEq)] pub struct RegSpec { - pub num: u8, - pub bank: RegisterBank + num: u8, + bank: RegisterBank +} + +impl RegSpec { + pub fn num(&self) -> u8 { + self.num + } + + pub fn class(&self) -> RegisterClass { + RegisterClass { kind: self.bank } + } } use core::hash::Hash; @@ -257,53 +267,39 @@ impl RegSpec { } #[inline] + pub fn r8b() -> RegSpec { + RegSpec { bank: RegisterBank::rB, num: 8 } + } + + #[inline] + pub fn zmm0() -> RegSpec { + RegSpec { bank: RegisterBank::Z, num: 0 } + } + + #[inline] + pub fn ymm0() -> RegSpec { + RegSpec { bank: RegisterBank::Y, num: 0 } + } + + #[inline] + pub fn xmm0() -> RegSpec { + RegSpec { bank: RegisterBank::X, num: 0 } + } + + #[inline] + pub fn st0() -> RegSpec { + RegSpec { bank: RegisterBank::ST, num: 0 } + } + + #[inline] + pub fn mm0() -> RegSpec { + RegSpec { bank: RegisterBank::MM, num: 0 } + } + + /// return the size of this register, in bytes + #[inline] pub fn width(&self) -> u8 { - match self.bank { - RegisterBank::Q => 8, - RegisterBank::D => 4, - RegisterBank::W => 2, - RegisterBank::B | - RegisterBank::rB => { - 1 - }, - RegisterBank::CR | - RegisterBank::DR => { - 8 - }, - RegisterBank::S => { - 2 - }, - RegisterBank::EIP => { - 4 - } - RegisterBank::RIP => { - 8 - } - RegisterBank::EFlags => { - 4 - } - RegisterBank::RFlags => { - 8 - } - RegisterBank::X => { - 16 - } - RegisterBank::Y => { - 32 - } - RegisterBank::Z => { - 64 - } - RegisterBank::ST => { - 10 - } - RegisterBank::MM => { - 8 - } - RegisterBank::K => { - 8 - } - } + self.class().width() } } @@ -439,6 +435,7 @@ impl Operand { } } } + pub fn is_memory(&self) -> bool { match self { Operand::DisplacementU32(_) | @@ -508,20 +505,125 @@ fn operand_size() { // assert_eq!(core::mem::size_of::(), 40); } +#[cfg(feature="use-serde")] +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct RegisterClass { + kind: RegisterBank, +} + +#[cfg(not(feature="use-serde"))] +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct RegisterClass { + kind: RegisterBank, +} + +const REGISTER_CLASS_NAMES: &[&'static str] = &[ + "qword", + "BUG. PLEASE REPORT.", + "dword", + "BUG. PLEASE REPORT.", + "word", + "BUG. PLEASE REPORT.", + "byte", + "BUG. PLEASE REPORT.", + "rex-byte", + "BUG. PLEASE REPORT.", + "cr", + "BUG. PLEASE REPORT.", + "dr", + "BUG. PLEASE REPORT.", + "segment", + "xmm", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "ymm", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "zmm", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "x87-stack", + "mmx", + "k", + "eip", + "rip", + "eflags", + "rflags", +]; + +impl RegisterClass { + pub fn name(&self) -> &'static str { + REGISTER_CLASS_NAMES[self.kind as usize] + } + + pub fn width(&self) -> u8 { + match self.kind { + RegisterBank::Q => 8, + RegisterBank::D => 4, + RegisterBank::W => 2, + RegisterBank::B | + RegisterBank::rB => { + 1 + }, + RegisterBank::CR | + RegisterBank::DR => { + 8 + }, + RegisterBank::S => { + 2 + }, + RegisterBank::EIP => { + 4 + } + RegisterBank::RIP => { + 8 + } + RegisterBank::EFlags => { + 4 + } + RegisterBank::RFlags => { + 8 + } + RegisterBank::X => { + 16 + } + RegisterBank::Y => { + 32 + } + RegisterBank::Z => { + 64 + } + RegisterBank::ST => { + 10 + } + RegisterBank::MM => { + 8 + } + RegisterBank::K => { + 8 + } + } + } +} + #[allow(non_camel_case_types)] #[cfg(feature="use-serde")] #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum RegisterBank { +enum RegisterBank { Q = 0, D = 2, W = 4, B = 6, rB = 8, // Quadword, Dword, Word, Byte CR = 10, DR = 12, S = 14, EIP = 30, RIP = 31, EFlags = 32, RFlags = 33, // Control reg, Debug reg, Selector, ... X = 15, Y = 19, Z = 23, // XMM, YMM, ZMM ST = 27, MM = 28, // ST, MM regs (x87, mmx) K = 29, // AVX512 mask registers } + #[allow(non_camel_case_types)] #[cfg(not(feature="use-serde"))] #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub enum RegisterBank { +enum RegisterBank { Q = 0, D = 2, W = 4, B = 6, rB = 8, // Quadword, Dword, Word, Byte CR = 10, DR = 12, S = 14, EIP = 30, RIP = 31, EFlags = 32, RFlags = 33, // Control reg, Debug reg, Selector, ... X = 15, Y = 19, Z = 23, // XMM, YMM, ZMM @@ -1565,7 +1667,7 @@ pub struct Instruction { operands: [OperandSpec; 4], imm: u64, disp: u64, - pub opcode: Opcode, + opcode: Opcode, } impl yaxpeax_arch::Instruction for Instruction { @@ -3079,25 +3181,25 @@ impl Default for Instruction { } impl Instruction { + pub fn opcode(&self) -> Opcode { + self.opcode + } + pub fn operand(&self, i: u8) -> Operand { assert!(i < 4); Operand::from_spec(self, self.operands[i as usize]) } pub fn operand_count(&self) -> u8 { - let mut i = 0; - for op in self.operands.iter() { - if let OperandSpec::Nothing = op { - return i; - } else { - i += 1; - } - } - return i; + self.operand_count } pub fn operand_present(&self, i: u8) -> bool { assert!(i < 4); + if i >= self.operand_count { + return false; + } + if let OperandSpec::Nothing = self.operands[i as usize] { false } else { @@ -3245,7 +3347,7 @@ impl Prefixes { #[inline] pub fn lock(&self) -> bool { self.bits & 0x4 == 4 } #[inline] - fn cs(&mut self) { self.segment = Segment::CS } + pub fn cs(&mut self) { self.segment = Segment::CS } #[inline] fn set_cs(&mut self) { self.segment = Segment::CS } #[inline] @@ -3520,7 +3622,7 @@ impl OperandCodeBuilder { // ---------------------------> read modr/m? #[repr(u16)] #[derive(Copy, Clone, Debug, PartialEq)] -pub enum OperandCode { +enum OperandCode { Ivs = OperandCodeBuilder::new().special_case(25).bits(), I_3 = OperandCodeBuilder::new().special_case(27).bits(), Nothing = OperandCodeBuilder::new().special_case(28).bits(), diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 1517c7c..cb19a15 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -9,14 +9,24 @@ use yaxpeax_arch::{AddressDiff, Decoder, LengthedInstruction}; #[cfg(feature="use-serde")] #[derive(Copy, Clone, Debug, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)] pub struct RegSpec { - pub num: u8, - pub bank: RegisterBank + num: u8, + bank: RegisterBank } #[cfg(not(feature="use-serde"))] #[derive(Copy, Clone, Debug, PartialOrd, Ord, Eq, PartialEq)] pub struct RegSpec { - pub num: u8, - pub bank: RegisterBank + num: u8, + bank: RegisterBank +} + +impl RegSpec { + pub fn num(&self) -> u8 { + self.num + } + + pub fn class(&self) -> RegisterClass { + RegisterClass { kind: self.bank } + } } use core::hash::Hash; @@ -236,43 +246,33 @@ impl RegSpec { } #[inline] + pub fn zmm0() -> RegSpec { + RegSpec { bank: RegisterBank::Z, num: 0 } + } + + #[inline] + pub fn ymm0() -> RegSpec { + RegSpec { bank: RegisterBank::Y, num: 0 } + } + + #[inline] + pub fn xmm0() -> RegSpec { + RegSpec { bank: RegisterBank::X, num: 0 } + } + + #[inline] + pub fn st0() -> RegSpec { + RegSpec { bank: RegisterBank::ST, num: 0 } + } + + #[inline] + pub fn mm0() -> RegSpec { + RegSpec { bank: RegisterBank::MM, num: 0 } + } + + #[inline] pub fn width(&self) -> u8 { - match self.bank { - RegisterBank::D => 4, - RegisterBank::W => 2, - RegisterBank::B => 1, - RegisterBank::CR | - RegisterBank::DR => { - 4 - }, - RegisterBank::S => { - 2 - }, - RegisterBank::EIP => { - 4 - } - RegisterBank::EFlags => { - 4 - } - RegisterBank::X => { - 16 - } - RegisterBank::Y => { - 32 - } - RegisterBank::Z => { - 64 - } - RegisterBank::ST => { - 10 - } - RegisterBank::MM => { - 8 - } - RegisterBank::K => { - 8 - } - } + self.class().width() } } @@ -463,10 +463,95 @@ fn operand_size() { // assert_eq!(core::mem::size_of::(), 40); } +#[cfg(feature="use-serde")] +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct RegisterClass { + pub(self) kind: RegisterBank, +} + +#[cfg(not(feature="use-serde"))] +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct RegisterClass { + pub(self) kind: RegisterBank, +} + +const REGISTER_CLASS_NAMES: &[&'static str] = &[ + "dword", + "word", + "byte", + "cr", + "dr", + "segment", + "xmm", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "ymm", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "zmm", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "BUG. PLEASE REPORT.", + "x87-stack", + "mmx", + "k", + "eip", + "eflags", +]; + +impl RegisterClass { + /// return a human-friendly name for this register class + pub fn name(&self) -> &'static str { + REGISTER_CLASS_NAMES[self.kind as usize] + } + + /// return the size of this register class, in bytes + pub fn width(&self) -> u8 { + match self.kind { + RegisterBank::D => 4, + RegisterBank::W => 2, + RegisterBank::B => 1, + RegisterBank::CR | + RegisterBank::DR => { + 4 + }, + RegisterBank::S => { + 2 + }, + RegisterBank::EIP => { + 4 + } + RegisterBank::EFlags => { + 4 + } + RegisterBank::X => { + 16 + } + RegisterBank::Y => { + 32 + } + RegisterBank::Z => { + 64 + } + RegisterBank::ST => { + 10 + } + RegisterBank::MM => { + 8 + } + RegisterBank::K => { + 8 + } + } + } +} + #[allow(non_camel_case_types)] #[cfg(feature="use-serde")] #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum RegisterBank { +enum RegisterBank { D = 0, W = 1, B = 2, // Dword, Word, Byte CR = 3, DR = 4, S = 5, EIP = 21, EFlags = 22, // Control reg, Debug reg, Selector, ... X = 6, Y = 10, Z = 14, // XMM, YMM, ZMM @@ -476,7 +561,7 @@ pub enum RegisterBank { #[allow(non_camel_case_types)] #[cfg(not(feature="use-serde"))] #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub enum RegisterBank { +enum RegisterBank { D = 0, W = 1, B = 2, // Dword, Word, Byte CR = 3, DR = 4, S = 5, EIP = 21, EFlags = 22, // Control reg, Debug reg, Selector, ... X = 6, Y = 10, Z = 14, // XMM, YMM, ZMM @@ -1532,7 +1617,7 @@ pub struct Instruction { operands: [OperandSpec; 4], imm: u32, disp: u32, - pub opcode: Opcode, + opcode: Opcode, } impl yaxpeax_arch::Instruction for Instruction { @@ -3044,25 +3129,25 @@ impl Default for Instruction { } impl Instruction { + pub fn opcode(&self) -> Opcode { + self.opcode + } + pub fn operand(&self, i: u8) -> Operand { assert!(i < 4); Operand::from_spec(self, self.operands[i as usize]) } pub fn operand_count(&self) -> u8 { - let mut i = 0; - for op in self.operands.iter() { - if let OperandSpec::Nothing = op { - return i; - } else { - i += 1; - } - } - return i; + self.operand_count } pub fn operand_present(&self, i: u8) -> bool { assert!(i < 4); + if i >= self.operand_count { + return false; + } + if let OperandSpec::Nothing = self.operands[i as usize] { false } else { @@ -3212,7 +3297,7 @@ impl Prefixes { #[inline] pub fn lock(&self) -> bool { self.bits & 0x4 == 4 } #[inline] - fn cs(&mut self) { self.segment = Segment::CS } + pub fn cs(&mut self) { self.segment = Segment::CS } #[inline] fn set_cs(&mut self) { self.segment = Segment::CS } #[inline] @@ -3463,7 +3548,7 @@ impl OperandCodeBuilder { // ---------------------------> read modr/m? #[repr(u16)] #[derive(Copy, Clone, Debug, PartialEq)] -pub enum OperandCode { +enum OperandCode { Ivs = OperandCodeBuilder::new().special_case(25).bits(), I_3 = OperandCodeBuilder::new().special_case(27).bits(), Nothing = OperandCodeBuilder::new().special_case(28).bits(), diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs index a3ff318..33fc8c7 100644 --- a/test/long_mode/mod.rs +++ b/test/long_mode/mod.rs @@ -12,7 +12,7 @@ fn test_invalid(data: &[u8]) { fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) { if let Ok(inst) = decoder.decode(data.into_iter().cloned()) { - assert_eq!(inst.opcode, Opcode::Invalid, "decoded {:?} from {:02x?} under decoder {}", inst.opcode, data, decoder); + assert_eq!(inst.opcode(), Opcode::Invalid, "decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder); } else { // this is fine } diff --git a/test/long_mode/regspec.rs b/test/long_mode/regspec.rs index 914c376..e126ac3 100644 --- a/test/long_mode/regspec.rs +++ b/test/long_mode/regspec.rs @@ -20,3 +20,22 @@ fn test_labels() { assert_eq!(RegSpec::gs().name(), "gs"); assert_eq!(RegSpec::al().name(), "al"); } + +#[test] +fn test_bank_names() { + assert_eq!(RegSpec::al().class().name(), "byte"); + assert_eq!(RegSpec::r8b().class().name(), "rex-byte"); + assert_eq!(RegSpec::ax().class().name(), "word"); + assert_eq!(RegSpec::eax().class().name(), "dword"); + assert_eq!(RegSpec::rax().class().name(), "qword"); + assert_eq!(RegSpec::fs().class().name(), "segment"); + assert_eq!(RegSpec::eflags().class().name(), "eflags"); + assert_eq!(RegSpec::rflags().class().name(), "rflags"); + assert_eq!(RegSpec::eip().class().name(), "eip"); + assert_eq!(RegSpec::rip().class().name(), "rip"); + assert_eq!(RegSpec::st0().class().name(), "x87-stack"); + assert_eq!(RegSpec::mm0().class().name(), "mmx"); + assert_eq!(RegSpec::xmm0().class().name(), "xmm"); + assert_eq!(RegSpec::ymm0().class().name(), "ymm"); + assert_eq!(RegSpec::zmm0().class().name(), "zmm"); +} diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs index bf9315c..c5c3c7b 100644 --- a/test/protected_mode/mod.rs +++ b/test/protected_mode/mod.rs @@ -12,7 +12,7 @@ fn test_invalid(data: &[u8]) { fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) { if let Ok(inst) = decoder.decode(data.into_iter().cloned()) { - assert_eq!(inst.opcode, Opcode::Invalid, "decoded {:?} from {:02x?} under decoder {}", inst.opcode, data, decoder); + assert_eq!(inst.opcode(), Opcode::Invalid, "decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder); } else { // this is fine } diff --git a/test/protected_mode/regspec.rs b/test/protected_mode/regspec.rs index bd996cc..eb8d018 100644 --- a/test/protected_mode/regspec.rs +++ b/test/protected_mode/regspec.rs @@ -18,3 +18,18 @@ fn test_labels() { assert_eq!(RegSpec::gs().name(), "gs"); assert_eq!(RegSpec::al().name(), "al"); } + +#[test] +fn test_bank_names() { + assert_eq!(RegSpec::al().class().name(), "byte"); + assert_eq!(RegSpec::ax().class().name(), "word"); + assert_eq!(RegSpec::eax().class().name(), "dword"); + assert_eq!(RegSpec::fs().class().name(), "segment"); + assert_eq!(RegSpec::eflags().class().name(), "eflags"); + assert_eq!(RegSpec::eip().class().name(), "eip"); + assert_eq!(RegSpec::st0().class().name(), "x87-stack"); + assert_eq!(RegSpec::mm0().class().name(), "mmx"); + assert_eq!(RegSpec::xmm0().class().name(), "xmm"); + assert_eq!(RegSpec::ymm0().class().name(), "ymm"); + assert_eq!(RegSpec::zmm0().class().name(), "zmm"); +} -- cgit v1.1