Compare commits

...
7 Commits
Author SHA1 Message Date
iris 6cc81d7a5c fix tests 2026-06-17 01:57:41 -04:00
iris 85eacd783d add more adds 2026-06-17 01:34:25 -04:00
iris 4fe4b50c8b idek 2026-06-17 00:53:26 -04:00
iris 026aec8565 stuff 2026-06-17 00:22:10 -04:00
iris 113f3d4d9c refactor 2026-06-16 23:55:47 -04:00
iris d66f8f02b7 modrm 2026-06-16 21:03:30 -04:00
iris 4e06e474ea add 2026-06-16 02:29:56 -04:00
15 changed files with 581 additions and 297 deletions
+68
View File
@@ -0,0 +1,68 @@
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 add_sub() {
let c = &mut TestCtx::new("add_sub");
// add
for dst in regs() {
for src in imms() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src))
}
}
for dst in regs() {
for src in regs() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src))
}
}
for dst in mems() {
for src in imms() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src))
}
}
// sub
for dst in regs() {
for src in imms() {
eq(c, format!("sub {dst}, {src}"), |c| c.sub(dst, src))
}
}
}
+118 -87
View File
@@ -11,25 +11,20 @@ pub struct Code {
}
impl Code {
pub fn mov(&mut self, dst: impl Into<RegMem>, src: impl Into<RegImmMem>) -> ERes {
let dst = dst.into();
pub fn mov(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
let src = src.into();
match dst {
RegMem::Reg(mut dst) => match src {
RegImmMem::Reg(src) => {
match dst.kind() {
RegMemKind::Reg(mut dst) => match src {
RegMemImm::Reg(src) => {
if dst.width() != src.width() {
return Err("src and dst are not same width".into());
}
if dst.incompatible(&src) {
return Err("incompatible registers due to rex".into());
}
let width = dst.width();
self.prefix16(width);
self.rex(width, src, 0, dst);
self.bytes.push(0x88 | width.not8());
self.bytes.push(modrm_regs(src, dst));
self.prefix16(dst);
self.rex(dst, src, 0, dst)?;
self.bytes.push(0x88 | dst.not8());
self.modrm(src, dst);
}
RegImmMem::Imm(src) => {
RegMemImm::Imm(src) => {
let src_width = src.width_unsigned()?;
if src_width > dst.width() {
return Err("immediate cannot fit in register".into());
@@ -44,40 +39,34 @@ impl Code {
if src_width <= Width::B32 {
dst = dst.lower64();
}
self.rex(dst, 0, 0, dst);
self.rex(dst, 0, 0, dst)?;
self.bytes.push(0xb0 | (dst.not8() << 3) | dst.base());
self.imm(src, dst.width());
}
}
RegImmMem::Mem(src) => {
RegMemImm::Mem(src) => {
if src.width != dst.width() {
return Err("register & memory sizes don't match".into());
}
if dst.high() && src.reg.gt8() {
return Err("registers incompatible (REX)".into());
}
self.prefix32(&src)?;
self.prefix32(src)?;
self.prefix16(dst);
self.rex(dst, dst, 0, src);
self.rex(dst, dst, 0, src)?;
self.bytes.push(0x8a | dst.not8());
self.modrm_regdisp(dst, src);
self.modrm(dst, src);
}
},
RegMem::Mem(dst) => match src {
RegImmMem::Reg(src) => {
RegMemKind::Mem(dst) => match src {
RegMemImm::Reg(src) => {
if src.width() != dst.width {
return Err("register & memory sizes don't match".into());
}
if src.high() && dst.reg.gt8() {
return Err("registers incompatible (REX)".into());
}
self.prefix32(&dst)?;
self.prefix32(dst)?;
self.prefix16(src);
self.rex(src, src, 0, dst);
self.rex(dst, src, 0, dst)?;
self.bytes.push(0x88 | src.not8());
self.modrm_regdisp(src, dst);
self.modrm(src, dst);
}
RegImmMem::Imm(src) => {
RegMemImm::Imm(src) => {
let encode_width = dst.width.min(Width::B32);
let src_width = if dst.width == Width::B64 {
src.width_signed()
@@ -90,32 +79,32 @@ impl Code {
if src_width > dst.width {
return Err("source cannot fit in destination".into());
}
self.prefix32(&dst)?;
self.prefix32(dst)?;
self.prefix16(encode_width);
self.rex(dst, 0, 0, dst);
self.rex(dst, 0, 0, dst)?;
self.bytes.push(0xc6 | encode_width.not8());
self.modrm_regdisp(None, dst);
self.modrm(0, dst);
self.imm(src, encode_width);
}
RegImmMem::Mem(_) => return Err("cannot move memory to memory".into()),
RegMemImm::Mem(_) => return Err("cannot move memory to memory".into()),
},
}
Ok(())
}
pub fn push(&mut self, reg: impl Into<RegImmMem>) -> ERes {
pub fn push(&mut self, reg: impl Into<RegMemImm>) -> ERes {
match reg.into() {
RegImmMem::Reg(reg) => match reg.width() {
RegMemImm::Reg(reg) => match reg.width() {
Width::B64 => {
if reg.gt8() {
self.bytes.push(0x41);
}
self.bytes.push(0x50 | reg.base());
}
Width::B16 => {}
Width::B16 => todo!(),
_ => return Err("register must be 64 or 16 bit".into()),
},
RegImmMem::Imm(imm) => match imm.width_unsigned()? {
RegMemImm::Imm(imm) => match imm.width_unsigned()? {
Width::B8 => {
self.bytes.push(0x6a);
self.bytes.push(imm.0 as u8);
@@ -126,7 +115,7 @@ impl Code {
}
Width::B64 => return Err("immediate must be 32 bit or less".into()),
},
RegImmMem::Mem(mem) => todo!(),
RegMemImm::Mem(mem) => todo!(),
}
Ok(())
}
@@ -144,10 +133,11 @@ impl Code {
Ok(())
}
pub fn lea(&mut self, dst: Reg, sym: Symbol) {
self.bytes
.extend([rex(1, dst, 0, 0), 0x8d, modrm_disp32(dst)]);
self.sym_offset4(sym);
pub fn lea(&mut self, dst: Reg, sym: Symbol) -> ERes {
self.rex(1, dst, 0, 0)?;
self.bytes.push(0x8d);
self.modrm(dst, sym);
Ok(())
}
pub fn int(&mut self, code: u8) {
@@ -172,44 +162,81 @@ impl Code {
self.bytes.push(0xc3);
}
pub fn sub(&mut self, dst: Reg, src: impl Into<Imm>) -> 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());
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>, ext: u8) -> ERes {
match src.into() {
RegMemImm::Reg(src) => {
if src.width() != dst.width() {
return Err("incompatible widths".into());
}
self.prefix32(dst)?;
self.prefix16(src);
self.rex(dst, src, 0, dst)?;
self.bytes.push(src.not8());
self.modrm(src, dst);
}
src = src.reinterpret(dst_width);
width = src.width_signed()?;
}
RegMemImm::Imm(mut src) => {
let mut imm_width = src.width_signed()?;
let dst_width = dst.width().min(Width::B32);
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;
}
if imm_width > dst_width {
imm_width = src.width_unsigned()?;
if dst.width() == Width::B64 || imm_width > dst_width {
return Err("immediate overflow".into());
}
src = src.reinterpret(dst_width);
imm_width = src.width_signed()?;
}
let code = if dst.width() == Width::B8 {
0x80
} else if imm_width == Width::B8 {
0x83
} else {
imm_width = dst_width;
0x81
};
self.bytes.push(modrm(0b11, 0b101, dst.base()));
self.imm(src, width);
self.prefix32(dst)?;
self.prefix16(dst_width);
self.rex(dst, 0, 0, dst)?;
self.bytes.push(code);
self.modrm(ext, dst);
self.imm(src, imm_width);
}
RegMemImm::Mem(src) => {
let RegMemKind::Reg(dst) = dst.kind() else {
return Err("cannot add memory to memory".into());
};
if src.width() != dst.width() {
return Err("incompatible widths".into());
}
self.prefix32(src)?;
self.prefix16(dst);
self.rex(dst, dst, 0, src)?;
self.bytes.push(0x2 | dst.not8());
self.modrm(dst, src);
}
}
Ok(())
}
pub fn add(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
self.add_sub(dst, src, 0)
}
pub fn sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
self.add_sub(dst, src, 5)
}
fn prefix16(&mut self, width: impl Into<Width>) {
if width.into() == Width::B16 {
self.bytes.push(0x66);
}
}
fn prefix32(&mut self, mem: &Mem) -> Result<(), CompilerMsg> {
fn prefix32(&mut self, mem: impl MaybeMem) -> Result<(), CompilerMsg> {
let Some(mem) = mem.mem() else {
return Ok(());
};
match mem.reg.width() {
Width::B8 | Width::B16 => return Err("invalid register width".into()),
Width::B32 => self.bytes.push(0x67),
@@ -218,31 +245,35 @@ impl Code {
Ok(())
}
fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) {
if w.rexw() || r.rex() || x.rex() || b.rex() | r.req() | b.req() {
fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> ERes {
if r.req() && b.req_no() || r.req_no() && b.req() {
return Err("registers incompatible (REX)".into());
}
if w.rexw() || r.rex() || x.rex() || b.rex() || r.req() || b.req() {
self.bytes.push(rex(w, r, x, b));
}
Ok(())
}
fn modrm_regdisp(&mut self, reg: impl Into<Option<Reg>>, mem: Mem) {
const I8_MIN: i32 = i8::MIN as i32;
const I8_MAX: i32 = i8::MAX as i32;
let mod_ = match mem.disp {
0 => 0b00,
I8_MIN..=I8_MAX => 0b01,
_ => 0b10,
fn modrm(&mut self, reg: impl ModRMReg, rm: impl ModRMRM) {
let addr = rm.addr();
let mod_ = match addr {
EffAddr::Mem0 | EffAddr::Sym(_) => 0b00,
EffAddr::Mem8(_) => 0b01,
EffAddr::Mem32(_) => 0b10,
EffAddr::None => 0b11,
};
let r = reg.into().map(|r| Reg::base(&r)).unwrap_or(0);
self.bytes.push(modrm(mod_, r, mem.reg.base()));
if mem.reg.base() == rsp.base() {
self.bytes
.push(((mod_ as u8) << 6) | (reg.val() << 3) | rm.rm());
if !matches!(addr, EffAddr::None) && rm.rm() == 0b100 {
// SIB
self.bytes.push(0x24);
}
match mod_ {
0b00 => (),
0b01 => self.bytes.push(mem.disp as u8),
0b10 => self.bytes.extend(mem.disp.to_le_bytes()),
_ => unreachable!(),
match addr {
EffAddr::Mem8(disp) => self.bytes.push(disp as u8),
EffAddr::Mem32(disp) => self.bytes.extend(disp.to_le_bytes()),
EffAddr::Sym(sym) => self.sym_offset4(sym),
_ => (),
}
}
-2
View File
@@ -1,6 +1,5 @@
mod compile;
mod encode;
mod reg;
#[cfg(test)]
mod test;
mod types;
@@ -14,7 +13,6 @@ use crate::{
pub use compile::*;
pub use encode::*;
pub use reg::*;
pub use types::*;
use util::*;
+34 -2
View File
@@ -37,9 +37,41 @@ fn mov() {
}
#[test]
fn sub() {
let c = &mut TestCtx::new("mov");
fn add_sub() {
let c = &mut TestCtx::new("add_sub");
// add
for dst in regs() {
for src in imms() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src))
}
}
for dst in regs() {
for src in regs() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src))
}
}
for dst in regs() {
for src in mems() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src))
}
}
for dst in mems() {
for src in imms() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src))
}
}
for dst in mems() {
for src in regs() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src))
}
}
// sub
for dst in regs() {
for src in imms() {
eq(c, format!("sub {dst}, {src}"), |c| c.sub(dst, src))
+36 -16
View File
@@ -59,42 +59,62 @@ pub struct TestCtx {
changed: bool,
}
#[track_caller]
pub fn eq(
ctx: &mut TestCtx,
asm: impl AsRef<str>,
instr: impl FnOnce(&mut Code) -> Result<(), CompilerMsg>,
instr: impl Fn(&mut Code) -> Result<(), CompilerMsg>,
) {
let asm = asm.as_ref();
let expected = if let Some(val) = ctx.cache.get(asm) {
val
let (mut res, cache) = eq_(ctx, asm, &instr);
if res.is_err() && cache {
ctx.cache.remove(asm);
res = eq_(ctx, asm, &instr).0;
}
if let Err(err) = res {
panic!("{err}");
}
}
#[track_caller]
pub fn eq_(
ctx: &mut TestCtx,
asm: &str,
instr: impl FnOnce(&mut Code) -> Result<(), CompilerMsg>,
) -> (Result<(), String>, bool) {
let (expected, cache) = if let Some(val) = ctx.cache.get(asm) {
(val, true)
} else {
ctx.changed = true;
let res = nasm(asm);
ctx.cache.insert(asm.to_string(), res);
ctx.cache.get(asm).unwrap()
(ctx.cache.get(asm).unwrap(), false)
};
let code = &mut ctx.code;
let res = instr(code);
match (expected, res) {
(Ok(expected), Err(e)) => {
panic!(
"{asm}: failed to compile: {}\nexpected: {expected:x?}",
e.msg
);
}
let res = match (expected, res) {
(Ok(expected), Err(e)) => Err(format!(
"{asm}: failed to compile: {}\nexpected: {expected:x?}",
e.msg
)),
(Err(e), Ok(_)) => {
let res = &code.bytes[..];
panic!("{asm}: should not have compiled:\n{e}\ngot: {res:x?}");
Err(format!(
"{asm}: should not have compiled:\n{e}\ngot: {res:x?}"
))
}
(Err(_), Err(_)) => (),
(Err(_), Err(_)) => Ok(()),
(Ok(expected), Ok(_)) => {
let res = &code.bytes[..];
if expected != res {
panic!("{asm}: expected {expected:x?}, got {res:x?}")
Err(format!("{asm}: expected {expected:x?}, got {res:x?}"))
} else {
Ok(())
}
}
}
code.bytes.clear();
};
ctx.code.bytes.clear();
(res, cache)
}
fn nasm(input: &str) -> Result<Vec<u8>, String> {
+257
View File
@@ -0,0 +1,257 @@
use super::*;
use crate::backend::Symbol;
pub trait RegMem: RexBit + RexW + ModRMRM + Copy + MaybeMem {
fn width(&self) -> Width;
fn kind(self) -> RegMemKind;
}
#[derive(Clone, Copy)]
pub enum RegMemKind {
Reg(Reg),
Mem(Mem),
}
#[derive(Clone, Copy)]
pub enum RegMemImm {
Reg(Reg),
Imm(Imm),
Mem(Mem),
}
pub trait MaybeMem {
fn mem(&self) -> Option<Mem>;
}
impl RegMem for Reg {
fn width(&self) -> Width {
self.width()
}
fn kind(self) -> RegMemKind {
RegMemKind::Reg(self)
}
}
impl MaybeMem for Reg {
fn mem(&self) -> Option<Mem> {
None
}
}
impl RegMem for Mem {
fn width(&self) -> Width {
self.width
}
fn kind(self) -> RegMemKind {
RegMemKind::Mem(self)
}
}
impl MaybeMem for Mem {
fn mem(&self) -> Option<Mem> {
Some(*self)
}
}
// fromrot
impl From<Reg> for RegMemImm {
fn from(value: Reg) -> Self {
Self::Reg(value)
}
}
impl From<Reg> for RegMemKind {
fn from(value: Reg) -> Self {
Self::Reg(value)
}
}
impl From<Mem> for RegMemImm {
fn from(value: Mem) -> Self {
Self::Mem(value)
}
}
impl From<Mem> for RegMemKind {
fn from(value: Mem) -> Self {
Self::Mem(value)
}
}
impl From<u64> for RegMemImm {
fn from(value: u64) -> Self {
Self::Imm(value.into())
}
}
impl From<i64> for RegMemImm {
fn from(value: i64) -> Self {
Self::Imm(value.into())
}
}
impl From<i32> for RegMemImm {
fn from(value: i32) -> Self {
Self::Imm(value.into())
}
}
impl From<i128> for RegMemImm {
fn from(value: i128) -> Self {
Self::Imm(value.into())
}
}
pub trait ModRMRM {
fn rm(&self) -> u8;
fn addr(&self) -> EffAddr;
}
pub enum EffAddr {
Mem0,
Mem8(i8),
Mem32(i32),
Sym(Symbol),
None,
}
impl ModRMRM for Reg {
fn rm(&self) -> u8 {
self.base()
}
fn addr(&self) -> EffAddr {
EffAddr::None
}
}
impl ModRMRM for Mem {
fn rm(&self) -> u8 {
self.reg.base()
}
fn addr(&self) -> EffAddr {
const I8_MIN: i32 = i8::MIN as i32;
const I8_MAX: i32 = i8::MAX as i32;
let disp = self.disp;
match disp {
0 => {
if self.reg.base() == 0b101 {
EffAddr::Mem8(0)
} else {
EffAddr::Mem0
}
}
I8_MIN..=I8_MAX => EffAddr::Mem8(disp as i8),
_ => EffAddr::Mem32(disp),
}
}
}
impl ModRMRM for i32 {
fn rm(&self) -> u8 {
0b101
}
fn addr(&self) -> EffAddr {
EffAddr::Mem32(*self)
}
}
impl ModRMRM for Symbol {
fn rm(&self) -> u8 {
0b101
}
fn addr(&self) -> EffAddr {
EffAddr::Sym(*self)
}
}
impl ModRMReg for u8 {
fn val(&self) -> u8 {
*self
}
}
impl ModRMReg for Reg {
fn val(&self) -> u8 {
self.base()
}
}
pub trait ModRMReg {
fn val(&self) -> u8;
}
#[inline(always)]
pub fn rex(w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> u8 {
0b0100_0000 | bit(w.rexw(), 3) | bit(r.rex(), 2) | bit(x.rex(), 1) | bit(b.rex(), 0)
}
#[inline(always)]
fn bit(val: bool, pos: u8) -> u8 {
(val as u8) << pos
}
pub trait RexBit: Sized {
fn rex(&self) -> bool;
fn req(&self) -> bool {
false
}
fn req_no(&self) -> bool {
false
}
}
impl RexBit for u8 {
fn rex(&self) -> bool {
*self != 0
}
}
impl RexBit for Reg {
fn rex(&self) -> bool {
self.gt8()
}
fn req(&self) -> bool {
self.gt4() && (self.width() == Width::B8) && !self.high()
}
fn req_no(&self) -> bool {
self.high()
}
}
impl RexBit for Mem {
fn rex(&self) -> bool {
self.reg.rex()
}
fn req(&self) -> bool {
self.reg.gt8()
}
}
pub trait RexW {
fn rexw(&self) -> bool;
}
impl RexW for Width {
fn rexw(&self) -> bool {
*self == Width::B64
}
}
impl RexW for Reg {
fn rexw(&self) -> bool {
self.width().rexw()
}
}
impl RexW for u8 {
fn rexw(&self) -> bool {
*self == 1
}
}
impl RexW for Mem {
fn rexw(&self) -> bool {
self.width.rexw()
}
}
@@ -1,34 +1,10 @@
use super::Width;
use crate::io::CompilerMsg;
use std::num::TryFromIntError;
use super::*;
#[derive(Clone, Copy)]
pub struct Mem {
pub reg: Reg,
pub disp: i32,
pub width: Width,
}
#[derive(Clone, Copy)]
pub enum RegImmMem {
Reg(Reg),
Imm(Imm),
Mem(Mem),
}
#[derive(Clone, Copy)]
pub enum RegMem {
Reg(Reg),
Mem(Mem),
}
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct Imm(pub i128);
pub fn mem(reg: Reg, disp: i32, width: Width) -> Mem {
Mem { reg, disp, width }
}
impl Imm {
pub fn overflow_msg() -> CompilerMsg {
"immediate overflow".into()
@@ -72,68 +48,6 @@ impl TryFrom<Imm> for u8 {
}
}
impl std::fmt::Display for Mem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Mem { reg, disp, width } = *self;
let size = match width {
Width::B8 => "BYTE",
Width::B16 => "WORD",
Width::B32 => "DWORD",
Width::B64 => "QWORD",
};
write!(f, "{size} [{reg} {}]", signed_hex(disp as i128, true))
}
}
// fromrot
impl From<Reg> for RegImmMem {
fn from(value: Reg) -> Self {
Self::Reg(value)
}
}
impl From<Reg> for RegMem {
fn from(value: Reg) -> Self {
Self::Reg(value)
}
}
impl From<Mem> for RegImmMem {
fn from(value: Mem) -> Self {
Self::Mem(value)
}
}
impl From<Mem> for RegMem {
fn from(value: Mem) -> Self {
Self::Mem(value)
}
}
impl From<u64> for RegImmMem {
fn from(value: u64) -> Self {
Self::Imm(value.into())
}
}
impl From<i64> for RegImmMem {
fn from(value: i64) -> Self {
Self::Imm(value.into())
}
}
impl From<i32> for RegImmMem {
fn from(value: i32) -> Self {
Self::Imm(value.into())
}
}
impl From<i128> for RegImmMem {
fn from(value: i128) -> Self {
Self::Imm(value.into())
}
}
impl From<u64> for Imm {
fn from(value: u64) -> Self {
Self(value as i128)
+27
View File
@@ -0,0 +1,27 @@
use crate::arch::x86_64::util::signed_hex;
use super::*;
#[derive(Clone, Copy)]
pub struct Mem {
pub reg: Reg,
pub disp: i32,
pub width: Width,
}
pub fn mem(reg: Reg, disp: i32, width: Width) -> Mem {
Mem { reg, disp, width }
}
impl std::fmt::Display for Mem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Mem { reg, disp, width } = *self;
let size = match width {
Width::B8 => "BYTE",
Width::B16 => "WORD",
Width::B32 => "DWORD",
Width::B64 => "QWORD",
};
write!(f, "{size} [{reg} {}]", signed_hex(disp as i128, true))
}
}
+11
View File
@@ -0,0 +1,11 @@
mod arg;
mod imm;
mod mem;
mod reg;
mod width;
pub use arg::*;
pub use imm::*;
pub use mem::*;
pub use reg::*;
pub use width::*;
@@ -1,3 +1,5 @@
use super::Width;
#[derive(Clone, Copy, PartialEq)]
pub struct Reg {
val: u8,
@@ -5,15 +7,6 @@ pub struct Reg {
width: Width,
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Width {
B8 = 0,
B16 = 1,
B32 = 2,
B64 = 3,
}
def_regs! {
0b0000 : rax eax ax al,
0b0001 : rcx ecx cx cl !_,
@@ -21,7 +14,7 @@ def_regs! {
0b0011 : rbx ebx bx bl,
0b0100 : rsp esp sp spl norex=ah !_,
0b0101 : rbp ebp bp bpl norex=ch,
0b0101 : rbp ebp bp bpl norex=ch !_,
0b0110 : rsi esi si sil norex=dh !_,
0b0111 : rdi edi di dil norex=bh,
@@ -189,9 +182,3 @@ macro_rules! def_regs {
use def_regs;
use crate::arch::x86_64::Imm;
impl From<Reg> for Width {
fn from(value: Reg) -> Self {
value.width
}
}
+22
View File
@@ -0,0 +1,22 @@
use super::*;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Width {
B8 = 0,
B16 = 1,
B32 = 2,
B64 = 3,
}
impl From<Reg> for Width {
fn from(value: Reg) -> Self {
value.width()
}
}
impl From<Mem> for Width {
fn from(value: Mem) -> Self {
value.width
}
}
-83
View File
@@ -1,86 +1,3 @@
use super::*;
#[inline(always)]
pub fn modrm_regs(reg: impl Into<Reg>, reg_rm: impl Into<Reg>) -> u8 {
modrm(0b11, reg.into().base(), reg_rm.into().base())
}
#[inline(always)]
pub fn modrm_disp32(reg: impl Into<Reg>) -> u8 {
modrm(0b00, reg.into().base(), 0b101)
}
#[inline(always)]
pub fn modrm(mod_: u8, reg: u8, rm: u8) -> u8 {
(mod_ << 6) | (reg << 3) | rm
}
#[inline(always)]
pub fn rex(w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> u8 {
0b0100_0000 | bit(w.rexw(), 3) | bit(r.rex(), 2) | bit(x.rex(), 1) | bit(b.rex(), 0)
}
#[inline(always)]
fn bit(val: bool, pos: u8) -> u8 {
(val as u8) << pos
}
pub trait RexBit: Sized {
fn rex(&self) -> bool;
fn req(&self) -> bool {
false
}
}
impl RexBit for u8 {
fn rex(&self) -> bool {
*self != 0
}
}
impl RexBit for Reg {
fn rex(&self) -> bool {
self.gt8()
}
fn req(&self) -> bool {
self.gt4() && (self.width() == Width::B8) && !self.high()
}
}
impl RexBit for Mem {
fn rex(&self) -> bool {
self.reg.rex()
}
}
pub trait RexW {
fn rexw(&self) -> bool;
}
impl RexW for Width {
fn rexw(&self) -> bool {
*self == Width::B64
}
}
impl RexW for Reg {
fn rexw(&self) -> bool {
self.width().rexw()
}
}
impl RexW for u8 {
fn rexw(&self) -> bool {
*self == 1
}
}
impl RexW for Mem {
fn rexw(&self) -> bool {
self.width.rexw()
}
}
/// assumes the next instruction is directly after
pub fn addr_offset(pos: usize, addr: u64) -> [u8; 4] {
let pos = (pos + 4) as i32;
+3 -3
View File
@@ -64,12 +64,12 @@ pub fn parse_imm(mut s: &str, span: Span) -> Result<Imm, CompilerMsg> {
Ok(Imm(val))
}
pub fn parse_rmi(ctx: &mut crate::parser::ParseCtx) -> Result<RegImmMem, CompilerMsg> {
pub fn parse_rmi(ctx: &mut crate::parser::ParseCtx) -> Result<RegMemImm, CompilerMsg> {
let next = ctx.expect_next()?;
let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register or immediate");
Ok(match &next {
Token::Ident(ident) => RegImmMem::Reg(Reg::parse(ident).ok_or_else(err)?),
Token::Lit(LitTy::Number(num)) => RegImmMem::Imm(parse_imm(num, ctx.span)?),
Token::Ident(ident) => RegMemImm::Reg(Reg::parse(ident).ok_or_else(err)?),
Token::Lit(LitTy::Number(num)) => RegMemImm::Imm(parse_imm(num, ctx.span)?),
_ => return Err(err()),
})
}
Binary file not shown.
Binary file not shown.