as0 works

This commit is contained in:
janis 2026-07-13 23:33:07 +02:00
parent 2f7282577a
commit 4690b3c7ec
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
3 changed files with 554 additions and 101 deletions

View file

@ -8,8 +8,11 @@ as0.o: as0.asm
test: test.bin test: test.bin
./test.bin ./test.bin
as0: as0.o
ld -m elf_x86_64 -e _entry_as0 -o as0 as0.o
print_table: as0.o print_table: as0.o
ld -m elf_x86_64 -e _entry_print_table -o print_table as0.o ld -m elf_x86_64 -e _entry_print_table -o print_table as0.o
clean: clean:
rm -f as0.o test.bin rm -f as0.o test.bin as0 print_table

View file

@ -2,16 +2,8 @@
;; It's also the first stage in this bootstrapping experiment that will ;; It's also the first stage in this bootstrapping experiment that will
;; consume proper mnemonic assembly, rather than hex input. ;; consume proper mnemonic assembly, rather than hex input.
struc OFile
.fd resd 1
.offset resd 1
endstruc
section .bss section .bss
global buf global buf
ifile resb 0x20
ofile resb 0x20
buf resb 0x100
heap resq 1 heap resq 1
section .data section .data
@ -49,8 +41,8 @@ mnemonics:
mnemonics_table: mnemonics_table:
MN_MOV db "mov", 0 MN_MOV db "mov", 0
MN_TEST db "test", 0 MN_TEST db "test", 0
MN_XOR db "xor", 0
MN_OR db "or", 0 MN_OR db "or", 0
MN_XOR db "xor", 0
MN_AND db "and", 0 MN_AND db "and", 0
MN_SHL db "shl", 0 MN_SHL db "shl", 0
MN_SHR db "shr", 0 MN_SHR db "shr", 0
@ -406,7 +398,6 @@ op_table_end:
section .text section .text
panic_abort: panic_abort:
mov rdi, 1
mov rax, 60 mov rax, 60
syscall syscall
@ -867,6 +858,9 @@ print_op_table:
pop rbx pop rbx
ret ret
;; Common Library Functions
;; rdi: *u8 ;; rdi: *u8
strlen: strlen:
xor rax, rax xor rax, rax
@ -1036,6 +1030,7 @@ to_digit:
ret ret
exit: exit:
xor edi, edi
mov rax, 60 mov rax, 60
syscall syscall
@ -1305,6 +1300,8 @@ try_parse_num:
skip_whitespace_rbx: skip_whitespace_rbx:
mov al, byte [rbx] mov al, byte [rbx]
test al, al
jz .done
cmp al, ' ' cmp al, ' '
ja .done ja .done
inc rbx inc rbx
@ -1349,14 +1346,14 @@ try_parse_mem:
mov edi, dword [rbx] mov edi, dword [rbx]
xor eax, eax xor eax, eax
mov dword [r12 + operand.size], 2 ; 16-bit mov dword [r12 + operand.size], 2 ; 32-bit
cmp dl, 'q' cmp dl, 'q'
sete al sete al
cmp dl, 'd' cmp dl, 'd'
sete dl sete dl
or al, dl or dl, al
jz .not_mem jz .not_mem
add dword [r12 + operand.size], eax ; 32-bit? add dword [r12 + operand.size], eax ; 64-bit?
cmp edi, 'word' cmp edi, 'word'
jne .not_mem jne .not_mem
@ -1512,6 +1509,8 @@ try_parse_operand:
mov dword [r12 + operand.disp], 0 mov dword [r12 + operand.disp], 0
mov al, byte [rbx] mov al, byte [rbx]
test al, al
jz .done
cmp al, '[' cmp al, '['
je .mem je .mem
cmp al, `'` cmp al, `'`
@ -1533,10 +1532,10 @@ try_parse_operand:
bsr rcx, rdx ; # of bits required to represent the immediate bsr rcx, rdx ; # of bits required to represent the immediate
mov edx, ecx mov edx, ecx
cmp edx, 1 shr edx, 3 ; divide by 8 to get the number of bytes required
adc edx, -1 ; saturating sub jz .done
bsr edx, edx ; log2 of the (# of bits - 1) + 1 bsr edx, edx ; log2 of the (# of bytes) + 1
inc edx inc edx
cmp ecx, 2 cmp ecx, 2
@ -1614,6 +1613,7 @@ try_parse_label:
pop rbx pop rbx
ret ret
;; Instruction
struc Instruction struc Instruction
.mnemonic_offs resd 1 .mnemonic_offs resd 1
@ -1633,6 +1633,8 @@ try_parse_inst:
mov dword [r13 + Instruction.num_operands], 0 mov dword [r13 + Instruction.num_operands], 0
mov dword [r13 + Instruction.mnemonic_offs], 0 mov dword [r13 + Instruction.mnemonic_offs], 0
mov qword [r13 + Instruction.operand1], 0
mov qword [r13 + Instruction.operand2], 0
call skip_whitespace_rbx call skip_whitespace_rbx
mov rdi, rbx mov rdi, rbx
@ -1697,7 +1699,6 @@ try_parse_inst:
ret ret
;; find the op_table entry returning the pointer to the entry in $rax, or 0 if not found. ;; find the op_table entry returning the pointer to the entry in $rax, or 0 if not found.
;; inputs: ;; inputs:
;; $edi: mnemonic offset ;; $edi: mnemonic offset
@ -1728,10 +1729,15 @@ find_op_table_entry:
jne .next jne .next
mov r8d, dword [rax + 16] ; encoding mov r8d, dword [rax + 16] ; encoding
cmp r8d, esi
je .found
mov r9d, esi mov r9d, esi
test r8d, 0xff00
jz .enc1
and r9d, 0xff00 and r9d, 0xff00
and r9d, r8d and r9d, r8d
jz .next jz .next
.enc1:
mov r9d, esi mov r9d, esi
and r9d, 0xff and r9d, 0xff
and r9d, r8d and r9d, r8d
@ -1754,7 +1760,7 @@ encode_inst:
push r12 push r12
push r13 push r13
xor ebx, ebx ; bytes written mov rbx, rsi ; bytes written
mov r12, rdi ; inst ptr mov r12, rdi ; inst ptr
; calculate the encoding ; calculate the encoding
@ -1766,7 +1772,7 @@ encode_inst:
; calculate size mask of op2. ; calculate size mask of op2.
; for reg and mem operands, this is 1 << .size. ; for reg and mem operands, this is 1 << .size.
; for imm operands, this is !(!0 << .size) ; for imm operands, this is (!0 << .size) && 0xff
mov ecx, dword [r12 + Instruction.operand2 + operand.size] mov ecx, dword [r12 + Instruction.operand2 + operand.size]
mov eax, 1 mov eax, 1
shl eax, cl shl eax, cl
@ -1774,9 +1780,9 @@ encode_inst:
cmp dword [r12 + Instruction.operand2 + operand.type], KIND_IMM cmp dword [r12 + Instruction.operand2 + operand.type], KIND_IMM
jne .size_done_op2 jne .size_done_op2
xor eax, eax xor eax, eax
lea ecx, [ecx - 1]
sub eax, 1 sub eax, 1
shl eax, cl shl eax, cl
and eax, 0xf
.size_done_op2: .size_done_op2:
shl eax, 8 shl eax, 8
@ -1802,16 +1808,16 @@ encode_inst:
; calculate size mask of op1. ; calculate size mask of op1.
; for reg and mem operands, this is 1 << .size. ; for reg and mem operands, this is 1 << .size.
; for imm operands, this is !(!0 << .size) ; for imm operands, this is (!0 << .size) & 0xf
mov ecx, dword [r12 + Instruction.operand1 + operand.size] mov ecx, dword [r12 + Instruction.operand1 + operand.size]
mov edx, 1 mov edx, 1
shl edx, cl shl edx, cl
cmp dword [r12 + Instruction.operand1 + operand.type], KIND_IMM cmp dword [r12 + Instruction.operand1 + operand.type], KIND_IMM
jne .size_done jne .size_done
xor edx, edx xor edx, edx
lea ecx, [ecx - 1]
sub edx, 1 sub edx, 1
shl edx, cl shl edx, cl
and edx, 0xf
.size_done: .size_done:
xor edx, eax xor edx, eax
@ -1822,8 +1828,13 @@ encode_inst:
jz .done jz .done
mov r13, rax ; pointer to the op_table entry mov r13, rax ; pointer to the op_table entry
mov eax, dword [r13 + OpEntry.op1_size]
mov dword [r12 + Instruction.operand1 + operand.size], eax
mov eax, dword [r13 + OpEntry.op2_size]
mov dword [r12 + Instruction.operand2 + operand.size], eax
; write the prefix, if any ; write the prefix, if any
cmp dword [r13 + 24], 0 cmp dword [r13 + OpEntry.prefix], 0
je .rex je .rex
movzx edi, byte [r13 + 24] movzx edi, byte [r13 + 24]
@ -1868,6 +1879,9 @@ encode_inst:
mov edi, dword [r13 + OpEntry.opcode] mov edi, dword [r13 + OpEntry.opcode]
call write_opcode_with_bits call write_opcode_with_bits
cmp dword [r13 + OpEntry.num_operands], 0
jz .ref
test dword [r13 + OpEntry.encoding], ENC_O_SIGNAL_BITS | ENC_O_SIGNAL_BITS << 8 test dword [r13 + OpEntry.encoding], ENC_O_SIGNAL_BITS | ENC_O_SIGNAL_BITS << 8
jnz .imm jnz .imm
@ -1884,48 +1898,68 @@ encode_inst:
call write_non_zero_bytes call write_non_zero_bytes
pop rax pop rax
mov cl, al
and cl, 0b11000111
cmp cl, 0b00000101 ; if modRM.mod == 00b and modRM.rm == 101b, then we have a disp32
jne .disp
xor edi, edi
mov esi, 4
call write_bytes
jmp .imm
.disp:
shr eax, 6 shr eax, 6
dec eax dec eax
cmp eax, 2 cmp eax, 2
jge .imm jae .imm
mov rdi, qword [rsp + 8] mov rdi, qword [rsp + 8]
mov edi, dword [rdi + operand.disp] mov edi, dword [rdi + operand.disp]
call write_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: .imm:
xor eax, eax ; clear return
mov rdi, qword [rsp] mov rdi, qword [rsp]
mov rsi, qword [rsp + 8] mov rsi, qword [rsp + 8]
add rsp, 16
; write immediate, if any ; write immediate, if any
cmp dword [rsi + operand.type], KIND_IMM test dword [rsi + operand.type], KIND_IMM
je .imm1 jnz .imm1
mov rsi, rdi mov rsi, rdi
cmp dword [rsi + operand.type], KIND_IMM test dword [rsi + operand.type], KIND_IMM
jne .done jz .ref
.imm1: .imm1:
push rsi push rsi
test dword [rsi + operand.type], KIND_LABEL_FLAG test dword [rsi + operand.type], KIND_LABEL_FLAG
jz .imm2 jz .imm2
mov edi, dword [rsi + operand.disp] mov edi, dword [rsi + operand_imm.imm] ; hash
mov rsi, rbx mov rsi, rbx ; write offset
lea rdx, [rbx + 4] ; disp offset (after the imm32 is written)
call push_lbl_ref_by_hash call push_lbl_ref_by_hash
.imm2: .imm2:
pop rsi pop rsi
mov rdi, qword [rsi + operand_imm.imm] mov rdi, qword [rsi + operand_imm.imm]
mov esi, dword [r13 + OpEntry.op2_size] mov esi, dword [rsi + operand.size]
call write_imm call write_imm
.ref:
mov rdi, qword [rsp + 8]
add rsp, 16
test dword [rdi + operand.type], KIND_LABEL_FLAG
jz .done
test dword [rdi + operand.type], KIND_MEM
jz .done
lea rsi, [rbx - 4]
sub rsi, rax ; write offset (before the immediate)
mov rdx, rbx ; disp offset
mov edi, dword [rdi + operand.disp]
call push_lbl_ref_by_hash
.done: .done:
mov rax, rbx ; bytes written mov rax, rbx ; bytes written
pop r13 pop r13
@ -1945,19 +1979,27 @@ write_disp:
jmp write_bytes jmp write_bytes
write_bytes: write_bytes:
call print_u8s_le push rdi
lea rdi, [rsp]
call ofile_write_bytes
add rbx, rax add rbx, rax
pop rdi
ret ret
write_opcode_with_bits: write_opcode_with_bits:
call swap_bytes_and_shift_in_place
or rdi, rsi or rdi, rsi
call swap_bytes_and_shift_in_place
jmp write_non_zero_bytes jmp write_non_zero_bytes
;; writes the contents of the $rdi register until the first zero byte. ;; writes the contents of the $rdi register until the first zero byte.
write_non_zero_bytes: write_non_zero_bytes:
call print_non_zero_u8s_le push rdi
lea rdi, [rsp]
call strlen
mov esi, eax
call ofile_write_bytes
add rbx, rax add rbx, rax
pop rdi
ret ret
;; compute the modRM and SIB bytes for the operands $rdi and $rsi, in reg-mem order, with the $rdx reg-bits ;; compute the modRM and SIB bytes for the operands $rdi and $rsi, in reg-mem order, with the $rdx reg-bits
@ -1986,14 +2028,23 @@ compute_modrm_sib:
je .done_mem je .done_mem
and eax, 0x37 ; clear mod bits, mod = 00b and eax, 0x37 ; clear mod bits, mod = 00b
; rip-relative is mod = 00b, r/m = 101b, and no SIB byte, even though disp32
cmp dword [rsi + operand.type], KIND_MEM_LABEL
je .done_mem
test dword [rsi + operand.type], KIND_MEM test dword [rsi + operand.type], KIND_MEM
jz .done_mem jz .done_mem
cmp dword [rsi + operand.disp], 0 cmp dword [rsi + operand.disp], 0
jne .sib ; mod = 01b or 10b 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 mov ecx, eax
and ecx, 7 and ecx, 7
; if mod == 0 and r/m == 100b (RSP or R12), then we need a SIB byte, but no displacement
cmp ecx, 4
je .sib_no_disp
; if mod == 0 && r/m & 111 == 101 (rbp or r13), then we need a displacement, even if its 0
cmp ecx, 5 cmp ecx, 5
jne .done_mem jne .done_mem
@ -2005,16 +2056,16 @@ compute_modrm_sib:
shl edx, 6 ; mod = 01b or 10b shl edx, 6 ; mod = 01b or 10b
or eax, edx or eax, edx
.sib_no_disp:
xor edx, edx ; sib xor edx, edx ; sib
; copy r/m bits to SIB base bits ; copy r/m bits to SIB base bits
mov edx, eax mov edx, eax
shr edx, 3
and edx, 7 and edx, 7
and eax, 0b11000111 ; clear the r/m bits and eax, 0b11111000 ; clear the r/m bits
or eax, 0b00100000 ; set the r/m bits to 100b (SIB follows) or eax, 0b00000100 ; set the r/m bits to 100b (SIB follows)
mov edx, 0b00100000 ; index = 100b (no index) or edx, 0b00100000 ; index = 100b (no index)
mov ecx, dword [rsi + operand.scale] mov ecx, dword [rsi + operand.scale]
and ecx, 3 and ecx, 3
@ -2032,8 +2083,6 @@ compute_modrm_sib:
.done_mem: .done_mem:
ret ret
;; compute the rex bits for the operands $rdi and $rsi, in reg-mem order ;; compute the rex bits for the operands $rdi and $rsi, in reg-mem order
compute_rex: compute_rex:
@ -2048,7 +2097,7 @@ compute_rex:
shl dl, 2 ; REX.R shl dl, 2 ; REX.R
or al, dl or al, dl
cmp dword [rdi + operand.size], 3 cmp dword [rdi + operand.size], 8 ; this is no longer in log2 form
setnb dl setnb dl
shl dl, 3 ; REX.W shl dl, 3 ; REX.W
or al, dl or al, dl
@ -2063,7 +2112,7 @@ compute_rex:
setne dl setne dl
or al, dl ; REX.B or al, dl ; REX.B
cmp dword [rsi + operand.size], 3 cmp dword [rsi + operand.size], 8 ; this is no longer in log2 form
setnb dl setnb dl
shl dl, 3 ; REX.W shl dl, 3 ; REX.W
or al, dl or al, dl
@ -2088,9 +2137,195 @@ compute_rex:
ret ret
;; Input and Output
struc OFile
.fd resd 1
.offset resd 1
endstruc
struc IFile
.fd resd 1
.peeked_byte resb 1
.peeked_flags resb 1
.pad resb 2
.buffer resq 1
.buffer_len resq 1
.buffer_cursor resq 1
endstruc
%define DID_PEEK 1
%define DID_PEEK_SOME 2
%define DID_PEEK_AND_SOME 3
section .bss
ifile resb 0x20
ofile resb 0x20
buf resb 0x100
section .text
;; seek to offset $rsi in file $rdi
fseek:
mov rax, 8 ; syscall: lseek
mov rdx, 0 ; SEEK_SET
syscall
ret
;; write $rsi bytes from buffer $rdi to file $rdx, returning the number of bytes written in $rax
fwrite_bytes:
mov rax, 1 ; syscall: write
mov rcx, rdx ; fd
mov rdx, rsi ; count
mov rsi, rdi ; buffer
mov rdi, rcx ; fd
syscall
ret
ofile_write_bytes:
lea rdx, [rel ofile]
mov edx, dword [rdx + OFile.fd]
jmp fwrite_bytes
;; open file $rdi with flags $rsi and mode $rdx, returning the file descriptor in $rax, or -1 on error.
fopen:
mov rax, 2 ; syscall: open
syscall
ret
close:
mov rax, 3 ; syscall: close
syscall
ret
init_ifile_with_fd:
mov eax, edi
jmp init_ifile.with_fd
;; open the input file $rdi and initialise the ifile structure.
init_ifile:
xor esi, esi
xor edx, edx
call fopen
.with_fd:
lea rdi, [rel ifile]
mov dword [rdi + IFile.fd], eax
mov byte [rdi + IFile.peeked_flags], 0
mov edi, 0x1000
mov esi, 0x8
call heap_alloc
lea rdi, [rel ifile]
mov qword [rdi + IFile.buffer], rax
mov qword [rdi + IFile.buffer_len], 0x1000
mov qword [rdi + IFile.buffer_cursor], 0
ret
getc_inner:
lea rax, [rel ifile]
mov cl, byte [rax + IFile.peeked_flags] ; peeked
test cl, DID_PEEK
jz .iter_next
test cl, DID_PEEK_SOME ; peeked_some
setnz dl
and edx, 1
mov al, byte [rax + IFile.peeked_byte]
ret
.iter_next:
mov rdi, qword [rax + IFile.buffer_cursor] ; buf_cur
cmp rdi, qword [rax + IFile.buffer_len] ; buf_len
jae .read
inc qword [rax + IFile.buffer_cursor] ; buf_cur++
mov rsi, qword [rax + IFile.buffer] ; buf
mov al, byte [rsi + rdi]
mov edx, 1 ; peeked_some = true
ret
.read:
mov rdi, qword [rax + IFile.fd] ; fd
mov rsi, qword [rax + IFile.buffer] ; buf
mov rdx, 0x1000 ; read 0x1000 bytes
push rax
mov rax, 0 ; syscall: read
syscall
cmp rax, 0
jle .eof
mov rdi, rax ; number of bytes read
pop rax
mov qword [rax + IFile.buffer_len], rdi ; buf_len = bytes read
mov qword [rax + IFile.buffer_cursor], 0 ; buf_cur = 0
jmp .iter_next
.eof:
pop rax
xor dl, dl ; peeked_some = false
ret
;; returns the next byte from the input without advancing the cursor. returns 1 or 0 in dl depending on EOF
peekc:
call getc_inner
lea rdi, [rel ifile]
movzx ecx, dl
shl ecx, 1
inc ecx
mov byte [rdi + IFile.peeked_byte], al ; peeked_byte = c
mov byte [rdi + IFile.peeked_flags], cl ; peeked = true, peeked_some = dl
ret
;; returns the next byte from the input. returns 1 or 0 in dl depending on EOF
getc:
call getc_inner
lea rdi, [rel ifile]
mov word [rdi + IFile.peeked_flags], 0
and eax, 0xff
ret
ifile_skip_whitespaces:
call peekc
cmp al, ' '
ja .done
call getc
jmp ifile_skip_whitespaces
.done:
ret
ifile_skip_comment:
call peekc
cmp al, `;`
jne .done
.loop:
call getc
cmp al, 10
jne .loop
call ifile_skip_whitespaces
jmp ifile_skip_comment
.done:
ret
;; read a line from the input file into `buf`
read_line:
push rbx
; skip whitespaces, empty lines and comments
call ifile_skip_whitespaces
; guaranteed to be at the start of a token or EOF
lea rbx, [rel buf]
.loop:
call getc
test dl, dl
jz .done
test al, al
jz .done
cmp al, 10
je .done
mov byte [rbx], al
inc rbx
jmp .loop
.done:
mov byte [rbx], 0
lea rax, [rel buf]
sub rbx, rax
mov rax, rbx
pop rbx
ret
;; parse buffer $rdi, bytes written in $rsi, return updated bytes written. ;; parse buffer $rdi, bytes written in $rsi, return updated bytes written.
parse_line: parse_line:
@ -2107,14 +2342,15 @@ parse_line:
mov rdi, rbx mov rdi, rbx
sub rsp, 72 sub rsp, 72
mov rsi, rsp lea rsi, [rsp]
call try_parse_inst call try_parse_inst
test eax, eax test eax, eax
jz .done_inst jz .done_inst
lea rdi, [rsp] lea rdi, [rsp]
mov rsi, r12
call encode_inst call encode_inst
add r12, rax mov r12, rax
.done_inst: .done_inst:
add rsp, 72 add rsp, 72
@ -2142,22 +2378,80 @@ parse_line:
;; Labels ;; Labels
struc Vec
.data resq 1 ; pointer to elements
.size resd 1 ; size in element count
.cap resd 1 ; cap in element count
.ele_size resd 1 ; size of each element in bytes
.ele_align resd 1 ; alignment of each element in bytes
endstruc
struc Label struc Label
.name_hash resd 1 .name_hash resd 1
.offset resd 1 .offset resd 1
endstruc endstruc
struc LabelTable struc LabelRef
.size resd 1 .name_hash resd 1
.cap resd 1 .write_offset resd 1
.labels resq 1 .disp_offset resd 1
endstruc endstruc
section .data section .data
labels dq 0, 0 labels dq 0, 0, 0
label_refs dq 0, 0 label_refs dq 0, 0, 0
section .text
vec_get_nth:
mov rax, rsi
mov ecx, dword [rdi + Vec.ele_size]
mul ecx
add rax, qword [rdi + Vec.data]
ret
;; reserve space for $rsi additional entries in vec $rdi.
vec_reserve:
sub rsp, 16
mov edx, dword [rdi + Vec.ele_size]
mov ecx, dword [rdi + Vec.ele_align]
mov dword [rsp], edx ; obj-size
mov dword [rsp + 4], ecx ; alignment
mov qword [rsp + 8], rdi ; table pointer
mov eax, dword [rdi + Vec.size]
add eax, esi
cmp eax, dword [rdi + Vec.cap]
jl .done
mov eax, esi
add eax, dword [rdi + Vec.size]
mov ecx, dword [rdi + Vec.cap]
mov edx, ecx
shl edx, 1
cmp eax, edx
cmovl eax, edx
; new cap
mul dword [rsp] ; obj-size
; old cap
xchg eax, ecx
mul dword [rsp] ; obj-size
mov rdi, qword [rdi + Vec.data] ; old pointer
mov esi, eax ; old size
mov edx, ecx ; new size
mov ecx, dword [rsp + 4] ; alignment
call heap_realloc
mov rdi, qword [rsp + 8] ; table pointer
mov qword [rdi + Vec.data], rax
.done:
add rsp, 16
ret
section .text
;; calculates the hash of a byte sequence in $rdi of length $rsi, returning it in $eax ;; calculates the hash of a byte sequence in $rdi of length $rsi, returning it in $eax
global fasthash global fasthash
fasthash: fasthash:
@ -2177,80 +2471,112 @@ fasthash:
global init_label_tables global init_label_tables
init_label_tables: init_label_tables:
mov edi, 0x100 mov edi, 0x100
mov esi, 8 mov esi, 4
call heap_alloc call heap_alloc
lea rdi, [rel labels] lea rdi, [rel labels]
mov dword [rdi + LabelTable.size], 0 mov dword [rdi + Vec.size], 0
mov dword [rdi + LabelTable.cap], 0x100 / 8 mov dword [rdi + Vec.cap], 0x100 / Label_size
mov qword [rdi + LabelTable.labels], rax mov dword [rdi + Vec.ele_size], Label_size
mov dword [rdi + Vec.ele_align], 4
mov qword [rdi + Vec.data], rax
mov edi, 0x100 mov edi, 0x180
mov esi, 8 mov esi, 4
call heap_alloc call heap_alloc
lea rdi, [rel label_refs] lea rdi, [rel label_refs]
mov dword [rdi + LabelTable.size], 0 mov dword [rdi + Vec.size], 0
mov dword [rdi + LabelTable.cap], 0x100 / 8 mov dword [rdi + Vec.cap], 0x180 / LabelRef_size
mov qword [rdi + LabelTable.labels], rax mov dword [rdi + Vec.ele_size], LabelRef_size
mov dword [rdi + Vec.ele_align], 4
mov qword [rdi + Vec.data], rax
ret ret
;; push label hash $edi with offset $esi into the label ref table. ;; push label hash $edi with write offset $esi and disp offset $edx into the label ref table.
push_lbl_ref_by_hash: push_lbl_ref_by_hash:
push rbx push rbx
push r12 push r12
push r13 push r13
push r14
lea rbx, [rel label_refs] lea rbx, [rel label_refs]
mov r13d, esi ; label offset mov r13d, esi ; label write offset
mov r12d, edi ; label hash mov r12d, edi ; label hash
jmp push_lbl_common.hash_in_r12d mov r14d, edx ; label disp offset
jmp push_lbl_ref.hash_in_r12d
;; push label $rdi with length $rsi and offset $rdx into the label ref table ;; push label $rdi with length $rsi, write offset $edx and disp offset $ecx into the label ref table.
push_lbl_ref: push_lbl_ref:
lea rcx, [rel label_refs] push rbx
jmp push_lbl_common push r12
push r13
push r14
lea rbx, [rel label_refs]
mov r13d, edx ; label write offset
mov r14d, ecx ; label disp offset
call fasthash
mov r12d, eax ; hash
.hash_in_r12d:
mov rdi, rbx
mov rsi, 1
call vec_reserve
mov rdi, rbx
mov esi, dword [rbx + Vec.size]
call vec_get_nth
mov dword [rax + LabelRef.name_hash], r12d
mov dword [rax + LabelRef.write_offset], r13d
mov dword [rax + LabelRef.disp_offset], r14d
inc dword [rbx + Vec.size]
pop r14
pop r13
pop r12
pop rbx
ret
;; push label $rdi with length $rsi and offset $edx into the label table ;; push label $rdi with length $rsi and offset $edx into the label table
push_lbl: push_lbl:
lea rcx, [rel labels]
push_lbl_common:
push rbx push rbx
push r12 push r12
push r13 push r13
mov rbx, rcx lea rbx, [rel labels]
mov r13d, edx ; label offset mov r13d, edx ; label offset
call fasthash call fasthash
mov r12d, eax ; store the hash in r12d mov r12d, eax ; hash
.hash_in_r12d:
mov eax, dword [rbx + LabelTable.size] mov rdi, rbx
cmp eax, dword [rbx + LabelTable.cap] mov rsi, 1
jl .has_space call vec_reserve
mov rdi, qword [rbx + LabelTable.labels] mov rdi, rbx
mov esi, dword [rbx + LabelTable.cap] mov esi, dword [rbx + Vec.size]
mov edx, 8 call vec_get_nth
mov ecx, esi
shl ecx, 1
call heap_realloc
mov qword [rbx + LabelTable.labels], rax
shl dword [rbx + LabelTable.cap], 1
.has_space:
mov rax, qword [rbx + LabelTable.labels]
mov edi, dword [rbx + LabelTable.size]
lea rax, [rax + rdi * 8]
mov dword [rax + Label.name_hash], r12d mov dword [rax + Label.name_hash], r12d
mov dword [rax + Label.offset], r13d mov dword [rax + Label.offset], r13d
inc dword [rbx + LabelTable.size] inc dword [rbx + Vec.size]
pop r13 pop r13
pop r12 pop r12
pop rbx pop rbx
ret ret
find_lbl_by_hash:
push rbx
push r12
push r13
lea rbx, [rel labels]
mov r12d, edi ; label hash
jmp find_lbl.by_hash
find_lbl: find_lbl:
push rbx push rbx
push r12 push r12
@ -2261,12 +2587,13 @@ find_lbl:
call fasthash call fasthash
mov r12d, eax ; store the hash in r12d mov r12d, eax ; store the hash in r12d
.by_hash:
xor r13d, r13d xor r13d, r13d
.loop: .loop:
cmp r13d, dword [rbx + LabelTable.size] cmp r13d, dword [rbx + Vec.size]
jge .not_found jge .not_found
mov rax, qword [rbx + LabelTable.labels] mov rax, qword [rbx + Vec.data]
lea rax, [rax + r13 * 8] lea rax, [rax + r13 * 8]
cmp dword [rax + Label.name_hash], r12d cmp dword [rax + Label.name_hash], r12d
je .found je .found
@ -2276,14 +2603,121 @@ find_lbl:
mov eax, dword [rax + Label.offset] mov eax, dword [rax + Label.offset]
jmp .done jmp .done
.not_found: .not_found:
xor rax, rax mov rax, -1
.done: .done:
pop r13 pop r13
pop r12 pop r12
pop rbx pop rbx
ret ret
fixup_lbl_refs:
push rbx ; label ref table
push r12 ; index
push r13 ; ref offset
push r14
xor r12d, r12d
.loop:
lea rbx, [rel label_refs]
cmp r12d, dword [rbx + Vec.size]
jge .done
mov rax, qword [rbx + Vec.data]
lea rcx, [r12 + r12 * 2]
shl rcx, 2 ; multiply by 12
add rax, rcx
inc r12d
mov edi, dword [rax + LabelRef.name_hash]
mov r13d, dword [rax + LabelRef.write_offset]
mov r14d, dword [rax + LabelRef.disp_offset]
call find_lbl_by_hash
cmp rax, -1
je .loop
mov esi, eax ; label source offset
sub eax, r14d ; displacement
mov r14d, eax
lea rdi, [rel ofile]
mov edi, dword [rdi + OFile.fd] ; fd
mov esi, r13d ; write offset
call fseek
mov edi, r14d ; displacement
mov esi, 4 ; size
call write_bytes
jmp .loop
.done:
pop r14
pop r13
pop r12
pop rbx
ret
global _entry_print_table global _entry_print_table
_entry_print_table: _entry_print_table:
call print_op_table call print_op_table
call exit call exit
a.out: db "a.out", 0
global _entry_as0
;; /as0 <input file>? <output file>
_entry_as0:
mov eax, dword [rsp]
lea rcx, [rsp + 8]
sub rsp, 24
mov qword [rsp], rcx ; argv
mov qword [rsp + 8], rax ; argc
mov rdi, [rcx + 8] ; argv[1] (input file)
mov qword [rsp + 16], rdi ; output file
mov edi, 0 ; stdin
cmp eax, 2 ; check if we have an input file
jl .init_ifile
mov rdi, [rcx + 16] ; argv[2] (output file)
mov qword [rsp + 16], rdi ; output file
mov rdi, [rcx + 8] ; argv[1]
xor esi, esi ; O_RDONLY
xor edx, edx ; mode = 0
call fopen
mov edi, eax ; fd
.init_ifile:
call init_ifile_with_fd
;; mov rdi, qword [rsp + 16] ; output file
lea rdi, [rel a.out] ; default output file
mov esi, 0x241 ; O_WRONLY | O_CREAT | O_TRUNC
mov edx, 0x1b6 ; mode = 0666
call fopen
xor edi, edi
sub edi, eax
jg panic_abort
lea rdi, [rel ofile]
mov dword [rdi + OFile.fd], eax
mov dword [rdi + OFile.offset], 0
call init_label_tables
xor ebx, ebx ; bytes written
.loop:
call read_line
test rax, rax
jz .done
lea rdi, [rel buf]
mov rsi, rbx ; bytes written
call parse_line
mov rbx, rax ; update bytes written
jmp .loop
.done:
call fixup_lbl_refs
lea rdi, [rel ofile]
mov edi, dword [rdi + OFile.fd]
call close
call exit

View file

@ -0,0 +1,16 @@
@_start:
mov rdi, 0xa21444C52
push rdi
mov rdi, 0x4F57204F
shl rdi, 32
mov rsi, 0x4C4C4548
or rdi, rsi
push rdi
@init:
lea rsi, qword [rsp]
mov edx, 13
mov eax, 1
mov edi, 1
syscall
jmp 'init
jmp '_start