commit ad1ee3243fe6a83760842cbf5c6b13fcb253cc0e
parent ef492b4f00282956ab266259992fa2bf86d1447f
Author: Christian Ermann <christianermann@gmail.com>
Date: Thu, 14 Dec 2023 21:13:58 -0500
Add KEY, WORD, DOT, and U_DOT
Diffstat:
M | efi.asm | | | 37 | ++++++++++++++++++++++++------------- |
M | forth.asm | | | 203 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
M | run.sh | | | 1 | - |
3 files changed, 219 insertions(+), 22 deletions(-)
diff --git a/efi.asm b/efi.asm
@@ -123,6 +123,18 @@ struc INPUT_KEY {
}
struct INPUT_KEY
+macro preserve_registers {
+ push rbx
+ push rcx
+ push rdx
+}
+
+macro restore_registers {
+ pop rdx
+ pop rcx
+ pop rbx
+}
+
sys_initialize:
; Store the address of the EFI system table in the `system_table` variable.
;
@@ -132,7 +144,9 @@ sys_initialize:
; `system_table` is defined as an 8-byte variable in the data section at the
; bottom of this file.
;
+ preserve_registers
mov [system_table], rdx
+ restore_registers
ret
sys_clear_screen:
@@ -140,12 +154,14 @@ sys_clear_screen:
;
; This function will clobber the RCX and RBX registers.
;
+ preserve_registers
mov rcx, [system_table]
mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
mov rbx, [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.ClearScreen]
sub rsp, 32
call rbx
add rsp, 32
+ restore_registers
ret
sys_print_string:
@@ -155,13 +171,14 @@ sys_print_string:
; null-terminated version of the input string. 1024 bytes (512 characters) are
; reserved for this buffer in the data section at the bottom of this file.
;
-; This function will clobber the RDI, RAX, RBX, RCX, and RDX registers.
+; This function will clobber the RDI and RAX registers.
;
; Args:
; RCX: The address of an ASCII string. The string does not need to be
; null-terminated.
; RDX: The length of the string.
;
+ preserve_registers
mov rdi, .output_buffer
.copy_char:
@@ -222,6 +239,7 @@ sys_print_string:
sub rsp, 32
call rbx
add rsp, 32
+ restore_registers
ret
sys_read_char:
@@ -230,6 +248,7 @@ sys_read_char:
; The EFI ReadKeyStroke function actually returns a UTF-16 character but I only
; want to deal with ASCII characters for now.
;
+ preserve_registers
.read_key:
; Call the EFI ReadKeyStroke function until a key is pressed.
@@ -258,21 +277,13 @@ sys_read_char:
;
movzx rax, word [.input_key.UnicodeChar]
cmp ax, 0xD
- jne .no_enter
+ jne .end
mov rax, 0xA
-.no_enter:
-; Echo the character onto stdout and return.
-;
-; The echo will likely be removed in the future but is here currently for
-; debugging purposes.
+.end:
+; Restore registers saved at the start of the function
;
- push rax
- mov [.char_buffer], al
- mov rcx, .char_buffer
- mov rdx, 1
- call sys_print_string
- pop rax
+ restore_registers
ret
section '.data' data readable writeable
diff --git a/forth.asm b/forth.asm
@@ -28,6 +28,8 @@ include 'efi.asm'
; 1. The Basic Structure of Forth
; 2. Executing this Program
; 3. Hello, World!
+; 4. Reading Words
+; 5. Printing Numbers
;
;------------------------------------------------------------------------------
;
@@ -171,7 +173,7 @@ macro defcode name, name_length, flags, label_
; Define a primitive word.
;
{
- section '.data' data readable
+section '.rodata' data readable
align 8
name_#label_:
dq link ; 1. Set the link pointer.
@@ -183,7 +185,7 @@ name_#label_:
align 8 ; 6. Add any padding we may need.
label_:
dq code_#label_ ; 7. Set the codeword.
- section '.text' code readable executable
+section '.text' code readable executable
align 1
code_#label_: ; 8. This is where our assembly code will go.
@@ -240,14 +242,21 @@ defcode "EXIT", 4, 0, EXIT
; We'll use the variable `program` to store the address of the w
;
+postpone {
+section '.text' code executable readable
main:
cld
call sys_initialize
call sys_clear_screen
- push version_string
- push version_string.length
+ initialize_stack
mov rsi, program
NEXT
+}
+
+macro set_program init_stack_macro, program_label {
+ macro initialize_stack \{ init_stack_macro \}
+ program equ program_label
+}
;------------------------------------------------------------------------------
;
@@ -291,16 +300,194 @@ defcode "TYPE", 4, 0, TYPE
call sys_print_string
NEXT
-section '.rodata' readable
+section '.rodata' data readable
+
+macro initialize_stack_s3 {
+ push version_string
+ push version_string.length
+}
+
+program_s3:
+ dq TYPE
+
+set_program initialize_stack_s3, program_s3
+
+;------------------------------------------------------------------------------
+;
+; Section 4 - Reading Words
+;
+
+defcode "KEY", 3, 0, KEY
+ call sys_read_char
+ push rax
+ NEXT
+
+defcode "WORD", 4, 0, WORD_
+
+.skip_whitespace_and_comments:
+ call sys_read_char
+ cmp al, 0x20
+ je .skip_whitespace_and_comments
+ cmp al, 0xA
+ je .skip_whitespace_and_comments
+ cmp al, 0xD
+ je .skip_whitespace_and_comments
+
+ mov rdx, .buffer
+
+.store_char:
+ mov byte [rdx], al
+ inc rdx
+
+.next_char:
+ call sys_read_char
+ cmp al, 0x20
+ je .end
+ cmp al, 0xA
+ jne .store_char
+
+.end:
+ mov rcx, .buffer
+ sub rdx, rcx
+ push rcx
+ push rdx
+ NEXT
+
+macro initialize_stack_s4 {
+ push version_string
+ push version_string.length
+}
+
+program_s4:
+ dq TYPE
+ dq WORD_
+ dq TYPE
+
+set_program initialize_stack_s4, program_s4
+
+;------------------------------------------------------------------------------
+;
+; Section 5 - Printing Numbers
+;
+
+defcode ".", 1, 0, DOT
+; Print a signed integer.
+;
+ pop rax
+ movzx rcx, [BASE]
+ xor rbx, rbx
+ xor r8, r8
+
+.check_negative:
+ test rax, rax
+ jns .divide
+ neg rax
+ inc r8
+
+.divide:
+ xor rdx, rdx
+ div rcx
+ push rdx
+ inc rbx
+ test rax, rax
+ jnz .divide
+
+.reverse_digits:
+ mov rcx, rbx
+ mov rdx, .buffer
+
+.handle_negative:
+ test r8, r8
+ jz .next_digit
+ mov byte [rdx], 0x2D
+ inc rdx
+ inc rbx
+
+.next_digit:
+ pop rax
+ add al, 48
+ cmp al, 58
+ jl .store_in_buffer
+ add al, 8
+
+.store_in_buffer:
+ mov [rdx], al
+ inc rdx
+ dec rcx
+ jnz .next_digit
+
+.end:
+ mov byte [rdx], 0xA
+ inc rbx
+ mov rcx, .buffer
+ mov rdx, rbx
+ call sys_print_string
+ NEXT
+
+
+defcode "U.", 2, 0, U_DOT
+; Print an unsigned integer.
+;
+ pop rax
+ movzx rcx, [BASE]
+ xor rbx, rbx
+
+.divide:
+ xor rdx, rdx
+ div rcx
+ push rdx
+ inc rbx
+ test rax, rax
+ jnz .divide
+
+.reverse_digits:
+ mov rcx, rbx
+ mov rdx, .buffer
+
+.next_digit:
+ pop rax
+ add al, 48
+ cmp al, 58
+ jl .store_in_buffer
+ add al, 7
+
+.store_in_buffer:
+ mov [rdx], al
+ inc rdx
+ dec rcx
+ jnz .next_digit
-program_s1:
+.end:
+ mov byte [rdx], 0xA
+ inc rbx
+ mov rcx, .buffer
+ mov rdx, rbx
+ call sys_print_string
+ ret
+
+macro initialize_stack_s5 {
+ push 0xBADC0DE
+ push -0xBAD
+ push version_string
+ push version_string.length
+}
+
+program_s5:
dq TYPE
+ dq DOT
+ dq U_DOT
-program dq program_s1
+;set_program initialize_stack_s5, program_s5
-section '.data' readable writable
+section '.data' data readable writable
version_string db 'soup forth v0.1', 0xA
.length = $ - version_string
code_EMIT.char_buffer db ?
+
+code_DOT.buffer rb 64
+code_U_DOT.buffer rb 64
+code_WORD_.buffer rb 64
+
+BASE db 16
diff --git a/run.sh b/run.sh
@@ -6,4 +6,3 @@ exec qemu-system-x86_64 \
-net none \
-bios /usr/share/ovmf/OVMF.fd \
-drive file=boot.img,format=raw
-