can syscall!

This commit is contained in:
janis 2026-06-12 03:17:45 +02:00
parent b66b4f6668
commit 927b8517c5
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
2 changed files with 391 additions and 11 deletions

View file

@ -3,7 +3,7 @@
section .bss
buf resb 0x100
align 8,db 0
atoms times 20 resb 8
atoms times 24 resb 8
global env
env resq 1
env_tail resq 1
@ -57,7 +57,7 @@ strcmp:
xor eax, eax
.strcmp_loop:
cmp rsi, rax
je .strcmp_equal
jz .strcmp_equal
movzx ecx, byte [rdx + rax]
cmp byte [rdi + rax], cl
lea rax, [rax + 1]
@ -134,6 +134,10 @@ align 8,db 0
ATOM_IF equ 15
ATOM_LET equ 16
ATOM_LET_STAR equ 17
ATOM_STR_LEN equ 18
ATOM_LIST_LEN equ 19
ATOM_STR_PARTS equ 20
ATOM_SYSCALL equ 21
QUOTE_STR db "quote"
QUOTE_STR_LEN equ $ - QUOTE_STR
@ -171,6 +175,21 @@ align 8,db 0
LET_STR_LEN equ $ - LET_STR
LET_STAR_STR db "let*"
LET_STAR_STR_LEN equ $ - LET_STAR_STR
STR_LEN_STR db "str-len"
STR_LEN_STR_LEN equ $ - STR_LEN_STR
LIST_LEN_STR db "list-len"
LIST_LEN_STR_LEN equ $ - LIST_LEN_STR
STR_PARTS_STR db "str-parts"
STR_PARTS_STR_LEN equ $ - STR_PARTS_STR
SYSCALL_STR db "syscall"
SYSCALL_STR_LEN equ $ - SYSCALL_STR
SPACE_CHAR db "\Space"
SPACE_CHAR_LEN equ $ - SPACE_CHAR
NL_CHAR db "\NL"
NL_CHAR_LEN equ $ - NL_CHAR
TAB_CHAR db "\Tab"
TAB_CHAR_LEN equ $ - TAB_CHAR
;; folds all elements in the list $rdi using the binary function $rdx in the environment $rsi with an accumulator value of $rcx, returning the result in $rax
;; fn fold_list(List<T>, Env, (U, T, Env) -> U, U) -> U
@ -602,11 +621,11 @@ p_if:
call get_car
mov rsi, rax
call get_cdr
mov rdi, rax
call get_car
test cl, cl
cmovnz rsi, rax
mov rdi, rsi
call get_car
mov rdi, rax
mov rsi, qword [rsp + 8] ; restore $rsi = env
call eval
add rsp, 16
@ -698,8 +717,221 @@ p_let_star:
call eval
add rsp, 24
ret
; p_let_rec:
; p_let_rec:
;; returns the length of the input list as a number
p_list_len:
call eval_list
xor rcx, rcx ; acc = 0
.len_loop:
mov rdi, rax ; list
call is_nil
test al, al
jnz .len_done
inc rcx
call get_cdr
jmp .len_loop
.len_done:
push rcx
mov rdi, 16
mov rsi, 8
call alloc
mov dword [rax], 1 ; refcount = 1
pop rcx
mov qword [rax + 8], rcx ; value = length
mov rdi, rax
mov rsi, OBJ_NUM
call obj_set_tag
ret
;; concatenates a list of strings into a single string
;; (str_concat "Hello," " my" " name is" " Alice") -> "Hello, my name is Alice"
p_str_concat:
;; appends a list of chars to a string
;; (str_append "hello" \Space \w \o \r \l \d) -> "hello world"
p_str_append:
;; return the nth element of a string
p_str_nth:
;; returns a new string that is a substring from a a+b of the input string
;; (substr "hello world" 0 5) -> "hello"
p_str_substr:
;; returns 't if the haystack-string contains the needle-char , and nil otherwise
p_str_contains_char:
;; returns 't if the haystack-string contains the needle-string, and nil otherwise
;; (contains_str? "hello world" "lo wo") -> 't
p_str_contains_str:
;; returns the length of the input string as a number
p_str_len:
call eval_list
mov rdi, rax
call get_car
mov rdi, rax
call obj_tag_part
cmp al, OBJ_STR
jne do_panic_abort
call obj_addr_part
movzx rax, dword [rax + 4]
push rax
mov rdi, 16
mov rsi, 8
call alloc
mov rdi, rax
mov dword [rdi], 1 ; refcount = 1
pop rax
mov qword [rdi + 8], rax ; value = string length
mov rsi, OBJ_NUM
call obj_set_tag
ret
;; returns the pointer and length part of the string object as a pair of numbers
p_str_decompose:
call eval_list
mov rdi, rax
call get_car
mov rdi, rax
call obj_tag_part
cmp al, OBJ_STR
jne do_panic_abort
sub rsp, 16
call obj_addr_part
mov edi, dword [rax + 4] ; length
mov rsi, qword [rax + 8] ; ptr
mov dword [rsp], edi
mov qword [rsp + 8], rsi
mov rdi, 16
mov rsi, 8
call alloc
mov rdi, rax
mov dword [rdi], 1 ; refcount = 1
movzx rax, dword [rsp] ; length
mov qword [rdi + 8], rax ; value = length
mov rsi, OBJ_NUM
call obj_set_tag
mov qword [rsp], rax ; length number
mov rdi, 16
mov rsi, 8
call alloc
mov rdi, rax
mov dword [rdi], 1 ; refcount = 1
mov rax, qword [rsp + 8] ; ptr
mov qword [rdi + 8], rax ; value = ptr
mov rsi, OBJ_NUM
call obj_set_tag
mov rdi, rax
mov rsi, qword [rsp] ; length number
call cons
add rsp, 16
ret
;; returns the ascii value of the char
p_char_code:
call eval_list
mov rdi, rax
call get_car
mov rdi, rax
call obj_tag_part
cmp al, OBJ_BYTE
jne do_panic_abort
shr rax, 8
and rax, 0xFF
push rax
mov rdi, 16
mov rsi, 8
call alloc
mov dword [rax], 1 ; refcount = 1
mov rdi, rax
pop rax
mov qword [rdi + 8], rax ; value = char code
mov rsi, OBJ_NUM
call obj_set_tag
ret
;; returns a char object representing the ascii value
p_make_char:
call eval_list
mov rdi, rax
call get_car
mov rdi, rax
call obj_tag_part
cmp al, OBJ_NUM
jne do_panic_abort
call obj_addr_part
mov rcx, qword [rax + 8] ; char code
xor rax, rax
mov ah, cl
mov al, 0 ; OBJ_BYTE
ret
;; performs a system call, returning the result as a number
;; (syscall num arg1 arg2 arg3) -> syscall(num, arg1, arg2, arg3)
p_syscall:
push r12
call eval_list
mov rdi, rax
; r11 = syscall num
; r12 = arg0
xor r11, r11
xor r12, r12
call ._next_arg ; syacall num
jnz .do_syscall
mov r11, rcx
call ._next_arg ; arg0
jnz .do_syscall
mov r12, rcx
call ._next_arg ; arg1
jnz .do_syscall
mov rsi, rcx
call ._next_arg ; arg2
jnz .do_syscall
mov rdx, rcx
call ._next_arg ; arg3
jz .do_syscall
mov r10, rcx
call ._next_arg ; arg4
jz .do_syscall
mov r8, rcx
call ._next_arg ; arg5
jz .do_syscall
mov r9, rcx
.do_syscall:
mov rax, r11 ; syscall num
mov rdi, r12 ; arg0
syscall
push rax
mov rdi, 16
mov rsi, 8
call alloc
mov rdi, rax
mov dword [rdi], 1 ; refcount = 1
pop rax
mov qword [rdi + 8], rax ; value = syscall result
mov rsi, OBJ_NUM
call obj_set_tag
pop r12
ret
;; fn _next_arg(list) -> ($rcx=arg, $rdi=rest)
._next_arg:
call is_nil
test al, al
jnz ._next_arg_done
call get_car
mov rcx, rax ; arg
call get_cdr
mov rdi, rax ; rest
xchg rdi, rcx
call obj_tag_part
cmp al, OBJ_NUM
jne do_panic_abort
call obj_addr_part
mov rdi, qword [rax + 8] ; arg value
xchg rdi, rcx
mov al, 0
test al, al
._next_arg_done:
ret
global init_env
;; appends var=$rdi bound to val=$rsi to the global environment
@ -809,6 +1041,22 @@ init_env:
lea rsi, [rel p_let_star]
call genv_append_atom_prim_by_idx
mov rdi, ATOM_STR_LEN
lea rsi, [rel p_str_len]
call genv_append_atom_prim_by_idx
mov rdi, ATOM_LIST_LEN
lea rsi, [rel p_list_len]
call genv_append_atom_prim_by_idx
mov rdi, ATOM_STR_PARTS
lea rsi, [rel p_str_decompose]
call genv_append_atom_prim_by_idx
mov rdi, ATOM_SYSCALL
lea rsi, [rel p_syscall]
call genv_append_atom_prim_by_idx
mov rax, qword [rel env]
ret
@ -957,6 +1205,30 @@ init_atoms:
lea rcx, [rel atoms]
mov qword [rcx + ATOM_LET_STAR * 8], rax ; atoms[17] = "let*"
mov rdi, STR_LEN_STR
mov rsi, STR_LEN_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_STR_LEN * 8], rax ; atoms[18] = "str-len"
mov rdi, LIST_LEN_STR
mov rsi, LIST_LEN_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_LIST_LEN * 8], rax ; atoms[21] = "list-len"
mov rdi, STR_PARTS_STR
mov rsi, STR_PARTS_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_STR_PARTS * 8], rax ; atoms[19] = "str-parts"
mov rdi, SYSCALL_STR
mov rsi, SYSCALL_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_SYSCALL * 8], rax ; atoms[20] = "syscall"
ret
@ -964,6 +1236,8 @@ init_atoms:
;; returns 0 if the objects pointed at by $rdi and $rsi are equal, and 1 otherwise.
obj_eq:
sub rsp, 24
cmp rdi, rsi
je .equal
mov qword [rsp], rdi ; save obj1
mov qword [rsp + 8], rsi ; save obj2
call obj_tag_part
@ -972,6 +1246,8 @@ obj_eq:
call obj_tag_part
cmp al, byte [rsp + 16] ; compare tag2 with tag1
jne .not_equal
cmp al, OBJ_BYTE
je .compare_bytes
cmp al, OBJ_ATOM
je .compare_atoms
cmp al, OBJ_STR
@ -985,6 +1261,8 @@ obj_eq:
cmp al, OBJ_PRIM
je .compare_prims
jmp .equal
.compare_bytes:
jmp do_panic_abort
.compare_atoms:
.compare_strs:
call obj_addr_part
@ -1257,8 +1535,10 @@ is_ch:
next_token:
push r14
push rdi
xor r14, r14
sub rsp, 16
mov qword [rsp + 8], 0 ; flags
mov qword [rsp], rdi ; source iterator
.skip_spaces:
mov rsi, ' '
call is_ch
@ -1275,7 +1555,11 @@ next_token:
cmp al, ')'
je .leading_kw
cmp al, "'"
jne .eat
je .leading_kw
cmp al, `"`
sete al
mov byte [rsp + 8], al ; set string-ness if the next char is a quote
jmp .eat
.leading_kw:
mov rdi, qword [rsp]
call getc
@ -1287,11 +1571,46 @@ next_token:
.eat:
mov rdi, qword [rsp]
call getc
; handle escapes if we're in an escape sequence
cmp byte [rsp + 9], 1
jne .skip_escape
movzx ecx, al
cmp al, `n`
mov edi, `\n`
cmove ecx, edi
cmp al, `t`
mov edi, `\t`
cmove ecx, edi
cmp al, `r`
mov edi, `\r`
cmove ecx, edi
lea rdi, [rel buf]
mov byte [rdi + r14], cl
mov byte [rsp + 9], 0
inc r14
jmp .eat
.skip_escape:
cmp al, `\\`
sete cl
mov byte [rsp + 9], cl
je .eat
lea rcx, [rel buf]
lea rcx, [rcx + r14]
mov byte [rcx], al
inc r14
cmp al, `"`
sete dl
mov cl, byte [rsp + 8] ; get string-ness
xchg cl, dl
shl dl, cl ; string-ness = 2^(num_quotes_eaten)
mov byte [rsp + 8], dl
cmp dl, 2
jg .done
cmp dl, 0
jne .eat
mov rdi, qword [rsp]
mov rsi, ' '
call is_ch
@ -1315,7 +1634,7 @@ next_token:
lea rcx, [rel buf]
lea rax, [rcx + r14]
mov byte [rax], 0
pop rdi
add rsp, 16
pop r14
mov al, byte [rel buf]
ret
@ -1328,6 +1647,8 @@ parse_cur_token:
je .quote
cmp byte [rel buf], `"`
je .string
cmp byte [rel buf], `\\`
je .char
pop rdi
call parse_atom
ret
@ -1340,7 +1661,56 @@ parse_cur_token:
call parse_quote
ret
.string:
pop rdi
call parse_string
ret
.char:
pop rdi
call parse_char
ret
;; fn parse_string(&mut iter) -> Char
parse_char:
sub rsp, 8
lea rdi, [rel buf]
call strlen
mov qword [rsp], rax ; len
lea rdi, [rel buf]
mov rsi, rax
lea rdx, [rel SPACE_CHAR]
mov rcx, SPACE_CHAR_LEN
call strcmp
test al, al
je .is_space
lea rdi, [rel buf]
mov rsi, qword [rsp] ; len
lea rdx, [rel NL_CHAR]
mov rcx, NL_CHAR_LEN
call strcmp
test al, al
je .is_newline
lea rdi, [rel buf]
mov rsi, qword [rsp] ; len
lea rdx, [rel TAB_CHAR]
mov rcx, TAB_CHAR_LEN
call strcmp
test al, al
je .is_tab
lea rdi, [rel buf]
mov ah, byte [rdi + 1] ; get the char after the backslash
.done:
add rsp, 8
mov al, 0 ; OBJ_BYTE
ret
.is_space:
mov ah, ' '
jmp .done
.is_newline:
mov ah, `\n`
jmp .done
.is_tab:
mov ah, `\t`
jmp .done
;; fn parse_string(&mut iter) -> Box<[u8]>
parse_string:
@ -1349,7 +1719,7 @@ parse_string:
.loop:
; scan for closing quote
lea rax, [rel buf]
mov al, byte [rax + r12]
mov al, byte [rax + r12 + 1]
; cmp al, `\\`
; je .escape
cmp al, `"`
@ -1363,10 +1733,11 @@ parse_string:
mov rsi, 1
call alloc
lea rdi, [rel buf]
inc rdi
mov rsi, rax
mov rdx, r12
call memcpy
push rax
call memcpy
mov rdi, 16
mov rsi, 8
call alloc

View file

@ -91,6 +91,7 @@ impl fmt::Debug for Object {
return write!(f, "nil");
}
match self.tag() {
0 => write!(f, "'{}'", (self.0.addr() >> 8) as u8 as char),
1 => unsafe {
let car = self.ptr().byte_add(8).cast::<Object>().read();
let cdr = self.ptr().byte_add(16).cast::<Object>().read();
@ -154,7 +155,15 @@ fn test_parse_list() {
let env = unsafe { *ENV_INIT };
println!("env: {:?}\n", env);
let input = b"(let ((x 10) (y 20)) (+ x y))";
// let input = b"(let ((x 10) (y 20)) (+ x y))";
let input = b"(if () 1 2)";
// let hello_world = b"(let* (
// (msg \"hello world\\n\")
// (msg-parts (str-parts msg))
// (ptr (car msg-parts))
// (len (cdr msg-parts))
// (fd 1))
// (syscall 1 fd ptr len))";
let mut src = Source::from(&input[..]);
let sexp = unsafe { parse_next_token(&raw mut src) };
println!("> {:?}", sexp);