c713036c82afb2743c45a547e4b315ac4bf41ba4
[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   mov rdi, rsi
146   mov rdx, 10
147
148   add rsi, 10
149   sub rcx, 10
150
151   ret
152
153 ;; Parses a string.
154 ;;
155 ;; Parameters:
156 ;;   * rcx = Length of string
157 ;;   * rdi = Pointer to string buffer
158 ;;
159 ;; Results:
160 ;;   * rax = Value
161 ;;
162 ;; Clobbers
163 parse_number:
164   mov r8, 0                     ; Result
165
166   ;; Add (10^(rcx-1) * parse_char(rdi[length - rcx])) to the accumulated value
167   ;; for each rcx.
168   mov [.length], rcx
169 .loop:
170   ;; First, calcuate 10^(rcx - 1)
171   mov rax, 1
172
173   mov r9, rcx
174   .exp_loop:
175     dec r9
176     jz .break
177     mov rbx, 10
178     mul rbx
179     jmp .exp_loop
180   .break:
181
182   ;; Now, rax = 10^(rcx - 1).
183
184   ;; We need to calulate the value of the character at rdi[length - rcx].
185    mov rbx, rdi
186   add rbx, [.length]
187   sub rbx, rcx
188   movzx rbx, byte [rbx]
189   sub rbx, '0'
190
191   cmp rbx, 10
192   jae .error
193
194   ;; Multiply this value by rax to get (10^(rcx-1) * parse_char(rdi[length - rcx])),
195   ;; then add this to the result.
196   mul rbx
197
198   ;; Add that value to r8
199   add r8, rax
200
201   dec rcx
202   jnz .loop
203
204   mov rax, r8
205   ret
206
207 .error:
208   push rdi
209   print parse_number.error_msg
210   pop rdi
211   printlen rdi, [.length]
212   newline
213   exit 100
214
215 segment readable writable
216
217 find.search_length dq ?
218 find.search_buffer dq ?
219
220 read_word.max_size = $FF
221 read_word.buffer rb read_word.max_size
222 read_word.length db ?
223 read_word.char_buffer db ?
224
225 parse_number.length dq ?
226 parse_number.error_msg string "Invalid number: "
227