stage 1 hex
This commit is contained in:
parent
6b6bacd9dd
commit
6f76735710
287
stages/hex0/hex1.asm
Normal file
287
stages/hex0/hex1.asm
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
;; This is the 2nd stage of the hex compiler.
|
||||||
|
;; It still requires hex input, but it can automatically calculate rip-relative offsets to labels
|
||||||
|
|
||||||
|
;; we need the following variables:
|
||||||
|
;; +0 = buf: [u8; 0x1000]
|
||||||
|
;; +0x1000 = write_buf: [u8; 0x4000]
|
||||||
|
;; +0x5000 = read_cursor: u32
|
||||||
|
;; +0x5004 write_cursor: u32
|
||||||
|
;; +0x5008 read_end: u32
|
||||||
|
;; +0x500c file_offset: u32 (the offset of the current file in the overall input, used for calculating label offsets)
|
||||||
|
;; +0x5010 labels: [(u64, u32); 340]
|
||||||
|
;; +0x6000 num_labels: u32
|
||||||
|
;; +0x6004 scratch space
|
||||||
|
|
||||||
|
;; exits the program
|
||||||
|
panic:
|
||||||
|
mov rax, 60 ; syscall: exit
|
||||||
|
mov rdi, 1 ; status: 1
|
||||||
|
syscall
|
||||||
|
|
||||||
|
;; exits the program
|
||||||
|
die:
|
||||||
|
mov rax, 60 ; syscall: exit
|
||||||
|
mov rdi, 0 ; status: 0
|
||||||
|
syscall
|
||||||
|
|
||||||
|
;; calculates a quick hash of a byte sequence given by rdi (pointer) and rsi (length)
|
||||||
|
.fashhash_exit:
|
||||||
|
ret
|
||||||
|
fasthash:
|
||||||
|
mov eax, 0x811c9dc5 ; FNV offset basis
|
||||||
|
.fashhash_loop:
|
||||||
|
test rsi, rsi
|
||||||
|
jz .fashhash_done
|
||||||
|
movzx ecx, byte ptr [rdi] ; get next byte
|
||||||
|
xor eax, ecx
|
||||||
|
imul eax, 0x1000193 ; FNV prime
|
||||||
|
inc rdi
|
||||||
|
dec rsi
|
||||||
|
jmp .fashhash_loop
|
||||||
|
|
||||||
|
;; push the label pointed at by rdi with length rsi, and the current file offset onto the labels array.
|
||||||
|
push_lbl:
|
||||||
|
mov rax, num_labels
|
||||||
|
cmp rax, 340
|
||||||
|
jge panic
|
||||||
|
add rax, rax ; rax = num_labels * 2
|
||||||
|
add rax, num_labels ; rax = num_labels * 3
|
||||||
|
sal rax, 2 ; rax = num_labels * 12 (size of each label entry)
|
||||||
|
push rax
|
||||||
|
call fasthash
|
||||||
|
pop rdx ; rdx = offset of label entry in labels array
|
||||||
|
mov [labels + rdx], rax ; store hash in labels array
|
||||||
|
mov eax, file_offset
|
||||||
|
mov [labels + rdx + 8], eax ; store file offset in labels array
|
||||||
|
inc num_labels
|
||||||
|
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_not_found:
|
||||||
|
call panic
|
||||||
|
.find_lbl_found:
|
||||||
|
mov rax, [rdx + 8] ; get file offset of label
|
||||||
|
ret
|
||||||
|
find_lbl:
|
||||||
|
call fasthash
|
||||||
|
mov rbx, num_labels
|
||||||
|
lea rdx, [labels] ; rdx = pointer to start of labels array
|
||||||
|
.find_lbl_loop:
|
||||||
|
test rbx, rbx
|
||||||
|
jz .find_lbl_not_found
|
||||||
|
dec rbx
|
||||||
|
mov rcx, [rdx] ; get hash of label at rdx
|
||||||
|
cmp rcx, rax
|
||||||
|
je .find_lbl_found
|
||||||
|
add rdx, 12 ; move to next label entry
|
||||||
|
jmp .find_lbl_loop
|
||||||
|
|
||||||
|
die_jmp_start:
|
||||||
|
sub rsp, 8
|
||||||
|
mov qword ptr [rsp], '\0\0trats_'
|
||||||
|
mov rdi, rsp
|
||||||
|
mov rsi, 6
|
||||||
|
call find_lbl
|
||||||
|
neg eax
|
||||||
|
mov byte ptr [write_buf + write_cursor], 0xe9 ; write the opcode for jmp rel32
|
||||||
|
add write_cursor, 1
|
||||||
|
mov dword ptr [write_buf + write_cursor], eax ; write the offset of the label reference
|
||||||
|
add write_cursor, 4
|
||||||
|
add offset 5
|
||||||
|
call die
|
||||||
|
|
||||||
|
;; reads up to 0x1000 bytes from stdin into buf, and sets read_end to the number of bytes read, and resets read_cursor to 0
|
||||||
|
;; dies if no bytes were read (if it returns, there were bytes read)
|
||||||
|
rd_stdin:
|
||||||
|
mov rax, 0 ; syscall: read
|
||||||
|
mov rdi, 0 ; fd: stdin
|
||||||
|
mov rsi, buf ; buf: pointer to buffer
|
||||||
|
mov rdx, 0x1000 ; count: size of buffer
|
||||||
|
syscall
|
||||||
|
mov read_end, rax
|
||||||
|
mov read_cursor, 0
|
||||||
|
cmp read_end, 0
|
||||||
|
je die_jmp_start
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; write up to write_cursor to stdout, and then reset the write cursor to 0
|
||||||
|
wrt_stdout:
|
||||||
|
mov rax, 1 ; syscall: write
|
||||||
|
mov rdi, 1 ; fd: stdout
|
||||||
|
mov rsi, write_buf ; buf: pointer to buffer
|
||||||
|
mov rdx, write_cursor ; count: number of bytes to write
|
||||||
|
syscall
|
||||||
|
mov write_cursor, 0
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; return the next byte from the input, or 0 if we are at the end of the input
|
||||||
|
;; automatically writes to stdout and reads from stdin if we are at the end of the buffer
|
||||||
|
.rd_buf_end:
|
||||||
|
mov al, byte ptr [buf + read_cursor] ; get byte at read_cursor
|
||||||
|
inc read_cursor
|
||||||
|
ret
|
||||||
|
rd_buf:
|
||||||
|
cmp read_cursor, read_end
|
||||||
|
jl .rd_buf_end
|
||||||
|
call wrt_stdout
|
||||||
|
call rd_stdin
|
||||||
|
jmp .rd_buf_end
|
||||||
|
|
||||||
|
;; The grammar is slightly updated from hex0, as follows:
|
||||||
|
;; program = (label-def / instruction-seq / comment)*
|
||||||
|
;; comment = ";" [^\n]* "\n"
|
||||||
|
;; label-def = "@" label-name ":" comment?
|
||||||
|
;; instruction-seq = instruction+ comment?
|
||||||
|
;; instruction = label-ref / opcode
|
||||||
|
;; label-ref = "'" label-name
|
||||||
|
;; opcode = hex hex
|
||||||
|
;; label-name = [a-zA-Z0-9_-]+
|
||||||
|
;; hex = [0-9a-f][0-9a-f]
|
||||||
|
;;
|
||||||
|
;; whitespaces may occur between any tokens and are ignored.
|
||||||
|
;; whitespace = [ \t\n]+
|
||||||
|
|
||||||
|
|
||||||
|
.skip_line_read:
|
||||||
|
call rd_buf
|
||||||
|
skip_line:
|
||||||
|
cmp al, '\n'
|
||||||
|
jne .skip_line_read
|
||||||
|
call rd_buf
|
||||||
|
ret
|
||||||
|
|
||||||
|
.skip_whitespace_read:
|
||||||
|
call rd_buf
|
||||||
|
skip_whitespaces:
|
||||||
|
cmp al, ' '
|
||||||
|
je .skip_whitespace_read
|
||||||
|
cmp al, '\t'
|
||||||
|
je .skip_whitespace_read
|
||||||
|
cmp al, '\n'
|
||||||
|
je .skip_whitespace_read
|
||||||
|
ret
|
||||||
|
|
||||||
|
.skip_comment_ret:
|
||||||
|
ret
|
||||||
|
.skip_comment_read:
|
||||||
|
call rd_buf
|
||||||
|
skip_comment:
|
||||||
|
cmp al, '\;'
|
||||||
|
jne .skip_comment_ret
|
||||||
|
call skip_line
|
||||||
|
ret
|
||||||
|
|
||||||
|
write_u32le:
|
||||||
|
mov dword ptr [write_buf + write_cursor], edi
|
||||||
|
add write_cursor, 4
|
||||||
|
ret
|
||||||
|
|
||||||
|
.get_next_token_skip:
|
||||||
|
call skip_whitespaces
|
||||||
|
call skip_comment
|
||||||
|
get_next_token:
|
||||||
|
cmp al, '\;'
|
||||||
|
je .get_next_token_skip
|
||||||
|
cmp al, ' '
|
||||||
|
je .get_next_token_skip
|
||||||
|
cmp al, '\t'
|
||||||
|
je .get_next_token_skip
|
||||||
|
cmp al, '\n'
|
||||||
|
je .get_next_token_skip
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; read the label into scratch, call push_lbl
|
||||||
|
.label_ret
|
||||||
|
lea rdi, [r15 + 0x6004]
|
||||||
|
call push_lbl
|
||||||
|
ret
|
||||||
|
label:
|
||||||
|
lea rdi, [r15 + 0x6004] ; rdi = pointer to scratch
|
||||||
|
xor rsi, rsi
|
||||||
|
cmp al, ':'
|
||||||
|
je .label_ret
|
||||||
|
mov byte ptr [rdi + rsi], al
|
||||||
|
inc rsi
|
||||||
|
call rd_buf
|
||||||
|
jmp label
|
||||||
|
|
||||||
|
.label_ref_ret:
|
||||||
|
lea rdi, [r15 + 0x6004]
|
||||||
|
call find_lbl
|
||||||
|
add file_offset, 4
|
||||||
|
sub eax, file_offset
|
||||||
|
mov dword ptr [write_buf + write_cursor], eax ; write the offset of the label reference
|
||||||
|
add write_cursor, 4
|
||||||
|
ret
|
||||||
|
label_ref:
|
||||||
|
lea rdi, [r15 + 0x6004] ; rdi = pointer to scratch
|
||||||
|
xor rsi, rsi
|
||||||
|
call rd_buf
|
||||||
|
cmp al, ' '
|
||||||
|
je .label_ref_ret
|
||||||
|
cmp al, '\t'
|
||||||
|
je .label_ref_ret
|
||||||
|
cmp al, '\n'
|
||||||
|
je .label_ref_ret
|
||||||
|
mov byte ptr [rdi + rsi], al
|
||||||
|
inc rsi
|
||||||
|
jmp label_ref
|
||||||
|
|
||||||
|
.invalid_hex:
|
||||||
|
call panic
|
||||||
|
.is_digit:
|
||||||
|
sub al, '0'
|
||||||
|
ret
|
||||||
|
.is_lower_hex:
|
||||||
|
sub al, 'a' - 10
|
||||||
|
ret
|
||||||
|
.is_upper_hex:
|
||||||
|
sub al, 'A' - 10
|
||||||
|
ret
|
||||||
|
char_to_u4:
|
||||||
|
cmp al, '0'
|
||||||
|
jb .invalid_hex
|
||||||
|
cmp al, '9'
|
||||||
|
jbe .is_digit
|
||||||
|
cmp al, 'a'
|
||||||
|
jb .invalid_hex
|
||||||
|
cmp al, 'f'
|
||||||
|
jbe .is_lower_hex
|
||||||
|
cmp al, 'A'
|
||||||
|
jb .invalid_hex
|
||||||
|
cmp al, 'F'
|
||||||
|
jbe .is_upper_hex
|
||||||
|
jmp .invalid_hex
|
||||||
|
|
||||||
|
octet:
|
||||||
|
sub rsp, 8
|
||||||
|
call char_to_u4
|
||||||
|
shl rax, 4
|
||||||
|
mov byte ptr [rsp], al
|
||||||
|
call rd_buf
|
||||||
|
call char_to_u4
|
||||||
|
or byte ptr [rsp], al
|
||||||
|
mov al, byte ptr [rsp]
|
||||||
|
add rsp, 8
|
||||||
|
inc file_offset
|
||||||
|
ret
|
||||||
|
|
||||||
|
_start:
|
||||||
|
push rbp
|
||||||
|
mov rbp, rsp
|
||||||
|
sub rsp, 0x8000 ; allocate space for our variables + some scratch space
|
||||||
|
mov r15, rsp ; r15 will point to the start of our variables
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
call get_next_token
|
||||||
|
cmp al, '@'
|
||||||
|
;; label
|
||||||
|
call label
|
||||||
|
cmp al, '\''
|
||||||
|
;; label-ref
|
||||||
|
call label_ref
|
||||||
|
;; else opcode
|
||||||
|
call octet
|
||||||
|
jmp .loop
|
||||||
|
|
||||||
|
|
||||||
4
stages/hex1/hex1.in
Normal file
4
stages/hex1/hex1.in
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# panic: +0
|
||||||
|
# mov rax, 60 ; syscall: exit
|
||||||
|
# mov rdi, 1 ; status: 1
|
||||||
|
# syscall
|
||||||
Loading…
Reference in a new issue