Added printf and stackrotate functions to VVS stdlib.
authorAaron Taylor <ataylor@subgeniuskitty.com>
Fri, 19 Jul 2019 22:54:53 +0000 (15:54 -0700)
committerAaron Taylor <ataylor@subgeniuskitty.com>
Fri, 19 Jul 2019 22:54:53 +0000 (15:54 -0700)
Modified slurp to use stackrotate instead of heap registers.

examples/hello-stdlib/hello.pvvs
stdlib/README.md
stdlib/debug.pvvs
stdlib/heap.pvvs
stdlib/stack.pvvs [new file with mode: 0644]
stdlib/stdio.pvvs

index a70ebff..dace168 100644 (file)
@@ -1,8 +1,8 @@
 @@ This program outputs "Hello, world!"
 
 A"Hello, world!\n"
-@TODO: Change this to printf call
-NSTTSSSTSSN     | FC: JSR>1000100 (print string from stack; see stdlib)
+SSSSN           | ST: PUSH 0
+NSTTSSSN        | FC: JSR>1000 (printf; see stdlib)
 NNN             | FC: Terminate program
 
 #include "stdio.pvvs"
index 12a5ff6..3c92dde 100644 (file)
@@ -32,10 +32,10 @@ The following labels are entry points to stdlib functions. Read the
 header comment for each function to learn the call and return stack.
 
          000xxx - reserved
-         001xxx - print functions
-           1000 ----- print string from stack       (stdio.pvvs)
-           1001 ----- print string from heap        (stdio.pvvs)
-           1010 ----- print number from stack       (stdio.pvvs)
+         001xxx - core functions
+           1000 ----- printf                        (stdio.pvvs)
+           1001 ----- print number from stack       (stdio.pvvs)
+           1010 ----- stackrotate                   (stack.pvvs)
          010xxx - math functions
           10000 ----- random                        (math.pvvs)
           10001 ----- absolute value                (math.pvvs)
index 6328cf8..138b2cb 100644 (file)
@@ -27,7 +27,7 @@ SSSTTTSTSN               | PUSH ASCII ':'
 SSSTSSTN                 | PUSH ASCII '\t'
 TNSS                     | PUTC
 TNSS                     | PUTC
-NSTTSTSN                 | JSR>1010 (print number from stack)
+NSTTSSTN                 | JSR>1001 (print number from stack)
 SSSTSTSN                 | PUSH ASCII '\n'
 TNSS                     | PUTC
 
@@ -72,7 +72,7 @@ SSSTSSTN                 | PUSH ASCII '\t'
 TNSS                     | PUTC
 TNSS                     | PUTC
 SNT                      | SWAP
-NSTTSTSN                 | JSR>1010 (print number from stack)
+NSTTSSTN                 | JSR>1001 (print number from stack)
 SSSTSTSN                 | PUSH ASCII '\n'
 TNSS                     | PUTC
 
index 69ed992..4953fad 100644 (file)
@@ -291,6 +291,10 @@ NTN                     | RTS
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 NSSVTSSSSSTN            | Mark: 1000001 (spewreg)
 
+@ To simplify other functions, sprewreg shall accept 0 as a valid count value.
+SNS                     | DUP
+NTSSTSSSSSTSSSSSSSTN    | BRZ > 01000001 00000001
+
 @ Create a counter in heap[15] that doubles as a destination pointer generator.
 SSSTTTTN                | PUSH 15 (ptr)
 SSSTN                   | PUSH 1
@@ -340,30 +344,30 @@ NTN                     | RTS
 @   heap[address]  <-- TOS
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 NSSVTTTTSN              | Mark: 11110 (slurp)
-SSSTSN                  | PUSH 2
-NSTTSSSSSTN             | JSR > 1000001 (spewreg)
 
 @ Load one word from heap on each pass.
-NSSVSSSTTTTSSSSSSSSSN   | Mark: 00011110 00000000
-SSSTN                   | PUSH 1 (ptr)
-TTT                     | LOAD
-SSSTSN                  | PUSH 2 (ptr)
-TTT                     | LOAD
+SNS                     | DUP
+SSSTTN                  | PUSH 3
+NSTTSTSN                | JSR > 1010 (stackrotate)
+SNT                     | SWAP
+SNS                     | DUP
+SSSTTN                  | PUSH 3
+NSTTSTSN                | JSR > 1010 (stackrotate)
 TSSS                    | ADD
 TTT                     | LOAD
+SSSTTN                  | PUSH 3
+NSTTSTSN                | JSR > 1010 (stackrotate)
+SNT                     | SWAP
 
 @ Check for loop completion.
-@ As a side effect, prepare the next address.
-SSSTN                   | PUSH 1 (ptr)
 SNS                     | DUP
-TTT                     | LOAD
-SNS                     | DUP
-NTSSSSTTTTSSSSSSSSTN    | BRZ > 00011110 00000001
+NTSSSSTTTTSSSSSSSSSN    | BRZ > 00011110 00000000
 SSSTN                   | PUSH 1
 TSST                    | SUBTRACT
-TTS                     | STORE
-NSNSSSTTTTSSSSSSSSSN    | JMP > 00011110 00000000
-NSSVSSSTTTTSSSSSSSSTN   | Mark: 00011110 00000001
+NSNTTTTSN               | JMP > 11110 (slurp)
+
+@ Clean up and return.
+NSSVSSSTTTTSSSSSSSSSN   | Mark: 00011110 00000000
 SNN                     | DROP
 SNN                     | DROP
 NTN                     | RTS
diff --git a/stdlib/stack.pvvs b/stdlib/stack.pvvs
new file mode 100644 (file)
index 0000000..a5115ac
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef VVS_STDLIB_STACK
+#define VVS_STDLIB_STACK
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ Name:
+@   stackrotate
+@ Description:
+@   Maximum rotation depth is five. Stomps on heap[9]-heap[14].
+@   Assumes rotation depth greater than two, otherwise use SWAP.
+@ Call Stack:
+@   stack word n
+@   ...
+@   stack word 1
+@   rotation depth (rd)  <-- TOS
+@ Return Stack (n>rd=3):
+@   stack word n
+@   ...
+@   stack word 1
+@   stack word 3
+@   stack word 2   <-- TOS
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+NSSVTSTSN               | Mark: 1010 (stackrotate)
+
+@ For the convenience of other functions, modulo the
+@ rotation depth by the available registers.
+SSSTSTN                 | PUSH 5
+TSTT                    | MODULO
+
+@ Use heap[14] for generating register addresses.
+SSSTTTSN                | PUSH 14 (ptr)
+SSSSN                   | PUSH 0
+TTS                     | STORE
+
+@ Dump one word from stack to heap each pass through the loop.
+NSSVSSSSTSTSSSSSSSSSN   | Mark: 00001010 00000000
+SNT                     | SWAP
+SSSTSSTN                | PUSH 9 (starting register)
+SSSTTTSN                | PUSH 14 (ptr)
+TTT                     | LOAD
+TSSS                    | ADD
+SNT                     | SWAP
+TTS                     | STORE
+
+@ See if the loop is complete.
+SNS                     | DUP
+SSSTTTSN                | PUSH 14 (ptr)
+TTT                     | LOAD
+SSSTN                   | PUSH 1
+TSSS                    | ADD
+TSST                    | SUBTRACT
+NTSSSSSTSTSSSSSSSSTN    | BRZ > 00001010 00000001
+
+@ Increment the loop counter
+SSSTTTSN                | PUSH 14 (ptr)
+TTT                     | LOAD
+SSSTN                   | PUSH 1
+TSSS                    | ADD
+SSSTTTSN                | PUSH 14 (ptr)
+SNT                     | SWAP
+TTS                     | STORE
+NSNSSSSTSTSSSSSSSSSN    | JMP > 00001010 00000000
+
+@ The correct number of words have been stored to registers.
+@ Time to read them back in rotated order.
+
+@ First, prepare the counter in heap[14] again.
+@ This consumes 'rd' from the stack.
+NSSVSSSSTSTSSSSSSSSTN   | Mark: 00001010 00000001
+SSSTN                   | PUSH 1
+TSST                    | SUBTRACT
+SSSTTTSN                | PUSH 14 (ptr)
+SNT                     | SWAP
+TTS                     | STORE
+
+@ Second, read in the old TOS manually.
+SSSTSSTN                | PUSH 9 (starting register)
+TTT                     | LOAD
+
+@ Read one word per pass through this loop.
+NSSVSSSSTSTSSSSSSSTSN   | Mark: 00001010 00000010
+SSSTSSTN                | PUSH 9 (starting register)
+SSSTTTSN                | PUSH 14 (ptr)
+TTT                     | LOAD
+TSSS                    | ADD
+TTT                     | LOAD
+
+@ See if the loop is complete.
+SSSTTTSN                | PUSH 14 (ptr)
+TTT                     | LOAD
+SSSTN                   | PUSH 1
+TSST                    | SUBTRACT
+NTSSSSSTSTSSSSSSSTTN    | BRZ > 00001010 00000011
+
+@ Decrement the loop counter and loop again.
+SSSTTTSN                | PUSH 14 (ptr)
+TTT                     | LOAD
+SSSTN                   | PUSH 1
+TSST                    | SUBTRACT
+SSSTTTSN                | PUSH 14 (ptr)
+SNT                     | SWAP
+TTS                     | STORE
+NSNSSSSTSTSSSSSSSTSN    | JMP > 00001010 00000010
+
+@ Return
+NSSVSSSSTSTSSSSSSSTTN   | Mark: 00001010 00000011
+NTN                     | RTS
+
+#endif
index f18d97c..950ad6c 100644 (file)
@@ -50,7 +50,7 @@ NTN                     | RTS
 
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @ Name:
-@   printstacknumber (1010)
+@   printstacknumber (1001)
 @ Description:
 @   Prints 'number' from the stack in sign-magnitude format.
 @   Leading zeros are suppressed.
@@ -59,7 +59,7 @@ NTN                     | RTS
 @ Return Stack:
 @   <empty>
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-NSSVTSTSN               | Mark: 1010 (print number from stack)
+NSSVTSSTN               | Mark: 1001 (print number from stack)
 SNS                     | DUP
 NSTTSSSSTSN             | JSR > 1000010
 NSTTSSSSTTN             | JSR > 1000011
@@ -126,4 +126,265 @@ SNN                     | DROP
 NSTTSSSTSSN             | JSR > 1000100 (print string from stack)
 NTN                     | RTS
 
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ 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 of 7 substitutions.
+@ Call Stack:
+@   ACSII '\0'
+@   string word n
+@   ...
+@   string word 1
+@   substitution n
+@   ...
+@   substitution 1
+@   number of substitutions  <-- TOS
+@ Return Stack:
+@   <empty>
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#include <heap.pvvs>
+#include <string.pvvs>
+NSSVTSSSN               | Mark: 1000 (printf)
+
+@ Copy any substitution words into heap registers.
+NSTTSSSSSTN             | JSR > 1000001 (spewreg)
+
+@ heap[8] will track the current substitution register.
+SSSTSSSN                | PUSH 8 (ptr)
+SSSTN                   | PUSH 1
+TTS                     | STORE
+
+@ 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.
+@ Do the test this way so we can keep the code inline.
+SNS                     | DUP
+SSTTN                   | PUSH -1
+TSSN                    | MULTIPLY
+NTTSSSSTSSSSSSSSSSTN    | BMI > 00001000 00000001
+SNN                     | DROP
+SNS                     | DUP
+NSTTSSSSSN              | JSR > 100000 (strlen)
+NSTTTTTSN               | JSR > 11110 (slurp)
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ Initialization is now complete.
+@ Stack contains a printf-compatible, null-terminated string.
+@ heap[1]-heap[7] contain the substitutions, if any.
+@ heap[8] tracks which substitution is next.
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ 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 TOS 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
+@ 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
+@ Check for ASCII '\n'
+SNS                     | DUP
+SSSTTSTTTSN             | PUSH ASCII 'n'
+TSST                    | SUBTRACT
+NTSSSSSTSSSSSSSSTSSN    | BRZ > 00001000 00000100
+@ Check for ASCII '\t'
+SNS                     | DUP
+SSSTSSTN                | 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
+@ 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 increment the substition counter (heap[8])
+@ and loop back to testing level 1 branches.
+@@@@@@@@@@@@@@@@@@@@
+
+@ Print a character
+NSSVSSSSTSSSSSSSSTTSN   | Mark: 00001000 00000110
+SNN                     | DROP
+SSSTSSSN                | PUSH 8 (ptr)
+TTT                     | LOAD
+TTT                     | LOAD
+TNSS                    | PUTC
+NSTSSSSTSSSTSSSSSSSN    | JSR > 00001000 10000000 (increment heap[8])
+NSNSSSSTSSSSSSSSSSTN    | JMP > 00001000 00000001
+@ Print a string
+NSSVSSSSTSSSSSSSSTTTN   | Mark: 00001000 00000111
+SNN                     | DROP
+SSSTSSSN                | PUSH 8 (ptr)
+TTT                     | LOAD
+TTT                     | LOAD
+NSTTSSSTSTN             | JSR > 1000101 (print string from heap)
+NSTSSSSTSSSTSSSSSSSN    | JSR > 00001000 10000000 (increment heap[8])
+NSNSSSSTSSSSSSSSSSTN    | JMP > 00001000 00000001
+@ Print a decimal digit
+NSSVSSSSTSSSSSSSTSSSN   | Mark: 00001000 00001000
+SNN                     | DROP
+SSSTSSSN                | PUSH 8 (ptr)
+TTT                     | LOAD
+TTT                     | LOAD
+TNST                    | PUTDIGIT
+NSTSSSSTSSSTSSSSSSSN    | JSR > 00001000 10000000 (increment heap[8])
+NSNSSSSTSSSSSSSSSSTN    | JMP > 00001000 00000001
+@ Print an unsigned integer
+NSSVSSSSTSSSSSSSTSSTN   | Mark: 00001000 00001001
+SNN                     | DROP
+SSSTSSSN                | PUSH 8 (ptr)
+TTT                     | LOAD
+TTT                     | LOAD
+NSTTSSSSTTN             | JSR > 1000011 (print magnitude of number from stack)
+NSTSSSSTSSSTSSSSSSSN    | JSR > 00001000 10000000 (increment heap[8])
+NSNSSSSTSSSSSSSSSSTN    | JMP > 00001000 00000001
+@ Print a signed integer
+NSSVSSSSTSSSSSSSTSTSN   | Mark: 00001000 00001010
+SNN                     | DROP
+SSSTSSSN                | PUSH 8 (ptr)
+TTT                     | LOAD
+TTT                     | LOAD
+NSTTSSTN                | JSR > 1001 (print number from stack)
+NSTSSSSTSSSTSSSSSSSN    | JSR > 00001000 10000000 (increment heap[8])
+NSNSSSSTSSSSSSSSSSTN    | JMP > 00001000 00000001
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@ These are misc labels associated with the printf function.
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+@ Increments heap[8]. Used after a substitution is complete.
+NSSVSSSSTSSSTSSSSSSSN   | Mark: 00001000 10000000 (increment heap[8])
+SSSTSSSN                | PUSH 8 (ptr)
+TTT                     | LOAD
+SSSTN                   | PUSH 1
+TSSS                    | ADD
+SSSTSSSN                | PUSH 8 (ptr)
+SNT                     | SWAP
+TTS                     | STORE
+NTN                     | RTS
+
+@ Found an ASCII "\0" when processing the format string. Clean up and exit.
+NSSVSSSSTSSSSSSSSSSSN   | Mark: 00001000 00000000
+SNN                     | DROP
+NTN                     | RTS
+
 #endif