+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ Name:
+@ printstacknumber (1001)
+@ Description:
+@ Prints 'number' from the stack in sign-magnitude format.
+@ Leading zeros are suppressed.
+@ Call Stack:
+@ number <-- TOS
+@ Return Stack:
+@ <empty>
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+NSSVTSSTN | Mark: 1001 (print number from stack)
+SNS | DUP
+NSTTSSSSTSN | JSR > 1000010 (printstacknumbersign)
+NSTTSSSSTTN | JSR > 1000011 (printstacknumbermagnitude)
+NTN | RTS
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ Name:
+@ printstacknumbersign (1000010)
+@ Description:
+@ Prints the sign of 'number' from the stack.
+@ Call Stack:
+@ number <-- TOS
+@ Return Stack:
+@ <empty>
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+NSSVTSSSSTSN | Mark: 1000010 (print sign of number from stack)
+NTTSTSSSSTSSSSSSSSTN | BMI > 01000010 00000001
+SSSTSTSTTN | PUSH ASCII '+'
+NSNSTSSSSTSSSSSSSTSN | JMP > 01000010 00000010
+NSSVSTSSSSTSSSSSSSSTN | Mark: 01000010 00000001
+SSSTSTTSTN | PUSH ASCII '-'
+NSSVSTSSSSTSSSSSSSTSN | Mark: 01000010 00000010
+TNSS | PUTC
+NTN | RTS
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ Name:
+@ printstacknumbermagnitude (1000011)
+@ Description:
+@ Prints the magnitude of 'number' from the stack.
+@ Call Stack:
+@ number <-- TOS
+@ Return Stack:
+@ <empty>
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#include <math.pvvs>
+NSSVTSSSSTTN | Mark: 1000011 (print magnitude of number from stack)
+
+@ Catch -(2^63) as a special case since its absolute value will overflow
+@ a twos-complement 64-bit word.
+SNS | DUP
+SSTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSN | -(2^63)
+TSST | SUBTRACT
+NTSSTSSSSTTSSSSSSTSN | BRZ > 01000011 00000010
+
+@ No special case applies. Prepare for computation by converting the number
+@ to absolute value and preparing a string on the stack.
+NSTTSSSTN | JSR > 10001 (absolute value)
+SSSSN | PUSH ASCII '\0'
+SNT | SWAP
+
+@ Pick off one digit on each pass through this loop.
+NSSVSTSSSSTTSSSSSSSSN | Mark: 01000011 00000000
+SNS | DUP
+
+@ Mod-off a digit, convert to ASCII, store on stack as part of the string.
+SSSTSTSN | PUSH +10
+TSTT | MODULO
+SSSTTSSSSN | PUSH ASCII '0'
+TSSS | ADD
+SNT | SWAP
+
+@ Divide down to next digit and keep looping if number != 0 yet.
+SSSTSTSN | PUSH +10
+TSTS | DIVIDE
+SNS | DUP
+NTSSTSSSSTTSSSSSSSTN | BRZ > 01000011 00000001
+NSNSTSSSSTTSSSSSSSSN | JMP > 01000011 00000000
+
+@ Print the string we have built on the stack.
+NSSVSTSSSSTTSSSSSSSTN | Mark: 01000011 00000001
+SNN | DROP
+NSTTSSSTSSN | JSR > 1000100 (print string from stack)
+NTN | RTS
+
+@ Replace the number on the stack with its decimal ASCII representation.
+NSSVSTSSSSTTSSSSSSTSN | BRZ > 01000011 00000010
+SNN | DROP
+A"-9223372036854775808"
+NSNSTSSSSTTSSSSSSSTN | JMP > 01000011 00000001
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ Name:
+@ printf
+@ Description:
+@ If printing a static string (i.e. no substitutions), pass
+@ 'number of substitutions' as 0, immediately followed by 'string word 1'.
+@ If printing a string from the heap instead of stack, pass an empty string
+@ on the stack followed by a pointer to the first word of the
+@ null-terminated string on the heap.
+@ For example:
+@ pointer
+@ ASCII '\0'
+@ substitution n
+@ <remainder of call stack is unchanged>
+@ Maximum substitutions determined by upper heap limit in stackrotate and
+@ stackrotatereverse subroutines.
+@ Call Stack:
+@ ACSII '\0'
+@ string word n
+@ ...
+@ string word 1
+@ substitution n
+@ ...
+@ substitution 1
+@ number of substitutions <-- TOS
+@ Return Stack:
+@ <empty>
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#include <stack.pvvs>
+NSSVTSSSN | Mark: 1000 (printf)
+
+@ If the stack contains an empty string (i.e. just an ASCII '\0'), the next
+@ word is a pointer we must use to load the string from the heap.
+@ This will leave the stack looking exactly like the example call stack above.
+@ Do the test this way so we can keep the code inline.
+SNS | DUP
+SSSTSN | PUSH 2
+TSSS | ADD
+NSTTTSSN | JSR > 1100 (deepdup)
+SSTTN | PUSH -1
+TSSN | MULTIPLY
+NTTSSSSTSSSSSSSSSSTN | BMI > 00001000 00000001
+SNS | DUP
+SSSTSN | PUSH 2
+TSSS | ADD
+NSTTSTTN | JSR > 1011 (stackrotatereverse)
+SNN | DROP
+SNS | DUP
+SSSTSN | PUSH 2
+TSSS | ADD
+NSTTSTTN | JSR > 1011 (stackrotatereverse)
+NSTSSSSTSSSTSSSTTSSN | JSR > 00001000 10001100 (printf_deepslurp)
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ The rest of printf parses a string according to the following information.
+@ ASCII '\ ':
+@ ASCII '\ ': putchar '\ '
+@ ASCII '%': putchar '%'
+@ ASCII 'n': putchar '\n'
+@ ASCII 't': putchar '\t'
+@ ASCII '%':
+@ ASCII 'c': (print character)
+@ ASCII 's': (print string)
+@ ASCII 'd': (print decimal digit)
+@ ASCII 'u': (print abs(integer), w/o sign)
+@ ASCII 'i': (print integer w/sign)
+@ ASCII '\0':
+@ cleanup and exit
+@ default:
+@ putchar
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+@@@@@@@@@@@@@@@@@@@@
+@ The next block tests the char against all possible level 1 branches (see above).
+@ If there is a match, execution jumps to the appropriate level 2 branch label.
+@ If no match is found, print the character and move on.
+@@@@@@@@@@@@@@@@@@@@
+NSSVSSSSTSSSSSSSSSSTN | Mark: 00001000 00000001
+@ Move the next character of the string to TOS.
+NSTSSSSTSSSTSSSTSTTN | JSR > 00001000 10001011 (next char to TOS)
+@ TOS is an ASCII '\ '. Jump to process the possible level 2 branches.
+SNS | DUP
+SSSTSTTTSSN | PUSH ASCII slash
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSSSTSN | BRZ > 00001000 00000010
+@ TOS is an ASCII '%'. Jump to process the possible level 2 branches.
+SNS | DUP
+SSSTSSTSTN | PUSH ASCII '%'
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSSSTTN | BRZ > 00001000 00000011
+@ TOS is an ASCII "\0". Jump to clean-up-and-exit.
+SNS | DUP
+NTSSSSSTSSSSSSSSSSSN | BRZ > 00001000 00000000
+@ TOS is a normal character. Print it and loop again.
+TNSS | PUTC
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ Level 2 - ASCII '\ ' - Escapes
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+@@@@@@@@@@@@@@@@@@@@
+@ The level 1 match was an ASCII '\ '.
+@ Now look for level 2 matches that trigger a character substitution (n -> newline, etc).
+@ If no matches are found, print the character directly (e.g. "\%" -> '%')
+@ When finished, loop back to testing level 1 branches.
+@@@@@@@@@@@@@@@@@@@@
+NSSVSSSSTSSSSSSSSSTSN | Mark: 00001000 00000010
+SNN | DROP
+@ Move the next character of the string to TOS.
+NSTSSSSTSSSTSSSTSTTN | JSR > 00001000 10001011 (next char to TOS)
+@ Check for ASCII '\n'
+SNS | DUP
+SSSTTSTTTSN | PUSH ASCII 'n'
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSSTSSN | BRZ > 00001000 00000100
+@ Check for ASCII '\t'
+SNS | DUP
+SSSTTTSTSSN | PUSH ASCII 't'
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSSTSTN | BRZ > 00001000 00000101
+@ No substitution necessary. Print literally.
+TNSS | PUTC
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+
+@@@@@@@@@@@@@@@@@@@@
+@ These are utility labels to output the appropriate non-printable ASCII character.
+@ After output, they loop back to testing level 1 branches.
+@@@@@@@@@@@@@@@@@@@@
+
+@ Print a newline and loop for the next character.
+NSSVSSSSTSSSSSSSSTSSN | Mark: 00001000 00000100
+SNN | DROP
+SSSTSTSN | PUSH ASCII '\n'
+TNSS | PUTC
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+
+@ Print a horizontal tab and loop for the next character.
+NSSVSSSSTSSSSSSSSTSTN | Mark: 00001000 00000101
+SNN | DROP
+SSSTSSTN | PUSH ASCII '\t'
+TNSS | PUTC
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ Level 2 - ASCII '%' - Substitutions
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+@@@@@@@@@@@@@@@@@@@@
+@ The level 1 match was an ASCII '%'.
+@ Now look for level 2 matches that trigger a substitution.
+@ When finished, loop back to testing level 1 branches.
+@@@@@@@@@@@@@@@@@@@@
+NSSVSSSSTSSSSSSSSSTTN | Mark: 00001000 00000011
+SNN | DROP
+@ Move the next character of the string to TOS.
+NSTSSSSTSSSTSSSTSTTN | JSR > 00001000 10001011 (next char to TOS)
+@ Check for ASCII 'c' - Print character
+SNS | DUP
+SSSTTSSSTTN | PUSH ASCII 'c'
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSSTTSN | BRZ > 00001000 00000110
+@ Check for ASCII 's' - Print string
+SNS | DUP
+SSSTTTSSTTN | PUSH ASCII 's'
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSSTTTN | BRZ > 00001000 00000111
+@ Check for ASCII 'd' - Print decimal digit
+SNS | DUP
+SSSTTSSTSSN | PUSH ASCII 'd'
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSTSSSN | BRZ > 00001000 00001000
+@ Check for ASCII 'u' - Print unsigned number
+SNS | DUP
+SSSTTTSTSTN | PUSH ASCII 'u'
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSTSSTN | BRZ > 00001000 00001001
+@ Check for ASCII 'i' - Print signed number
+SNS | DUP
+SSSTTSTSSTN | PUSH ASCII 'i'
+TSST | SUBTRACT
+NTSSSSSTSSSSSSSTSTSN | BRZ > 00001000 00001010
+@ Unrecognized substitution specifier.
+@ For now, silently consume it and continue.
+@ Do not increment the substitution counter.
+@ TODO: Is this really what I want to do here?
+SNN | DROP
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+
+
+@@@@@@@@@@@@@@@@@@@@
+@ These are utility labels to call the appropriate type of output subroutine.
+@ After output, they decrement the substition counter and loop back to testing
+@ level 1 branches.
+@@@@@@@@@@@@@@@@@@@@
+
+@ Print a character
+NSSVSSSSTSSSSSSSSTTSN | Mark: 00001000 00000110
+SNN | DROP
+SNT | SWAP
+TNSS | PUTC
+SSSTN | PUSH 1
+TSST | SUBTRACT
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+@ Print a string
+NSSVSSSSTSSSSSSSSTTTN | Mark: 00001000 00000111
+SNN | DROP
+SNT | SWAP
+NSTTSSSTSTN | JSR > 1000101 (print string from heap)
+SSSTN | PUSH 1
+TSST | SUBTRACT
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+@ Print a decimal digit
+NSSVSSSSTSSSSSSSTSSSN | Mark: 00001000 00001000
+SNN | DROP
+SNT | SWAP
+TNST | PUTDIGIT
+SSSTN | PUSH 1
+TSST | SUBTRACT
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+@ Print an unsigned integer
+NSSVSSSSTSSSSSSSTSSTN | Mark: 00001000 00001001
+SNN | DROP
+SNT | SWAP
+NSTTSSSSTTN | JSR > 1000011 (print magnitude of number from stack)
+SSSTN | PUSH 1
+TSST | SUBTRACT
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+@ Print a signed integer
+NSSVSSSSTSSSSSSSTSTSN | Mark: 00001000 00001010
+SNN | DROP
+SNT | SWAP
+NSTTSSTN | JSR > 1001 (print number from stack)
+SSSTN | PUSH 1
+TSST | SUBTRACT
+NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ These are misc labels associated with the printf function.
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+@ Found an ASCII "\0" when processing the format string. Clean up and exit.
+NSSVSSSSTSSSSSSSSSSSN | Mark: 00001000 00000000
+SNN | DROP
+SNN | DROP
+NTN | RTS
+
+@ Move the next string character to TOS.
+@ Stack should look like the printf call stack, with num-of-subs at TOS.
+NSSVSSSSTSSSTSSSTSTTN | Mark: 00001000 10001011 (next char to TOS)
+SNS | DUP
+SSSTSN | PUSH 2
+TSSS | ADD
+NSTTSTTN | JSR > 1011 (stackrotatereverse)
+NTN | RTS
+
+@ Slurps a string from the heap to the stack, storing it behind the substitutions.
+@ Call Stack:
+@ substitution n
+@ ...
+@ substitution 1
+@ number of substitutions <-- TOS
+@ pointer to string
+@ Return Stack:
+@ ACSII '\0'
+@ string word n
+@ ...
+@ string word 1
+@ substitution n
+@ ...
+@ substitution 1
+@ number of substitutions <-- TOS
+@ TODO: This, along with a deepspew, should probably be stdlib routines.
+NSSVSSSSTSSSTSSSTTSSN | Mark: 00001000 10001100 (printf_deepslurp)
+SNS | DUP
+@ Advance a duplicate copy of the pointer until it points to the null-terminator.
+NSSVSSSSTSSSTSSSTTSTN | Mark: 00001000 10001101
+SNS | DUP
+TTT | LOAD
+NTSSSSSTSSSTSSSTTTSN | BRZ > 00001000 10001110
+SSSTN | PUSH 1
+TSSS | ADD
+NSNSSSSTSSSTSSSTTSTN | JMP > 00001000 10001101
+@ Load a character to the stack on each pass through this loop.
+NSSVSSSSTSSSTSSSTTTSN | Mark: 00001000 10001110
+SNS | DUP
+TTT | LOAD
+SSSTSSN | PUSH 4
+NSTTTSSN | JSR > 1100 (deepdup)
+SSSTSSN | PUSH 4
+TSSS | ADD
+NSTTSTSN | JSR > 1010 (stackrotate)
+@ Test for end of loop.
+SNS | DUP
+SSSTTN | PUSH 3
+NSTTTSSN | JSR > 1100 (deepdup)
+TSST | SUBTRACT
+NTSSSSSTSSSTSSSTTTTN | BRZ > 00001000 10001111
+@ Decrement pointer to end of string, loop again.
+SSSTN | PUSH 1
+TSST | SUBTRACT
+NSNSSSSTSSSTSSSTTTSN | JMP > 00001000 10001110
+@ Clean up and return.
+NSSVSSSSTSSSTSSSTTTTN | Mark: 00001000 10001111
+SNN | DROP
+SNN | DROP
+NTN | RTS
+
+#endif