Change indentation to use 2 spaces
[rrq/jonasforth.git] / main.asm
index 537796de252a2c12db2f3597825afb3a1614f4ea..0a8ff4cf256dcd602e3c8c59b3bde3418422343b 100644 (file)
--- a/main.asm
+++ b/main.asm
@@ -4,17 +4,105 @@ format ELF64 executable
 ;; executing a definition, this code is what causes execution to resume at the
 ;; next word in that definition.
 macro next {
-    ;; RSI points to the address of the definition of the next word to execute.
-    lodsq                   ; Load value at RSI into RAX and increment RSI
-    ;; Now RAX contains the location of the next word to execute. The first 8
-    ;; bytes of this word is the address of the codeword, which is what we want
-    ;; to execute.
-    jmp qword [rax]         ; Jump to the codeword of the current word
+  ;; RSI points to the address of the definition of the next word to execute.
+  lodsq                   ; Load value at RSI into RAX and increment RSI
+  ;; Now RAX contains the location of the next word to execute. The first 8
+  ;; bytes of this word is the address of the codeword, which is what we want
+  ;; to execute.
+  jmp qword [rax]         ; Jump to the codeword of the current word
+}
+
+;; pushr and popr work on the return stack, whose location is stored in the
+;; register RBP.
+macro pushr x {
+  sub rbp, 8
+  mov qword [rbp], x
+}
+macro popr x {
+  mov x, [rbp]
+  add rbp, 8
 }
 
 segment readable executable
 
-start:
-    jmp $
+main:
+  cld                        ; Clear direction flag so LODSQ does the right thing.
+  mov rbp, return_stack_top  ; Initialize return stack
+
+  mov rsi, program
+  next
+
+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.
+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
+  next                 ; Execute word pointed to by RSI
+
+;; This word is called at the end of a Forth definition. It just needs to
+;; restore the old value of RSI (saved by 'docol') and resume execution.
+EXIT:
+  dq .start
+.start:
+  popr rsi
+  next
+
+EMIT:
+  dq .start
+.start:
+  pushr rsi
+  pushr rax
+  mov rax, 1
+  mov rdi, 1
+  lea rsi, [rsp]
+  mov rdx, 1
+  syscall
+  add rsp, 8
+  popr rax
+  popr rsi
+  next
+
+PUSH_HELLO_CHARS:
+  dq .start
+  .start:
+  push $A
+  push 'o'
+  push 'l'
+  push 'l'
+  push 'e'
+  push 'H'
+  next
+
+HELLO:
+  dq docol
+  dq PUSH_HELLO_CHARS
+  dq EMIT
+  dq EMIT
+  dq EMIT
+  dq EMIT
+  dq EMIT
+  dq EMIT
+  dq EXIT
+
+TERMINATE:
+  dq .start
+  .start:
+  mov rax, $3C
+  mov rdi, 0
+  syscall
+
+MAIN:
+  dq docol
+  dq HELLO
+  dq HELLO
+  dq HELLO
+  dq TERMINATE
+
+segment readable writable
 
-segment readable
+;; Return stack
+rq $2000
+return_stack_top: