diff --git a/src/arch/x86_64/encode.rs b/src/arch/x86_64/encode.rs index 456abf2..9499c62 100644 --- a/src/arch/x86_64/encode.rs +++ b/src/arch/x86_64/encode.rs @@ -172,9 +172,35 @@ impl Code { self.bytes.push(0xc3); } - pub fn sub(&mut self) { - // sub esp 40 iirc - self.bytes.extend([0x48, 0x83, 0xec, 0x28]); + pub fn sub(&mut self, mut dst: Reg, src: impl Into) -> ERes { + let mut src = src.into(); + let mut width = src.width_signed()?; + let dst_width = dst.width().min(Width::B32); + + self.prefix16(dst_width); + self.rex(dst, 0, 0, dst); + + if width > dst_width { + width = src.width_unsigned()?; + if dst.width() == Width::B64 || width > dst_width { + return Err("immediate overflow".into()); + } + src = src.reinterpret(dst_width); + width = src.width_signed()?; + } + if dst.width() == Width::B8 { + self.bytes.push(0x80); + } else { + if width == Width::B8 { + self.bytes.push(0x83); + } else { + self.bytes.push(0x81); + width = dst_width; + } + } + self.bytes.push(modrm(0b11, 0b101, dst.base())); + self.imm(src, width); + Ok(()) } fn prefix16(&mut self, width: impl Into) { diff --git a/src/arch/x86_64/test/asm/mod.rs b/src/arch/x86_64/test/asm/mod.rs new file mode 100644 index 0000000..e84f419 --- /dev/null +++ b/src/arch/x86_64/test/asm/mod.rs @@ -0,0 +1,48 @@ +mod setup; +use setup::*; + +#[test] +fn mov() { + let c = &mut TestCtx::new("mov"); + + for dst in regs() { + for src in regs() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } + + for dst in regs() { + for src in mems() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } + + for dst in regs() { + for src in imms() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } + + for dst in mems() { + for src in regs() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } + + for dst in mems() { + for src in imms() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } +} + +#[test] +fn sub() { + let c = &mut TestCtx::new("mov"); + + for dst in regs() { + for src in imms() { + eq(c, format!("sub {dst}, {src}"), |c| c.sub(dst, src)) + } + } +} diff --git a/src/arch/x86_64/test/nasm.rs b/src/arch/x86_64/test/asm/setup.rs similarity index 81% rename from src/arch/x86_64/test/nasm.rs rename to src/arch/x86_64/test/asm/setup.rs index 2b533c8..3050a40 100644 --- a/src/arch/x86_64/test/nasm.rs +++ b/src/arch/x86_64/test/asm/setup.rs @@ -32,50 +32,15 @@ const IMMS: &[i128] = &[ const WIDTHS: &[Width] = &[Width::B8, Width::B16, Width::B32, Width::B64]; -#[test] -fn mov() { - let c = &mut TestCtx::new("mov"); - - for dst in regs() { - for src in regs() { - eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); - } - } - - for dst in regs() { - for src in mems() { - eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); - } - } - - for dst in regs() { - for src in imms() { - eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); - } - } - - for dst in mems() { - for src in regs() { - eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); - } - } - - for dst in mems() { - for src in imms() { - eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); - } - } -} - -fn imms() -> impl Iterator { +pub fn imms() -> impl Iterator { IMMS.iter().cloned() } -fn regs() -> impl Iterator { +pub fn regs() -> impl Iterator { Reg::IMPORTANT.iter().cloned() } -fn mems() -> impl Iterator { +pub fn mems() -> impl Iterator { gen move { for ® in Reg::IMPORTANT { for &disp in DISPS { @@ -87,14 +52,14 @@ fn mems() -> impl Iterator { } } -struct TestCtx { +pub struct TestCtx { path: String, code: Code, cache: HashMap, String>>, changed: bool, } -fn eq( +pub fn eq( ctx: &mut TestCtx, asm: impl AsRef, instr: impl FnOnce(&mut Code) -> Result<(), CompilerMsg>, @@ -176,7 +141,7 @@ fn write(path: &str, binary: &[u8]) { const CACHE_PATH: &str = "test/nasm_cache"; impl TestCtx { - fn new(name: &str) -> Self { + pub fn new(name: &str) -> Self { let path = CACHE_PATH.to_string() + "/" + name; let cache = match std::fs::read(&path) { Ok(bytes) => bitcode::decode(&bytes).unwrap_or_default(), diff --git a/src/arch/x86_64/test/full/windows.rs b/src/arch/x86_64/test/full/windows.rs index 7c9b44c..478292e 100644 --- a/src/arch/x86_64/test/full/windows.rs +++ b/src/arch/x86_64/test/full/windows.rs @@ -11,7 +11,7 @@ fn hello() -> Result<(), CompilerMsg> { let entry = program.func( "main", [Instr::Asm(encode(|c| { - c.sub(); + c.sub(esp, 0x28)?; // stdout c.mov(ecx, -11)?; c.call_mem(get_std_handle); diff --git a/src/arch/x86_64/test/mod.rs b/src/arch/x86_64/test/mod.rs index 3a6a91a..b47d229 100644 --- a/src/arch/x86_64/test/mod.rs +++ b/src/arch/x86_64/test/mod.rs @@ -1,2 +1,2 @@ mod full; -mod nasm; +mod asm; diff --git a/src/arch/x86_64/types.rs b/src/arch/x86_64/types.rs index 664cdb6..3f592b6 100644 --- a/src/arch/x86_64/types.rs +++ b/src/arch/x86_64/types.rs @@ -53,6 +53,15 @@ impl Imm { _ => return Err(Self::overflow_msg()), }) } + + pub fn reinterpret(&self, width: Width) -> Self { + Self(match width { + Width::B8 => self.0 as i8 as i128, + Width::B16 => self.0 as i16 as i128, + Width::B32 => self.0 as i32 as i128, + Width::B64 => self.0 as i64 as i128, + }) + } } impl TryFrom for u8 { diff --git a/test/bin/x86_64_test.exe b/test/bin/x86_64_test.exe index 6e01c6a..ea88623 100755 Binary files a/test/bin/x86_64_test.exe and b/test/bin/x86_64_test.exe differ diff --git a/test/nasm_cache/mov b/test/nasm_cache/mov index e53395c..b65b4cb 100644 Binary files a/test/nasm_cache/mov and b/test/nasm_cache/mov differ