@ (c) 2020 Aaron Taylor @ See LICENSE.txt file for copyright and license details. #ifndef VVS_STDLIB_STDIO #define VVS_STDLIB_STDIO @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ Name: @ printstackstring (1000100) @ Description: @ Prints a null-terminated string from the stack. @ Call Stack: @ null-terminator (ASCII '\0') @ char n @ ... @ char 2 @ char 1 <-- TOS @ Return Stack: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NSSVTSSSTSSN | Mark: 1000100 (print string from stack) SNS | DUP NTSSTSSSTSSSSSSSSSTN | BRZ > 01000100 00000001 TNSS | Print character NSNTSSSTSSN | JMP > 1000100 NSSVSTSSSTSSSSSSSSSTN | Mark: 01000100 00000001 SNN | DROP NTN | RTS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ Name: @ printheapstring (1000101) @ Description: @ Prints a null-terminated string from the heap. @ Call Stack: @ pointer to first character <-- TOS @ Return Stack: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NSSVTSSSTSTN | Mark: 1000101 (print string from heap) SNS | DUP TTT | LOAD SNS | DUP NTSSTSSSTSTSSSSSSSTN | BRZ > 01000101 00000001 TNSS | Print character SSSTN | Push +1 TSSS | ADD NSNTSSSTSTN | JMP > 1000101 NSSVSTSSSTSTSSSSSSSTN | Mark: 01000101 00000001 SNN | DROP SNN | DROP NTN | RTS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ Name: @ printstacknumber (1001) @ Description: @ Prints 'number' from the stack in sign-magnitude format. @ Leading zeros are suppressed. @ Call Stack: @ number <-- TOS @ Return Stack: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 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: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 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: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include 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 @ @ 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: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include 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 substitution 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