https://github.com/akkartik/mu/blob/master/311decimal-int.subx
  1 # Helpers for decimal ints.
  2 
  3 parse-decimal-int-from-slice:  # in: (addr slice) -> out/eax: int
  4     # . prologue
  5     55/push-ebp
  6     89/<- %ebp 4/r32/esp
  7     # . save registers
  8     51/push-ecx
  9     # ecx = in
 10     8b/-> *(ebp+8) 1/r32/ecx
 11     #
 12     (parse-decimal-int-helper *ecx *(ecx+4))  # => eax
 13 $parse-decimal-int-from-slice:end:
 14     # . restore registers
 15     59/pop-to-ecx
 16     # . epilogue
 17     89/<- %esp 5/r32/ebp
 18     5d/pop-to-ebp
 19     c3/return
 20 
 21 parse-decimal-int:  # in: (addr array byte) -> result/eax: int
 22     # . prologue
 23     55/push-ebp
 24     89/<- %ebp 4/r32/esp
 25     # . save registers
 26     51/push-ecx
 27     52/push-edx
 28     # eax = in
 29     8b/-> *(ebp+8) 0/r32/eax
 30     # var start/ecx: (addr byte) = &in->data
 31     8d/copy-address *(eax+4) 1/r32/ecx
 32     # var end/edx: (addr byte) = &in->data[in->size]
 33     8b/-> *eax 2/r32/edx
 34     8d/copy-address *(eax+edx+4) 2/r32/edx
 35     #
 36     (parse-decimal-int-helper %ecx %edx)  # => eax
 37 $parse-decimal-int:end:
 38     # . restore registers
 39     5a/pop-to-edx
 40     59/pop-to-ecx
 41     # . epilogue
 42     89/<- %esp 5/r32/ebp
 43     5d/pop-to-ebp
 44     c3/return
 45 
 46 parse-decimal-int-from-stream:  # in: (addr stream byte) -> result/eax: int
 47     # . prologue
 48     55/push-ebp
 49     89/<- %ebp 4/r32/esp
 50     # . save registers
 51     51/push-ecx
 52     52/push-edx
 53     # eax = in
 54     8b/-> *(ebp+8) 0/r32/eax
 55     # var start/ecx: (addr byte) = &in->data[in->read]
 56     8b/-> *(eax+4) 1/r32/ecx
 57     8d/copy-address *(eax+ecx+0xc) 1/r32/ecx
 58     # var end/edx: (addr byte) = &in->data[in->write]
 59     8b/-> *eax 2/r32/edx
 60     8d/copy-address *(eax+edx+0xc) 2/r32/edx
 61     #
 62     (parse-decimal-int-helper %ecx %edx)  # => eax
 63 $parse-decimal-int-from-stream:end:
 64     # . restore registers
 65     5a/pop-to-edx
 66     59/pop-to-ecx
 67     # . epilogue
 68     89/<- %esp 5/r32/ebp
 69     5d/pop-to-ebp
 70     c3/return
 71 
 72 parse-decimal-int-helper:  # start: (addr byte), end: (addr byte) -> result/eax: int
 73     # . prologue
 74     55/push-ebp
 75     89/<- %ebp 4/r32/esp
 76     # . save registers
 77     51/push-ecx
 78     52/push-edx
 79     53/push-ebx
 80     56/push-esi
 81     57/push-edi
 82     # var curr/esi: (addr byte) = start
 83     8b/-> *(ebp+8) 6/r32/esi
 84     # edi = end
 85     8b/-> *(ebp+0xc) 7/r32/edi
 86     # var negate?/edx: boolean = false
 87     ba/copy-to-edx 0/imm32/false
 88     # if (*curr == '-') ++curr, negate = true
 89     {
 90 $parse-decimal-int-helper:negative:
 91       b8/copy-to-eax 0/imm32
 92       8a/copy-byte *esi 0/r32/AL
 93       3d/compare-eax-and 0x2d/imm32/-
 94       75/jump-if-!= break/disp8
 95       # . ++curr
 96       46/increment-esi
 97       # . negate = true
 98       ba/copy-to-edx  1/imm32/true
 99     }
100     # spill negate?
101     52/push-edx
102     # var result/eax: int = 0
103     b8/copy-to-eax 0/imm32
104     # var digit/ecx: int = 0
105     b9/copy-to-ecx 0/imm32
106     # const TEN/ebx: int = 10
107     bb/copy-to-ebx 0xa/imm32
108     {
109 $parse-decimal-int-helper:loop:
110       # if (curr >= in->end) break
111       39/compare %esi 7/r32/edi
112       73/jump-if-addr>= break/disp8
113       # digit = from-decimal-char(*curr)
114       8a/copy-byte *esi 1/r32/CL
115       81 5/subop/subtract %ecx 0x30/imm32/zero
116       # TODO: error checking
117       # result = result * 10 + digit
118       ba/copy-to-edx 0/imm32
119       f7 4/subop/multiply-into-edx-eax %ebx
120       # TODO: check edx for overflow
121       01/add %eax 1/r32/ecx
122       # ++curr
123       46/increment-esi
124       #
125       eb/jump loop/disp8
126     }
127 $parse-decimal-int-helper:negate:
128     # if (negate?) result = -result
129     5a/pop-to-edx
130     {
131       81 7/subop/compare %edx 0/imm32/false
132       74/jump-if-= break/disp8
133       f7 3/subop/negate %eax
134     }
135 $parse-decimal-int-helper:end:
136     # . restore registers
137     5f/pop-to-edi
138     5e/pop-to-esi
139     5b/pop-to-ebx
140     5a/pop-to-edx
141     59/pop-to-ecx
142     # . epilogue
143     89/<- %esp 5/r32/ebp
144     5d/pop-to-ebp
145     c3/return
146 
147 test-parse-decimal-int-from-slice-single-digit:
148     # . prologue
149     55/push-ebp
150     89/<- %ebp 4/r32/esp
151     # . save registers
152     50/push-eax
153     51/push-ecx
154     # (eax..ecx) = "3"
155     b8/copy-to-eax "3"/imm32
156     8b/-> *eax 1/r32/ecx
157     8d/copy-address *(eax+ecx+4) 1/r32/ecx
158     05/add-to-eax 4/imm32
159     # var slice/ecx: slice = {eax, ecx}
160     51/push-ecx
161     50/push-eax
162     89/<- %ecx 4/r32/esp
163     #
164     (parse-decimal-int-from-slice %ecx)  # => eax
165     (check-ints-equal %eax 3 "F - test-parse-decimal-int-from-slice-single-digit")
166 $test-parse-decimal-int-helper-single-digit:end:
167     # . restore registers
168     59/pop-to-ecx
169     58/pop-to-eax
170     # . epilogue
171     89/<- %esp 5/r32/ebp
172     5d/pop-to-ebp
173     c3/return
174 
175 test-parse-decimal-int-from-slice-multi-digit:
176     # . prologue
177     55/push-ebp
178     89/<- %ebp 4/r32/esp
179     # . save registers
180     50/push-eax
181     51/push-ecx
182     # (eax..ecx) = "34"
183     b8/copy-to-eax "34"/imm32
184     8b/-> *eax 1/r32/ecx
185     8d/copy-address *(eax+ecx+4) 1/r32/ecx
186     05/add-to-eax 4/imm32
187     # var slice/ecx: slice = {eax, ecx}
188     51/push-ecx
189     50/push-eax
190     89/<- %ecx 4/r32/esp
191     #
192     (parse-decimal-int-from-slice %ecx)  # => eax
193     (check-ints-equal %eax 0x22 "F - test-parse-decimal-int-from-slice-multi-digit")  # 34 in hex
194 $test-parse-decimal-int-helper-multi-digit:end:
195     # . restore registers
196     59/pop-to-ecx
197     58/pop-to-eax
198     # . epilogue
199     89/<- %esp 5/r32/ebp
200     5d/pop-to-ebp
201     c3/return
202 
203 test-parse-decimal-int-from-slice-zero:
204     # . prologue
205     55/push-ebp
206     89/<- %ebp 4/r32/esp
207     # . save registers
208     50/push-eax
209     51/push-ecx
210     # (eax..ecx) = "00"
211     b8/copy-to-eax "00"/imm32
212     8b/-> *eax 1/r32/ecx
213     8d/copy-address *(eax+ecx+4) 1/r32/ecx
214     05/add-to-eax 4/imm32
215     # var slice/ecx: slice = {eax, ecx}
216     51/push-ecx
217     50/push-eax
218     89/<- %ecx 4/r32/esp
219     #
220     (parse-decimal-int-from-slice %ecx)  # => eax
221     (check-ints-equal %eax 0 "F - test-parse-decimal-int-from-slice-zero")
222 $test-parse-decimal-int-helper-zero:end:
223     # . restore registers
224     59/pop-to-ecx
225     58/pop-to-eax
226     # . epilogue
227     89/<- %esp 5/r32/ebp
228     5d/pop-to-ebp
229     c3/return
230 
231 test-parse-decimal-int-from-slice-negative:
232     # . prologue
233     55/push-ebp
234     89/<- %ebp 4/r32/esp
235     # . save registers
236     50/push-eax
237     51/push-ecx
238     # (eax..ecx) = "-3"
239     b8/copy-to-eax "-3"/imm32
240     8b/-> *eax 1/r32/ecx
241     8d/copy-address *(eax+ecx+4) 1/r32/ecx
242     05/add-to-eax 4/imm32
243     # var slice/ecx: slice = {eax, ecx}
244     51/push-ecx
245     50/push-eax
246     89/<- %ecx 4/r32/esp
247     #
248     (parse-decimal-int-from-slice %ecx)  # => eax
249     (check-ints-equal %eax -3 "F - test-parse-decimal-int-from-slice-negative")
250 $test-parse-decimal-int-helper-negative:end:
251     # . restore registers
252     59/pop-to-ecx
253     58/pop-to-eax
254     # . epilogue
255     89/<- %esp 5/r32/ebp
256     5d/pop-to-ebp
257     c3/return
258 
259 test-parse-decimal-int-from-slice-multi-digit-negative:
260     # . prologue
261     55/push-ebp
262     89/<- %ebp 4/r32/esp
263     # . save registers
264     50/push-eax
265     51/push-ecx
266     # (eax..ecx) = "-32"
267     b8/copy-to-eax "-32"/imm32
268     8b/-> *eax 1/r32/ecx
269     8d/copy-address *(eax+ecx+4) 1/r32/ecx
270     05/add-to-eax 4/imm32
271     # var slice/ecx: slice = {eax, ecx}
272     51/push-ecx
273     50/push-eax
274     89/<- %ecx 4/r32/esp
275     #
276     (parse-decimal-int-from-slice %ecx)  # => eax
277     (check-ints-equal %eax -0x20 "F - test-parse-decimal-int-from-slice-multi-digit-negative")  # -32 in hex
278 $test-parse-decimal-int-helper-multi-digit-negative:end:
279     # . restore registers
280     59/pop-to-ecx
281     58/pop-to-eax
282     # . epilogue
283     89/<- %esp 5/r32/ebp
284     5d/pop-to-ebp
285     c3/return
286 
287 decimal-size:  # n: int -> result/eax: int
288     # pseudocode:
289     #   edi = 0
290     #   eax = n
291     #   if eax < 0
292     #     ++edi  # for '-'
293     #     negate eax
294     #   while true
295     #     edx = 0
296     #     eax, edx = eax/10, eax%10
297     #     ++edi
298     #     if (eax == 0) break
299     #   eax = edi
300     #
301     # . prologue
302     55/push-ebp
303     89/<- %ebp 4/r32/esp
304     # . save registers
305     51/push-ecx
306     52/push-edx
307     57/push-edi
308     # edi = 0
309     bf/copy-to-edi 0/imm32
310     # eax = n
311     8b/-> *(ebp+8) 0/r32/eax
312     # if (n < 0) negate n, increment edi
313     {
314       3d/compare-eax-with 0/imm32
315       7d/jump-if->= break/disp8
316       f7 3/subop/negate %eax
317       47/increment-edi
318     }
319     # const ten/ecx = 10
320     b9/copy-to-ecx  0xa/imm32
321     {
322       ba/copy-to-edx 0/imm32
323       f7 7/subop/idiv-edx-eax-by %ecx  # eax = edx:eax/10; edx = edx:eax%10
324       47/increment-edi
325       3d/compare-eax-and 0/imm32
326       75/jump-if-!= loop/disp8
327     }
328 $decimal-size:end:
329     89/<- %eax 7/r32/edi
330     # . restore registers
331     5f/pop-to-edi
332     5a/pop-to-edx
333     59/pop-to-ecx
334     # . epilogue
335     89/<- %esp 5/r32/ebp
336     5d/pop-to-ebp
337     c3/return
338 
339 test-decimal-size-of-zero:
340     # . prologue
341     55/push-ebp
342     89/<- %ebp 4/r32/esp
343     #
344     (decimal-size 0)  # => eax
345     (check-ints-equal %eax 1 "F - test-decimal-size-of-zero")
346 $test-decimal-size-of-zero:end:
347     # . epilogue
348     89/<- %esp 5/r32/ebp
349     5d/pop-to-ebp
350     c3/return
351 
352 test-decimal-size-single-digit:
353     # . prologue
354     55/push-ebp
355     89/<- %ebp 4/r32/esp
356     #
357     (decimal-size 4)  # => eax
358     (check-ints-equal %eax 1 "F - test-decimal-size-single-digit")
359 $test-decimal-size-single-digit:end:
360     # . epilogue
361     89/<- %esp 5/r32/ebp
362     5d/pop-to-ebp
363     c3/return
364 
365 test-decimal-size-multi-digit:
366     # . prologue
367     55/push-ebp
368     89/<- %ebp 4/r32/esp
369     #
370     (decimal-size 0xa)  # => eax
371     (check-ints-equal %eax 2 "F - test-decimal-size-multi-digit")
372 $test-decimal-size-multi-digit:end:
373     # . epilogue
374     89/<- %esp 5/r32/ebp
375     5d/pop-to-ebp
376     c3/return
377 
378 test-decimal-size-single-digit-negative:
379     # . prologue
380     55/push-ebp
381     89/<- %ebp 4/r32/esp
382     #
383     (decimal-size -4)  # => eax
384     (check-ints-equal %eax 2 "F - test-decimal-size-single-digit-negative")
385 $test-decimal-size-single-digit-negative:end:
386     # . epilogue
387     89/<- %esp 5/r32/ebp
388     5d/pop-to-ebp
389     c3/return
390 
391 test-decimal-size-multi-digit-negative:
392     # . prologue
393     55/push-ebp
394     89/<- %ebp 4/r32/esp
395     #
396     (decimal-size -0xa)  # => eax
397     (check-ints-equal %eax 3 "F - test-decimal-size-multi-digit-negative")
398 $test-decimal-size-multi-digit-negative:end:
399     # . epilogue
400     89/<- %esp 5/r32/ebp
401     5d/pop-to-ebp
402     c3/return