-;; The UEFI module defines the following functions. Each of these functions
-;; preserve the value of RSI and RSP. They may use other registers as they like.
+;; The UEFI module defines the following functions. Each of these
+;; functions preserve the value of RSI and RSP. They may use other
+;; registers as they like.
;;
;; os_initialize
;; Called at initialization.
;;
;; os_terminate
;; Shut down the system, returning the error code given in RAX.
+
include 'src/uefi.asm'
;; The code in this macro is placed at the end of each Forth word. When we are
}
;; pushr and popr work on the return stack, whose location is stored in the
-;; register RBP.
+;; register RBP. Always allocates an extra 8 bytes as "local frame"
macro pushr x {
sub rbp, 8
mov qword [rbp], x
initial_latest_entry = label#_entry
}
-;; Define a Forth word that is implemented in assembly. See 'header' for details.
+;; Define a Forth word that is implemented in assembly. See 'header'
+;; for details.
macro forth_asm label, name, immediate {
header label, name, immediate
dq .start
include "bootstrap.asm" ; Forth words encoded in Assembly
main:
- cld ; Clear direction flag so LODSQ does the right thing.
+ cld ; Clear direction flag so LODSQ does the right thing.
mov rbp, return_stack_top ; Initialize return stack
call os_initialize
program: dq MAIN
-;; The codeword is the code that will be executed at the beginning of a forth
-;; word. It needs to save the old RSI and update it to point to the next word to
-;; execute.
+;; The codeword is the code that will be executed at the beginning of
+;; a forth word. It needs to save the old RSI and update it to point
+;; to the next word to execute.
header DOCOL, 'DOCOL'
- pushr rsi ; Save old value of RSI on return stack; we will continue execution there after we are done executing this word
- lea rsi, [rax + 8] ; RAX currently points to the address of the codeword, so we want to continue at RAX+8
+ pushr rsi ; Save old value of RSI on return stack; we
+ ; will continue execution there after we are
+ ; done executing this word
+ lea rsi, [rax + 8] ; RAX currently points to the address of the
+ ; codeword, so we want to continue at RAX+8
next ; Execute word pointed to by RSI
;; This word is called at the end of a Forth definition. It just needs to
popr rsi
next
-;; LIT is a special word that reads the next "word pointer" and causes it to be
-;; placed on the stack rather than executed.
+;; LIT is a special word that reads the next "word pointer" and causes
+;; it to be placed on the stack rather than executed.
forth_asm LIT, 'LIT'
lodsq
push rax
next
-;; When LITSTRING is encountered while executing a word, it instead reads a
-;; string from the definition of that word, and places that string on the stack
-;; as (buffer, length).
+;; When LITSTRING is encountered while executing a word, it instead
+;; reads a string from the definition of that word, and places that
+;; string on the stack as (buffer, length).
forth_asm LITSTRING, 'LITSTRING'
lodsb
push rsi ; Buffer
add rsi, rax ; Skip over string before resuming execution
next
-;; Given a string (a pointer following by a size), return the location of the
-;; dictionary entry for that word. If no such word exists, return 0.
+;; Given a string (a pointer following by a size), return the location
+;; of the dictionary entry for that word. If no such word exists,
+;; return 0.
forth_asm FIND, 'FIND'
mov [.rsi], rsi
mov rsi, [.rsi]
next
-;; Given an entry in the dictionary, return a pointer to the codeword of that
-;; entry.
+;; Given an entry in the dictionary, return a pointer to the codeword
+;; of that entry.
forth_asm TCFA, '>CFA'
pop rax
add rax, 8 + 1 ; [rax] = length of name
push rax
next
-;; BRANCH is the fundamental mechanism for branching. BRANCH reads the next word
-;; as a signed integer literal and jumps by that offset.
+;; BRANCH is the fundamental mechanism for branching. BRANCH reads the
+;; next word as a signed integer literal and jumps by that offset.
+
forth_asm BRANCH, 'BRANCH'
- add rsi, [rsi] ; [RSI], which is the next word, contains the offset; we add this to the instruction pointer.
+ add rsi, [rsi] ; [RSI], which is the next word, contains the offset
+ ; we add this to the instruction pointer.
next ; Then, we can just continue execution as normal
-;; 0BRANCH is like BRANCH, but it jumps only if the top of the stack is zero.
+;; 0BRANCH is like BRANCH, but it jumps only if the top of the stack
+;; is zero.
forth_asm ZBRANCH, '0BRANCH'
- ;; Compare top of stack to see if we should branch
pop rax
- cmp rax, 0
+ cmp rax, 0 ; Compare top of stack to see if we should branch
jnz .dont_branch
.do_branch:
- jmp BRANCH.start
+ add rsi,[rsi]
+ next
.dont_branch:
- add rsi, 8 ; We need to skip over the next word, which contains the offset.
+ add rsi, 8 ; We need to skip over the next word, which contains
+ ; the offset.
+ next
+
+;; Push the return stack pointer. "grows" negatively
+forth_asm RSPGET, 'R='
+ push rbp
+ next
+
+;; The return stack "grows" negatively, and rbp is the address of the top
+;; Move rbp by n (from stack) bytes
+forth_asm RSPADD, 'R+'
+ pop rax
+ sub rbp, rax
+ next
+
+;; Push top of the stack.
+forth_asm TOP_, 'TOP'
+ push rsp
next
;; Duplicate the top of the stack.
pop rax
jmp qword [rax]
+;; This word skips a word without exectuing, but pushes its address
+forth_asm SKIP_, 'SKIP'
+ push rsi
+ add rsi, 8 ; We need to skip over the next word, which contains
+ ; the offset.
+ next
+
;; Expects a character on the stack and prints it to standard output.
forth_asm EMIT, 'EMIT'
pushr rsi
popr rsi
next
-;; Read a single character from the current input stream. Usually, this will wait
-;; for the user to press a key, and then return the corresponding character. When
-;; reading from a special buffer, it will instead return the next characater from
-;; that buffer.
+;; Read a single character from the current input stream. Usually,
+;; this will wait for the user to press a key, and then return the
+;; corresponding character. When reading from a special buffer, it
+;; will instead return the next characater from that buffer.
;;
;; The ASCII character code is placed on the stack.
forth_asm KEY, 'KEY'
dec [input_buffer_length]
ret
-;; Read a word and push it onto the stack as a pointer and a size. The pointer
-;; is valid until the next call to READ_WORD.
+;; Read a word and push it onto the stack as a pointer and a size. The
+;; pointer is valid until the next call to READ_WORD.
forth_asm READ_WORD, 'READ-WORD'
push rsi
.skip_whitespace:
;; Read characters until one of them is not whitespace.
call KEY.impl
- ;; We consider newlines and spaces to be whitespace.
+ ;; We consider newlines, tabs and spaces to be whitespace.
cmp al, ' '
je .skip_whitespace
+ cmp al, $9
+ je .skip_whitespace
cmp al, $A
je .skip_whitespace
cmp al, ' '
je .end
+ cmp al, 9
+ je .end
cmp al, $A
jne .read_alpha
next
-;; Takes a string on the stack and replaces it with the decimal number that the
-;; string represents.
+;; Takes a string on the stack and replaces it with the decimal number
+;; that the string represents.
forth_asm PARSE_NUMBER, 'PARSE-NUMBER'
pop rcx ; Length
pop rdi ; String pointer
add rsp, 8
next
+;; Takes a value and an address, and stores the value at the given address.
+forth_asm AND_, '&'
+ pop rbx ; a
+ pop rax ; b
+ and rax, rbx
+ push rax
+ next
+
forth_asm NOT_, 'NOT'
pop rax
cmp rax, 0
push rax
next
-;; Calculate difference between two integers on the stack. The second number is
-;; subtracted from the first.
+;; Calculate difference between two integers on the stack. The second
+;; number is subtracted from the first.
forth_asm MINUS, '-'
pop rax
pop rbx
push rbx
next
-;; Given two integers a and b on the stack, pushes the quotient and remainder of
-;; division of a by b.
+;; Multiply two integers on the stack ignoring overflow
+forth_asm MULT, '*'
+ pop rax
+ pop rbx
+ mul rbx
+ push rax
+ next
+
+;; Given two integers a and b on the stack, pushes the quotient and
+;; remainder of division of a by b.
forth_asm TIMESMOD, '/MOD'
pop rbx ; b
pop rax ; a
push rdx ; a % b
next
-;; Read input until next " character is found. Push a string containing the
-;; input on the stack as (buffer length). Note that the buffer is only valid
-;; until the next call to S" and that no more than 255 characters can be read.
+;; Read input until next " character is found. Push a string
+;; containing the input on the stack as (buffer length). Note that the
+;; buffer is only valid until the next call to S" and that no more
+;; than 255 characters can be read.
forth_asm READ_STRING, 'S"'
;; If the input buffer is set, we should read from there instead.
cmp [input_buffer], 0
next
-;; CREATE inserts a new header in the dictionary, and updates LATEST so that it
-;; points to the header. To compile a word, the user can then call ',' to
-;; continue to append data after the header.
+;; CREATE inserts a new header in the dictionary, and updates LATEST
+;; so that it points to the header. To compile a word, the user can
+;; then call ',' to continue to append data after the header.
;;
-;; It takes the name of the word as a string (address length) on the stack.
+;; It takes the name of the word as a string (address length) on the
+;; stack.
forth_asm CREATE, 'CREATE'
pop rcx ; Word string length
pop rdx ; Word string pointer
pop rax
pop rbx
pop rdx
+ push rbx
push rax
push rdx
- push rbx
next
forth_asm PICK, 'PICK'
push rax
next
+forth_asm OVER, 'OVER'
+ push qword [rsp + 8]
+ next
+
forth_asm EQL, '='
pop rax
pop rbx
push 1
next
+forth_asm LT_, '<'
+ pop rax
+ pop rbx
+ cmp rax, rbx
+ jle .le
+.notle:
+ push 1
+ next
+.le:
+ push 0
+ next
+
+forth_asm GT_, '>'
+ pop rax
+ pop rbx
+ cmp rax, rbx
+ jge .ge
+.notge:
+ push 1
+ next
+.ge:
+ push 0
+ next
+
forth MAIN, 'MAIN'
dq SYSCODE
dq INTERPRET_STRING
dq LIT, system_table, GET
dq EXIT
+forth_asm EFICALL1, 'EFICALL1'
+ pop rax ; function pointer
+ pop rcx ; 1st argument
+ sub rsp, 32
+ call rax
+ add rsp, 32
+ next
+
forth_asm EFICALL2, 'EFICALL2'
pop rax ; function pointer
pop rdx ; 2nd argument
pop rcx ; 1st argument
-
sub rsp, 32
call rax
add rsp, 32
-
next
forth_asm EFICALL3, 'EFICALL3'
pop r8 ; 3rd argument
pop rdx ; 2nd argument
pop rcx ; 1st argument
-
sub rsp, 32
call rax
add rsp, 32
+ push rax
+ next
+forth_asm EFICALL4, 'EFICALL4'
+ pop rax ; function pointer
+ pop r9 ; 4th argument
+ pop r8 ; 3rd argument
+ pop rdx ; 2nd argument
+ pop rcx ; 1st argument
+ sub rsp, 32
+ call rax
+ add rsp, 32
push rax
+ next
+forth_asm EFICALL5, 'EFICALL5'
+ pop rax ; function pointer
+ pop r10 ; 5th argument
+ pop r9 ; 4th argument
+ pop r8 ; 3rd argument
+ pop rdx ; 2nd argument
+ pop rcx ; 1st argument
+ push r10 ; restore as stack argument
+ sub rsp, 32
+ call rax
+ add rsp, 32 + 8
+ push rax
next
forth_asm EFICALL10, 'EFICALL10'
pop rax ; function pointer
-
- mov rcx, [rsp + 8 * 9]
- mov rdx, [rsp + 8 * 8]
+ mov rcx, [rsp + 8 * 9] ; 1st
+ mov rdx, [rsp + 8 * 8] ; 2nd
mov r8, [rsp + 8 * 7]
mov r9, [rsp + 8 * 6]
-
;; Reverse order of stack arguments
mov r10, [rsp + 8 * 5]
mov r11, [rsp + 8 * 0]
mov [rsp + 8 * 5], r11
mov [rsp + 8 * 0], r10
-
mov r10, [rsp + 8 * 4]
mov r11, [rsp + 8 * 1]
mov [rsp + 8 * 4], r11
mov [rsp + 8 * 1], r10
-
mov r10, [rsp + 8 * 3]
mov r11, [rsp + 8 * 2]
mov [rsp + 8 * 3], r11
mov [rsp + 8 * 2], r10
-
sub rsp, 32
call rax
add rsp, 32 + 8 * 10
-
push rax
-
next
;; Built-in variables:
section '.data' readable writable
-;; The LATEST variable holds a pointer to the word that was last added to the
-;; dictionary. This pointer is updated as new words are added, and its value is
-;; used by FIND to look up words.
+;; The LATEST variable holds a pointer to the word that was last added
+;; to the dictionary. This pointer is updated as new words are added,
+;; and its value is used by FIND to look up words.
latest_entry dq initial_latest_entry
-;; The STATE variable is 0 when the interpreter is executing, and non-zero when
-;; it is compiling.
+;; The STATE variable is 0 when the interpreter is executing, and
+;; non-zero when it is compiling.
var_STATE dq 0
-;; The interpreter can read either from standard input or from a buffer. When
-;; input-buffer is set (non-null), words like READ-WORD and S" will use this
-;; buffer instead of reading user input.
+;; The interpreter can read either from standard input or from a
+;; buffer. When input-buffer is set (non-null), words like READ-WORD
+;; and S" will use this buffer instead of reading user input.
input_buffer dq 0
input_buffer_length dq 0
READ_STRING.buffer rb $FF
READ_STRING.length dq ?
-DOTU.chars db '0123456789ABCDEF'
-DOTU.buffer rq 16 ; 64-bit number has no more than 16 digits in hex
+DOTU.chars db '0123456789abcdef'
+DOTU.buffer rq 16 ; 64-bit number has no more than 16 digits in hex
DOTU.rbuffer rq 16
DOTU.length dq ?
DOTU.printed_length dq ?
rq $2000
return_stack_top:
-;; We store some Forth code in sys.f that defined common words that the user
-;; would expect to have available at startup. To execute these words, we just
-;; include the file directly in the binary, and then interpret it at startup.
+;; We store some Forth code in sys.f that defined common words that
+;; the user would expect to have available at startup. To execute
+;; these words, we just include the file directly in the binary, and
+;; then interpret it at startup.
sysf:
file '../init/sys.f'
file '../init/uefi.f'
+file '../init/blurb.f'
sysf.len = $ - sysf
-