aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2023-02-27 20:32:32 +0100
committerJohannes Stoelp <johannes.stoelp@gmail.com>2023-02-27 20:32:32 +0100
commit7c080806361e23e2e2a528fb391d6bf9c15404f0 (patch)
tree2ac0d7e4bf0937a9f47bcc08ff9b8a79e8ff2833
parentf2f0a8eb4795342a985c49d66eeda73d059e6033 (diff)
downloadjuicebox-asm-7c080806361e23e2e2a528fb391d6bf9c15404f0.tar.gz
juicebox-asm-7c080806361e23e2e2a528fb391d6bf9c15404f0.zip
Add MOV tests
-rw-r--r--src/lib.rs43
-rw-r--r--src/reg.rs39
-rw-r--r--tests/mov.rs118
3 files changed, 181 insertions, 19 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 35f7919..23c9ca0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -24,7 +24,8 @@ impl MemOp {
}
/// Encode the `REX` byte.
-const fn rex(w: u8, r: u8, x: u8, b: u8) -> u8 {
+const fn rex(w: bool, r: u8, x: u8, b: u8) -> u8 {
+ let w = if w { 1 } else { 0 };
let r = (r >> 3) & 1;
let x = (x >> 3) & 1;
let b = (b >> 3) & 1;
@@ -227,7 +228,7 @@ trait EncodeMR<T: Reg> {
}
fn rex(op1: &MemOp, op2: T) -> Option<u8> {
- if op1.base().need_rex() || op2.need_rex() {
+ if op2.need_rex() || (op1.base().is_ext()) {
Some(rex(op2.rexw(), op2.idx(), 0, op1.base().idx()))
} else {
None
@@ -235,11 +236,19 @@ trait EncodeMR<T: Reg> {
}
}
+impl EncodeMR<Reg8> for Asm {}
+impl EncodeMR<Reg16> for Asm {
+ fn legacy_prefix() -> Option<u8> {
+ Some(0x66)
+ }
+}
impl EncodeMR<Reg32> for Asm {}
impl EncodeMR<Reg64> for Asm {}
// -- Instruction implementations.
+// -- MOV : reg reg
+
impl Mov<Reg64, Reg64> for Asm {
fn mov(&mut self, op1: Reg64, op2: Reg64) {
self.encode_rr(0x89, op1, op2);
@@ -264,6 +273,8 @@ impl Mov<Reg8, Reg8> for Asm {
}
}
+// -- MOV : mem reg
+
impl Mov<MemOp, Reg64> for Asm {
fn mov(&mut self, op1: MemOp, op2: Reg64) {
self.encode_mr(0x89, op1, op2);
@@ -276,6 +287,20 @@ impl Mov<MemOp, Reg32> for Asm {
}
}
+impl Mov<MemOp, Reg16> for Asm {
+ fn mov(&mut self, op1: MemOp, op2: Reg16) {
+ self.encode_mr(0x89, op1, op2);
+ }
+}
+
+impl Mov<MemOp, Reg8> for Asm {
+ fn mov(&mut self, op1: MemOp, op2: Reg8) {
+ self.encode_mr(0x88, op1, op2);
+ }
+}
+
+// -- MOV : reg mem
+
impl Mov<Reg64, MemOp> for Asm {
fn mov(&mut self, op1: Reg64, op2: MemOp) {
self.encode_rm(0x8b, op1, op2);
@@ -288,6 +313,20 @@ impl Mov<Reg32, MemOp> for Asm {
}
}
+impl Mov<Reg16, MemOp> for Asm {
+ fn mov(&mut self, op1: Reg16, op2: MemOp) {
+ self.encode_rm(0x8b, op1, op2);
+ }
+}
+
+impl Mov<Reg8, MemOp> for Asm {
+ fn mov(&mut self, op1: Reg8, op2: MemOp) {
+ self.encode_rm(0x8a, op1, op2);
+ }
+}
+
+// -- MOV : reg imm
+
impl Mov<Reg64, Imm64> for Asm {
fn mov(&mut self, op1: Reg64, op2: Imm64) {
self.encode_oi(0xb8, op1, op2);
diff --git a/src/reg.rs b/src/reg.rs
index 149d04d..b349878 100644
--- a/src/reg.rs
+++ b/src/reg.rs
@@ -3,12 +3,17 @@ pub(crate) trait Reg {
/// Get the raw x64 register code.
fn idx(&self) -> u8;
- /// Get the `REX.W` bit.
- fn rexw(&self) -> u8;
+ /// Check if the registers needs the `REX.W` bit.
+ fn rexw(&self) -> bool;
+
+ /// Check if the register is an extended registers.
+ fn is_ext(&self) -> bool {
+ self.idx() > 7
+ }
/// Check if the register requires a `REX` byte.
fn need_rex(&self) -> bool {
- self.idx() > 7 || self.rexw() > 0
+ self.is_ext() || self.rexw()
}
/// Check if the register requires a `SIB` byte if used as addressing operand.
@@ -58,20 +63,20 @@ macro_rules! impl_reg {
*self as u8
}
- /// Get the `REX.W` bit.
- fn rexw(&self) -> u8 {
+ /// Check if the registers needs the `REX.W` bit.
+ fn rexw(&self) -> bool {
$rexw
}
}
}
}
-impl_reg!(Reg64, 1, { rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15 });
-impl_reg!(Reg32, 0, { eax, ecx, edx, ebx, esp, ebp, esi, edi, r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d });
-impl_reg!(Reg16, 0, { ax, cx, dx, bx, sp, bp, si, di, r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w });
+impl_reg!(Reg64, true, { rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15 });
+impl_reg!(Reg32, false, { eax, ecx, edx, ebx, esp, ebp, esi, edi, r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d });
+impl_reg!(Reg16, false, { ax, cx, dx, bx, sp, bp, si, di, r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w });
impl_reg!(ENUM_ONLY,
- Reg8, { al, cl, dl, bl, spl, bpl, sil, dil, r8l, r9l, r10l, r11l, r12l, r13l, r14l, r15l,
- ah, ch, dh, bh });
+ Reg8, { al, cl, dl, bl, spl, bpl, sil, dil, r8l, r9l, r10l, r11l, r12l, r13l, r14l, r15l,
+ ah, ch, dh, bh });
impl Reg for Reg8 {
/// Get the raw x64 register code.
@@ -85,9 +90,9 @@ impl Reg for Reg8 {
}
}
- /// Get the `REX.W` bit.
- fn rexw(&self) -> u8 {
- 0
+ /// Check if the registers needs the `REX.W` bit.
+ fn rexw(&self) -> bool {
+ false
}
/// Check whether the gp register needs a `REX` prefix
@@ -139,7 +144,7 @@ mod tests {
assert_eq!(r.idx(), idx);
// Check REX.W bit.
- assert_eq!(r.rexw(), 0);
+ assert_eq!(r.rexw(), false);
// Check need REX byte.
let rex = match r {
@@ -191,7 +196,7 @@ mod tests {
assert_eq!(r.idx(), idx);
// Check REX.W bit.
- assert_eq!(r.rexw(), 0);
+ assert_eq!(r.rexw(), false);
// Check need REX byte.
let rex = match r {
@@ -243,7 +248,7 @@ mod tests {
assert_eq!(r.idx(), idx);
// Check REX.W bit.
- assert_eq!(r.rexw(), 0);
+ assert_eq!(r.rexw(), false);
// Check need REX byte.
let rex = match r {
@@ -295,7 +300,7 @@ mod tests {
assert_eq!(r.idx(), idx);
// Check REX.W bit.
- assert_eq!(r.rexw(), 1);
+ assert_eq!(r.rexw(), true);
// Check need REX byte.
assert_eq!(r.need_rex(), true);
diff --git a/tests/mov.rs b/tests/mov.rs
new file mode 100644
index 0000000..fce50a1
--- /dev/null
+++ b/tests/mov.rs
@@ -0,0 +1,118 @@
+use juicebox_asm::Asm;
+use juicebox_asm::MemOp;
+use juicebox_asm::{Imm16, Imm32, Imm64, Imm8};
+use juicebox_asm::{Reg16::*, Reg32::*, Reg64::*, Reg8::*};
+
+macro_rules! mov {
+ ($op1:expr, $op2:expr) => {{
+ let mut asm = Asm::new();
+ asm.mov($op1, $op2);
+ asm.into_code()
+ }};
+}
+
+#[rustfmt::skip]
+#[test]
+fn mov_rr() {
+ // 64bit.
+ assert_eq!(mov!(rcx, rdx), [0x48, 0x89, 0xd1]);
+ assert_eq!(mov!(r11, rdx), [0x49, 0x89, 0xd3]);
+ assert_eq!(mov!(rdi, r12), [0x4c, 0x89, 0xe7]);
+ assert_eq!(mov!(r15, r12), [0x4d, 0x89, 0xe7]);
+
+ // 32bit.
+ assert_eq!(mov!(ecx, edx), [0x89, 0xd1]);
+ assert_eq!(mov!(r11d, edx), [0x41, 0x89, 0xd3]);
+ assert_eq!(mov!(edi, r12d), [0x44, 0x89, 0xe7]);
+ assert_eq!(mov!(r15d, r12d), [0x45, 0x89, 0xe7]);
+
+ // 16bit.
+ assert_eq!(mov!(cx, dx), [0x66, 0x89, 0xd1]);
+ assert_eq!(mov!(r11w, dx), [0x66, 0x41, 0x89, 0xd3]);
+ assert_eq!(mov!(di, r12w), [0x66, 0x44, 0x89, 0xe7]);
+ assert_eq!(mov!(r15w, r12w), [0x66, 0x45, 0x89, 0xe7]);
+
+ // 8bit.
+ assert_eq!(mov!(cl, dl), [0x88, 0xd1]);
+ assert_eq!(mov!(ch, dh), [0x88, 0xf5]);
+ assert_eq!(mov!(dil, sil), [0x40, 0x88, 0xf7]);
+ assert_eq!(mov!(r11l, dl), [0x41, 0x88, 0xd3]);
+ assert_eq!(mov!(dil, r12l), [0x44, 0x88, 0xe7]);
+ assert_eq!(mov!(r15l, r12l), [0x45, 0x88, 0xe7]);
+}
+
+#[rustfmt::skip]
+#[test]
+fn mov_ri() {
+ // 64bit.
+ assert_eq!(mov!(rdi, Imm64::from(0xaabb)), [0x48, 0xbf, 0xbb, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+ assert_eq!(mov!(r12, Imm64::from(0xaabb)), [0x49, 0xbc, 0xbb, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+
+ // 32bit.
+ assert_eq!(mov!(edi, Imm32::from(0xaabb)), [0xbf, 0xbb, 0xaa, 0x00, 0x00]);
+ assert_eq!(mov!(r12d, Imm32::from(0xaabb)), [0x41, 0xbc, 0xbb, 0xaa, 0x00, 0x00]);
+
+ // 16bit.
+ assert_eq!(mov!(di, Imm16::from(0xaabbu16)), [0x66, 0xbf, 0xbb, 0xaa]);
+ assert_eq!(mov!(r12w, Imm16::from(0xaabbu16)), [0x66, 0x41, 0xbc, 0xbb, 0xaa]);
+
+ // 8bit.
+ assert_eq!(mov!(dil, Imm8::from(0xaau8)), [0x40, 0xb7, 0xaa]);
+ assert_eq!(mov!(r12l, Imm8::from(0xaau8)), [0x41, 0xb4, 0xaa]);
+}
+
+#[rustfmt::skip]
+#[test]
+fn mov_rm() {
+ // 64bit.
+ assert_eq!(mov!(rcx, MemOp::Indirect(rdx)), [0x48, 0x8b, 0x0a]);
+ assert_eq!(mov!(r11, MemOp::Indirect(rsi)), [0x4c, 0x8b, 0x1e]);
+ assert_eq!(mov!(rdi, MemOp::Indirect(r14)), [0x49, 0x8b, 0x3e]);
+ assert_eq!(mov!(r15, MemOp::Indirect(r14)), [0x4d, 0x8b, 0x3e]);
+
+ // 32bit.
+ assert_eq!(mov!(ecx, MemOp::Indirect(rdx)), [0x8b, 0x0a]);
+ assert_eq!(mov!(r11d, MemOp::Indirect(rsi)), [0x44, 0x8b, 0x1e]);
+ assert_eq!(mov!(edi, MemOp::Indirect(r14)), [0x41, 0x8b, 0x3e]);
+ assert_eq!(mov!(r15d, MemOp::Indirect(r14)), [0x45, 0x8b, 0x3e]);
+
+ // 16bit.
+ assert_eq!(mov!(cx, MemOp::Indirect(rdx)), [0x66, 0x8b, 0x0a]);
+ assert_eq!(mov!(r11w, MemOp::Indirect(rsi)), [0x66, 0x44, 0x8b, 0x1e]);
+ assert_eq!(mov!(di, MemOp::Indirect(r14)), [0x66, 0x41, 0x8b, 0x3e]);
+ assert_eq!(mov!(r15w, MemOp::Indirect(r14)), [0x66, 0x45, 0x8b, 0x3e]);
+
+ // 8bit.
+ assert_eq!(mov!(cl, MemOp::Indirect(rdx)), [0x8a, 0x0a]);
+ assert_eq!(mov!(r11l, MemOp::Indirect(rsi)), [0x44, 0x8a, 0x1e]);
+ assert_eq!(mov!(dil, MemOp::Indirect(r14)), [0x41, 0x8a, 0x3e]);
+ assert_eq!(mov!(r15l, MemOp::Indirect(r14)), [0x45, 0x8a, 0x3e]);
+}
+
+#[rustfmt::skip]
+#[test]
+fn mov_mr() {
+ // 64bit.
+ assert_eq!(mov!(MemOp::Indirect(rdx), rcx), [0x48, 0x89, 0x0a]);
+ assert_eq!(mov!(MemOp::Indirect(rsi), r11), [0x4c, 0x89, 0x1e]);
+ assert_eq!(mov!(MemOp::Indirect(r14), rdi), [0x49, 0x89, 0x3e]);
+ assert_eq!(mov!(MemOp::Indirect(r14), r15), [0x4d, 0x89, 0x3e]);
+
+ // 32bit.
+ assert_eq!(mov!(MemOp::Indirect(rdx), ecx), [0x89, 0x0a]);
+ assert_eq!(mov!(MemOp::Indirect(rsi), r11d), [0x44, 0x89, 0x1e]);
+ assert_eq!(mov!(MemOp::Indirect(r14), edi), [0x41, 0x89, 0x3e]);
+ assert_eq!(mov!(MemOp::Indirect(r14), r15d), [0x45, 0x89, 0x3e]);
+
+ // 16bit.
+ assert_eq!(mov!(MemOp::Indirect(rdx), cx), [0x66, 0x89, 0x0a]);
+ assert_eq!(mov!(MemOp::Indirect(rsi), r11w), [0x66, 0x44, 0x89, 0x1e]);
+ assert_eq!(mov!(MemOp::Indirect(r14), di), [0x66, 0x41, 0x89, 0x3e]);
+ assert_eq!(mov!(MemOp::Indirect(r14), r15w), [0x66, 0x45, 0x89, 0x3e]);
+
+ // 8bit.
+ assert_eq!(mov!(MemOp::Indirect(rdx), cl), [0x88, 0x0a]);
+ assert_eq!(mov!(MemOp::Indirect(rsi), r11l), [0x44, 0x88, 0x1e]);
+ assert_eq!(mov!(MemOp::Indirect(r14), dil), [0x41, 0x88, 0x3e]);
+ assert_eq!(mov!(MemOp::Indirect(r14), r15l), [0x45, 0x88, 0x3e]);
+}