diff --git a/stages/as0/as0.asm b/stages/as0/as0.asm index c6b4502..719c89b 100644 --- a/stages/as0/as0.asm +++ b/stages/as0/as0.asm @@ -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] diff --git a/stages/as0/test.rs b/stages/as0/test.rs index 02a7c12..e73e2d0 100644 --- a/stages/as0/test.rs +++ b/stages/as0/test.rs @@ -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::::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::::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,