https://github.com/akkartik/mu/blob/master/311decimal-int.subx
  1 # Helpers for decimal ints.
  2 
  3 # if slice doesn't contain a decimal number, return 0
  4 parse-decimal-int-from-slice:  # in: (addr slice) -> out/eax: int
  5     # . prologue
  6     55/push-ebp
  7     89/<- %ebp 4/r32/esp
  8     # . save registers
  9     51/push-ecx
 10     # ecx = in
 11     8b/-> *(ebp+8) 1/r32/ecx
 12     #
 13     (parse-decimal-int-helper *ecx *(ecx+4))  # => eax
 14 $parse-decimal-int-from-slice:end:
 15     # . restore registers
 16     59/pop-to-ecx
 17     # . epilogue
 18     89/<- %esp 5/r32/ebp
 19     5d/pop-to-ebp
 20     c3/return
 21 
 22 # if slice doesn't contain a decimal number, return 0
 23 parse-decimal-int:  # in: (addr array byte) -> result/eax: int
 24     # . prologue
 25     55/push-ebp
 26     89/<- %ebp 4/r32/esp
 27     # . save registers
 28     51/push-ecx
 29     52/push-edx
 30     # eax = in
 31     8b/-> *(ebp+8) 0/r32/eax
 32     # var start/ecx: (addr byte) = &in->data
 33     8d/copy-address *(eax+4) 1/r32/ecx
 34     # var end/edx: (addr byte) = &in->data[in->size]
 35     8b/-> *eax 2/r32/edx
 36     8d/copy-address *(eax+edx+4) 2/r32/edx
 37     #
 38     (parse-decimal-int-helper %ecx %edx)  # => eax
 39 $parse-decimal-int:end:
 40     # . restore registers
 41     5a/pop-to-edx
 42     59/pop-to-ecx
 43     # . epilogue
 44     89/<- %esp 5/r32/ebp
 45     5d/pop-to-ebp
 46     c3/return
 47 
 48 parse-decimal-int-from-stream:  # in: (addr stream byte) -> result/eax: int
 49     # . prologue
 50     55/push-ebp
 51     89/<- %ebp 4/r32/esp
 52     # . save registers
 53     51/push-ecx
 54     52/push-edx
 55     # eax = in
 56     8b/-> *(ebp+8) 0/r32/eax
 57     # var start/ecx: (addr byte) = &in->data[in->read]
 58     8b/-> *(eax+4) 1/r32/ecx
 59     8d/copy-address *(eax+ecx+0xc) 1/r32/ecx
 60     # var end/edx: (addr byte) = &in->data[in->write]
 61     8b/-> *eax 2/r32/edx
 62     8d/copy-address *(eax+edx+0xc) 2/r32/edx
 63     #
 64     (parse-decimal-int-helper %ecx %edx)  # => eax
 65 $parse-decimal-int-from-stream:end:
 66     # . restore registers
 67     5a/pop-to-edx
 68     59/pop-to-ecx
 69     # . epilogue
 70     89/<- %esp 5/r32/ebp
 71     5d/pop-to-ebp
 72     c3/return
 73 
 74 parse-decimal-int-helper:  # start: (addr byte), end: (addr byte) -> result/eax: int
 75     # . prologue
 76     55/push-ebp
 77     89/<- %ebp 4/r32/esp
 78     # . save registers
 79     51/push-ecx
 80     52/push-edx
 81     53/push-ebx
 82     56/push-esi
 83     57/push-edi
 84     # var curr/esi: (addr byte) = start
 85     8b/-> *(ebp+8) 6/r32/esi
 86     # edi = end
 87     8b/-> *(ebp+0xc) 7/r32/edi
 88     # var negate?/edx: boolean = false
 89     ba/copy-to-edx 0/imm32/false
 90     # if (*curr == '-') ++curr, negate = true
 91     {
 92 $parse-decimal-int-helper:negative:
 93       b8/copy-to-eax 0/imm32
 94       8a/copy-byte *esi 0/r32/AL
 95       3d/compare-eax-and 0x2d/imm32/-
 96       75/jump-if-!= break/disp8
 97       # . ++curr
 98       46/increment-esi
 99       # . negate = true
100       ba/copy-to-edx  1/imm32/true
101     }
102     # spill negate?
103     52/push-edx
104     # var result/eax: int = 0
105     b8/copy-to-eax 0/imm32
106     # var digit/ecx: int = 0
107     b9/copy-to-ecx 0/imm32
108     # const TEN/ebx: int = 10
109     bb/copy-to-ebx 0xa/imm32
110     {
111 $parse-decimal-int-helper:loop:
112       # if (curr >= in->end) break
113       39/compare %esi 7/r32/edi
114       73/jump-if-addr>= break/disp8
115       # if !is-decimal-digit?(*curr) return 0
116       8a/copy-byte *esi 1/r32/CL
117       50/push-eax
118       (is-decimal-digit? %ecx)  # => eax
119       {
120         3d/compare-eax-and 0/imm32/false
121         75/jump-if-!= break/disp8
122         58/pop-to-eax
123         b8/copy-to-eax 0/imm32
124         eb/jump $parse-decimal-int-helper:negate/disp8
125       }
126       58/pop-to-eax
127       # digit = from-decimal-char(*curr)
128       81 5/subop/subtract %ecx 0x30/imm32/zero
129       # TODO: error checking
130       # result = result * 10 + digit
131       ba/copy-to-edx 0/imm32
132       f7 4/subop/multiply-into-edx-eax %ebx
133       # TODO: check edx for overflow
134       01/add %eax 1/r32/ecx
135       # ++curr
136       46/increment-esi
137       #
138       eb/jump loop/disp8
139     }
140 $parse-decimal-int-helper:negate:
141     # if (negate?) result = -result
142     5a/pop-to-edx
143     {
144       81 7/subop/compare %edx 0/imm32/false
145       74/jump-if-= break/disp8
146       f7 3/subop/negate %eax
147     }
148 $parse-decimal-int-helper:end:
149     # . restore registers
150     5f/pop-to-edi
151     5e/pop-to-esi
152     5b/pop-to-ebx
153     5a/pop-to-edx
154     59/pop-to-ecx
155     # . epilogue
156     89/<- %esp 5/r32/ebp
157     5d/pop-to-ebp
158     c3/return
159 
160 test-parse-decimal-int-from-slice-single-digit:
161     # . prologue
162     55/push-ebp
163     89/<- %ebp 4/r32/esp
164     # . save registers
165     50/push-eax
166     51/push-ecx
167     # (eax..ecx) = "3"
168     b8/copy-to-eax "3"/imm32
169     8b/-> *eax 1/r32/ecx
170     8d/copy-address *(eax+ecx+4) 1/r32/ecx
171     05/add-to-eax 4/imm32
172     # var slice/ecx: slice = {eax, ecx}
173     51/push-ecx
174     50/push-eax
175     89/<- %ecx 4/r32/esp
176     #
177     (parse-decimal-int-from-slice %ecx)  # => eax
178     (check-ints-equal %eax 3 "F - test-parse-decimal-int-from-slice-single-digit")
179 $test-parse-decimal-int-helper-single-digit:end:
180     # . restore registers
181     59/pop-to-ecx
182     58/pop-to-eax
183     # . epilogue
184     89/<- %esp 5/r32/ebp
185     5d/pop-to-ebp
186     c3/return
187 
188 test-parse-decimal-int-from-slice-multi-digit:
189     # . prologue
190     55/push-ebp
191     89/<- %ebp 4/r32/esp
192     # . save registers
193     50/push-eax
194     51/push-ecx
195     # (eax..ecx) = "34"
196     b8/copy-to-eax "34"/imm32
197     8b/-> *eax 1/r32/ecx
198     8d/copy-address *(eax+ecx+4) 1/r32/ecx
199     05/add-to-eax 4/imm32
200     # var slice/ecx: slice = {eax, ecx}
201     51/push-ecx
202     50/push-eax
203     89/<- %ecx 4/r32/esp
204     #
205     (parse-decimal-int-from-slice %ecx)  # => eax
206     (check-ints-equal %eax 0x22 "F - test-parse-decimal-int-from-slice-multi-digit")  # 34 in hex
207 $test-parse-decimal-int-helper-multi-digit:end:
208     # . restore registers
209     59/pop-to-ecx
210     58/pop-to-eax
211     # . epilogue
212     89/<- %esp 5/r32/ebp
213     5d/pop-to-ebp
214     c3/return
215 
216 test-parse-decimal-int-from-slice-zero:
217     # . prologue
218     55/push-ebp
219     89/<- %ebp 4/r32/esp
220     # . save registers
221     50/push-eax
222     51/push-ecx
223     # (eax..ecx) = "00"
224     b8/copy-to-eax "00"/imm32
225     8b/-> *eax 1/r32/ecx
226     8d/copy-address *(eax+ecx+4) 1/r32/ecx
227     05/add-to-eax 4/imm32
228     # var slice/ecx: slice = {eax, ecx}
229     51/push-ecx
230     50/push-eax
231     89/<- %ecx 4/r32/esp
232     #
233     (parse-decimal-int-from-slice %ecx)  # => eax
234     (check-ints-equal %eax 0 "F - test-parse-decimal-int-from-slice-zero")
235 $test-parse-decimal-int-helper-zero:end:
236     # . restore registers
237     59/pop-to-ecx
238     58/pop-to-eax
239     # . epilogue
240     89/<- %esp 5/r32/ebp
241     5d/pop-to-ebp
242     c3/return
243 
244 test-parse-decimal-int-from-slice-negative:
245     # . prologue
246     55/push-ebp
247     89/<- %ebp 4/r32/esp
248     # . save registers
249     50/push-eax
250     51/push-ecx
251     # (eax..ecx) = "-3"
252     b8/copy-to-eax "-3"/imm32
253     8b/-> *eax 1/r32/ecx
254     8d/copy-address *(eax+ecx+4) 1/r32/ecx
255     05/add-to-eax 4/imm32
256     # var slice/ecx: slice = {eax, ecx}
257     51/push-ecx
258     50/push-eax
259     89/<- %ecx 4/r32/esp
260     #
261     (parse-decimal-int-from-slice %ecx)  # => eax
262     (check-ints-equal %eax -3 "F - test-parse-decimal-int-from-slice-negative")
263 $test-parse-decimal-int-helper-negative:end:
264     # . restore registers
265     59/pop-to-ecx
266     58/pop-to-eax
267     # . epilogue
268     89/<- %esp 5/r32/ebp
269     5d/pop-to-ebp
270     c3/return
271 
272 test-parse-decimal-int-from-slice-multi-digit-negative:
273     # . prologue
274     55/push-ebp
275     89/<- %ebp 4/r32/esp
276     # . save registers
277     50/push-eax
278     51/push-ecx
279     # (eax..ecx) = "-32"
280     b8/copy-to-eax "-32"/imm32
281     8b/-> *eax 1/r32/ecx
282     8d/copy-address *(eax+ecx+4) 1/r32/ecx
283     05/add-to-eax 4/imm32
284     # var slice/ecx: slice = {eax, ecx}
285     51/push-ecx
286     50/push-eax
287     89/<- %ecx 4/r32/esp
288     #
289     (parse-decimal-int-from-slice %ecx)  # => eax
290     (check-ints-equal %eax -0x20 "F - test-parse-decimal-int-from-slice-multi-digit-negative")  # -32 in hex
291 $test-parse-decimal-int-helper-multi-digit-negative:end:
292     # . restore registers
293     59/pop-to-ecx
294     58/pop-to-eax
295     # . epilogue
296     89/<- %esp 5/r32/ebp
297     5d/pop-to-ebp
298     c3/return
299 
300 decimal-size:  # n: int -> result/eax: int
301     # pseudocode:
302     #   edi = 0
303     #   eax = n
304     #   if eax < 0
305     #     ++edi  # for '-'
306     #     negate eax
307     #   while true
308     #     edx = 0
309     #     eax, edx = eax/10, eax%10
310     #     ++edi
311     #     if (eax == 0) break
312     #   eax = edi
313     #
314     # . prologue
315     55/push-ebp
316     89/<- %ebp 4/r32/esp
317     # . save registers
318     51/push-ecx
319     52/push-edx
320     57/push-edi
321     # edi = 0
322     bf/copy-to-edi 0/imm32
323     # eax = n
324     8b/-> *(ebp+8) 0/r32/eax
325     # if (n < 0) negate n, increment edi
326     {
327       3d/compare-eax-with 0/imm32
328       7d/jump-if->= break/disp8
329       f7 3/subop/negate %eax
330       47/increment-edi
331     }
332     # const ten/ecx = 10
333     b9/copy-to-ecx  0xa/imm32
334     {
335       ba/copy-to-edx 0/imm32
336       f7 7/subop/idiv-edx-eax-by %ecx  # eax = edx:eax/10; edx = edx:eax%10
337       47/increment-edi
338       3d/compare-eax-and 0/imm32
339       75/jump-if-!= loop/disp8
340     }
341 $decimal-size:end:
342     89/<- %eax 7/r32/edi
343     # . restore registers
344     5f/pop-to-edi
345     5a/pop-to-edx
346     59/pop-to-ecx
347     # . epilogue
348     89/<- %esp 5/r32/ebp
349     5d/pop-to-ebp
350     c3/return
351 
352 test-decimal-size-of-zero:
353     # . prologue
354     55/push-ebp
355     89/<- %ebp 4/r32/esp
356     #
357     (decimal-size 0)  # => eax
358     (check-ints-equal %eax 1 "F - test-decimal-size-of-zero")
359 $test-decimal-size-of-zero:end:
360     # . epilogue
361     89/<- %esp 5/r32/ebp
362     5d/pop-to-ebp
363     c3/return
364 
365 test-decimal-size-single-digit:
366     # . prologue
367     55/push-ebp
368     89/<- %ebp 4/r32/esp
369     #
370     (decimal-size 4)  # => eax
371     (check-ints-equal %eax 1 "F - test-decimal-size-single-digit")
372 $test-decimal-size-single-digit:end:
373     # . epilogue
374     89/<- %esp 5/r32/ebp
375     5d/pop-to-ebp
376     c3/return
377 
378 test-decimal-size-multi-digit:
379     # . prologue
380     55/push-ebp
381     89/<- %ebp 4/r32/esp
382     #
383     (decimal-size 0xa)  # => eax
384     (check-ints-equal %eax 2 "F - test-decimal-size-multi-digit")
385 $test-decimal-size-multi-digit:end:
386     # . epilogue
387     89/<- %esp 5/r32/ebp
388     5d/pop-to-ebp
389     c3/return
390 
391 test-decimal-size-single-digit-negative:
392     # . prologue
393     55/push-ebp
394     89/<- %ebp 4/r32/esp
395     #
396     (decimal-size -4)  # => eax
397     (check-ints-equal %eax 2 "F - test-decimal-size-single-digit-negative")
398 $test-decimal-size-single-digit-negative:end:
399     # . epilogue
400     89/<- %esp 5/r32/ebp
401     5d/pop-to-ebp
402     c3/return
403 
404 test-decimal-size-multi-digit-negative:
405     # . prologue
406     55/push-ebp
407     89/<- %ebp 4/r32/esp
408     #
409     (decimal-size -0xa)  # => eax
410     (check-ints-equal %eax 3 "F - test-decimal-size-multi-digit-negative")
411 $test-decimal-size-multi-digit-negative:end:
412     # . epilogue
413     89/<- %esp 5/r32/ebp
414     5d/pop-to-ebp
415     c3/return
416 
417 _parse-array-of-decimal-ints:  # ad: (addr allocation-descriptor), s: (addr array byte), out: (addr handle array int)
418     # pseudocode
419     #   end = &s->data[s->size]
420     #   curr = s->data
421     #   size = 0
422     #   while true
423     #     if (curr >= end) break
424     #     curr = skip-chars-matching-in-slice(curr, end, ' ')
425     #     if (curr >= end) break
426     #     curr = skip-chars-not-matching-in-slice(curr, end, ' ')
427     #     ++size
428     #   allocate-array(ad, size*4, out)
429     #   var slice: slice = {s->data, 0}
430     #   curr = lookup(out)->data
431     #   while true
432     #     if (slice->start >= end) break
433     #     slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
434     #     if (slice->start >= end) break
435     #     slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
436     #     *curr = parse-hex-int-from-slice(slice)
437     #     curr += 4
438     #     slice->start = slice->end
439     #   return result
440     #
441     # . prologue
442     55/push-ebp
443     89/<- %ebp 4/r32/esp
444     # . save registers
445     50/push-eax
446     51/push-ecx
447     52/push-edx
448     53/push-ebx
449     56/push-esi
450     57/push-edi
451     # esi = s
452     8b/-> *(ebp+0xc) 6/r32/esi
453     # var curr/ecx: (addr byte) = s->data
454     8d/copy-address *(esi+4) 1/r32/ecx
455     # var end/edx: (addr byte) = &s->data[s->size]
456     # . edx = s->size
457     8b/-> *esi 2/r32/edx
458     # . edx += curr
459     01/add-to %edx 1/r32/ecx
460     # var size/ebx: int = 0
461     31/xor-with %ebx 3/r32/ebx
462 $_parse-array-of-decimal-ints:loop1:
463     # if (curr >= end) break
464     39/compare %ecx 2/r32/edx
465     73/jump-if-addr>= $_parse-array-of-decimal-ints:break1/disp8
466     # curr = skip-chars-matching-in-slice(curr, end, ' ')
467     (skip-chars-matching-in-slice %ecx %edx 0x20)  # => eax
468     89/<- %ecx 0/r32/eax
469     # if (curr >= end) break
470     39/compare %ecx 2/r32/edx
471     73/jump-if-addr>= $_parse-array-of-decimal-ints:break1/disp8
472     # curr = skip-chars-not-matching-in-slice(curr, end, ' ')
473     (skip-chars-not-matching-in-slice %ecx %edx 0x20)  # => eax
474     89/<- %ecx 0/r32/eax
475     # size += 4
476     81 0/subop/add %ebx 4/imm32
477     eb/jump $_parse-array-of-decimal-ints:loop1/disp8
478 $_parse-array-of-decimal-ints:break1:
479     (allocate-array *(ebp+8) %ebx *(ebp+0x10))
480 $_parse-array-of-decimal-ints:pass2:
481     # var slice/edi: slice = {s->data, 0}
482     68/push 0/imm32/end
483     8d/copy-address *(esi+4) 7/r32/edi
484     57/push-edi
485     89/<- %edi 4/r32/esp
486     # curr = lookup(out)->data
487     8b/-> *(ebp+0x10) 0/r32/eax
488     (lookup *eax *(eax+4))  # => eax
489     8d/copy-address *(eax+4) 1/r32/ecx
490 $_parse-array-of-decimal-ints:loop2:
491     # if (slice->start >= end) break
492     39/compare *edi 2/r32/edx
493     73/jump-if-addr>= $_parse-array-of-decimal-ints:end/disp8
494     # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
495     (skip-chars-matching-in-slice *edi %edx 0x20)  # => eax
496     89/<- *edi 0/r32/eax
497     # if (slice->start >= end) break
498     39/compare *edi 2/r32/edx
499     73/jump-if-addr>= $_parse-array-of-decimal-ints:end/disp8
500     # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
501     (skip-chars-not-matching-in-slice *edi %edx 0x20)  # => eax
502     89/<- *(edi+4) 0/r32/eax
503     # *curr = parse-hex-int-from-slice(slice)
504     (parse-decimal-int-from-slice %edi)
505     89/<- *ecx 0/r32/eax
506     # curr += 4
507     81 0/subop/add %ecx 4/imm32
508     # slice->start = slice->end
509     8b/-> *(edi+4) 0/r32/eax
510     89/<- *edi 0/r32/eax
511     eb/jump $_parse-array-of-decimal-ints:loop2/disp8
512 $_parse-array-of-decimal-ints:end:
513     # . reclaim locals
514     81 0/subop/add %esp 8/imm32
515     # . restore registers
516     5f/pop-to-edi
517     5e/pop-to-esi
518     5b/pop-to-ebx
519     5a/pop-to-edx
520     59/pop-to-ecx
521     58/pop-to-eax
522     # . epilogue
523     89/<- %esp 5/r32/ebp
524     5d/pop-to-ebp
525     c3/return
526 
527 test-parse-array-of-decimal-ints:
528     # . prologue
529     55/push-ebp
530     89/<- %ebp 4/r32/esp
531     # var h/esi: (handle array int)
532     68/push 0/imm32
533     68/push 0/imm32
534     89/<- %esi 4/r32/esp
535     # var ecx: (array int) = [1, 2, 3]
536     68/push 3/imm32
537     68/push 2/imm32
538     68/push 1/imm32
539     68/push 0xc/imm32/size
540     89/<- %ecx 4/r32/esp
541     #
542     (_parse-array-of-decimal-ints Heap "1 2 3" %esi)
543     (lookup *esi *(esi+4))  # => eax
544     (array-equal? %ecx %eax)  # => eax
545     (check-ints-equal %eax 1 "F - test-parse-array-of-decimal-ints")
546     # . epilogue
547     89/<- %esp 5/r32/ebp
548     5d/pop-to-ebp
549     c3/return
550 
551 test-parse-array-of-decimal-ints-empty:
552     # - empty string = empty array
553     # . prologue
554     55/push-ebp
555     89/<- %ebp 4/r32/esp
556     # var h/esi: handle
557     68/push 0/imm32
558     68/push 0/imm32
559     89/<- %esi 4/r32/esp
560     #
561     (_parse-array-of-decimal-ints Heap "" %esi)
562     (lookup *esi *(esi+4))  # => eax
563     (check-ints-equal *eax 0 "F - test-parse-array-of-decimal-ints-empty")
564     # . epilogue
565     89/<- %esp 5/r32/ebp
566     5d/pop-to-ebp
567     c3/return
568 
569 test-parse-array-of-decimal-ints-just-whitespace:
570     # - just whitespace = empty array
571     # . prologue
572     55/push-ebp
573     89/<- %ebp 4/r32/esp
574     # var h/esi: handle
575     68/push 0/imm32
576     68/push 0/imm32
577     89/<- %esi 4/r32/esp
578     #
579     (_parse-array-of-decimal-ints Heap Space %esi)
580     (lookup *esi *(esi+4))  # => eax
581     (check-ints-equal *eax 0 "F - test-parse-array-of-decimal-ints-just-whitespace")
582     # . epilogue
583     89/<- %esp 5/r32/ebp
584     5d/pop-to-ebp
585     c3/return
586 
587 test-parse-array-of-decimal-ints-extra-whitespace:
588     # . prologue
589     55/push-ebp
590     89/<- %ebp 4/r32/esp
591     # var h/esi: handle
592     68/push 0/imm32
593     68/push 0/imm32
594     89/<- %esi 4/r32/esp
595     # var ecx: (array int) = [1, 2, 3]
596     68/push 3/imm32
597     68/push 2/imm32
598     68/push 1/imm32
599     68/push 0xc/imm32/size
600     89/<- %ecx 4/r32/esp
601     #
602     (_parse-array-of-decimal-ints Heap " 1 2  3  " %esi)
603     (lookup *esi *(esi+4))  # => eax
604     (array-equal? %ecx %eax)  # => eax
605     (check-ints-equal %eax 1 "F - test-parse-array-of-decimal-ints-extra-whitespace")
606     # . epilogue
607     89/<- %esp 5/r32/ebp
608     5d/pop-to-ebp
609     c3/return
610 
611 parse-array-of-decimal-ints:  # s: (addr array byte), out: (addr handle array int)
612     # . prologue
613     55/push-ebp
614     89/<- %ebp 4/r32/esp
615     #
616     (_parse-array-of-decimal-ints Heap *(ebp+8) *(ebp+0xc))
617 $parse-array-of-decimal-ints:end:
618     # . epilogue
619     89/<- %esp 5/r32/ebp
620     5d/pop-to-ebp
621     c3/return
622