From: Aaron Taylor Date: Fri, 19 Jul 2019 22:54:53 +0000 (-0700) Subject: Added printf and stackrotate functions to VVS stdlib. X-Git-Url: http://git.subgeniuskitty.com/vvhitespace/.git/commitdiff_plain/23d1724712fae7dc9645d862d57d030cf3e2e764 Added printf and stackrotate functions to VVS stdlib. Modified slurp to use stackrotate instead of heap registers. --- diff --git a/examples/hello-stdlib/hello.pvvs b/examples/hello-stdlib/hello.pvvs index a70ebff..dace168 100644 --- a/examples/hello-stdlib/hello.pvvs +++ b/examples/hello-stdlib/hello.pvvs @@ -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" diff --git a/stdlib/README.md b/stdlib/README.md index 12a5ff6..3c92dde 100644 --- a/stdlib/README.md +++ b/stdlib/README.md @@ -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) diff --git a/stdlib/debug.pvvs b/stdlib/debug.pvvs index 6328cf8..138b2cb 100644 --- a/stdlib/debug.pvvs +++ b/stdlib/debug.pvvs @@ -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 diff --git a/stdlib/heap.pvvs b/stdlib/heap.pvvs index 69ed992..4953fad 100644 --- a/stdlib/heap.pvvs +++ b/stdlib/heap.pvvs @@ -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 index 0000000..a5115ac --- /dev/null +++ b/stdlib/stack.pvvs @@ -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 diff --git a/stdlib/stdio.pvvs b/stdlib/stdio.pvvs index f18d97c..950ad6c 100644 --- a/stdlib/stdio.pvvs +++ b/stdlib/stdio.pvvs @@ -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: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -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 +@ +@ Maximum of 7 substitutions. +@ Call Stack: +@ ACSII '\0' +@ string word n +@ ... +@ string word 1 +@ substitution n +@ ... +@ substitution 1 +@ number of substitutions <-- TOS +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +#include +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