.loop:
movzx rcx, byte [rsi + 8 + 1] ; Length of word being looked at
cmp rcx, [.search_length]
- jne .next ; If the words don't have the same length, we have the wrong word
+ jne .next ; If the words don't have the same length, we have the
+ ; wrong word
;; Otherwise, we need to compare strings
- lea rdx, [rsi + 8 + 1 + 1] ; Location of character being compared in entry
- mov rdi, [.search_buffer] ; Location of character being compared in search buffer
+ lea rdx, [rsi + 8 + 1 + 1] ; Location of character being compared in entry
+ mov rdi, [.search_buffer] ; Location of character being compared in
+ ; search buffer
.compare_char:
mov al, [rdx]
mov ah, [rdi]
cmp al, ah
- jne .next ; They don't match; try again
- inc rdx ; These characters match; look at the next ones
+ jne .next ; They don't match; try again
+ inc rdx ; These characters match; look at the
+ ; next ones
inc rdi
loop .compare_char
- jmp .found ; They match! We are done.
+ jmp .found ; They match! We are done.
.next:
- mov rsi, [rsi] ; Look at the previous entry
+ mov rsi, [rsi] ; Look at the previous entry
cmp rsi, 0
- jnz .loop ; If there is no previous word, exit and return 0
+ jnz .loop ; If there is no previous word, exit and return 0
.found:
ret
-;; Read a word from a buffer. Returns the buffer without the word, as well as
-;; the word that was read (including lengths).
+;; Read a word from a buffer. Returns the buffer without the word, as
+;; well as the word that was read (including lengths).
;;
;; Inputs:
;; * rsi = Input buffer
je .got_whitespace
jmp .alpha
.got_whitespace:
- ;; The buffer starts with whitespace; discard the first character from the buffer.
+ ;; The buffer starts with whitespace; discard the first character
+ ;; from the buffer.
inc rsi
dec rcx
jmp .skip_whitespace
.alpha:
- ;; We got a character that wasn't whitespace. Now read the actual word.
+ ;; We got a character that wasn't whitespace. Now read the actual
+ ;; word.
mov rdi, rsi ; This is where the word starts
mov rdx, 1 ; Length of word
parse_number:
mov r8, 0 ; Result
- ;; Add (10^(rcx-1) * parse_char(rdi[length - rcx])) to the accumulated value
- ;; for each rcx.
+ ;; Add (10^(rcx-1) * parse_char(rdi[length - rcx])) to the
+ ;; accumulated value for each rcx.
mov [.length], rcx
.loop:
;; First, calcuate 10^(rcx - 1)
cmp rbx, 10
jae .error
- ;; Multiply this value by rax to get (10^(rcx-1) * parse_char(rdi[length - rcx])),
+ ;; Multiply this value by rax to get
+ ;; (10^(rcx-1) * parse_char(rdi[length - rcx]))
;; then add this to the result.
mul rbx
-;; 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
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
.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
;; Duplicate the top of the stack.
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:
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
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 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
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
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.length dq ?
DOTU.chars db '0123456789ABCDEF'
-DOTU.buffer rq 16 ; 64-bit number has no more than 16 digits in hex
+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'
-;; vim: syntax=fasm
-
format pe64 dll efi
entry main
.FirmwareVendor dq ? ; CHAR16*
.FirmwareRevision dd ? ; UINT32
align 8
- .ConsoleInHandle dq ? ; EFI_HANDLE
- .ConIn dq ? ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL*
- .ConsoleOutHandle dq ? ; EFI_HANDLE
- .ConOut dq ? ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*
- ; ...
+ .ConsoleInHandle dq ? ; EFI_HANDLE
+ .ConIn dq ? ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL*
+ .ConsoleOutHandle dq ? ; EFI_HANDLE
+ .ConOut dq ? ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*
+ .StandardErroHandle dq ? ; EFI_HANDLE
+ .StdErr dq ? ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*
+ .RuntimeServices dq ? ; EFI_RUNTIME_SERVICES*
+ .BootServices dq ? ; EFI_BOOT_SERVICES*
+ .NumberOfTableEntries dq ? ; UINTN (native width!!)
+ .ConfigurationTable dq ? ; EFI_CONFIGURATION_TABLE*
}
struct EFI_SYSTEM_TABLE
ret
os_print_string:
- ;; We take an input string of bytes without any terminator. We need to turn
- ;; this string into a string of words, terminated by a null character.
+ ;; We take an input string of bytes without any terminator. We need
+ ;; to turn this string into a string of words, terminated by a null
+ ;; character.
mov rdi, .output_buffer ; Current location in output string
.copy_byte:
- ;; When there are no characters left in the input string, we are done.
+ ;; When there are no characters left in the input string, we are
+ ;; done.
cmp rdx, 0
je .done
mov byte [rdi], al
inc rdi
- ;; The output string has words rather than bytes for charactesr, so we need
- ;; to add an extra zero:
+ ;; The output string has words rather than bytes for charactesr, so
+ ;; we need to add an extra zero:
mov byte [rdi], 0
inc rdi
.pop:
- ;; We finished copying character to output string, so pop it from the input
- ;; string.
+ ;; We finished copying character to output string, so pop it from
+ ;; the input string.
inc rcx
dec rdx
;; Append a final null-word:
mov word [rdi], 0
- ; At this point we have our null-terminated word-string at .output_buffer. Now
- ; we just need to print it.
+ ; At this point we have our null-terminated word-string at
+ ; .output_buffer. Now we just need to print it.
- mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx
- mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut] ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx
+ mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx
+ mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
+ ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx
mov rdx, .output_buffer
- mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx
+ mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString]
+ ; EFI_TEXT_STRING rbx
sub rsp, 32
call rbx
add rsp, 32
os_read_char:
.read_key:
- mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx
- mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn] ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx
- mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx
- mov rdx, input_key ; EFI_INPUT_KEY* rdx
+ mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx
+ mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn]
+ ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx
+ mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke]
+ ; EFI_INPUT_READ_KEY rbx
+ mov rdx, input_key ; EFI_INPUT_KEY* rdx
sub rsp, 32
call rbx
add rsp, 32