parse, encode inst

This commit is contained in:
janis 2026-07-09 13:51:01 +02:00
parent c4f9eb0e55
commit c48c4261da
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
2 changed files with 812 additions and 28 deletions

View file

@ -2,24 +2,42 @@
;; It's also the first stage in this bootstrapping experiment that will
;; consume proper mnemonic assembly, rather than hex input.
struc OFile
.fd resd 1
.offset resd 1
endstruc
section .bss
global buf
ifile resb 0x20
ofile resb 0x20
buf resb 0x100
labels resq 1
label_refs resq 1
heap resq 1
section .data
_start_lbl db "_start", 0
ENC_RM equ 1
ENC_MR equ 2
ENC_MI equ 3
ENC_OI equ 4
ENC_M equ 5
ENC_O equ 6
ENC_I equ 7
ENC_MC equ 8 ; memory with cl
ENC_M1 equ 9 ; memory with 1
section .data
ENC_R_BITS equ 1
ENC_M_BITS equ 2
ENC_I_BITS equ 4
ENC_CL_BITS equ 8
ENC_ONE_BITS equ 16
ENC_O_SIGNAL_BITS equ 32
ENC_RM_BITS equ ENC_R_BITS | ENC_M_BITS
ENC_O_BITS equ ENC_R_BITS | ENC_O_SIGNAL_BITS
ENC_DISCARD_BITS equ ENC_CL_BITS | ENC_ONE_BITS
ENC_RM equ ENC_R_BITS | ENC_RM_BITS << 8
ENC_MR equ ENC_RM_BITS | ENC_R_BITS << 8
ENC_MI equ ENC_RM_BITS | ENC_I_BITS << 8
ENC_OI equ ENC_O_BITS | ENC_I_BITS << 8
ENC_M equ ENC_RM_BITS
ENC_O equ ENC_O_BITS
ENC_I equ ENC_I_BITS
; r/m with cl
ENC_MC equ ENC_RM_BITS | ENC_CL_BITS << 8
; r/m with 1
ENC_M1 equ ENC_RM_BITS | ENC_ONE_BITS << 8
OPSIZE_8 equ 1
OPSIZE_16 equ 2
@ -83,8 +101,19 @@ mnemonics_table:
MN_CALL db "call", 0
MN_XCHG db "xchg", 0
MN_SYSCALL db "syscall", 0
MNEMONICS_SIZE equ $ - mnemonics_table
;; Opcodes support up to 2 operands
struc OpEntry
.mnemonic_offset resd 1
.num_operands resd 1
.op1_size resd 1
.op2_size resd 1
.encoding resd 1
.opcode resd 1
.reg_bits resd 1
.prefix resd 1
endstruc
op_table:
;; MN_OFFS | NUM | SIZES | ENCOD | OPCODE | RM | PRE
@ -99,11 +128,11 @@ op_table:
dd mnt - MN_MOV, 2, 1, 1, ENC_OI, 0B0h, 00, 00h
dd mnt - MN_MOV, 2, 2, 2, ENC_OI, 0B8h, 00, 66h
dd mnt - MN_MOV, 2, 4, 4, ENC_OI, 0B8h, 00, 00h
dd mnt - MN_MOV, 2, 8, 8, ENC_OI, 0B8h, 00, 00h
dd mnt - MN_MOV, 2, 1, 1, ENC_MI, 0C6h, 00, 00h
dd mnt - MN_MOV, 2, 2, 2, ENC_MI, 0C7h, 00, 66h
dd mnt - MN_MOV, 2, 4, 4, ENC_MI, 0C7h, 00, 00h
dd mnt - MN_MOV, 2, 8, 4, ENC_MI, 0C7h, 00, 00h
dd mnt - MN_MOV, 2, 8, 8, ENC_OI, 0B8h, 00, 00h
dd mnt - MN_TEST, 2, 1, 1, ENC_MR, 84h, 00, 00h
dd mnt - MN_TEST, 2, 2, 2, ENC_MR, 85h, 00, 66h
@ -642,6 +671,84 @@ format_u32:
add rsp, 16
ret
;; print the first $rsi bytes of the contents of $rdi as hex
print_u8s_le:
test esi, esi
jz .ret
push rbx
push r12
push r13
sub rsp, 16
xor r12d, r12d
mov rbx, rdi
mov r13d, esi
.loop:
cmp r12d, r13d
jge .done
movzx edi, bl
call format_u8
mov word [rsp + r12 * 2], ax
inc r12d
shr rbx, 8
jmp .loop
.done:
lea rdi, [rsp]
mov esi, r13d
shl rsi, 1
call print_str
add rsp, 16
pop r13
pop r12
pop rbx
.ret:
ret
print_non_zero_u8s_le:
test rdi, rdi
jz .ret
push rbx
push r12
xor r12d, r12d
mov rbx, rdi
sub rsp, 16
.loop:
movzx edi, bl
test edi, edi
jz .done
call format_u8
mov word [rsp + r12 * 2], ax
inc r12d
shr rbx, 8
jmp .loop
.done:
lea rdi, [rsp]
mov rsi, r12
shl rsi, 1
call print_str
add rsp, 16
pop r12
pop rbx
.ret:
ret
swap_bytes_and_shift_in_place:
bswap rdi
mov rcx, 64
bsf rcx, rdi
and cl, 56
shr rdi, cl
ret
print_non_zero_u8s_be:
test rdi, rdi
jz .done
call swap_bytes_and_shift_in_place
xor eax, eax
call print_non_zero_u8s_le
.done:
ret
print_u8:
call format_u8
sub rsp, 8
@ -763,14 +870,53 @@ print_op_table:
;; rdi: *u8
strlen:
xor rax, rax
.strlen_loop:
.loop:
cmp byte [rdi + rax], 0
je .strlen_done
je .done
inc rax
jmp .strlen_loop
.strlen_done:
jmp .loop
.done:
ret
;; find in the first $rsi bytes of $rdi (haystack) the first $rcx bytes of $rdx (needle).
;; returns the index in $rax, or -1 if not found.
strstr:
push rbx
push r12
push r13
mov r13, rdi ; save original haystack pointer
mov rax, rsi ; remaining haystack length
.loop_haystack:
cmp rax, rcx
jb .not_found ; not enough haystack left for needle
xor rbx, rbx ; needle index
.loop_needle:
cmp rbx, rcx
jz .found ; found the needle
mov r12b, byte [rdx + rbx] ; needle[needle_index]
cmp r12b, byte [rdi + rbx] ; haystack[haystack_index + needle_index]
jne .next_haystack ; mismatch, move to next haystack index
inc rbx
jmp .loop_needle
.next_haystack:
inc rdi ; move to next haystack index
dec rax ; decrease remaining haystack length
jmp .loop_haystack
.not_found:
mov rax, -1
jmp .done
.found:
sub rdi, r13 ; calculate the index of the found needle
mov rax, rdi
.done:
pop r13
pop r12
pop rbx
ret
;; lhs: (rdi, rsi)
;; rhs: (rdx, rcx)
;; al
@ -899,9 +1045,12 @@ exit:
%define KIND_NONE 0
%define KIND_REG 1
%define KIND_MEM 2
%define KIND_IMM 3
%define KIND_MEM_LABEL 4 ; a memory operand whose displacement is a label
%define KIND_IMM_LABEL 5 ; an immediate value that is a label
%define KIND_IMM 4
%define KIND_LABEL_FLAG 8
; a memory operand whose displacement is a label
%define KIND_MEM_LABEL KIND_LABEL_FLAG | KIND_MEM
; an immediate value that is a label
%define KIND_IMM_LABEL KIND_LABEL_FLAG | KIND_IMM
struc operand
.type resd 1
@ -918,6 +1067,11 @@ struc operand_imm
.imm resq 1
endstruc
struc register
.size resd 1
.num resd 1
endstruc
abcd_reg_indices: db 0, 3, 1, 2
rX_suffixes: db 'b', 'w', 'd'
@ -1169,7 +1323,8 @@ try_parse_mem:
mov dword [r12 + operand.type], KIND_MEM
mov dword [r12 + operand.size], 0
mov qword [r12 + operand.reg], -1
mov dword [r12 + operand.reg + register.size], 0
mov dword [r12 + operand.reg + register.num], 5 ; default to EBP (no base)
mov qword [r12 + operand.index], -1
mov dword [r12 + operand.scale], 0
mov dword [r12 + operand.disp], 0
@ -1414,6 +1569,7 @@ try_parse_operand:
.label:
inc rbx ; skip the leading `'`
mov dword [r12 + operand.type], KIND_IMM_LABEL
mov dword [r12 + operand.size], 2 ; 32-bit
mov rdi, rbx
call try_parse_label
test eax, eax
@ -1458,7 +1614,531 @@ try_parse_label:
pop rbx
ret
struc Instruction
.mnemonic_offs resd 1
.num_operands resd 1
.operand1 resb operand_size
.operand2 resb operand_size
endstruc
global try_parse_inst
try_parse_inst:
push rbx ; buf
push r13 ; inst ptr
push rdi
mov rbx, rdi
mov r13, rsi ; output inst ptr
mov dword [r13 + Instruction.num_operands], 0
mov dword [r13 + Instruction.mnemonic_offs], 0
call skip_whitespace_rbx
mov rdi, rbx
call try_parse_label ; parse mnemonic
test eax, eax
jz .not_inst
lea rdi, [rel mnemonics]
mov esi, MNEMONICS_SIZE
mov rdx, rbx
mov ecx, eax
add rbx, rax
call strstr
cmp rax, -1
je .not_inst
mov dword [r13 + Instruction.mnemonic_offs], eax
call skip_whitespace_rbx
mov rdi, rbx
lea rsi, [r13 + Instruction.operand1]
call try_parse_operand
test eax, eax
jz .done
mov dword [r13 + Instruction.num_operands], 1
add rbx, rax
call skip_whitespace_rbx
cmp byte [rbx], `,`
jne .done
inc rbx
call skip_whitespace_rbx
mov rdi, rbx
lea rsi, [r13 + Instruction.operand2]
call try_parse_operand
test eax, eax
jz .done
mov dword [r13 + Instruction.num_operands], 2
add rbx, rax
.done:
mov rax, rbx
pop rdi
sub rax, rdi ; number of bytes consumed
mov rdx, r13 ; output inst ptr
pop r13
pop rbx
ret
.not_inst:
xor rax, rax
mov rdx, r13 ; output inst ptr
pop rdi
pop r13
pop rbx
ret
;; find the op_table entry returning the pointer to the entry in $rax, or 0 if not found.
;; inputs:
;; $edi: mnemonic offset
;; $esi: encoding
;; $edx: size mask
;; $ecx: number of operands
find_op_table_entry:
push rbx
lea rax, [rel op_table]
lea rbx, [rel op_table_end]
neg edi
.loop:
cmp rax, rbx
jge .not_found
cmp edi, dword [rax] ; mnemonic offset
jne .next
cmp ecx, dword [rax + 4] ; number of operands
jne .next
mov r8d, dword [rax + 12] ; op2 size
shl r8d, 8
or r8d, dword [rax + 8] ; op1 size
mov r9d, edx
and r9d, r8d
cmp r9d, r8d
jne .next
mov r8d, dword [rax + 16] ; encoding
mov r9d, esi
and r9d, 0xff00
and r9d, r8d
jz .next
mov r9d, esi
and r9d, 0xff
and r9d, r8d
jnz .found
.next:
add rax, 32
jmp .loop
.not_found:
xor eax, eax
.found:
pop rbx
ret
;; encode the instruction at $rdi into ofile
global encode_inst
encode_inst:
push rbx
push r12
push r13
xor ebx, ebx ; bytes written
mov r12, rdi ; inst ptr
; calculate the encoding
xor esi, esi
xor eax, eax
; we have to check if the 2nd operand, if it exists, is CL or 1
cmp dword [r12 + Instruction.num_operands], 2
jb .op1
; calculate size mask of op2.
; for reg and mem operands, this is 1 << .size.
; for imm operands, this is !(!0 << .size)
mov ecx, dword [r12 + Instruction.operand2 + operand.size]
mov eax, 1
shl eax, cl
cmp dword [r12 + Instruction.operand2 + operand.type], KIND_IMM
jne .size_done_op2
xor eax, eax
lea ecx, [ecx - 1]
sub eax, 1
shl eax, cl
.size_done_op2:
shl eax, 8
mov esi, dword [r12 + Instruction.operand2 + operand.type]
shl esi, 8
cmp dword [r12 + Instruction.operand2 + operand.type], KIND_REG
jne .check_imm_one
cmp qword [r12 + Instruction.operand2 + operand.reg], 1
jne .op1
or esi, ENC_CL_BITS << 8
.check_imm_one:
cmp dword [r12 + Instruction.operand2 + operand.type], KIND_IMM
jne .op1
cmp qword [r12 + Instruction.operand2 + operand_imm.imm], 1
jne .op1
or esi, ENC_ONE_BITS << 8
.op1:
or esi, dword [r12 + Instruction.operand1 + operand.type]
; calculate size mask of op1.
; for reg and mem operands, this is 1 << .size.
; for imm operands, this is !(!0 << .size)
mov ecx, dword [r12 + Instruction.operand1 + operand.size]
mov edx, 1
shl edx, cl
cmp dword [r12 + Instruction.operand1 + operand.type], KIND_IMM
jne .size_done
xor edx, edx
lea ecx, [ecx - 1]
sub edx, 1
shl edx, cl
.size_done:
xor edx, eax
mov ecx, dword [r12 + Instruction.num_operands]
mov edi, dword [r12 + Instruction.mnemonic_offs]
call find_op_table_entry
test rax, rax
jz .done
mov r13, rax ; pointer to the op_table entry
; write the prefix, if any
cmp dword [r13 + 24], 0
je .rex
movzx edi, byte [r13 + 24]
mov esi, 1
call write_bytes
.rex:
; shuffle operands into reg-mem order
lea rdi, [r12 + Instruction.operand1]
lea rsi, [r12 + Instruction.operand2]
; discard rsi if it is not used in the encoding
test dword [r13 + OpEntry.encoding], ENC_DISCARD_BITS << 8
jz .no_discard
xor rsi, rsi
.no_discard:
test dword [r13 + OpEntry.encoding], ENC_M_BITS
jz .no_swap
xchg rdi, rsi
.no_swap:
sub rsp, 16
mov qword [rsp], rdi ; reg operand
mov qword [rsp + 8], rsi ; mem operand
; compute the REX byte, if any
call compute_rex
mov edi, eax
call write_non_zero_bytes
; write the opcode and any ENC_O bits
xor esi, esi
test dword [r13 + OpEntry.encoding], ENC_O_SIGNAL_BITS
jz .no_o_bits
mov rsi, qword [rsp]
mov esi, dword [rsi + operand.reg + register.num]
and esi, 0x7
.no_o_bits:
mov edi, dword [r13 + OpEntry.opcode]
call write_opcode_with_bits
test dword [r13 + OpEntry.encoding], ENC_O_SIGNAL_BITS | ENC_O_SIGNAL_BITS << 8
jnz .imm
; compute the ModRM and SIB bytes, if any
mov rdi, qword [rsp]
mov rsi, qword [rsp + 8]
mov edx, dword [r13 + OpEntry.reg_bits]
call compute_modrm_sib
push rax
shl edx, 8 ; shift SIB
or eax, edx ; combine modRM and SIB
mov edi, eax
call write_non_zero_bytes
pop rax
shl eax, 6
dec eax
cmp eax, 2
jge .imm
mov rdi, qword [rsp + 8]
mov edi, dword [rdi + operand.disp]
call write_disp
mov rdi, qword [rsp + 8]
test dword [rdi + operand.type], KIND_LABEL_FLAG
jz .imm
mov edi, dword [rdi + operand.disp]
lea rsi, [rbx - 4]
call push_lbl_ref_by_hash
.imm:
mov rdi, qword [rsp]
mov rsi, qword [rsp + 8]
add rsp, 16
; write immediate, if any
cmp dword [rsi + operand.type], KIND_IMM
je .imm1
mov rsi, rdi
cmp dword [rsi + operand.type], KIND_IMM
jne .done
.imm1:
push rsi
test dword [rsi + operand.type], KIND_LABEL_FLAG
jz .imm2
mov edi, dword [rsi + operand.disp]
mov rsi, rbx
call push_lbl_ref_by_hash
.imm2:
pop rsi
mov rdi, qword [rsi + operand_imm.imm]
mov esi, dword [r13 + OpEntry.op2_size]
call write_imm
.done:
mov rax, rbx ; bytes written
pop r13
pop r12
pop rbx
ret
write_imm:
jmp write_bytes
write_disp:
cmp edi, 0xff
seta sil
shl sil, 1
inc sil
movzx rsi, sil
jmp write_bytes
write_bytes:
call print_u8s_le
add rbx, rax
ret
write_opcode_with_bits:
call swap_bytes_and_shift_in_place
or rdi, rsi
jmp write_non_zero_bytes
;; writes the contents of the $rdi register until the first zero byte.
write_non_zero_bytes:
call print_non_zero_u8s_le
add rbx, rax
ret
;; compute the modRM and SIB bytes for the operands $rdi and $rsi, in reg-mem order, with the $rdx reg-bits
compute_modrm_sib:
xor eax, eax ; modrm
mov eax, edx
shl eax, 3 ; reg-bits
xor edx, edx ; sib
cmp dword [rdi + operand.type], KIND_REG
jne .check_mem
mov eax, dword [rdi + operand.reg + register.num]
and eax, 7
shl eax, 3 ; reg-bits
.check_mem:
test rsi, rsi
jz .done_mem
mov ecx, dword [rsi + operand.reg + register.num]
and ecx, 7
or eax, ecx ; r/m bits
or eax, 0xC0 ; mod = 11b
cmp dword [rsi + operand.type], KIND_REG
je .done_mem
and eax, 0x37 ; clear mod bits, mod = 00b
cmp dword [rsi + operand.type], KIND_MEM
jne .done_mem
cmp dword [rsi + operand.disp], 0
jne .sib ; mod = 01b or 10b
; if mod == 0 && r/m & 111 == 101 (rbp or r13), then we need a displacement, even if its 0
mov ecx, eax
and ecx, 7
cmp ecx, 5
jne .done_mem
.sib:
cmp dword [rsi + operand.disp], 0xff
seta cl
mov edx, 1
shl edx, cl
shl edx, 6 ; mod = 01b or 10b
or eax, edx
xor edx, edx ; sib
; copy r/m bits to SIB base bits
mov edx, eax
shr edx, 3
and edx, 7
and eax, 0b11000111 ; clear the r/m bits
or eax, 0b00100000 ; set the r/m bits to 100b (SIB follows)
mov edx, 0b00100000 ; index = 100b (no index)
mov ecx, dword [rsi + operand.scale]
and ecx, 3
shl ecx, 6 ; scale bits
or edx, ecx
mov ecx, dword [rsi + operand.index + register.num]
cmp ecx, -1
jne .done_mem
and ecx, 7
shl ecx, 3 ; index bits
or edx, ecx
.done_mem:
ret
;; compute the rex bits for the operands $rdi and $rsi, in reg-mem order
compute_rex:
xor eax, eax
test rdi, rdi
jz .check_mem
cmp dword [rdi + operand.type], KIND_REG
jne .check_mem
test dword [rdi + operand.reg + register.num], 8
setne dl
shl dl, 2 ; REX.R
or al, dl
cmp dword [rdi + operand.size], 3
setnb dl
shl dl, 3 ; REX.W
or al, dl
.check_mem:
test rsi, rsi
jz .done
test dword [rsi + operand.type], KIND_REG | KIND_MEM
jz .done
test dword [rsi + operand.reg + register.num], 8
setne dl
or al, dl ; REX.B
cmp dword [rsi + operand.size], 3
setnb dl
shl dl, 3 ; REX.W
or al, dl
test dword [rsi + operand.type], KIND_MEM
jz .done
mov ecx, dword [rsi + operand.index + register.num]
cmp ecx, -1
je .done
test ecx, 8
setne dl
shl dl, 1 ; REX.X
or al, dl
.done:
test al, al
setne dl
shl dl, 6 ; REX 4Xh
or al, dl
ret
;; parse buffer $rdi, bytes written in $rsi, return updated bytes written.
parse_line:
push rbx ; buf
push r12 ; line length
push rdi
mov rbx, rdi
mov r12, rsi ; bytes written
call skip_whitespace_rbx
cmp byte [rbx], '@'
je .label
mov rdi, rbx
sub rsp, 72
mov rsi, rsp
call try_parse_inst
test eax, eax
jz .done_inst
lea rdi, [rsp]
call encode_inst
add r12, rax
.done_inst:
add rsp, 72
jmp .done
.label:
inc rbx ; skip the leading '@'
mov rdi, rbx
call try_parse_label
test eax, eax
jz .done
mov rdi, rbx
mov rsi, rax
add rbx, rax
mov rdx, r12 ; label offset
call push_lbl
.done:
mov rax, r12
pop rdi
pop r12
pop rbx
ret
;; Labels
@ -1473,7 +2153,11 @@ struc LabelTable
.labels resq 1
endstruc
section .data
labels dq 0
label_refs dq 0
section .text
;; calculates the hash of a byte sequence in $rdi of length $rsi, returning it in $eax
global fasthash
fasthash:
@ -1490,6 +2174,36 @@ fasthash:
.done:
ret
init_label_tables:
mov edi, 0x100
mov esi, 8
call heap_alloc
lea rdi, [rel labels]
mov dword [rdi + LabelTable.size], 0
mov dword [rdi + LabelTable.cap], 0x100 / 8
mov qword [rdi + LabelTable.labels], rax
mov edi, 0x100
mov esi, 8
call heap_alloc
lea rdi, [rel label_refs]
mov dword [rdi + LabelTable.size], 0
mov dword [rdi + LabelTable.cap], 0x100 / 8
mov qword [rdi + LabelTable.labels], rax
ret
;; push label hash $edi with offset $esi into the label ref table.
push_lbl_ref_by_hash:
push rbx
push r12
push r13
mov rbx, [rel label_refs]
mov r13d, esi ; label offset
mov r12d, edi ; label hash
jmp push_lbl_common.hash_in_r12d
;; push label $rdi with length $rsi and offset $rdx into the label ref table
push_lbl_ref:
mov rcx, [rel label_refs]
@ -1508,6 +2222,7 @@ push_lbl_common:
call fasthash
mov r12d, eax ; store the hash in r12d
.hash_in_r12d:
mov eax, dword [rbx + LabelTable.size]
cmp eax, dword [rbx + LabelTable.cap]

View file

@ -7,6 +7,13 @@ unsafe extern "C" {
#[link_name = "try_parse_operand"]
unsafe fn try_parse_operand_impl(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
#[link_name = "try_parse_inst"]
unsafe fn try_parse_inst_impl(_: *const u8, _: *mut Instruction)
-> (usize, *const Instruction);
#[link_name = "encode_inst"]
unsafe fn encode_inst_impl(_: *mut Instruction) -> usize;
unsafe fn fasthash(_: *const u8, _: usize) -> u32;
#[link_name = "buf"]
@ -26,9 +33,9 @@ enum OperandKind {
None = 0,
Reg = 1,
Mem = 2,
Imm = 3,
MemLabel = 4,
ImmLabel = 5,
Imm = 4,
MemLabel = 2 | 8,
ImmLabel = 4 | 8,
}
#[repr(C, align(8))]
@ -56,7 +63,7 @@ impl Operand {
fn label(name: &[u8]) -> Self {
let mut this = Self {
kind: OperandKind::ImmLabel,
size: 0,
size: 2,
reg: Register::invalid(),
index: Register::invalid(),
scale: 0,
@ -100,6 +107,26 @@ impl Operand {
}
}
#[repr(C)]
#[derive(Debug, PartialEq, Eq)]
struct Instruction {
mnemonic_offset: i32,
num_operands: u32,
operands: [Operand; 2],
}
impl Instruction {
fn parse(bytes: &[u8]) -> Option<(usize, Self)> {
let mut inst = core::mem::MaybeUninit::<Instruction>::uninit();
let (n, ptr) = unsafe { try_parse_inst_impl(bytes.as_ptr(), inst.as_mut_ptr()) };
if n == 0 {
None
} else {
Some((n, unsafe { inst.assume_init() }))
}
}
}
impl Register {
fn new(num: u32, size: u32) -> Self {
Self {
@ -110,6 +137,10 @@ impl Register {
fn invalid() -> Self {
Self { num: !0, size: !0 }
}
fn rbp() -> Self {
Self { num: 5, size: 0 }
}
}
#[cfg(test)]
@ -124,6 +155,44 @@ mod tests {
}
}
#[test]
fn parse_inst() {
let res = Instruction::parse(b"mov byte [rax], 4\0");
// assert_eq!(
// res,
// Some((
// 12,
// Instruction {
// mnemonic_offset: 0,
// num_operands: 2,
// operands: [
// Operand {
// kind: OperandKind::Reg,
// size: 3,
// reg: Register::new(0, 8),
// index: Register::invalid(),
// scale: 0,
// disp: 0,
// },
// Operand {
// kind: OperandKind::Reg,
// size: 3,
// reg: Register::new(3, 8),
// index: Register::invalid(),
// scale: 0,
// disp: 0,
// },
// ],
// }
// ))
// );
unsafe {
let mut inst = res.unwrap().1;
encode_inst_impl((&raw mut inst).cast());
}
}
#[test]
fn parse_mem() {
let mut op = MaybeUninit::<Operand>::uninit();
@ -169,7 +238,7 @@ mod tests {
&Operand {
kind: OperandKind::Mem,
size: 1,
reg: Register::invalid(),
reg: Register::rbp(),
index: Register::new(8, 8),
scale: 8,
disp: 5,
@ -219,7 +288,7 @@ mod tests {
Operand {
kind: OperandKind::Mem,
size: 1,
reg: Register::invalid(),
reg: Register::rbp(),
index: Register::new(8, 8),
scale: 8,
disp: 5,