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