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