about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-05-25 23:47:49 -0700
committerKartik Agaram <vc@akkartik.com>2019-05-25 23:47:49 -0700
commit561028444397d2722130ef3bbf4a403acf7964b4 (patch)
tree1917d966a501d5a24aa51ddf08c9d1a5c5dd2c65
parentbd31dbe85418b05a5f9e101525a829c48aa9bf37 (diff)
downloadmu-561028444397d2722130ef3bbf4a403acf7964b4.tar.gz
new primitive: parse-array-of-ints
Mostly for tests. For every new type we want to compare in a test, we're
now going to start using some primitive that can parse its value from string. In this manner we can get syntax for literals in machine code.

Open question: parsing aggregates of aggregates. Like an array of structs.

This is the first time we allocate from the heap in standard library tests.
So we now need to start initializing the heap in all our apps.
-rw-r--r--subx/073next-token.subx10
-rw-r--r--subx/075array-equal.subx308
-rwxr-xr-xsubx/apps/assortbin23253 -> 23949 bytes
-rwxr-xr-xsubx/apps/crenshaw2-1bin20430 -> 21147 bytes
-rw-r--r--subx/apps/crenshaw2-1.subx9
-rwxr-xr-xsubx/apps/crenshaw2-1bbin20989 -> 21706 bytes
-rw-r--r--subx/apps/crenshaw2-1b.subx9
-rwxr-xr-xsubx/apps/factorialbin19346 -> 20063 bytes
-rw-r--r--subx/apps/factorial.subx9
-rwxr-xr-xsubx/apps/handlebin20173 -> 20869 bytes
-rwxr-xr-xsubx/apps/hexbin23439 -> 24156 bytes
-rw-r--r--subx/apps/hex.subx9
-rwxr-xr-xsubx/apps/packbin38071 -> 38788 bytes
-rw-r--r--subx/apps/pack.subx9
14 files changed, 355 insertions, 8 deletions
diff --git a/subx/073next-token.subx b/subx/073next-token.subx
index 83abc556..b632e47f 100644
--- a/subx/073next-token.subx
+++ b/subx/073next-token.subx
@@ -726,7 +726,7 @@ test-skip-chars-matching-in-slice:
     05/add-to-EAX  4/imm32
     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
     # . . push args
-    68/push  0x20/imm32
+    68/push  0x20/imm32/space
     51/push-ECX
     50/push-EAX
     # . . call
@@ -755,7 +755,7 @@ test-skip-chars-matching-in-slice-none:
     05/add-to-EAX  4/imm32
     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
     # . . push args
-    68/push  0x20/imm32
+    68/push  0x20/imm32/space
     51/push-ECX
     50/push-EAX
     # . . call
@@ -822,7 +822,7 @@ test-skip-chars-not-matching-in-slice:
     05/add-to-EAX  4/imm32
     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
     # . . push args
-    68/push  0x20/imm32
+    68/push  0x20/imm32/space
     51/push-ECX
     50/push-EAX
     # . . call
@@ -851,7 +851,7 @@ test-skip-chars-not-matching-in-slice-none:
     05/add-to-EAX  4/imm32
     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
     # . . push args
-    68/push  0x20/imm32
+    68/push  0x20/imm32/space
     51/push-ECX
     50/push-EAX
     # . . call
@@ -880,7 +880,7 @@ test-skip-chars-not-matching-in-slice-all:
     05/add-to-EAX  4/imm32
     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
     # . . push args
-    68/push  0x20/imm32
+    68/push  0x20/imm32/space
     51/push-ECX
     50/push-EAX
     # . . call
diff --git a/subx/075array-equal.subx b/subx/075array-equal.subx
index 24f99b0c..e2b973f8 100644
--- a/subx/075array-equal.subx
+++ b/subx/075array-equal.subx
@@ -17,7 +17,7 @@ Entry:
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 
     # for debugging: run a single test
-#?     e8/call test-compare-inequal-arrays-equal-lengths/disp32
+#?     e8/call test-parse-array-of-ints-empty/disp32
 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 #?     eb/jump  $array-equal-main:end/disp8
 
@@ -110,8 +110,6 @@ $array-equal?:end:
     5d/pop-to-EBP
     c3/return
 
-# - tests
-
 test-compare-empty-with-empty-array:
     # . prolog
     55/push-EBP
@@ -253,6 +251,310 @@ test-compare-inequal-arrays-equal-lengths:
     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->curr >= end) break
+    #     slice->curr = skip-chars-matching-in-slice(slice->curr, end, ' ')
+    #     if (slice->curr >= end) break
+    #     slice->end = skip-chars-not-matching-in-slice(slice->curr, end, ' ')
+    #     *out = parse-hex-int(slice)
+    #     out += 4
+    #     slice->curr = 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
+    7d/jump-if-greater-or-equal  $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
+    7d/jump-if-greater-or-equal  $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->curr >= end) break
+    39/compare                      0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare *ECX with EDX
+    7d/jump-if-greater-or-equal  $parse-array-of-ints:end/disp8
+    # slice->curr = skip-chars-matching-in-slice(slice->curr, end, ' ')
+    # . EAX = skip-chars-matching-in-slice(slice->curr, 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->curr = EAX
+    89/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ECX
+    # if (slice->curr >= end) break
+    39/compare                      0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare *ECX with EDX
+    7d/jump-if-greater-or-equal  $parse-array-of-ints:end/disp8
+    # slice->end = skip-chars-not-matching-in-slice(slice->curr, 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->curr = 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  " "/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
+
 == data
 
 Heap:
diff --git a/subx/apps/assort b/subx/apps/assort
index 7a2c318f..dc280d0a 100755
--- a/subx/apps/assort
+++ b/subx/apps/assort
Binary files differdiff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1
index 64fee587..15856474 100755
--- a/subx/apps/crenshaw2-1
+++ b/subx/apps/crenshaw2-1
Binary files differdiff --git a/subx/apps/crenshaw2-1.subx b/subx/apps/crenshaw2-1.subx
index 2f1fec9e..5ec2e829 100644
--- a/subx/apps/crenshaw2-1.subx
+++ b/subx/apps/crenshaw2-1.subx
@@ -31,6 +31,15 @@
 # . 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
 
 Entry:  # run tests if necessary, call 'compile' if not
+    # initialize heap
+    # . Heap = new-segment(64KB)
+    # . . push args
+    68/push  Heap/imm32
+    68/push  0x10000/imm32/64KB
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 
 #?     # for debugging: run a single test; don't bother setting status code
 #?     e8/call test-get-num-aborts-on-non-digit-in-Look/disp32
diff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b
index 47dbafa0..ba29e49c 100755
--- a/subx/apps/crenshaw2-1b
+++ b/subx/apps/crenshaw2-1b
Binary files differdiff --git a/subx/apps/crenshaw2-1b.subx b/subx/apps/crenshaw2-1b.subx
index 8dee0dbc..4f350cf4 100644
--- a/subx/apps/crenshaw2-1b.subx
+++ b/subx/apps/crenshaw2-1b.subx
@@ -31,6 +31,15 @@
 # . 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
 
 Entry:  # run tests if necessary, call 'compile' if not
+    # initialize heap
+    # . Heap = new-segment(64KB)
+    # . . push args
+    68/push  Heap/imm32
+    68/push  0x10000/imm32/64KB
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 
 #?     # for debugging: run a single test; don't bother setting status code
 #?     e8/call test-get-num-reads-single-digit/disp32
diff --git a/subx/apps/factorial b/subx/apps/factorial
index 828f3064..02c3a2d4 100755
--- a/subx/apps/factorial
+++ b/subx/apps/factorial
Binary files differdiff --git a/subx/apps/factorial.subx b/subx/apps/factorial.subx
index 20b0a4fe..a9f88363 100644
--- a/subx/apps/factorial.subx
+++ b/subx/apps/factorial.subx
@@ -19,6 +19,15 @@
 # . 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
 
 Entry:  # run tests if necessary, compute `factorial(5)` if not
+    # initialize heap
+    # . Heap = new-segment(64KB)
+    # . . push args
+    68/push  Heap/imm32
+    68/push  0x10000/imm32/64KB
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 
 #?     # for debugging: run a single test; don't bother setting status code
 #?     e8/call test-get-num-reads-single-digit/disp32
diff --git a/subx/apps/handle b/subx/apps/handle
index dd8c77c6..ea47b0d4 100755
--- a/subx/apps/handle
+++ b/subx/apps/handle
Binary files differdiff --git a/subx/apps/hex b/subx/apps/hex
index eda84c07..b70b32c6 100755
--- a/subx/apps/hex
+++ b/subx/apps/hex
Binary files differdiff --git a/subx/apps/hex.subx b/subx/apps/hex.subx
index 3730351f..6b82a1e9 100644
--- a/subx/apps/hex.subx
+++ b/subx/apps/hex.subx
@@ -18,6 +18,15 @@
 # . 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
 
 Entry:  # run tests if necessary, convert stdin if not
+    # initialize heap
+    # . Heap = new-segment(64KB)
+    # . . push args
+    68/push  Heap/imm32
+    68/push  0x10000/imm32/64KB
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 
 #?     # for debugging: run a single test
 #?     e8/call test-convert-next-octet-aborts-on-single-hex-byte/disp32
diff --git a/subx/apps/pack b/subx/apps/pack
index 3bbfa65a..3c0c501e 100755
--- a/subx/apps/pack
+++ b/subx/apps/pack
Binary files differdiff --git a/subx/apps/pack.subx b/subx/apps/pack.subx
index c035c361..74c21e88 100644
--- a/subx/apps/pack.subx
+++ b/subx/apps/pack.subx
@@ -19,6 +19,15 @@
 # . 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
 
 Entry:  # run tests if necessary, convert stdin if not
+    # initialize heap
+    # . Heap = new-segment(64KB)
+    # . . push args
+    68/push  Heap/imm32
+    68/push  0x10000/imm32/64KB
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 
     # for debugging: run a single test
 #?     e8/call test-emit-non-number-with-all-hex-digits-and-metadata/disp32