about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-05-10 16:45:22 -0700
committerKartik Agaram <vc@akkartik.com>2019-05-10 16:45:22 -0700
commitc88b9e31259d433c7e63fcb19d430daf9b1c723c (patch)
tree12de1a20312a788d07ef6564084b7ca81aa15ba1
parentcdfb2dbfcf18cd2f23bf74199082392c3692a599 (diff)
downloadmu-c88b9e31259d433c7e63fcb19d430daf9b1c723c.tar.gz
5151 - use mmap everywhere we need a heap
All tests passing now. Things are very explicit; before a program can `allocate`
memory, it has to first obtain a segment from the OS using `new-segment`.
-rw-r--r--subx/012elf.cc17
-rw-r--r--subx/020syscalls.cc21
-rw-r--r--subx/070new-stream.subx15
-rw-r--r--subx/072slice.subx16
-rwxr-xr-xsubx/apps/assortbin21414 -> 21492 bytes
-rw-r--r--subx/apps/assort.subx24
-rwxr-xr-xsubx/apps/crenshaw2-1bin18602 -> 18651 bytes
-rwxr-xr-xsubx/apps/crenshaw2-1bbin19161 -> 19210 bytes
-rw-r--r--subx/apps/dquotesbin20916 -> 21106 bytes
-rw-r--r--subx/apps/dquotes.subx24
-rwxr-xr-xsubx/apps/factorialbin17518 -> 17567 bytes
-rwxr-xr-xsubx/apps/handlebin18292 -> 18394 bytes
-rw-r--r--subx/apps/handle.subx74
-rwxr-xr-xsubx/apps/hexbin21611 -> 21660 bytes
-rwxr-xr-xsubx/apps/packbin36203 -> 36252 bytes
15 files changed, 146 insertions, 45 deletions
diff --git a/subx/012elf.cc b/subx/012elf.cc
index 11e0e4bc..d0a3fbd2 100644
--- a/subx/012elf.cc
+++ b/subx/012elf.cc
@@ -134,13 +134,20 @@ void load_segment_from_program_header(uint8_t* elf_contents, int segment_index,
 //   code:  0x09000000 -> 0x09ffffff (specified in ELF binary)
 //   data:  0x0a000000 -> 0x0affffff (specified in ELF binary)
 //   --- heap gets mmap'd somewhere here ---
-//   stack: 0xbdffffff -> 0xbd000000 (downward; not in ELF binary)
-//   argv hack: 0xbf000000 -> 0xc0000000 (not in ELF binary)
+//   stack: 0x7dffffff -> 0x7d000000 (downward; not in ELF binary)
+//   argv hack: 0x7f000000 -> 0x7fffffff (not in ELF binary)
+//
+// For now we avoid addresses with the most significant bit set; SubX doesn't
+// support unsigned comparison yet (https://github.com/akkartik/mu/issues/30)
+// Once we do, we can go up to 0xc0000000; higher addresses are reserved for
+// the Linux kernel.
 const int CODE_SEGMENT      = 0x09000000;
 const int DATA_SEGMENT =      0x0a000000;
-const int STACK_SEGMENT     = 0xbd000000;
-const int AFTER_STACK       = 0xbe000000;
-const int ARGV_DATA_SEGMENT = 0xbf000000;
+const int STACK_SEGMENT     = 0x7d000000;
+const int AFTER_STACK       = 0x7e000000;
+const int ARGV_DATA_SEGMENT = 0x7f000000;
+// When updating the above memory map, don't forget to update `mmap`'s
+// implementation in the 'syscalls' layer.
 :(before "End Dump Info for Instruction")
 //? dump_stack();  // slow
 :(code)
diff --git a/subx/020syscalls.cc b/subx/020syscalls.cc
index a17e9525..6b9faa2c 100644
--- a/subx/020syscalls.cc
+++ b/subx/020syscalls.cc
@@ -24,14 +24,14 @@ void process_int80() {
     trace(Callstack_depth+1, "run") << "read: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
     Reg[EAX].i = read(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
-    if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
+    if (Reg[EAX].i == -1) raise << "read: " << strerror(errno) << '\n' << end();
     break;
   case 4:
     trace(Callstack_depth+1, "run") << "write: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end();
     Reg[EAX].i = write(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
-    if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
+    if (Reg[EAX].i == -1) raise << "write: " << strerror(errno) << '\n' << end();
     break;
   case 5: {
     check_flags(ECX);
@@ -40,14 +40,14 @@ void process_int80() {
     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
     Reg[EAX].i = open(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*flags*/Reg[ECX].u, /*mode*/0640);
     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
-    if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
+    if (Reg[EAX].i == -1) raise << "open: " << strerror(errno) << '\n' << end();
     break;
   }
   case 6:
     trace(Callstack_depth+1, "run") << "close: " << Reg[EBX].u << end();
     Reg[EAX].i = close(/*file descriptor*/Reg[EBX].u);
     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
-    if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
+    if (Reg[EAX].i == -1) raise << "close: " << strerror(errno) << '\n' << end();
     break;
   case 8:
     check_mode(ECX);
@@ -55,14 +55,14 @@ void process_int80() {
     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
     Reg[EAX].i = creat(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*mode*/0640);
     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
-    if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
+    if (Reg[EAX].i == -1) raise << "creat: " << strerror(errno) << '\n' << end();
     break;
   case 10:
     trace(Callstack_depth+1, "run") << "unlink: " << Reg[EBX].u << end();
     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
     Reg[EAX].i = unlink(/*filename*/mem_addr_kernel_string(Reg[EBX].u));
     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
-    if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
+    if (Reg[EAX].i == -1) raise << "unlink: " << strerror(errno) << '\n' << end();
     break;
   case 38:
     trace(Callstack_depth+1, "run") << "rename: " << Reg[EBX].u << " -> " << Reg[ECX].u << end();
@@ -70,7 +70,7 @@ void process_int80() {
     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end();
     Reg[EAX].i = rename(/*old filename*/mem_addr_kernel_string(Reg[EBX].u), /*new filename*/mem_addr_kernel_string(Reg[ECX].u));
     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
-    if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
+    if (Reg[EAX].i == -1) raise << "rename: " << strerror(errno) << '\n' << end();
     break;
   case 45:  // brk: modify size of data segment
     trace(Callstack_depth+1, "run") << "grow data segment to " << Reg[EBX].u << end();
@@ -110,7 +110,12 @@ void check_mode(int reg) {
 }
 
 :(before "End Globals")
-uint32_t Next_segment = 0xb0000000;  // 0xc0000000 and up is reserved for Linux kernel
+// Very primitive/fixed/insecure mmap segments for now.
+// For now we avoid addresses with the most significant bit set; SubX doesn't
+// support unsigned comparison yet (https://github.com/akkartik/mu/issues/30)
+// Once we do, we can go up to 0xc0000000; higher addresses are reserved for
+// the Linux kernel.
+uint32_t Next_segment = 0x7c000000;
 const uint32_t SPACE_FOR_SEGMENT = 0x01000000;
 :(code)
 uint32_t new_segment(uint32_t length) {
diff --git a/subx/070new-stream.subx b/subx/070new-stream.subx
index ad6ab68d..8a833581 100644
--- a/subx/070new-stream.subx
+++ b/subx/070new-stream.subx
@@ -68,20 +68,21 @@ test-new-stream:
     # . prolog
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-    # var ad/ECX : (address allocation-descriptor) = allocate-region(Heap, 512)
-    # . EAX = allocate-region(Heap, 512)
+    # var heap/ECX : (address allocation-descriptor) = {0, 0}
+    68/push  0/imm32/limit
+    68/push  0/imm32/curr
+    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+    # heap = new-segment(512)
     # . . push args
+    51/push-ECX
     68/push  0x200/imm32
-    68/push  Heap/imm32
     # . . call
-    e8/call  allocate-region/disp32
+    e8/call  new-segment/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . ECX = EAX
-    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
     # var start/EDX = ad->curr
     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
-    # EAX = new-stream(ad, 3, 2)
+    # EAX = new-stream(heap, 3, 2)
     # . . push args
     68/push  2/imm32
     68/push  3/imm32
diff --git a/subx/072slice.subx b/subx/072slice.subx
index 9c285097..683c4c7b 100644
--- a/subx/072slice.subx
+++ b/subx/072slice.subx
@@ -917,14 +917,26 @@ test-slice-to-string:
     # . prolog
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # var heap/EDX : (address allocation-descriptor) = {0, 0}
+    68/push  0/imm32/limit
+    68/push  0/imm32/curr
+    89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+    # heap = new-segment(512)
+    # . . push args
+    52/push-EDX
+    68/push  0x200/imm32
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # var slice/ECX = "Abc"
     68/push  _test-slice-data-3/imm32/end
     68/push  _test-slice-data-0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-    # EAX = slice-to-string(Heap, slice)
+    # EAX = slice-to-string(heap, slice)
     # . . push args
     51/push-ECX
-    68/push  Heap/imm32
+    52/push-EDX
     # . . call
     e8/call  slice-to-string/disp32
     # . . discard args
diff --git a/subx/apps/assort b/subx/apps/assort
index a0e89c63..e331ce8a 100755
--- a/subx/apps/assort
+++ b/subx/apps/assort
Binary files differdiff --git a/subx/apps/assort.subx b/subx/apps/assort.subx
index ecb1c213..cfdef5e1 100644
--- a/subx/apps/assort.subx
+++ b/subx/apps/assort.subx
@@ -26,12 +26,30 @@
 Entry:  # run tests if necessary, convert stdin if not
 
     # for debugging: run a single test
+#?     # . Heap = new-segment(4096)
+#?     # . . push args
+#?     68/push  Heap/imm32
+#?     68/push  0x1000/imm32
+#?     # . . call
+#?     e8/call  new-segment/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+#?     # . test()
 #?     e8/call test-convert/disp32
 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 #?     eb/jump  $main:end/disp8
 
     # . prolog
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # initialize heap
+    # . Heap = new-segment(4096)
+    # . . push args
+    68/push  Heap/imm32
+    68/push  0x1000/imm32
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # - if argc > 1 and argv[1] == "test", then return run_tests()
     # . argc > 1
     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
@@ -1316,4 +1334,10 @@ Segment-size:
   0x100/imm32
 #?   0x1000/imm32/4KB
 
+Heap:
+  # curr
+  0/imm32
+  # limit
+  0/imm32
+
 # . . vim:nowrap:textwidth=0
diff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1
index 9e07eb8d..eb187bb3 100755
--- a/subx/apps/crenshaw2-1
+++ b/subx/apps/crenshaw2-1
Binary files differdiff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b
index 173ea621..e8d72937 100755
--- a/subx/apps/crenshaw2-1b
+++ b/subx/apps/crenshaw2-1b
Binary files differdiff --git a/subx/apps/dquotes b/subx/apps/dquotes
index e1d12550..b63744d1 100644
--- a/subx/apps/dquotes
+++ b/subx/apps/dquotes
Binary files differdiff --git a/subx/apps/dquotes.subx b/subx/apps/dquotes.subx
index b236ac2f..fcac2698 100644
--- a/subx/apps/dquotes.subx
+++ b/subx/apps/dquotes.subx
@@ -22,12 +22,30 @@
 Entry:  # run tests if necessary, convert stdin if not
 
     # for debugging: run a single test
+#?     # . Heap = new-segment(4096)
+#?     # . . push args
+#?     68/push  Heap/imm32
+#?     68/push  0x1000/imm32
+#?     # . . call
+#?     e8/call  new-segment/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+#?     # . test()
 #?     e8/call test-next-word-returns-string-with-escapes/disp32
 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 #?     eb/jump  $main:end/disp8
 
     # . prolog
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # initialize heap
+    # . Heap = new-segment(4096)
+    # . . push args
+    68/push  Heap/imm32
+    68/push  0x1000/imm32
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # - if argc > 1 and argv[1] == "test", then return run_tests()
     # . argc > 1
     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
@@ -1054,4 +1072,10 @@ Segment-size:
 Next-string-literal:  # tracks the next auto-generated variable name
   1/imm32
 
+Heap:
+  # curr
+  0/imm32
+  # limit
+  0/imm32
+
 # . . vim:nowrap:textwidth=0
diff --git a/subx/apps/factorial b/subx/apps/factorial
index 8313b27a..2c47ab8a 100755
--- a/subx/apps/factorial
+++ b/subx/apps/factorial
Binary files differdiff --git a/subx/apps/handle b/subx/apps/handle
index fa345498..274677fe 100755
--- a/subx/apps/handle
+++ b/subx/apps/handle
Binary files differdiff --git a/subx/apps/handle.subx b/subx/apps/handle.subx
index f866ea59..0ed12067 100644
--- a/subx/apps/handle.subx
+++ b/subx/apps/handle.subx
@@ -79,17 +79,29 @@ test-new:
     # . prolog
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # var heap/EDX : (address allocation-descriptor) = {0, 0}
+    68/push  0/imm32/limit
+    68/push  0/imm32/curr
+    89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+    # heap = new-segment(512)
+    # . . push args
+    52/push-EDX
+    68/push  0x200/imm32
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # *Next-alloc-id = 0x34
     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     Next-alloc-id/disp32  0x34/imm32        # copy to *Next-alloc-id
     # var handle/ECX = {0, 0}
     68/push  0/imm32/address
     68/push  0/imm32/alloc-id
     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-    # new(Heap, 2, handle/ECX)
+    # new(heap, 2, handle/ECX)
     # . . push args
     51/push-ECX
     68/push  2/imm32/size
-    68/push  Heap/imm32
+    52/push-EDX
     # . . call
     e8/call  new/disp32
     # . . discard args
@@ -234,17 +246,29 @@ test-lookup-success:
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     # . save registers
+    # var heap/EBX : (address allocation-descriptor) = {0, 0}
+    68/push  0/imm32/limit
+    68/push  0/imm32/curr
+    89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
+    # heap = new-segment(512)
+    # . . push args
+    53/push-EBX
+    68/push  0x200/imm32
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # var handle/ECX = {0, 0}
     68/push  0/imm32/address
     68/push  0/imm32/alloc-id
     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-    # var old_top/EDX = Heap->curr
-    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           2/r32/EDX   Heap/disp32     .                 # copy *Heap to EDX
-    # new(Heap, 2, handle)
+    # var old_top/EDX = heap->curr
+    8b/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # copy *EBX to EDX
+    # new(heap, 2, handle)
     # . . push args
     51/push-ECX
     68/push  2/imm32/size
-    68/push  Heap/imm32
+    53/push-EBX
     # . . call
     e8/call  new/disp32
     # . . discard args
@@ -256,7 +280,7 @@ test-lookup-success:
     e8/call  lookup/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-    # EAX contains old top of Heap, except skipping the alloc id in the payload
+    # EAX contains old top of heap, except skipping the alloc id in the payload
     # . check-ints-equal(EAX, old_top+4, msg)
     # . . push args
     68/push  "F - test-lookup-success"/imm32
@@ -282,38 +306,46 @@ test-lookup-failure:
     # . prolog
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-    # . save registers
-    50/push-EAX
-    51/push-ECX
-    52/push-EDX
+    # var heap/ESI : (address allocation-descriptor) = {0, 0}
+    68/push  0/imm32/limit
+    68/push  0/imm32/curr
+    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+    # heap = new-segment(512)
+    # . . push args
+    56/push-ESI
+    68/push  0x200/imm32
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # var h1/ECX = {0, 0}
     68/push  0/imm32/address
     68/push  0/imm32/alloc-id
     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-    # var old_top/EBX = Heap->curr
-    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Heap/disp32     .                 # copy *Heap to EBX
+    # var old_top/EBX = heap->curr
+    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
     # first allocation, to h1
-    # . new(Heap, 2, h1)
+    # . new(heap, 2, h1)
     # . . push args
     51/push-ECX
     68/push  2/imm32/size
-    68/push  Heap/imm32
+    56/push-ESI
     # . . call
     e8/call  new/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # reset Heap->curr to mimic reclamation
-    89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Heap/disp32     .                 # copy EBX to *Heap
+    # reset heap->curr to mimic reclamation
+    89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy EBX to *ESI
     # second allocation that returns the same address as the first
     # var h2/EDX = {0, 0}
     68/push  0/imm32/address
     68/push  0/imm32/alloc-id
     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
-    # . new(Heap, 2, h2)
+    # . new(heap, 2, h2)
     # . . push args
     52/push-EDX
     68/push  2/imm32/size
-    68/push  Heap/imm32
+    56/push-ESI
     # . . call
     e8/call  new/disp32
     # . . discard args
@@ -338,10 +370,6 @@ test-lookup-failure:
     # clean up
     # . *Next-alloc-id = 1
     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     Next-alloc-id/disp32  1/imm32           # copy to *Next-alloc-id
-    # . restore registers
-    5a/pop-to-EDX
-    59/pop-to-ECX
-    58/pop-to-EAX
     # . epilog
     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     5d/pop-to-EBP
diff --git a/subx/apps/hex b/subx/apps/hex
index 8992e424..70734450 100755
--- a/subx/apps/hex
+++ b/subx/apps/hex
Binary files differdiff --git a/subx/apps/pack b/subx/apps/pack
index 1da1a476..7e8cc761 100755
--- a/subx/apps/pack
+++ b/subx/apps/pack
Binary files differ