summary refs log tree commit diff
path: root/quine.asm
diff options
context:
space:
mode:
Diffstat (limited to 'quine.asm')
-rw-r--r--quine.asm391
1 files changed, 257 insertions, 134 deletions
diff --git a/quine.asm b/quine.asm
index c5148ef..0a5d96d 100644
--- a/quine.asm
+++ b/quine.asm
@@ -4,6 +4,12 @@
 ;;;
 ;;; There's some tabular information, but diagrams have been avoided, in an
 ;;; attempt to make this manageable in screen readers. Feedback welcome.
+;;;
+;;; First the hex
+;;; Second the spark
+;;; Third the words
+;;; Then tie the knot
+;;;   [draft]
 
 
 ;;;;;;;;;;;;;;;;;;;;;
@@ -2902,6 +2908,24 @@ cold_start:
   dq hlt
   dq lit, 8, packalign, early_here_store
 
+  ; This was "align_size".
+  dq litstring, "align-size", early_create, early_docol_codeword
+  dq litstring, "dup", early_find, entry_to_execution_token, early_comma
+  dq litstring, "3unroll", early_find, entry_to_execution_token, early_comma
+  dq litstring, "dup", early_find, entry_to_execution_token, early_comma
+  dq litstring, "3unroll", early_find, entry_to_execution_token, early_comma
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 1, early_comma
+  dq litstring, "-", early_find, entry_to_execution_token, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "/%", early_find, entry_to_execution_token, early_comma
+  dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "drop", early_find, entry_to_execution_token, early_comma
+  dq litstring, "*", early_find, entry_to_execution_token, early_comma
+  dq litstring, "exit", early_find, entry_to_execution_token, early_comma
+  dq early_here, fetch, lit, 8, packalign, early_here_store
+
   ; We could in theory call this 64pack, by analogy to 32! and so on, but it's
   ; kept like this to be more similar to reg64, imm64 etc.
   ;
@@ -3078,7 +3102,8 @@ cold_start:
   dq early_here, fetch, lit, 8, packalign, early_here_store
 
   dq litstring, "unpackalign", early_create, early_docol_codeword
-  dq litstring, "packalign", early_find, entry_to_execution_token, early_comma
+  dq litstring, "align-size", early_find, entry_to_execution_token
+  dq early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
   dq early_here, fetch, lit, 8, packalign, early_here_store
 
@@ -6469,74 +6494,159 @@ cold_start:
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
   dq early_here, fetch, lit, 8, packalign, early_here_store
 
-  ;;; As a convenience for "word", we have some facilities for working with
+
+  ;;;   As a convenience for "word", we have some facilities for working with
   ;;; stack-allocated strings. Yeah, trippy concept. Also, it would be a
   ;;; buffer overrun hazard if we were worried about that, which is why this
   ;;; is no longer common practice in C.
-  dq litstring, "stack-allocate-string-accumulator", early_create
-  dq early_docol_codeword
+  ;;;
+  ;;;   The most important of these is accumulate-string, but we need some
+  ;;; smaller pieces first.
+
+  ; In:
+  ;   (multiple words) string
+  ;   (multiple words) items to be left alone
+  ;   item to be unrolled
+  ;   number of items above string that participate in the unroll
+  ; Out:
+  ;   item that was unrolled
+  ;   (multiple words) string
+  ;   (multiple words) items left alone
+  dq litstring, "unroll-past-string", early_create, early_docol_codeword
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 0, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "*", early_find, entry_to_execution_token, early_comma
+  ; (string, other items, top item, byte offset to string start)
+  dq litstring, "dup", early_find, entry_to_execution_token, early_comma
   dq litstring, "value@", early_find, entry_to_execution_token, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  ; We have two copies of the offset present, in addition to the stuff we want
+  ; to rotate. So, the actual string starts two words on... We could have
+  ; adjusted the offset instead, but we'll want the unmodified offset again
+  ; later.
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 16, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  ; (string, other items, top item, offset to start, string pointer)
+  dq litstring, "stringlen", early_find, entry_to_execution_token, early_comma
+  ; Same reasoning as in accumulate-string (see below).
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 1, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "align-size", early_find, entry_to_execution_token, early_comma
+  ; (string, other items, top item, offset to start, string length w/ padding)
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "/%", early_find, entry_to_execution_token, early_comma
+  dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "drop", early_find, entry_to_execution_token, early_comma
+  ; (string, other items, top item, number of words to unroll)
+  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
   dq early_here, fetch, lit, 8, packalign, early_here_store
 
-  dq litstring, "stack-deallocate-string-accumulator", early_create
-  dq early_docol_codeword
+  ; In:
+  ;   (multiple words) string
+  ;   item to be swapped
+  ; Out:
+  ;   item that was swapped
+  ;   (multiple words) string
+  dq litstring, "swap-past-string", 0, early_create, early_docol_codeword
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 1, early_comma
+  dq litstring, "unroll-past-string", early_find, entry_to_execution_token
+  dq early_comma
+  dq litstring, "exit", early_find, entry_to_execution_token, early_comma
+  dq early_here, fetch, lit, 8, packalign, early_here_store
+
+  ; In:
+  ;   (multiple words) string
+  dq litstring, "dropstring", early_create, early_docol_codeword
+  dq litstring, "value@", early_find, entry_to_execution_token, early_comma
+  dq litstring, "stringlen", early_find, entry_to_execution_token, early_comma
+  ; Same reasoning as in accumulate-string (see below).
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 1, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "align-size", early_find, entry_to_execution_token, early_comma
+  dq litstring, "value@", early_find, entry_to_execution_token, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  ;   At the time we fetched the stack pointer, there was an extra value atop
+  ; it, so we have to add one more word.
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
   dq litstring, "value!", early_find, entry_to_execution_token, early_comma
-  dq litstring, "drop", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
   dq early_here, fetch, lit, 8, packalign, early_here_store
 
   ; In:
   ;   (multiple words) string
-  ;   pointer to start of string
-  ;   result
+  ;   item to be kept
   ; Out:
-  ;   result
-  dq litstring, "stack-deallocate-string-accumulator-with-result"
-  dq early_create, early_docol_codeword
-  dq litstring, "swap", early_find, entry_to_execution_token, early_comma
-  dq litstring, "dup", early_find, entry_to_execution_token, early_comma
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
-  dq litstring, "swap", early_find, entry_to_execution_token, early_comma
-  dq litstring, "!", early_find, entry_to_execution_token, early_comma
-  dq litstring, "value!", early_find, entry_to_execution_token, early_comma
+  ;   item that was kept
+  dq litstring, "dropstring-with-result", early_create, early_docol_codeword
+  dq litstring, "swap-past-string", 0, early_find, entry_to_execution_token
+  dq early_comma
+  dq litstring, "dropstring", early_find, entry_to_execution_token
+  dq early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
   dq early_here, fetch, lit, 8, packalign, early_here_store
 
   ; In:
   ;   (multiple words) string-so-far
-  ;   pointer to start of string-so-far
   ;   new character byte
   ; Out:
-  ;   updated string-so-far
-  ;   pointer to start of string-so-far
-  dq litstring, "accumulate-string", early_create, early_docol_codeword
+  ;   (multiple words) updated string-so-far
+  dq litstring, "accumulate-string", early_create
+  dq early_docol_codeword
 
-  ; Examine the final word of the string, leaving other stuff undisturbed.
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
+  ; Compute the address of the final word of the string.
+  ;
+  ;   It's a little bit difficult to get the start pointer right, since all
+  ; our intermediate products affect what we get from value@, so we compute
+  ; that just once, here at the beginning.
+  dq litstring, "value@", early_find, entry_to_execution_token, early_comma
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  ; (string so far, new character byte, pointer to start of string)
   dq litstring, "dup", early_find, entry_to_execution_token, early_comma
+  dq litstring, "stringlen", early_find, entry_to_execution_token, early_comma
+  ; There are two concerns here that overlap: First, we always want at least
+  ; one word. Recall that a length of zero bytes won't receive any alignment
+  ; padding because it's already divisible by 8. Second, the result of
+  ; stringlen doesn't include the null byte, which might be in a word by
+  ; itself that needs to be counted. We can address both of them by
+  ; unconditionally adding 1 to the length before applying alignment.
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 4, early_comma
-  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
+  dq lit, 1, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  ; Pad the length for alignment.
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 0xFF00000000000000, early_comma
-  dq litstring, "and", early_find, entry_to_execution_token, early_comma
-  dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
-  dq lit, 3*8, early_comma
-
-  ; If the top byte of the final word of the string is occupied, we need to
-  ; start a new word. Our representation makes that trivial.
-  dq litstring, "swap", early_find, entry_to_execution_token, early_comma
-  dq litstring, "exit", early_find, entry_to_execution_token, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "align-size", early_find, entry_to_execution_token
+  dq early_comma
+  ; We want an offset from the first word of the string to the last word of
+  ; the string, so we subtract one word from the length.
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "-", early_find, entry_to_execution_token, early_comma
+  dq litstring, "+", early_find, entry_to_execution_token, early_comma
+  ; (string so far, new character byte, address of final word)
 
-  ; If the top byte is unoccupied, figure out where to store the new byte.
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
+  ; Examine the final word of the string, leaving other stuff undisturbed.
+  ; Work low-to-high to figure out where to store the new byte, taking the
+  ; first one that's available.
+  ; (string so far, new character byte, address of final word)
+  dq litstring, "dup", early_find, entry_to_execution_token, early_comma
+  dq litstring, "@", early_find, entry_to_execution_token, early_comma
   dq litstring, "dup", early_find, entry_to_execution_token, early_comma
-  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 4, early_comma
-  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x00000000000000FF, early_comma
   dq litstring, "and", early_find, entry_to_execution_token, early_comma
@@ -6544,144 +6654,177 @@ cold_start:
   dq lit, 0, early_comma
   dq litstring, "=", early_find, entry_to_execution_token, early_comma
   dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
-  dq lit, 5*8, early_comma
+  dq lit, 6*8, early_comma
+  ; (string so far, new character byte, address of final word, old value)
   dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "or", early_find, entry_to_execution_token, early_comma
   dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "!", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
+  ; (string so far, new character byte, address of final word, old value)
 
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
+  ; This next part is repeated several times, changing only the offsets, for
+  ; bytes 1 through 6; bytes 0 (above) and 7 (way below) are different.
+  ; (string so far, new character byte, address of final word, old value)
   dq litstring, "dup", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 4, early_comma
-  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
-  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x000000000000FF00, early_comma
   dq litstring, "and", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0, early_comma
   dq litstring, "=", early_find, entry_to_execution_token, early_comma
   dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
-  dq lit, 8*8, early_comma
+  dq lit, 9*8, early_comma
+  ; (string so far, new character byte, address of final word, old value)
+  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0000000000000100, early_comma
   dq litstring, "*", early_find, entry_to_execution_token, early_comma
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "or", early_find, entry_to_execution_token, early_comma
   dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "!", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
 
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
+  ; Highly repetitive code, see above.
+  ; (string so far, new character byte, address of final word, old value)
   dq litstring, "dup", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 4, early_comma
-  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
-  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0000000000FF0000, early_comma
   dq litstring, "and", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0, early_comma
   dq litstring, "=", early_find, entry_to_execution_token, early_comma
   dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
-  dq lit, 8*8, early_comma
+  dq lit, 9*8, early_comma
+  ; (string so far, new character byte, address of final word, old value)
+  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0000000000010000, early_comma
   dq litstring, "*", early_find, entry_to_execution_token, early_comma
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "or", early_find, entry_to_execution_token, early_comma
   dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "!", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
 
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
+  ; Highly repetitive code, see above.
+  ; (string so far, new character byte, address of final word, old value)
   dq litstring, "dup", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 4, early_comma
-  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
-  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x00000000FF000000, early_comma
   dq litstring, "and", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0, early_comma
   dq litstring, "=", early_find, entry_to_execution_token, early_comma
   dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
-  dq lit, 8*8, early_comma
+  dq lit, 9*8, early_comma
+  ; (string so far, new character byte, address of final word, old value)
+  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0000000001000000, early_comma
   dq litstring, "*", early_find, entry_to_execution_token, early_comma
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "or", early_find, entry_to_execution_token, early_comma
   dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "!", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
 
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
+  ; Highly repetitive code, see above.
+  ; (string so far, new character byte, address of final word, old value)
   dq litstring, "dup", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 4, early_comma
-  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
-  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x000000FF00000000, early_comma
   dq litstring, "and", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0, early_comma
   dq litstring, "=", early_find, entry_to_execution_token, early_comma
   dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
-  dq lit, 8*8, early_comma
+  dq lit, 9*8, early_comma
+  ; (string so far, new character byte, address of final word, old value)
+  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0000000100000000, early_comma
   dq litstring, "*", early_find, entry_to_execution_token, early_comma
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "or", early_find, entry_to_execution_token, early_comma
   dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "!", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
 
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
+  ; Highly repetitive code, see above.
+  ; (string so far, new character byte, address of final word, old value)
   dq litstring, "dup", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 4, early_comma
-  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
-  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0000FF0000000000, early_comma
   dq litstring, "and", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0, early_comma
   dq litstring, "=", early_find, entry_to_execution_token, early_comma
   dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
-  dq lit, 8*8, early_comma
+  dq lit, 9*8, early_comma
+  ; (string so far, new character byte, address of final word, old value)
+  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0000010000000000, early_comma
   dq litstring, "*", early_find, entry_to_execution_token, early_comma
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "or", early_find, entry_to_execution_token, early_comma
   dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "!", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
 
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
+  ; Highly repetitive code, see above.
+  ; (string so far, new character byte, address of final word, old value)
   dq litstring, "dup", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
-  dq lit, 4, early_comma
-  dq litstring, "unroll", early_find, entry_to_execution_token, early_comma
-  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x00FF000000000000, early_comma
   dq litstring, "and", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0, early_comma
   dq litstring, "=", early_find, entry_to_execution_token, early_comma
   dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
-  dq lit, 8*8, early_comma
+  dq lit, 9*8, early_comma
+  ; (string so far, new character byte, address of final word, old value)
+  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0001000000000000, early_comma
   dq litstring, "*", early_find, entry_to_execution_token, early_comma
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "or", early_find, entry_to_execution_token, early_comma
   dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  dq litstring, "!", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
 
+  ;   The top byte of the final word is always zero (or else stringlen
+  ; wouldn't have called it the final word), so we don't need to check it, we
+  ; can just use it.
+  ;
+  ;   We need to put the new value in the top byte, which will mean we have no
+  ; null terminator, so we also need to start a new word.
+  ;
+  ;   There is a fiddly order-dependency here: unroll-past-string relies on
+  ; being able to find the null terminator, which won't work if we've gotten
+  ; rid of it. Also, calling it will move all the earlier words, including
+  ; the one we intend to write to, which will invalidate any pointer we're
+  ; keeping at that point. There's a few ways to resolve this; what we do is
+  ; put the new terminator in place first, manually nudge the pointer, and
+  ; then write the new value.
+  ; (string so far, new character byte, address of final word, old value)
+  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0x0100000000000000, early_comma
   dq litstring, "*", early_find, entry_to_execution_token, early_comma
-  dq litstring, "3roll", early_find, entry_to_execution_token, early_comma
   dq litstring, "or", early_find, entry_to_execution_token, early_comma
   dq litstring, "swap", early_find, entry_to_execution_token, early_comma
+  ; (string so far, new value, address of final word)
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 0, early_comma
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 3, early_comma
+  dq litstring, "unroll-past-string", early_find, entry_to_execution_token
+  dq early_comma
+  ; (new null terminator, string so far, new value, invalid address)
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 8, early_comma
+  dq litstring, "-", early_find, entry_to_execution_token, early_comma
+  ; (new null terminator, string so far, new value, updated address)
+  dq litstring, "!", early_find, entry_to_execution_token, early_comma
+  ; (updated string)
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
   dq early_here, fetch, lit, 8, packalign, early_here_store
 
@@ -6690,13 +6833,12 @@ cold_start:
   ;
   ; Out:
   ;   (multiple words) string
-  ;   pointer to start of string
   dq litstring, "word", early_create, early_docol_codeword
 
-  ; We allocate this first, so that the result of "key" wil conveniently be
-  ; on the easy-to-find end of it.
-  dq litstring, "stack-allocate-string-accumulator", early_find
-  dq entry_to_execution_token, early_comma
+  ; We allocate an empty string first, so that the result of "key" will
+  ; conveniently be on the easy-to-find end of it.
+  dq litstring, "lit", early_find, entry_to_execution_token, early_comma
+  dq lit, 0, early_comma
 
   ; Skip whitespace.
   dq litstring, "key", early_find, entry_to_execution_token, early_comma
@@ -6715,8 +6857,8 @@ cold_start:
   dq litstring, "0branch", early_find, entry_to_execution_token, early_comma
   dq lit, 6*8, early_comma
   dq litstring, "drop", early_find, entry_to_execution_token, early_comma
-  dq litstring, "stack-deallocate-string-accumulator", early_find
-  dq entry_to_execution_token, early_comma
+  dq litstring, "dropstring", early_find, entry_to_execution_token
+  dq early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
   dq lit, 0, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
@@ -6940,7 +7082,10 @@ cold_start:
   dq litstring, "drop", early_find, entry_to_execution_token, early_comma
   dq litstring, "exit", early_find, entry_to_execution_token, early_comma
 
-  dq litstring, "dup", early_find, entry_to_execution_token, early_comma
+  ; The string is on the top of the stack, so to get a pointer to it we get
+  ; the stack address.
+  ; (string)
+  dq litstring, "value@", early_find, entry_to_execution_token, early_comma
   dq litstring, "find", early_find, entry_to_execution_token, early_comma
 
   ; Check whether the word was found in the dictionary.
@@ -6952,8 +7097,8 @@ cold_start:
   dq lit, 6*8, early_comma
 
   ; If the word is in the dictionary, run it.
-  dq litstring, "stack-deallocate-string-accumulator-with-result", early_find
-  dq entry_to_execution_token, early_comma
+  dq litstring, "dropstring-with-result", early_find, entry_to_execution_token
+  dq early_comma
   dq litstring, "entry-to-execution-token", 0, early_find
   dq entry_to_execution_token, early_comma
   dq litstring, "execute", early_find, entry_to_execution_token, early_comma
@@ -6963,8 +7108,9 @@ cold_start:
 
   ; If it's not in the dictionary, check whether it's a decimal number.
   dq litstring, "drop", early_find, entry_to_execution_token, early_comma
-  ; (string contents..., string pointer)
-  dq litstring, "dup", early_find, entry_to_execution_token, early_comma
+  ; As before, we get the stack address and use it as a string pointer.
+  ; (string)
+  dq litstring, "value@", early_find, entry_to_execution_token, early_comma
   dq litstring, "read-decimal", early_find, entry_to_execution_token
   dq early_comma
   dq litstring, "lit", early_find, entry_to_execution_token, early_comma
@@ -6976,15 +7122,15 @@ cold_start:
   ; It's a number; push it to the stack. Or at least, that's what the code
   ; we're interpreting will see. Really it's already on the stack, just clean
   ; everything else up and leave it there.
-  dq litstring, "stack-deallocate-string-accumulator-with-result", early_find
-  dq entry_to_execution_token, early_comma
+  dq litstring, "dropstring-with-result", early_find, entry_to_execution_token
+  dq early_comma
   dq litstring, "branch", early_find, entry_to_execution_token, early_comma
   ; o/~ Like a whirlpool and it never ends. o/~
   dq lit, -32*8, early_comma
 
   ; If it's neither in the dictionary nor a number, exit.
-  dq litstring, "stack-deallocate-string-accumulator", early_find
-  dq entry_to_execution_token, early_comma
+  dq litstring, "dropstring", early_find, entry_to_execution_token
+  dq early_comma
   dq litstring, "litstring", early_find, entry_to_execution_token, early_comma
   dq lit, "No such ", early_comma, lit, "word.", early_comma
   dq litstring, "emitstring", early_find, entry_to_execution_token
@@ -6993,6 +7139,7 @@ cold_start:
   dq early_here, fetch, lit, 8, packalign, early_here_store
 
 
+  ; The next layer is built now, so let's move on to it.
   dq litstring, "interpret", early_find, entry_to_execution_token
 
   ; Get rid of that heap pointer on the stack, we're finally done with it!
@@ -7003,42 +7150,6 @@ cold_start:
   dq lit, 1, sys_exit
 
 
-  ;;; TODO rethink this, it needs to not use space on the value stack
-  ;; Stack-allocate a buffer.
-  ;;
-  ;;   For brevity's sake, in notation below here when we give the contents of
-  ;; the stack we'll leave out the original copy of the heap pointer, and the
-  ;; four words used for the buffer.
-  ;dq lit, 0, lit, 0, lit, 0, lit, 0, fetch_value_stack
-  ;;   Make a copy of the heap pointer so we can have it a little closer. If we
-  ;; instead were to try to get to the copy on the other side of the buffer by
-  ;; rolling, it would move the buffer, invalidating the pointers.
-  ;dq lit, 6, roll, dup, lit, 7, unroll, swap
-  ;dq dup
-  ;dq lit, boot_source
-  ;; (heap pointer, buffer start pointer, buffer end pointer, input pointer)
-  ;
-  ;; Start of the loop
-  ;dq key                                                       ; 1 word
-  ;
-  ;; (heap pointer, buffer start pointer, buffer end pointer,
-  ;;  input pointer, input byte)
-  ;dq dup, lit, " ", ne, zbranch, 13*8                          ; 6 words
-  ;dq dup, lit, 0x0a, ne, zbranch, 7*8                          ; 6 words
-  ;
-  ;; If we got here, it's a body character.
-  ;dq roll3, swap, pack8, swap, branch, -18*8                   ; 6 words
-  ;
-  ;; If we got here, it's a space character.
-  ;dq drop, swap, lit, 0, pack8                                 ; 5 words
-  ;dq swap, roll3, dup, lit, 4, unroll                          ; 6 words
-  ;
-  ;; (heap pointer, buffer start pointer, buffer end pointer,
-  ;;  input pointer, word pointer)
-  ;dq lit, 5, roll, swap, early_find, swap, lit, 5, unroll      ; 8 words
-  ;dq entry_to_execution_token, execute                         ; 2 words
-  ;dq branch, -41*8
-
   ;;; For triage's sake, here's an inventory of everything else in the file.
   ;;;
   ;;; Macros:
@@ -8504,6 +8615,18 @@ defword litpack8, 0
 ;;; which it updates to point after the data item being read. Since this is
 ;;; input, the routines return data items rather than accepting them.
 
+; In:
+;   proposed size
+;   alignment
+; Out:
+;   adjusted size
+defword align_size, 0
+  dq docol
+  dq dup, unroll3, dup, unroll3
+  ; (alignment, proposed size, alignment)
+  dq lit, 1, sub, add, swap, divmod, swap, drop, mul
+  dq exit
+
 ; In: base address
 ; Out: new base address, value
 defword unpack64, 0
@@ -8523,8 +8646,8 @@ defword unpack8, 0
   dq dup, fetch8, swap, lit, 1, add, swap
   dq exit
 
-;   Since there's no data, this is identical to its output version, but we
-; allow this name as an alias for clarity.
+;   You might think this would be identical to packalign, but packalign has
+; side effects.
 ;
 ; Stack in:
 ;   base address
@@ -8532,7 +8655,7 @@ defword unpack8, 0
 ; Stack out:
 ;   new base address
 defword unpackalign, 0
-  dq docol, packalign, exit
+  dq docol, align_size, exit
 
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;