; sanctuary ; macros {{{ ; TODO: error handling (once i add that) %macro pspush 1 lea r14, [r14-8] mov qword [r14], r15 mov r15, %1 %endmacro %macro pspop 1 mov %1, r15 mov r15, qword [r14] lea r14, [r14+8] %endmacro %macro psdrop 0 lea r14, [r14+8] %endmacro %define s_latest 0 %macro defdict 3 ; name label flags %strlen slen %1 global lfa_%2 lfa_%2: dq s_latest %define s_latest lfa_%2 ffa_%2: db %3 nfa_%2: db slen db %1 %endmacro %macro defcode 3 defdict %1, %2, %3 %2: %endmacro ; this is just taken from jewelforth, and does not correspond ; to how user variables are planned to work in sanctuary ; so todo make better later? i don't know if it really matters ; because it will only apply to builtin variables. %macro defvar 4 %2: dq %4 defdict %1, %2, %3 pspush qword %2 %endmacro ; }}} %assign INTERPRET 0x0 %assign COMPILING (~0x1) %assign __NR_mprotect 10 %assign __NR_brk 12 %assign __NR_exit 60 section .bss resq 4091 wstk: resq 1 section .text global _start _start: lea r14, [wstk + 8] call brk@ mov qword [dp], r15 mov qword [dp0], r15 mov r15, 0x9c400 call grow call bye defcode "brk@", brk@, 0 xor rdi, rdi mov rax, __NR_brk syscall pspush rax ret defcode "grow", grow, 0 call brk@ pspop rdi pspop r13 add rdi, r13 mov rax, __NR_brk syscall mov qword [dp$], rax ret defcode "executable", executable, 0 mov rdx, 0x7 ; PROT_{READ,WRITE,EXEC} pspop rdi ; addr pspop rsi mov rax, __NR_mprotect syscall ret defcode "here", here, 0 pspush qword [dp] ret defcode "bye", bye, 0 mov rdi, 0 mov rax, __NR_exit syscall ret ; input parsing {{{ ; r11: string character count ; rsi: input buffer address ; al: char being parsed ; r10: end of input buffer defcode "parse-name", parse_name, 0 mov rsi, qword [to_in] mov r10, qword [tib] add rsi, r10 add r10, qword [n_tib] xor rax, rax .wsloop: cmp rsi, r10 jge .empty lodsb cmp al, 0x20 je .wsloop cmp al, 0x09 je .wsloop cmp al, 0x0a je .wsloop cmp rsi, r10 jge .empty mov r11, 1 dec rsi ; bring down by one to point to the start push rsi ; will become `a` inc rsi .wordloop: cmp al, 0x20 je .wordloop_e cmp al, 0x09 je .wordloop_e cmp al, 0x0a je .wordloop_e ; is there a better way of checking before? cmp rsi, r10 jge .wordloop_e inc r11 lodsb je .wordloop .wordloop_e: sub rsi, qword [tib] mov qword [to_in], rsi pop rsi pspush rsi pspush r11 ret .empty: pspush 0 pspush 0 ret ; r11: string character count ; rsi: input buffer address ; al: char being parsed ; r10: end of input buffer defcode "parse", parse, 0 mov rsi, qword [to_in] mov r10, qword [tib] add rsi, r10 add r10, qword [n_tib] xor rax, rax .wsloop: cmp rsi, r10 jge .empty lodsb cmp al, r15b je .wsloop cmp al, 0x0a je .wsloop cmp rsi, r10 jge .empty mov r11, 1 dec rsi ; bring down by one to point to the start push rsi ; will become `a` inc rsi .wordloop: cmp al, r15b je .wordloop_e cmp al, 0x0a je .wordloop_e ; is there a better way of checking before? cmp rsi, r10 jge .wordloop_e inc r11 lodsb je .wordloop .wordloop_e: sub rsi, qword [tib] mov qword [to_in], rsi pop rsi pspop r8 pspush rsi pspush r11 ret .empty: pspop r8 pspush 0 pspush 0 ret ; }}} defvar "state", state, 0, INTERPRET defvar "dp", dp, 0, 0 defvar "dp0", dp0, 0, 0 defvar "dp$", dp$, 0, 0 defvar "tib", tib, 0, 0 ; todo set correct initial value defvar "#tib", n_tib, 0, 0 ; todo set correct initial value defvar ">in", to_in, 0, 0 defvar "latest", latest, 0, lfa_latest