forth

A WIP implementation of Forth targeting UEFI x86-64.
git clone git://git.christianermann.dev/forth
Log | Files | Refs

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:
Mefi.asm | 37++++++++++++++++++++++++-------------
Mforth.asm | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mrun.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 -