+;; 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
+
+;; 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
+ pop [.search_length]
+ pop [.search_buffer]
+
+ ;; RSI contains the entry we are currently looking at
+ mov rsi, [latest_entry] ; Start with the last added word
+
+.loop:
+ movzx rcx, byte [rsi + 8] ; 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
+
+ ;; Otherwise, we need to compare strings
+ lea rdx, [rsi + 8 + 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
+ inc rdi
+ loop .compare_char
+
+ jmp .found ; They match! We are done.
+
+.next:
+ mov rsi, [rsi] ; Look at the previous entry
+ cmp rsi, 0
+ jnz .loop ; If there is no previous word, exit and return 0
+
+.found:
+ push rsi
+
+ mov rsi, [.rsi]
+ next
+
+;; Given an entry in the dictionary, return a pointer to the codeword of that
+;; entry.
+forth_asm TCFA, '>CFA'
+ pop rax
+ add rax, 8 ; [rax] = length of name
+ movzx rbx, byte [rax]
+ inc rax
+ add rax, rbx ; [rax] = codeword
+ 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.
+forth_asm BRANCH, 'BRANCH'
+ 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.
+forth_asm ZBRANCH, '0BRANCH'
+ ;; Compare top of stack to see if we should branch
+ pop rax
+ cmp rax, 0
+ 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.
+ next
+
+;; Duplicate the top of the stack.
+forth_asm DUP_, 'DUP'
+ push qword [rsp]
+ next
+
+;; Execute the codeword at the given address.
+forth_asm EXEC, 'EXEC'
+ pop rax
+ jmp qword [rax]
+
+;; Expects a character on the stack and prints it to standard output.
+forth_asm EMIT, 'EMIT'