#+HTML_HEAD:
#+TITLE: 小蟬 / cicada-nymph
#+AUTHOR: 謝宇恆 / XIE Yuheng
* todo
*** add length field into jo in assembly code :maybe:
*** memory :limit:
* need to protect the overloading of a variety of memories
*** reading-stack :limit:
* need to find a better way to protect reading-stack
* now
"1 2 3 add add . jo" eval-string
will crush the interpreter
*** div :bug:
* div can not handle the following
-8 2 div .
*** stack :limit:
* there are 64 positions below the all those stacks
when you are belowing-stack so much
bad things happen
*** string-reverse! :bug:
* string-reverse! can not apply on empty-string
* prolog
*** note conditional preprocessing
* flower bar-ket can not be nested in fasm's "match"
so
1. when defining macro conditionally
one should use "if eq" & "finish if"
2. when doing "define" or "equ"
one should use "match { }"
*** platform configuration
#+begin_src fasm :tangle cicada-nymph.fasm
;;;; before you compile the code
;;;; do not forget to choose your platform
;;;; in the following file
include "platform-configuration.inc"
#+end_src
*** misc
#+begin_src fasm :tangle cicada-nymph.fasm
;; in fasm, "dup" is a reserved word
dup equ duplicate
;; in fasm, "end" is a reserved word
finish equ end
end equ exit
#+end_src
*** cell_size :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
cell_size = 8 ;; (byte)
xx equ dq
}
#+end_src
*** cell_size :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
cell_size = 4 ;; (byte)
xx equ dd
rax equ eax
rbx equ ebx
rcx equ ecx
rdx equ edx
rsp equ esp
rbp equ ebp
rsi equ esi
rdi equ edi
syscall equ int 80h
}
#+end_src
*** header :64bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
define linux64_sys_6_r8 r8
define linux64_sys_5_r9 r9
define linux64_sys_4_r10 r10
define linux64_sys_3_rdx rdx
define linux64_sys_2_rsi rsi
define linux64_sys_1_rdi rdi
define linux64_sys_n_rax rax
define linux64_syscall_read 0
define linux64_syscall_write 1
define linux64_syscall_open 2
define linux64_syscall_close 3
define linux64_syscall_exit 60
;; about open & read & write
}
#+end_src
*** format :64bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
format ELF64 executable 3
}
#+end_src
*** entry :64bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
entry begin_to_interpret_threaded_code
segment readable executable writeable
}
#+end_src
*** header :32bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =32bit, platform machine {
define linux32_sys_6_ebp ebp
define linux32_sys_5_edi edi
define linux32_sys_4_esi esi
define linux32_sys_3_edx edx
define linux32_sys_2_ecx ecx
define linux32_sys_1_ebx ebx
define linux32_sys_n_eax eax
define linux32_syscall_exit 1
define linux32_syscall_read 3
define linux32_syscall_write 4
define linux32_syscall_open 5
define linux32_syscall_close 6
}
#+end_src
*** format :32bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =32bit, platform machine {
format ELF executable 3
}
#+end_src
*** entry :32bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =32bit, platform machine {
entry begin_to_interpret_threaded_code
segment readable executable writeable
}
#+end_src
*** memory allocation in un_initialized_memory
* implemented as a memory map
#+begin_src fasm :tangle cicada-nymph.fasm
current_free_address$un_initialized_memory = address$un_initialized_memory
labeling equ = current_free_address$un_initialized_memory
preserve equ current_free_address$un_initialized_memory = current_free_address$un_initialized_memory +
#+end_src
* argument-stack
*** note stack
* when doing "push"
a stack-pointer moves to lower address
* note that another style is that
when doing "push"
a stack-pointer moves to higher address
* the stack-pointer
always stores the address of current-free-address of the stack
* note that another style is that
under the stack-pointer
there always stores the value of the-top-of-the-stack
*** memory allocation
* for we do not build border-check
into the interface of pop and push
we allocation some memory below the stacks
#+begin_src fasm :tangle cicada-nymph.fasm
size$argument_stack = 1024 * 1024 * cell_size
preserve 64 * cell_size
address$argument_stack labeling
preserve size$argument_stack
#+end_src
*** pointer :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
;; if you want to extend cicada in assembly
;; the following registers must NOT be used
define pointer$argument_stack r15
}
#+end_src
*** push & pop :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
macro push_argument_stack register \{
mov [pointer$argument_stack], register
add pointer$argument_stack, cell_size
\}
macro pop_argument_stack register \{
sub pointer$argument_stack, cell_size
mov register, [pointer$argument_stack]
\}
}
#+end_src
*** pointer :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
pointer$argument_stack:
xx address$argument_stack
}
#+end_src
*** push & pop :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
macro push_argument_stack register \{
if register in
push ebx
mov ebx, [pointer$argument_stack]
mov [ebx], register
add ebx, cell_size
mov [pointer$argument_stack], ebx
pop ebx
else
push eax
mov eax, [pointer$argument_stack]
mov [eax], register
add eax, cell_size
mov [pointer$argument_stack], eax
pop eax
finish if
\}
macro pop_argument_stack register \{
if register in
push ebx
mov ebx, [pointer$argument_stack]
sub ebx, cell_size
mov register, [ebx]
mov [pointer$argument_stack], ebx
pop ebx
else
push eax
mov eax, [pointer$argument_stack]
sub eax, cell_size
mov register, [eax]
mov [pointer$argument_stack], eax
pop eax
finish if
\}
}
#+end_src
* return-stack
*** 記
* jo 的詮釋者
決定了 如何入這個棧
* 結尾詞
決定了 如何出這個棧
*** memory allocation
#+begin_src fasm :tangle cicada-nymph.fasm
size$return_stack = 1024 * 1024 * cell_size
preserve 64 * cell_size
address$return_stack labeling
preserve size$return_stack
#+end_src
*** pointer :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
;; if you want to extend cicada in assembly
;; the following registers must NOT be used
define pointer$return_stack r14
}
#+end_src
*** push & pop :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
macro push_return_stack register \{
mov [pointer$return_stack], register
add pointer$return_stack, cell_size
\}
macro pop_return_stack register \{
sub pointer$return_stack, cell_size
mov register, [pointer$return_stack]
\}
}
#+end_src
*** pointer :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
pointer$return_stack:
xx address$return_stack
}
#+end_src
*** push & pop :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
macro push_return_stack register \{
if register in
push ebx
mov ebx, [pointer$return_stack]
mov [ebx], register
add ebx, cell_size
mov [pointer$return_stack], ebx
pop ebx
else
push eax
mov eax, [pointer$return_stack]
mov [eax], register
add eax, cell_size
mov [pointer$return_stack], eax
pop eax
finish if
\}
macro pop_return_stack register \{
if register in
push ebx
mov ebx, [pointer$return_stack]
sub ebx, cell_size
mov register, [ebx]
mov [pointer$return_stack], ebx
pop ebx
else
push eax
mov eax, [pointer$return_stack]
sub eax, cell_size
mov register, [eax]
mov [pointer$return_stack], eax
pop eax
finish if
\}
}
#+end_src
* next
*** macro next
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
macro next \{
pop_return_stack rbx
mov rax, [rbx]
add rbx, cell_size
push_return_stack rbx
jmp qword [rax]
\}
}
match =32bit, machine {
macro next \{
pop_return_stack rbx
mov rax, [rbx]
add rbx, cell_size
push_return_stack rbx
jmp dword [rax]
\}
}
#+end_src
*** note play with jo & jojo
1. at the beginning
* argument-stack
<< 2 >>
* return-stack
#+begin_src return-stack
- [@][@][ (square) ]
(square)
(end)
#+end_src
2. next
* argument-stack
<< 2 >>
* return-stack
#+begin_src return-stack
(square)
- [@][@][ (square) ] - [@][@][ (dup) ]
(end) (mul)
(end)
#+end_src
3. next
* argument-stack
<< 2, 2 >>
* return-stack
#+begin_src return-stack
(square) (dup)
- [@][@][ (square) ] - [@][@][ (mul) ]
(end) (end)
#+end_src
4. next
* argument-stack << 4 >>
* return-stack
#+begin_src return-stack
(dup)
(square) (mul)
- [@][@][ (square) ] - [@][@][ (end) ]
(end)
#+end_src
5. next
* argument-stack << 4 >>
* return-stack
#+begin_src return-stack
(square)
(square)
- [@][@][ (end) ] - [@][@][ (dup) ]
(mul)
(end)
#+end_src
6. next
* argument-stack
<< 4, 4 >>
* return-stack
#+begin_src return-stack
(square)
(square) (dup)
- [@][@][ (end) ] - [@][@][ (mul) ]
(end)
#+end_src
7. next
* argument-stack
<< 16 >>
* return-stack
#+begin_src return-stack
(square) (dup)
(square) (mul)
- [@][@][ (end) ] - [@][@][ (end) ]
#+end_src
8. next
* argument-stack
<< 16 >>
* return-stack
#+begin_src return-stack
(square)
(square)
- [@][@][ (end) ]
#+end_src
9. next
* argument-stack
<< 16 >>
* return-stack
#+begin_src return-stack
- [ ]
#+end_src
10. it is really simple
^-^
is it not ?
* function-jo
*** note jo
* jo as data-structure
|----+----------------|
| | length |
| jo | explainer |
| | body |
|----+----------------|
* the body for
* primitive-function-jo
assembly code
* function-jo
jojo
* variable-jo
value-list
*** offset of jo
#+begin_src fasm :tangle cicada-nymph.fasm
offset__jo$other = - (cell_size * 2)
offset__jo$length = - (cell_size * 1)
offset__jo$body = (cell_size * 1)
#+end_src
*** note link
* link as data-structure
|------+----------------|
| link | link |
|------+----------------|
| | jo |
| | string-address |
| | string-length |
|------+----------------|
* if the link field of a link is 0
the link is the last-link
*** offset of link
#+begin_src fasm :tangle cicada-nymph.fasm
offset__link$jo = (cell_size * 1)
offset__link$string_address = (cell_size * 2)
offset__link$string_length = (cell_size * 3)
#+end_src
*** null link
#+begin_src fasm :tangle cicada-nymph.fasm
;; initial link to point to 0 (as null)
link = 0
#+end_src
*** *string-area*
***** memory allocation
#+begin_src fasm :tangle cicada-nymph.fasm
address$string_area:
times 64 * 1024 db 0
address$core_file:
file "core/core.cn"
end$core_file:
end$string_area:
current_free_address$string_area = address$string_area
#+end_src
***** ASSEMBLY__length_string
#+begin_src fasm :tangle cicada-nymph.fasm
macro ASSEMBLY__length_string string {
virtual at 0
.start$string:
db string
.end$string:
dw (.end$string - .start$string)
load .length word from (.end$string)
finish virtual
}
#+end_src
***** ASSEMBLY__make_string
* note that
the following is using local label
#+begin_src fasm :tangle cicada-nymph.fasm
macro ASSEMBLY__make_string string {
repeat .length
virtual at 0
db string
load .char byte from (% - 1)
finish virtual
store byte .char at (current_free_address$string_area)
current_free_address$string_area = current_free_address$string_area + 1
finish repeat
store byte 0 at (current_free_address$string_area)
current_free_address$string_area = current_free_address$string_area + 1
}
#+end_src
*** note
* note that
after a "next" "jmp" to a explainer
the "rax" stores the value of the jo to be explained
so
"rax" is used as an inexplicit argument
of the following functions
* explain$function is used as jojo-head
and explains the meaning of the jojo as function
* a jojo-head identifies one type of jo
*** define_function
#+begin_src fasm :tangle cicada-nymph.fasm
macro define_function string, jo {
link__#jo:
xx link
link = link__#jo
xx jo
ASSEMBLY__length_string string
.address = current_free_address$string_area
xx .address
xx .length
ASSEMBLY__make_string string
;; xx (end__#jo - jo)/cell_size
xx 0
jo:
xx explain$function
;; here follows a jojo as function-body
}
#+end_src
*** explain$function
* push the jojo of a jo to return-stack
* a jojo can not be of size 0
* use rax as an argument
which stores a jo
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
explain$function:
mov rbx, pointer$return_stack
sub rbx, address$return_stack
test rbx, unit__return_point
jz .return_stack_even
mov rbx, [V__local_memory_odd__current_free_address + cell_size]
push_return_stack rbx
mov rbx, [V__local_memory_even__current_free_address + cell_size]
push_return_stack rbx
mov rbx, [local_variable$current_free_address]
push_return_stack rbx
add rax, cell_size
push_return_stack rax
next
.return_stack_even:
mov rbx, [V__local_memory_even__current_free_address + cell_size]
push_return_stack rbx
mov rbx, [V__local_memory_odd__current_free_address + cell_size]
push_return_stack rbx
mov rbx, [local_variable$current_free_address]
push_return_stack rbx
add rax, cell_size
push_return_stack rax
next
}
match =32bit, machine {
explain$function:
mov rbx, [pointer$return_stack]
sub rbx, address$return_stack
test rbx, unit__return_point
jz .return_stack_even
mov rbx, [V__local_memory_odd__current_free_address + cell_size]
push_return_stack rbx
mov rbx, [V__local_memory_even__current_free_address + cell_size]
push_return_stack rbx
mov rbx, [local_variable$current_free_address]
push_return_stack rbx
add rax, cell_size
push_return_stack rax
next
.return_stack_even:
mov rbx, [V__local_memory_even__current_free_address + cell_size]
push_return_stack rbx
mov rbx, [V__local_memory_odd__current_free_address + cell_size]
push_return_stack rbx
mov rbx, [local_variable$current_free_address]
push_return_stack rbx
add rax, cell_size
push_return_stack rax
next
}
#+end_src
* primitive-function-jo
*** note
* primitive functions are special
they explain themself
and their type is not identified by jojo-head
*** define_primitive_function
#+begin_src fasm :tangle cicada-nymph.fasm
macro define_primitive_function string, jo {
link__#jo:
xx link
link = link__#jo
xx jo
ASSEMBLY__length_string string
.address = current_free_address$string_area
xx .address
xx .length
ASSEMBLY__make_string string
;; xx (end__#jo - jo)
xx 0
jo:
xx assembly_code__#jo
assembly_code__#jo:
;; here follows assembly code
;; as primitive function body
}
#+end_src
* variable-jo
*** note
* no constant
only variable
* when a variable jo in the jojo
it push the value of the variable to argument_stack
* when wish to change a variable's value
use key_word "address" to get the address of the variable
*** define_variable
#+begin_src fasm :tangle cicada-nymph.fasm
macro define_variable string, jo {
link__#jo:
xx link
link = link__#jo
xx jo
ASSEMBLY__length_string string
.address = current_free_address$string_area
xx .address
xx .length
ASSEMBLY__make_string string
;; length
xx 1
jo:
xx explain$variable
;; here follows a value of cell_size
;; only one value is allowed
}
#+end_src
*** explain$variable :64bit:
* in memory
| value-1 |
| ... |
| value-n |
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
explain$variable:
mov rcx, [rax + offset__jo$length]
add rax, cell_size
.loop:
mov rdx, qword [rax]
push_argument_stack rdx
add rax, cell_size
loop .loop
next
}
#+end_src
*** explain$variable :32bit:
* in memory
| value-1 |
| ... |
| value-n |
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
explain$variable:
mov rcx, [rax + offset__jo$length]
add rax, cell_size
.loop:
mov rdx, dword [rax]
push_argument_stack rdx
add rax, cell_size
loop .loop
next
}
#+end_src
* jo
*** apply
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "apply", apply
;; << jo -- unknown >>
pop_argument_stack rax
jmp qword [rax]
}
match =32bit, machine {
define_primitive_function "apply", apply
;; << jo -- unknown >>
pop_argument_stack eax
jmp dword [eax]
}
#+end_src
*** *cell-size*
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "*cell-size*", V__cell_size
xx cell_size
#+end_src
*** jo->explainer
* the type of primitive function jo
is encoded by 0
* other types of jo
are encoded by their explainers
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jo->explainer", jo_to_explainer
;; << jo -- type >>
xx dup
xx dup, get
xx swap, subtraction, literal, cell_size, equal?, false?branch, 4
xx drop, zero
xx end
xx get
xx end
#+end_src
*** jo->length
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jo->length", jo_to_length
;; << jo -- length >>
xx literal, offset__jo$length, addition
xx get
xx end
#+end_src
*** jo,set-length
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jo,set-length", jo__set_length
;; << length, jo -- >>
xx literal, offset__jo$length, addition
xx set
xx end
#+end_src
*** jo->body
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jo->body", jo_to_body
;; << jo -- body >>
xx literal, offset__jo$body
xx addition
xx end
#+end_src
*** primitive-function-jo?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "primitive-function-jo?", primitive_function_jo?
;; << jo -- bool >>
xx jo_to_explainer
xx zero?
xx end
#+end_src
*** function-jo?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "function-jo?", function_jo?
;; << jo -- bool >>
xx jo_to_explainer
xx literal, explain$function
xx equal?
xx end
#+end_src
*** variable-jo?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "variable-jo?", variable_jo?
;; << jo -- bool >>
xx jo_to_explainer
xx literal, explain$variable
xx equal?
xx end
#+end_src
*** variable-jo->address
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "variable-jo->address", variable_jo_to_address
;; << jo -- body >>
xx literal, offset__jo$body
xx addition
xx end
#+end_src
* string-area
*** note interface
* the interface of string-area is not good
one can NOT use n-get and n-set
to get and set value from the address
*** *string-area*
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "*string-area*", V__string_area
xx address$string_area
define_variable "*string-area,size*", V__string_area__size
xx (end$string_area - address$string_area)
;; *string-area,current-free-address*
;; is at epilog
#+end_src
*** string-area,stay
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string-area,stay", string_area__stay
;; << string[address, length] -- >>
xx tuck
xx V__string_area__current_free_address
xx string_to_buffer!
xx address, V__string_area__current_free_address
xx add_set
xx end
#+end_src
*** make-string
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "make-string", make_string
;; << string[address, length] -- string-copy[address, length] >>
xx V__string_area__current_free_address
xx xxswapx
xx tuck
xx string_area__stay
xx end
#+end_src
* return-stack
*** note return-point
* structure
| return-point | conjugate-local-memory |
| | local-memory |
| | local-variable |
| | jojo |
* the interface is implemented by needs
*** offset of return-point
#+begin_src fasm :tangle cicada-nymph.fasm
offset__return_point$conjugate_local_memory = (cell_size * 0)
offset__return_point$local_memory = (cell_size * 1)
offset__return_point$local_variable = (cell_size * 2)
offset__return_point$jojo = (cell_size * 3)
unit__return_point = (cell_size * 4)
#+end_src
*** return-stack:*unit*
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "return-stack:*unit*", V__return_stack__unit
xx unit__return_point
#+end_src
*** 記 插 珠珠 人返回棧 到某珠珠的前面 [等效替換法 以理解]
* 有了下面的 (return-stack:insert-jo) 這個素函數之後
即便是在非素函數中也能夠對返回棧進行有限的操作了
* before insert-jo
|----------------+--------------------------|
| return-point-1 | conjugate-local-memory-1 |
| | local-memory-1 |
| | local-variable-1 |
| | jojo-1 |
|----------------+--------------------------|
| return-point-2 | conjugate-local-memory-2 |
| | local-memory-2 |
| | local-variable-2 |
| | jojo-2 |
|----------------+--------------------------|
* after
jo return-point-2 (return-stack:insert-jo)
|-----------------+--------------------------|
| return-point-1 | conjugate-local-memory-1 |
| | local-memory-1 |
| | local-variable-1 |
| | jojo-1 |
|-----------------+--------------------------|
| return-point-in | conjugate-local-memory-2 |
| | local-memory-2 |
| | local-variable-2 |
| | jojo of jo |
|-----------------+--------------------------|
| return-point-no | local-memory-2 |
| | conjugate-local-memory-2 |
| | local-variable-2 |
| | jojo of nothing |
|-----------------+--------------------------|
| return-point-2 | conjugate-local-memory-2 |
| | local-memory-2 |
| | local-variable-2 |
| | jojo-2 |
|-----------------+--------------------------|
* 之所以有這樣的效果
是因爲
這個 素函數 是爲了 實現 expect 而作
它可能不具有一般性
這要看能不能在被插入的珠珠中使用局部變元
答案看來是肯定的
但是 這想來並不合理
爲什麼我能夠隨意在返回棧中插入珠珠
並且還能在這個珠珠中使用局部變元呢
下面的考慮方式能夠讓人放心
即 返回棧中的各個返回點
是在展開函數體的過程中
用以記錄調用子函數之後應該返回的地址的
其中返回點的序關係 記錄了 函數之間的調用關係
(1) 調用 (2)
因而 (1) 在 (2) 之前
然而插入而得的效果
(1) (in) (2)
並不是 (1) 調用 (in) 調用 (2)
(in) 與周圍的兩個函數之間並沒有調用關係
但是我可以假想出等效的調用關係
[等效替換法 或 等量代換法]
即 插入而得的效果
(1) (in) (2)
可以被想爲是
(1) 調用 (in)
(in) 調用 (2)
並且 (in) 在調用 (2) 之前
沒有使用局部數據區域來爲任何局部變元分配空間
* 上面的討論就證明了這種實現方式性質良好
*** nothing
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "nothing", nothing
xx end
#+end_src
*** return-stack:insert-jo
* 先在個循環中複製 return-stack 之後的部分
以空出一個位置
然後把 jo 插入到空位
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "return-stack:insert-jo", return_stack__insert_jo
;; << jo, return-stack-pointer -- >>
pop_argument_stack rbx
mov rsi, pointer$return_stack
add pointer$return_stack, (cell_size * 4 * 2)
mov rdi, pointer$return_stack
.loop:
cmp rbx, rsi
je .end
sub rsi, cell_size
sub rdi, cell_size
mov rax, qword [rsi]
mov qword [rdi], rax
jmp .loop
.end:
pop_argument_stack rax
add rax, cell_size
mov qword [rsi + (cell_size * 3)], rax
mov rax, qword [rsi + (cell_size * 1)]
mov qword [rsi + (cell_size * 4)], rax
mov rax, qword [rsi + (cell_size * 0)]
mov qword [rsi + (cell_size * 5)], rax
mov rax, qword [rsi + (cell_size * 2)]
mov qword [rsi + (cell_size * 6)], rax
mov rax, nothing + cell_size
mov qword [rsi + (cell_size * 7)], rax
next
}
match =32bit, machine {
define_primitive_function "return-stack:insert-jo", return_stack__insert_jo
;; << jo, return-stack-pointer -- >>
pop_argument_stack rbx
mov rcx, [pointer$return_stack]
mov rsi, rcx
add rcx, (cell_size * 4 * 2)
mov rdi, rcx
mov [pointer$return_stack], rcx
.loop:
cmp rbx, rsi
je .end
sub rsi, cell_size
sub rdi, cell_size
mov rax, dword [rsi]
mov dword [rdi], rax
jmp .loop
.end:
pop_argument_stack rax
add rax, cell_size
mov dword [rsi + (cell_size * 3)], rax
mov rax, dword [rsi + (cell_size * 1)]
mov dword [rsi + (cell_size * 4)], rax
mov rax, dword [rsi + (cell_size * 0)]
mov dword [rsi + (cell_size * 5)], rax
mov rax, dword [rsi + (cell_size * 2)]
mov dword [rsi + (cell_size * 6)], rax
mov rax, nothing + cell_size
mov dword [rsi + (cell_size * 7)], rax
next
}
#+end_src
*** get-return-stack-pointer
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "get-return-stack-pointer", get_return_stack_pointer
push_argument_stack pointer$return_stack
next
}
match =32bit, machine {
define_primitive_function "get-return-stack-pointer", get_return_stack_pointer
mov rax, [pointer$return_stack]
push_argument_stack rax
next
}
#+end_src
*** apply-with-return-point
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "apply-with-return-point", apply_with_return_point
;; << return-point, function -- >>
pop_argument_stack rax
pop_argument_stack rbx
mov pointer$return_stack, rbx
jmp qword [rax]
}
match =32bit, machine {
define_primitive_function "apply-with-return-point", apply_with_return_point
;; << return-point, function -- >>
pop_argument_stack rax
pop_argument_stack rbx
mov [pointer$return_stack], rbx
jmp dword [rax]
}
#+end_src
* end & tail-call
*** end
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "end", end
mov rax, pointer$return_stack
sub rax, address$return_stack
test rax, unit__return_point
jnz .return_stack_even
pop_return_stack rbx
pop_return_stack rax
mov [local_variable$current_free_address], rax
pop_return_stack rax
pop_return_stack rax
mov [V__local_memory_odd__current_free_address + cell_size], rax
next
.return_stack_even:
pop_return_stack rbx
pop_return_stack rax
mov [local_variable$current_free_address], rax
pop_return_stack rax
pop_return_stack rax
mov [V__local_memory_even__current_free_address + cell_size], rax
next
}
match =32bit, machine {
define_primitive_function "end", end
mov rax, [pointer$return_stack]
sub rax, address$return_stack
test rax, unit__return_point
jnz .return_stack_even
pop_return_stack rbx
pop_return_stack rax
mov [local_variable$current_free_address], rax
pop_return_stack rax
pop_return_stack rax
mov [V__local_memory_odd__current_free_address + cell_size], rax
next
.return_stack_even:
pop_return_stack rbx
pop_return_stack rax
mov [local_variable$current_free_address], rax
pop_return_stack rax
pop_return_stack rax
mov [V__local_memory_even__current_free_address + cell_size], rax
next
}
#+end_src
*** tail-call
* tail-call
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "tail-call", tail_call
mov rax, pointer$return_stack
sub rax, address$return_stack
test rax, unit__return_point
jnz .return_stack_even
pop_return_stack rbx
pop_return_stack rax
mov [local_variable$current_free_address], rax
pop_return_stack rax
pop_return_stack rax
mov [V__local_memory_odd__current_free_address + cell_size], rax
mov rax, [rbx]
jmp qword [rax]
.return_stack_even:
pop_return_stack rbx
pop_return_stack rax
mov [local_variable$current_free_address], rax
pop_return_stack rax
pop_return_stack rax
mov [V__local_memory_even__current_free_address + cell_size], rax
mov rax, [rbx]
jmp qword [rax]
}
match =32bit, machine {
define_primitive_function "tail-call", tail_call
mov rax, [pointer$return_stack]
sub rax, address$return_stack
test rax, unit__return_point
jnz .return_stack_even
pop_return_stack rbx
pop_return_stack rax
mov [local_variable$current_free_address], rax
pop_return_stack rax
pop_return_stack rax
mov [V__local_memory_odd__current_free_address + cell_size], rax
mov rax, [rbx]
jmp dword [rax]
.return_stack_even:
pop_return_stack rbx
pop_return_stack rax
mov [local_variable$current_free_address], rax
pop_return_stack rax
pop_return_stack rax
mov [V__local_memory_even__current_free_address + cell_size], rax
mov rax, [rbx]
jmp dword [rax]
}
#+end_src
*** note explicit tail call in action
1. the tail position of a function body must be recognized
explicit tail call is used to achieve this
2. thus
tail-recursive-call can be use to do loop
without pushing too many address into return-stack
3. for example if we have a function
which is called "example"
#+begin_src fasm
define_function "example", example
xx fun1
xx fun2
xx tail_call, example
#+end_src
4. and we have the following jojo in return-stack
#+begin_src return-stack
- [@][@][ (example) ]
(end)
#+end_src
5. next
#+begin_src return-stack
(example)
- [@][@][ (end) ] - [@][@][ (fun1) ]
(fun2)
(tail-call)
(example)
#+end_src
6. next
#+begin_src return-stack
(example) (fun1)
- [@][@][ (end) ] - [@][@][ (fun2) ]
(tail-call)
(example)
#+end_src
7. next
#+begin_src return-stack
(fun1)
(example) (fun2)
- [@][@][ (end) ] - [@][@][ (tail-call) ]
(example)
#+end_src
8. next
by the definition of tail_call
#+begin_src return-stack
(example)
- [@][@][ (end) ] - [@][@][ (fun1) ]
(fun2)
(tail-call)
(example)
#+end_src
9. you can see return-stack of (8.)
is the same as (5.)
it is clear how the example function
is actually a loop now
* helper function in assembly code
*** __exit_with_tos :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
__exit_with_tos:
;; << exit-code -- >>
pop_argument_stack linux64_sys_1_rdi
mov linux64_sys_n_rax, linux64_syscall_exit
syscall
}
match =linux =32bit, platform machine {
__exit_with_tos:
;; << exit-code -- >>
pop_argument_stack linux32_sys_1_ebx
mov linux32_sys_n_eax, linux32_syscall_exit
syscall
}
#+end_src
*** __exit_with_zero :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
__exit_with_zero:
;; << -- >>
xor linux64_sys_1_rdi, linux64_sys_1_rdi
mov linux64_sys_n_rax, linux64_syscall_exit
syscall
}
match =linux =32bit, platform machine {
__exit_with_zero:
;; << -- >>
xor linux32_sys_1_ebx, linux32_sys_1_ebx
mov linux32_sys_n_eax, linux32_syscall_exit
syscall
}
#+end_src
*** __exit_with_six :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
__exit_with_six:
;; << -- >>
mov linux64_sys_1_rdi, 6
mov linux64_sys_n_rax, linux64_syscall_exit
syscall
}
match =linux =32bit, platform machine {
__exit_with_six:
;; << -- >>
mov linux32_sys_1_ebx, 6
mov linux32_sys_n_eax, linux32_syscall_exit
syscall
}
#+end_src
*** __write_string :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
__write_string:
;; << address, length -- >>
pop_argument_stack linux64_sys_3_rdx ;; max length to be write
pop_argument_stack linux64_sys_2_rsi ;; address
mov linux64_sys_1_rdi, 1 ;; stdout
mov linux64_sys_n_rax, linux64_syscall_write
syscall
ret
}
match =linux =32bit, platform machine {
__write_string:
;; << address, length -- >>
pop_argument_stack linux32_sys_3_edx ;; max length to be write
pop_argument_stack linux32_sys_2_ecx ;; address
mov linux32_sys_1_ebx, 1 ;; stdout
mov linux32_sys_n_eax, linux32_syscall_write
syscall
ret
}
#+end_src
*** __reset_argument_stack :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
__reset_argument_stack:
;; << -- >>
mov pointer$argument_stack, address$argument_stack
ret
}
match =linux =32bit, platform machine {
__reset_argument_stack:
;; << -- >>
mov rax, address$argument_stack
mov [pointer$argument_stack], rax
ret
}
#+end_src
*** __reset_return_stack :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
__reset_return_stack:
;; << -- >>
mov pointer$return_stack, address$return_stack
ret
}
match =linux =32bit, platform machine {
__reset_return_stack:
;; << -- >>
mov rax, address$return_stack
mov [pointer$return_stack], rax
ret
}
#+end_src
*** __reset_syntax_stack :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
__reset_syntax_stack:
;; << -- >>
mov rax, address$syntax_stack
mov [V__syntax_stack__pointer + cell_size], rax
ret
#+end_src
*** __reset_local_variable
#+begin_src fasm :tangle cicada-nymph.fasm
__reset_local_variable:
;; << -- >>
mov rax, address$local_variable
mov [local_variable$current_free_address], rax
ret
#+end_src
*** __reset_local_memory
#+begin_src fasm :tangle cicada-nymph.fasm
__reset_local_memory:
;; << -- >>
mov rax, address$local_memory_even
mov [V__local_memory_even__current_free_address + cell_size], rax
mov rax, address$local_memory_odd
mov [V__local_memory_odd__current_free_address + cell_size], rax
ret
#+end_src
* *the-story-begin*
*** 記 匯編代碼中的初始化
* 注意
所入返回棧的應該是 jojo 而不是 jo
*** begin_to_interpret_threaded_code :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux, platform {
begin_to_interpret_threaded_code:
cld ;; set DF = 0, then rsi and rdi are incremented
call __reset_argument_stack
call __reset_return_stack
pop_return_stack rax
pop_return_stack rax
pop_return_stack rax
pop_return_stack rax
mov rax, address$local_memory_odd
push_return_stack rax
mov rax, address$local_memory_even
push_return_stack rax
mov rax, address$local_variable
push_return_stack rax
mov rax, jojo_for__report_return_stack_is_empty_and_exit
push_return_stack rax
mov rax, address$local_memory_even
push_return_stack rax
mov rax, address$local_memory_odd
push_return_stack rax
mov rax, address$local_variable
push_return_stack rax
mov rax, first_jojo
push_return_stack rax
next
first_jojo:
;; xx little_test
xx initialization
xx load_core_file
xx tail_call, basic_REPL
}
#+end_src
*** initialization
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "initialization", initialization
;; << -- >>
xx init__syntax_rule_set__jojo_compiler
xx end
#+end_src
*** note top-level-REPL
* a top-level-REPL always lives at the bottom of return-stack
#+begin_src return-stack
(function)
(function)
- [@][@][ (tail-call) ]
(top-level-REPL)
#+end_src
* right below the return-stack
there is a (report-return-stack-is-empty-and-exit)
so actually
#+begin_src return-stack
(function)
(function)
- [ (report-return-stack-is-empty-and-exit) ] - [@][@][ (tail-call) ]
(top-level-REPL)
#+end_src
* when you say bye to a top-level-REPL
(report-return-stack-is-empty-and-exit) will be executed
*** report-return-stack-is-empty-and-exit
#+begin_src fasm :tangle cicada-nymph.fasm
string$report_return_stack_is_empty_and_exit:
db "* the return-stack is empty now", 10
db " good bye ^-^/", 10
.end:
length$report_return_stack_is_empty_and_exit = (.end - string$report_return_stack_is_empty_and_exit)
define_primitive_function "report-return-stack-is-empty-and-exit", report_return_stack_is_empty_and_exit
;; << -- >>
mov rax, string$report_return_stack_is_empty_and_exit
mov rcx, length$report_return_stack_is_empty_and_exit
push_argument_stack rax
push_argument_stack rcx
call __write_string
call __exit_with_zero
jojo_for__report_return_stack_is_empty_and_exit:
xx report_return_stack_is_empty_and_exit
#+end_src
*** reset-top-level-REPL
* local_variable & local_memory
will get reseted in by this function
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "reset-top-level-REPL", reset_top_level_REPL
;; << top_level_REPL [jo] -- >>
call __reset_return_stack
call __reset_syntax_stack
call __reset_local_variable
call __reset_local_memory
pop_argument_stack rax
jmp qword [rax]
}
match =32bit, machine {
define_primitive_function "reset-top-level-REPL", reset_top_level_REPL
;; << top_level_REPL [jo] -- >>
call __reset_return_stack
call __reset_syntax_stack
call __reset_local_variable
call __reset_local_memory
pop_argument_stack rax
jmp dword [rax]
}
#+end_src
*** exit_with_tos a.k.a. bye
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "bye", exit_with_tos
call __exit_with_tos
#+end_src
*** little_test
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "", V__little_test_number
xx 3
define_function "little_test", little_test
;;;; variable
;; xx V__little_test_number
;; xx exit_with_tos
;;;; exit ocde : 3
;;;; literal
;; xx literal, 4
;; xx exit_with_tos
;;;; exit ocde : 4
;;;; address
;; xx address, V__little_test_number, get, add2
;; xx address, V__little_test_number, set
;; xx V__little_test_number
;; xx exit_with_tos
;;;; exit ocde : 5
;;;; end
;; xx literal, 2, negate
;; xx literal, 8
;; xx addition
;; xx exit_with_tos
;;;; 6
;;;; tail_call
;; xx literal, 2
;; xx literal, 4
;; xx power
;; xx exit_with_tos
;;;; exit ocde : 16
;;;; write_byte
;; xx literal, 64, write_byte
;; xx literal, 10, write_byte
;; xx zero
;; xx exit_with_tos
;;;; @
;;;; read_byte
;; xx read_byte, write_byte
;; xx exit_with_tos
;;;;
;;;; branch
;; xx read_byte, write_byte
;; xx branch, -3
;;;; read a string that ended by
;;;; write the readed string
;;;; or we can say
;;;; read line and write line
;;;; or we can say
;;;; echo line
;;;; false?branch
;; xx false, false?branch, 9
;; xx literal, 64, write_byte
;; xx literal, 10, write_byte
;; xx zero
;; xx exit_with_tos
;; xx true, false?branch, 9
;; xx literal, 65, write_byte
;; xx literal, 10, write_byte
;; xx zero
;; xx exit_with_tos
;; xx zero
;; xx exit_with_tos
;;;; A
;;;; read_word & write_string
;; xx read_word, write_string
;; xx literal, 10, write_byte
;; xx read_word, write_string
;; xx literal, 10, write_byte
;; xx zero
;; xx exit_with_tos
;;;; read line
;;;; write first two words of the line
;;;; string->integer
;; xx read_word, string_to_integer
;; xx exit_with_tos
;;;; type 123
;;;; exit code 123
;;;; xxoverxx
;; xx literal, 1
;; xx literal, 2
;; xx literal, 3
;; xx literal, 4
;; xx xxoverxx
;; xx pretty_write_integer
;; xx pretty_write_integer
;; xx pretty_write_integer
;; xx pretty_write_integer
;; xx pretty_write_integer
;; xx pretty_write_integer
;; xx zero
;; xx exit_with_tos
;;;; 2 1 4 3 2 1
;;;; find_link
;; xx read_word, string_to_integer ;; number
;; xx read_word, string_to_integer ;; number
;; xx read_word, V__link, find_link ;; add
;; xx drop ;; true
;; xx link_to_jo
;; xx apply
;; xx write_integer
;; xx zero
;; xx exit_with_tos
;;;; 1 2 add
;;;; print "3"
;;;; basic-REPL (without the ability to define function)
;;;; after this test
;;;; we will use basic-REPL to do further tests
;; xx basic_REPL
;;;; 1 2 add .
#+end_src
* instruction
*** note side-effect
* an instruction
is a special primitive function
which does special side-effect on return-stack
* note that
side-effect on return-stack
should all be done in primitive functions
*** note naming
* the naming convention in assembly code
of instruction
is the same as it of jo
*** instruction,literal
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "instruction,literal", literal
;; << -- fixnum >>
pop_return_stack rbx
mov rax, [rbx]
push_argument_stack rax
add rbx, cell_size
push_return_stack rbx
next
#+end_src
*** instruction,address
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "instruction,address", address
;; << -- address >>
pop_return_stack rbx
mov rax, [rbx]
add rax, cell_size
push_argument_stack rax
add rbx, cell_size
push_return_stack rbx
next
#+end_src
*** instruction,branch
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "instruction,branch", branch
pop_return_stack rbx
mov rax, [rbx]
imul rax, cell_size
add rbx, rax
push_return_stack rbx
next
#+end_src
*** instruction,false?branch
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "instruction,false?branch", false?branch
;; << true of false -- >>
pop_argument_stack rax
test rax, rax
jnz help__false?branch__not_to_branch
pop_return_stack rbx
mov rax, [rbx]
imul rax, cell_size
add rbx, rax
push_return_stack rbx
next
help__false?branch__not_to_branch:
pop_return_stack rbx
add rbx, cell_size
push_return_stack rbx
next
#+end_src
* the stack
*** note
* the stack is the argument-stack
*** drop
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "drop", drop
;; << a -- >>
pop_argument_stack rax
next
define_primitive_function "drop2", drop2
;; << a b -- >>
pop_argument_stack rax
pop_argument_stack rax
next
#+end_src
*** dup :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "dup", dup
;; << a -- a, a >>
mov rax, [pointer$argument_stack - (1 * cell_size)]
push_argument_stack rax
next
define_primitive_function "dup2", dup2
;; << a b -- a b a b >>
mov rbx, [pointer$argument_stack - (1 * cell_size)]
mov rax, [pointer$argument_stack - (2 * cell_size)]
push_argument_stack rax
push_argument_stack rbx
next
}
#+end_src
*** dup :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
define_primitive_function "dup", dup
;; << a -- a a >>
pop_argument_stack rax
push_argument_stack rax
push_argument_stack rax
next
define_primitive_function "dup2", dup2
;; << a b -- a b a b >>
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rax
push_argument_stack rbx
next
}
#+end_src
*** over :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "over", over
;; << a b -- a b | a >>
mov rax, [pointer$argument_stack - (2 * cell_size)]
push_argument_stack rax
next
define_primitive_function "x|over|xx", xoverxx
;; << a | b c -- a | b c | a >>
mov rax, [pointer$argument_stack - (3 * cell_size)]
push_argument_stack rax
next
define_primitive_function "xx|over|x", xxoverx
;; << a b | c -- a b | c | a b >>
mov rax, [pointer$argument_stack - (3 * cell_size)]
push_argument_stack rax
mov rax, [pointer$argument_stack - (3 * cell_size)]
push_argument_stack rax
next
define_primitive_function "xx|over|xx", xxoverxx
;; << a b | c d -- a b | c d | a b >>
mov rax, [pointer$argument_stack - (4 * cell_size)]
push_argument_stack rax
mov rax, [pointer$argument_stack - (4 * cell_size)]
push_argument_stack rax
next
define_primitive_function "x|over|xxx", xoverxxx
;; << a | b c d -- a | b c d | a >>
mov rax, [pointer$argument_stack - (4 * cell_size)]
push_argument_stack rax
next
define_primitive_function "x|over|xxxx", xoverxxxx
;; << a | b c d -- a | b c d | a >>
mov rax, [pointer$argument_stack - (5 * cell_size)]
push_argument_stack rax
next
define_primitive_function "xx|over|xxxx", xxoverxxxx
;; << a b | c d e f -- a b | c d e f | a b >>
mov rax, [pointer$argument_stack - (6 * cell_size)]
push_argument_stack rax
mov rax, [pointer$argument_stack - (6 * cell_size)]
push_argument_stack rax
next
}
#+end_src
*** over :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
define_primitive_function "over", over
;; << a b -- a b | a >>
mov rbx, [pointer$argument_stack]
mov rax, [rbx - (2 * cell_size)]
push_argument_stack rax
next
define_primitive_function "x|over|xx", xoverxx
;; << a | b c -- a | b c | a >>
mov rbx, [pointer$argument_stack]
mov rax, [rbx - (3 * cell_size)]
push_argument_stack rax
next
define_primitive_function "xx|over|x", xxoverx
;; << a b | c -- a b | c | a b >>
mov rbx, [pointer$argument_stack]
mov rax, [rbx - (3 * cell_size)]
push_argument_stack rax
mov rax, [rbx - (2 * cell_size)]
push_argument_stack rax
next
define_primitive_function "xx|over|xx", xxoverxx
;; << a b | c d -- a b | c d | a b >>
mov rbx, [pointer$argument_stack]
mov rax, [rbx - (4 * cell_size)]
push_argument_stack rax
mov rax, [rbx - (3 * cell_size)]
push_argument_stack rax
next
define_primitive_function "x|over|xxx", xoverxxx
;; << a | b c d -- a | b c d | a >>
mov rbx, [pointer$argument_stack]
mov rax, [rbx - (4 * cell_size)]
push_argument_stack rax
next
define_primitive_function "x|over|xxxx", xoverxxxx
;; << a | b c d -- a | b c d | a >>
mov rbx, [pointer$argument_stack]
mov rax, [rbx - (5 * cell_size)]
push_argument_stack rax
next
define_primitive_function "xx|over|xxxx", xxoverxxxx
;; << a b | c d e f -- a b | c d e f | a b >>
mov rbx, [pointer$argument_stack]
mov rax, [rbx - (6 * cell_size)]
push_argument_stack rax
mov rax, [rbx - (5 * cell_size)]
push_argument_stack rax
next
}
#+end_src
*** tuck
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "tuck", tuck
;; << a b -- b | a b >>
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rbx
push_argument_stack rax
push_argument_stack rbx
next
define_primitive_function "x|tuck|xx", xtuckxx
;; << a | b c -- b c | a | b c >>
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
next
define_primitive_function "xx|tuck|x", xxtuckx
;; << a b | c -- c | a b | c >>
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rcx
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
next
define_primitive_function "xx|tuck|xx", xxtuckxx
;; << a b | c d -- c d | a b | c d >>
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rcx
push_argument_stack rdx
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rdx
next
define_primitive_function "xxx|tuck|x", xxxtuckx
;; << a b c | d -- d | a b c | d >>
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rdx
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rdx
next
define_primitive_function "xxxx|tuck|x", xxxxtuckx
;; << a b c d | e -- e | a b c d | e >>
pop_argument_stack rdi ;; e
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rdi ;; e
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rdx
push_argument_stack rdi ;; e
next
#+end_src
*** swap
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "swap", swap
;; << a b -- b a >>
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rbx
push_argument_stack rax
next
define_primitive_function "x|swap|xx", xswapxx
;; << a | b c -- b c | a >>
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rax
next
define_primitive_function "xx|swap|x", xxswapx
;; << a b | c -- c | a b >>
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rcx
push_argument_stack rax
push_argument_stack rbx
next
define_primitive_function "x|swap|xxx", xswapxxx
;; << a | b c d -- b c d | a >>
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rdx
push_argument_stack rax
next
define_primitive_function "xxx|swap|x", xxxswapx
;; << a b c | d -- d | a b c >>
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rdx
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
next
define_primitive_function "xx|swap|xx", xxswapxx
;; << a b | c d -- c d | a b >>
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rcx
push_argument_stack rdx
push_argument_stack rax
push_argument_stack rbx
next
define_primitive_function "x|swap|xxxx", xswapxxxx
;; << a | b c d e -- b c d e | a >>
pop_argument_stack rsi ;; e
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rdx
push_argument_stack rsi ;; e
push_argument_stack rax
next
define_primitive_function "xxxx|swap|x", xxxxswapx
;; << a b c d | e -- e | a b c d >>
pop_argument_stack rsi ;; e
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rsi ;; e
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rdx
next
define_primitive_function "xx|swap|xxxx", xxswapxxxx
;; << a b | c d e f -- c d e f | a b >>
pop_argument_stack rsi ;; f
pop_argument_stack rdi ;; e
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rcx
push_argument_stack rdx
push_argument_stack rdi ;; e
push_argument_stack rsi ;; f
push_argument_stack rax
push_argument_stack rbx
next
define_primitive_function "xxxx|swap|xx", xxxxswapxx
;; << a b c d | e f -- e f | a b c d >>
pop_argument_stack rsi ;; f
pop_argument_stack rdi ;; e
pop_argument_stack rdx
pop_argument_stack rcx
pop_argument_stack rbx
pop_argument_stack rax
push_argument_stack rdi ;; e
push_argument_stack rsi ;; f
push_argument_stack rax
push_argument_stack rbx
push_argument_stack rcx
push_argument_stack rdx
next
#+end_src
*** address
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "*the-stack*", V__the_stack
xx address$argument_stack
#+end_src
*** pointer
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_variable "*the-stack-pointer-snapshot*", V__the_stack_pointer_snapshot
xx address$argument_stack
define_primitive_function "snapshot-the-stack-pointer", snapshot_the_stack_pointer
;; << -- >>
mov [V__the_stack_pointer_snapshot + cell_size], pointer$argument_stack
next
}
match =32bit, machine {
define_variable "*the-stack-pointer-snapshot*", V__the_stack_pointer_snapshot
xx address$argument_stack
define_primitive_function "snapshot-the-stack-pointer", snapshot_the_stack_pointer
;; << -- >>
mov eax, [pointer$argument_stack]
mov [V__the_stack_pointer_snapshot + cell_size], eax
next
}
#+end_src
*** set-argument-stack-pointer
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "set-argument-stack-pointer", set_argument_stack_pointer
;; << address -- >>
pop_argument_stack pointer$argument_stack
next
}
match =32bit, machine {
define_primitive_function "set-argument-stack-pointer", set_argument_stack_pointer
;; << address -- >>
pop_argument_stack rbx
mov [pointer$argument_stack], rbx
next
}
#+end_src
* bool
*** false & true
* they are defined as function
and viewed as constant
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "false", false
;; << -- false >>
xor rax, rax
push_argument_stack rax
next
define_primitive_function "true", true
;; << -- true >>
xor rax, rax
inc rax
push_argument_stack rax
next
#+end_src
*** false? & true?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "false?", false?
;; << bool -- bool >>
xx false, equal?
xx end
define_function "true?", true?
;; << bool -- bool >>
xx true, equal?
xx end
#+end_src
*** and & or & not & xor
* the following two functions are for bool value
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "not", CICADA__not
;; << bool -- bool >>
xx false, equal?
xx end
define_function "and", CICADA__and
;; << bool, bool -- bool >>
xx false?, false?branch, (.true-$)/cell_size
xx drop
xx false
xx end
.true:
xx false?branch, 3
xx true
xx end
xx false
xx end
define_function "or", CICADA__or
;; << bool, bool -- bool >>
xx false?branch, (.false-$)/cell_size
xx drop
xx true
xx end
.false:
xx false?branch, 3
xx true
xx end
xx false
xx end
define_function "xor", CICADA__xor
;; << bool, bool -- bool >>
xx false?branch, (.false-$)/cell_size
xx CICADA__not
xx end
.false:
xx end
#+end_src
* fixnum
*** zero & one
* they are defined as function
and viewed as constant
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "zero", zero
;; << -- 0 >>
xor rax, rax
push_argument_stack rax
next
define_primitive_function "one", one
;; << -- 1 >>
xor rax, rax
inc rax
push_argument_stack rax
next
#+end_src
*** zero? & one?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "zero?", zero?
;; << bool -- bool >>
xx zero, equal?
xx end
define_function "one?", one?
;; << bool -- bool >>
xx one, equal?
xx end
#+end_src
*** add & sub :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "add1", add1
;; << n -- n+1 >>
inc qword [pointer$argument_stack - (1 * cell_size)]
next
define_primitive_function "add2", add2
;; << n -- n+2 >>
add qword [pointer$argument_stack - (1 * cell_size)], 2
next
define_primitive_function "add3", add3
;; << n -- n+3 >>
add qword [pointer$argument_stack - (1 * cell_size)], 3
next
define_primitive_function "add4", add4
;; << n -- n+4 >>
add qword [pointer$argument_stack - (1 * cell_size)], 4
next
define_primitive_function "add8", add8
;; << n -- n+8 >>
add qword [pointer$argument_stack - (1 * cell_size)], 8
next
define_primitive_function "sub1", sub1
;; << n -- n-1 >>
dec qword [pointer$argument_stack - (1 * cell_size)]
next
define_primitive_function "sub2", sub2
;; << n -- n-2 >>
sub qword [pointer$argument_stack - (1 * cell_size)], 2
next
define_primitive_function "sub3", sub3
;; << n -- n-3 >>
sub qword [pointer$argument_stack - (1 * cell_size)], 3
next
define_primitive_function "sub4", sub4
;; << n -- n-4 >>
sub qword [pointer$argument_stack - (1 * cell_size)], 4
next
define_primitive_function "sub8", sub8
;; << n -- n-8 >>
sub qword [pointer$argument_stack - (1 * cell_size)], 8
next
define_primitive_function "add", addition
;; << a b -- a+b >>
pop_argument_stack rax
add qword [pointer$argument_stack - (1 * cell_size)], rax
next
define_primitive_function "sub", subtraction
;; << a b -- a-b >>
pop_argument_stack rax
sub qword [pointer$argument_stack - (1 * cell_size)], rax
next
}
#+end_src
*** add & sub :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
define_primitive_function "add1", add1
;; << n -- n+1 >>
pop_argument_stack rax
inc rax
push_argument_stack rax
next
define_primitive_function "add2", add2
;; << n -- n+2 >>
pop_argument_stack rax
inc rax
inc rax
push_argument_stack rax
next
define_primitive_function "add3", add3
;; << n -- n+3 >>
pop_argument_stack rax
inc rax
inc rax
inc rax
push_argument_stack rax
next
define_primitive_function "add4", add4
;; << n -- n+4 >>
pop_argument_stack rax
inc rax
inc rax
inc rax
inc rax
push_argument_stack rax
next
define_primitive_function "add8", add8
;; << n -- n+8 >>
pop_argument_stack rax
add rax, 8
push_argument_stack rax
next
define_primitive_function "sub1", sub1
;; << n -- n-1 >>
pop_argument_stack rax
dec rax
push_argument_stack rax
next
define_primitive_function "sub2", sub2
;; << n -- n-2 >>
pop_argument_stack rax
dec rax
dec rax
push_argument_stack rax
next
define_primitive_function "sub3", sub3
;; << n -- n-3 >>
pop_argument_stack rax
dec rax
dec rax
dec rax
push_argument_stack rax
next
define_primitive_function "sub4", sub4
;; << n -- n-4 >>
pop_argument_stack rax
dec rax
dec rax
dec rax
dec rax
push_argument_stack rax
next
define_primitive_function "sub8", sub8
;; << n -- n-8 >>
pop_argument_stack rax
sub rax, 8
push_argument_stack rax
next
define_primitive_function "add", addition
;; << a b -- a+b >>
pop_argument_stack rbx
pop_argument_stack rax
add rax, rbx
push_argument_stack rax
next
define_primitive_function "sub", subtraction
;; << a b -- a-b >>
pop_argument_stack rbx
pop_argument_stack rax
sub rax, rbx
push_argument_stack rax
next
}
#+end_src
*** mul
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "mul", multiple
;; << a b -- a*b >>
pop_argument_stack rbx ;; 2ed arg
pop_argument_stack rax ;; 1st arg
imul rbx, rax
;; imul will ignore overflow
;; when there are two registers as arg
;; imul will set the result into the first register
push_argument_stack rbx
next
#+end_src
*** negate
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "negate", negate
;; << n -- -n >>
xx zero
xx swap, subtraction
xx end
#+end_src
*** power
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "power", power
;; n must be nature number for now
;; << a, n -- a^n >>
;; 1. when a = 0, n =/= 0
;; the power__loop returns 0
;; 2. when a = 0, n = 0
;; the power__loop returns 1
;; but I need it to return 0
xx over, zero?, false?branch, 3
xx drop
xx end
xx literal, 1, swap ;; leave product
xx power__loop
xx end
define_function "power,loop", power__loop
;; << a, product, n -- a^n >>
xx dup, zero?, false?branch, 5
xx drop, swap, drop
xx end
xx sub1
xx swap
xx xoverxx, multiple
xx swap
xx tail_call, power__loop
#+end_src
*** div & mod
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "moddiv", moddiv
;; << a, b -- a mod b, quotient >>
;; << dividend, divisor -- remainder, quotient >>
;; the arg of idiv is divisor
;; the lower half of dividend is taken from rax
;; the upper half of dividend is taken from rdx
xor rdx, rdx ;; high-part of dividend is not used
pop_argument_stack rbx ;; 2ed arg
pop_argument_stack rax ;; 1st arg
idiv rbx
;; the remainder is stored in rdx
;; the quotient is stored in rax
push_argument_stack rdx ;; remainder
push_argument_stack rax ;; quotient
next
define_function "divmod", divmod
;; << a, b -- quotient, a mod b >>
xx moddiv, swap
xx end
define_function "div", division
;; << a, b -- quotient >>
xx divmod, drop
xx end
define_function "mod", modulo
;; << a, b -- a mod b >>
xx moddiv, drop
xx end
#+end_src
*** equal? & greater-than? & less-than?
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "equal?", equal?
;; << a, b -- bool >>
pop_argument_stack rbx
pop_argument_stack rax
cmp rbx, rax
sete al
movzx rax, al
push_argument_stack rax
next
define_primitive_function "less-than?", less_than?
pop_argument_stack rbx
pop_argument_stack rax
cmp rax, rbx
setl al
movzx rax, al
push_argument_stack rax
next
define_primitive_function "greater-than?", greater_than?
pop_argument_stack rbx
pop_argument_stack rax
cmp rax, rbx
setg al
movzx rax, al
push_argument_stack rax
next
define_primitive_function "less-or-equal?", less_or_equal?
pop_argument_stack rbx
pop_argument_stack rax
cmp rax, rbx
setle al
movzx rax, al
push_argument_stack rax
next
define_primitive_function "greater-or-equal?", greater_or_equal?
pop_argument_stack rbx
pop_argument_stack rax
cmp rax, rbx
setge al
movzx rax, al
push_argument_stack rax
next
#+end_src
*** equal2?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "equal2?", equal2?
;; << a1, b1, a2, b2 -- bool >>
xx xswapxx
xx equal?, false?, false?branch, 4
xx drop2, false
xx end
xx equal?
xx end
#+end_src
*** negative? & positive?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "negative?", negative?
;; << integer -- bool >>
xx zero, less_than?
xx end
define_function "positive?", positive?
;; << integer -- bool >>
xx zero, greater_than?
xx end
#+end_src
*** within?
0 1 2 3 are within 0 3
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "within?", within?
;; << x, a, b -- bool >>
xx xoverxx
xx greater_or_equal?, CICADA__not, false?branch, 4
xx drop2
xx false
xx end
xx greater_or_equal?
xx end
#+end_src
*** test
#+begin_src cicada-nymph
: test,within?
<< -- >>
0 0 3 within? .
1 0 3 within? .
2 0 3 within? .
3 0 3 within? .
.l
4 0 3 within? .
-1 0 3 within? .
.l
end
; define-function
test,within?
#+end_src
* memory
*** note get & set
* although the following functions are all side-effect
but I use "set" instead of "set!"
* (get) and (set) default to a cell_size
*** note endianness of n-get & n-set
* big-endian is used
in memory
| value-1 |
| value-2 |
| value-3 |
on stack
<< value-1, value-2, value-3 >>
* thus
what setd into the address
will re-occur when geting through the address
* thus
I do not implement n-get-byte & n-set-byte
for the endianness of machine
might not be big-endian
*** get :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "get", get
;; ( address -- value )
pop_argument_stack rbx
mov rax, [rbx]
push_argument_stack rax
next
define_primitive_function "get-byte", get_byte
;; ( address -- value )
pop_argument_stack rbx
xor rax, rax
mov al, byte[rbx]
push_argument_stack rax
next
define_primitive_function "get-two-bytes", get_two_bytes
;; ( address -- value )
pop_argument_stack rbx
xor rax, rax
mov ax, word [rbx]
push_argument_stack rax
next
define_primitive_function "get-four-bytes", get_four_bytes
;; ( address -- value )
pop_argument_stack rbx
xor rax, rax
mov eax, dword [rbx]
push_argument_stack rax
next
define_primitive_function "n-get", n_get
;; << address, n -- value-1, ..., value-n >>
pop_argument_stack rcx
pop_argument_stack rdx
.loop:
mov rax, qword [rdx]
push_argument_stack rax
add rdx, cell_size
loop .loop
next
}
#+end_src
*** set :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "set", set
;; ( value, address -- )
pop_argument_stack rbx
pop_argument_stack rax
mov [rbx], rax
next
define_primitive_function "set-byte", set_byte
;; ( value, address -- )
pop_argument_stack rbx
pop_argument_stack rax
mov byte[rbx], al
next
define_primitive_function "set-two-bytes", set_two_bytes
;; ( value, address -- )
pop_argument_stack rbx
pop_argument_stack rax
mov word [rbx], ax
next
define_primitive_function "set-four-bytes", set_four_bytes
;; ( value, address -- )
pop_argument_stack rbx
pop_argument_stack rax
mov dword [rbx], eax
next
define_primitive_function "n-set", n_set
;; << value-n, ..., value-1, address, n -- >>
pop_argument_stack rcx
pop_argument_stack rdx
mov rax, cell_size
imul rax, rcx
add rdx, rax
;; for address is based on 0
;; but n is based on 1
sub rdx, cell_size
.loop:
pop_argument_stack rax
mov qword [rdx], rax
sub rdx, cell_size
loop .loop
next
define_primitive_function "add-set", add_set
;; ( number to add, address -- )
pop_argument_stack rbx
pop_argument_stack rax
add qword [rbx], rax
next
define_primitive_function "sub-set", sub_set
;; ( number to sub, address -- )
pop_argument_stack rbx
pop_argument_stack rax
sub qword [rbx], rax
next
}
#+end_src
*** get :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
define_primitive_function "get", get
;; ( address -- value )
pop_argument_stack rbx
mov rax, [rbx]
push_argument_stack rax
next
define_primitive_function "get-byte", get_byte
;; ( address -- value )
pop_argument_stack rbx
xor rax, rax
mov al, byte[rbx]
push_argument_stack rax
next
define_primitive_function "get-two-bytes", get_two_bytes
;; ( address -- value )
pop_argument_stack rbx
xor rax, rax
mov ax, word [rbx]
push_argument_stack rax
next
define_primitive_function "get-four-bytes", get_four_bytes
;; ( address -- value )
pop_argument_stack rbx
xor rax, rax
mov eax, dword [rbx]
push_argument_stack rax
next
define_primitive_function "n-get", n_get
;; << address, n -- value-1, ..., value-n >>
pop_argument_stack rcx
pop_argument_stack rdx
.loop:
mov rax, dword [rdx]
push_argument_stack rax
add rdx, cell_size
loop .loop
next
}
#+end_src
*** set :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
define_primitive_function "set", set
;; ( value, address -- )
pop_argument_stack rbx
pop_argument_stack rax
mov [rbx], rax
next
define_primitive_function "set-byte", set_byte
;; ( value, address -- )
pop_argument_stack rbx
pop_argument_stack rax
mov byte[rbx], al
next
define_primitive_function "set-two-bytes", set_two_bytes
;; ( value, address -- )
pop_argument_stack rbx
pop_argument_stack rax
mov word [rbx], ax
next
define_primitive_function "set-four-bytes", set_four_bytes
;; ( value, address -- )
pop_argument_stack rbx
pop_argument_stack rax
mov dword [rbx], eax
next
define_primitive_function "n-set", n_set
;; << value-n, ..., value-1, address, n -- >>
pop_argument_stack rcx
pop_argument_stack rdx
mov rax, cell_size
imul rax, rcx
add rdx, rax
;; for address is based on 0
;; but n is based on 1
sub rdx, cell_size
.loop:
pop_argument_stack rax
mov dword [rdx], rax
sub rdx, cell_size
loop .loop
next
define_primitive_function "add-set", add_set
;; ( number to add, address -- )
pop_argument_stack rbx
pop_argument_stack rax
add dword [rbx], rax
next
define_primitive_function "sub-set", sub_set
;; ( number to sub, address -- )
pop_argument_stack rbx
pop_argument_stack rax
sub dword [rbx], rax
next
}
#+end_src
*** clear
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "clear-memory", clear_memory
;; << size, address -- >>
pop_argument_stack rdx
pop_argument_stack rcx
xor rax, rax
.loop:
mov byte [rdx], al
inc rdx
dec rcx
loop .loop
next
#+end_src
* bit
*** note
* xor a.k.a. diff
*** or & and & xor & invert :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "bit-and", bit_and
;; << a, b -- a and b >>
pop_argument_stack rbx
and [pointer$argument_stack - (1 * cell_size)], rbx
next
define_primitive_function "bit-or", bit_or
;; << a, b -- a or b >>
pop_argument_stack rbx
or [pointer$argument_stack - (1 * cell_size)], rbx
next
define_primitive_function "bit-xor", bit_xor
;; << a, b -- a xor b >>
pop_argument_stack rbx
xor [pointer$argument_stack - (1 * cell_size)], rbx
next
define_primitive_function "bit-invert", bit_invert
;; << a -- invert a >>
not qword [pointer$argument_stack - (1 * cell_size)]
next
}
#+end_src
*** or & and & xor & invert :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
define_primitive_function "bit-and", bit_and
;; << a, b -- a and b >>
pop_argument_stack rbx
mov rax, [pointer$argument_stack]
and [rax - (1 * cell_size)], rbx
next
define_primitive_function "bit-or", bit_or
;; << a, b -- a or b >>
pop_argument_stack rbx
mov rax, [pointer$argument_stack]
or [rax - (1 * cell_size)], rbx
next
define_primitive_function "bit-xor", bit_xor
;; << a, b -- a xor b >>
pop_argument_stack rbx
mov rax, [pointer$argument_stack]
xor [rax - (1 * cell_size)], rbx
next
define_primitive_function "bit-invert", bit_invert
;; << a -- invert a >>
mov rax, [pointer$argument_stack]
not dword [rax - (1 * cell_size)]
next
}
#+end_src
*** left & right[,sign]
* "shl"
shifts the destination operand left
by the number of bits specified in the second operand
The destination operand can be general register or memory
The second operand can be an immediate value or the CL register
as bits exit from the left, zeros in from the right
The last bit that exited is stored in CF
"sal" is a synonym for "shl"
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "bit-left", bit_left
;; ( fixnum, step -- fixnum * 2^step )
pop_argument_stack rcx
pop_argument_stack rax
shl rax, cl
push_argument_stack rax
next
define_primitive_function "bit-right", bit_right
;; ( fixnum, step -- fixnum / 2^step )
pop_argument_stack rcx
pop_argument_stack rax
shr rax, cl
push_argument_stack rax
next
define_primitive_function "bit-right,sign", bit_right__sign
;; ( fixnum, step -- new fixnum )
pop_argument_stack rcx
pop_argument_stack rax
sar rax, cl
push_argument_stack rax
next
#+end_src
*** get & set & clear & invert
1. offset is of LSB
2. offset in [0, ..., 63]
3. step in [1, ..., 64]
4. >< need error handling on them
#+begin_src fasm :tangle cicada-nymph.fasm
;; BT copies a bit from a given register to the carry flag
define_primitive_function "get-bit", get_bit
;; ( fixnum, offset -- bit )
pop_argument_stack rbx
pop_argument_stack rax
bt rax, rbx
setc al
movzx rax, al
push_argument_stack rax
next
define_primitive_function "set-bit", set_bit
;; ( fixnum, offset -- fixnum )
pop_argument_stack rbx
pop_argument_stack rax
bts rax, rbx
push_argument_stack rax
next
define_primitive_function "clear-bit", clear_bit
;; ( fixnum, offset -- fixnum )
pop_argument_stack rbx
pop_argument_stack rax
btr rax, rbx
push_argument_stack rax
next
define_primitive_function "invert-bit", invert_bit
;; ( fixnum, offset -- fixnum )
pop_argument_stack rbx
pop_argument_stack rax
btc rax, rbx
push_argument_stack rax
next
#+end_src
*** find-[lowest|highest]-set-bit
* "bsf" "bsr"
instructions scan a word or double word for first set bit
and store the index of this bit into destination operand
which must be general register
The bit string being scanned is specified by source operand
it may be either general register or memory
The ZF flag is set if the entire string is zero (no set bits are found)
otherwise it is cleared
* If no set bit is found
the value of the destination register is undefined
"bsf" scans from low order to high order (starting from bit index zero)
"bsr" scans from high order to low order
* note that
if can not find set-bit
the following functions will return -1
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "find-lowest-set-bit", find_lowest_set_bit
;; ( fixnum -- offset )
pop_argument_stack rax
bsf rax, rax
jz @f
push_argument_stack rax
next
@@:
mov rax, -1
push_argument_stack rax
next
define_primitive_function "find-highest-set-bit", find_highest_set_bit
;; ( fixnum -- offset )
pop_argument_stack rax
bsr rax, rax
jz @f
push_argument_stack rax
next
@@:
mov rax, -1
push_argument_stack rax
next
#+end_src
*** test
* test is written in cicada-nymph
for these primitive-functions are added lately
#+begin_src cicada-nymph
2#10011001 2#01100110 bit-or .#2
2#10011001 2#01100110 bit-and .#2
2#10011001 2#11111111 bit-xor .#2
2#10011001 2#10011001 bit-xor .#2
2#10011001 4 bit-left .#2
2#10011001 4 bit-right .#2
2#-10011001 4 bit-right,sign .#2
2#10011001 0 get-bit .
2#10011001 1 get-bit .
2#10011001 2 get-bit .
2#10011001 3 get-bit .
2#10011001 0 clear-bit .#2
2#10011001 1 set-bit .#2
2#10011001 2 set-bit .#2
2#10011001 3 clear-bit .#2
2#10011001 0 invert-bit .#2
2#10011001 1 invert-bit .#2
2#10011001 2 invert-bit .#2
2#10011001 3 invert-bit .#2
2#10011000 find-lowest-set-bit
2#10011000 find-highest-set-bit
2#00000000 find-lowest-set-bit
2#00000000 find-highest-set-bit
#+end_src
* write-byte
*** write-byte :64bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
buffer$write_byte:
db 0
define_primitive_function "write-byte", write_byte
;; << byte -- >>
pop_argument_stack rax
;; write can not just write the byte in al to stdout
;; write needs the address of the byte to write
mov [buffer$write_byte], al
mov linux64_sys_3_rdx, 1 ;; max length to be write
mov linux64_sys_2_rsi, buffer$write_byte ;; address
mov linux64_sys_1_rdi, 1 ;; stdout
mov linux64_sys_n_rax, linux64_syscall_write
syscall
next
}
#+end_src
*** write-byte :32bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =32bit, platform machine {
buffer$write_byte:
db 0
define_primitive_function "write-byte", write_byte
;; << byte -- >>
;; just calls the Linux write system call
pop_argument_stack rax
;; write can not just write the byte in al to stdout
;; write needs the address of the byte to write
mov [buffer$write_byte], al
mov linux32_sys_3_edx, 1 ;; max length to be write
mov linux32_sys_2_ecx, buffer$write_byte ;; address
mov linux32_sys_1_ebx, 1 ;; stdout
mov linux32_sys_n_eax, linux32_syscall_write
syscall
next
}
#+end_src
* reading-stack
*** note
* for we do not build border-check
into the interface of pop and push
we allocation some memory below the stacks
* (read-byte) only sees the tos of reading-stack
* reading-stack helps to implement (eval-string)
push and pop of reading-stack happens in the function (eval-string)
* the interface action on string
i.e. two values
*** memory allocation
#+begin_src fasm :tangle cicada-nymph.fasm
size$reading_stack = 1024 * cell_size
preserve 64 * cell_size
address$reading_stack labeling
preserve size$reading_stack
define_variable "reading-stack:*pointer*", V__reading_stack__pointer
xx address$reading_stack
#+end_src
*** push & pop & drop :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "push-reading-stack", push_reading_stack
;; << string[address, length] -- >>
pop_argument_stack rax
mov rbx, [V__reading_stack__pointer + cell_size]
mov [rbx], rax
add qword [V__reading_stack__pointer + cell_size], cell_size
pop_argument_stack rax
mov rbx, [V__reading_stack__pointer + cell_size]
mov [rbx], rax
add qword [V__reading_stack__pointer + cell_size], cell_size
next
define_primitive_function "pop-reading-stack", pop_reading_stack
;; << -- string[address, length] >>
sub qword [V__reading_stack__pointer + cell_size], cell_size
mov rbx, [V__reading_stack__pointer + cell_size]
mov rax, [rbx]
push_argument_stack rax
sub qword [V__reading_stack__pointer + cell_size], cell_size
mov rbx, [V__reading_stack__pointer + cell_size]
mov rax, [rbx]
push_argument_stack rax
next
define_primitive_function "drop-reading-stack", drop_reading_stack
;; << -- >>
sub qword [V__reading_stack__pointer + cell_size], (cell_size * 2)
next
}
#+end_src
*** push & pop & drop :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
define_primitive_function "push-reading-stack", push_reading_stack
;; << string[address, length] -- >>
pop_argument_stack rax
mov rsi, [V__reading_stack__pointer + cell_size]
mov [rsi], rax
add dword [V__reading_stack__pointer + cell_size], cell_size
pop_argument_stack rax
mov rsi, [V__reading_stack__pointer + cell_size]
mov [rsi], rax
add dword [V__reading_stack__pointer + cell_size], cell_size
next
define_primitive_function "pop-reading-stack", pop_reading_stack
;; << -- string[address, length] >>
sub dword [V__reading_stack__pointer + cell_size], cell_size
mov rsi, [V__reading_stack__pointer + cell_size]
mov rax, [rsi]
push_argument_stack rax
sub dword [V__reading_stack__pointer + cell_size], cell_size
mov rsi, [V__reading_stack__pointer + cell_size]
mov rax, [rsi]
push_argument_stack rax
next
define_primitive_function "drop-reading-stack", drop_reading_stack
;; << -- >>
sub dword [V__reading_stack__pointer + cell_size], (cell_size * 2)
next
}
#+end_src
*** tos
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "tos-reading-stack", tos_reading_stack
;; << -- string[address, length] >>
mov rbx, [V__reading_stack__pointer + cell_size]
sub rbx, cell_size
mov rax, [rbx]
push_argument_stack rax
mov rbx, [V__reading_stack__pointer + cell_size]
sub rbx, cell_size
sub rbx, cell_size
mov rax, [rbx]
push_argument_stack rax
next
#+end_src
*** reading-stack-empty?
* only one string is in reading-stack and it length is zero
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "reading-stack-empty?", reading_stack_empty?
;; << -- bool >>
xx literal, V__reading_stack__pointer + cell_size, get
xx literal, address$reading_stack
xx equal?
xx end
#+end_src
* read-byte
*** note end of file
* do not exit the program
when meeting
so
when you hit
some you will not exit the interpreter
*** note factoring
* reading from file of stdin is slow
thus
1. when reading from file
a whole file is readed at a time
and setd to a buffer
2. when reading from stdin
a whole line is readed at a time
3. note that
reading line instead of keyboard-code
will limit the design of the user interface
* by factoring out the low-level calls
that read a line from stdin
we are able to implement eval-string easily
*** read-line-from-stdin :64bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
define_primitive_function "read-line-from-stdin", read_line_from_stdin
;; << buffer address, max length -- counter >>
pop_argument_stack linux64_sys_3_rdx
pop_argument_stack linux64_sys_2_rsi
xor linux64_sys_1_rdi, linux64_sys_1_rdi ;; stdin
mov linux64_sys_n_rax, linux64_syscall_read
syscall
;; the return value
;; is a count of the number of bytes transferred
push_argument_stack rax
next
}
#+end_src
*** read-line-from-stdin :32bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =32bit, platform machine {
define_primitive_function "read-line-from-stdin", read_line_from_stdin
;; << buffer address, max length -- counter >>
pop_argument_stack linux32_sys_3_edx
pop_argument_stack linux32_sys_2_ecx
xor linux32_sys_1_ebx, linux32_sys_1_ebx ;; stdin
mov linux32_sys_n_eax, linux32_syscall_read
syscall
;; the return value
;; is a count of the number of bytes transferred
push_argument_stack rax
next
}
#+end_src
*** test read-line-from-stdin
#+begin_src fasm
define_function "", test__read_line_from_stdin
xx literal, buffer$reading
xx literal, max_input_length
xx read_line_from_stdin
xx pretty_write_integer
xx literal, buffer$reading
xx literal, 10
xx write_string
xx exit_with_tos
xx end
#+end_src
*** read-byte
#+begin_src fasm :tangle cicada-nymph.fasm
max_input_length = 64 * 1024
buffer$reading labeling
preserve max_input_length
replace$reading labeling
preserve 1024
define_function "read-byte", read_byte
;; << -- byte >>
xx pop_reading_stack
xx dup2, empty_string?, CICADA__not, false?branch, (.bad_tos-$)/cell_size
xx sub1, swap
xx tuck
xx add1, swap
xx push_reading_stack
xx get_byte
xx end
.bad_tos:
xx reading_stack_empty?, false?branch, (.not_empty-$)/cell_size
xx drop2
xx literal, buffer$reading
xx literal, max_input_length
xx read_line_from_stdin
xx dup, positive?, false?branch, (.read_error-$)/cell_size
xx literal, buffer$reading
xx swap
xx push_reading_stack
xx tail_call, read_byte
.read_error:
;; ignore
;; ignore reading error
xx drop
xx literal, buffer$reading
xx literal, 0
xx push_reading_stack
xx tail_call, read_byte
.not_empty:
xx literal, error$read_byte
xx literal, length$read_byte
xx write_string
xx literal, replace$reading
xx literal, 1024
xx read_line_from_stdin
xx literal, replace$reading
xx swap
xx push_reading_stack
xx tail_call, read_byte
error$read_byte:
db "* (read-byte) meets empty-string in reading-stack", 10
db " and this empty-stack is not at the bottom of the reading-stack", 10
db " you can type a line to replace this empty string", 10
.end:
length$read_byte = (.end - error$read_byte)
#+end_src
* load-core-file
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "load-core-file", load_core_file
;; << unknown -- unknown >>
xx literal, address$core_file
xx literal, (end$core_file - address$core_file)
xx push_reading_stack
xx end
#+end_src
* byte
*** space-byte?
* as for space-byte
I only use two
ASCII 10 (newline)
ASCII 32 (whitespace)
* note that
I use the term "whitespace" to denotes the byte
I use the term "space" to denotes the set of bytes
* I will simply view number less-or-equal 32 as space-byte
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "space-byte?", space_byte?
;; << byte -- bool >>
xx literal, 0
xx literal, 32
xx within?
xx end
#+end_src
*** bar-ket-byte?
* () [] {}
but not <>
* double-quote is viewed as special bar-ket-byte
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "bar-ket-byte?", bar_ket_byte?
;; << byte -- bool >>
xx dup, literal, '(', equal?, false?branch, 4
xx drop, true
xx end
xx dup, literal, ')', equal?, false?branch, 4
xx drop, true
xx end
xx dup, literal, '[', equal?, false?branch, 4
xx drop, true
xx end
xx dup, literal, ']', equal?, false?branch, 4
xx drop, true
xx end
xx dup, literal, '{', equal?, false?branch, 4
xx drop, true
xx end
xx dup, literal, '}', equal?, false?branch, 4
xx drop, true
xx end
xx dup, literal, '"', equal?, false?branch, 4
xx drop, true
xx end
xx drop, false
xx end
#+end_src
*** digit-byte?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "digit-byte?", digit_byte?
;; << byte -- bool >>
xx literal, '0'
xx literal, '9'
xx within?
xx end
#+end_src
*** digit-byte->number & number->digit-byte
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "digit-byte->number", digit_byte_to_number
;; << byte -- number >>
xx literal, '0'
xx subtraction
xx end
define_function "number->digit-byte", number_to_digit_byte
;; << number -- byte >>
xx literal, '0'
xx addition
xx end
#+end_src
* buffer
*** note
* a buffer is a large vector
and some functions do not care about how large it is
*** compare-buffer
#+begin_src fasm :tangle cicada-nymph.fasm
;; return false when length == 0
define_primitive_function "compare-buffer", compare_buffer
;; << address, address, length -- bool >>
pop_argument_stack rcx
pop_argument_stack rdi
pop_argument_stack rsi
repe cmpsb
sete al
movzx rax, al
push_argument_stack rax
next
#+end_src
*** cursor->next-matching-byte
* note that
it is the NEXT matching-byte
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "cursor->next-matching-byte", cursor_to_next_matching_byte
;; << cursor, byte -- cursor new address >>
xx over, add1, get_byte
xx over, equal?, false?branch, 4
xx drop, add1
xx end
xx swap
xx add1, swap
xx tail_call, cursor_to_next_matching_byte
#+end_src
* string
*** note io about string
*** write-string
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "write-string", write_string
;; << string[address, length] -- >>
xx dup, zero?, false?branch, 3
xx drop2
xx end
xx sub1, swap
xx dup, get_byte, write_byte
xx add1, swap
xx tail_call, write_string
define_function ".s", ALIAS__write_string
;; << integer -- >>
xx write_string
xx end
#+end_src
*** pretty_write_string
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "pretty-write-string", pretty_write_string
;; << integer -- >>
xx write_string
xx literal, 10
xx write_byte
xx end
#+end_src
*** string:empty?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:empty?", empty_string?
;; << string[address, length] -- bool >>
xx swap, drop
xx zero?
xx end
#+end_src
*** string:equal?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:equal?", string_equal?
;; << string[address, length], string[address, length] -- bool >>
xx xoverxx, equal?, false?branch, 4
xx swap
xx compare_buffer
xx end
xx drop, drop2
xx false
xx end
#+end_src
*** string:byte
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:byte", string__byte
;; << string[address, length] -- byte >>
xx drop, get_byte
xx end
#+end_src
*** string:byte-tail
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:byte-tail", string__byte_tail
;; << string[address, length] -- [address + 1, length - 1] >>
xx sub1, swap
xx add1
xx swap
xx end
#+end_src
*** string:byte-back
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:byte-back", string__byte_back
;; << string[address, length] -- [address - 1, length + 1] >>
xx add1, swap
xx sub1
xx swap
xx end
#+end_src
*** string->buffer!
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "string->buffer!", string_to_buffer!
;; ( string[address, length], buffer[address] -- )
pop_argument_stack rdi ;; destination
pop_argument_stack rcx ;; counter
pop_argument_stack rsi ;; source
rep movsb
next
#+end_src
*** string-reverse! :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
buffer$string_reverse! labeling
preserve 1024
define_primitive_function "string-reverse!", string_reverse!
;; << string[address, length] -- string[address, length] >>
mov rdi, buffer$string_reverse!
mov rcx, [pointer$argument_stack - (1 * cell_size)]
mov rsi, [pointer$argument_stack - (2 * cell_size)]
rep movsb
mov rcx, [pointer$argument_stack - (1 * cell_size)]
dec rdi ;; cursor back into string in buffer$string_reverse!
mov rsi, [pointer$argument_stack - (2 * cell_size)]
.loop:
mov al, byte [rdi]
mov byte [rsi], al
dec rdi
inc rsi
loop .loop
next
}
#+end_src
*** string-reverse! :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
buffer$string_reverse! labeling
preserve 1024
define_primitive_function "string-reverse!", string_reverse!
;; << string[address, length] -- string[address, length] >>
mov rbx, [pointer$argument_stack]
mov rdi, buffer$string_reverse!
mov rcx, [rbx - (1 * cell_size)]
mov rsi, [rbx - (2 * cell_size)]
rep movsb
mov rcx, [rbx - (1 * cell_size)]
dec rdi ;; cursor back into string in buffer$string_reverse!
mov rsi, [rbx - (2 * cell_size)]
.loop:
mov al, byte [rdi]
mov byte [rsi], al
dec rdi
inc rsi
loop .loop
next
}
#+end_src
*** one-byte-string?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "one-byte-string?", one_byte_string?
;; << string[address, length], byte -- bool >>
xx xxswapx
xx dup, one?, false?, false?branch, 5
xx drop2, drop
xx false
xx end
xx string__byte, equal?, false?branch, 3
xx true
xx end
xx false
xx end
#+end_src
*** zero-string?
* "0" or "-0"
0 is special when compiling literal number
for we are using 0 as "end"
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "zero-string?", zero_string?
;; << string[address, length] -- bool >>
xx dup2, literal, '0', one_byte_string?, false?branch, 4
xx drop2, true
xx end
xx dup2
xx string__byte, literal, '-', equal?, false?, false?branch, 4
xx drop2, false
xx end
xx string__byte_tail, literal, '0', one_byte_string?
xx end
#+end_src
*** digit-string?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "digit-string?", digit_string?
;; << string[address, length] -- bool >>
xx dup, zero?, false?branch, 4
xx drop2, true
xx end
xx over, get_byte, digit_byte?, false?branch, 4
xx string__byte_tail
xx tail_call, digit_string?
xx drop2, false
xx end
#+end_src
*** integer-string?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "integer-string?", integer_string?
;; << string[address, length] -- bool >>
xx dup, zero?, false?branch, 4
xx drop2, false
xx end
xx dup2, literal, '-', one_byte_string?, false?branch, 4
xx drop2, false
xx end
xx dup2, string__byte, literal, '-', equal?, false?branch, 4
xx string__byte_tail
xx digit_string?
xx end
xx digit_string?
xx end
#+end_src
*** digit-string->number
#+begin_src fasm :tangle cicada-nymph.fasm
sum$digit_string_to_number:
xx 0
counter$digit_string_to_number:
xx 0
define_function "digit-string->number", digit_string_to_number
;; << string[address, length] -- integer >>
xx zero, literal, sum$digit_string_to_number, set
xx zero, literal, counter$digit_string_to_number, set
xx dup2, string_reverse!
xx help__digit_string_to_number
xx string_reverse!, drop2
xx literal, sum$digit_string_to_number
xx get
xx end
define_function "help,digit-string->number", help__digit_string_to_number
;; << reversed-string[address, length] -- >>
xx dup, zero?, false?branch, 3
xx drop2
xx end
xx dup2, string__byte, digit_byte_to_number
xx literal, 10
xx literal, counter$digit_string_to_number, get
xx one
xx literal, counter$digit_string_to_number
xx add_set
xx power
xx multiple
xx literal, sum$digit_string_to_number
xx add_set
xx string__byte_tail
xx tail_call, help__digit_string_to_number
#+end_src
*** string->integer
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string->integer", string_to_integer
;; << string[address, length] -- integer >>
xx dup2, string__byte, literal, '-', equal?, false?, false?branch, 3
xx digit_string_to_number
xx end
xx string__byte_tail
xx digit_string_to_number
xx negate
xx end
#+end_src
*** string:find-byte
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:find-byte", string__find_byte
;; << string[address, length], byte
;; -- address, true
;; -- false >>
xx over, zero?, false?branch, 5
xx drop, drop2
xx false
xx end
xx xoverxx, get_byte
xx over, equal?, false?branch, 4
xx drop2
xx true
xx end
xx xxswapx
xx string__byte_tail
xx xswapxx
xx tail_call, string__find_byte
#+end_src
*** test
#+begin_src cicada-nymph
: XIE Yuheng ;
32 string:find-byte . << 1 >>
get-byte . << 32 >>
#+end_src
*** string-end,byte
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string-end,byte", string_end__byte
;; << string[address, length] -- byte >>
xx addition, sub1, get_byte
xx end
#+end_src
*** note
* one should use string:space? to make sure
that the string is not space-string
before calling the following functions
*** string:space?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:space?", space_string?
;; << string[address, length] -- bool >>
xx dup, zero?, false?branch, 4
xx drop2, true
xx end
xx dup2, string__byte, space_byte?, false?branch, 4
xx string__byte_tail
xx tail_call, space_string?
xx drop2, false
xx end
#+end_src
*** string:word-begin
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:word-begin", string__word_begin
;; << string[address, length] -- string[address, length] >>
xx dup, zero?, false?branch, 2
;; no error handling
;; the same empty-string is returned
xx end
xx dup2
xx string__byte, space_byte?, false?, false?branch, 2
xx end
xx string__byte_tail
xx tail_call, string__word_begin
#+end_src
*** string:word-end
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:word-end,loop", string__word_end__loop
;; << string[address, length] -- string[address, length] >>
xx dup, zero?, false?branch, 2
;; no error handling
;; the current empty-string is returned
xx end
xx dup2
xx string__byte, space_byte?, false?branch, 2
xx end
xx dup2
xx string__byte, bar_ket_byte?, false?branch, 2
xx end
xx string__byte_tail
xx tail_call, string__word_end__loop
define_function "string:word-end", string__word_end
;; << string[address, length] -- string[address, length] >>
xx dup, zero?, false?branch, 2
;; no error handling
;; the same empty-string is returned
xx end
xx dup2
xx string__byte, bar_ket_byte?, false?branch, 3
xx string__byte_tail
xx end
xx string__word_end__loop
xx end
#+end_src
*** string:word
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:word", string__word
;; << string[address, length] -- word[address, length] >>
xx string__word_begin
xx dup2, string__word_end
xx swap, drop
xx subtraction
xx end
#+end_src
*** string:word-tail
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "string:word-tail", string__word_tail
;; << string[address, length] -- string[address, length] >>
xx string__word_begin
xx string__word_end
xx end
#+end_src
* write number
*** write-number
#+begin_src fasm :tangle cicada-nymph.fasm
;; 2 ^ 64 = 18446744073709551616
;; which is of length 20
;; so
;; I use 32 to align to 16
buffer$write_number labeling
preserve 32
counter$write_number:
xx 0
define_function "write-number", write_number
;; << number -- >>
xx write_number__fill_buffer
xx write_string
xx end
define_function "write-number,fill-buffer", write_number__fill_buffer
;; << number -- string[address, length] >>
xx zero
xx literal, counter$write_number, set
xx write_number__loop
xx literal, buffer$write_number
xx literal, counter$write_number, get
xx string_reverse!
xx end
define_function "write-number,loop", write_number__loop
;; << rest-number -- >>
xx literal, 10, divmod
xx number_to_digit_byte
xx literal, buffer$write_number
xx literal, counter$write_number, get
xx addition
xx set_byte
xx one
xx literal, counter$write_number
xx add_set
xx dup, zero?, false?branch, 3
xx drop
xx end
xx tail_call, write_number__loop
#+end_src
*** write-integer
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "write-integer", write_integer
;; << integer -- >>
xx dup, negative?, false?, false?branch, 3
xx write_number
xx end
xx literal, '-', write_byte
xx negate
xx write_number
xx end
#+end_src
*** pretty_write_integer
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "pretty-write-integer", pretty_write_integer
;; << integer -- >>
xx write_integer
xx literal, 32
xx write_byte
xx end
define_function ".", ALIAS__pretty_write_integer
;; << integer -- >>
xx pretty_write_integer
xx end
#+end_src
* word
*** note io about word
* words are separated by spaces
* a bar-ket is a word
even when there are no spaces around it
*** note bar-ket
* (read-word) is not implemented by (read-byte)
instead it is implemented directly by side-effect on reading-stack
[just like (read-byte)]
thus
we can implement bar-ket as word easily
and nothing like un-read is needed
and
it is (string:word) and (string:word-tail)
these two functions are maintaining the "bar-ket as word" feature
* otherwise
the implementation of (eval-string) will meet problems
*** memory allocation
#+begin_src fasm :tangle cicada-nymph.fasm
max_word_length = 512
buffer$read_word labeling
preserve max_word_length
#+end_src
*** read-word->buffer
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "read-word->buffer", read_word_to_buffer
;; << buffer -- word[address, length] >>
xx pop_reading_stack
xx dup2, space_string?, CICADA__not, false?branch, (.bad_tos-$)/cell_size
xx dup2, string__word_tail, push_reading_stack
xx string__word
xx xoverxx, xxoverx
xx xswapxx
xx string_to_buffer!
xx swap, drop
xx end
.bad_tos:
xx reading_stack_empty?, false?branch, (.not_empty-$)/cell_size
xx drop2
xx literal, buffer$reading
xx literal, max_input_length
xx read_line_from_stdin
xx dup, positive?, false?branch, (.read_error-$)/cell_size
xx literal, buffer$reading
xx swap
xx push_reading_stack
xx tail_call, read_word_to_buffer
.read_error:
;; ignore
;; ignore reading error
xx drop
xx literal, buffer$reading
xx literal, 0
xx push_reading_stack
xx tail_call, read_word_to_buffer
.not_empty:
xx literal, error$read_word_to_buffer
xx literal, length$read_word_to_buffer
xx write_string
xx literal, replace$reading
xx literal, 1024
xx read_line_from_stdin
xx literal, replace$reading
xx swap
xx push_reading_stack
xx tail_call, read_word_to_buffer
error$read_word_to_buffer:
db "* (read-word->buffer) meets empty-string in reading-stack", 10
db " and this empty-stack is not at the bottom of the reading-stack", 10
db " you can type a line to replace this empty string", 10
.end:
length$read_word_to_buffer = (.end - error$read_word_to_buffer)
#+end_src
*** read-word
* read-word will override the word readed before
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "read-word", read_word
;; << -- word[address of buffer$read_word, length] >>
xx literal, buffer$read_word, read_word_to_buffer
xx end
#+end_src
* link
*** last-link?
* fatal error on zero
* the last-link is not zero
but is a link of which the link field is zero
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "last-link?", last_link?
;; << link -- bool >>
xx get, zero?
xx end
#+end_src
*** link->next-link
* fatal error on zero
* the test of zero? is for less core dump
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "link->next-link", link_to_next_link
;; << link -- next-link >>
xx get
xx end
#+end_src
*** link->name-string
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "link->name-string", link_to_name_string
;; << link -- string[address, length] >>
xx literal, offset__link$string_address, addition
xx literal, 2
xx n_get
xx end
#+end_src
*** link->jo
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "link->jo", link_to_jo
;; << link -- jo >>
xx literal, offset__link$jo, addition, get
xx end
#+end_src
*** link,set-jo
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "link,set-jo", link__set_jo
;; << jo, link -- >>
xx literal, offset__link$jo, addition
xx set
xx end
#+end_src
*** *link*
* a link is the first jo in the link
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "*link*", V__link
xx the_last_link_in_assembly_code
#+end_src
*** note about find
* a function whoes name is prefixed by "find"
maybe fail to find
and maybe returns a signal
to inform the function who calls it
*** 記 遍歷鏈表的方式
* 循環進行的方式是
1. 保證循環開始時 所得到的 jo 不是最後一個
2. 處理這個 jo
3. 看看這個 jo 是否是最後一個
* 如果是
退出
* 如果不是
取下一個 jo 以循環
*** find-link
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "find-link", find_link
;; << word[address, length], link
;; -- link, true
;; -- false >>
xx xxtuckx
xx link_to_name_string, xxoverxx
;; xx xxoverxx
;; xx xxoverxx
;; xx write_string, literal, 32, write_byte
;; xx write_string, literal, 10, write_byte
xx string_equal?, false?branch, 4
xx drop2, true
xx end
xx xswapxx
xx dup, last_link?, false?branch, 5
xx drop, drop2
xx false
xx end
xx link_to_next_link
xx tail_call, find_link
#+end_src
* basic-REPL
*** word-interpreter
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "word-interpreter", word_interpreter
;; << word[address, length] -- unknown >>
xx dup2, integer_string?, false?branch, 3
xx string_to_integer
xx end
;; maybe more
xx dup2 ;; for to report undefined word
xx V__link
xx find_link, false?branch, 6
xx xxswapx, drop2
xx link_to_jo, apply
xx end
xx write_undefined_word_report__for_word_interpreter
xx write_string
xx literal, 10
xx write_byte
xx end
define_function "write-undefined-word-report,for-word-interpreter", write_undefined_word_report__for_word_interpreter
;; << -- >>
xx literal, string$undefined_word_report__for_word_interpreter
xx literal, length$undefined_word_report__for_word_interpreter
xx write_string
xx end
string$undefined_word_report__for_word_interpreter:
db "* (word-interpreter) meets undefined word : "
.end:
length$undefined_word_report__for_word_interpreter = (.end - string$undefined_word_report__for_word_interpreter)
#+end_src
*** basic-REPL
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "basic-REPL", basic_REPL
;; << unknown -- unknown >>
xx read_word
xx word_interpreter
xx tail_call, basic_REPL
#+end_src
* colon semicolon
*** note
* from the aesthetics point of view
I do NOT think which of the following is better then the other
but I choose the second one
* first
#+begin_src cicada-nymph
define-function factorial
<< n -- n! >>
dup one? if
end
then
dup sub1 factorial mul
end
end
#+end_src
* second
#+begin_src cicada-nymph
: factorial
<< n -- n! >>
dup one? if
end
then
dup sub1 factorial mul
end
; define-function
#+end_src
*** [colon|semicolon]-string?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "colon-string?", colon_string?
;; << string[address, length] -- bool >>
xx literal, ':'
xx one_byte_string?
xx end
define_function "semicolon-string?", semicolon_string?
;; << string[address, length] -- bool >>
xx literal, ';'
xx one_byte_string?
xx end
#+end_src
*** comment-[begin|end]-string?
#+begin_src fasm :tangle cicada-nymph.fasm
string$comment_begin:
db "<<"
define_function "comment-begin-string?", comment_begin_string?
;; << string[address, length] -- bool >>
xx literal, string$comment_begin
xx literal, 2
xx string_equal?
xx end
string$comment_end:
db ">>"
define_function "comment-end-string?", comment_end_string?
;; << -- >>
xx literal, string$comment_end
xx literal, 2
xx string_equal?
xx end
#+end_src
*** colon & semicolon
* nested : ; is NOT allow
and no error check for it
#+begin_src fasm :tangle cicada-nymph.fasm
buffer$colon labeling
preserve 1024 * 1024
cursor$colon:
xx 0
length$colon:
xx 0
define_function ":", colon
;; << -- string[address of buffer$colon, length] >>
xx literal, buffer$colon
xx literal, cursor$colon, set
xx colon__loop
;; address
xx literal, buffer$colon
;; length
xx literal, cursor$colon, get
xx literal, buffer$colon
xx subtraction
xx dup, literal, length$colon, set
xx end
define_function "colon,loop", colon__loop
;; << -- >>
xx read_byte
xx colon__set_byte
xx colon__meet_end?, false?branch, 7
xx literal, 3 ;; for the string " ; "
xx literal, cursor$colon
xx sub_set
xx end
xx tail_call, colon__loop
define_function "colon,set-byte", colon__set_byte
;; << byte -- >>
xx literal, cursor$colon, get
xx set_byte
xx one
xx literal, cursor$colon
xx add_set
xx end
define_function "colon,meet-end?", colon__meet_end?
;; << -- bool >>
xx literal, cursor$colon, get
xx literal, 3, subtraction
xx get_byte, space_byte?
xx false?, false?branch, 3
xx false
xx end
xx literal, cursor$colon, get
xx literal, 2, subtraction
xx get_byte, literal, ';', equal?
xx false?, false?branch, 3
xx false
xx end
xx literal, cursor$colon, get
xx literal, 1, subtraction
xx get_byte, space_byte?
xx false?, false?branch, 3
xx false
xx end
xx true
xx end
#+end_src
*** ignore-comment
* this function is for basic-REPL
but it is reused by colon
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "<<", ignore_comment
;; << -- >>
xx read_word
xx dup2, comment_begin_string?, false?branch, 5
xx drop2
xx ignore_comment ;; for the new nested-comment
xx tail_call, ignore_comment ;; for the rest-comment
xx dup2, comment_end_string?, false?branch, 3
xx drop2
xx end
xx drop2
xx tail_call, ignore_comment
#+end_src
*** test
#+begin_src cicada-nymph
1 << 989 >> 64 add .
<< 65 >>
: kkk << 989 << 989 >> >> ; .s
<< kkk >>
#+end_src
* jojo-area
*** note
* you can see how the naming convention is used
for functions that create structured data into memory
*** memory allocation
#+begin_src fasm :tangle cicada-nymph.fasm
size$jojo_area = 1024 * 1024 * cell_size
define_variable "*jojo-area*", V__jojo_area
xx address$jojo_area
define_variable "*jojo-area,size*", V__jojo_area__size
xx size$jojo_area
address$jojo_area labeling
preserve size$jojo_area
define_variable "*jojo-area,current-free-address*", V__jojo_area__current_free_address
xx address$jojo_area
#+end_src
*** jojo-area,stay
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jojo-area,stay", jojo_area__stay
;; << number -- >>
xx V__jojo_area__current_free_address
xx set
xx literal, cell_size
xx address, V__jojo_area__current_free_address
xx add_set
xx end
#+end_src
* make-link
* link as data-structure
|------+----------------|
| link | next-link |
|------+----------------|
| | jo |
| | string-address |
| | string-length |
|------+----------------|
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "make-link", make_link
;; << string[address, length], next-link, jo -- link >>
xx V__jojo_area__current_free_address
xx xxxxswapx
xx swap
xx jojo_area__stay
xx jojo_area__stay
xx swap
xx jojo_area__stay
xx jojo_area__stay
xx end
#+end_src
* make-jo-head
* jo as data-structure
|----+----------------|
| | length |
|----+----------------|
| jo | explainer |
|----+----------------|
| | body |
|----+----------------|
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "make-jo-head", make_jo_head
;; << explainer, length -- jo >>
xx jojo_area__stay
xx V__jojo_area__current_free_address
xx swap
xx jojo_area__stay
xx end
#+end_src
* syntax-stack
*** 記
* *syntax-stack* 轉換語境
* *syntax-rule-set:...* 添加語法
*** memory allocate
#+begin_src fasm :tangle cicada-nymph.fasm
preserve cell_size * 64
address$syntax_stack labeling
preserve cell_size * 1024
define_variable "syntax-stack:*address*", V__syntax_stack__address
xx address$syntax_stack
define_variable "syntax-stack:*pointer*", V__syntax_stack__pointer
xx address$syntax_stack
#+end_src
*** push-syntax-stack
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "push-syntax-stack", push_syntax_stack
;; << syntax-set[address] -- >>
xx V__syntax_stack__pointer
xx set
xx V__cell_size
xx address, V__syntax_stack__pointer
xx add_set
xx end
#+end_src
*** pop-syntax-stack
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "pop-syntax-stack", pop_syntax_stack
;; << -- syntax-set[address] >>
xx V__cell_size
xx address, V__syntax_stack__pointer
xx sub_set
xx V__syntax_stack__pointer, get
xx end
#+end_src
*** tos-syntax-stack
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "tos-syntax-stack", tos_syntax_stack
;; << -- syntax-set[address] >>
xx V__syntax_stack__pointer
xx V__cell_size
xx subtraction
xx get
xx end
#+end_src
*** drop-syntax-stack
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "drop-syntax-stack", drop_syntax_stack
;; << -- >>
xx V__cell_size
xx address, V__syntax_stack__pointer
xx sub_set
xx end
#+end_src
*** syntax-stack-empty?
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax-stack-empty?", syntax_stack_empty?
;; << -- bool >>
xx V__syntax_stack__pointer
xx literal, address$syntax_stack
xx equal?
xx end
#+end_src
*** find-syntax
* only search the first syntax-rule-set in syntax-stack
thus a switch of syntax will get you a clean syntax
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "find-syntax", find_syntax
;; << word[address, length]
;; -- function, true
;; -- false >>
xx syntax_stack_empty?, false?branch, 4
xx drop2
xx false
xx end
xx tos_syntax_stack
xx syntax_rule_set__find
xx end
#+end_src
* syntax-rule-set
*** 記 使用
* syntax-rule-set:jojo-compiler 是
語法謂詞 還有 語法函數 這種對子
所形成的有序集合
這樣就可以形成簡單的語境概念了
* 每個語境都有責任
在進入和退出時
維護好 syntax-rule-set:jojo-compiler
* 以 syntax 爲前綴的 function 的類型常常是
1. 以 jojo-compiler 爲後綴時
<< jo, string[address, length], word[address, length]
-- jo, string[address, length] >>
2. 在 REPL 中使用時又可以是
<< word[address, length] -- integer >>
所以在使用時
一定要注意維護棧中的值的良好性
*** note border
* border is current-free-address of syntax-rule-set
*** 記 interface
* structure
| syntax-rule | predicate |
| | function |
* on stack
<< syntax-rule[predicate, function] >>
* set is an ordered set
its interface is as the following
1. (add-syntax-rule)
add a syntax-rule into syntax-rule-set
2. (sub-syntax-rule)
try to sub a syntax-rule from syntax-rule-set
once a time
if can not find the syntax-rule in the syntax-rule-set
do nothing
3. (find-syntax-rule)
find a function from a word
4. (list-syntax-rule)
*** syntax-rule:*unit*
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "syntax-rule:*unit*", V__syntax_rule__unit
xx (cell_size * 2)
#+end_src
*** syntax-rule:get-predicate
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax-rule:get-predicate", syntax_rule__get_predicate
;; << syntax-rule[address] -- predicate >>
xx get
xx end
#+end_src
*** syntax-rule:get-function
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax-rule:get-function", syntax_rule__get_function
;; << syntax-rule[address] -- function >>
xx V__cell_size, addition
xx get
xx end
#+end_src
*** syntax-rule:get
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax-rule:get", syntax_rule__get
;; << syntax-rule[address] -- syntax-rule[predicate, function] >>
xx V__syntax_rule__unit, V__cell_size, division
xx n_get
xx end
#+end_src
*** syntax-rule-set:get-border
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax-rule-set:get-border", syntax_rule_set__get_border
;; << syntax-rule-set -- border >>
xx V__cell_size, subtraction
xx get
xx end
#+end_src
*** syntax-rule-set:set-border
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax-rule-set:set-border", syntax_rule_set__set_border
;; << border, syntax-rule-set -- >>
xx V__cell_size, subtraction
xx set
xx end
#+end_src
*** syntax-rule:add
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax-rule:add", syntax_rule__add
;; << syntax-rule-set: syntax-rule[predicate, function] -- >>
xx xoverxx
xx syntax_rule_set__get_border
xx literal, 2
xx n_set
xx dup
xx syntax_rule_set__get_border
xx V__cell_size, addition
xx V__cell_size, addition
xx swap
xx syntax_rule_set__set_border
xx end
#+end_src
*** syntax-rule-set:find
#+begin_src fasm :tangle cicada-nymph.fasm
cursor$syntax_rule_set__find:
xx 0
define_function "syntax-rule-set:find", syntax_rule_set__find
;; << word[address, length], syntax-rule-set
;; -- function, true
;; -- false >>
xx dup, syntax_rule_set__get_border
xx literal, cursor$syntax_rule_set__find, set
xx syntax_rule_set__find__loop
xx end
define_function "syntax-rule-set:find,loop", syntax_rule_set__find__loop
;; << word[address, length], syntax-rule-set
;; -- function, true
;; -- false >>
xx literal, cursor$syntax_rule_set__find, get
xx over, equal?, false?branch, 5
xx drop, drop2
xx false
xx end
xx xxoverx
xx literal, cursor$syntax_rule_set__find, get
xx V__syntax_rule__unit, subtraction
xx syntax_rule__get_predicate
xx apply, false?branch, (.not_found-$)/cell_size
xx drop, drop2
xx literal, cursor$syntax_rule_set__find, get
xx V__syntax_rule__unit, subtraction
xx syntax_rule__get_function
xx true
xx end
.not_found:
xx literal, cursor$syntax_rule_set__find, get
xx V__cell_size, subtraction
xx V__cell_size, subtraction
xx literal, cursor$syntax_rule_set__find, set
xx tail_call, syntax_rule_set__find__loop
#+end_src
* jojo-compiler:*syntax-rule-set*
*** note
* a syntax is a function to be called at compile time
with a string to be compiled as one argument
and do side-effect to store data into memory
and return a shorter string
[this can be viewed as moving a cursor forward]
*** jojo-compiler-syntax:integer-string
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jojo-compiler-syntax:integer-string", jojo_compiler_syntax__integer_string
;; << string[address, length], word[address, length] --
;; string[address, length] >>
xx literal, literal
xx jojo_area__stay
xx string_to_integer
xx jojo_area__stay
xx end
#+end_src
*** literal-word:address?
#+begin_src fasm :tangle cicada-nymph.fasm
string$word_is_address?:
db "address"
.end:
length$word_is_address? = (.end - string$word_is_address?)
define_function "literal-word:address?", word_is_address?
;; << word[address, length] -- bool >>
xx literal, string$word_is_address?
xx literal, length$word_is_address?
xx string_equal?
xx end
#+end_src
*** jojo-compiler-syntax:address
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jojo-compiler-syntax:address", jojo_compiler_syntax__address
;; << string[address, length], literal-word:address --
;; string[address, length] >>
xx drop2
xx literal, address
xx jojo_area__stay
xx end
#+end_src
*** literal-word:jo?
#+begin_src fasm :tangle cicada-nymph.fasm
string$word_is_jo?:
db "jo"
.end:
length$word_is_jo? = (.end - string$word_is_jo?)
define_function "literal-word:jo?", word_is_jo?
;; << word[address, length] -- bool >>
xx literal, string$word_is_jo?
xx literal, length$word_is_jo?
xx string_equal?
xx end
#+end_src
*** jojo-compiler-syntax:jo
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jojo-compiler-syntax:jo", jojo_compiler_syntax__jo
;; << string[address, length], literal-word:jo --
;; string[address, length] >>
xx drop2
xx literal, literal
xx jojo_area__stay
xx end
#+end_src
*** literal-word:double-quote?
#+begin_src fasm :tangle cicada-nymph.fasm
string$word_is_double_quote?:
db '"'
.end:
length$word_is_double_quote? = (.end - string$word_is_double_quote?)
define_function "literal-word:double-quote?", word_is_double_quote?
;; << word[double-quote, length] -- bool >>
xx literal, string$word_is_double_quote?
xx literal, length$word_is_double_quote?
xx string_equal?
xx end
#+end_src
*** syntax,double-quote,jojo-compiler
* string-area is used
to allocate string literal in function body
* in ASCII encode double-quote is 34
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax,double-quote,jojo-compiler", syntax__double_quote__jojo_compiler
;; << string[address, length], literal-word:double-quote --
;; string[address, length] >>
xx drop2
xx dup2
xx literal, '"', string__find_byte
xx false?branch, (.not_found-$)/cell_size
xx xoverxx, subtraction
;; << string[address, length], new-length >>
xx xxtuckx
xx xoverxx, swap
xx make_string
;; << new-length
;; string[address, length]
;; string[address, new-length] >>
xx swap
;; address
xx literal, literal
xx jojo_area__stay
xx jojo_area__stay
;; new-length
xx literal, literal
xx jojo_area__stay
xx jojo_area__stay
;; << new-length
;; string[address, length] >>
xx xoverxx, subtraction
xx xxswapx
xx addition
xx swap
xx string__byte_tail ;; over the ending double-quote
xx end
.not_found:
xx write_not_integer_string_report__for_double_quote
xx literal, 10, write_byte
xx end
define_function "write-not-integer-string-report,for-double-quote", write_not_integer_string_report__for_double_quote
;; << -- >>
xx literal, string$not_integer_string_report__for_double_quote
xx literal, length$not_integer_string_report__for_double_quote
xx write_string
xx end
string$not_integer_string_report__for_double_quote:
db "* (syntax,double-quote,jojo-compiler) can not find the ending double-quote"
.end:
length$not_integer_string_report__for_double_quote = (.end - string$not_integer_string_report__for_double_quote)
#+end_src
*** note
* comment can not cross ";"
*** syntax,comment,jojo-compiler
* nested << >> must be handled
* note that
syntax,comment,jojo-compiler is a syntax
its interface is special
thus
when implement it as one recursive function
the structure of the function is special
otherwise
just implement it by two functions
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "syntax,comment,jojo-compiler", syntax__comment__jojo_compiler
;; << string[address, length], word --
;; string[address, length] >>
xx drop2
xx dup2, space_string?, false?branch, 2
xx end
xx dup2
xx string__word_tail, xxswapxx
xx string__word
xx dup2, comment_end_string?, false?branch, 3
xx drop2
xx end
xx dup2, comment_begin_string?, CICADA__not, false?branch, 3
xx tail_call, syntax__comment__jojo_compiler
;; nested
xx syntax__comment__jojo_compiler
xx dup2, space_string?, false?branch, 2
xx end
xx dup2
xx string__word_tail, xxswapxx
xx string__word
xx tail_call, syntax__comment__jojo_compiler
#+end_src
*** memory allocation
#+begin_src fasm :tangle cicada-nymph.fasm
size$syntax_rule_set__jojo_compiler = 1024 * cell_size
cursor$syntax_rule_set__jojo_compiler:
xx address$syntax_rule_set__jojo_compiler
address$syntax_rule_set__jojo_compiler:
times size$syntax_rule_set__jojo_compiler db 0
#+end_src
*** jojo-compiler:*syntax-rule-set*
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "jojo-compiler:*syntax-rule-set*", V__syntax_rule_set__jojo_compiler
xx address$syntax_rule_set__jojo_compiler
#+end_src
*** init,syntax-rule-set:jojo-compiler
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "init,syntax-rule-set:jojo-compiler", init__syntax_rule_set__jojo_compiler
;; << -- >>
xx V__syntax_rule_set__jojo_compiler
xx dup
xx literal, integer_string?
xx literal, jojo_compiler_syntax__integer_string
xx syntax_rule__add
xx dup
xx literal, word_is_address?
xx literal, jojo_compiler_syntax__address
xx syntax_rule__add
xx dup
xx literal, word_is_jo?
xx literal, jojo_compiler_syntax__jo
xx syntax_rule__add
xx dup
xx literal, word_is_double_quote?
xx literal, syntax__double_quote__jojo_compiler
xx syntax_rule__add
xx dup
xx literal, local_variable_set_word?
xx literal, jojo_compiler_syntax__local_variable_set
xx syntax_rule__add
xx dup
xx literal, local_variable_get_word?
xx literal, jojo_compiler_syntax__local_variable_get
xx syntax_rule__add
xx literal, comment_begin_string?
xx literal, syntax__comment__jojo_compiler
xx syntax_rule__add
xx end
#+end_src
* jojo-compiler
*** note
* the jojo-compiler is a macro dispatcher
it can be viewed as make-function-body
it gets next word and use predicates on word to do dispatch
* note that
jojo-compiler can be viewed as the "compiler" of the cicada-nymph
it does NOT (can not) compile file to file
but creates structured data directly into memory
*** jojo-compiler,dispatch-word
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jojo-compiler,dispatch-word", jojo_compiler__dispatch_word
;; << jo, string[address, length], word[address, length] --
;; jo, string[address, length] >>
xx dup2
xx find_syntax, false?branch, 3
xx apply
xx end
xx dup2
xx V__link
xx find_link, false?branch, 6
xx xxswapx, drop2
xx link_to_jo
xx jojo_area__stay
xx end
xx write_undefined_word_report__for_jojo_compiler
xx write_string
xx literal, 10, write_byte
xx end
define_function "write-undefined-word-report,for-jojo-compiler", write_undefined_word_report__for_jojo_compiler
;; << -- >>
xx literal, string$undefined_word_report__for_jojo_compiler
xx literal, length$undefined_word_report__for_jojo_compiler
xx write_string
xx end
string$undefined_word_report__for_jojo_compiler:
db "* (jojo-compiler) meets undefined word : "
.end:
length$undefined_word_report__for_jojo_compiler = (.end - string$undefined_word_report__for_jojo_compiler)
#+end_src
*** jojo-compiler
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jojo-compiler", jojo_compiler
;; << jo, string[address, length] -- >>
xx local_variable_table__clear
xx V__syntax_rule_set__jojo_compiler, push_syntax_stack
xx jojo_compiler__loop
xx drop_syntax_stack
xx end
define_function "jojo-compiler,loop", jojo_compiler__loop
;; << jo, string[address, length] -- >>
xx dup2, space_string?, false?branch, 4
xx drop2, drop
xx end
xx dup2
xx string__word_tail
xx xxswapxx
xx string__word
;; << tail[address, length], head[address, length] >>
xx jojo_compiler__dispatch_word
xx tail_call, jojo_compiler__loop
#+end_src
* define-function
*** note
* for the following function
I add the "CICADA__" as prefix
to distinguish from their assembly code version
*** define-function
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "define-function", CICADA__define_function
;; << string[address, length] -- >>
xx dup2, string__word
;; << string[address, length]
;; name-string[address, length] >>
xx make_string
;; << string[address, length]
;; name-string[address, length] >>
xx dup2
xx V__link
xx literal, 0
;; << string[address, length]
;; name-string[address, length]
;; name-string[address, length], next-link, jo >>
xx make_link
;; << string[address, length]
;; name-string[address, length]
;; link >>
xx xxtuckx
xx address, V__link, set
xx drop2
xx literal, explain$function
xx literal, 0
;; << string[address, length]
;; link
;; name-string[address, length], explainer, 0 >>
xx make_jo_head
;; << string[address, length]
;; link
;; jo >>
xx tuck
xx swap
;; << string[address, length]
;; jo
;; jo
;; link >>
xx link__set_jo
;; << string[address, length]
;; jo >>
xx dup
xx xxswapxx
xx string__word_tail
xx V__jojo_area__current_free_address
xx xxxswapx
;; << jo
;; V__jojo_area__current_free_address
;; jo
;; body[address, length] >>
xx jojo_compiler
;; << jo
;; V__jojo_area__current_free_address >>
xx V__jojo_area__current_free_address
xx swap, subtraction
xx V__cell_size, division
xx swap, jo__set_length
xx end
#+end_src
*** test
#+begin_src cicada-nymph
: addadd add add end ; define-function
1 2 3 addadd . << 6 >>
: addadd ; string:word *link* find-link drop
link->jo dup jo->length . << 3 >>
1 2 3 x|swap|xxx apply . << 6 >>
: add1 1 add end ; define-function
1 add1 . << 2 >>
: negate 0 swap sub end ; define-function
1 negate . << -1 >>
#+end_src
*** test double-quote
#+begin_src cicada-nymph
: test,double-quote
<< -- >>
"test,double-quote"
write-string
end
; define-function
test,double-quote
#+end_src
* define-variable,with-tos
*** define-variable,with-tos
* not undo is needed for define-variable,with-tos
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "define-variable,with-tos", CICADA__define_variable__with_tos
;; << value, string[address, length] -- >>
xx dup2, string__word
;; << string[address, length]
;; name-string[address, length] >>
xx make_string
;; << string[address, length]
;; name-string[address, length] >>
xx dup2
xx V__link
xx literal, 0
;; << string[address, length]
;; name-string[address, length]
;; name-string[address, length], next-link, jo >>
xx make_link
;; << string[address, length]
;; name-string[address, length]
;; link >>
xx xxtuckx
xx address, V__link, set
xx drop2
xx literal, explain$variable
xx literal, 0
;; << string[address, length]
;; link
;; name-string[address, length], explainer, 0 >>
xx make_jo_head
;; << string[address, length]
;; link
;; jo >>
xx tuck
xx swap
;; << string[address, length]
;; jo
;; jo
;; link >>
xx link__set_jo
;; << string[address, length]
;; jo >>
xx literal, 1
xx swap
;; << string[address, length]
;; 1
;; jo >>
xx jo__set_length
;; << string[address, length] >>
xx drop2
xx jojo_area__stay
xx end
#+end_src
*** test
#+begin_src cicada-nymph
233 : *three* ; define-variable,with-tos
: add-three *three* add end ; define-function
1 add-three . << 234 >>
<< you get the address of the variable *three*
by add "address" in front of it >>
: fix-*three* 3 address *three* set end ; define-function
fix-*three*
1 add-three . << 4 >>
: jo-*three* jo *three* apply . end ; define-function
jo-*three*
#+end_src
* *local-variable*
*** 記 有名字的局部變量 與 變長的局部數據
* local-variable
用來實現 有名字的局部變量
在編譯時計算 offset
到這個 local-variable 中的 offset 做爲變量的值
有 *local-variable-table* 這個數據結構
幫助計算 offset
* local-memory
用來分配 變長的局部數據
在運行時計算 offset
* 兩個機制配合使用
*** 記 再增加局部變元支持之後 需要重寫的部分
***** 詮釋者 與 收尾詞
* 在進行時
每次進入一個函數體的執行
即 每次將一串珠珠入棧時
同時在這串珠子底部加上
local_variable$current_free_address
即 在 explain$function 中需要做特殊處理
* 這個值在函數退出時
[即 在 end 這個函數中]
用以重置 local_variable$current_free_address
也就是 釋放在這次函數作用過程中所分配的內存
* 每次 >:name 的時候
都更新 local_variable$current_free_address
以分配內存就行了
* 也就是說
return-stack 中的大多數有效值
都是以兩個值一對的方式存在的
* 兩個結尾詞是 end 和 tail_call
對於 tail_call
即 對於明顯的尾遞歸調用
需要利用棧中的值重置 local_variable$current_free_address
但是並不入棧新值
***** 語法擴展方面的支持
* 這裏需要識別 >:name 還有 :name 等等
並對它們做特殊處理
這些東西應該藉助設計良好的語法擴展機制來實現
* 我將使用一個 語法謂詞 的棧
可以發現
這樣的話
我就能很容易地臨時改變語法了
*** 記 總結
***** interface
* 首先要滿足最基本的
長度爲 *cell-size* 的倍數的
局部變量的需求
其次
還要能夠在所申請的局部空間裏使用字符串
這兩種長度的數據結構需要共存
使用 offset 就行了
* 底層
local-data-allocate,jo
這個只讓 local_variable$current_free_address 前進
而不後退
* 注意
最爲重要的特點是
所有的對 局部數據堆 的使用
都必須在編譯時期被靜態地算出來
所以必須設計語法幫助編譯器作計算
>:name :name 用以 分配 和 使用
*cell-size* 倍數大小的內存
* 語義方面
>:name 的重複出現有兩種語義
1. 更新這個局部變元的值
2. 覆蓋上一個局部變元綁定
我選擇第一種
因爲這樣
我就不必設計額外的語法來更新局部變元的值了
比較簡潔
***** 語義特點總結
* 所有有名局部變元的名字與值的對應
都由編譯器處理
* 每個函數體就是一個非常線性的東西
函數體中不能嵌套別的函數體
***** 語用特點總結
* 所有的函數都是全局的
包括輔助函數
* 所以設計輔助函數的時候
應該格外小心
儘量使得輔助函數能夠被重用
* 改代碼並調整對輔助函數的使用
就被稱作是 "re-factoring"
即 函數的因子的重新分解
*** 記 關於分支結構
* 對 包含 >:name 的函數體的處理是純線性的
根本沒有考慮到 函數體中 因 if else then 而產生的分支結構
但是這並不會引起錯誤
原因如下
* 對 >:name 和 :name 的處理是純粹在編譯時期進行的
* 所保證的特性是
1. 每一個 name 在 一個函數體中的出現
都是在編譯時期對 local-variable 的 一個 offset 的分配
2. 同一個 name 在 一個函數體中的出現
會得到相同的 offset
*** memory allocation
#+begin_src fasm :tangle cicada-nymph.fasm
size$local_variable = 1024 * 1024 * cell_size
address$local_variable labeling
preserve size$local_variable
local_variable$current_free_address:
xx address$local_variable
#+end_src
*** 記 注意結尾詞會初始化局部變量指針
* 下面的接口函數必須是 primitive-function
因爲 否則 遇到 end 和 tail_call 的時候
local_variable$current_free_address 就又被初始化了
*** allocate-local-variable
#+begin_src fasm :tangle cicada-nymph.fasm
define_primitive_function "allocate-local-variable", allocate_local_variable
;; << number -- >>
pop_argument_stack rax
imul rax, cell_size
add [local_variable$current_free_address], rax
next
#+end_src
*** note many get & set
* in memory
| 1 : value-1 |
| 1 : value-2 |
| 1 : value-3 |
* on stack
<< value-1, value-2, value-3, ... >>
*** instruction,local-variable,[n-get|n-set] :64bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "instruction,local-variable,n-get", instruction__local_variable__n_get
;; << -- value-1, ..., value-n >>
pop_return_stack rbx
mov rdx, [rbx] ;; offset
mov rcx, [rbx + cell_size] ;; numebr
add rbx, (cell_size * 2)
push_return_stack rbx
mov rbx, [pointer$return_stack - unit__return_point + offset__return_point$local_variable]
add rbx, rdx
.loop:
mov rax, [rbx]
push_argument_stack rax
add rbx, cell_size
loop .loop
next
define_primitive_function "instruction,local-variable,n-set", instruction__local_variable__n_set
;; << value-n, ..., value-1 -- >>
pop_return_stack rbx
mov rdx, [rbx] ;; offset
mov rcx, [rbx + cell_size] ;; numebr
add rbx, (cell_size * 2)
push_return_stack rbx
mov rbx, [pointer$return_stack - unit__return_point + offset__return_point$local_variable]
add rbx, rdx
mov rax, cell_size
imul rax, rcx
add rbx, rax
;; for address is based on 0
;; but n is based on 1
sub rbx, cell_size
.loop:
pop_argument_stack rax
mov [rbx], rax
sub rbx, cell_size
loop .loop
next
}
#+end_src
*** instruction,local-variable,[n-get|n-set] :32bit:
#+begin_src fasm :tangle cicada-nymph.fasm
match =32bit, machine {
define_primitive_function "instruction,local-variable,n-get", instruction__local_variable__n_get
;; << -- value-1, ..., value-n >>
pop_return_stack rbx
mov rdx, [rbx] ;; offset
mov rcx, [rbx + cell_size] ;; numebr
add rbx, (cell_size * 2)
push_return_stack rbx
mov rax, [pointer$return_stack]
mov rbx, [rax - unit__return_point + offset__return_point$local_variable]
add rbx, rdx
.loop:
mov rax, [rbx]
push_argument_stack rax
add rbx, cell_size
loop .loop
next
define_primitive_function "instruction,local-variable,n-set", instruction__local_variable__n_set
;; << value-n, ..., value-1 -- >>
pop_return_stack rbx
mov rdx, [rbx] ;; offset
mov rcx, [rbx + cell_size] ;; numebr
add rbx, (cell_size * 2)
push_return_stack rbx
mov rax, [pointer$return_stack]
mov rbx, [rax - unit__return_point + offset__return_point$local_variable]
add rbx, rdx
mov rax, cell_size
imul rax, rcx
add rbx, rax
;; for address is based on 0
;; but n is based on 1
sub rbx, cell_size
.loop:
pop_argument_stack rax
mov [rbx], rax
sub rbx, cell_size
loop .loop
next
}
#+end_src
*** note example result
* example
#+begin_src cicada-nymph
: example
<< number1, number2, number3, number4
-- number1, number2 >>
>::var2
>::var2
::var2
end
; define-function
#+end_src
* result
#+begin_src fasm
define_function "example", example
;; >::var2
xx literal, 2, allocate_local_variable
xx instruction__local_variable__n_set, 0, 2
;; >::var2
xx instruction__local_variable__n_set, 0, 2
;; ::var2
xx instruction__local_variable__n_get, 0, 2
xx end
#+end_src
*** test
#+begin_src cicada-nymph
: local-variable,test
>:var1
:var1
end
; define-function
1 local-variable,test .
<< 1 >>
: local-variable,test
>:var1
>:var2
>:var3
>:var4
:var4
:var3
:var2
:var1
end
; define-function
1 2 3 4 local-variable,test . . . .
<< 4 3 2 1 >>
: local-variable,test
>:var2
>:var2
>:var1
:var1
:var2
add
end
; define-function
1 2 4 local-variable,test .
<< 3 >>
: local-variable,test,2
<< number1, number2 -- number2 + number3 >>
>::var2
::var2
end
; define-function
1 2 local-variable,test,2 . .
<< 1 2 >>
: local-variable,test,3
<< number1, number2, number3 -- number2 + number3 >>
>::var2
>:var1
::var2
add
end
; define-function
1 2 4 local-variable,test,3 .
<< 6 >>
#+end_src
*** test nested call
#+begin_src cicada-nymph
: k1
9 >:k
:k .
end
; define-function
k1 << 9 >>
: k2
10 >:k
k1
:k .
end
; define-function
k2 << 9 10 >>
#+end_src
*** 記 問題
***** nested block
* 因爲使用全局的 local-variable-table
所以沒法在 nested block 中使用 local-variable
***** branch
* example
#+begin_src cicada-nymph
: example
<< number1, number2, number3, number4
-- number1, number2 >>
>::var2
>::var2
::var2
end
; define-function
#+end_src
* result
#+begin_src fasm
define_function "example", example
;; >::var2
xx literal, 2, allocate_local_variable
xx instruction__local_variable__n_set, 0, 2
;; >::var2
xx instruction__local_variable__n_set, 0, 2
;; ::var2
xx instruction__local_variable__n_get, 0, 2
xx end
#+end_src
* 看上面的例子
可以發現
#+begin_src cicada-nymph
: example
if
>::var2
else
>::var2
then
::var2
end
; define-function
#+end_src
不同分支下
局部變元的編譯效果不等同了
*** local-variable-table
***** 記
* 只有一個 local-variable-table
用以在編譯時期解決局部變元的名與值的對應
這個數據結構被 jojo_compiler_syntax__local_variable_set
和 jojo_compiler_syntax__local_variable_get 所使用
* 其中保存
* offset-in-local-variable
* length-of-string
* address-of-string
* 並且每次在定義一個新的函數體的時候
這個 local-variable-table 會被初始化
***** 記 接口
* (clear)
清空 offset 和 border
* (insert)
插入字符串 和 offset-in-local-variable
* (find)
通過字符串尋找 offset-in-local-variable
* 有兩個全局變量幫助實現這些接口
* (cursor)
每次 find 的時候使用一個新的 cursor 來做循環
* (border)
insert 會擴大 border
find 以 border 爲邊界
* 另外 還有一個全局變量
* (offset)
用以計算 offset-in-local-variable
***** memory allocation
#+begin_src fasm :tangle cicada-nymph.fasm
size$local_variable_table = 100 * 1024
address$local_variable_table labeling
preserve size$local_variable_table
#+end_src
***** local-variable-table,clear
#+begin_src fasm :tangle cicada-nymph.fasm
border$local_variable_table:
xx address$local_variable_table
offset$local_variable_table:
xx 0
define_function "local-variable-table,clear", local_variable_table__clear
;; << -- >>
xx literal, address$local_variable_table
xx literal, border$local_variable_table, set
xx literal, 0
xx literal, offset$local_variable_table, set
xx end
#+end_src
***** local-variable-table,insert
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "local-variable-table,insert", local_variable_table__insert
;; << string[address, length] -- offset >>
;; leave offset
xx literal, offset$local_variable_table, get
xx xxtuckx ;; return value
xx literal, border$local_variable_table, get, set
xx V__cell_size
xx literal, border$local_variable_table, add_set
;; update offset$local_variable_table
xx dup2
xx count_front_colon
xx V__cell_size, multiple
xx literal, offset$local_variable_table, add_set
;; leave length
xx dup
xx literal, border$local_variable_table, get, set
xx V__cell_size
xx literal, border$local_variable_table, add_set
xx tuck ;; for to update border$local_variable_table
;; leave string
xx literal, border$local_variable_table, get
xx string_to_buffer!
;; update border$local_variable_table
xx literal, border$local_variable_table, add_set
xx end
#+end_src
***** local-variable-table,find
#+begin_src fasm :tangle cicada-nymph.fasm
cursor$local_variable_table:
xx address$local_variable_table
define_function "local-variable-table,find", local_variable_table__find
;; << string[address, length]
;; -- offset, true
;; -- false >>
xx literal, address$local_variable_table
xx literal, cursor$local_variable_table, set
xx local_variable_table__find__loop
xx end
define_function "local-variable-table,find,loop", local_variable_table__find__loop
;; << string[address, length]
;; -- offset, true
;; -- false >>
xx literal, cursor$local_variable_table, get
xx literal, border$local_variable_table, get
xx greater_or_equal?, false?branch, 4
xx drop2
xx false
xx end
xx dup2
xx literal, cursor$local_variable_table, get
xx V__cell_size, addition
xx V__cell_size, addition ;; address of string
xx literal, cursor$local_variable_table, get
xx V__cell_size, addition
xx get ;; length of string
xx string_equal?, false?branch, 8
xx drop2
xx literal, cursor$local_variable_table, get
xx get ;; offset
xx true
xx end
xx literal, cursor$local_variable_table, get
xx V__cell_size, addition
xx get ;; length of string
xx V__cell_size, addition
xx V__cell_size, addition
xx literal, cursor$local_variable_table, add_set
xx tail_call, local_variable_table__find__loop
#+end_src
***** count-front-colon
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "count-front-colon", count_front_colon
;; << string[address, length] -- number >>
xx literal, 0 ;; counter
xx count_front_colon__loop
xx end
define_function "count-front-colon,loop", count_front_colon__loop
;; << string[address, length], counter -- number >>
xx over, zero?, false?branch, 4
xx xxswapx, drop2
xx end
xx xxoverx, string__byte
xx literal, ':', equal?, false?, false?branch, 4
xx xxswapx, drop2
xx end
xx add1, xxswapx
xx string__byte_tail, xswapxx
xx tail_call, count_front_colon__loop
#+end_src
*** two syntaxes
***** local-variable-get-word?
* :name
::name
* but not
:name:
::name:
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "local-variable-get-word?", local_variable_get_word?
;; << string[address, length] -- bool >>
xx dup, zero?, false?branch, 4
xx drop2, false
xx end
xx dup2, addition, sub1
xx get_byte, literal, ':'
xx equal?, false?branch, 4
xx drop2, false
xx end
xx dup2, count_front_colon
xx dup, literal, 0, greater_than?, false?, false?branch, 5
xx drop, drop2, false
xx end
xx subtraction
xx swap, drop
xx literal, 0, greater_than?
xx end
#+end_src
***** jojo-compiler-syntax:local-variable-get
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jojo-compiler-syntax:local-variable-get", jojo_compiler_syntax__local_variable_get
;; << string[address, length], word[address, length] --
;; string[address, length] >>
xx dup2
xx local_variable_table__find, false?branch, (.not_found-$)/cell_size
;; instruction__local_variable__n_get, , n
xx literal, instruction__local_variable__n_get
xx jojo_area__stay
;; offset
xx jojo_area__stay
;; n
xx count_front_colon
xx jojo_area__stay
xx end
.not_found:
xx write_local_variable_not_bound_report
xx write_string
xx literal, 10, write_byte
xx end
define_function "write-local-variable-not-bound-report", write_local_variable_not_bound_report
xx literal, string$local_variable_not_bound_report
xx literal, length$local_variable_not_bound_report
xx write_string
xx end
string$local_variable_not_bound_report:
db "* local-variable not bound : "
.end:
length$local_variable_not_bound_report = (.end - string$local_variable_not_bound_report)
#+end_src
***** local-variable-set-word?
* >:name
>::name
* but not
>:name:
>::name:
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "local-variable-set-word?", local_variable_set_word?
;; << string[address, length] -- bool >>
xx dup, zero?, false?branch, 4
xx drop2, false
xx end
xx dup2, addition, sub1
xx get_byte, literal, ':'
xx equal?, false?branch, 4
xx drop2, false
xx end
xx dup2, string__byte
xx literal, '>', equal?, false?, false?branch, 4
xx drop2, false
xx end
xx string__byte_tail
xx dup2, count_front_colon
xx dup, literal, 0, greater_than?, false?, false?branch, 5
xx drop, drop2, false
xx end
xx subtraction
xx swap, drop
xx literal, 0, greater_than?
xx end
#+end_src
***** jojo-compiler-syntax:local-variable-set
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "jojo-compiler-syntax:local-variable-set", jojo_compiler_syntax__local_variable_set
;; << string[address, length], word[address, length] --
;; string[address, length] >>
xx string__byte_tail
xx dup2
xx local_variable_table__find, false?branch, (.not_found-$)/cell_size
;; instruction__local_variable__n_set, , n
xx literal, instruction__local_variable__n_set
xx jojo_area__stay
;; offset
xx jojo_area__stay
;; n
xx count_front_colon
xx jojo_area__stay
xx end
.not_found:
xx dup2
xx local_variable_table__insert
xx xxswapx
xx count_front_colon
;; literal, , allocate_local_variable
xx literal, literal
xx jojo_area__stay
;; number of jo
xx dup, jojo_area__stay
xx literal, allocate_local_variable
xx jojo_area__stay
;; instruction__local_variable__n_set, , n
xx literal, instruction__local_variable__n_set
xx jojo_area__stay
;; offset
xx swap
xx jojo_area__stay
;; n
xx jojo_area__stay
xx end
#+end_src
* *local-memory*
*** 記
* 並不需要給這裏的接口設計特殊的語法擴展
直接使用函數就可以了
* 但是接口必須是 primitive-function
因爲 否則 遇到 end 和 tail_call 的時候
local_memory 就又被初始化了
*** 記 使用
* 在一個函數內
用 allocate-local-memory 所申請的局部數據空間
是可以被這個函數內所調用的函數所使用的
但是當函數退出的時候
其所申請的空間就被結尾珠 (end) 收回了
而沒法重用了
而用 (tail-call) 來實現循環的時候
下一次函數的執行過程中
所申請的 allocate-local-memory
和上一次函數執行過程中
所申請的 allocate-local-memory 是相同的
*** *local-memory-even*
#+begin_src fasm :tangle cicada-nymph.fasm
size$local_memory_even = 1024 * 1024 * 6
address$local_memory_even labeling
preserve size$local_memory_even
define_variable "*local-memory-even,current-free-address*", V__local_memory_even__current_free_address
xx address$local_memory_even
#+end_src
*** *local-memory-odd*
#+begin_src fasm :tangle cicada-nymph.fasm
size$local_memory_odd = 1024 * 1024 * 6
address$local_memory_odd labeling
preserve size$local_memory_odd
define_variable "*local-memory-odd,current-free-address*", V__local_memory_odd__current_free_address
xx address$local_memory_odd
#+end_src
*** 記 奇偶性
* 用 返回棧指針 來判斷 奇偶性
對 explain$function 來說
當前的 return-point 就是 返回棧指針
對 結尾珠 還有 下面的兩個函數來說
當前的 return-point 就是 返回棧指針 之前的 一個單位
* 在偶的 return-point
其中 local-memory 域 是 local-memory-even
其中 conjugate-local-memory 域 是 local-memory-odd
在奇的 return-point 則相反
* 函數退出時只重置 local-memory 而不重置 conjugate-local-memory
*** allocate-local-memory
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "allocate-local-memory", allocate_local_memory
;; << size -- address >>
mov rax, pointer$return_stack
sub rax, address$return_stack
test rax, unit__return_point
jnz .return_stack_even
pop_argument_stack rbx
mov rax, [V__local_memory_odd__current_free_address + cell_size]
push_argument_stack rax
add [V__local_memory_odd__current_free_address + cell_size], rbx
next
.return_stack_even:
pop_argument_stack rbx
mov rax, [V__local_memory_even__current_free_address + cell_size]
push_argument_stack rax
add [V__local_memory_even__current_free_address + cell_size], rbx
next
}
match =32bit, machine {
define_primitive_function "allocate-local-memory", allocate_local_memory
;; << size -- address >>
mov rax, [pointer$return_stack]
sub rax, address$return_stack
test rax, unit__return_point
jnz .return_stack_even
pop_argument_stack rbx
mov rax, [V__local_memory_odd__current_free_address + cell_size]
push_argument_stack rax
add [V__local_memory_odd__current_free_address + cell_size], rbx
next
.return_stack_even:
pop_argument_stack rbx
mov rax, [V__local_memory_even__current_free_address + cell_size]
push_argument_stack rax
add [V__local_memory_even__current_free_address + cell_size], rbx
next
}
#+end_src
*** allocate-conjugate-local-memory
#+begin_src fasm :tangle cicada-nymph.fasm
match =64bit, machine {
define_primitive_function "allocate-conjugate-local-memory", allocate_conjugate_local_memory
;; << size -- address >>
mov rax, pointer$return_stack
sub rax, address$return_stack
test rax, unit__return_point
jnz .return_stack_even
pop_argument_stack rbx
mov rax, [V__local_memory_even__current_free_address + cell_size]
push_argument_stack rax
add [V__local_memory_even__current_free_address + cell_size], rbx
next
.return_stack_even:
pop_argument_stack rbx
mov rax, [V__local_memory_odd__current_free_address + cell_size]
push_argument_stack rax
add [V__local_memory_odd__current_free_address + cell_size], rbx
next
}
match =32bit, machine {
define_primitive_function "allocate-conjugate-local-memory", allocate_conjugate_local_memory
;; << size -- address >>
mov rax, [pointer$return_stack]
sub rax, address$return_stack
test rax, unit__return_point
jnz .return_stack_even
pop_argument_stack rbx
mov rax, [V__local_memory_even__current_free_address + cell_size]
push_argument_stack rax
add [V__local_memory_even__current_free_address + cell_size], rbx
next
.return_stack_even:
pop_argument_stack rbx
mov rax, [V__local_memory_odd__current_free_address + cell_size]
push_argument_stack rax
add [V__local_memory_odd__current_free_address + cell_size], rbx
next
}
#+end_src
*** test
#+begin_src cicada-nymph
: test,allocate-local-memory
<< -- address, address >>
16 allocate-local-memory
16 allocate-local-memory
end
; define-function
test,allocate-local-memory
<<
40218513
40218529
>>
: test,allocate-local-memory,2
<< -- address, address >>
16 allocate-local-memory
test,allocate-local-memory
16 allocate-local-memory
test,allocate-local-memory
16 allocate-local-memory
end
; define-function
test,allocate-local-memory,2
<<
40218513
40218529
40218545
40218529
40218545
40218561
40218545
>>
#+end_src
* syscall
*** note
* there are two ways to treat the syscall
1. syscall is NOT expose to cicada-nymph
system functions are wraped in assembly code
thus
make cicada-nymph code be more clean
2. syscall is expose to cicada-nymph
system functions are wraped in cicada-nymph code
thus
more easy to wraped more system functions
and
make assembly code be more clean
[only needs some system functions to load core file]
* I choose the second way for now
*** string->syscall-string
#+begin_src fasm :tangle cicada-nymph.fasm
string$string_to_syscall_string:
times 256 db 0
define_function "string->syscall-string", string_to_syscall_string
;; << string[address, length] -- syscall-string[address] >>
xx dup
xx literal, string$string_to_syscall_string
xx addition
xx literal, 0
xx swap, set_byte
xx literal, string$string_to_syscall_string
xx string_to_buffer!
xx literal, string$string_to_syscall_string
xx end
#+end_src
*** string->syscall-string-2
#+begin_src fasm :tangle cicada-nymph.fasm
string$string_to_syscall_string_2:
times 256 db 0
define_function "string->syscall-string-2", string_to_syscall_string_2
;; << string[address, length] -- syscall-string[address] >>
xx dup
xx literal, string$string_to_syscall_string_2
xx addition
xx literal, 0
xx swap, set_byte
xx literal, string$string_to_syscall_string_2
xx string_to_buffer!
xx literal, string$string_to_syscall_string_2
xx end
#+end_src
*** syscall :64bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =64bit, platform machine {
define_primitive_function "syscall", CICADA__syscall
;; << ..., argument2, argument1,
;; syscall-number,
;; number-of-arguments
;; -- return-value >>
pop_argument_stack rax
cmp rax, 0
je __syscall_with_0
cmp rax, 1
je __syscall_with_1
cmp rax, 2
je __syscall_with_2
cmp rax, 3
je __syscall_with_3
cmp rax, 4
je __syscall_with_4
cmp rax, 5
je __syscall_with_5
cmp rax, 6
je __syscall_with_6
jmp __syscall_with_too_many
__syscall_with_0:
pop_argument_stack linux64_sys_n_rax
syscall
push_argument_stack rax
next
__syscall_with_1:
pop_argument_stack linux64_sys_n_rax
pop_argument_stack linux64_sys_1_rdi
syscall
push_argument_stack rax
next
__syscall_with_2:
pop_argument_stack linux64_sys_n_rax
pop_argument_stack linux64_sys_1_rdi
pop_argument_stack linux64_sys_2_rsi
syscall
push_argument_stack rax
next
__syscall_with_3:
pop_argument_stack linux64_sys_n_rax
pop_argument_stack linux64_sys_1_rdi
pop_argument_stack linux64_sys_2_rsi
pop_argument_stack linux64_sys_3_rdx
syscall
push_argument_stack rax
next
__syscall_with_4:
pop_argument_stack linux64_sys_n_rax
pop_argument_stack linux64_sys_1_rdi
pop_argument_stack linux64_sys_2_rsi
pop_argument_stack linux64_sys_3_rdx
pop_argument_stack linux64_sys_4_r10
syscall
push_argument_stack rax
next
__syscall_with_5:
pop_argument_stack linux64_sys_n_rax
pop_argument_stack linux64_sys_1_rdi
pop_argument_stack linux64_sys_2_rsi
pop_argument_stack linux64_sys_3_rdx
pop_argument_stack linux64_sys_4_r10
pop_argument_stack linux64_sys_5_r9
syscall
push_argument_stack rax
next
__syscall_with_6:
pop_argument_stack linux64_sys_n_rax
pop_argument_stack linux64_sys_1_rdi
pop_argument_stack linux64_sys_2_rsi
pop_argument_stack linux64_sys_3_rdx
pop_argument_stack linux64_sys_4_r10
pop_argument_stack linux64_sys_5_r9
pop_argument_stack linux64_sys_6_r8
syscall
push_argument_stack rax
next
__syscall_with_too_many:
call __exit_with_six
}
#+end_src
*** syscall :32bit:linux:
#+begin_src fasm :tangle cicada-nymph.fasm
match =linux =32bit, platform machine {
define_primitive_function "syscall", CICADA__syscall
;; << ..., argument2, argument1,
;; syscall-number,
;; number-of-arguments
;; -- return-value >>
pop_argument_stack rax
cmp rax, 0
je __syscall_with_0
cmp rax, 1
je __syscall_with_1
cmp rax, 2
je __syscall_with_2
cmp rax, 3
je __syscall_with_3
cmp rax, 4
je __syscall_with_4
cmp rax, 5
je __syscall_with_5
jmp __syscall_with_too_many
__syscall_with_0:
pop_argument_stack linux32_sys_n_eax
syscall
push_argument_stack rax
next
__syscall_with_1:
pop_argument_stack linux32_sys_n_eax
pop_argument_stack linux32_sys_1_ebx
syscall
push_argument_stack rax
next
__syscall_with_2:
pop_argument_stack linux32_sys_n_eax
pop_argument_stack linux32_sys_1_ebx
pop_argument_stack linux32_sys_2_ecx
syscall
push_argument_stack rax
next
__syscall_with_3:
pop_argument_stack linux32_sys_n_eax
pop_argument_stack linux32_sys_1_ebx
pop_argument_stack linux32_sys_2_ecx
pop_argument_stack linux32_sys_3_edx
syscall
push_argument_stack rax
next
__syscall_with_4:
pop_argument_stack linux32_sys_n_eax
pop_argument_stack linux32_sys_1_ebx
pop_argument_stack linux32_sys_2_ecx
pop_argument_stack linux32_sys_3_edx
pop_argument_stack linux32_sys_4_esi
syscall
push_argument_stack rax
next
__syscall_with_5:
pop_argument_stack linux32_sys_n_eax
pop_argument_stack linux32_sys_1_ebx
pop_argument_stack linux32_sys_2_ecx
pop_argument_stack linux32_sys_3_edx
pop_argument_stack linux32_sys_4_esi
pop_argument_stack linux32_sys_5_edi
syscall
push_argument_stack rax
next
__syscall_with_too_many:
call __exit_with_six
}
#+end_src
* epilog
*** constant
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "*explainer,function*", CICADA__explain$function
xx explain$function
define_variable "*explainer,variable*", CICADA__explain$variable
xx explain$variable
#+end_src
*** platform
* this word is implemented as a function
#+begin_src fasm :tangle cicada-nymph.fasm
define_function "platform", the_platform
xx literal, string$platform
xx literal, length$platform
xx end
string$platform:
match =linux, platform {
db "linux"
}
.end:
length$platform = (.end - string$platform)
#+end_src
*** *un-initialized-memory*
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "*un-initialized-memory*", V__un_initialized_memory
xx address$un_initialized_memory
define_variable "*un-initialized-memory,size*", V__un_initialized_memory__size
xx size$un_initialized_memory
define_variable "*un-initialized-memory,current-free-address*", V__un_initialized_memory__current_free_address
xx current_free_address$un_initialized_memory
#+end_src
*** *string-area,current-free-address*
* the last_name_string_in_assembly
is just "*string-area,current-free-address*"
#+begin_src fasm :tangle cicada-nymph.fasm
define_variable "*string-area,current-free-address*", V__string_area__current_free_address
xx current_free_address$string_area
#+end_src
*** the_last_link_in_assembly_code
* this word helps to initialize V__link
#+begin_src fasm :tangle cicada-nymph.fasm
the_last_link_in_assembly_code = link
#+end_src
*** un_initialized_memory :linux:
#+begin_src fasm :tangle cicada-nymph.fasm
size$un_initialized_memory = 88 * 1024 * 1024 ;; (byte)
match =linux, platform {
segment readable writeable
address$un_initialized_memory:
rb size$un_initialized_memory
}
#+end_src