diff --git a/src/arch/x86_64/asm.rs b/src/arch/x86_64/asm.rs index 1b43598..fdb3db6 100644 --- a/src/arch/x86_64/asm.rs +++ b/src/arch/x86_64/asm.rs @@ -1,5 +1,5 @@ use crate::{ - arch::x86_64::{BitWidth, Reg, RegMode}, + arch::x86_64::{Reg, RegH, RegWH, Width, WidthH}, backend::Symbol, }; @@ -9,34 +9,42 @@ pub struct Asm { #[derive(Clone, Copy)] pub enum Instr { - Mov { dst: RegMode, src: RegImmMem }, + Movr { dst: RegH, src: RegH, width: Width }, + Movi { dst: RegWH, imm: u64 }, // TODO: horrible - MemMov { reg: RegMode, offset: u64, val: u64 }, + Movm { reg: RegWH, offset: u32, val: u32 }, Int(u8), Call(Symbol), - CallMem(Symbol), + Callm(Symbol), Ret, Syscall, - Lea { dst: RegMode, sym: Symbol }, - Push(RegImm), + Lea { dst: RegWH, sym: Symbol }, + Pushr(Reg), + Pushi(u32), Pop(Reg), Sub, } +#[derive(Clone, Copy)] +pub struct Mem { + reg: Reg, + disp: u32, +} + #[derive(Clone, Copy)] pub enum RegImmMem { - Reg(RegMode), + Reg(RegWH), Imm(u64), } #[derive(Clone, Copy)] -pub enum RegImm { +pub enum RegImm { Reg(Reg), Imm(u64), } -impl From for RegImmMem { - fn from(value: RegMode) -> Self { +impl From for RegImmMem { + fn from(value: RegWH) -> Self { Self::Reg(value) } } @@ -60,31 +68,46 @@ impl From for RegImmMem { } mod fns { + use crate::io::CompilerMsg; + use super::*; - pub fn mov(dst: RegMode, src: impl Into) -> Instr { - Instr::Mov { - dst, - src: src.into(), - } - } - - pub fn lea(dst: RegMode, sym: Symbol) -> Instr { - Instr::Lea { dst, sym } - } - - pub fn push(reg: impl Into) -> Instr { - Instr::Push(match reg.into() { - RegImmMem::Reg(reg) => { - assert_eq!(reg.width, BitWidth::B64); - assert!(!reg.high); - RegImm::Reg(reg.reg) + pub fn mov(dst: RegWH, src: impl Into) -> Result { + Ok(match src.into() { + RegImmMem::Reg(src) => { + if src.widthh.width() != dst.widthh.width() { + return Err("src and dst are not same width".into()); + } + Instr::Movr { + dst: dst.into(), + src: src.into(), + width: dst.widthh.into(), + } } - RegImmMem::Imm(imm) => RegImm::Imm(imm), + RegImmMem::Imm(imm) => Instr::Movi { dst, imm }, }) } - pub fn pop(reg: RegMode) -> Instr { - assert!(reg.width == BitWidth::B64); + pub fn lea(dst: RegWH, sym: Symbol) -> Instr { + Instr::Lea { dst, sym } + } + + pub fn push(reg: impl Into) -> Result { + Ok(match reg.into() { + RegImmMem::Reg(reg) => { + if reg.widthh != WidthH::B64 { + return Err("register must be 64 bit".into()); + } + Instr::Pushr(reg.reg) + } + RegImmMem::Imm(imm) => match imm.try_into() { + Ok(imm) => Instr::Pushi(imm), + Err(_) => return Err("immediate must be 32 bit".into()), + }, + }) + } + + pub fn pop(reg: RegWH) -> Instr { + assert!(reg.widthh == WidthH::B64); Instr::Pop(reg.reg) } } diff --git a/src/arch/x86_64/encode.rs b/src/arch/x86_64/encode.rs index 264ff36..923e157 100644 --- a/src/arch/x86_64/encode.rs +++ b/src/arch/x86_64/encode.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use super::*; use crate::backend::{LibImport, LinkedProgram, SymImport, SymTable, Symbol}; +use util::*; pub struct Encoder<'a> { pub data: Vec, @@ -71,63 +72,42 @@ fn compile_instr(encoder: &mut Encoder, instr: &BInstr) -> Result<(), CompilerMs impl Encoder<'_> { // assembly - pub fn mov(&mut self, dst: RegMode, src: impl Into) -> Result<(), CompilerMsg> { - let src = src.into(); - let width = dst.width; - if width == BitWidth::B16 { + pub fn movi(&mut self, dst: RegWH, imm: u64) -> Result<(), CompilerMsg> { + if dst.widthh == WidthH::B16 { self.data.push(0x66); } - let dst8 = dst.gt8(); - let b64 = width == BitWidth::B64; - let b8 = width == BitWidth::B8; - let src8 = if let RegImmMem::Reg(src) = src { - src.gt8() - } else { - false - }; - // special 64-bit / register 4-7 indicator - if dst8 || src8 || b64 || (dst.gt4() && !dst.high) { - self.data - .push(0x40 | dst8 as u8 | ((b64 as u8) << 3) | ((src8 as u8) << 2)); + if dst.requires_rex() { + self.data.push(rex(dst.widthh, 0, 0, dst)); } - match src { - RegImmMem::Reg(src) => { - if dst.width != src.width { - return Err("src and dst are not the same size".into()); - } - self.data.push(0x88 | !b8 as u8); - let modrm = 0b11_000_000 | (src.base() << 3) | dst.base(); - self.data.push(modrm); - } - RegImmMem::Imm(imm) => { - if imm > width.max() { - return Err("immediate cannot fit in register".into()); - } - self.data.push(0xb0 | ((!b8 as u8) << 3) | dst.base()); - self.data.extend(&imm.to_le_bytes()[..width.bytes()]); - } + if imm > dst.widthh.max() { + return Err("immediate cannot fit in register".into()); } + let opcode = 0xb0 | ((dst.widthh.gt8() as u8) << 3); + self.data.push(opcode | dst.base()); + self.data.extend(&imm.to_le_bytes()[..dst.widthh.bytes()]); Ok(()) } - pub fn mem_mov(&mut self, reg: RegMode, offset: u64, val: u64) { - assert!(offset <= u8::MAX as u64); - assert!(val <= u32::MAX as u64); - self.data - .extend([0x48 | ((reg.gt8() as u8) << 2), 0xc7, 0x40 | reg.base()]); - if reg.reg == rsp.reg { - self.data.push(0x24) + pub fn movr(&mut self, dst: RegH, src: RegH, width: Width) { + if width == Width::B16 { + self.data.push(0x66); } - self.data.push(offset as u8); - self.data.extend((val as u32).to_le_bytes()); + if src.requires_rex(width) || dst.requires_rex(width) { + self.data.push(rex(width, src, 0, dst)); + } + self.data.push(0x88 | width.gt8() as u8); + self.data.push(modrm_regs(src, dst)); } - pub fn lea(&mut self, dst: RegMode, sym: Symbol) { - self.data.extend([ - 0x48 | ((dst.gt8() as u8) << 2), - 0x8d, - 0x05 | (dst.base() << 3), - ]); + pub fn movm(&mut self, reg: RegWH, offset: u32, val: u32) { + self.data.extend([rex(1, reg, 0, 0), 0xc7]); + self.modrm_regdisp(reg, offset); + self.data.extend(val.to_le_bytes()); + } + + pub fn lea(&mut self, dst: RegWH, sym: Symbol) { + self.data + .extend([rex(1, dst, 0, 0), 0x8d, modrm_disp32(dst)]); self.sym_offset4(sym); } @@ -144,7 +124,7 @@ impl Encoder<'_> { self.sym_offset4(sym); } - pub fn call_mem(&mut self, sym: Symbol) { + pub fn callm(&mut self, sym: Symbol) { self.data.extend([0xff, 0x15]); self.sym_offset4(sym); } @@ -153,15 +133,15 @@ impl Encoder<'_> { self.data.push(0xc3); } - pub fn push(&mut self, reg: Reg) { + pub fn pushr(&mut self, reg: Reg) { if reg.gt8() { self.data.push(0x41); } self.data.push(0x50 | reg.base()); } - pub fn push_imm(&mut self, imm: u64) { - const U8: u64 = 2 << 8; + pub fn pushi(&mut self, imm: u32) { + const U8: u32 = 2 << 8; if let 0..U8 = imm { self.data.push(0x6a); self.data.push(imm as u8); @@ -195,30 +175,38 @@ impl Encoder<'_> { pub fn asm(&mut self, instr: Instr) -> Result<(), CompilerMsg> { match instr { - Instr::Mov { dst, src } => self.mov(dst, src)?, - Instr::MemMov { reg, offset, val } => self.mem_mov(reg, offset, val), + Instr::Movr { dst, src, width } => self.movr(dst, src, width), + Instr::Movi { dst, imm } => self.movi(dst, imm)?, + Instr::Movm { reg, offset, val } => self.movm(reg, offset, val), Instr::Int(code) => self.int(code), Instr::Syscall => self.syscall(), Instr::Lea { dst, sym } => self.lea(dst, sym), Instr::Call(sym) => self.call(sym), + Instr::Callm(sym) => self.callm(sym), Instr::Ret => self.ret(), - Instr::Push(val) => match val { - RegImm::Reg(reg) => self.push(reg), - RegImm::Imm(imm) => self.push_imm(imm), - }, + Instr::Pushr(reg) => self.pushr(reg), + Instr::Pushi(imm) => self.pushi(imm), Instr::Pop(reg) => self.pop(reg), - Instr::CallMem(sym) => self.call_mem(sym), Instr::Sub => self.data.extend([0x48, 0x83, 0xec, 0x28]), } Ok(()) } -} -/// assumes the next instruction is directly after -fn addr_offset(pos: usize, addr: u64) -> [u8; 4] { - let pos = (pos + 4) as i32; - let offset = addr as i32 - pos; - offset.to_le_bytes() + pub fn modrm_regdisp(&mut self, reg: impl Into, disp: u32) { + let reg = reg.into(); + let disp8 = disp < u8::MAX as u32; + let mod_ = if disp8 { 0b01 } else { 0b10 }; + self.data.push(modrm(mod_, 0, reg.base())); + if reg == rsp.reg { + // SIB + self.data.push(0x24); + } + if disp8 { + self.data.push(disp as u8); + } else { + self.data.extend(disp.to_le_bytes()); + } + } } impl<'a> Encoder<'a> { diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 9364860..23a74e5 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -2,6 +2,7 @@ mod asm; mod encode; mod reg; mod test; +mod util; use crate::{ arch::Arch, diff --git a/src/arch/x86_64/reg.rs b/src/arch/x86_64/reg.rs index ea3a1e4..64e0c05 100644 --- a/src/arch/x86_64/reg.rs +++ b/src/arch/x86_64/reg.rs @@ -2,47 +2,35 @@ pub struct Reg(u8); #[derive(Clone, Copy)] -pub struct RegMode { +pub struct RegH { pub reg: Reg, - pub width: BitWidth, pub high: bool, } +#[derive(Clone, Copy)] +pub struct RegWH { + pub reg: Reg, + pub widthh: WidthH, +} + #[derive(Debug, Clone, Copy, PartialEq)] -pub enum BitWidth { +pub enum Width { B64, B32, B16, B8, } -impl Reg { - pub fn base(&self) -> u8 { - self.0 & 0b111 - } - /// checks if register is not one of the first 8 (0-7) - pub fn gt8(&self) -> bool { - self.0 >= 0b1000 - } - pub fn gt4(&self) -> bool { - self.0 >= 0b0100 - } +/// width that also specifies if high for 8 bit +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum WidthH { + B64, + B32, + B16, + B8 { high: bool }, } -impl RegMode { - pub fn base(&self) -> u8 { - self.reg.base() - } - /// checks if register is not one of the first 8 (0-7) - pub fn gt8(&self) -> bool { - self.reg.gt8() - } - pub fn gt4(&self) -> bool { - self.reg.gt4() - } -} - -def_regs! { +def_regs! { RegWH; 0b0000 : rax eax ax al ah=spl, 0b0001 : rcx ecx cx cl ch=bpl, 0b0010 : rdx edx dx dl dh=sil, @@ -63,13 +51,40 @@ def_regs! { 0b1111 : r15 r15d r15w r15b, } -impl BitWidth { +impl Reg { + pub fn base(&self) -> u8 { + self.0 & 0b111 + } + /// checks if register is not one of the first 8 (0-7) + pub fn gt8(&self) -> bool { + self.0 >= 0b1000 + } + pub fn gt4(&self) -> bool { + self.0 >= 0b0100 + } +} + +impl RegWH { + pub fn requires_rex(&self) -> bool { + self.gt8() + || self.widthh == WidthH::B64 + || (self.gt4() && self.widthh == WidthH::B8 { high: false }) + } +} + +impl RegH { + pub fn requires_rex(&self, width: Width) -> bool { + self.gt8() || width == Width::B64 || (self.gt4() && width == Width::B8 && !self.high) + } +} + +impl WidthH { pub const fn max(&self) -> u64 { match self { Self::B64 => u64::MAX, Self::B32 => u32::MAX as u64, Self::B16 => u16::MAX as u64, - Self::B8 => u8::MAX as u64, + Self::B8 { .. } => u8::MAX as u64, } } pub const fn bytes(&self) -> usize { @@ -77,28 +92,84 @@ impl BitWidth { Self::B64 => 8, Self::B32 => 4, Self::B16 => 2, - Self::B8 => 1, + Self::B8 { .. } => 1, + } + } + /// greater than 8 bits + pub const fn gt8(&self) -> bool { + !matches!(self, Self::B8 { .. }) + } + + pub const fn width(&self) -> Width { + match self { + WidthH::B64 => Width::B64, + WidthH::B32 => Width::B32, + WidthH::B16 => Width::B16, + WidthH::B8 { .. } => Width::B8, + } + } +} + +impl Width { + /// greater than 8 bits + pub const fn gt8(&self) -> bool { + !matches!(self, Self::B8) + } +} + +impl From for Reg { + fn from(value: RegWH) -> Self { + value.reg + } +} + +impl From for Reg { + fn from(value: RegH) -> Self { + value.reg + } +} + +impl From for RegH { + fn from(value: RegWH) -> Self { + Self { + reg: value.reg, + high: if let WidthH::B8 { high } = value.widthh { + high + } else { + false + }, + } + } +} + +impl From for Width { + fn from(value: WidthH) -> Self { + match value { + WidthH::B64 => Self::B64, + WidthH::B32 => Self::B32, + WidthH::B16 => Self::B16, + WidthH::B8 { .. } => Self::B8, } } } macro_rules! def_regs { - ($($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $($B8H:ident=$hval:expr)?,)*) => { + ($Struct: ident; $($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $($B8H:ident=$hval:expr)?,)*) => { $( #[allow(non_upper_case_globals)] - pub const $B64: RegMode = RegMode { reg: Reg($val), width: BitWidth::B64, high: false }; + pub const $B64: $Struct = $Struct { reg: Reg($val), widthh: WidthH::B64 }; #[allow(non_upper_case_globals)] - pub const $B32: RegMode = RegMode { reg: Reg($val), width: BitWidth::B32, high: false }; + pub const $B32: $Struct = $Struct { reg: Reg($val), widthh: WidthH::B32 }; #[allow(non_upper_case_globals)] - pub const $B16: RegMode = RegMode { reg: Reg($val), width: BitWidth::B16, high: false }; + pub const $B16: $Struct = $Struct { reg: Reg($val), widthh: WidthH::B16 }; #[allow(non_upper_case_globals)] - pub const $B8 : RegMode = RegMode { reg: Reg($val), width: BitWidth::B8, high: false }; + pub const $B8 : $Struct = $Struct { reg: Reg($val), widthh: WidthH::B8 { high: false } }; $( #[allow(non_upper_case_globals)] - pub const $B8H: RegMode = RegMode { reg: $hval.reg, width: BitWidth::B8, high: true }; + pub const $B8H: $Struct = $Struct { reg: $hval.reg, widthh: WidthH::B8 { high: true } }; )? )* - impl RegMode { + impl $Struct { pub fn parse(s: &str) -> Option { Some(match s.to_lowercase().as_str() { $( @@ -116,4 +187,21 @@ macro_rules! def_regs { } }; } + use def_regs; + +impl std::ops::Deref for RegWH { + type Target = Reg; + + fn deref(&self) -> &Self::Target { + &self.reg + } +} + +impl std::ops::Deref for RegH { + type Target = Reg; + + fn deref(&self) -> &Self::Target { + &self.reg + } +} diff --git a/src/arch/x86_64/test/bin.rs b/src/arch/x86_64/test/bin.rs index c3c89a2..38ebacf 100644 --- a/src/arch/x86_64/test/bin.rs +++ b/src/arch/x86_64/test/bin.rs @@ -5,11 +5,12 @@ use crate::{ use std::{fs::OpenOptions, io::Write, os::unix::fs::OpenOptionsExt, process::Command}; pub fn run() { - windows(); - // linux(); + if let Err(err) = linux() { + println!("{err:?}"); + } } -fn linux() { +fn linux() -> Result<(), CompilerMsg> { let mut program = Program::::default(); let text = b"Hello world!\n"; let text_sym = program.ro_data("hello_en", text); @@ -19,10 +20,10 @@ fn linux() { "hello2", [BInstr::Asm(Asm { instrs: vec![ - mov(ax, 1), - mov(di, 1), + mov(ax, 1)?, + mov(di, 1)?, lea(rsi, text_sym2), - mov(dx, text2.len() as u64), + mov(dx, text2.len() as u64)?, Instr::Syscall, Instr::Ret, ], @@ -32,15 +33,15 @@ fn linux() { "main", [BInstr::Asm(Asm { instrs: vec![ - mov(di, 39), - push(rdi), - mov(ax, 1), - mov(di, 1), + mov(di, 39)?, + push(rdi)?, + mov(ax, 1)?, + mov(di, 1)?, lea(rsi, text_sym), - mov(dx, text.len() as u64), + mov(dx, text.len() as u64)?, Instr::Syscall, Instr::Call(hello2), - mov(ax, 0x3c), + mov(ax, 0x3c)?, pop(rdi), Instr::Syscall, ], @@ -73,9 +74,10 @@ fn linux() { if let Some(code) = status.code() { std::process::exit(code); } + Ok(()) } -fn windows() { +fn windows() -> Result<(), CompilerMsg> { let mut program = Program::::default(); let [get_std_handle, write_file, exit_process] = program.external("KERNEL32.dll", ["GetStdHandle", "WriteFile", "ExitProcess"]); @@ -88,22 +90,22 @@ fn windows() { instrs: vec![ Instr::Sub, // stdout - mov(ecx, -11), - Instr::CallMem(get_std_handle), + mov(ecx, -11)?, + Instr::Callm(get_std_handle), // write - mov(rcx, rax), + mov(rcx, rax)?, lea(rdx, text_sym), - mov(r8d, text.len() as u64), + mov(r8d, text.len() as u64)?, lea(r9, written), - Instr::MemMov { + Instr::Movm { reg: rsp, offset: 0x20, val: 0, }, - Instr::CallMem(write_file), + Instr::Callm(write_file), // exit - mov(ecx, 39), - Instr::CallMem(exit_process), + mov(ecx, 39)?, + Instr::Callm(exit_process), ], })], ); @@ -121,6 +123,7 @@ fn windows() { if let Some(code) = status.code() { std::process::exit(code); } + Ok(()) } fn write(path: &str, binary: &[u8]) { diff --git a/src/arch/x86_64/test/reg.rs b/src/arch/x86_64/test/reg.rs index 28e15f0..5cfaa4f 100644 --- a/src/arch/x86_64/test/reg.rs +++ b/src/arch/x86_64/test/reg.rs @@ -1,9 +1,15 @@ use super::*; -fn eq(expected: impl AsRef<[u8]>, asm: Instr) { +fn eq(expected: impl AsRef<[u8]>, asm: Result) { let expected = expected.as_ref(); let program = Program::default(); let mut encoder = Encoder::new(&program); + let asm = match asm { + Ok(v) => v, + Err(e) => { + panic!("expected {expected:x?}, failed to compile: {}", e.msg); + } + }; if let Err(e) = encoder.asm(asm) { panic!("expected {expected:x?}, failed to compile: {}", e.msg); } @@ -12,7 +18,7 @@ fn eq(expected: impl AsRef<[u8]>, asm: Instr) { } #[test] -fn reg_reg() { +fn mov_reg_reg() { // used objdump on some nasm compiled assembly eq([0x48, 0x89, 0xd8], mov(rax, rbx)); eq([0x89, 0xd8], mov(eax, ebx)); @@ -34,10 +40,13 @@ fn reg_reg() { eq([0x4d, 0x89, 0xd1], mov(r9, r10)); eq([0x4d, 0x89, 0xe0], mov(r8, r12)); + + eq([0x89, 0xe0], mov(eax, esp)); + eq([0x89, 0xc4], mov(esp, eax)); } #[test] -fn reg_imm() { +fn mov_reg_imm() { eq( [0x49, 0xbf, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12], mov(r15, 0x123456789abcdef0u64), diff --git a/src/arch/x86_64/util.rs b/src/arch/x86_64/util.rs new file mode 100644 index 0000000..2be5599 --- /dev/null +++ b/src/arch/x86_64/util.rs @@ -0,0 +1,73 @@ +use super::*; + +pub trait RexBit { + fn val(self) -> bool; +} + +impl RexBit for u8 { + fn val(self) -> bool { + self != 0 + } +} + +impl RexBit for bool { + fn val(self) -> bool { + self + } +} + +impl RexBit for RegH { + fn val(self) -> bool { + self.gt8() + } +} + +impl RexBit for RegWH { + fn val(self) -> bool { + self.gt8() + } +} + +impl RexBit for WidthH { + fn val(self) -> bool { + self == WidthH::B64 + } +} + +impl RexBit for Width { + fn val(self) -> bool { + self == Width::B64 + } +} + +#[inline(always)] +pub fn modrm_regs(reg: impl Into, reg_rm: impl Into) -> u8 { + modrm(0b11, reg.into().base(), reg_rm.into().base()) +} + +#[inline(always)] +pub fn modrm_disp32(reg: impl Into) -> 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 RexBit, r: impl RexBit, x: impl RexBit, b: impl RexBit) -> u8 { + 0b0100_0000 | bit(w, 3) | bit(r, 2) | bit(x, 1) | bit(b, 0) +} + +#[inline(always)] +pub fn bit(val: impl RexBit, pos: u8) -> u8 { + (val.val() as u8) << pos +} + +/// assumes the next instruction is directly after +pub fn addr_offset(pos: usize, addr: u64) -> [u8; 4] { + let pos = (pos + 4) as i32; + let offset = addr as i32 - pos; + offset.to_le_bytes() +} diff --git a/src/parser/nodes/asm/x86_64.rs b/src/parser/nodes/asm/x86_64.rs index 9722cde..f0b6cce 100644 --- a/src/parser/nodes/asm/x86_64.rs +++ b/src/parser/nodes/asm/x86_64.rs @@ -17,7 +17,7 @@ impl Node for Asm { let dst = parse_reg(ctx)?; ctx.expect(Token::Comma)?; let src = parse_rmi(ctx)?; - instrs.push(Instr::Mov { dst, src }); + instrs.push(mov(dst, src)?); } "int" => { ctx.next(); @@ -60,17 +60,17 @@ pub fn parse_rmi(ctx: &mut crate::parser::ParseCtx) -> Result RegImmMem::Reg(RegMode::parse(ident).ok_or_else(err)?), + Token::Ident(ident) => RegImmMem::Reg(RegWH::parse(ident).ok_or_else(err)?), Token::Lit(LitTy::Number(num)) => RegImmMem::Imm(parse_imm(num, ctx.span)?), _ => return Err(err()), }) } -pub fn parse_reg(ctx: &mut crate::parser::ParseCtx) -> Result { +pub fn parse_reg(ctx: &mut crate::parser::ParseCtx) -> Result { let next = ctx.expect_next()?; let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register"); let Token::Ident(next) = &next else { return Err(err()); }; - RegMode::parse(next).ok_or_else(err) + RegWH::parse(next).ok_or_else(err) } diff --git a/x86_64_test b/x86_64_test index d8879f7..ae8f616 100755 Binary files a/x86_64_test and b/x86_64_test differ