From 4690b3c7ec7fe3999185ac43eef2934a8c670910 Mon Sep 17 00:00:00 2001 From: janis Date: Mon, 13 Jul 2026 23:33:07 +0200 Subject: [PATCH] as0 works --- stages/as0/Makefile | 5 +- stages/as0/as0.asm | 634 +++++++++++++++++++++++++++----- stages/as0/tests/helloworld.as0 | 16 + 3 files changed, 554 insertions(+), 101 deletions(-) create mode 100644 stages/as0/tests/helloworld.as0 diff --git a/stages/as0/Makefile b/stages/as0/Makefile index 6a886ce..5c63031 100644 --- a/stages/as0/Makefile +++ b/stages/as0/Makefile @@ -8,8 +8,11 @@ as0.o: as0.asm test: test.bin ./test.bin +as0: as0.o + ld -m elf_x86_64 -e _entry_as0 -o as0 as0.o + print_table: as0.o ld -m elf_x86_64 -e _entry_print_table -o print_table as0.o clean: - rm -f as0.o test.bin + rm -f as0.o test.bin as0 print_table diff --git a/stages/as0/as0.asm b/stages/as0/as0.asm index bccb612..ccbed13 100644 --- a/stages/as0/as0.asm +++ b/stages/as0/as0.asm @@ -2,16 +2,8 @@ ;; 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 heap resq 1 section .data @@ -49,8 +41,8 @@ mnemonics: mnemonics_table: MN_MOV db "mov", 0 MN_TEST db "test", 0 - MN_XOR db "xor", 0 MN_OR db "or", 0 + MN_XOR db "xor", 0 MN_AND db "and", 0 MN_SHL db "shl", 0 MN_SHR db "shr", 0 @@ -406,7 +398,6 @@ op_table_end: section .text panic_abort: - mov rdi, 1 mov rax, 60 syscall @@ -867,6 +858,9 @@ print_op_table: pop rbx ret + +;; Common Library Functions + ;; rdi: *u8 strlen: xor rax, rax @@ -1036,6 +1030,7 @@ to_digit: ret exit: + xor edi, edi mov rax, 60 syscall @@ -1305,6 +1300,8 @@ try_parse_num: skip_whitespace_rbx: mov al, byte [rbx] + test al, al + jz .done cmp al, ' ' ja .done inc rbx @@ -1349,14 +1346,14 @@ try_parse_mem: mov edi, dword [rbx] xor eax, eax - mov dword [r12 + operand.size], 2 ; 16-bit + mov dword [r12 + operand.size], 2 ; 32-bit cmp dl, 'q' sete al cmp dl, 'd' sete dl - or al, dl + or dl, al jz .not_mem - add dword [r12 + operand.size], eax ; 32-bit? + add dword [r12 + operand.size], eax ; 64-bit? cmp edi, 'word' jne .not_mem @@ -1512,6 +1509,8 @@ try_parse_operand: mov dword [r12 + operand.disp], 0 mov al, byte [rbx] + test al, al + jz .done cmp al, '[' je .mem cmp al, `'` @@ -1533,10 +1532,10 @@ try_parse_operand: bsr rcx, rdx ; # of bits required to represent the immediate mov edx, ecx - cmp edx, 1 - adc edx, -1 ; saturating sub + shr edx, 3 ; divide by 8 to get the number of bytes required + jz .done - bsr edx, edx ; log2 of the (# of bits - 1) + 1 + bsr edx, edx ; log2 of the (# of bytes) + 1 inc edx cmp ecx, 2 @@ -1614,6 +1613,7 @@ try_parse_label: pop rbx ret +;; Instruction struc Instruction .mnemonic_offs resd 1 @@ -1633,6 +1633,8 @@ try_parse_inst: mov dword [r13 + Instruction.num_operands], 0 mov dword [r13 + Instruction.mnemonic_offs], 0 + mov qword [r13 + Instruction.operand1], 0 + mov qword [r13 + Instruction.operand2], 0 call skip_whitespace_rbx mov rdi, rbx @@ -1697,7 +1699,6 @@ try_parse_inst: ret - ;; find the op_table entry returning the pointer to the entry in $rax, or 0 if not found. ;; inputs: ;; $edi: mnemonic offset @@ -1728,10 +1729,15 @@ find_op_table_entry: jne .next mov r8d, dword [rax + 16] ; encoding + cmp r8d, esi + je .found mov r9d, esi + test r8d, 0xff00 + jz .enc1 and r9d, 0xff00 and r9d, r8d jz .next +.enc1: mov r9d, esi and r9d, 0xff and r9d, r8d @@ -1754,7 +1760,7 @@ encode_inst: push r12 push r13 - xor ebx, ebx ; bytes written + mov rbx, rsi ; bytes written mov r12, rdi ; inst ptr ; calculate the encoding @@ -1766,7 +1772,7 @@ encode_inst: ; calculate size mask of op2. ; 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 eax, 1 shl eax, cl @@ -1774,9 +1780,9 @@ encode_inst: 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 + and eax, 0xf .size_done_op2: shl eax, 8 @@ -1802,16 +1808,16 @@ encode_inst: ; calculate size mask of op1. ; 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 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 + and edx, 0xf .size_done: xor edx, eax @@ -1822,8 +1828,13 @@ encode_inst: jz .done 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 - cmp dword [r13 + 24], 0 + cmp dword [r13 + OpEntry.prefix], 0 je .rex movzx edi, byte [r13 + 24] @@ -1868,6 +1879,9 @@ encode_inst: mov edi, dword [r13 + OpEntry.opcode] 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 jnz .imm @@ -1884,48 +1898,68 @@ encode_inst: call write_non_zero_bytes 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 dec eax cmp eax, 2 - jge .imm + jae .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: + xor eax, eax ; clear return 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 + test dword [rsi + operand.type], KIND_IMM + jnz .imm1 mov rsi, rdi - cmp dword [rsi + operand.type], KIND_IMM - jne .done + test dword [rsi + operand.type], KIND_IMM + jz .ref .imm1: push rsi test dword [rsi + operand.type], KIND_LABEL_FLAG jz .imm2 - mov edi, dword [rsi + operand.disp] - mov rsi, rbx + mov edi, dword [rsi + operand_imm.imm] ; hash + mov rsi, rbx ; write offset + lea rdx, [rbx + 4] ; disp offset (after the imm32 is written) call push_lbl_ref_by_hash .imm2: pop rsi mov rdi, qword [rsi + operand_imm.imm] - mov esi, dword [r13 + OpEntry.op2_size] + mov esi, dword [rsi + operand.size] 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: mov rax, rbx ; bytes written pop r13 @@ -1945,19 +1979,27 @@ write_disp: jmp write_bytes write_bytes: - call print_u8s_le + push rdi + lea rdi, [rsp] + call ofile_write_bytes add rbx, rax + pop rdi ret write_opcode_with_bits: - call swap_bytes_and_shift_in_place or rdi, rsi + call swap_bytes_and_shift_in_place 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 + push rdi + lea rdi, [rsp] + call strlen + mov esi, eax + call ofile_write_bytes add rbx, rax + pop rdi ret ;; 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 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 jz .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 + ; 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 jne .done_mem @@ -2005,16 +2056,16 @@ compute_modrm_sib: shl edx, 6 ; mod = 01b or 10b or eax, edx +.sib_no_disp: 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) + and eax, 0b11111000 ; clear the r/m bits + 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] and ecx, 3 @@ -2032,8 +2083,6 @@ compute_modrm_sib: .done_mem: ret - - ;; compute the rex bits for the operands $rdi and $rsi, in reg-mem order compute_rex: @@ -2048,7 +2097,7 @@ compute_rex: shl dl, 2 ; REX.R or al, dl - cmp dword [rdi + operand.size], 3 + cmp dword [rdi + operand.size], 8 ; this is no longer in log2 form setnb dl shl dl, 3 ; REX.W or al, dl @@ -2063,7 +2112,7 @@ compute_rex: setne dl 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 shl dl, 3 ; REX.W or al, dl @@ -2088,9 +2137,195 @@ compute_rex: 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_line: @@ -2107,14 +2342,15 @@ parse_line: mov rdi, rbx sub rsp, 72 - mov rsi, rsp + lea rsi, [rsp] call try_parse_inst test eax, eax jz .done_inst lea rdi, [rsp] + mov rsi, r12 call encode_inst - add r12, rax + mov r12, rax .done_inst: add rsp, 72 @@ -2142,22 +2378,80 @@ parse_line: ;; 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 .name_hash resd 1 .offset resd 1 endstruc -struc LabelTable -.size resd 1 -.cap resd 1 -.labels resq 1 +struc LabelRef +.name_hash resd 1 +.write_offset resd 1 +.disp_offset resd 1 endstruc section .data - labels dq 0, 0 - label_refs dq 0, 0 + labels dq 0, 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 global fasthash fasthash: @@ -2177,80 +2471,112 @@ fasthash: global init_label_tables init_label_tables: mov edi, 0x100 - mov esi, 8 + mov esi, 4 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 dword [rdi + Vec.size], 0 + mov dword [rdi + Vec.cap], 0x100 / Label_size + 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 esi, 8 + mov edi, 0x180 + mov esi, 4 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 + mov dword [rdi + Vec.size], 0 + mov dword [rdi + Vec.cap], 0x180 / LabelRef_size + mov dword [rdi + Vec.ele_size], LabelRef_size + mov dword [rdi + Vec.ele_align], 4 + mov qword [rdi + Vec.data], rax 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 rbx push r12 push r13 + push r14 lea rbx, [rel label_refs] - mov r13d, esi ; label offset + mov r13d, esi ; label write offset 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: - lea rcx, [rel label_refs] - jmp push_lbl_common + push rbx + 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_lbl: - lea rcx, [rel labels] -push_lbl_common: push rbx push r12 push r13 - mov rbx, rcx + lea rbx, [rel labels] mov r13d, edx ; label offset call fasthash - mov r12d, eax ; store the hash in r12d -.hash_in_r12d: + mov r12d, eax ; hash - mov eax, dword [rbx + LabelTable.size] - cmp eax, dword [rbx + LabelTable.cap] - jl .has_space + mov rdi, rbx + mov rsi, 1 + call vec_reserve - mov rdi, qword [rbx + LabelTable.labels] - mov esi, dword [rbx + LabelTable.cap] - mov edx, 8 - mov ecx, esi - shl ecx, 1 - call heap_realloc - mov qword [rbx + LabelTable.labels], rax - shl dword [rbx + LabelTable.cap], 1 + mov rdi, rbx + mov esi, dword [rbx + Vec.size] + call vec_get_nth -.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.offset], r13d - inc dword [rbx + LabelTable.size] + inc dword [rbx + Vec.size] pop r13 pop r12 pop rbx 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: push rbx push r12 @@ -2261,12 +2587,13 @@ find_lbl: call fasthash mov r12d, eax ; store the hash in r12d +.by_hash: xor r13d, r13d .loop: - cmp r13d, dword [rbx + LabelTable.size] + cmp r13d, dword [rbx + Vec.size] jge .not_found - mov rax, qword [rbx + LabelTable.labels] + mov rax, qword [rbx + Vec.data] lea rax, [rax + r13 * 8] cmp dword [rax + Label.name_hash], r12d je .found @@ -2276,14 +2603,121 @@ find_lbl: mov eax, dword [rax + Label.offset] jmp .done .not_found: - xor rax, rax + mov rax, -1 .done: pop r13 pop r12 pop rbx 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 _entry_print_table: call print_op_table call exit + +a.out: db "a.out", 0 +global _entry_as0 + ;; /as0 ? +_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 diff --git a/stages/as0/tests/helloworld.as0 b/stages/as0/tests/helloworld.as0 new file mode 100644 index 0000000..91c1c4a --- /dev/null +++ b/stages/as0/tests/helloworld.as0 @@ -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 \ No newline at end of file