Implement BUF"
[rrq/jonasforth.git] / impl.asm
1 ;; vim: syntax=fasm
2
3 segment readable executable
4
5 macro printlen msg, len {
6   push rsi
7   add rsp, 8
8
9   mov rsi, msg
10   mov rdx, len
11   mov rax, 1
12   mov rdi, 1
13   syscall
14
15   sub rsp, 8
16   pop rsi
17 }
18
19 macro newline {
20   push $A
21   printlen rsp, 1
22 }
23
24 macro print msg {
25   printlen msg, msg#.len
26 }
27
28 macro exit code {
29   mov rax, $3C
30   mov rdi, code
31   syscall
32 }
33
34 struc string bytes {
35   . db bytes
36   .len = $ - .
37 }
38
39 ;; Find the given word in the dictionary of words. If no such word exists,
40 ;; return 0.
41 ;;
42 ;; Parameters:
43 ;;   * [find.search_length] = Length of the word in bytes.
44 ;;   * [find.search_buffer] = Pointer to the string containing the word.
45 ;;   * rsi = Pointer to the last entry in the dictionary.
46 ;;
47 ;; Results:
48 ;;   * rsi = Pointer to the found entry in the dictionary or 0.
49 ;;
50 ;; Clobbers rcx, rdx, rdi, rax.
51 find:
52   ;; RSI contains the entry we are currently looking at
53 .loop:
54   movzx rcx, byte [rsi + 8 + 1]    ; Length of word being looked at
55   cmp rcx, [.search_length]
56   jne .next    ; If the words don't have the same length, we have the wrong word
57
58   ;; Otherwise, we need to compare strings
59   lea rdx, [rsi + 8 + 1 + 1]    ; Location of character being compared in entry
60   mov rdi, [.search_buffer]     ; Location of character being compared in search buffer
61 .compare_char:
62   mov al, [rdx]
63   mov ah, [rdi]
64   cmp al, ah
65   jne .next                     ; They don't match; try again
66   inc rdx                       ; These characters match; look at the next ones
67   inc rdi
68   loop .compare_char
69
70   jmp .found                    ; They match! We are done.
71
72 .next:
73   mov rsi, [rsi]                ; Look at the previous entry
74   cmp rsi, 0
75   jnz .loop                    ; If there is no previous word, exit and return 0
76
77 .found:
78   ret
79
80 ;; Read a word from standard input. Returns pointer to string containing word as
81 ;; well as length.
82 ;;
83 ;; Results:
84 ;;   * rdx = Length of string
85 ;;   * rdi = Pointer to string buffer
86 ;;
87 ;; Clobbers pretty much everything.
88 read_word:
89 .skip_whitespace:
90   ;; Read characters into .char_buffer until one of them is not whitespace.
91   mov rax, 0
92   mov rdi, 0
93   mov rsi, .char_buffer
94   mov rdx, 1
95   syscall
96
97   ;; We consider newlines and spaces to be whitespace.
98   cmp [.char_buffer], ' '
99   je .skip_whitespace
100   cmp [.char_buffer], $A
101   je .skip_whitespace
102
103 .alpha:
104   ;; We got a character that wasn't whitespace. Now read the actual word.
105   mov [.length], 0
106
107 .read_alpha:
108   mov al, [.char_buffer]
109   movzx rbx, [.length]
110   mov rsi, .buffer
111   add rsi, rbx
112   mov [rsi], al
113   inc [.length]
114
115   mov rax, 0
116   mov rdi, 0
117   mov rsi, .char_buffer
118   mov rdx, 1
119   syscall
120
121   cmp [.char_buffer], ' '
122   je .end
123   cmp [.char_buffer], $A
124   jne .read_alpha
125
126 .end:
127   mov rdi, .buffer
128   movzx rdx, [.length]
129
130   ret
131
132 ;; Read a word from a buffer. Returns the buffer without the word, as well as
133 ;; the word that was read (including lengths).
134 ;;
135 ;; Inputs:
136 ;;   * rsi = Input buffer
137 ;;   * rcx = Length of buffer
138 ;;
139 ;; Outputs:
140 ;;   * rsi = Updated buffer
141 ;;   * rcx = Length of updated buffer
142 ;;   * rdi = Word buffer
143 ;;   * rdx = Length of word buffer
144 pop_word:
145 .skip_whitespace:
146   mov al, [rsi]
147   cmp al, ' '
148   je .got_whitespace
149   cmp al, $A
150   je .got_whitespace
151   jmp .alpha
152 .got_whitespace:
153   ;; The buffer starts with whitespace; discard the first character from the buffer.
154   inc rsi
155   dec rcx
156   jmp .skip_whitespace
157
158 .alpha:
159   ;; We got a character that wasn't whitespace. Now read the actual word.
160   mov rdi, rsi ; This is where the word starts
161   mov rdx, 1   ; Length of word
162
163 .read_alpha:
164   ;; Extract character from original buffer:
165   inc rsi
166   dec rcx
167
168   ;; When we hit whitespace, we are done with this word
169   mov al, [rsi]
170   cmp al, ' '
171   je .end
172   cmp al, $A
173   je .end
174
175   ;; It wasn't whitespace; add it to word buffer
176   inc rdx
177   jmp .read_alpha
178
179 .end:
180
181   ret
182
183 ;; Parses a string.
184 ;;
185 ;; Parameters:
186 ;;   * rcx = Length of string
187 ;;   * rdi = Pointer to string buffer
188 ;;
189 ;; Results:
190 ;;   * rax = Value
191 ;;
192 ;; Clobbers
193 parse_number:
194   mov r8, 0                     ; Result
195
196   ;; Add (10^(rcx-1) * parse_char(rdi[length - rcx])) to the accumulated value
197   ;; for each rcx.
198   mov [.length], rcx
199 .loop:
200   ;; First, calcuate 10^(rcx - 1)
201   mov rax, 1
202
203   mov r9, rcx
204   .exp_loop:
205     dec r9
206     jz .break
207     mov rbx, 10
208     mul rbx
209     jmp .exp_loop
210   .break:
211
212   ;; Now, rax = 10^(rcx - 1).
213
214   ;; We need to calulate the value of the character at rdi[length - rcx].
215    mov rbx, rdi
216   add rbx, [.length]
217   sub rbx, rcx
218   movzx rbx, byte [rbx]
219   sub rbx, '0'
220
221   cmp rbx, 10
222   jae .error
223
224   ;; Multiply this value by rax to get (10^(rcx-1) * parse_char(rdi[length - rcx])),
225   ;; then add this to the result.
226   mul rbx
227
228   ;; Add that value to r8
229   add r8, rax
230
231   dec rcx
232   jnz .loop
233
234   mov rax, r8
235   ret
236
237 .error:
238   push rdi
239   print parse_number.error_msg
240   pop rdi
241   printlen rdi, [.length]
242   newline
243   exit 100
244
245 segment readable writable
246
247 find.search_length dq ?
248 find.search_buffer dq ?
249
250 read_word.max_size = $FF
251 read_word.buffer rb read_word.max_size
252 read_word.length db ?
253 read_word.char_buffer db ?
254
255 parse_number.length dq ?
256 parse_number.error_msg string "Invalid number: "
257