summary refs log tree commit diff
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-05-22 05:32:50 -0700
committerIrene Knapp <ireneista@irenes.space>2026-05-22 05:34:11 -0700
commit02a165b1e5ce2960207976e0c27b568ab2d33f8d (patch)
tree008c533f786953c99d9a3e0700c4dd5430dd34b3
parent3f3bda4692b4017c7ad6ed1c434313b9dcc62cfb (diff)
de-branch'd all the internals
so it's all just high-level flow control now, which makes it load in itself

the output isn't identical on the first self-compilation, but it converges on the second

with this, it is officially self-hosting. WOW.

Force-Push: yes
Change-Id: Ic14de1dae209b200cada269b07c3826c86fa494f
-rw-r--r--amd64.e156
-rw-r--r--input.e60
-rw-r--r--output.e221
3 files changed, 205 insertions, 232 deletions
diff --git a/amd64.e b/amd64.e
index 4d3c734..8e53822 100644
--- a/amd64.e
+++ b/amd64.e
@@ -145,64 +145,62 @@ s" :cc-greater" keyword
 
 ~ (register -- 3-bit encoded value for register)
 : reg64
-  ~   In counting the words for the branches, notice that each integer literal
-  ~ is two words.
-  dup :rax = 0branch [ 5 8 * , ] drop 0 exit
-  dup :rcx = 0branch [ 5 8 * , ] drop 1 exit
-  dup :rdx = 0branch [ 5 8 * , ] drop 2 exit
-  dup :rbx = 0branch [ 5 8 * , ] drop 3 exit
-  dup :rsp = 0branch [ 5 8 * , ] drop 4 exit
-  dup :rbp = 0branch [ 5 8 * , ] drop 5 exit
-  dup :rsi = 0branch [ 5 8 * , ] drop 6 exit
-  dup :rdi = 0branch [ 5 8 * , ] drop 7 exit
+  dup :rax = { drop 0 exit } if
+  dup :rcx = { drop 1 exit } if
+  dup :rdx = { drop 2 exit } if
+  dup :rbx = { drop 3 exit } if
+  dup :rsp = { drop 4 exit } if
+  dup :rbp = { drop 5 exit } if
+  dup :rsi = { drop 6 exit } if
+  dup :rdi = { drop 7 exit } if
   ." Parameter to reg64 is not a reg64." 1 sys-exit ;
 
 ~ (register -- 3-bit encoded value for register)
 : extrareg64
-  dup :r8 = 0branch [ 5 8 * , ] drop 0 exit
-  dup :r9 = 0branch [ 5 8 * , ] drop 1 exit
-  dup :r10 = 0branch [ 5 8 * , ] drop 2 exit
-  dup :r11 = 0branch [ 5 8 * , ] drop 3 exit
-  dup :r12 = 0branch [ 5 8 * , ] drop 4 exit
-  dup :r13 = 0branch [ 5 8 * , ] drop 5 exit
-  dup :r14 = 0branch [ 5 8 * , ] drop 6 exit
-  dup :r15 = 0branch [ 5 8 * , ] drop 7 exit
+  dup :r8 = { drop 0 exit } if
+  dup :r9 = { drop 1 exit } if
+  dup :r10 = { drop 2 exit } if
+  dup :r11 = { drop 3 exit } if
+  dup :r12 = { drop 4 exit } if
+  dup :r13 = { drop 5 exit } if
+  dup :r14 = { drop 6 exit } if
+  dup :r15 = { drop 7 exit } if
   ." Parameter to extrareg64 is not an extrareg64." 1 sys-exit ;
 
 ~ (register -- 3-bit encoded value for register)
 : reg32
-  dup :eax = 0branch [ 5 8 * , ] drop 0 exit
-  dup :ecx = 0branch [ 5 8 * , ] drop 0 exit
-  dup :edx = 0branch [ 5 8 * , ] drop 0 exit
-  dup :ebx = 0branch [ 5 8 * , ] drop 0 exit
-  dup :esp = 0branch [ 5 8 * , ] drop 0 exit
-  dup :ebp = 0branch [ 5 8 * , ] drop 0 exit
-  dup :esi = 0branch [ 5 8 * , ] drop 0 exit
-  dup :edi = 0branch [ 5 8 * , ] drop 0 exit
+  dup :eax = { drop 0 exit } if
+  dup :ecx = { drop 0 exit } if
+  dup :edx = { drop 0 exit } if
+  dup :ebx = { drop 0 exit } if
+  dup :esp = { drop 0 exit } if
+  dup :ebp = { drop 0 exit } if
+  dup :esi = { drop 0 exit } if
+  dup :edi = { drop 0 exit } if
   ." Parameter to reg32 is not a reg32." 1 sys-exit ;
 
 ~ (register -- 3-bit encoded value for register)
 : reg16
-  dup :ax = 0branch [ 5 8 * , ] drop 0 exit
-  dup :cx = 0branch [ 5 8 * , ] drop 1 exit
-  dup :dx = 0branch [ 5 8 * , ] drop 2 exit
-  dup :bx = 0branch [ 5 8 * , ] drop 3 exit
-  dup :sp = 0branch [ 5 8 * , ] drop 4 exit
-  dup :bp = 0branch [ 5 8 * , ] drop 5 exit
-  dup :si = 0branch [ 5 8 * , ] drop 6 exit
-  dup :di = 0branch [ 5 8 * , ] drop 7 exit
+  dup :ax = { drop 0 exit } if
+  dup :cx = { drop 1 exit } if
+  dup :dx = { drop 2 exit } if
+  dup :bx = { drop 3 exit } if
+  dup :sp = { drop 4 exit } if
+  dup :bp = { drop 5 exit } if
+  dup :si = { drop 6 exit } if
+  dup :di = { drop 7 exit } if
   ." Parameter to reg16 is not a reg16." 1 sys-exit ;
 
 ~ (register -- 3-bit encoded value for register)
 : reg8
-  dup :al = 0branch [ 5 8 * , ] drop 0 exit
-  dup :cl = 0branch [ 5 8 * , ] drop 1 exit
-  dup :dl = 0branch [ 5 8 * , ] drop 2 exit
-  dup :bl = 0branch [ 5 8 * , ] drop 3 exit
-  dup :ah = 0branch [ 5 8 * , ] drop 4 exit
-  dup :ch = 0branch [ 5 8 * , ] drop 5 exit
-  dup :dh = 0branch [ 5 8 * , ] drop 6 exit
-  dup :bh = 0branch [ 5 8 * , ] drop 7 exit
+  dup :al = { drop 0 exit } if
+  dup :cl = { drop 1 exit } if
+  dup :dl = { drop 2 exit } if
+  dup :bl = { drop 3 exit } if
+  dup :ah = { drop 4 exit } if
+  dup :ch = { drop 5 exit } if
+  dup :dh = { drop 6 exit } if
+  dup :bh = { drop 7 exit } if
   ." Parameter to reg8 is not a reg8." 1 sys-exit ;
 
 
@@ -215,10 +213,10 @@ s" :cc-greater" keyword
 ~
 ~ (scale factor -- 2-bit encoded value)
 : scalefield
-  dup 1 = 0branch [ 5 8 * , ] drop 0 exit
-  dup 2 = 0branch [ 5 8 * , ] drop 1 exit
-  dup 5 = 0branch [ 5 8 * , ] drop 2 exit
-  dup 8 = 0branch [ 5 8 * , ] drop 3 exit
+  dup 1 = { drop 0 exit } if
+  dup 2 = { drop 1 exit } if
+  dup 5 = { drop 2 exit } if
+  dup 8 = { drop 3 exit } if
   ." Parameter to scalefield is not 1, 2, 4, or 8." 1 sys-exit ;
 
 
@@ -238,22 +236,22 @@ s" :cc-greater" keyword
 ~
 ~ (condition -- 4-bit encoded value)
 : condition-code
-  dup :cc-overflow = 0branch [ 5 8 * , ] drop 0 exit
-  dup :cc-no-overflow = 0branch [ 5 8 * , ] drop 1 exit
-  dup :cc-below = 0branch [ 5 8 * , ] drop 2 exit
-  dup :cc-above-equal = 0branch [ 5 8 * , ] drop 3 exit
-  dup :cc-equal = 0branch [ 5 8 * , ] drop 4 exit
-  dup :cc-not-equal = 0branch [ 5 8 * , ] drop 5 exit
-  dup :cc-below-equal = 0branch [ 5 8 * , ] drop 6 exit
-  dup :cc-above = 0branch [ 5 8 * , ] drop 7 exit
-  dup :cc-sign = 0branch [ 5 8 * , ] drop 8 exit
-  dup :cc-not-sign = 0branch [ 5 8 * , ] drop 9 exit
-  dup :cc-even = 0branch [ 5 8 * , ] drop 10 exit
-  dup :cc-odd = 0branch [ 5 8 * , ] drop 11 exit
-  dup :cc-less = 0branch [ 5 8 * , ] drop 12 exit
-  dup :cc-greater-equal = 0branch [ 5 8 * , ] drop 13 exit
-  dup :cc-less-equal = 0branch [ 5 8 * , ] drop 14 exit
-  dup :cc-greater = 0branch [ 5 8 * , ] drop 15 exit
+  dup :cc-overflow = { drop 0 exit } if
+  dup :cc-no-overflow = { drop 1 exit } if
+  dup :cc-below = { drop 2 exit } if
+  dup :cc-above-equal = { drop 3 exit } if
+  dup :cc-equal = { drop 4 exit } if
+  dup :cc-not-equal = { drop 5 exit } if
+  dup :cc-below-equal = { drop 6 exit } if
+  dup :cc-above = { drop 7 exit } if
+  dup :cc-sign = { drop 8 exit } if
+  dup :cc-not-sign = { drop 9 exit } if
+  dup :cc-even = { drop 10 exit } if
+  dup :cc-odd = { drop 11 exit } if
+  dup :cc-less = { drop 12 exit } if
+  dup :cc-greater-equal = { drop 13 exit } if
+  dup :cc-less-equal = { drop 14 exit } if
+  dup :cc-greater = { drop 15 exit } if
   ." Parameter to condition-code is not a condition code." 1 sys-exit ;
 
 
@@ -399,15 +397,16 @@ s" :cc-greater" keyword
 ~  -- output point)
 : addressing-indirect-reg64
   ~ Exit with an error if the R/M register is :rbp.
-  dup :rbp != 0branch [ 23 8 * , ]
-  ~ Check whether the R/M register is :rsp. Save the test result for later.
-  dup :rsp = 4 unroll
-  ~ (equality result, output point, reg/op value, reg/mem name)
-  reg64 0 3unroll modrm
-  ~ (equality result, output point)
-  ~ If the R/M register was rsp, we need an SIB byte; otherwise, skip it.
-  swap 0branch [ 8 8 * , ] 0 4 :rsp reg64 sib
-  exit
+  dup :rbp != {
+    ~ Check whether the R/M register is :rsp. Save the test result for later.
+    dup :rsp = 4 unroll
+    ~ (equality result, output point, reg/op value, reg/mem name)
+    reg64 0 3unroll modrm
+    ~ (equality result, output point)
+    ~ If the R/M register was rsp, we need an SIB byte; otherwise, skip it.
+    swap { 0 4 :rsp reg64 sib } if
+    exit
+  } if
   ." R/M parameter to addressing-indirect-reg64 is :rbp." 1 sys-exit ;
 
 ~ (output point, reg/op field value, reg/mem field register,
@@ -420,7 +419,7 @@ s" :cc-greater" keyword
   4 unroll
   reg64 1 3unroll modrm
   ~ If the R/M register was rsp, we need an SIB byte; otherwise, skip it.
-  3roll 0branch [ 8 8 * , ] 0 4 :rsp reg64 sib
+  3roll { 0 4 :rsp reg64 sib } if
   ~ The displacement byte.
   swap pack8 ;
 
@@ -434,7 +433,7 @@ s" :cc-greater" keyword
   4 unroll
   reg64 2 3unroll modrm
   ~ If the R/M register was rsp, we need an SIB byte; otherwise, skip it.
-  3roll 0branch [ 8 8 * , ] 0 4 :rsp reg64 sib
+  3roll { 0 4 :rsp reg64 sib } if
   ~ The displacement value.
   swap pack32 ;
 
@@ -443,11 +442,12 @@ s" :cc-greater" keyword
 ~  -- output point)
 : addressing-indexed-reg64
   ~ Exit with an error if the base register is :rbp.
-  dup :rbp != 0branch [ 23 8 * , ]
-  ~ Reg/mem value 4 means to use an SIB byte (at least, with this mode).
-  5 roll 0 6 roll 4 modrm 4 unroll
-  reg64 3unroll reg64 3unroll scalefield 3unroll sib
-  exit
+  dup :rbp != {
+    ~ Reg/mem value 4 means to use an SIB byte (at least, with this mode).
+    5 roll 0 6 roll 4 modrm 4 unroll
+    reg64 3unroll reg64 3unroll scalefield 3unroll sib
+    exit
+  } if
   ." Base parameter to addressing-indexed-reg64 is :rbp." 1 sys-exit ;
 
 ~ (output point, reg/op field value,
diff --git a/input.e b/input.e
index 8203673..dd1da1c 100644
--- a/input.e
+++ b/input.e
@@ -97,7 +97,7 @@
 ~ (metadata pointer --)
 : consume-from
   ~ If the length is zero, exit without doing anything.
-  dup buffer-logical-length @ 0 = 0branch [ 2 8 * , ] exit
+  dup buffer-logical-length @ 0 = { exit } if
 
   ~ Decrement the logical length. We do this now to get it over with, since
   ~ adjusting the start pointer is more complex.
@@ -116,13 +116,13 @@
   ~ Check whether the updated start is equal to the physical end.
   dup 4 roll =
   ~ (metadata pointer, updated start, updated start, physical end)
-  0branch [ 5 8 * , ]
-
-  ~ If the logical start pointer is now equal to the physical end pointer,
-  ~ we want to wrap to the physical start. That's what makes it a circular
-  ~ buffer.
-  ~ (metadata pointer, updated start)
-  drop dup buffer-physical-start @
+  {
+    ~ If the logical start pointer is now equal to the physical end pointer,
+    ~ we want to wrap to the physical start. That's what makes it a circular
+    ~ buffer.
+    ~ (metadata pointer, updated start)
+    drop dup buffer-physical-start @
+  } if
 
   ~ However we got here, save the updated logical start pointer.
   ~ (metadata pointer, updated start)
@@ -131,28 +131,28 @@
 
 ~ (metadata pointer -- byte or 0)
 : peek-from
-  dup buffer-logical-length @ 0 = 0branch [ 28 8 * , ]
-
-  ~ If the length is zero, there is no input, but we can still try calling
-  ~ the "refill" word.
-  ~ (metadata pointer)
-  dup input-buffer-refill @ dup 0branch [ 17 8 * , ]
-
-  ~ If the refill word is nonzero, call it. It expects a copy of the metadata
-  ~ pointer as its parameter, so set that up.
-  ~ (metadata pointer, refill word)
-  swap dup 3roll execute
-  ~ (metadata pointer)
-  ~ Now we check if the length is still zero.
-  dup buffer-logical-length @ 0 = 0branch [ 10 8 * , ]
-
-  ~ The length is zero even after calling the refill word, so return null.
-  ~ (metadata pointer)
-  drop 0 exit
-
-  ~ If the refill word is zero, we can't help, just return null.
-  ~ (metadata pointer, refill word)
-  drop drop 0 exit
+  dup buffer-logical-length @ {
+    ~ If the length is zero, there is no input, but we can still try calling
+    ~ the "refill" word.
+    ~ (metadata pointer)
+    dup input-buffer-refill @ dup {
+      ~ If the refill word is zero, we can't help, just return null.
+      ~ (metadata pointer, refill word)
+      drop drop 0 exit
+    } unless
+
+    ~ If the refill word is nonzero, call it. It expects a copy of the metadata
+    ~ pointer as its parameter, so set that up.
+    ~ (metadata pointer, refill word)
+    swap dup 3roll execute
+    ~ (metadata pointer)
+    ~ Now we check if the length is still zero.
+    dup buffer-logical-length @ {
+      ~ The length is zero even after calling the refill word, so return null.
+      ~ (metadata pointer)
+      drop 0 exit
+    } unless
+  } unless
 
   ~   The buffer is non-empty, so read a byte from it. We might have reached
   ~ this point either from the original check, or from the second check after
diff --git a/output.e b/output.e
index 80f1577..5c4b31d 100644
--- a/output.e
+++ b/output.e
@@ -26,21 +26,20 @@
 : pow
   1 swap
 
-  ~ If the count of remaining powers is NOT equal to zero, the comparison will
-  ~ return 0, which will cause the zbranch to skip the rest of the line.
-  dup 0 = 0branch [ 5 8 * , ] drop swap drop exit
-  ~ (base, result so far, count of remaining powers)
+  {
+    ~ If the count of remaining powers is equal to zero, exit.
+    dup { drop swap drop exit } unless
+    ~ (base, result so far, count of remaining powers)
 
-  1 - 3unroll
-  ~ (updated count of remaining powers, base, result so far)
+    1 - 3unroll
+    ~ (updated count of remaining powers, base, result so far)
 
-  swap dup 4 unroll
-  ~ (base, updated count of remaining powers, result so far, base)
+    swap dup 4 unroll
+    ~ (base, updated count of remaining powers, result so far, base)
 
-  * swap
-  ~ (base, updated result so far, updated count of remaining powers)
-
-  branch [ -22 8 * , ] ;
+    * swap
+    ~ (base, updated result so far, updated count of remaining powers)
+  } forever ;
 
 
 ~ (base, value -- floor of logarithm of value in base base)
@@ -48,29 +47,28 @@
   ~ Start with a product equal to the base, and a count of 0.
   swap dup 3unroll 0
 
-  ~ This is the start of the loop body.
-  ~ (base, value, product so far, count of powers included so far)
-  3unroll 2dup
-  ~ (base, count so far, value, product so far)
-  >unsigned 0branch [ 6 8 * , ]
+  {
+    ~ This is the start of the loop body.
+    ~ (base, value, product so far, count of powers included so far)
+    3unroll 2dup
+    ~ (base, count so far, value, product so far)
 
-  ~ If we get here, we're done.
-  ~ (base, count so far, value, product so far)
-  drop drop swap drop exit
+    ~ If we get here, we're done.
+    ~ (base, count so far, value, product so far)
+    >unsigned { drop drop swap drop exit } if
 
-  ~ If we're here, we need to do another loop.
-  ~ (base, count so far, value, product so far)
-  4 roll dup 5 unroll * 3roll 1 +
-  ~ (base, value, updated product so far, updated count so far)
+    ~ If we're here, we need to do another loop.
+    ~ (base, count so far, value, product so far)
+    4 roll dup 5 unroll * 3roll 1 +
+    ~ (base, value, updated product so far, updated count so far)
 
-  ~ If the product is less than the base, we overflowed. In that case, the
-  ~ product-so-far is the maximum, so just return it.
-  4 roll dup 5 unroll 3roll dup 4 unroll
-  < 0branch [ 8 8 * , ]
-  4 unroll drop drop drop exit
+    ~ If the product is less than the base, we overflowed. In that case, the
+    ~ product-so-far is the maximum, so just return it.
+    4 roll dup 5 unroll 3roll dup 4 unroll
+    < { 4 unroll drop drop drop exit } if
 
-  ~ Nothing else weird going on, so loop.
-  branch [ -45 8 * , ] ;
+    ~ Nothing else weird going on, so loop.
+  } forever ;
 
 
 ~ (base, value -- ceiling of logarithm of value in base base)
@@ -78,29 +76,28 @@
   ~ Start with a product of 1 and a count of 0.
   1 0
 
-  ~ This is the start of the loop body.
-  ~ (base, value, product so far, count of powers included so far)
-  3unroll 2dup
-  ~ (base, count so far, value, product so far)
-  >=unsigned 0branch [ 6 8 * , ]
+  {
+    ~ This is the start of the loop body.
+    ~ (base, value, product so far, count of powers included so far)
+    3unroll 2dup
+    ~ (base, count so far, value, product so far)
 
-  ~ If we get here, we're done.
-  ~ (base, count so far, value, product so far)
-  drop drop swap drop exit
+    ~ If we get here, we're done.
+    ~ (base, count so far, value, product so far)
+    >=unsigned { drop drop swap drop exit } if
 
-  ~ If we're here, we need to do another loop.
-  ~ (base, count so far, value, product so far)
-  4 roll dup 5 unroll * 3roll 1 +
-  ~ (base, value, updated product so far, updated count so far)
+    ~ If we're here, we need to do another loop.
+    ~ (base, count so far, value, product so far)
+    4 roll dup 5 unroll * 3roll 1 +
+    ~ (base, value, updated product so far, updated count so far)
 
-  ~ If the product is less than the base, we overflowed. In that case, the
-  ~ product-so-far is the maximum, so just return it.
-  4 roll dup 5 unroll 3roll dup 4 unroll
-  < 0branch [ 8 8 * , ]
-  4 unroll drop drop drop exit
+    ~ If the product is less than the base, we overflowed. In that case, the
+    ~ product-so-far is the maximum, so just return it.
+    4 roll dup 5 unroll 3roll dup 4 unroll
+    < { 4 unroll drop drop drop exit } if
 
-  ~ Nothing else weird going on, so loop.
-  branch [ -45 8 * , ] ;
+    ~ Nothing else weird going on, so loop.
+  } forever ;
 
 
 ~   This is an extremely inefficient implementation, but on the plus side,
@@ -115,47 +112,52 @@
   ~ logic of always printing at least one digit is already handled for us.
   3unroll swap 2dup logfloor 1 +
   ~ (width, base, input, number of digits if no padding)
-  4 roll 2dup > 0branch [ 5 8 * , ]
-  ~ (base, input, number of digits if no padding, width)
-  ~ If we're here, we should use the padded width
-  swap drop branch [ 2 8 * , ]
-  ~ (base, input, number of digits if no padding, width)
-  ~ If we're here, we should use the unpadded width
-  drop
-
-  ~ (base, input, number of digits remaining)
-  ~ This is the start of the loop.
-  2dup 1 -
-  ~ (base, input, number of digits remaining, input, intermediate value)
-  5 roll dup 6 unroll swap
-  ~ (base, input, number of digits remaining, input, base, intermediate value)
-  pow /% swap drop
-  ~ (base, input, number of digits remaining,
-  ~  input divided by base^x appropriately)
-  4 roll dup 5 unroll
-  ~ (base, input, number of digits remaining,
-  ~  input divided by base^x appropriately, base)
-  /% drop
-  ~ (base, input, number of digits remaining, current digit)
-
-  ~   We construct a one-character string on the stack, then use a pointer to
-  ~ it. It will always contain its own null-termination without us having to
-  ~ do anything special to that end.
-  dup 10 > 0branch [ 5 8 * , ]
-  0x30 branch [ 6 8 * , ]                        ~ ASCII "0"
-  10 - 0x61                                      ~ ASCII "a"
-  +
-  value@ emitstring
-
-  ~ We deallocate the string by dropping it.
-  ~
-  ~ Saying it like that makes it sound obvious; contemplate it until it feels
-  ~ surprising.
-  drop
-
-  1 -
-  dup 0branch [ 3 8 * , ] branch [ -51 8 * , ]
-  drop drop drop ;
+  4 roll 2dup > {
+    ~ (base, input, number of digits if no padding, width)
+    ~ If we're here, we should use the padded width
+    swap drop
+  } {
+    ~ (base, input, number of digits if no padding, width)
+    ~ If we're here, we should use the unpadded width
+    drop
+  } if-else
+
+  {
+    ~ (base, input, number of digits remaining)
+    ~ This is the start of the loop.
+    2dup 1 -
+    ~ (base, input, number of digits remaining, input, intermediate value)
+    5 roll dup 6 unroll swap
+    ~ (base, input, number of digits remaining, input, base, intermediate value)
+    pow /% swap drop
+    ~ (base, input, number of digits remaining,
+    ~  input divided by base^x appropriately)
+    4 roll dup 5 unroll
+    ~ (base, input, number of digits remaining,
+    ~  input divided by base^x appropriately, base)
+    /% drop
+    ~ (base, input, number of digits remaining, current digit)
+
+    ~   We construct a one-character string on the stack, then use a pointer to
+    ~ it. It will always contain its own null-termination without us having to
+    ~ do anything special to that end.
+    dup 10 > {
+      0x30                                         ~ ASCII "0"
+    } {
+      10 - 0x61                                    ~ ASCII "a"
+    } if-else
+    +
+    value@ emitstring
+
+    ~ We deallocate the string by dropping it.
+    ~
+    ~ Saying it like that makes it sound obvious; contemplate it until it feels
+    ~ surprising.
+    drop
+
+    1 -
+    dup { drop drop drop exit } unless
+  } forever ;
 
 
 ~ (integer to print, base to print in --)
@@ -164,9 +166,10 @@
   ~ (base, input)
 
   ~ Deal with negative numbers.
-  dup 0 > 0branch [ 7 8 * , ]
-  -1 *
-  s" -" emitstring
+  dup 0 > {
+    -1 *
+    s" -" emitstring
+  } if
 
   swap 0 .base-unsigned ;
 
@@ -187,33 +190,3 @@
 ~ (integer to print, width --)
 : .hexn 16 swap .base-unsigned ;
 
-
-~ Debugging tools
-~ ~~~~~~~~~~~~~~~
-
-~ TODO remove these altogether, they're in dynamic.e now
-~ ~ ~ TODO this is a horrible, horrible hack
-~ : s0-kludge 0x1000010008 ;
-~
-~ ~ TODO replace these with the implementations that use proper flow-control
-~ : stack
-~   s0-kludge @ 8 -
-~
-~   dup value@ 8 + !=
-~   0branch [ 19 8 * , ]
-~   dup s0-kludge @ 8 - != 0branch [ 2 8 * , ] space dup @ . 8 -
-~   branch [ -25 8 * , ]
-~
-~   drop newline ;
-~
-~
-~ : stackhex
-~   s0-kludge @ 8 -
-~
-~   dup value@ 8 + !=
-~   0branch [ 19 8 * , ]
-~   dup s0-kludge @ 8 - != 0branch [ 2 8 * , ] space dup @ .hex64 8 -
-~   branch [ -25 8 * , ]
-~
-~   drop newline ;
-