about summary refs log tree commit diff stats
path: root/085array-equal.subx
diff options
context:
space:
mode:
Diffstat (limited to '085array-equal.subx')
-rw-r--r--085array-equal.subx603
1 files changed, 603 insertions, 0 deletions
diff --git a/085array-equal.subx b/085array-equal.subx
new file mode 100644
index 00000000..d6dec878
--- /dev/null
+++ b/085array-equal.subx
@@ -0,0 +1,603 @@
+# Comparing arrays of numbers.
+
+== code
+#   instruction                     effective address                                                   register    displacement    immediate
+# . op          subop               mod             rm32          base        index         scale       r32
+# . 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
+
+array-equal?:  # a : (address array int), b : (address array int) -> eax : boolean
+    # pseudocode:
+    #   lena = a->length
+    #   if (lena != b->length) return false
+    #   i = 0
+    #   curra = a->data
+    #   currb = b->data
+    #   while i < lena
+    #     i1 = *curra
+    #     i2 = *currb
+    #     if (c1 != c2) return false
+    #     i+=4, curra+=4, currb+=4
+    #   return true
+    #
+    # registers:
+    #   i: ecx
+    #   lena: edx
+    #   curra: esi
+    #   currb: edi
+    #   i1: eax
+    #   i2: ebx
+    #
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = a
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+    # edi = b
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
+    # lena/edx = a->length
+    8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
+$array-equal?:lengths:
+    # if (lena != b->length) return false
+    39/compare                      0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare *edi and edx
+    75/jump-if-not-equal  $array-equal?:false/disp8
+    # curra/esi = a->data
+    81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
+    # currb/edi = b->data
+    81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
+    # i/ecx = i1/eax = i2/ebx = 0
+    31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
+$array-equal?:loop:
+    # if (i >= lena) return true
+    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
+    7d/jump-if-greater-or-equal  $array-equal?:true/disp8
+    # i1 = *curra
+    8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
+    # i2 = *currb
+    8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           3/r32/ebx   .               .                 # copy *edi to ebx
+    # if (i1 != i2) return false
+    39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
+    75/jump-if-not-equal  $array-equal?:false/disp8
+    # i += 4
+    81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # add to ecx
+    # currs += 4
+    81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
+    # currb += 4
+    81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
+    eb/jump  $array-equal?:loop/disp8
+$array-equal?:true:
+    b8/copy-to-eax  1/imm32
+    eb/jump  $array-equal?:end/disp8
+$array-equal?:false:
+    b8/copy-to-eax  0/imm32
+$array-equal?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-empty-with-empty-array:
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # var ecx = []
+    68/push  0/imm32/size
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+    # var edx = []
+    68/push  0/imm32/size
+    89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
+    # eax = array-equal?(ecx, edx)
+    # . . push args
+    52/push-edx
+    51/push-ecx
+    # . . call
+    e8/call  array-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(eax, 1, msg)
+    # . . push args
+    68/push  "F - test-compare-empty-with-empty-array"/imm32
+    68/push  1/imm32/true
+    50/push-eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-empty-with-non-empty-array:  # also checks length-mismatch code path
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # var ecx = [1]
+    68/push  1/imm32
+    68/push  4/imm32/size
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+    # var edx = []
+    68/push  0/imm32/size
+    89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
+    # eax = array-equal?(ecx, edx)
+    # . . push args
+    52/push-edx
+    51/push-ecx
+    # . . call
+    e8/call  array-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(eax, 0, msg)
+    # . . push args
+    68/push  "F - test-compare-empty-with-non-empty-array"/imm32
+    68/push  0/imm32/false
+    50/push-eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-equal-arrays:
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # var ecx = [1, 2, 3]
+    68/push  3/imm32
+    68/push  2/imm32
+    68/push  1/imm32
+    68/push  0xc/imm32/size
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+    # var edx = [1, 2, 3]
+    68/push  3/imm32
+    68/push  2/imm32
+    68/push  1/imm32
+    68/push  0xc/imm32/size
+    89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
+    # eax = array-equal?(ecx, edx)
+    # . . push args
+    52/push-edx
+    51/push-ecx
+    # . . call
+    e8/call  array-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(eax, 1, msg)
+    # . . push args
+    68/push  "F - test-compare-equal-arrays"/imm32
+    68/push  1/imm32/true
+    50/push-eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-inequal-arrays-equal-lengths:
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # var ecx = [1, 4, 3]
+    68/push  3/imm32
+    68/push  4/imm32
+    68/push  1/imm32
+    68/push  0xc/imm32/size
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+    # var edx = [1, 2, 3]
+    68/push  3/imm32
+    68/push  2/imm32
+    68/push  1/imm32
+    68/push  0xc/imm32/size
+    89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
+    # eax = array-equal?(ecx, edx)
+    # . . push args
+    52/push-edx
+    51/push-ecx
+    # . . call
+    e8/call  array-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(eax, 0, msg)
+    # . . push args
+    68/push  "F - test-compare-inequal-arrays-equal-lengths"/imm32
+    68/push  0/imm32/false
+    50/push-eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+parse-array-of-ints:  # ad : (address allocation-descriptor), s : (address string) -> result/eax : (address array int)
+    # pseudocode
+    #   end = s->data + s->length
+    #   curr = s->data
+    #   size = 0
+    #   while true
+    #     if (curr >= end) break
+    #     curr = skip-chars-matching-in-slice(curr, end, ' ')
+    #     if (curr >= end) break
+    #     curr = skip-chars-not-matching-in-slice(curr, end, ' ')
+    #     ++size
+    #   result = allocate(ad, (size+1)*4)
+    #   result->size = (size+1)*4
+    #   var slice = {s->data, 0}
+    #   out = result->data
+    #   while true
+    #     if (slice->start >= end) break
+    #     slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
+    #     if (slice->start >= end) break
+    #     slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
+    #     *out = parse-hex-int(slice)
+    #     out += 4
+    #     slice->start = slice->end
+    #   return result
+    #
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = s
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
+    # curr/ecx = s->data
+    8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy esi+4 to ecx
+    # end/edx = s->data + s->length
+    # . edx = s->length
+    8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
+    # . edx += curr
+    01/add                          3/mod/direct    2/rm32/edx    .           .             .           1/r32/ecx   .               .                 # add ecx to edx
+    # size/ebx = 0
+    31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
+$parse-array-of-ints:loop1:
+    # if (curr >= end) break
+    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
+    73/jump-if-greater-or-equal-unsigned  $parse-array-of-ints:break1/disp8
+    # curr = skip-chars-matching-in-slice(curr, end, ' ')
+    # . eax = skip-chars-matching-in-slice(curr, end, ' ')
+    # . . push args
+    68/push  0x20/imm32/space
+    52/push-edx
+    51/push-ecx
+    # . . call
+    e8/call  skip-chars-matching-in-slice/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . ecx = eax
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
+    # if (curr >= end) break
+    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
+    73/jump-if-greater-or-equal-unsigned  $parse-array-of-ints:break1/disp8
+    # curr = skip-chars-not-matching-in-slice(curr, end, ' ')
+    # . eax = skip-chars-not-matching-in-slice(curr, end, ' ')
+    # . . push args
+    68/push  0x20/imm32/space
+    52/push-edx
+    51/push-ecx
+    # . . call
+    e8/call  skip-chars-not-matching-in-slice/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . ecx = eax
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
+    # size += 4
+    81          0/subop/add         3/mod/direct    3/rm32/ebx    .           .             .           .           .               4/imm32           # add to ebx
+    eb/jump  $parse-array-of-ints:loop1/disp8
+$parse-array-of-ints:break1:
+    # result/edi = allocate(ad, size+4)
+    # . eax = allocate(ad, size+4)
+    # . . push args
+    89/copy                         3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to eax
+    05/add-to-eax  4/imm32
+    50/push-eax
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+    # . . call
+    e8/call  allocate/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . edi = eax
+    89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
+    # result->size = size
+    89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to *eax
+$parse-array-of-ints:pass2:
+    # var slice/ecx = {s->data, 0}
+    # . push 0
+    68/push  0/imm32/end
+    # . push s->data
+    8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy esi+4 to ecx
+    51/push-ecx
+    # . bookmark
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+    # out/ebx = result->data
+    8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   4/disp8         .                 # copy eax+4 to ebx
+$parse-array-of-ints:loop2:
+    # if (slice->start >= end) break
+    39/compare                      0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare *ecx with edx
+    73/jump-if-greater-or-equal-unsigned  $parse-array-of-ints:end/disp8
+    # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
+    # . eax = skip-chars-matching-in-slice(slice->start, end, ' ')
+    # . . push args
+    68/push  0x20/imm32/space
+    52/push-edx
+    ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
+    # . . call
+    e8/call  skip-chars-matching-in-slice/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . slice->start = eax
+    89/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to *ecx
+    # if (slice->start >= end) break
+    39/compare                      0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare *ecx with edx
+    73/jump-if-greater-or-equal-unsigned  $parse-array-of-ints:end/disp8
+    # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
+    # . eax = skip-chars-not-matching-in-slice(curr, end, ' ')
+    # . . push args
+    68/push  0x20/imm32/space
+    52/push-edx
+    50/push-eax
+    # . . call
+    e8/call  skip-chars-not-matching-in-slice/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . slice->end = eax
+    89/copy                         1/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(ecx+4)
+    # *out = parse-hex-int(slice)
+    # . eax = parse-hex-int(slice)
+    # . . push args
+    51/push-ecx
+    # . . call
+    e8/call  parse-hex-int/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # *out = eax
+    89/copy                         0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to *ebx
+    # out += 4
+    81          0/subop/add         3/mod/direct    3/rm32/ebx    .           .             .           .           .               4/imm32           # add to ebx
+    # slice->start = slice->end
+    8b/copy                         1/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
+    89/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to *ecx
+    81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # add to ecx
+    eb/jump  $parse-array-of-ints:loop2/disp8
+$parse-array-of-ints:end:
+    # return edi
+    89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
+    # . reclaim locals
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-array-of-ints:
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # var ecx = [1, 2, 3]
+    68/push  3/imm32
+    68/push  2/imm32
+    68/push  1/imm32
+    68/push  0xc/imm32/size
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+    # eax = parse-array-of-ints(Heap, "1 2 3")
+    # . . push args
+    68/push  "1 2 3"/imm32
+    68/push  Heap/imm32
+    # . . call
+    e8/call  parse-array-of-ints/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # eax = array-equal?(ecx, eax)
+    # . . push args
+    50/push-eax
+    51/push-ecx
+    # . . call
+    e8/call  array-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(eax, 1, msg)
+    # . . push args
+    68/push  "F - test-parse-array-of-ints"/imm32
+    68/push  1/imm32/true
+    50/push-eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-array-of-ints-empty:
+    # - empty string = empty array
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # eax = parse-array-of-ints(Heap, "")
+    # . . push args
+    68/push  ""/imm32
+    68/push  Heap/imm32
+    # . . call
+    e8/call  parse-array-of-ints/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(*eax, 0, msg)
+    # . . push args
+    68/push  "F - test-parse-array-of-ints-empty"/imm32
+    68/push  0/imm32/size
+    ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-array-of-ints-just-whitespace:
+    # - just whitespace = empty array
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # eax = parse-array-of-ints(Heap, " ")
+    # . . push args
+    68/push  Space/imm32
+    68/push  Heap/imm32
+    # . . call
+    e8/call  parse-array-of-ints/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(*eax, 0, msg)
+    # . . push args
+    68/push  "F - test-parse-array-of-ints-empty"/imm32
+    68/push  0/imm32/size
+    ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-array-of-ints-extra-whitespace:
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # var ecx = [1, 2, 3]
+    68/push  3/imm32
+    68/push  2/imm32
+    68/push  1/imm32
+    68/push  0xc/imm32/size
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+    # eax = parse-array-of-ints(Heap, " 1 2  3  ")
+    # . . push args
+    68/push  " 1 2  3  "/imm32
+    68/push  Heap/imm32
+    # . . call
+    e8/call  parse-array-of-ints/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # eax = array-equal?(ecx, eax)
+    # . . push args
+    50/push-eax
+    51/push-ecx
+    # . . call
+    e8/call  array-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(eax, 1, msg)
+    # . . push args
+    68/push  "F - test-parse-array-of-ints-extra-whitespace"/imm32
+    68/push  1/imm32/true
+    50/push-eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+# helper for later tests
+# compare an array with a string representation of an array literal
+check-array-equal:  # a : (address array int), expected : (address string), msg : (address string)
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # . save registers
+    50/push-eax
+    # var b/ecx = parse-array-of-ints(Heap, expected)
+    # . eax = parse-array-of-ints(Heap, expected)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    68/push  Heap/imm32
+    # . . call
+    e8/call  parse-array-of-ints/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . b = eax
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
+    # eax = array-equal?(a, b)
+    # . . push args
+    51/push-ecx
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+    # . . call
+    e8/call  array-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-ints-equal(eax, 1, msg)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
+    68/push  1/imm32
+    50/push-eax
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+$check-array-equal:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+test-check-array-equal:
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # var ecx = [1, 2, 3]
+    68/push  3/imm32
+    68/push  2/imm32
+    68/push  1/imm32
+    68/push  0xc/imm32/size
+    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+    # check-array-equal(ecx, "1 2 3", "msg")
+    # . . push args
+    68/push  "F - test-check-array-equal"/imm32
+    68/push  "1 2 3"/imm32
+    51/push-ecx
+    # . . call
+    e8/call  check-array-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+# . . vim:nowrap:textwidth=0