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