some cleanup
[rrq/jonasforth.git] / src / impl.asm
1 ;; This file provides a small collection of fundamental subroutines that
2 ;; are used by the core forth words.
3
4 section '.text' code readable executable
5
6 macro printlen msg, len {
7   push rsi
8   add rsp, 8
9
10   mov rcx, msg
11   mov rdx, len
12   call os_print_string
13
14   sub rsp, 8
15   pop rsi
16 }
17
18 macro newline {
19   push $A
20   printlen rsp, 1
21 }
22
23 macro print msg {
24   printlen msg, msg#.len
25 }
26
27 struc string bytes {
28   . db bytes
29   .len = $ - .
30 }
31
32 ;; Find the given word in the dictionary of words. If no such word exists,
33 ;; return 0.
34 ;;
35 ;; Parameters:
36 ;;   * [find.search_length] = Length of the word in bytes.
37 ;;   * [find.search_buffer] = Pointer to the string containing the word.
38 ;;   * rsi = Pointer to the last entry in the dictionary.
39 ;;
40 ;; Results:
41 ;;   * rsi = Pointer to the found entry in the dictionary or 0.
42 ;;
43 ;; Clobbers rcx, rdx, rdi, rax.
44 find:
45   ;; RSI contains the entry we are currently looking at
46 .loop:
47   movzx rcx, byte [rsi + 8 + 1]    ; Length of word being looked at
48   cmp rcx, [.search_length]
49   jne .next    ; If the words don't have the same length, we have the
50                ; wrong word
51
52   ;; Otherwise, we need to compare strings
53   lea rdx, [rsi + 8 + 1 + 1] ; Location of character being compared in entry
54   mov rdi, [.search_buffer]  ; Location of character being compared in
55                              ; search buffer
56 .compare_char:
57   mov al, [rdx]
58   mov ah, [rdi]
59   cmp al, ah
60   jne .next                  ; They don't match; try again
61   inc rdx                    ; These characters match; look at the
62                              ; next ones
63   inc rdi
64   loop .compare_char
65
66   jmp .found                 ; They match! We are done.
67
68 .next:
69   mov rsi, [rsi]             ; Look at the previous entry
70   cmp rsi, 0
71   jnz .loop                  ; If there is no previous word, exit and return 0
72
73 .found:
74   ret
75
76 ;; Read a word from a buffer. Returns the buffer without the word, as
77 ;; well as the word that was read (including lengths). Treats space,
78 ;; tab and newline as whitespace.
79 ;;
80 ;; Inputs:
81 ;;   * rsi = Input buffer
82 ;;   * rcx = Length of buffer
83 ;;
84 ;; Outputs:
85 ;;   * rsi = Updated buffer
86 ;;   * rcx = Length of remaining buffer
87 ;;   * rdi = Word start in buffer
88 ;;   * rdx = Length of word buffer
89
90 pop_word:
91   mov rdx, 0
92   jmp .skip_whitespace
93
94 .got_whitespace:
95   ;; The buffer starts with whitespace: consume that and try again.
96   ;; Note: indefinite
97   inc rsi
98   dec rcx
99   je .no_more
100
101 .skip_whitespace:
102   mov al, [rsi]
103   cmp al, ' '
104   je .got_whitespace
105   cmp al, 9
106   je .got_whitespace
107   cmp al, $A
108   je .got_whitespace
109
110   ;; We got a character that wasn't whitespace. Now read the actual word.
111   mov rdi, rsi ; This is where the word starts
112
113 .read_alpha:
114   inc rdx      ; Length of word
115
116   ;; Extract character from original buffer:
117   inc rsi
118   dec rcx
119   je .no_more
120
121   ;; When we hit whitespace, we are done with this word
122   mov al, [rsi]
123   cmp al, ' '
124   je .end
125   cmp al, 9
126   je .end
127   cmp al, $A
128   je .end
129
130   ;; It wasn't whitespace; include it in word
131   jmp .read_alpha
132
133 .end:
134   ;; Finally, we want to skip one whitespace character after the word.
135   inc rsi
136   dec rcx
137
138 .no_more:
139   ;; Buffer ended
140   ret
141
142 ;; Parses a string of decimal digits
143 ;;
144 ;; Parameters:
145 ;;   * rcx = Length of string
146 ;;   * rdi = Pointer to string buffer
147 ;;
148 ;; Results:
149 ;;   * rax = Value
150 ;;
151 ;; Clobbers
152 parse_number:
153   mov r8, 0                     ; Result
154
155   ;; Add (10^(rcx-1) * parse_char(rdi[length - rcx])) to the
156   ;; accumulated value for each rcx.
157   mov [.length], rcx
158 .loop:
159   ;; First, calcuate 10^(rcx - 1)
160   mov rax, 1
161
162   mov r9, rcx
163   .exp_loop:
164     dec r9
165     jz .break
166     mov rbx, 10
167     mul rbx
168     jmp .exp_loop
169   .break:
170
171   ;; Now, rax = 10^(rcx - 1).
172
173   ;; We need to calulate the value of the character at rdi[length - rcx].
174    mov rbx, rdi
175   add rbx, [.length]
176   sub rbx, rcx
177   movzx rbx, byte [rbx]
178   sub rbx, '0'
179
180   cmp rbx, 10
181   jae .error
182
183   ;; Multiply this value by rax to get
184   ;;   (10^(rcx-1) * parse_char(rdi[length - rcx]))
185   ;; then add this to the result.
186   mul rbx
187
188   ;; Add that value to r8
189   add r8, rax
190
191   dec rcx
192   jnz .loop
193
194   mov rax, r8
195   ret
196
197 .error:
198   push rdi
199   print parse_number.error_msg
200   pop rdi
201   printlen rdi, [.length]
202   newline
203   mov rax, 100
204   call os_terminate
205
206 section '.data' readable writable
207
208 find.search_length dq ?
209 find.search_buffer dq ?
210
211 parse_number.length dq ?
212 parse_number.error_msg string "Invalid number: "