6267b3e133980579a94bc8dacaf6654add18962e
[rrq/jonasforth.git] / 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 wrong word
47
48   ;; Otherwise, we need to compare strings
49   lea rdx, [rsi + 8 + 1 + 1]    ; Location of character being compared in entry
50   mov rdi, [.search_buffer]     ; Location of character being compared in search buffer
51 .compare_char:
52   mov al, [rdx]
53   mov ah, [rdi]
54   cmp al, ah
55   jne .next                     ; They don't match; try again
56   inc rdx                       ; These characters match; look at the next ones
57   inc rdi
58   loop .compare_char
59
60   jmp .found                    ; They match! We are done.
61
62 .next:
63   mov rsi, [rsi]                ; Look at the previous entry
64   cmp rsi, 0
65   jnz .loop                    ; If there is no previous word, exit and return 0
66
67 .found:
68   ret
69
70 ;; Read a word from a buffer. Returns the buffer without the word, as well as
71 ;; the word that was read (including lengths).
72 ;;
73 ;; Inputs:
74 ;;   * rsi = Input buffer
75 ;;   * rcx = Length of buffer
76 ;;
77 ;; Outputs:
78 ;;   * rsi = Updated buffer
79 ;;   * rcx = Length of updated buffer
80 ;;   * rdi = Word buffer
81 ;;   * rdx = Length of word buffer
82 pop_word:
83 .skip_whitespace:
84   mov al, [rsi]
85   cmp al, ' '
86   je .got_whitespace
87   cmp al, $A
88   je .got_whitespace
89   jmp .alpha
90 .got_whitespace:
91   ;; The buffer starts with whitespace; discard the first character from the buffer.
92   inc rsi
93   dec rcx
94   jmp .skip_whitespace
95
96 .alpha:
97   ;; We got a character that wasn't whitespace. Now read the actual word.
98   mov rdi, rsi ; This is where the word starts
99   mov rdx, 1   ; Length of word
100
101 .read_alpha:
102   ;; Extract character from original buffer:
103   inc rsi
104   dec rcx
105
106   ;; When we hit whitespace, we are done with this word
107   mov al, [rsi]
108   cmp al, ' '
109   je .end
110   cmp al, $A
111   je .end
112
113   ;; It wasn't whitespace; add it to word buffer
114   inc rdx
115   jmp .read_alpha
116
117 .end:
118   ;; Finally, we want to skip one whitespace character after the word.
119   inc rsi
120   dec rcx
121
122   ret
123
124 ;; Parses a string.
125 ;;
126 ;; Parameters:
127 ;;   * rcx = Length of string
128 ;;   * rdi = Pointer to string buffer
129 ;;
130 ;; Results:
131 ;;   * rax = Value
132 ;;
133 ;; Clobbers
134 parse_number:
135   mov r8, 0                     ; Result
136
137   ;; Add (10^(rcx-1) * parse_char(rdi[length - rcx])) to the accumulated value
138   ;; for each rcx.
139   mov [.length], rcx
140 .loop:
141   ;; First, calcuate 10^(rcx - 1)
142   mov rax, 1
143
144   mov r9, rcx
145   .exp_loop:
146     dec r9
147     jz .break
148     mov rbx, 10
149     mul rbx
150     jmp .exp_loop
151   .break:
152
153   ;; Now, rax = 10^(rcx - 1).
154
155   ;; We need to calulate the value of the character at rdi[length - rcx].
156    mov rbx, rdi
157   add rbx, [.length]
158   sub rbx, rcx
159   movzx rbx, byte [rbx]
160   sub rbx, '0'
161
162   cmp rbx, 10
163   jae .error
164
165   ;; Multiply this value by rax to get (10^(rcx-1) * parse_char(rdi[length - rcx])),
166   ;; then add this to the result.
167   mul rbx
168
169   ;; Add that value to r8
170   add r8, rax
171
172   dec rcx
173   jnz .loop
174
175   mov rax, r8
176   ret
177
178 .error:
179   push rdi
180   print parse_number.error_msg
181   pop rdi
182   printlen rdi, [.length]
183   newline
184   mov rax, 100
185   call os_terminate
186
187 section '.data' readable writable
188
189 find.search_length dq ?
190 find.search_buffer dq ?
191
192 parse_number.length dq ?
193 parse_number.error_msg string "Invalid number: "
194