From 927b8517c56311f431036183463af2cc7ab19049 Mon Sep 17 00:00:00 2001 From: janis Date: Fri, 12 Jun 2026 03:17:45 +0200 Subject: [PATCH] can syscall! --- stages/lisp0/lisp.asm | 391 ++++++++++++++++++++++++++++++++++++++++-- stages/lisp0/lisp.rs | 11 +- 2 files changed, 391 insertions(+), 11 deletions(-) diff --git a/stages/lisp0/lisp.asm b/stages/lisp0/lisp.asm index 636dda5..a350714 100644 --- a/stages/lisp0/lisp.asm +++ b/stages/lisp0/lisp.asm @@ -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, 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 diff --git a/stages/lisp0/lisp.rs b/stages/lisp0/lisp.rs index 609e40b..46df7cd 100644 --- a/stages/lisp0/lisp.rs +++ b/stages/lisp0/lisp.rs @@ -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::().read(); let cdr = self.ptr().byte_add(16).cast::().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);