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