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