parsing operands
This commit is contained in:
parent
9bd36905af
commit
07435f5839
|
|
@ -2,6 +2,9 @@
|
|||
;; It's also the first stage in this bootstrapping experiment that will
|
||||
;; consume proper mnemonic assembly, rather than hex input.
|
||||
|
||||
section .bss
|
||||
buf resb 0x100
|
||||
labels resb 0x1000
|
||||
section .data
|
||||
_start_lbl db "_start", 0
|
||||
|
||||
|
|
@ -42,6 +45,214 @@ _mov:
|
|||
db 2
|
||||
dq instr_table - _mov_handler
|
||||
|
||||
;; calculates a quick hash of a byte sequence given by rdi (pointer) and rsi (length)
|
||||
fasthash:
|
||||
mov eax, 0x811c9dc5 ; FNV offset basis
|
||||
.fashhash_loop:
|
||||
test rsi, rsi
|
||||
jz .fashhash_exit
|
||||
movzx ecx, byte [rdi] ; get next byte
|
||||
xor eax, ecx
|
||||
imul eax, 0x1000193 ; FNV prime
|
||||
inc rdi
|
||||
dec rsi
|
||||
jmp .fashhash_loop
|
||||
|
||||
.fashhash_exit:
|
||||
ret
|
||||
|
||||
;; push the label pointed at by rdi with length rsi, and the current file offset onto the labels array.
|
||||
push_lbl:
|
||||
push r12
|
||||
lea r12, [rel labels]
|
||||
mov rax, dword [r12 + 0x1000 - 4] ; get current label count
|
||||
cmp rax, 341
|
||||
jge panic
|
||||
lea rax, [rax + rax*2]
|
||||
shl rax, 2 ; $rax = num_labels + size_of::<label_entry>()
|
||||
add r12, rax
|
||||
call fasthash
|
||||
mov qword [r12], rax ; store hash in labels array
|
||||
mov dword [r12 + 8], -1 ; initialize file offset to -1 (invalid)
|
||||
inc dword [r12 + 0x1000 - 4] ; increment label count
|
||||
pop r12
|
||||
ret
|
||||
|
||||
;; searches for the offset of the label with the name pointed at by rdi with length rsi, and returns it in rax. If the label is not found, dies.
|
||||
find_lbl_ptr:
|
||||
call fasthash
|
||||
lea rdx, [r15 + 0x5010] ; rdx = pointer to start of labels array
|
||||
mov ebx, dword [r15 + 0x6000]
|
||||
.find_lbl_loop:
|
||||
test rbx, rbx
|
||||
jz .find_lbl_not_found
|
||||
dec rbx
|
||||
cmp qword [rdx], rax
|
||||
je .find_lbl_found
|
||||
add rdx, 12 ; move to next label entry
|
||||
jmp .find_lbl_loop
|
||||
|
||||
push_or_find_lbl:
|
||||
call fasthash
|
||||
push rax
|
||||
mov rdi, rax
|
||||
call find_lbl_ptr
|
||||
test rax, rax
|
||||
jz .push
|
||||
add rsp, 8
|
||||
ret
|
||||
.push:
|
||||
pop rdi
|
||||
call push_lbl
|
||||
ret
|
||||
|
||||
.find_lbl_not_found:
|
||||
xor rax, rax
|
||||
ret
|
||||
.find_lbl_found:
|
||||
mov rax, rdx
|
||||
ret
|
||||
|
||||
find_lbl_offset:
|
||||
call find_lbl_ptr
|
||||
test rax, rax
|
||||
jz .not_found
|
||||
mov eax, dword [rax + 8] ; get file offset of label
|
||||
ret
|
||||
.not_found:
|
||||
xor rdi, rdi
|
||||
call panic_abort
|
||||
|
||||
set_lbl_offset:
|
||||
push rsi
|
||||
call find_lbl_ptr
|
||||
pop rsi
|
||||
mov dword [rax + 8], esi ; set file offset of label
|
||||
ret
|
||||
|
||||
is_alpha:
|
||||
movzx edi, dil
|
||||
and edi, 2097119
|
||||
add edi, -65
|
||||
cmp edi, 26
|
||||
setb al
|
||||
ret
|
||||
|
||||
;; returns 1 if $dil is a valid identifier character (alphanumeric, '-' or '_'), and 0 otherwise.
|
||||
;; clobbers rdi, rax, rcx
|
||||
is_id_cont:
|
||||
movzx rdi, dil
|
||||
lea eax, [rdi - 48]
|
||||
cmp eax, 10
|
||||
setb al
|
||||
mov ecx, edi
|
||||
and ecx, 2097119
|
||||
add ecx, -65
|
||||
cmp ecx, 26
|
||||
setb cl
|
||||
or al, cl
|
||||
cmp edi, 45 ; '-'
|
||||
sete cl
|
||||
or al, cl
|
||||
cmp edi, 95 ; '_'
|
||||
sete cl
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
is_digit:
|
||||
movzx edi, dil
|
||||
add edi, -48
|
||||
cmp edi, 10
|
||||
setb al
|
||||
ret
|
||||
|
||||
;; converts char $dil to a digit with radix $rsi, returning it in $edx. $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
||||
to_digit:
|
||||
lea eax, [rsi - 2]
|
||||
cmp eax, 35
|
||||
jae .invalid
|
||||
movzx rdi, dil
|
||||
lea edx, [rdi - 65] ; 'A' = 65
|
||||
and edx, -33 ; convert to uppercase
|
||||
add edx, 10 ; 'A' should map to 10
|
||||
lea eax, [rdi - 48] ; '0' = 48
|
||||
cmp esi, 11
|
||||
cmovb edx, eax ; if radix <= 10, then take the difference from '0'
|
||||
cmp edi, 58
|
||||
cmovb edx, eax ; or if char < '9', then take the difference from '0'
|
||||
xor eax, eax
|
||||
cmp edx, esi
|
||||
setb al ; al = edx < radix
|
||||
ret
|
||||
.invalid:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
parse_num:
|
||||
sub rsp, 24
|
||||
mov qword [rsp], 0 ; acc
|
||||
mov qword [rsp + 8], rdi ; source iterator
|
||||
mov dword [rsp + 16], 10 ; radix
|
||||
call peekc
|
||||
cmp al, `-`
|
||||
jne .skip_sign
|
||||
mov qword [rsp], -1 ; acc = -1
|
||||
call consuming_peekc
|
||||
.skip_sign:
|
||||
cmp al, `0`
|
||||
jne .skip_radix
|
||||
call consuming_peekc
|
||||
cmp al, `x`
|
||||
jne .skip_radix
|
||||
mov dword [rsp + 16], 16 ; radix = 16
|
||||
call consuming_peekc
|
||||
.skip_radix:
|
||||
mov dil, al
|
||||
call to_digit
|
||||
test al, al
|
||||
jz .done
|
||||
mov rax, qword [rsp] ; acc
|
||||
mov rsi, qword [rsp + 16] ; radix
|
||||
mov rcx, rdx
|
||||
imul rax, rsi
|
||||
add rax, rcx
|
||||
mov qword [rsp], rax ; acc = acc * radix + digit
|
||||
mov rdi, qword [rsp + 8] ; source iterator
|
||||
call consuming_peekc
|
||||
jmp .skip_radix
|
||||
.done:
|
||||
mov rax, qword [rsp] ; move the result into rax
|
||||
add rsp, 24
|
||||
ret
|
||||
|
||||
parse_label:
|
||||
push r12
|
||||
lea r12, [rel buf]
|
||||
sub rsp, 8
|
||||
mov qword [rsp], rdi ; source iterator
|
||||
call consuming_peekc
|
||||
mov dil, al
|
||||
call is_alpha
|
||||
test al, al
|
||||
jz .done
|
||||
mov byte [r12], al
|
||||
inc r12
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call consuming_peekc
|
||||
mov dil, al
|
||||
call is_id_cont
|
||||
test al, al
|
||||
jz .done
|
||||
jmp .loop
|
||||
.done:
|
||||
lea rdi, [rel buf]
|
||||
mov rsi, r12
|
||||
sub rsi, rdi
|
||||
call push_or_find_lbl
|
||||
add rsp, 8
|
||||
pop r12
|
||||
ret
|
||||
|
||||
;; @param lhs: (rdi, rsi)
|
||||
;; @param rhs: (rdx, rcx)
|
||||
;; @return al
|
||||
|
|
@ -83,8 +294,27 @@ read_file:
|
|||
|
||||
; ;; returns the next byte in the input stream and advances the read position.
|
||||
; getc:
|
||||
extern panic_abort
|
||||
extern peekc
|
||||
extern getc
|
||||
extern consuming_peekc
|
||||
|
||||
skip_whitespaces:
|
||||
push rdi
|
||||
call peekc
|
||||
.loop:
|
||||
cmp al, ' '
|
||||
je .read
|
||||
cmp al, `\t`
|
||||
je .read
|
||||
cmp al, `\n`
|
||||
je .read
|
||||
pop rdi
|
||||
ret
|
||||
|
||||
.read:
|
||||
call consuming_peekc
|
||||
jmp .loop
|
||||
|
||||
;; converts char $dil to a digit with radix $rsi, returning it in $edx. $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
||||
to_digit:
|
||||
|
|
@ -155,6 +385,8 @@ parse_digits:
|
|||
; R15,
|
||||
;; }
|
||||
|
||||
|
||||
|
||||
;; parses a non-extended GPR operand (e.g. a register name that isn't r8-r15)
|
||||
;; returns the register number in rax and the width in rdx
|
||||
;; if $rdx == 0, then parsing failed (invalid register name)
|
||||
|
|
@ -342,16 +574,187 @@ parse_reg:
|
|||
ret
|
||||
|
||||
|
||||
global parse_packed_reg
|
||||
parse_packed_reg:
|
||||
call parse_reg
|
||||
test rdx, rdx
|
||||
jz .invalid
|
||||
shl rdx, 4
|
||||
or rax, rdx
|
||||
ret
|
||||
.invalid:
|
||||
xor rax, rax
|
||||
ret
|
||||
|
||||
;; enum Operand {
|
||||
;; Reg(Reg, width: u8),
|
||||
;; Imm(u32, width: u8),
|
||||
;; Mem(width: u8, base: Option<Reg>, index: Option<(Reg, scale: u8)>, disp: Option<u32>),
|
||||
;; }
|
||||
|
||||
;; operands can be registers, memory operands, immediates or labels
|
||||
;; labels start with a '
|
||||
;; immediates start with a digit or a '-'
|
||||
;; memory operands may have the forms: [<label>] | [reg ( +reg*scale)? (+ disp)?] | [+reg*scale (+ disp)?]
|
||||
parse_operand:
|
||||
call peekc
|
||||
cmp al, `'`
|
||||
je .label
|
||||
cmp al, `[`
|
||||
je .mem
|
||||
cmp al, `-`
|
||||
je .imm
|
||||
cmp al, `9`
|
||||
jb .imm
|
||||
jmp .reg
|
||||
.reg:
|
||||
call parse_packed_reg
|
||||
test rax, rax
|
||||
jz .invalid
|
||||
mov rdx, rax
|
||||
mov rax, 0 ; operand type = reg
|
||||
ret
|
||||
.imm:
|
||||
call parse_num
|
||||
mov rdx, rax
|
||||
mov rax, 1 ; operand type = imm
|
||||
ret
|
||||
.label:
|
||||
call parse_label
|
||||
mov edx, dword [rax + 8] ; get the offset of the label
|
||||
mov rax, 3 ; operand type = label
|
||||
ret
|
||||
.mem:
|
||||
sub rsp, 40
|
||||
mov byte [rsp], 0xff ; base
|
||||
mov byte [rsp + 1], 0xff ; index
|
||||
mov byte [rsp + 2], 0 ; scale
|
||||
mov qword [rsp + 4], 0 ; disp
|
||||
mov qword [rsp + 32], rdi ; source iterator
|
||||
call getc
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call peekc
|
||||
cmp al, `'`
|
||||
je .mem_label
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call parse_packed_reg
|
||||
test rax, rax
|
||||
jz .invalid
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call skip_whitespaces
|
||||
call peekc
|
||||
cmp al, `+`
|
||||
je .index_part
|
||||
cmp al, `]`
|
||||
je .mem_done
|
||||
cmp al, `*`
|
||||
jne .invalid
|
||||
mov al, byte [rsp]
|
||||
mov byte [rsp + 1], al ; index = base if there's no base
|
||||
.scale_part:
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call getc ; consume the '*'
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call skip_whitespaces
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call parse_num
|
||||
bsr eax, eax ; TODO: maybe?
|
||||
mov byte [rsp + 2], al ; scale
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call skip_whitespaces
|
||||
cmp al, `+`
|
||||
je .disp_part
|
||||
cmp al, `]`
|
||||
je .mem_done
|
||||
jmp .invalid
|
||||
.index_part:
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call skip_whitespaces
|
||||
; check if displacement
|
||||
cmp al, `-`
|
||||
je .disp_part
|
||||
cmp al, `9`
|
||||
jb .disp_part
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call parse_packed_reg
|
||||
test rax, rax
|
||||
jz .invalid
|
||||
mov byte [rsp + 1], al ; index
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call skip_whitespaces
|
||||
cmp al, `+`
|
||||
je .disp_part
|
||||
cmp al, `]`
|
||||
je .mem_done
|
||||
cmp al, `*`
|
||||
jne .invalid
|
||||
jmp .scale_part
|
||||
.disp_part:
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call parse_num
|
||||
mov qword [rsp + 4], rax ; disp
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call skip_whitespaces
|
||||
cmp al, `]`
|
||||
je .mem_done
|
||||
jmp .invalid
|
||||
.mem_done:
|
||||
mov rax, 2 ; operand type = mem
|
||||
mov rdx, qword [rsp] ; 8b8i2s32d
|
||||
add rsp, 40
|
||||
ret
|
||||
.mem_label:
|
||||
mov rdi, qword [rsp + 32] ; restore source iterator
|
||||
call parse_label
|
||||
mov rdx, rax
|
||||
mov rax, 4 ; operand type = mem_label
|
||||
add rsp, 40
|
||||
ret
|
||||
.invalid:
|
||||
xor rdi, rdi
|
||||
call panic_abort
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;; mov r/m, r
|
||||
;; mov r, r/m
|
||||
;; mov r/m, imm
|
||||
_mov_handler:
|
||||
sub rsp, 40
|
||||
mov qword [rsp], rdi ; source iterator
|
||||
|
||||
call skip_whitespaces
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call parse_operand
|
||||
mov qword [rsp + 8], rax ; operand 1 type
|
||||
mov qword [rsp + 16], rdx ; operand 1 value
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call skip_whitespaces
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call peekc
|
||||
cmp al, `,`
|
||||
jne .invalid
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call getc
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call skip_whitespaces
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call parse_operand
|
||||
mov qword [rsp + 24], rax ; operand 2 type
|
||||
mov qword [rsp + 32], rdx ; operand 2 value
|
||||
|
||||
ret
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -146,3 +146,70 @@ fn test_parse_gpr() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Register {
|
||||
A = 0,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
Src,
|
||||
Dst,
|
||||
Sp,
|
||||
Bp,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15,
|
||||
}
|
||||
enum RegisterSize {
|
||||
Byte = 1,
|
||||
Word = 2,
|
||||
Dword = 4,
|
||||
Qword = 8,
|
||||
HighByte = 3,
|
||||
None = 0,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PackedRegister(u8);
|
||||
impl From<PackedRegister> for Option<(Register, RegisterSize)> {
|
||||
fn from(value: PackedRegister) -> Self {
|
||||
let reg_bits = value.0 & 0x00F;
|
||||
let size_bits = (value.0 >> 4) & 0x0F;
|
||||
let size = match size_bits {
|
||||
1 => RegisterSize::Byte,
|
||||
2 => RegisterSize::Word,
|
||||
4 => RegisterSize::Dword,
|
||||
8 => RegisterSize::Qword,
|
||||
3 => RegisterSize::HighByte,
|
||||
0 => return None,
|
||||
_ => panic!("Invalid register size"),
|
||||
};
|
||||
|
||||
let reg = match reg_bits {
|
||||
0 => Register::A,
|
||||
1 => Register::B,
|
||||
2 => Register::C,
|
||||
3 => Register::D,
|
||||
4 => Register::Sp,
|
||||
5 => Register::Bp,
|
||||
6 => Register::Src,
|
||||
7 => Register::Dst,
|
||||
8 => Register::R8,
|
||||
9 => Register::R9,
|
||||
10 => Register::R10,
|
||||
11 => Register::R11,
|
||||
12 => Register::R12,
|
||||
13 => Register::R13,
|
||||
14 => Register::R14,
|
||||
15 => Register::R15,
|
||||
_ => panic!("Invalid register number"),
|
||||
};
|
||||
|
||||
Some((reg, size))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue