From 7359501c4b44efadc76eef5570008dd52f6767fc Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Wed, 7 Aug 2019 13:27:11 -0700 Subject: [PATCH 01/16] Added tests for stdlib math functions. Added check for most-negative-number in `abs` subroutine. --- stdlib/math.pvvs | 17 +++++++++++++++++ stdlib_tests/5001_abs.pvvs | 26 ++++++++++++++++++++++++++ stdlib_tests/5002_random.pvvs | 10 ++++++++++ stdlib_tests/vv_test.py | 2 ++ 4 files changed, 55 insertions(+) create mode 100644 stdlib_tests/5001_abs.pvvs create mode 100644 stdlib_tests/5002_random.pvvs diff --git a/stdlib/math.pvvs b/stdlib/math.pvvs index d618d83..3876c6e 100644 --- a/stdlib/math.pvvs +++ b/stdlib/math.pvvs @@ -46,6 +46,17 @@ NTN | RTS @ abs(signed number) <-- TOS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NSSVTSSSTN | Mark: 10001 (absolute value) + +@ Catch -(2^63) as a special case since its absolute value will overflow +@ a twos-complement 64-bit word. Return zero as though the absolute value +@ overflowed to the bottom of the non-negative integers rather than +@ overflowing back to the most negative integer. +SNS | DUP +SSTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSN | -(2^63) +TSST | SUBTRACT +NTSSSSTSSSTSSSSSSTSN | BRZ > 00010001 00000010 + +@ Handle all the other numbers. SNS | DUP NTTSSSTSSSTSSSSSSSSN | BMI > 00010001 00000000 NSNSSSTSSSTSSSSSSSTN | JMP > 00010001 00000001 @@ -55,4 +66,10 @@ TSSN | MULTIPLY NSSVSSSTSSSTSSSSSSSTN | Mark: 00010001 00000001 NTN | RTS +@ Special case: Push 0 and return. +NSSVSSSTSSSTSSSSSSTSN | Mark: 00010001 00000010 +SNN | DROP +SSSSN | PUSH 0 +NTN | RTS + #endif diff --git a/stdlib_tests/5001_abs.pvvs b/stdlib_tests/5001_abs.pvvs new file mode 100644 index 0000000..7898e56 --- /dev/null +++ b/stdlib_tests/5001_abs.pvvs @@ -0,0 +1,26 @@ +@ +1 should remain +1 +SSSTN | PUSH +1 +NSTTSSSTN | JSR > 10001 (abs) +NSTTSSTN | JSR > 1001 (print number from stack) + +@ -1 should become +1 +SSTTN | PUSH -1 +NSTTSSSTN | JSR > 10001 (abs) +NSTTSSTN | JSR > 1001 (print number from stack) + +@ 0 should remain 0 +SSSSN | PUSH 0 +NSTTSSSTN | JSR > 10001 (abs) +NSTTSSTN | JSR > 1001 (print number from stack) + +@ Test the most negative number. +@ It is impossible to return the true absolute value but should return zero. +SSSTN | PUSH +1 +SSSTTTTTTN | PUSH +63 +NSTTSTTSTN | JSR > 101101 (lshift) +NSTTSSSTN | JSR > 10001 (abs) +NSTTSSTN | JSR > 1001 (print number from stack) +NNN | DIE + +#include +#include diff --git a/stdlib_tests/5002_random.pvvs b/stdlib_tests/5002_random.pvvs new file mode 100644 index 0000000..9a869ee --- /dev/null +++ b/stdlib_tests/5002_random.pvvs @@ -0,0 +1,10 @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ +@ This test intentionally blank. +@ +@ If there comes a day when the tests are extended beyond testing basic +@ functionality, include random in the new tests. +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +NNN | Die diff --git a/stdlib_tests/vv_test.py b/stdlib_tests/vv_test.py index a121659..d3420f1 100755 --- a/stdlib_tests/vv_test.py +++ b/stdlib_tests/vv_test.py @@ -37,6 +37,8 @@ tests = [ ['3005_or', '', '+0+1+1-1-1'], ['3006_xor', '', '+0+1+0-2-1'], ['4001_strlen', '', '+11'], + ['5001_abs', '', '+1+1+0+0'], + ['5002_random', '', ''], ] for test in tests: -- 2.20.1 From 4fba07dca7fb9275b51e5095a774fe69eeab4660 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Sun, 8 Sep 2019 04:25:30 -0700 Subject: [PATCH 02/16] Updated tests to use printsignednumber subroutine from debug.pvvs instead of stdio.pvvs. --- stdlib/README.md | 1 + stdlib/debug.pvvs | 21 +++++++++++++++++++-- stdlib_tests/2005_memcmp.pvvs | 4 ++-- stdlib_tests/2006_memsrch.pvvs | 4 ++-- stdlib_tests/3001_not.pvvs | 10 +++++----- stdlib_tests/3002_lshift.pvvs | 14 +++++++------- stdlib_tests/3003_rshift.pvvs | 16 ++++++++-------- stdlib_tests/3004_and.pvvs | 12 ++++++------ stdlib_tests/3005_or.pvvs | 12 ++++++------ stdlib_tests/3006_xor.pvvs | 12 ++++++------ stdlib_tests/4001_strlen.pvvs | 4 ++-- stdlib_tests/5001_abs.pvvs | 11 ++++++----- 12 files changed, 70 insertions(+), 51 deletions(-) diff --git a/stdlib/README.md b/stdlib/README.md index 9e1338f..99f3e69 100644 --- a/stdlib/README.md +++ b/stdlib/README.md @@ -67,6 +67,7 @@ header comment for each function to learn the call and return stack. 111010 ----- print sign (debug.pvvs) 111011 ----- print magnitude (debug.pvvs) 111100 ----- print string (debug.pvvs) + 111101 ----- print signed number (debug.pvvs) 1xxxxxx - reserved for less common entry points 1000000 ----- lowbitand (logic.pvvs) 1000001 ----- diff --git a/stdlib/debug.pvvs b/stdlib/debug.pvvs index 960c65b..4114d22 100644 --- a/stdlib/debug.pvvs +++ b/stdlib/debug.pvvs @@ -24,7 +24,7 @@ @ Return Stack: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -NSSVTTTSSSN | Mark: 111000 (dump heap) +NSSVTTTSSSN | Mark: 111000 (debug:dumpheap) @ Prepare a numeric address and value on the stack SNS | DUP @@ -74,7 +74,7 @@ NTN | RTS @ Return Stack: @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -NSSVTTTSSTN | Mark: 111001 (dump stack) +NSSVTTTSSTN | Mark: 111001 (debug:dumpstack) @ Orient the user SSSSSSSSSN | PUSH ASCII '\0' @@ -204,4 +204,21 @@ NSSVSSTTTTSSSSSSSSSTN | Mark: 00111100 00000001 SNN | DROP NTN | RTS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ printsignednumber (111101) +@ Description: +@ Prints 'number' from the stack in sign-magnitude format. +@ Leading zeros are suppressed. +@ Call Stack: +@ number <-- TOS +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTTTTSTN | Mark: 111101 (debug:printsignednumber) +SNS | DUP +NSTTTTSTSN | JSR > 111010 (debug:printsign) +NSTTTTSTTN | JSR > 111011 (debug:printmagnitude) +NTN | RTS + #endif diff --git a/stdlib_tests/2005_memcmp.pvvs b/stdlib_tests/2005_memcmp.pvvs index 5c59238..cd339b3 100644 --- a/stdlib_tests/2005_memcmp.pvvs +++ b/stdlib_tests/2005_memcmp.pvvs @@ -16,9 +16,9 @@ SSSTSSSSSN | PUSH 32 (blk1ptr) SSSTSSSSSSN | PUSH 64 (blk2ptr) NSTTTSTTN | JSR > 11011 (memcmp) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) NNN | DIE #include -#include +#include diff --git a/stdlib_tests/2006_memsrch.pvvs b/stdlib_tests/2006_memsrch.pvvs index 406f936..5bd1c87 100644 --- a/stdlib_tests/2006_memsrch.pvvs +++ b/stdlib_tests/2006_memsrch.pvvs @@ -10,9 +10,9 @@ SSSTSSSN | PUSH 8 (count) SSSSTTTSSN | PUSH 28 (address) NSTTTTSSN | JSR > 11100 (memsrch) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) NNN | DIE #include -#include +#include diff --git a/stdlib_tests/3001_not.pvvs b/stdlib_tests/3001_not.pvvs index 8af83b3..5bd49e2 100644 --- a/stdlib_tests/3001_not.pvvs +++ b/stdlib_tests/3001_not.pvvs @@ -1,28 +1,28 @@ @ All zeros to all ones SSSSN | PUSH 0 NSTTSTSSSN | JSR > 101000 (not) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ All ones to all zeros SSTTN | PUSH -1 NSTTSTSSSN | JSR > 101000 (not) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test alternating bits, leading zero. @ Note that 6148914691236517205 = 0101...0101 SSSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTN | PUSH +6148914691236517205 NSTTSTSSSN | JSR > 101000 (not) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test alternating bits, leading one. @ Note that -6148914691236517206 = 1010...1010 in twos-complement but we @ enter it in sign magnitude format so the bit pattern appears different. SSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTTSN | PUSH -6148914691236517206 NSTTSTSSSN | JSR > 101000 (not) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ All done. NNN | DIE #include -#include +#include diff --git a/stdlib_tests/3002_lshift.pvvs b/stdlib_tests/3002_lshift.pvvs index c4650b2..192a4fc 100644 --- a/stdlib_tests/3002_lshift.pvvs +++ b/stdlib_tests/3002_lshift.pvvs @@ -2,40 +2,40 @@ SSSTN | PUSH +1 SSSSN | PUSH 0 NSTTSTTSTN | JSR > 101101 (lshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift by one. SSSTN | PUSH +1 SSSTN | PUSH +1 NSTTSTTSTN | JSR > 101101 (lshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift by 62. SSSTN | PUSH +1 SSSTTTTTSN | PUSH +62 NSTTSTTSTN | JSR > 101101 (lshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift by 63. SSSTN | PUSH +1 SSSTTTTTTN | PUSH +63 NSTTSTTSTN | JSR > 101101 (lshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift all ones until a single one remains. SSTTN | PUSH -1 SSSTTTTTTN | PUSH +63 NSTTSTTSTN | JSR > 101101 (lshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift all ones to all zeros. SSTTN | PUSH -1 SSSTSSSSSSN | PUSH +64 NSTTSTTSTN | JSR > 101101 (lshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ All done. NNN | DIE #include -#include +#include diff --git a/stdlib_tests/3003_rshift.pvvs b/stdlib_tests/3003_rshift.pvvs index 5c70023..2071e50 100644 --- a/stdlib_tests/3003_rshift.pvvs +++ b/stdlib_tests/3003_rshift.pvvs @@ -2,19 +2,19 @@ SSSTN | PUSH +1 SSSSN | PUSH 0 NSTTSTTSSN | JSR > 101100 (rshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift by one, positive sign extension. SSSTSN | PUSH +2 SSSTN | PUSH +1 NSTTSTTSSN | JSR > 101100 (rshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift by four, positive sign extension. SSSTSSSSN | PUSH +16 SSSTSSN | PUSH +4 NSTTSTTSSN | JSR > 101100 (rshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift by one, negative sign extension. @ -9223372036854775805 = 1000000000000000000000000000000000000000000000000000000000000011 @@ -22,7 +22,7 @@ NSTTSSTN | JSR > 1001 (print number from stack) SSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSTN | PUSH -9223372036854775805 SSSTN | PUSH +1 NSTTSTTSSN | JSR > 101100 (rshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift by four, negative sign extension @ -9223372036854775638 = 1000000000000000000000000000000000000000000000000000000010101010 @@ -30,22 +30,22 @@ NSTTSSTN | JSR > 1001 (print number from stack) SSTSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSTSTSTTSN | PUSH -9223372036854775638 SSSTSSN | PUSH +4 NSTTSTTSSN | JSR > 101100 (rshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test for special case: rshift(-1) = -1 SSTTN | PUSH -1 SSSTN | PUSH +1 NSTTSTTSSN | JSR > 101100 (rshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Shift a single 1 in the MSb into all ones SSTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSN | PUSH -(2^63) SSSTTTTTTN | PUSH +63 NSTTSTTSSN | JSR > 101100 (rshift) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ All done. NNN | DIE #include -#include +#include diff --git a/stdlib_tests/3004_and.pvvs b/stdlib_tests/3004_and.pvvs index a468c82..d850925 100644 --- a/stdlib_tests/3004_and.pvvs +++ b/stdlib_tests/3004_and.pvvs @@ -2,25 +2,25 @@ SSSSN | PUSH 0 SSSSN | PUSH 0 NSTTSTSSTN | JSR > 101001 (and) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 0 ^ 1 = 0 SSSSN | PUSH 0 SSSTN | PUSH +1 NSTTSTSSTN | JSR > 101001 (and) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1 ^ 1 = 1 SSSTN | PUSH +1 SSSTN | PUSH +1 NSTTSTSSTN | JSR > 101001 (and) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1 ^ 1...1 = 1 SSSTN | PUSH +1 SSTTN | PUSH -1 NSTTSTSSTN | JSR > 101001 (and) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1010...1010 ^ 111111 = 101010 @ Note that -6148914691236517206 = 1010...1010 in twos-complement but we @@ -28,10 +28,10 @@ NSTTSSTN | JSR > 1001 (print number from stack) SSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTTSN | PUSH -6148914691236517206 SSSTTTTTTN | PUSH +63 NSTTSTSSTN | JSR > 101001 (and) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ All done. NNN | DIE #include -#include +#include diff --git a/stdlib_tests/3005_or.pvvs b/stdlib_tests/3005_or.pvvs index 756f8e1..3ec0341 100644 --- a/stdlib_tests/3005_or.pvvs +++ b/stdlib_tests/3005_or.pvvs @@ -2,25 +2,25 @@ SSSSN | PUSH 0 SSSSN | PUSH 0 NSTTSTSTSN | JSR > 101010 (or) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 0 v 1 = 1 SSSSN | PUSH 0 SSSTN | PUSH +1 NSTTSTSTSN | JSR > 101010 (or) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1 v 1 = 1 SSSTN | PUSH +1 SSSTN | PUSH +1 NSTTSTSTSN | JSR > 101010 (or) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1 v 1...1 = 1...1 SSSTN | PUSH +1 SSTTN | PUSH -1 NSTTSTSTSN | JSR > 101010 (or) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1010...1010 v 0101...0101 = 1...1 @ Note that -6148914691236517206 = 1010...1010 in twos-complement but we @@ -28,10 +28,10 @@ NSTTSSTN | JSR > 1001 (print number from stack) SSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTTSN | PUSH -6148914691236517206 SSSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTN | PUSH +6148914691236517205 NSTTSTSTSN | JSR > 101010 (or) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ All done. NNN | DIE #include -#include +#include diff --git a/stdlib_tests/3006_xor.pvvs b/stdlib_tests/3006_xor.pvvs index 68a72d1..f225190 100644 --- a/stdlib_tests/3006_xor.pvvs +++ b/stdlib_tests/3006_xor.pvvs @@ -2,25 +2,25 @@ SSSSN | PUSH 0 SSSSN | PUSH 0 NSTTSTSTTN | JSR > 101110 (xor) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 0 xor 1 = 1 SSSSN | PUSH 0 SSSTN | PUSH +1 NSTTSTSTTN | JSR > 101110 (xor) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1 xor 1 = 0 SSSTN | PUSH +1 SSSTN | PUSH +1 NSTTSTSTTN | JSR > 101110 (xor) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1 xor 1...1 = 1...10 SSSTN | PUSH +1 SSTTN | PUSH -1 NSTTSTSTTN | JSR > 101110 (xor) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test 1010...1010 xor 0101...0101 = 1...1 @ Note that -6148914691236517206 = 1010...1010 in twos-complement but we @@ -28,10 +28,10 @@ NSTTSSTN | JSR > 1001 (print number from stack) SSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTTSN | PUSH -6148914691236517206 SSSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTN | PUSH +6148914691236517205 NSTTSTSTTN | JSR > 101110 (xor) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ All done. NNN | DIE #include -#include +#include diff --git a/stdlib_tests/4001_strlen.pvvs b/stdlib_tests/4001_strlen.pvvs index 7c0e0af..6878370 100644 --- a/stdlib_tests/4001_strlen.pvvs +++ b/stdlib_tests/4001_strlen.pvvs @@ -5,10 +5,10 @@ NSTTTTTTN | JSR > 11111 (spew) SSSTSSSSSN | PUSH 32 (address) NSTTSSSSSN | JSR > 100000 (strlen) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) NNN | DIE -#include +#include #include #include diff --git a/stdlib_tests/5001_abs.pvvs b/stdlib_tests/5001_abs.pvvs index 7898e56..daf70ca 100644 --- a/stdlib_tests/5001_abs.pvvs +++ b/stdlib_tests/5001_abs.pvvs @@ -1,17 +1,17 @@ @ +1 should remain +1 SSSTN | PUSH +1 NSTTSSSTN | JSR > 10001 (abs) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ -1 should become +1 SSTTN | PUSH -1 NSTTSSSTN | JSR > 10001 (abs) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ 0 should remain 0 SSSSN | PUSH 0 NSTTSSSTN | JSR > 10001 (abs) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) @ Test the most negative number. @ It is impossible to return the true absolute value but should return zero. @@ -19,8 +19,9 @@ SSSTN | PUSH +1 SSSTTTTTTN | PUSH +63 NSTTSTTSTN | JSR > 101101 (lshift) NSTTSSSTN | JSR > 10001 (abs) -NSTTSSTN | JSR > 1001 (print number from stack) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) NNN | DIE +#include #include -#include +#include -- 2.20.1 From 08a3a286a6602752c2818e86f65d92fa35b806e3 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Sun, 8 Sep 2019 05:53:11 -0700 Subject: [PATCH 03/16] Added test for all stdio.pvvs subroutines. --- stdlib/stdio.pvvs | 2 +- stdlib_tests/6001_printstackstring.pvvs | 5 +++++ stdlib_tests/6002_printheapstring.pvvs | 12 +++++++++++ stdlib_tests/6003_printnumbersign.pvvs | 9 +++++++++ stdlib_tests/6004_printnumbermagnitude.pvvs | 15 ++++++++++++++ stdlib_tests/6005_printstacknumber.pvvs | 15 ++++++++++++++ .../6006_printf_staticstackstring.pvvs | 7 +++++++ .../6007_printf_staticheapstring.pvvs | 14 +++++++++++++ .../6008_printf_escapedstackstring.pvvs | 7 +++++++ .../6009_printf_substitutedstackstring.pvvs | 20 +++++++++++++++++++ stdlib_tests/vv_test.py | 9 +++++++++ 11 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 stdlib_tests/6001_printstackstring.pvvs create mode 100644 stdlib_tests/6002_printheapstring.pvvs create mode 100644 stdlib_tests/6003_printnumbersign.pvvs create mode 100644 stdlib_tests/6004_printnumbermagnitude.pvvs create mode 100644 stdlib_tests/6005_printstacknumber.pvvs create mode 100644 stdlib_tests/6006_printf_staticstackstring.pvvs create mode 100644 stdlib_tests/6007_printf_staticheapstring.pvvs create mode 100644 stdlib_tests/6008_printf_escapedstackstring.pvvs create mode 100644 stdlib_tests/6009_printf_substitutedstackstring.pvvs diff --git a/stdlib/stdio.pvvs b/stdlib/stdio.pvvs index 8637d67..2b77d1d 100644 --- a/stdlib/stdio.pvvs +++ b/stdlib/stdio.pvvs @@ -259,7 +259,7 @@ TSST | SUBTRACT NTSSSSSTSSSSSSSSTSSN | BRZ > 00001000 00000100 @ Check for ASCII '\t' SNS | DUP -SSSTSSTN | PUSH ASCII '\t' +SSSTTTSTSSN | PUSH ASCII 't' TSST | SUBTRACT NTSSSSSTSSSSSSSSTSTN | BRZ > 00001000 00000101 @ No substitution necessary. Print literally. diff --git a/stdlib_tests/6001_printstackstring.pvvs b/stdlib_tests/6001_printstackstring.pvvs new file mode 100644 index 0000000..05656bc --- /dev/null +++ b/stdlib_tests/6001_printstackstring.pvvs @@ -0,0 +1,5 @@ +A"test" +NSTTSSSTSSN | JSR > 1000100 (printstackstring) +NNN | DIE + +#include diff --git a/stdlib_tests/6002_printheapstring.pvvs b/stdlib_tests/6002_printheapstring.pvvs new file mode 100644 index 0000000..68e4942 --- /dev/null +++ b/stdlib_tests/6002_printheapstring.pvvs @@ -0,0 +1,12 @@ +A"test" +SSSTSSSSSN | PUSH 32 (address) +SSSTSSN | PUSH 4 (count) +NSTTTTTTN | JSR > 11111 (spew) + +SSSTSSSSSN | PUSH 32 (address) +NSTTSSSTSTN | JSR > 1000101 (printheapstring) + +NNN | DIE + +#include +#include diff --git a/stdlib_tests/6003_printnumbersign.pvvs b/stdlib_tests/6003_printnumbersign.pvvs new file mode 100644 index 0000000..5d27555 --- /dev/null +++ b/stdlib_tests/6003_printnumbersign.pvvs @@ -0,0 +1,9 @@ +SSSTSSSSSN | PUSH +32 +NSTTSSSSTSN | JSR > 1000010 (printnumbersign) + +SSTTSSSSSN | PUSH -32 +NSTTSSSSTSN | JSR > 1000010 (printnumbersign) + +NNN | DIE + +#include diff --git a/stdlib_tests/6004_printnumbermagnitude.pvvs b/stdlib_tests/6004_printnumbermagnitude.pvvs new file mode 100644 index 0000000..ad7bd8e --- /dev/null +++ b/stdlib_tests/6004_printnumbermagnitude.pvvs @@ -0,0 +1,15 @@ +SSSTSSSSSN | PUSH +32 +NSTTSSSSTTN | JSR > 1000011 (printnumbermagnitude) + +SSTTSSSSSN | PUSH -32 +NSTTSSSSTTN | JSR > 1000011 (printnumbermagnitude) + +SSTSN | PUSH -0 +NSTTSSSSTTN | JSR > 1000011 (printnumbermagnitude) + +SSTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSN | -(2^63) +NSTTSSSSTTN | JSR > 1000011 (printnumbermagnitude) + +NNN | DIE + +#include diff --git a/stdlib_tests/6005_printstacknumber.pvvs b/stdlib_tests/6005_printstacknumber.pvvs new file mode 100644 index 0000000..b6e88ed --- /dev/null +++ b/stdlib_tests/6005_printstacknumber.pvvs @@ -0,0 +1,15 @@ +SSSTSSSSSN | PUSH +32 +NSTTSSTN | JSR > 1001 (printstacknumber) + +SSTTSSSSSN | PUSH -32 +NSTTSSTN | JSR > 1001 (printstacknumber) + +SSTSN | PUSH -0 +NSTTSSTN | JSR > 1001 (printstacknumber) + +SSTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSN | -(2^63) +NSTTSSTN | JSR > 1001 (printstacknumber) + +NNN | DIE + +#include diff --git a/stdlib_tests/6006_printf_staticstackstring.pvvs b/stdlib_tests/6006_printf_staticstackstring.pvvs new file mode 100644 index 0000000..fe82476 --- /dev/null +++ b/stdlib_tests/6006_printf_staticstackstring.pvvs @@ -0,0 +1,7 @@ +A"test" +SSSSN | PUSH 0 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) + +NNN | DIE + +#include diff --git a/stdlib_tests/6007_printf_staticheapstring.pvvs b/stdlib_tests/6007_printf_staticheapstring.pvvs new file mode 100644 index 0000000..5d9b7b1 --- /dev/null +++ b/stdlib_tests/6007_printf_staticheapstring.pvvs @@ -0,0 +1,14 @@ +A"test" +SSSTSSSSSN | PUSH 32 (address) +SSSTSSN | PUSH 4 (count) +NSTTTTTTN | JSR > 11111 (spew) + +SSSTSSSSSN | PUSH 32 (address) +SSSSN | PUSH 0 (string terminator) +SSSSN | PUSH 0 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) + +NNN | DIE + +#include +#include diff --git a/stdlib_tests/6008_printf_escapedstackstring.pvvs b/stdlib_tests/6008_printf_escapedstackstring.pvvs new file mode 100644 index 0000000..721f943 --- /dev/null +++ b/stdlib_tests/6008_printf_escapedstackstring.pvvs @@ -0,0 +1,7 @@ +A"\\\%\t\n" +SSSSN | PUSH 0 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) + +NNN | DIE + +#include diff --git a/stdlib_tests/6009_printf_substitutedstackstring.pvvs b/stdlib_tests/6009_printf_substitutedstackstring.pvvs new file mode 100644 index 0000000..4b1dbef --- /dev/null +++ b/stdlib_tests/6009_printf_substitutedstackstring.pvvs @@ -0,0 +1,20 @@ +@ First setup a string on the heap for use as a substitution. +A"test" +SSSTSSSSSN | PUSH 32 (address) +SSSTSSN | PUSH 4 (count) +NSTTTTTTN | JSR > 11111 (spew) + +@ Now prepare arguments and call printf. +A"%c%s%d%u%i" +SSSTSTSTSN | PUSH +42 +SSSTSTSTSN | PUSH +42 +SSSTN | PUSH +1 +SSSTSSSSSN | PUSH +32 (address) +SSSTSSSSSTN | PUSH ASCII 'A' +SSSTSTN | PUSH 5 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) + +NNN | DIE + +#include +#include diff --git a/stdlib_tests/vv_test.py b/stdlib_tests/vv_test.py index d3420f1..bd6aeca 100755 --- a/stdlib_tests/vv_test.py +++ b/stdlib_tests/vv_test.py @@ -39,6 +39,15 @@ tests = [ ['4001_strlen', '', '+11'], ['5001_abs', '', '+1+1+0+0'], ['5002_random', '', ''], + ['6001_printstackstring', '', 'test'], + ['6002_printheapstring', '', 'test'], + ['6003_printnumbersign', '', '+-'], + ['6004_printnumbermagnitude', '', '323209223372036854775808'], + ['6005_printstacknumber', '', '+32-32+0-9223372036854775808'], + ['6006_printf_staticstackstring', '', 'test'], + ['6007_printf_staticheapstring', '', 'test'], + ['6008_printf_escapedstackstring', '', '\\%\t\n'], + ['6009_printf_substitutedstackstring', '', 'Atest142+42'], ] for test in tests: -- 2.20.1 From 37372ed0cccf20298fdefc4c3d1153eb58407e45 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Wed, 11 Dec 2019 17:03:23 -0800 Subject: [PATCH 04/16] Added `gcd` function to stdlib. --- stdlib/README.md | 1 + stdlib/math.pvvs | 60 ++++++++++++++++++++++++++++++++++++++ stdlib_tests/5003_gcd.pvvs | 41 ++++++++++++++++++++++++++ stdlib_tests/vv_test.py | 1 + 4 files changed, 103 insertions(+) create mode 100644 stdlib_tests/5003_gcd.pvvs diff --git a/stdlib/README.md b/stdlib/README.md index 99f3e69..8427f53 100644 --- a/stdlib/README.md +++ b/stdlib/README.md @@ -42,6 +42,7 @@ header comment for each function to learn the call and return stack. 010xxx - math functions 10000 ----- random (math.pvvs) 10001 ----- absolute value (math.pvvs) + 10010 ----- greatest common divisor (math.pvvs) 011xxx - heap functions 11000 ----- memset (heap.pvvs) 11001 ----- memcpy (heap.pvvs) diff --git a/stdlib/math.pvvs b/stdlib/math.pvvs index 3876c6e..742522e 100644 --- a/stdlib/math.pvvs +++ b/stdlib/math.pvvs @@ -72,4 +72,64 @@ SNN | DROP SSSSN | PUSH 0 NTN | RTS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ gcd (10010) +@ Description: +@ Returns greatest common divisor of X and Y. +@ Call Stack: +@ Y +@ X <-- TOS +@ Return Stack: +@ gcd(X,Y) <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTSN | Mark: 10010 (gcd) + +@ Since 1 > -1, transform problem into gcd(abs(X),abs(Y)). +SNT | SWAP +NSTTSSSTN | JSR > 10001 (abs) +SNT | SWAP +NSTTSSSTN | JSR > 10001 (abs) + +@ Verify neither operand is zero. +SNT | SWAP +SNS | DUP +NTSSSSTSSTSSSSSSSSSN | BRZ > 00010010 00000000 (gcd:zero input) +SNT | SWAP +SNS | DUP +NTSSSSTSSTSSSSSSSSSN | BRZ > 00010010 00000000 (gcd:zero input) + +@ Verify X != Y and sort X,Y so the smaller is TOS. +SNS | DUP +SSSTTN | PUSH 3 +NSTTTSSN | JSR > 1100 (deepdup) +SNT | SWAP +TSST | SUBTRACT +@ TOS> Y-X, X, Y +NTTSSSTSSTSSSSSSSSTN | BMI > 00010010 00000001 (gcd:swap inputs) +NSNSSSTSSTSSSSSSSTSN | JMP > 00010010 00000010 (gcd:main loop) +NSSVSSSTSSTSSSSSSSSTN | MARK: 00010010 00000001 (gcd:swap inputs) +SNT | SWAP + +@ Main gcd loop. +@ Euclidean algorithm. +NSSVSSSTSSTSSSSSSSTSN | MARK: 00010010 00000010 (gcd:main loop) +SNS | DUP +SSSTTN | PUSH 3 +NSTTSTSN | JSR > 1010 (stackrotate) +TSTT | MODULO +SNS | DUP +NTSSSSTSSTSSSSSSSTTN | BRZ > 00010010 00000011 (gcd:loop termination) +NSNSSSTSSTSSSSSSSTSN | JMP > 00010010 00000010 (gcd:main loop) +NSSVSSSTSSTSSSSSSSTTN | MARK: 00010010 00000011 (gcd:loop termination) +SNN | DROP +NTN | RTS + +@ At least one operand was zero. +@ Since we define gcd(a,0) = a, return the other operand. +NSSVSSSTSSTSSSSSSSSSN | MARK: 00010010 00000000 (gcd:zero input) +SNN | DROP +NTN | RTS + #endif diff --git a/stdlib_tests/5003_gcd.pvvs b/stdlib_tests/5003_gcd.pvvs new file mode 100644 index 0000000..84be3cf --- /dev/null +++ b/stdlib_tests/5003_gcd.pvvs @@ -0,0 +1,41 @@ +@ Verify gcd(0,0) = 0 +SSSSN | PUSH 0 +SSSSN | PUSH 0 +NSTTSSTSN | JSR > 10010 (math:gcd) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +@ Verify gcd(4,0) = 4 +SSSSN | PUSH 0 +SSSTSSN | PUSH 4 +NSTTSSTSN | JSR > 10010 (math:gcd) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +@ Verify gcd(0,4) = 4 +SSSTSSN | PUSH 4 +SSSSN | PUSH 0 +NSTTSSTSN | JSR > 10010 (math:gcd) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +@ Verify gcd(6,9) = 3 +SSSTSSTN | PUSH 9 +SSSTTSN | PUSH 6 +NSTTSSTSN | JSR > 10010 (math:gcd) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +@ Verify gcd(-6,9) = 3 +SSSTSSTN | PUSH 9 +SSTTTSN | PUSH -6 +NSTTSSTSN | JSR > 10010 (math:gcd) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +@ Verify gcd(-9,6) = 3 +SSTTSSTN | PUSH -9 +SSSTTSN | PUSH 6 +NSTTSSTSN | JSR > 10010 (math:gcd) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + + +NNN | DIE + +#include +#include diff --git a/stdlib_tests/vv_test.py b/stdlib_tests/vv_test.py index bd6aeca..b4f0cd0 100755 --- a/stdlib_tests/vv_test.py +++ b/stdlib_tests/vv_test.py @@ -39,6 +39,7 @@ tests = [ ['4001_strlen', '', '+11'], ['5001_abs', '', '+1+1+0+0'], ['5002_random', '', ''], + ['5003_gcd', '', '+0+4+4+3+3+3'], ['6001_printstackstring', '', 'test'], ['6002_printheapstring', '', 'test'], ['6003_printnumbersign', '', '+-'], -- 2.20.1 From 760461306c772548a58286fd154c0c4592e5152e Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Wed, 11 Dec 2019 17:03:46 -0800 Subject: [PATCH 05/16] Minor change to example in function description for `deepdup`. --- stdlib/stack.pvvs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/stack.pvvs b/stdlib/stack.pvvs index b6ef808..0403044 100644 --- a/stdlib/stack.pvvs +++ b/stdlib/stack.pvvs @@ -223,8 +223,8 @@ NTN | RTS @ stack word n @ ... @ stack word 1 -@ dupdepth <-- TOS -@ Return Stack: +@ dupdepth <-- TOS +@ Return Stack: (dupdepth=3) @ stack word n @ ... @ stack word 1 -- 2.20.1 From 2da7419427bc470dc1e03504a61f556b10f76c64 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Fri, 3 Jan 2020 09:33:13 -0800 Subject: [PATCH 06/16] Bulk commit of offline work on Hunt the Wumpus. At this point the code builds all relevant data structures for the cave, connecting the rooms with tunnels and populating with bats/pits/wumpus. User interaction progresses through providing the user with instructions, cave information and room information. The code does not yet allow the user to take actions like moving or shooting. --- examples/config.mk | 4 +- examples/wump/LICENSE | 1 - examples/wump/LICENSE.txt | 21 + examples/wump/Makefile | 4 +- examples/wump/README.txt | 83 ++++ examples/wump/wump.info | 41 -- examples/wump/wump.pvvs | 36 ++ examples/wump/wump_conf.pvvs | 51 +++ examples/wump/wump_game.pvvs | 337 ++++++++++++++ examples/wump/wump_init.pvvs | 757 ++++++++++++++++++++++++++++++++ examples/wump/wump_strings.pvvs | 33 +- examples/wump/wump_ui.pvvs | 255 +++++++++++ stdlib/stdio.pvvs | 1 + vv_interpreter.c | 8 +- 14 files changed, 1577 insertions(+), 55 deletions(-) delete mode 100644 examples/wump/LICENSE create mode 100644 examples/wump/LICENSE.txt create mode 100644 examples/wump/README.txt delete mode 100644 examples/wump/wump.info create mode 100644 examples/wump/wump.pvvs create mode 100644 examples/wump/wump_conf.pvvs create mode 100644 examples/wump/wump_game.pvvs create mode 100644 examples/wump/wump_init.pvvs create mode 100644 examples/wump/wump_ui.pvvs diff --git a/examples/config.mk b/examples/config.mk index 19bc033..bcc44c6 100644 --- a/examples/config.mk +++ b/examples/config.mk @@ -1,5 +1,5 @@ # (c) 2019 Aaron Taylor -# All rights reserved. +# See LICENSE.txt file for copyright and license details. #################################################################################################### # Configuration @@ -8,4 +8,4 @@ VVS_COMPILER = ../../vvc VVS_INTERPRETER = ../../vvi CPP = cpp -CPP_FLAGS = -I../../stdlib +CPP_FLAGS = -I../../stdlib -I. diff --git a/examples/wump/LICENSE b/examples/wump/LICENSE deleted file mode 100644 index 5de269e..0000000 --- a/examples/wump/LICENSE +++ /dev/null @@ -1 +0,0 @@ -TODO: Import the correct license for `wump.info` and for text strings from `wump.c`. diff --git a/examples/wump/LICENSE.txt b/examples/wump/LICENSE.txt new file mode 100644 index 0000000..d6b0400 --- /dev/null +++ b/examples/wump/LICENSE.txt @@ -0,0 +1,21 @@ +MIT/X Consortium License + +© 2018 Aaron Taylor + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/examples/wump/Makefile b/examples/wump/Makefile index 079f0a3..1fec45e 100644 --- a/examples/wump/Makefile +++ b/examples/wump/Makefile @@ -1,12 +1,12 @@ # (c) 2019 Aaron Taylor -# All rights reserved. +# See LICENSE.txt file for copyright and license details. include ../config.mk all: wump wump: - $(CPP) $(CPP_FLAGS) -o temp.pvvs wump_strings.pvvs + $(CPP) $(CPP_FLAGS) -o temp.pvvs wump.pvvs $(VVS_COMPILER) -i temp.pvvs -o wump.vvs @rm -f temp.pvvs diff --git a/examples/wump/README.txt b/examples/wump/README.txt new file mode 100644 index 0000000..78f7783 --- /dev/null +++ b/examples/wump/README.txt @@ -0,0 +1,83 @@ +TODO LIST: + + - Update README + - Write functions for moving wumpus, player movement, player shooting, parsing player commands. + - Write a function 'get highest tunnel index' for use in the 'check for pits/bats/wumpus' functions. + - Double-check per-file TODOs for critical tasks. + - Double-check #includes for all subroutines. + - Re-order subroutines in all files for readability. + +================================================================================ + +# Subroutine Labels # + +NSSVTTTTTTTTSSSSSSSSN | Mark: 11111111 00000000 (wump_kill) +NSSVTTTTTTTTSSSSSSSTN | Mark: 11111111 00000001 (kill_wump) +NSSVTTTTTTTTSSSSSSTSN | Mark: 11111111 00000010 (no_arrows) +NSSVTTTTTTTTSSSSSSTTN | Mark: 11111111 00000011 (shoot_self) +NSSVTTTTTTTTSSSSSTSSN | Mark: 11111111 00000100 (jump) +NSSVTTTTTTTTSSSSSTSTN | Mark: 11111111 00000101 (pit_kill) +NSSVTTTTTTTTSSSSSTTSN | Mark: 11111111 00000110 (pit_survive) +NSSVTTTTTTTTSSSSSTTTN | Mark: 11111111 00000111 (please_seed_rng) +NSSVTTTTTTTTSSSSTSSSN | Mark: 11111111 00001000 (problem_with_yes_no_answer) +NSSVTTTTTTTTSSSSTSSTN | Mark: 11111111 00001001 (instructions) +NSSVTTTTTTTTSSSSTSTSN | Mark: 11111111 00001010 (cave_description) + +NSSVTSSSSSSSN | MARK: 10000000 (wump) +NSSVTSSSSSSTN | MARK: 10000001 (set_config_values) +NSSVTSSSSSTSN | MARK: 10000010 (wump_init) +NSSVTSSSSSTTN | MARK: 10000011 (seed_rng) +NSSVTSSSSTSSN | MARK: 10000100 (build_cave) +NSSVTSSSSTSTN | MARK: 10000101 (populate_cave) +NSSVTSSSSTTSN | MARK: 10000110 (clear_cave_data) +NSSVTSSSSTTTN | MARK: 10000111 (generate_cave_hop_length) +NSSVTSSSTSSSN | MARK: 10001000 (get_tunnel_destination) +NSSVTSSSTSSTN | MARK: 10001001 (set_tunnel_destination) +NSSVTSSSTSTSN | MARK: 10001010 (get_random_room) +NSSVTSSSTSTTN | MARK: 10001011 (get_room_struct_size) +NSSVTSSSTTSSN | MARK: 10001100 (room_has_bats) +NSSVTSSSTTSTN | MARK: 10001101 (room_has_pits) +NSSVTSSSTTTSN | MARK: 10001110 (build_circular_tunnels) +NSSVTSSTSSSSN | MARK: 10010000 (set_bats) +NSSVTSSTSSSTN | MARK: 10010001 (set_pits) +NSSVTSSTSSTSN | MARK: 10010010 (reset_cave_population) +NSSVTSSTSSTTN | MARK: 10010011 (populate_bats) +NSSVTSSTSTSSN | MARK: 10010100 (populate_pits) +NSSVTSSTSTSTN | MARK: 10010101 (place_player) +NSSVTSSTSTTSN | MARK: 10010110 (build_random_tunnels) +NSSVTSSTSTTTN | MARK: 10010111 (get_next_tunnel_slot) +NSSVTSSTTSSSN | MARK: 10011000 (get_answer) +NSSVTSSTTSSTN | MARK: 10011001 (get_line) +NSSVTSSTTSTSN | MARK: 10011010 (wump_loop) +NSSVTSSTTSTTN | MARK: 10011011 (print_cave_description) +NSSVTSSTTTSSN | MARK: 10011100 (are_bats_near) +NSSVTSSTTTSTN | MARK: 10011101 (are_pits_near) +NSSVTSSTTTTSN | MARK: 10011110 (is_wumpus_near) +NSSVTSSTTTTTN | MARK: 10011111 (is_wumpus_very_near) +NSSVTSTSSSSSN | MARK: 10100000 (room_has_wumpus) +NSSVTSTSSSSTN | MARK: 10100001 (print_room_stats) + +================================================================================ + +# Heap Assignments # + +GAME_DATA_BASE = 0x1000 + +GAME_DATA_BASE = Number of rooms in cave +GAME_DATA_BASE+001 = Number of pits +GAME_DATA_BASE+002 = Number of bats +GAME_DATA_BASE+003 = Number of links per room +GAME_DATA_BASE+004 = Maximum arrow flight distance +GAME_DATA_BASE+005 = Number of arrows +GAME_DATA_BASE+006 = Player location +GAME_DATA_BASE+007 = Wumpus location + +ROOM_DATA_BASE = 0x2000 + Each room is structured as: + BASE+0 = bool:contains_pit? (valid options are 0 or 1) + BASE+1 = bool:contains_bat? (valid options are 0 or 1) + BASE+2 = int:connected room (-1 indicates no tunnel, 0 or greater is the index of the destination room) + ... + BASE+n = int:connected room (last: see 'links per room') + +USER_INPUT_BUFFER = 0x3000 diff --git a/examples/wump/wump.info b/examples/wump/wump.info deleted file mode 100644 index 329e821..0000000 --- a/examples/wump/wump.info +++ /dev/null @@ -1,41 +0,0 @@ -Welcome to the game of Hunt the Wumpus. - -The Wumpus typically lives in a cave of twenty rooms, with each room having -three tunnels connecting it to other rooms in the cavern. Caves may vary, -however, depending on options specified when starting the game. - -The game has the following hazards for intrepid adventurers to wind their -way through: - - Pits -- If you fall into one of the bottomless pits, you find yourself - slung back out on the far side of the Earth and in very poor - shape to continue your quest since you're dead. - - Bats -- As with any other cave, the Wumpus cave has bats in residence. - These are a bit more potent, however, and if you stumble into - one of their rooms they will rush up and carry you elsewhere in - the cave. - - Wumpus -- If you happen to walk into the room the Wumpus is in you'll find - that he has quite an appetite for young adventurous humans! Not - recommended. - -The Wumpus, by the way, is not bothered by the hazards since he has sucker -feet and is too big for a bat to lift. If you try to shoot him and miss, -there's also a chance that he'll up and move himself into another cave, -though by nature the Wumpus is a sedentary creature. - -Each turn you may either move or shoot a crooked arrow. Moving is done -simply by specifying "m" for move and the number of the room that you'd -like to move down a tunnel towards. Shooting is done similarly; indicate -that you'd like to shoot one of your magic arrows with an "s" for shoot, -then list a set of connected room numbers through which the deadly shaft -should fly! - -If your path for the arrow is incorrect, however, it will flail about in -the room it can't understand and randomly pick a tunnel to continue -through. You might just end up shooting yourself in the foot if you're -not careful! On the other hand, if you shoot the Wumpus you've WON! - -Good luck. - diff --git a/examples/wump/wump.pvvs b/examples/wump/wump.pvvs new file mode 100644 index 0000000..d5c5079 --- /dev/null +++ b/examples/wump/wump.pvvs @@ -0,0 +1,36 @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Hunt the Wumpus +@ (c) 2019 Aaron Taylor +@ See LICENSE.txt file for copyright and license details. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +NSNTSSSSSSSN | JMP > 10000000 (wump) + +#include +#include +#include +#include + +NSSVTSSSSSSSN | MARK: 10000000 (wump) + +@ Initialization (build the cave, etc). +NSTTSSSSSTSN | JSR > 10000010 (wump_init) + +@ Does the user need instructions? +A"Instructions? (y/n)\n" +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSTTSSTTSSSN | JSR > 10011000 (get_answer) +NTSTSSSSSSSSSSSSSSSN | BRZ > 10000000 00000000 (skip_instructions) +NSTTTTTTTTTSSSSTSSTN | JSR > 11111111 00001001 (instructions) +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSSVTSSSSSSSSSSSSSSSN | MARK: 10000000 00000000 (skip_instructions) + +@ Print a description of the cave. +NSTTSSTTSTTN | JSR > 10011011 (print_cave_description) + +@ Main game loop +NSSVTSSTTSTSN | MARK: 10011010 (wump_loop) +NSTTSTSSSSTN | JSR > 10100001 (print_room_stats) +NNN | TERMINATE diff --git a/examples/wump/wump_conf.pvvs b/examples/wump/wump_conf.pvvs new file mode 100644 index 0000000..d4bba75 --- /dev/null +++ b/examples/wump/wump_conf.pvvs @@ -0,0 +1,51 @@ +#ifndef WUMP_CONF +#define WUMP_CONF + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ (c) 2019 Aaron Taylor +@ See LICENSE.txt file for copyright and license details. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ All user configurable options are contained in this file. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ set_config_values +@ Description: +@ Place user-configurable values for Hunt the Wumpus on the heap. +@ The program assumes the values are sane. (e.g. #pits < #rooms, etc) +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSSSSSTN | MARK: 10000001 (set_config_values) + +@ Number of arrows +SSSTSTN | PUSH +5 + +@ Maximum arrow travel distance +SSSTSTN | PUSH +5 + +@ Maximum links per room (must be >2) +SSSTTN | PUSH +3 + +@ Number of bats in cave +SSSTSN | PUSH +2 + +@ Number of pits in cave +SSSTSN | PUSH +2 + +@ Number of rooms in cave +SSSTSTSSN | PUSH +20 + +@ Push config values to heap and return. +SSSTSSSSSSSSSSSSN | PUSH 0x1000 (GAME_DATA_BASE address) +SSSTSTN | PUSH +5 (word count) +NSTTTTTTN | JSR > 11111 (spew) +NTN | RTS + +#endif diff --git a/examples/wump/wump_game.pvvs b/examples/wump/wump_game.pvvs new file mode 100644 index 0000000..7d85f8d --- /dev/null +++ b/examples/wump/wump_game.pvvs @@ -0,0 +1,337 @@ +#ifndef WUMP_GAME +#define WUMP_GAME + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ (c) 2019 Aaron Taylor +@ See LICENSE.txt file for copyright and license details. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ get_tunnel_destination +@ Description: +@ Returns the room number corresponding to the destination of a tunnel +@ specified by room and slot. +@ Call Stack: +@ slot +@ room_number <-- TOS +@ Return Stack: +@ dst_room_number <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSSTSSSN | MARK: 10001000 (get_tunnel_destination) +@ The pointer we seek is: +@ (room_number * room_struct_size) + 2 + slot + ROOM_DATA_BASE +@ Where the '+2' accounts for the pit and bat booleans. +NSTTSSSTSTTN | JSR > 10001011 (get_room_struct_size) +TSSN | MULTIPLY +SSSTSN | PUSH +2 +TSSS | ADD +TSSS | ADD +SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (ROOM_DATA_BASE address) +TSSS | ADD +TTT | LOAD +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ get_room_struct_size +@ Description: +@ Returns the size in words of the data structure for a single room. +@ For example, with 3 links plus bat and pit booleans, the size is 5 words. +@ Call Stack: +@ +@ Return Stack: +@ size <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSSTSTTN | MARK: 10001011 (get_room_struct_size) +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (GAME_DATA_BASE+3 = links_per_room address) +TTT | LOAD +SSSTSN | PUSH +2 +TSSS | ADD +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ room_has_bats +@ Description: +@ Check if room_number contains bats. +@ Returns 1 or 0 representing true or false. +@ Call Stack: +@ room_number <-- TOS +@ Return Stack: +@ 1 or 0 <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSSTTSSN | MARK: 10001100 (room_has_bats) +@ We seek the pointer: +@ (room_number * room_struct_size) + 1 + ROOM_DATA_BASE +@ where '+1' accounts for the offset of the bat boolean in the desired room. +NSTTSSSTSTTN | JSR > 10001011 (get_room_struct_size) +TSSN | MULTIPLY +SSSTN | PUSH +1 +TSSS | ADD +SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (GAME_DATA_BASE address) +TSSS | ADD +TTT | LOAD +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ room_has_pits +@ Description: +@ Check if room_number contains pits. +@ Returns 1 or 0 representing true or false. +@ Call Stack: +@ room_number <-- TOS +@ Return Stack: +@ 1 or 0 <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSSTTSTN | MARK: 10001101 (room_has_pits) +@ We seek the pointer: +@ (room_number * room_struct_size) + 0 + ROOM_DATA_BASE +@ where '+0' accounts for the offset of the pit boolean in the desired room. +NSTTSSSTSTTN | JSR > 10001011 (get_room_struct_size) +TSSN | MULTIPLY +SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (GAME_DATA_BASE address) +TSSS | ADD +TTT | LOAD +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ room_has_wumpus +@ Description: +@ Check if room contains wumpus. +@ Returns 1 or 0 representing true or false. +@ Call Stack: +@ room_number <-- TOS +@ Return Stack: +@ 1 or 0 <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSTSSSSSN | MARK: 10100000 (room_has_wumpus) +SSSTSSSSSSSSSTTTN | PUSH 0x1007 (wumpus_location address) +TTT | LOAD +TSST | SUBTRACT +NTSTSTSSSSSSSSSSSSSN | BRZ > 10100000 00000000 (room_has_wumpus:true) +SSSSN | PUSH 0 (false) +NTN | RTS +NSSVTSTSSSSSSSSSSSSSN | MARK: 10100000 00000000 (room_has_wumpus:true) +SSSTN | PUSH 1 (true) +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ are_bats_near +@ Description: +@ Given a room number, checks rooms within one hop for bats. +@ Returns 1 if bats are present or 0 if no bats. +@ Call Stack: +@ room_number <-- TOS +@ Return Stack: +@ 1 or 0 <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTTTSSN | MARK: 10011100 (are_bats_near) + +@ Prepare the stack by loading the number of links per room and decrementing. +@ We will loop until this reaches 0 or we find bats. +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (number_of_links_per_room address) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT + +@ Check one nearby room on each pass through this loop. +@ TOS> tunnel_index, room_number +NSSVTSSTTTSSSSSSSSSSN | MARK: 10011100 00000000 (are_bats_near:loop) +SNS | DUP +SSSTTN | PUSH 3 +NSTTTSSN | JSR > 1100 (deepdup) +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +NSTTSSSTTSSN | JSR > 10001100 (room_has_bats) +NTSTSSTTTSSSSSSSSSTN | BRZ > 10011100 00000001 (no_bats_in_this_room) +@ Found bats. Clean up and return. +SNN | DROP +SNN | DROP +SSSTN | PUSH 1 +NTN | RTS +NSSVTSSTTTSSSSSSSSSTN | MARK: 10011100 00000001 (no_bats_in_this_room) +@ Test for end of loop. +SNS | DUP +NTSTSSTTTSSSSSSSSTSN | BRZ > 10011100 00000010 (are_bats_near:loop_end) +@ No bats found yet, but still need to check some rooms. +@ Decrement tunnel index and loop again. +SSSTN | PUSH 1 +TSST | SUBTRACT +NSNTSSTTTSSSSSSSSSSN | JMP > 10011100 00000000 (are_bats_near:loop) +@ No bats found in nearby rooms. Clean up and return. +NSSVTSSTTTSSSSSSSSTSN | MARK: 10011100 00000010 (are_bats_near:loop_end) +SNN | DROP +SNN | DROP +SSSSN | PUSH 0 +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ are_pits_near +@ Description: +@ Given a room number, checks rooms within one hop for pits. +@ Returns 1 if pits are present or 0 if no pits. +@ Call Stack: +@ room_number <-- TOS +@ Return Stack: +@ 1 or 0 <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTTTSTN | MARK: 10011101 (are_pits_near) + +@ Prepare the stack by loading the number of links per room and decrementing. +@ We will loop until this reaches 0 or we find pits. +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (number_of_links_per_room address) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT + +@ Check one nearby room on each pass through this loop. +@ TOS> tunnel_index, room_number +NSSVTSSTTTSTSSSSSSSSN | MARK: 10011101 00000000 (are_pits_near:loop) +SNS | DUP +SSSTTN | PUSH 3 +NSTTTSSN | JSR > 1100 (deepdup) +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +NSTTSSSTTSTN | JSR > 10001101 (room_has_pits) +NTSTSSTTTSTSSSSSSSTN | BRZ > 10011101 00000001 (no_pits_in_this_room) +@ Found pits. Clean up and return. +SNN | DROP +SNN | DROP +SSSTN | PUSH 1 +NTN | RTS +NSSVTSSTTTSTSSSSSSSTN | MARK: 10011101 00000001 (no_pits_in_this_room) +@ Test for end of loop. +SNS | DUP +NTSTSSTTTSTSSSSSSTSN | BRZ > 10011101 00000010 (are_pits_near:loop_end) +@ No pits found yet, but still need to check some rooms. +@ Decrement tunnel index and loop again. +SSSTN | PUSH 1 +TSST | SUBTRACT +NSNTSSTTTSTSSSSSSSSN | JMP > 10011101 00000000 (are_pits_near:loop) +@ No pits found in nearby rooms. Clean up and return. +NSSVTSSTTTSTSSSSSSTSN | MARK: 10011101 00000010 (are_pits_near:loop_end) +SNN | DROP +SNN | DROP +SSSSN | PUSH 0 +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ is_wumpus_very_near +@ Description: +@ Given a room number, checks rooms within one hop for the wumpus. +@ Returns 1 if wumpus is present, otherwise 0. +@ Call Stack: +@ room_number <-- TOS +@ Return Stack: +@ 1 or 0 <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTTTTTN | MARK: 10011111 (is_wumpus_very_near) + +@ Prepare the stack by loading the number of links per room and decrementing. +@ We will loop until this reaches 0 or we find the wumpus. +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (number_of_links_per_room address) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT + +@ Check one nearby room on each pass through this loop. +@ TOS> tunnel_index, room_number +NSSVTSSTTTTTSSSSSSSSN | MARK: 10011111 00000000 (is_wumpus_very_near:loop) +SNS | DUP +SSSTTN | PUSH 3 +NSTTTSSN | JSR > 1100 (deepdup) +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +NSTTSTSSSSSN | JSR > 10100000 (room_has_wumpus) +NTSTSSTTTTTSSSSSSSTN | BRZ > 10011111 00000001 (no_wumpus_in_this_room) +@ Found wumpus. Clean up and return. +SNN | DROP +SNN | DROP +SSSTN | PUSH 1 +NTN | RTS +NSSVTSSTTTTTSSSSSSSTN | MARK: 10011111 00000001 (no_wumpus_in_this_room) +@ Test for end of loop. +SNS | DUP +NTSTSSTTTTTSSSSSSTSN | BRZ > 10011111 00000010 (is_wumpus_very_near:loop_end) +@ No wumpus found yet, but still need to check some rooms. +@ Decrement tunnel index and loop again. +SSSTN | PUSH 1 +TSST | SUBTRACT +NSNTSSTTTTTSSSSSSSSN | JMP > 10011111 00000000 (is_wumpus_very_near:loop) +@ No wumpus found in nearby rooms. Clean up and return. +NSSVTSSTTTTTSSSSSSTSN | MARK: 10011111 00000010 (is_wumpus_very_near:loop_end) +SNN | DROP +SNN | DROP +SSSSN | PUSH 0 +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ is_wumpus_near +@ Description: +@ Given a room number, checks rooms within two hops for the wumpus. +@ Returns 1 if wumpus is present, otherwise 0. +@ Call Stack: +@ room_number <-- TOS +@ Return Stack: +@ 1 or 0 <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTTTTSN | MARK: 10011110 (is_wumpus_near) + +@ Prepare the stack by loading the number of links per room and decrementing. +@ We will loop until this reaches 0 or we find the wumpus. +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (number_of_links_per_room address) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT + +@ Check one nearby room and its connecting rooms on each pass through this loop. +@ TOS> tunnel_index, room_number +NSSVTSSTTTTSSSSSSSSSN | MARK: 10011110 00000000 (is_wumpus_near:loop) +SNS | DUP +SSSTTN | PUSH 3 +NSTTTSSN | JSR > 1100 (deepdup) +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +@ TOS> tunnel_endpoint, tunnel_index, room_number +SNS | DUP +NSTTSTSSSSSN | JSR > 10100000 (room_has_wumpus) +SSSTN | PUSH 1 +TSST | SUBTRACT +NTSTSSTTTTSSSSSSSTTN | BRZ > 10011110 00000011 (found_wumpus_one_hop) +NSTTSSTTTTTN | JSR > 10011111 (is_wumpus_very_near) +SSSTN | PUSH 1 +TSST | SUBTRACT +NTSTSSTTTTSSSSSSTSSN | BRZ > 10011110 00000100 (found_wumpus_two_hops) +@ Test for end of loop. +SNS | DUP +NTSTSSTTTTSSSSSSSTSN | BRZ > 10011110 00000010 (is_wumpus_near:loop_end) +@ No wumpus found yet, but still need to check some rooms. +@ Decrement tunnel index and loop again. +SSSTN | PUSH 1 +TSST | SUBTRACT +NSNTSSTTTTSSSSSSSSSN | JMP > 10011110 00000000 (is_wumpus_near:loop) + +@ No wumpus found in nearby rooms. Clean up and return. +NSSVTSSTTTTSSSSSSSTSN | MARK: 10011110 00000010 (is_wumpus_near:loop_end) +SNN | DROP +SNN | DROP +SSSSN | PUSH 0 +NTN | RTS + +@ Found wumpus. Clean up and return. +NSSVTSSTTTTSSSSSSSTTN | MARK: 10011110 00000011 (found_wumpus_one_hop) +SNN | DROP +NSSVTSSTTTTSSSSSSTSSN | MARK: 10011110 00000100 (found_wumpus_two_hops) +SNN | DROP +SNN | DROP +SSSTN | PUSH 1 +NTN | RTS + +#endif diff --git a/examples/wump/wump_init.pvvs b/examples/wump/wump_init.pvvs new file mode 100644 index 0000000..9d7543d --- /dev/null +++ b/examples/wump/wump_init.pvvs @@ -0,0 +1,757 @@ +#ifndef WUMP_INIT +#define WUMP_INIT + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ (c) 2019 Aaron Taylor +@ See LICENSE.txt file for copyright and license details. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ wump_init +@ Description: +@ Master function that creates a new cave, links the rooms, and populates them +@ according to the options set by the user in wump_conf.pvvs. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +#include +#include +#include +NSSVTSSSSSTSN | MARK: 10000010 (wump_init) + +@ Load the user-configurable options. +NSTTSSSSSSTN | JSR > 10000001 (set_config_values) + +@ Seed the RNG. +NSTTTTTTTTTSSSSSTTTN | JSR > 11111111 00000111 (please_seed_rng) +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSTTSSSSSTTN | JSR > 10000011 (seed_rng) + +@ Build the cave. +NSTTSSSSTSSN | JSR > 10000100 (build_cave) + +@ Populate the cave. +NSTTSSSSTSTN | JSR > 10000101 (populate_cave) + +@ Initialization is complete. +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ build_circular_tunnels +@ Description: +@ Builds a set of tunnels that ensures every room in the cave is connected. +@ Assumes that tunnel slots 0 and 1 are set to the value -1 (unconnected). +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +#include +NSSVTSSSTTTSN | MARK: 10001110 (build_circular_tunnels) + +@ Prepare TOS> current_room, hop_length +NSTTSSSSTTTN | JSR > 10000111 (generate_cave_hop_length) +SSSSN | PUSH 0 (current room) + +@ Have we already built a tunnel from this room? +@ If so, tunnel slot 0 will not be -1. +NSSVTSSSTTTSSSSSSSSSN | MARK: 10001110 00000000 (tunnel_loop) +SNS | DUP +SSSSN | PUSH 0 (first tunnel slot) +SNT | SWAP +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +NTTTSSSTTTSSSSSSSTSN | BMI > 10001110 00000010 +NSNTSSSTTTSSSSSSSSTN | JMP > 10001110 00000001 (loop_complete) +NSSVTSSSTTTSSSSSSSTSN | MARK: 10001110 00000010 +@ No tunnel yet, so start building a tunnel. +@ TOS> current_room, hop_length +SNS | DUP +SNS | DUP +SSSTSSN | PUSH 4 +NSTTTSSN | JSR > 1100 (deepdup) +TSSS | ADD +SSSTSSSSSSSSSSSSN | PUSH 0x1000 (GAME_DATA_BASE+0 = num_rooms address) +TTT | LOAD +TSTT | MODULO +SNT | SWAP +SSSSN | PUSH 0 (slot number) +SNT | SWAP +NSTTSSSTSSTN | JSR > 10001001 (set_tunnel_destination) +@ One direction of the tunnel is built. Now build the other direction. +SNS | DUP +SSSSN | PUSH 0 (slot number) +SNT | SWAP +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +SNT | SWAP +SSSTSN | PUSH 2 +NSTTTSSN | JSR > 1100 (deepdup) +SSSTN | PUSH 1 +SNT | SWAP +NSTTSSSTSSTN | JSR > 10001001 (set_tunnel_destination) +@ Loop again to build the next tunnel. +NSNTSSSTTTSSSSSSSSSN | JMP > 10001110 00000000 (tunnel_loop) + +@ Clean up and return +NSSVTSSSTTTSSSSSSSSTN | MARK: 10001110 00000001 (loop_complete) +SNN | DROP +SNN | DROP +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ get_next_tunnel_slot +@ Description: +@ Returns the index of next available tunnel slot for room_num, or -1 if full. +@ Call Stack: +@ room_num <-- TOS +@ Return Stack: +@ tunnel_index <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTSTTTN | MARK: 10010111 (get_next_tunnel_slot) + +@ Build the pointer: (room_num * size_of_room_struct) + 2 + ROOM_DATA_BASE +@ Where the '+2' skips over the 'pits' and 'bats' booleans for this room. +NSTTSSSTSTTN | JSR > 10001011 (get_room_struct_size) +TSSN | MULTIPLY +SSSTSN | PUSH 2 +TSSS | ADD +SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (ROOM_DATA_BASE address) +TSSS | ADD + +@ Save a copy of this pointer for later use when recovering the tunnel index. +SNS | DUP + +@ Push a loop counter onto the stack. +SSSSN | PUSH 0 (counter) +SNT | SWAP + +@ Main loop - Search for an available tunnel slot. +@ TOS> ptr_to_tunnel_slot, loop_counter, ptr_to_first_tunnel_slot +NSSVTSSTSTTTSSSSSSSSN | MARK: 10010111 00000000 (main_loop) +SNS | DUP +TTT | LOAD +NTTTSSTSTTTSSSSSSSTN | BMI > 10010111 00000001 (found_open_slot) +@ No match. +@ Increment the pointer. +SSSTN | PUSH 1 +TSSS | ADD +@ Increment the loop counter. +SNT | SWAP +SSSTN | PUSH 1 +TSSS | ADD +@ See if we have run out of possible tunnel slots. +SNS | DUP +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (GAME_DATA_BASE+3 = links_per_room address) +TTT | LOAD +TSST | SUBTRACT +NTSTSSTSTTTSSSSSSTSN | BRZ > 10010111 00000010 (no_open_slots) +@ There are still more slots to check. Prepare to loop again. +SNT | SWAP +NSNTSSTSTTTSSSSSSSSN | JMP > 10010111 00000000 (main_loop) + +@ No available tunnel slots for this room. +NSSVTSSTSTTTSSSSSSTSN | MARK: 10010111 00000010 (no_open_slots) +SNN | DROP +SNN | DROP +SNN | DROP +SSTTN | PUSH -1 +NTN | RTS + +@ Found an open tunnel slot. +NSSVTSSTSTTTSSSSSSSTN | MARK: 10010111 00000001 (found_open_slot) +@ Drop the loop counter. +SNT | SWAP +SNN | DROP +@ Recover the tunnel slot index from the tunnel slot pointer. +SNT | SWAP +TSST | SUBTRACT +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ build_random_tunnels +@ Description: +@ Randomly builds tunnels between rooms until all tunnels slots are used. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +#include +#include +NSSVTSSTSTTSN | MARK: 10010110 (build_random_tunnels) + +@ Build a temporary data structure in the buffer residing at 0x3000. +@ +@ 0x3000 contains the number of elements in an array that starts at 0x3001. +@ Each array element is one word long and contains the index of a room +@ with open tunnel slots. +@ +@ This init code is here rather than a subroutine since the structure it builds +@ is private to this function. +@ Before looping, push the number of elements in the array (=num_rooms). +@ Keep a copy of this number to use as a loop counter. +SSSTSSSSSSSSSSSSN | PUSH 0x1000 (GAME_DATA_BASE = num_rooms address) +TTT | LOAD +SNS | DUP +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +SNT | SWAP +TTS | STORE +@ Init loop - Populates the 'rooms with available tunnel slots' array. +@ TOS> loop_counter +NSSVTSSTSTTSSSSSSSTSN | MARK: 10010110 00000010 (init_loop) +@ Populate one of the array entries. +SNS | DUP +SNS | DUP +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TSSS | ADD +SNT | SWAP +SSSTN | PUSH +1 +TSST | SUBTRACT +TTS | STORE +@ Decrement the loop counter and test for end of loop. +SSSTN | PUSH +1 +TSST | SUBTRACT +SNS | DUP +NTSTSSTSTTSSSSSSSTTN | BRZ > 10010110 00000011 (init_loop_end) +NSNTSSTSTTSSSSSSSTSN | JMP > 10010110 00000010 (init_loop) +NSSVTSSTSTTSSSSSSSTTN | MARK: 10010110 00000011 (init_loop_end) +SNN | DROP +NSNTSSTSTTSSSSSSSSTN | JMP > 10010110 00000001 (main_loop) + +@@@@@ INIT IS COMPLETE - STACK IS EMPTY @@@@@ + +@ Build one random tunnel connection per pass through this loop. +NSSVTSSTSTTSSSSSSSSTN | MARK: 10010110 00000001 (main_loop) +@ Use 'random_number mod number_of_array_entries' to select a starting room. +NSTTSSSSN | JSR > 10000 (random) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TTT | LOAD +TSTT | MODULO +SSSTN | PUSH 1 +TSSS | ADD +SNS | DUP +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TSSS | ADD +TTT | LOAD +@ TOS> start_room_num, start_array_index +@ Now we randomly select a room for the endpoint. +NSSVTSSTSTTSSSSSSTSSN | MARK: 10010110 00000100 (select_random_dst_room) +NSTTSSSSN | JSR > 10000 (random) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TTT | LOAD +TSTT | MODULO +SSSTN | PUSH 1 +TSSS | ADD +SNS | DUP +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TSSS | ADD +TTT | LOAD +@ TOS> end_room_num, end_array_index, start_room_num, start_array_index +@ Verify the endpoint room is different from the startpoint room. +SNS | DUP +SSSTSSN | PUSH 4 +NSTTTSSN | JSR > 1100 (deepdup) +TSST | SUBTRACT +NTSTSSTSTTSSSSSSTSTN | BRZ > 10010110 00000101 (rooms_matched) +NSNTSSTSTTSSSSSSTTSN | JMP > 10010110 00000110 (connect_rooms) +@ The rooms matched. Drop the selected destination room and try again. +NSSVTSSTSTTSSSSSSTSTN | MARK: 10010110 00000101 (rooms_matched) +SNN | DROP +SNN | DROP +NSNTSSTSTTSSSSSSTSSN | JMP > 10010110 00000100 (select_random_dst_room) +@ TOS> end_room_num, end_array_index, start_room_num, start_array_index +@ The rooms are different. Time to build a connection between them. +NSSVTSSTSTTSSSSSSTTSN | MARK: 10010110 00000110 (connect_rooms) +@ First build a one-way tunnel. +SSSTTN | PUSH 3 +NSTTTSSN | JSR > 1100 (deepdup) +SSSTSN | PUSH 2 +NSTTTSSN | JSR > 1100 (deepdup) +SNS | DUP +NSTTSSTSTTTN | JSR > 10010111 (get_next_tunnel_slot) +SNT | SWAP +NSTTSSSTSSTN | JSR > 10001001 (set_tunnel_destination) +@ Then make it a two-way tunnel. +SNS | DUP +SSSTSSN | PUSH 4 +NSTTTSSN | JSR > 1100 (deepdup) +SNS | DUP +NSTTSSTSTTTN | JSR > 10010111 (get_next_tunnel_slot) +SNT | SWAP +NSTTSSSTSSTN | JSR > 10001001 (set_tunnel_destination) +@ TOS> end_room_num, end_array_index, start_room_num, start_array_index +@ If that was the last tunnel slot in either room, remove it +@ from the 'rooms_with_available_tunnel_slots' array. +NSTTSSTSTTTN | JSR > 10010111 (get_next_tunnel_slot) +NTTTSSTSTTSSSSSSTTTN | BMI > 10010110 00000111 (remove_empty_room_1) +SNN | DROP +NSNTSSTSTTSSSSSTSSSN | JMP > 10010110 00001000 (check_second_room) +NSSVTSSTSTTSSSSSSTTTN | MARK: 10010110 00000111 (remove_empty_room_1) +SNS | DUP +NSTTSSTSTTSSSSSTTSSN | JSR > 10010110 00001100 (remove_element_from_array) +SSSTTN | PUSH 3 +NSTTTSSN | JSR > 1100 (deepdup) +TSST | SUBTRACT +NTTTSSTSTTSSSSSTTSTN | BMI > 10010110 00001101 (update_second_room_index) +NSNTSSTSTTSSSSSTSSSN | JMP > 10010110 00001000 (check_second_room) +NSSVTSSTSTTSSSSSTTSTN | MARK: 10010110 00001101 (update_second_room_index) +SNT | SWAP +SSSTN | PUSH 1 +TSST | SUBTRACT +SNT | SWAP +NSSVTSSTSTTSSSSSTSSSN | MARK: 10010110 00001000 (check_second_room) +NSTTSSTSTTTN | JSR > 10010111 (get_next_tunnel_slot) +NTTTSSTSTTSSSSSTSSTN | BMI > 10010110 00001001 (remove_empty_room_2) +SNN | DROP +NSNTSSTSTTSSSSSTSTSN | JMP > 10010110 00001010 (main_loop_end_test) +NSSVTSSTSTTSSSSSTSSTN | MARK: 10010110 00001001 (remove_empty_room_2) +NSTTSSTSTTSSSSSTTSSN | JSR > 10010110 00001100 (remove_element_from_array) +@ If the array is now empty or only contains one room, return. +@ Otherwise, loop again and build another random tunnel. +NSSVTSSTSTTSSSSSTSTSN | MARK: 10010110 00001010 (main_loop_end_test) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TTT | LOAD +SSSTSN | PUSH 2 +TSST | SUBTRACT +NTTTSSTSTTSSSSSTSTTN | BMI > 10010110 00001011 (return) +NSNTSSTSTTSSSSSSSSTN | JMP > 10010110 00000001 (main_loop) +NSSVTSSTSTTSSSSSTSTTN | MARK: 10010110 00001011 (return) +NTN | RTS + +@ A private subroutine to remove one element from the temp array. +@ Consumes the array index from TOS. +NSSVTSSTSTTSSSSSTTSSN | MARK: 10010110 00001100 (remove_element_from_array) +@ First, stash a copy of the array index immediately after the temporary array. +@ This allows us to find it using the num_array_elements for use with spew +@ after slurp has filled the stack with a variable number of elements. +SNS | DUP +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TTT | LOAD +TSSS | ADD +SSSTN | PUSH 1 +TSSS | ADD +SNT | SWAP +TTS | STORE +@ Now slurp and spew the array to delete the desired element. +SSSTN | PUSH 1 +TSSS | ADD +SNS | DUP +@ TOS> index+1, index+1 +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TTT | LOAD +SNT | SWAP +TSST | SUBTRACT +@ Special case: deleting last element of array means no need to delete. +SNS | DUP +NTTTSSTSTTSSSSSTTTSN | BMI > 10010110 00001110 (deleting_last_element) +NSNTSSTSTTSSSSSTTTTN | JMP > 10010110 00001111 (not_deleting_last_element) +NSSVTSSTSTTSSSSSTTTSN | MARK: 10010110 00001110 (deleting_last_element) +SNN | DROP +SNN | DROP +NSNTSSTSTTSSSSTSSSSN | JMP > 10010110 00010000 (decrement_array_counter) +NSSVTSSTSTTSSSSSTTTTN | MARK: 10010110 00001111 (not_deleting_last_element) +SNT | SWAP +@ TOS> index+1, count=num_array_elements-(index+1) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TSSS | ADD +SNT | SWAP +@ TOS> count=num_array_elements-(index+1), address=0x3000+index+1 +NSTTTTTSN | JSR > 11110 (slurp) +@ Recover the array index that we stashed on the heap. +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TTT | LOAD +TSSS | ADD +SSSTN | PUSH 1 +TSSS | ADD +TTT | LOAD +@ TOS> index +SNS | DUP +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TSSS | ADD +SNT | SWAP +@ TOS> index, address=0x3000+index +SSSTN | PUSH 1 +TSSS | ADD +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TTT | LOAD +SNT | SWAP +TSST | SUBTRACT +@ TOS> count = num_array_elements-(index+1), address=0x3000+index +NSTTTTTTN | JSR > 11111 (spew) +@ Decrement the array element counter. +NSSVTSSTSTTSSSSTSSSSN | MARK: 10010110 00010000 (decrement_array_counter) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (BUFFER) +SNT | SWAP +TTS | STORE +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ build_cave +@ Description: +@ Clears the cave data structure on the heap and builds new tunnels. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSSSTSSN | MARK: 10000100 (build_cave) +NSTTSSSSTTSN | JSR > 10000110 (clear_cave_data) +NSTTSSSTTTSN | JSR > 10001110 (build_circular_tunnels) +NSTTSSTSTTSN | JSR > 10010110 (build_random_tunnels) +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ set_tunnel_destination +@ Description: +@ Sets 'slot' of 'room_number' to point to 'dst_room_number'. +@ Call Stack: +@ dst_room_number +@ slot +@ room_number <-- TOS +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSSTSSTN | MARK: 10001001 (set_tunnel_destination) +@ The pointer we seek is: +@ (room_number * room_struct_size) + 2 + slot + ROOM_DATA_BASE +@ Where the '+2' accounts for the pit and bat booleans. +NSTTSSSTSTTN | JSR > 10001011 (get_room_struct_size) +TSSN | MULTIPLY +SSSTSN | PUSH +2 +TSSS | ADD +TSSS | ADD +SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (ROOM_DATA_BASE address) +TSSS | ADD +SNT | SWAP +TTS | STORE +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ generate_cave_hop_length +@ Description: +@ In order to ensure cave is fully connected, generate a number, 'hop_length', +@ that is relatively prime to the number of rooms in the cave. +@ Call Stack: +@ +@ Return Stack: +@ hop_length +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSSSTTTN | MARK: 10000111 (generate_cave_hop_length) +NSTTSSSTSTSN | JSR > 10001010 (get_random_room) +@ Check for gcd(hop_length,num_rooms) = 1. +SNS | DUP +SSSTSSSSSSSSSSSSN | PUSH 0x1000 (GAME_DATA_BASE+0 = num_rooms address) +TTT | LOAD +NSTTSSTSN | JSR > 10010 (gcd) +SSSTN | PUSH +1 +TSST | SUBTRACT +NTSTSSSSTTTSSSSSSSSN | BRZ > 10000111 00000000 +SNN | DROP +NSNTSSSSTTTN | JMP > 10000111 (generate_cave_hop_length) +NSSVTSSSSTTTSSSSSSSSN | MARK: 10000111 00000000 +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ clear_cave_data +@ Description: +@ Writes -1 over the entire cave data array. +@ This ensures each room starts unconnected. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +#include +NSSVTSSSSTTSN | MARK: 10000110 (clear_cave_data) +SSTTN | PUSH -1 (pattern) +SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (ROOM_DATA_BASE address) +SSSTSSSSSSSSSSSSN | PUSH 0x1000 (GAME_DATA_BASE address) +TTT | LOAD +NSTTSSSTSTTN | JSR > 10001011 (get_room_struct_size) +TSSN | MULTIPLY +NSTTTSSSN | JSR > 11000 (memset) +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ get_random_room +@ Description: +@ Returns a valid room number +@ Call Stack: +@ +@ Return Stack: +@ room_num <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSSTSTSN | MARK: 10001010 (get_random_room) +NSTTSSSSN | JSR > 10000 (random) +SSSTSSSSSSSSSSSSN | PUSH 0x1000 (GAME_DATA_BASE+0 = num_rooms address) +TTT | LOAD +TSTT | MODULO +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ set_bats +@ Description: +@ Set the 'bats' boolean for a specific room. +@ Call Stack: +@ value (0 or 1) +@ room_number +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTSSSSN | MARK: 10010000 (set_bats) +@ We seek the pointer: +@ (room_number * room_struct_size) + 1 + ROOM_DATA_BASE +@ where '+1' accounts for the offset of the bat boolean in the desired room. +NSTTSSSTSTTN | JSR > 10001011 (get_room_struct_size) +TSSN | MULTIPLY +SSSTN | PUSH +1 +TSSS | ADD +SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (GAME_DATA_BASE address) +TSSS | ADD +SNT | SWAP +TTS | STORE +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ set_pits +@ Description: +@ Set the 'pits' boolean for a specific room. +@ Call Stack: +@ value (0 or 1) +@ room_number +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTSSSTN | MARK: 10010001 (set_pits) +@ We seek the pointer: +@ (room_number * room_struct_size) + 0 + ROOM_DATA_BASE +@ where '+0' accounts for the offset of the pit boolean in the desired room. +NSTTSSSTSTTN | JSR > 10001011 (get_room_struct_size) +TSSN | MULTIPLY +SSSTSSSSSSSSSSSSSN | PUSH 0x2000 (GAME_DATA_BASE address) +TSSS | ADD +SNT | SWAP +TTS | STORE +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ reset_cave_population +@ Description: +@ Remove all bats and pits from the cave. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSTSSTSN | MARK: 10010010 (reset_cave_population) + +@ Clear the bat and pit booleans by setting every room to false(=0). +@ First prepare a loop index. +SSSTSSSSSSSSSSSSN | PUSH 0x1000 (GAME_DATA_BASE+0 = number_of_rooms address) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT +NSSVTSSTSSTSSSSSSSSSN | MARK: 10010010 00000000 (empty_cave_loop) +@ Scare all the bats out of the room. +SNS | DUP +SSSSN | PUSH 0 (false) +SNT | SWAP +NSTTSSTSSSSN | JSR > 10010000 (set_bats) +@ Fill any pits. +SNS | DUP +SSSSN | PUSH 0 (false) +SNT | SWAP +NSTTSSTSSSTN | JSR > 10010001 (set_pits) +@ Decrement loop counter and test for end of loop. +SSSTN | PUSH 1 +TSST | SUBTRACT +SNS | DUP +NTTTSSTSSTSSSSSSSSTN | BMI > 10010010 00000001 (empty_cave_loop_end) +NSNTSSTSSTSSSSSSSSSN | JMP > 10010010 00000000 (empty_cave_loop) +@ Loop is complete. Clean up and return. +NSSVTSSTSSTSSSSSSSSTN | MARK: 10010010 00000001 (empty_cave_loop_end) +SNN | DROP +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ populate_bats +@ Description: +@ Randomly place a user-defined number of bats in the cave. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTSSTTN | MARK: 10010011 (populate_bats) + +@ First prepare a loop index. +SSSTSSSSSSSSSSTSN | PUSH 0x1002 (GAME_DATA_BASE+2 = number_of_bats address) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT +@ Find a random room that does not already contain bats. +NSSVTSSTSSTTSSSSSSTSN | MARK: 10010011 00000010 (place_bats) +NSTTSSSTSTSN | JSR > 10001010 (get_random_room) +SNS | DUP +NSTTSSSTTSSN | JSR > 10001100 (room_has_bats) +NTSTSSTSSTTSSSSSSTTN | BRZ > 10010011 00000011 (no_bats_in_room) +SNN | DROP +NSNTSSTSSTTSSSSSSTSN | JMP > 10010011 00000010 (place_bats) +@ Room is empty. Place bats. +NSSVTSSTSSTTSSSSSSTTN | MARK: 10010011 00000011 (no_bats_in_room) +SSSTN | PUSH 1 (true) +SNT | SWAP +NSTTSSTSSSSN | JSR > 10010000 (set_bats) +@ Decrement loop counter and test for loop completion. +SSSTN | PUSH 1 +TSST | SUBTRACT +SNS | DUP +NTTTSSTSSTTSSSSSTSSN | BMI > 10010011 00000100 (place_bats_loop_end) +NSNTSSTSSTTSSSSSSTSN | JMP > 10010011 00000010 (place_bats) +@ Loop is complete. Clean up and return. +NSSVTSSTSSTTSSSSSTSSN | MARK: 10010011 00000100 (place_bats_loop_end) +SNN | DROP +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ populate_pits +@ Description: +@ Randomly place a user-defined number of pits in the cave. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTSTSSN | MARK: 10010100 (populate_pits) + +@ First prepare a loop index. +SSSTSSSSSSSSSSSTN | PUSH 0x1001 (GAME_DATA_BASE+1 = number_of_pits address) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT +@ Find a random room that does not already contain pits. +NSSVTSSTSTSSSSSSSSTSN | MARK: 10010100 00000010 (place_pits) +NSTTSSSTSTSN | JSR > 10001010 (get_random_room) +SNS | DUP +NSTTSSSTTSTN | JSR > 10001101 (room_has_pits) +NTSTSSTSTSSSSSSSSTTN | BRZ > 10010100 00000011 (no_pits_in_room) +SNN | DROP +NSNTSSTSTSSSSSSSSTSN | JMP > 10010100 00000010 (place_pits) +@ Room is empty. Place pits. +NSSVTSSTSTSSSSSSSSTTN | MARK: 10010100 00000011 (no_pits_in_room) +SSSTN | PUSH 1 (true) +SNT | SWAP +NSTTSSTSSSTN | JSR > 10010001 (set_pits) +@ Decrement loop counter and test for loop completion. +SSSTN | PUSH 1 +TSST | SUBTRACT +SNS | DUP +NTTTSSTSTSSSSSSSTSSN | BMI > 10010100 00000100 (place_pits_loop_end) +NSNTSSTSTSSSSSSSSTSN | JMP > 10010100 00000010 (place_pits) +@ Loop is complete. Clean up and return. +NSSVTSSTSTSSSSSSSTSSN | MARK: 10010100 00000100 (place_pits_loop_end) +SNN | DROP +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ place_player +@ Description: +@ Place the player in a suitable starting room. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSTSTSTN | MARK: 10010101 (place_player) + +@ Find out where the wumpus is located. +SSSTSSSSSSSSSTTTN | PUSH 0x1007 (GAME_DATA_BASE+7 = wumpus_room address) +TTT | LOAD +@ Place the player in a different room than the wumpus. +NSSVTSSTSTSTSSSSSSSSN | MARK: 10010101 00000000 (place_player_loop) +NSTTSSSTSTSN | JSR > 10001010 (get_random_room) +SNT | SWAP +SNS | DUP +SSSTTN | PUSH 3 +NSTTTSSN | JSR > 1100 (deepdup) +@ TOS> rand_room, wump_room, wump_room, rand_room +TSST | SUBTRACT +NTSTSSTSTSTSSSSSSSTN | BRZ > 10010101 00000001 (same_room) +NSNTSSTSTSTSSSSSSTSN | JMP > 10010101 00000010 (different_rooms) +NSSVTSSTSTSTSSSSSSSTN | MARK: 10010101 00000001 (same_room) +SNN | DROP +NSNTSSTSTSTSSSSSSSSN | JMP > 10010101 00000000 (place_player_loop) +NSSVTSSTSTSTSSSSSSTSN | MARK: 10010101 00000010 (different_rooms) +SNN | DROP +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (GAME_DATA_BASE+6 = player_room address) +SNT | SWAP +TTS | STORE +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ populate_cave +@ Description: +@ Populate the cave with various creatures and features. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSSSTSTN | MARK: 10000101 (populate_cave) + +@ Initialize the cave to an unpopulated state. +NSTTSSTSSTSN | JSR > 10010010 (reset_cave_population) + +@ Place bats in the now-empty cave. +NSTTSSTSSTTN | JSR > 10010011 (populate_bats) + +@ Place pits in the now-empty cave. +NSTTSSTSTSSN | JSR > 10010100 (populate_pits) + +@ Place the wumpus. +SSSTSSSSSSSSSTTTN | PUSH 0x1007 (GAME_DATA_BASE+7 = wumpus_room address) +NSTTSSSTSTSN | JSR > 10001010 (get_random_room) +TTS | STORE + +@ Place the player. +NSTTSSTSTSTN | JSR > 10010101 (place_player) + +NTN | RTS + +#endif diff --git a/examples/wump/wump_strings.pvvs b/examples/wump/wump_strings.pvvs index a4278db..5b85906 100644 --- a/examples/wump/wump_strings.pvvs +++ b/examples/wump/wump_strings.pvvs @@ -1,9 +1,14 @@ -@ Imported from: +#ifndef WUMP_STRING +#define WUMP_STRING + +@ Some text strings in this file were imported from: @ $NetBSD: wump.c,v 1.17 2005/02/15 12:56:20 jsm Exp $ @ Copyright (c) 1989, 1993 @ The Regents of the University of California. All rights reserved. - -@ TODO: Import the license properly. +@ +@ Everything else is +@ (c) 2019 Aaron Taylor +@ See LICENSE.txt file for copyright and license details. NSSVTTTTTTTTSSSSSSSSN | Mark: 11111111 00000000 (wump_kill) A"\n*ROAR* *chomp* *snurfle* *chomp*!\nMuch to the delight of the Wumpus, you walked right into his mouth,\nmaking you one of the easiest dinners he's ever had! For you, however,\nit's a rather unpleasant death. The only good thing is that it's been\nso long since the evil Wumpus cleaned his teeth that you immediately\npassed out from the stench!\n" @@ -26,9 +31,27 @@ A"\nWith a jaunty step you enter the magic tunnel. As you do, you\nnotice that NTN | RTS NSSVTTTTTTTTSSSSSTSTN | Mark: 11111111 00000101 (pit_kill) -A"*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\nThe whistling sound and updraft as you walked into this room of the\ncave apparently wasn't enough to clue you in to the presence of the\nbottomless pit. You have a lot of time to reflect on this error as\nyou fall many miles to the core of the earth. Look on the bright side;\nyou can at least find out if Jules Verne was right...\n" +A"\n*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\nThe whistling sound and updraft as you walked into this room of the\ncave apparently wasn't enough to clue you in to the presence of the\nbottomless pit. You have a lot of time to reflect on this error as\nyou fall many miles to the core of the earth. Look on the bright side;\nyou can at least find out if Jules Verne was right...\n" NTN | RTS NSSVTTTTTTTTSSSSSTTSN | Mark: 11111111 00000110 (pit_survive) -A"Without conscious thought you grab for the side of the cave and manage\nto grasp onto a rocky outcrop. Beneath your feet stretches the limitless\ndepths of a bottomless pit! Rock crumbles beneath your feet!\n" +A"\nWithout conscious thought you grab for the side of the cave and manage\nto grasp onto a rocky outcrop. Beneath your feet stretches the limitless\ndepths of a bottomless pit! Rock crumbles beneath your feet!\n" +NTN | RTS + +NSSVTTTTTTTTSSSSSTTTN | Mark: 11111111 00000111 (please_seed_rng) +A"\n\n\n\nWelcome to Hunt the Wumpus!\n\nPlease press 16 keys to seed the RNG.\n |\n" +NTN | RTS + +NSSVTTTTTTTTSSSSTSSSN | Mark: 11111111 00001000 (problem_with_yes_no_answer) +A"I don't understand your answer; please enter 'y' or 'n'!\n" NTN | RTS + +NSSVTTTTTTTTSSSSTSSTN | Mark: 11111111 00001001 (instructions) +A"\n\nWelcome to the game of Hunt the Wumpus.\n\nThe Wumpus typically lives in a cave of twenty rooms, with each room having\nthree tunnels connecting it to other rooms in the cavern. Caves may vary,\nhowever, depending on options specified when starting the game.\n\nThe game has the following hazards for intrepid adventurers to wind their\nway through:\n\n Pits -- If you fall into one of the bottomless pits, you find yourself\n slung back out on the far side of the Earth and in very poor\n shape to continue your quest since you're dead.\n\n Bats -- As with any other cave, the Wumpus cave has bats in residence.\n These are a bit more potent, however, and if you stumble into\n one of their rooms they will rush up and carry you elsewhere in\n the cave.\n\n Wumpus -- If you happen to walk into the room the Wumpus is in you'll find\n that he has quite an appetite for young adventurous humans! Not\n recommended.\n\nThe Wumpus, by the way, is not bothered by the hazards since he has sucker\nfeet and is too big for a bat to lift. If you try to shoot him and miss,\nthere's also a chance that he'll up and move himself into another cave,\nthough by nature the Wumpus is a sedentary creature.\n\nEach turn you may either move or shoot a crooked arrow. Moving is done\nsimply by specifying 'm' for move and the number of the room that you'd\nlike to move down a tunnel towards. Shooting is done similarly; indicate\nthat you'd like to shoot one of your magic arrows with an 's' for shoot,\nthen list a set of connected room numbers through which the deadly shaft\nshould fly!\n\nIf your path for the arrow is incorrect, however, it will flail about in\nthe room it can't understand and randomly pick a tunnel to continue\nthrough. You might just end up shooting yourself in the foot if you're\nnot careful! On the other hand, if you shoot the Wumpus you've WON!\n\nLet the hunt begin!\n" +NTN | RTS + +NSSVTTTTTTTTSSSSTSTSN | Mark: 11111111 00001010 (cave_description) +A"\nYou're in a cave with %u rooms and %u tunnels leading from each room.\nThere are %u bats and %u pits scattered throughout the cave, and your\nquiver holds %u custom super anti-evil Wumpus arrows. Good luck.\n\n" +NTN | RTS + +#endif diff --git a/examples/wump/wump_ui.pvvs b/examples/wump/wump_ui.pvvs new file mode 100644 index 0000000..455e30d --- /dev/null +++ b/examples/wump/wump_ui.pvvs @@ -0,0 +1,255 @@ +#ifndef WUMP_UI +#define WUMP_UI + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ This files contains user interface functions for Hunt the Wumpus. +@ (c) 2019 Aaron Taylor +@ See LICENSE.txt file for copyright and license details. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ seed_rng +@ Description: +@ Generate seed from keyboard input. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSSSSTTN | MARK: 10000011 (seed_rng) + +SSSTSSSSN | PUSH 16 (loop counter) +SSSSN | PUSH 0 (rng seed) + +NSSVTSSSSSTTSSSSSSSSN | MARK: 10000011 00000000 (seed_rng:main loop) +@ Get character from user and print ASCII '.' as feedback. +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (USER_INPUT_BUFFER address) +TNTS | GETCHAR +SSSTSTTTSN | PUSH ASCII '.' +TNSS | PUTCHAR +@ Left shift the seed by 4 bits. +SSSTSSN | PUSH 4 (shift count) +NSTTSTTSTN | JSR > 101101 (lshift) +@ XOR seed with character from user. +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (USER_INPUT_BUFFER address) +TTT | LOAD +NSTTSTSTTN | JSR > 101011 (xor) +@ Decrement counter +SNT | SWAP +SSSTN | PUSH +1 +TSST | SUBTRACT +@ Test for loop completion +SNS | DUP +NTSTSSSSSTTSSSSSSSTN | BRZ > 10000011 00000001 (seed_rng:cleanup and return) +SNT | SWAP +NSNTSSSSSTTSSSSSSSSN | JMP > 10000011 00000000 (seed_rng:main loop) + +@ Store seed, clean up and return. +NSSVTSSSSSTTSSSSSSSTN | MARK: 10000011 00000001 (seed_rng:cleanup and return) +SNN | DROP +SSSSN | PUSH 0 (seed address) +SNT | SWAP +TTS | STORE +SSSTSTSN | PUSH ASCII '\n' +SSSTSTSN | PUSH ASCII '\n' +TNSS | PUTCHAR +TNSS | PUTCHAR +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ get_answer +@ Description: +@ Parse user input, returning 0 if user string started with 'n' or 1 if 'y'. +@ This function does not perform any boundary checks/limits. +@ Call Stack: +@ +@ Return Stack: +@ (1 or 0 for True/False) <--- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTTSSSN | MARK: 10011000 (get_answer) + +@ TODO: Consider extending the GETCHAR instruction in VVS to indicate an empty +@ buffer instead of blocking. This would allow a character by character +@ check without printing a slew of retry messages if the buffer is +@ non-empty. +NSTTSSTTSSTN | JSR > 10011001 (get_line) + +@ Examine the first character of the user input buffer for 'y' or 'n'. +@ If character is something else, prompt user to try again. +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (USER_INPUT_BUFFER address) +TTT | LOAD +SSSSTTTTSSTN | PUSH 121 (ASCII 'y') +TSST | SUBTRACT +NTSTSSTTSSSSSSSSSSSN | BRZ > 10011000 00000000 (answer: yes) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (USER_INPUT_BUFFER address) +TTT | LOAD +SSSSTTSTTTSN | PUSH 110 (ASCII 'n') +TSST | SUBTRACT +NTSTSSTTSSSSSSSSSSTN | BRZ > 10011000 00000001 (answer: no) +NSTTTTTTTTTSSSSTSSSN | JSR > 11111111 00001000 (problem_with_yes_no_answer) +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSNTSSTTSSSN | JMP > 10011000 (get_answer) + +@ User typed 'y' +NSSVTSSTTSSSSSSSSSSSN | MARK: 10011000 00000000 (answer: yes) +SSSTN | PUSH 1 +NTN | RTS + +@ User typed 'n' +NSSVTSSTTSSSSSSSSSSTN | MARK: 10011000 00000001 (answer: no) +SSSSN | PUSH 0 +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ get_line +@ Description: +@ Read one line of user input and store in buffer. +@ Appends null terminator to end of string. +@ This function does not perform any boundary checks/limits. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSTTSSTN | MARK: 10011001 (get_line) + +@ Get one character from user on each pass through this loop. +@ Terminate loop when line break character is received. +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (USER_INPUT_BUFFER address) +NSSVTSSTTSSTSSSSSSSSN | MARK: 10011001 00000000 (input loop) +SNS | DUP +SNS | DUP +TNTS | GETCHAR +TTT | LOAD +SNS | DUP +TNSS | PUTCHAR +SSSTSTSN | PUSH 10 (ASCII '\n') +TSST | SUBTRACT +NTSTSSTTSSTSSSSSSSTN | BRZ > 10011001 00000001 (input loop:terminate) +@ Character was not ENTER. Increment buffer pointer and loop again. +SSSTN | PUSH 1 +TSSS | ADD +NSNTSSTTSSTSSSSSSSSN | JMP > 10011001 00000000 (input loop) +@ Character was ENTER. Overwrite the line feed with a null term and return. +NSSVTSSTTSSTSSSSSSSTN | MARK: 10011001 00000001 (input loop:terminate) +SSSSN | PUSH 0 (ASCII '\0') +TTS | STORE +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ print_cave_description +@ Description: +@ Prints information about the cave (number of rooms, etc). +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSSTTSTTN | MARK: 10011011 (print_cave_description) +NSTTTTTTTTTSSSSTSTSN | JSR > 11111111 00001010 (cave_description) +SSSTSSSSSSSSSTSTN | PUSH 0x1005 (number_of_arrows address) +TTT | LOAD +SSSTSSSSSSSSSSSTN | PUSH 0x1001 (number_of_pits address) +TTT | LOAD +SSSTSSSSSSSSSSTSN | PUSH 0x1002 (number_of_bats address) +TTT | LOAD +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (number_of_tunnels address) +TTT | LOAD +SSSTSSSSSSSSSSSSN | PUSH 0x1000 (number_of_rooms address) +TTT | LOAD +SSSTSTN | PUSH 5 (number of substitions) +NSTTSSSN | JSR > 1000 (printf) +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ print_room_stats +@ Description: +@ Prints information about current room and hints about nearby rooms. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +#include +#include +NSSVTSTSSSSTN | MARK: 10100001 (print_room_stats) + +@ Print location and arrow quantity remaining. +A"You are in room %u of the cave and have %u arrows remaining.\n" +SSSTSSSSSSSSSTSTN | PUSH 0x1005 (number_of_arrows address) +TTT | LOAD +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (player_location address) +TTT | LOAD +SSSTSN | PUSH 2 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) + +@ Print if bats/pits/wumpus nearby. +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (player_location address) +TTT | LOAD +SNS | DUP +NSTTSSTTTSSN | JSR > 10011100 (are_bats_near) +NTSTSTSSSSTSSSSSSSSN | BRZ > 10100001 00000000 (no_bats) +A"*rustle* (Bats must be nearby.)\n" +SSSSN | PUSH 0 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSSVTSTSSSSTSSSSSSSSN | MARK: 10100001 00000000 (no_bats) +SNS | DUP +NSTTSSTTTSTN | JSR > 10011101 (are_pits_near) +NTSTSTSSSSTSSSSSSSTN | BRZ > 10100001 00000001 (no_pits) +A"*whoosh* (You feel a draft from nearby pits.)\n" +SSSSN | PUSH 0 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSSVTSTSSSSTSSSSSSSTN | MARK: 10100001 00000001 (no_pits) +NSTTSSTTTTSN | JSR > 10011110 (is_wumpus_near) +NTSTSTSSSSTSSSSSSTSN | BRZ > 10100001 00000010 (no_wumpus) +A"*sniff* (You smell the evil Wumpus nearby!)\n" +SSSSN | PUSH 0 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSSVTSTSSSSTSSSSSSTSN | MARK: 10100001 00000010 (no_wumpus) + +@ Print a list of nearby rooms. +A"This room contains tunnels to the following rooms:" +SSSSN | PUSH 0 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (player_location address) +TTT | LOAD +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (number_of_links_per_room address) +TTT | LOAD +SSSTN | PUSH 1 +TSST | SUBTRACT +@ Print one room on each pass through this loop. +@ TOS> tunnel_index, room_number +NSSVTSTSSSSTSSSSSSTTN | MARK: 10100001 00000011 (print_room_list_loop) +A" %u" +SSSTSTN | PUSH 5 +NSTTTSSN | JSR > 1100 (deepdup) +SSSTTTN | PUSH 7 +NSTTTSSN | JSR > 1100 (deepdup) +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +SSSTN | PUSH 1 (number of substitutions) +NSTTSSSN | JSR > 1000 (printf) +@ Test for end of loop +SNS | DUP +NTSTSTSSSSTSSSSSTSSN | BRZ > 10100001 00000100 (print_room_list_loop_end) +SSSTN | PUSH 1 +TSST | SUBTRACT +NSNTSTSSSSTSSSSSSTTN | JMP > 10100001 00000011 (print_room_list_loop) +@ Clean up and return. +NSSVTSTSSSSTSSSSSTSSN | MARK: 10100001 00000100 (print_room_list_loop_end) +SSSTSTSN | PUSH 10 (ASCII '\n') +TNSS | PUTCHAR +SNN | DROP +SNN | DROP +NTN | RTS + +#endif diff --git a/stdlib/stdio.pvvs b/stdlib/stdio.pvvs index 2b77d1d..15cbb2c 100644 --- a/stdlib/stdio.pvvs +++ b/stdlib/stdio.pvvs @@ -385,6 +385,7 @@ NSNSSSSTSSSSSSSSSSTN | JMP > 00001000 00000001 @ 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. diff --git a/vv_interpreter.c b/vv_interpreter.c index b548a50..cf5cdef 100644 --- a/vv_interpreter.c +++ b/vv_interpreter.c @@ -15,9 +15,9 @@ #define VERSION 1 -#define HEAPSIZE 1024 /* Size of heap in words */ -#define DATASTACKSIZE 1024 /* Size of stack in words */ -#define RETURNSTACKSIZE 1024 /* Max subroutine call depth */ +#define HEAPSIZE 65536 /* Size of heap in words */ +#define DATASTACKSIZE 65536 /* Size of stack in words */ +#define RETURNSTACKSIZE 65536 /* Max subroutine call depth */ void print_usage(char ** argv) @@ -455,8 +455,8 @@ main(int argc, char ** argv) if (pc >= ws_code_size) { fprintf(stderr, "SIM_ERROR: PC Overrun\n Requested PC: %lu\n Max Address: %lu\n", pc, ws_code_size-1); - exit(EXIT_FAILURE); unset_terminal_mode(); + exit(EXIT_FAILURE); } /* Decode the IMPs */ -- 2.20.1 From f969012f20e1d28118c4589dcc3d6ef689214533 Mon Sep 17 00:00:00 2001 From: Daniel Neshyba-Rowe Date: Sat, 11 Jan 2020 02:45:07 +0000 Subject: [PATCH 07/16] Replaced RNG with Xorshift implementation from Wikipedia. --- stdlib/math.pvvs | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/stdlib/math.pvvs b/stdlib/math.pvvs index 742522e..a72203f 100644 --- a/stdlib/math.pvvs +++ b/stdlib/math.pvvs @@ -5,34 +5,44 @@ @ Name: @ random (10000) @ Description: -@ Returns a kinda-random number. -@ Before using for the first time, seed heap[0] with a value. +@ Returns a pseudo-random number. +@ Before using for the first time, seed heap[0] with a non-zero value. +@ This PRNG was taken from: https://en.wikipedia.org/wiki/Xorshift @ Call Stack: @ empty @ Return Stack: @ random number <-- TOS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include NSSVTSSSSN | Mark: 10000 (random) -@ Generate the next seed value +@ Fetch seed from heap[0]. SSSSN | PUSH 0 (ptr) TTT | LOAD -SSSTSSSSSTTTSSSTTSSTSSTTTSSTTSTTSTN | PUSH 1103515245 -TSSN | MULTIPLY -SSSTTSSSSSSTTTSSTN | PUSH 12345 -TSSS | ADD -@ Store the next seed value but keep a copy on the stack. +@ Set TOS ^= TOS << 13 +SNS | DUP +SSSTTSTN | PUSH +13 +NSTTSTTSTN | JSR > 101101 (lshift) +NSTTSTSTTN | JSR > 101011 (xor) + +@ Set TOS ^= TOS >> 7 +SNS | DUP +SSSTTTN | PUSH +7 +NSTTSTTSSN | JSR > 101100 (rshift) +NSTTSTSTTN | JSR > 101011 (xor) + +@ Set TOS ^= TOS << 17 +SNS | DUP +SSSTSSSTN | PUSH +17 +NSTTSTTSTN | JSR > 101101 (lshift) +NSTTSTSTTN | JSR > 101011 (xor) + +@ Store a copy of the new seed at heap[0] and return. SNS | DUP SSSSN | PUSH 0 (ptr) SNT | SWAP TTS | STORE - -@ Calculate the random number and return. -SSSTSSSSSSSSSSSSSSSSN | PUSH 65536 -TSTS | DIVIDE -SSSTSSSSSSSSSSSSSSSN | PUSH 32768 -TSTT | MODULO NTN | RTS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -- 2.20.1 From f0e3023da0dabbd199dedfd00b84deb75bca8578 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Tue, 25 Feb 2020 23:49:40 -0800 Subject: [PATCH 08/16] Added move_or_shoot function to Wumpus example. Finished main game loop in wump.pvvs. --- examples/wump/wump.pvvs | 6 ++++- examples/wump/wump_strings.pvvs | 4 +-- examples/wump/wump_ui.pvvs | 45 ++++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/examples/wump/wump.pvvs b/examples/wump/wump.pvvs index d5c5079..799b24e 100644 --- a/examples/wump/wump.pvvs +++ b/examples/wump/wump.pvvs @@ -33,4 +33,8 @@ NSTTSSTTSTTN | JSR > 10011011 (print_cave_description) @ Main game loop NSSVTSSTTSTSN | MARK: 10011010 (wump_loop) NSTTSTSSSSTN | JSR > 10100001 (print_room_stats) -NNN | TERMINATE +A"Move or shoot? (m-s)\n" +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSTTSTSSSTSN | JSR > 10100010 (move_or_shoot) +NSNTSSTTSTSN | JMP > 10011010 (wump_loop) diff --git a/examples/wump/wump_strings.pvvs b/examples/wump/wump_strings.pvvs index 5b85906..d8cfb69 100644 --- a/examples/wump/wump_strings.pvvs +++ b/examples/wump/wump_strings.pvvs @@ -42,8 +42,8 @@ NSSVTTTTTTTTSSSSSTTTN | Mark: 11111111 00000111 (please_seed_rng) A"\n\n\n\nWelcome to Hunt the Wumpus!\n\nPlease press 16 keys to seed the RNG.\n |\n" NTN | RTS -NSSVTTTTTTTTSSSSTSSSN | Mark: 11111111 00001000 (problem_with_yes_no_answer) -A"I don't understand your answer; please enter 'y' or 'n'!\n" +NSSVTTTTTTTTSSSSTSSSN | Mark: 11111111 00001000 (problem_with_input) +A"I don't understand!\n" NTN | RTS NSSVTTTTTTTTSSSSTSSTN | Mark: 11111111 00001001 (instructions) diff --git a/examples/wump/wump_ui.pvvs b/examples/wump/wump_ui.pvvs index 455e30d..88ff4b0 100644 --- a/examples/wump/wump_ui.pvvs +++ b/examples/wump/wump_ui.pvvs @@ -58,6 +58,49 @@ TNSS | PUTCHAR TNSS | PUTCHAR NTN | RTS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ move_or_shoot +@ Description: +@ Parse user input, branching to the appropriate subroutine to move or shoot. +@ This function does not perform any boundary checks/limits. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSTSSSTSN | MARK: 10100010 (move_or_shoot) + +NSTTSSTTSSTN | JSR > 10011001 (get_line) + +@ Examine the first character of the user input buffer for 'm' or 's'. +@ If character is something else, prompt user to try again. +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (USER_INPUT_BUFFER address) +TTT | LOAD +SSSSTTSTTSTN | PUSH 109 (ASCII 'm') +TSST | SUBTRACT +NTSTSTSSSTSSSSSSSSSN | BRZ > 10100010 00000000 (move) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (USER_INPUT_BUFFER address) +TTT | LOAD +SSSSTTTSSTTN | PUSH 115 (ASCII 's') +TSST | SUBTRACT +NTSTSTSSSTSSSSSSSSTN | BRZ > 10100010 00000001 (shoot) +NSTTTTTTTTTSSSSTSSSN | JSR > 11111111 00001000 (problem_with_input) +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSNTSTSSSTSN | JMP > 10100010 (move_or_shoot) + +@ User typed 'm' +NSSVTSTSSSTSSSSSSSSSN | MARK: 10100010 00000000 (move) +@ TODO: JSR move +NTN | RTS + +@ User typed 's' +NSSVTSTSSSTSSSSSSSSTN | MARK: 10100010 00000001 (shoot) +@ TODO: JSR shoot +NTN | RTS + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ Name: @ get_answer @@ -90,7 +133,7 @@ TTT | LOAD SSSSTTSTTTSN | PUSH 110 (ASCII 'n') TSST | SUBTRACT NTSTSSTTSSSSSSSSSSTN | BRZ > 10011000 00000001 (answer: no) -NSTTTTTTTTTSSSSTSSSN | JSR > 11111111 00001000 (problem_with_yes_no_answer) +NSTTTTTTTTTSSSSTSSSN | JSR > 11111111 00001000 (problem_with_input) SSSSN | PUSH 0 (number of string substitutions) NSTTSSSN | JSR > 1000 (printf) NSNTSSTTSSSN | JMP > 10011000 (get_answer) -- 2.20.1 From 0e0d0a4a1058f9d75cea563a76c1113a0da87fc2 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Thu, 12 Mar 2020 21:50:16 -0700 Subject: [PATCH 09/16] Added 'isdigit' function to stdlib. --- stdlib/README.md | 1 + stdlib/string.pvvs | 32 ++++++++++++++++++++++++++++++++ stdlib_tests/4002_isdigit.pvvs | 20 ++++++++++++++++++++ stdlib_tests/vv_test.py | 1 + 4 files changed, 54 insertions(+) create mode 100644 stdlib_tests/4002_isdigit.pvvs diff --git a/stdlib/README.md b/stdlib/README.md index 8427f53..40b9f4a 100644 --- a/stdlib/README.md +++ b/stdlib/README.md @@ -54,6 +54,7 @@ header comment for each function to learn the call and return stack. 11111 ----- spew (heap.pvvs) 100xxx - string functions 100000 ----- strlen (string.pvvs) + 100001 ----- isdigit (string.pvvs) 101xxx - logic functions 101000 ----- not (logic.pvvs) 101001 ----- and (logic.pvvs) diff --git a/stdlib/string.pvvs b/stdlib/string.pvvs index 5571349..8b68e22 100644 --- a/stdlib/string.pvvs +++ b/stdlib/string.pvvs @@ -36,4 +36,36 @@ NSSVSSTSSSSSSSSSSSSTN | Mark: 00100000 00000001 SNN | DROP NTN | RTS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ isdigit (100001) +@ Description: +@ Tests 'character' on stack. Is it an ASCII number? +@ Returns 1 or 0 representing True/False. +@ Call Stack: +@ character <-- TOS +@ Return Stack: +@ 1 or 0 <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +NSSVTSSSSTN | Mark: 100001 (isdigit) + +SSSSSTTSSSSN | PUSH 48 (ASCII '0') +TSST | SUBTRACT +SNS | DUP +NTTSSTSSSSTSSSSSSSSN | BMI > 00100001 00000000 (not digit) +SNS | DUP +SSSTSTSN | PUSH 10 +TSST | SUBTRACT +NTTSSTSSSSTSSSSSSSTN | BMI > 00100001 00000001 (is digit) + +NSSVSSTSSSSTSSSSSSSSN | Mark: 00100001 00000000 (not digit) +SNN | DROP +SSSSN | PUSH 0 +NTN | RTS + +NSSVSSTSSSSTSSSSSSSTN | Mark: 00100001 00000001 (is digit) +SNN | DROP +SSSTN | PUSH 1 +NTN | RTS + #endif diff --git a/stdlib_tests/4002_isdigit.pvvs b/stdlib_tests/4002_isdigit.pvvs new file mode 100644 index 0000000..e148e1a --- /dev/null +++ b/stdlib_tests/4002_isdigit.pvvs @@ -0,0 +1,20 @@ +SSSSSTSTTTTN | PUSH 47 (ASCII '/') +NSTTSSSSTN | JSR > 100001 (isdigit) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +SSSSSTTSSSSN | PUSH 48 (ASCII '0') +NSTTSSSSTN | JSR > 100001 (isdigit) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +SSSSSTTTSSTN | PUSH 57 (ASCII '9') +NSTTSSSSTN | JSR > 100001 (isdigit) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +SSSSSTTTSTSN | PUSH 58 (ASCII ':') +NSTTSSSSTN | JSR > 100001 (isdigit) +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +NNN | DIE + +#include +#include diff --git a/stdlib_tests/vv_test.py b/stdlib_tests/vv_test.py index b4f0cd0..4ec6a81 100755 --- a/stdlib_tests/vv_test.py +++ b/stdlib_tests/vv_test.py @@ -37,6 +37,7 @@ tests = [ ['3005_or', '', '+0+1+1-1-1'], ['3006_xor', '', '+0+1+0-2-1'], ['4001_strlen', '', '+11'], + ['4002_isdigit', '', '+0+1+1+0'], ['5001_abs', '', '+1+1+0+0'], ['5002_random', '', ''], ['5003_gcd', '', '+0+4+4+3+3+3'], -- 2.20.1 From b6bea2cfd080c66a11074dd47484711482a2907a Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Thu, 12 Mar 2020 21:50:47 -0700 Subject: [PATCH 10/16] Added 'atoi' to stdlib. --- stdlib/README.md | 1 + stdlib/convert.pvvs | 88 +++++++++++++++++++++++++++++++++++++ stdlib_tests/7001_atoi.pvvs | 59 +++++++++++++++++++++++++ stdlib_tests/vv_test.py | 1 + 4 files changed, 149 insertions(+) create mode 100644 stdlib/convert.pvvs create mode 100644 stdlib_tests/7001_atoi.pvvs diff --git a/stdlib/README.md b/stdlib/README.md index 40b9f4a..eb92964 100644 --- a/stdlib/README.md +++ b/stdlib/README.md @@ -63,6 +63,7 @@ header comment for each function to learn the call and return stack. 101100 ----- rshift (logic.pvvs) 101101 ----- lshift (logic.pvvs) 110xxx - conversion functions + 110000 ----- atoi (convert.pvvs) 111xxx - debug functions 111000 ----- dump heap (debug.pvvs) 111001 ----- dump stack (debug.pvvs) diff --git a/stdlib/convert.pvvs b/stdlib/convert.pvvs new file mode 100644 index 0000000..e20c809 --- /dev/null +++ b/stdlib/convert.pvvs @@ -0,0 +1,88 @@ +#ifndef VVS_STDLIB_CONVERT +#define VVS_STDLIB_CONVERT + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ atoi (110000) +@ Description: +@ Convert null-terminated ASCII string to integer. +@ Result is negative if leading character is a hyphen ('-'). +@ Result is positive if leading character is a plus sign ('+') or number. +@ Excluding the sign, parsing halts on the first non-numeric ASCII character. +@ Since all possible return values are valid, this subroutine simply returns +@ zero if no parseable number was found. Any stronger validity checks are the +@ responsibility of the caller. Similarly, no overflow checks are performed. +@ In addition to returning the parsed integer, also returns a pointer to the +@ first character after the parsed integer. +@ Call Stack: +@ pointer to first character <-- TOS +@ Return Stack: +@ integer +@ pointer to last parsed character <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +#include +NSSVTTSSSSN | Mark: 110000 (atoi) + +@ Check the first character and create a 'sign' flag on the stack. +@ Update the pointer to point to the first numeric character. +SNS | DUP +TTT | LOAD +SSSSSTSTTSTN | PUSH 45 (ASCII '-') +TSST | SUBTRACT +NTSSSTTSSSSSSSSSSSSN | BRZ > 00110000 00000000 (found_minus_sign) +SNS | DUP +TTT | LOAD +SSSSSTSTSTTN | PUSH 43 (ASCII '+') +TSST | SUBTRACT +NTSSSTTSSSSSSSSSSSTN | BRZ > 00110000 00000001 (found_plus_sign) +NSNSSTTSSSSSSSSSSTSN | JMP > 00110000 00000010 (found_digit) +NSSVSSTTSSSSSSSSSSSTN | Mark: 00110000 00000001 (found_plus_sign) +SSSTN | PUSH +1 +TSSS | ADD +NSSVSSTTSSSSSSSSSSTSN | Mark: 00110000 00000010 (found_digit) +SSSTN | PUSH +1 (sign_flag) +SNT | SWAP +NSNSSTTSSSSSSSSSSTTN | JMP > 00110000 00000011 (done_with_sign_flag) +NSSVSSTTSSSSSSSSSSSSN | Mark: 00110000 00000000 (found_minus_sign) +SSSTN | PUSH +1 +TSSS | ADD +SSTTN | PUSH -1 (sign_flag) +SNT | SWAP +NSSVSSTTSSSSSSSSSSTTN | Mark: 00110000 00000011 (done_with_sign_flag) + +@ Create an accumulator on the TOS. +SSSSN | PUSH 0 (accumulator) + +@ Main loop examines string one character at a time, building result in accumulator. +@ TOS> accumulator, string_ptr, sign_flag +NSSVSSTTSSSSSSSSSTSSN | Mark: 00110000 00000100 (atoi:main_loop) +SSSTSN | PUSH +2 +NSTTTSSN | JSR > 1100 (deepdup) +TTT | LOAD +SNS | DUP +NSTTSSSSTN | JSR > 100001 (isdigit) +NTSSSTTSSSSSSSSSTSTN | BRZ > 00110000 00000101 (found_end_of_number) +SSSSSTTSSSSN | PUSH 48 (ASCII '0') +TSST | SUBTRACT +SNT | SWAP +SSSTSTSN | PUSH 10 +TSSN | MULTIPLY +TSSS | ADD +SNT | SWAP +SSSTN | PUSH 1 +TSSS | ADD +SNT | SWAP +NSNSSTTSSSSSSSSSTSSN | JMP > 00110000 00000100 (atoi:main_loop) + +@ Clean up and return +@ TOS> character, accumulator, string_ptr, sign_flag +NSSVSSTTSSSSSSSSSTSTN | Mark: 00110000 00000101 (found_end_of_number) +SNN | DROP +SSSTTN | PUSH 3 +NSTTSTTN | JSR > 1011 (stackrotatereverse) +TSSN | MULTIPLY +SNT | SWAP +NTN | RTS + +#endif diff --git a/stdlib_tests/7001_atoi.pvvs b/stdlib_tests/7001_atoi.pvvs new file mode 100644 index 0000000..c4ea625 --- /dev/null +++ b/stdlib_tests/7001_atoi.pvvs @@ -0,0 +1,59 @@ +A"42" +SSSTSSSSSN | PUSH 32 (address) +SSSTSN | PUSH 2 (count) +NSTTTTTTN | JSR > 11111 (spew) +SSSTSSSSSN | PUSH 32 (address) +NSTTTSSSSN | JSR > 110000 (atoi) +SNN | DROP +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +A"+42" +SSSTSSSSSN | PUSH 32 (address) +SSSTTN | PUSH 3 (count) +NSTTTTTTN | JSR > 11111 (spew) +SSSTSSSSSN | PUSH 32 (address) +NSTTTSSSSN | JSR > 110000 (atoi) +SNN | DROP +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +A"-42" +SSSTSSSSSN | PUSH 32 (address) +SSSTTN | PUSH 3 (count) +NSTTTTTTN | JSR > 11111 (spew) +SSSTSSSSSN | PUSH 32 (address) +NSTTTSSSSN | JSR > 110000 (atoi) +SNN | DROP +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +A"+" +SSSTSSSSSN | PUSH 32 (address) +SSSTN | PUSH 1 (count) +NSTTTTTTN | JSR > 11111 (spew) +SSSTSSSSSN | PUSH 32 (address) +NSTTTSSSSN | JSR > 110000 (atoi) +SNN | DROP +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +A"-" +SSSTSSSSSN | PUSH 32 (address) +SSSTN | PUSH 1 (count) +NSTTTTTTN | JSR > 11111 (spew) +SSSTSSSSSN | PUSH 32 (address) +NSTTTSSSSN | JSR > 110000 (atoi) +SNN | DROP +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +A"" +SSSTSSSSSN | PUSH 32 (address) +SSSSN | PUSH 0 (count) +NSTTTTTTN | JSR > 11111 (spew) +SSSTSSSSSN | PUSH 32 (address) +NSTTTSSSSN | JSR > 110000 (atoi) +SNN | DROP +NSTTTTTSTN | JSR > 111101 (debug:printsignednumber) + +NNN | DIE + +#include +#include +#include diff --git a/stdlib_tests/vv_test.py b/stdlib_tests/vv_test.py index 4ec6a81..045f81c 100755 --- a/stdlib_tests/vv_test.py +++ b/stdlib_tests/vv_test.py @@ -50,6 +50,7 @@ tests = [ ['6007_printf_staticheapstring', '', 'test'], ['6008_printf_escapedstackstring', '', '\\%\t\n'], ['6009_printf_substitutedstackstring', '', 'Atest142+42'], + ['7001_atoi', '', '+42+42-42+0+0+0'], ] for test in tests: -- 2.20.1 From 28a945f72864aae5dbc0da783628a7c0873bb467 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Thu, 12 Mar 2020 21:51:21 -0700 Subject: [PATCH 11/16] Added ability for player to move around the tunnels in Wumpus. --- examples/wump/wump.pvvs | 2 +- examples/wump/wump_ui.pvvs | 166 ++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 2 deletions(-) diff --git a/examples/wump/wump.pvvs b/examples/wump/wump.pvvs index 799b24e..01284a9 100644 --- a/examples/wump/wump.pvvs +++ b/examples/wump/wump.pvvs @@ -33,7 +33,7 @@ NSTTSSTTSTTN | JSR > 10011011 (print_cave_description) @ Main game loop NSSVTSSTTSTSN | MARK: 10011010 (wump_loop) NSTTSTSSSSTN | JSR > 10100001 (print_room_stats) -A"Move or shoot? (m-s)\n" +A"Move or shoot? (m/s)\n" SSSSN | PUSH 0 (number of string substitutions) NSTTSSSN | JSR > 1000 (printf) NSTTSTSSSTSN | JSR > 10100010 (move_or_shoot) diff --git a/examples/wump/wump_ui.pvvs b/examples/wump/wump_ui.pvvs index 88ff4b0..fa5c2a7 100644 --- a/examples/wump/wump_ui.pvvs +++ b/examples/wump/wump_ui.pvvs @@ -58,6 +58,170 @@ TNSS | PUTCHAR TNSS | PUTCHAR NTN | RTS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ is_room_adjacent +@ Description: +@ Checks if 'room_number' is adjacent to the player's current room. +@ Call Stack: +@ room_number <-- TOS +@ Return Stack: +@ (1 or 0) for true/false <-- TOS +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +NSSVTSTSSTSSN | MARK: 10100100 (is_room_adjacent) + +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (ptr to number_of_tunnels_per_room) +TTT | LOAD +SSSTN | PUSH +1 +TSST | SUBTRACT + +@ TOS> tunnel_index, destination_room_num +NSSVTSTSSTSSSSSSSSSSN | MARK: 10100100 00000000 (is_room_adjacent:main_loop) +SSSTSN | PUSH +2 +NSTTTSSN | JSR > 1100 (deepdup) +SSSTSN | PUSH +2 +NSTTTSSN | JSR > 1100 (deepdup) +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (ptr to player_location) +TTT | LOAD +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +TSST | SUBTRACT +NTSTSTSSTSSSSSSSSSTN | BRZ > 10100100 00000001 (is_room_adjacent:found_tunnel) +SSSTN | PUSH +1 +TSST | SUBTRACT +SNS | DUP +NTTTSTSSTSSSSSSSSTSN | BMI > 10100100 00000010 (is_room_adjacent:no_match) +NSNTSTSSTSSSSSSSSSSN | JMP > 10100100 00000000 (is_room_adjacent:main_loop) + +NSSVTSTSSTSSSSSSSSSTN | MARK: 10100100 00000001 (is_room_adjacent:found_tunnel) +SNN | DROP +SNN | DROP +SSSTN | PUSH +1 +NTN | RTS + +NSSVTSTSSTSSSSSSSSTSN | MARK: 10100100 00000010 (is_room_adjacent:no_match) +SNN | DROP +SNN | DROP +SSSSN | PUSH 0 +NTN | RTS + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Name: +@ move_player +@ Description: +@ Prompts the player for a room number. Moves to that room, checking the new +@ environment and executing consequences (fell in a pit, etc) as appropriate. +@ Call Stack: +@ +@ Return Stack: +@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +#include +#include +NSSVTSTSSSTTN | MARK: 10100011 (move_player) + +A"To which room do you wish to move?\n" +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSTTSSTTSSTN | JSR > 10011001 (get_line) +SSSTTSSSSSSSSSSSSN | PUSH 0x3000 (USER_INPUT_BUFFER address) +NSTTTSSSSN | JSR > 110000 (atoi) +SNN | DROP + +@ The desired room number is now on the TOS. Verify that it is valid. +SNS | DUP +NSTTSTSSTSSN | JSR > 10100100 (is_room_adjacent) +NTSTSTSSSTTSSSSSSSTN | BRZ > 10100011 00000001 (invalid room number) +NSNTSTSSSTTSSSSSSSSN | JMP > 10100011 00000000 (valid room number) + +NSSVTSTSSSTTSSSSSSSTN | MARK: 10100011 00000001 (invalid room number) +@ TOS> room_number +SNN | DROP +A"*Oof!* (you hit the wall)\n" +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSTTSSSSN | JSR > 10000 (random) +SSSTTSN | PUSH 6 (chance) +TSTT | MODULO +NTSTSTSSSTTSSSSSSTSN | BRZ > 10100011 00000010 (woke the wumpus) +NTN | RTS +NSSVTSTSSSTTSSSSSSTSN | MARK: 10100011 00000010 (woke the wumpus) +A"Your colorful comments awaken the wumpus!\n" +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSTTSSSSN | JSR > 10000 (random) +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (ptr to number of tunnels per room) +TSTT | MODULO +SSSTSSSSSSSSSTTTN | PUSH 0x1007 (ptr to wumpus location) +TTT | LOAD +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +SSSTSSSSSSSSSTTTN | PUSH 0x1007 (ptr to wumpus location) +SNT | SWAP +TTS | STORE +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (ptr to player location) +TTT | LOAD +NSTTSTSSSSSN | JSR > 10100000 (room_has_wumpus) +NTSTSTSSSTTSSSSSSTTN | BRZ > 10100011 00000011 (wumpus did not move to player) +NSTTTTTTTTTSSSSSSSSN | JSR > 11111111 00000000 (wump_kill) +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NNN | DIE +NSSVTSTSSSTTSSSSSSTTN | MARK: 10100011 00000011 (wumpus did not move to player) +NTN | RTS + +NSSVTSTSSSTTSSSSSSSSN | MARK: 10100011 00000000 (valid room number) +@ TOS> room_number +@ Move player to new room +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (ptr to player location) +SNT | SWAP +TTS | STORE +@ Check for wumpus in new player location +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (ptr to player location) +TTT | LOAD +NSTTSTSSSSSN | JSR > 10100000 (room_has_wumpus) +NTSTSTSSSTTSSSSSTSSN | BRZ > 10100011 00000100 (no wumpus in new room) +NSTTTTTTTTTSSSSSSSSN | JSR > 11111111 00000000 (wump_kill) +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NNN | DIE +NSSVTSTSSSTTSSSSSTSSN | MARK: 10100011 00000100 (no wumpus in new room) +@ Check for pits in new player location +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (ptr to player location) +TTT | LOAD +NSTTSSSTTSTN | JSR > 10001101 (room_has_pits) +NTSTSTSSSTTSSSSSTSTN | BRZ > 10100011 00000101 (no pits in new room) +NSTTSSSSN | JSR > 10000 (random) +SSSTTSN | PUSH 6 (chance) +TSTT | MODULO +NTSTSTSSSTTSSSSSTTSN | BRZ > 10100011 00000110 (survived the pits) +NSTTTTTTTTTSSSSSTSTN | JSR > 11111111 00000101 (pit_kill) +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NNN | DIE +NSSVTSTSSSTTSSSSSTTSN | MARK: 10100011 00000110 (survived the pits) +NSTTTTTTTTTSSSSSTTSN | JSR > 11111111 00000110 (pit_survive) +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSSVTSTSSSTTSSSSSTSTN | MARK: 10100011 00000101 (no pits in new room) +@ Check for bats in new player location +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (ptr to player location) +TTT | LOAD +NSTTSSSTTSSN | JSR > 10001100 (room_has_bats) +NTSTSTSSSTTSSSSSTTSN | BRZ > 10100011 00000110 (no bats in new room) +A"*flap* *flap* *flap* (humongous bats pick you up and move you!)\n" +SSSSN | PUSH 0 (number of string substitutions) +NSTTSSSN | JSR > 1000 (printf) +NSTTSSSSN | JSR > 10000 (random) +SSSTSSSSSSSSSSTTN | PUSH 0x1003 (ptr to number of tunnels per room) +TSTT | MODULO +SSSTSSSSSSSSSTTSN | PUSH 0x1006 (ptr to player location) +TTT | LOAD +NSTTSSSTSSSN | JSR > 10001000 (get_tunnel_destination) +NSNTSTSSSTTSSSSSSSSN | JMP > 10100011 00000000 (valid room number) +NSSVTSTSSSTTSSSSSTTSN | MARK: 10100011 00000110 (no bats in new room) +NTN | RTS + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ Name: @ move_or_shoot @@ -93,7 +257,7 @@ NSNTSTSSSTSN | JMP > 10100010 (move_or_shoot) @ User typed 'm' NSSVTSTSSSTSSSSSSSSSN | MARK: 10100010 00000000 (move) -@ TODO: JSR move +NSTTSTSSSTTN | JSR > 10100011 (move_player) NTN | RTS @ User typed 's' -- 2.20.1 From 6464ec02c9fe89f5092c3b942f67d752895f24bd Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Fri, 13 Mar 2020 00:27:16 -0700 Subject: [PATCH 12/16] Added `syntax_highlighting/` folder and instructions for vim and neatvi. --- syntax_highlighting/README.md | 107 ++++++++++++++++++ syntax_highlighting/vvhitespace.vim | 42 +++++++ .../vvs_syntax_highlighting.gif | Bin 0 -> 102250 bytes 3 files changed, 149 insertions(+) create mode 100644 syntax_highlighting/README.md create mode 100644 syntax_highlighting/vvhitespace.vim create mode 100644 syntax_highlighting/vvs_syntax_highlighting.gif diff --git a/syntax_highlighting/README.md b/syntax_highlighting/README.md new file mode 100644 index 0000000..0228471 --- /dev/null +++ b/syntax_highlighting/README.md @@ -0,0 +1,107 @@ +VVhitespace Syntax Highlighting +=============================== + +![VVS Syntax Highlighting Example](vvs_syntax_highlighting.gif) + +I find it useful to highlight each IMP a different color as well as highlight +code comments and label definitions. The above image is typical of my +VVhitespace editing environment. + +If you would like something similar, the configuration examples below can be +used with `vim` or `neatvi`. Other editors that use regex matching can reuse +the regular expressions from these examples to achieve similar results. + +This syntax highlighting definition works only with the way I write VVhitespace +code in this repository. Comments in particular can take many forms other +than specified here. + +If you take the time to put together a configuration for other editors, or +improve the regex definitions, let me know. + + +VIM Highlighting +---------------- + +If they don't already exist, create the folders `~/.vim/syntax` and +`~/.vim/ftdetect`. + + % mkdir -p ~/.vim/syntax + % mkdir -p ~/.vim/ftdetect + +Create a file named `~/.vim/ftdetect/vvhitespace.vim` to detect the filetype +based on the file extension. It should contain a single line: + + au BufRead,BufNewFile *.pvvs set filetype=vvhitespace + +Create a file named `~/.vim/syntax/vvhitespace.vim` containing the following: + + if exists("b:current_syntax") + finish + endif + + let b:current_synax = "vvhitespace" + + " Stack IMP + syn match stackIMP '^[sS][sS]' + syn match stackIMP '^[sS][nN][sStTnN]' + + " Arithmetic IMP + syn match arithIMP '^[tT][sS][sStT][sStTnN]' + + " Heap IMP + syn match heapIMP '^[tT][tT][sStT]' + + " Control Flow IMP + syn match controlIMP '^[nN][nNsStT][nNsStT]' + + " IO IMP + syn match ioIMP '^[tT][nN][sStT][sStT]' + + " Label Definition + syn match labelDef 'V[sStT]\+N' + + " Comments + syn match commentBlock '|.*$' + syn match commentBlock '@.*$' + + " Define colors using existing Highlight Groups. + " To see a list of other Highlight Groups, type `:highlight` in vim. + hi def link stackIMP Type + hi def link arithIMP ModeMsg + hi def link heapIMP Special + hi def link controlIMP Constant + hi def link ioIMP Keyword + hi def link labelDef Function + hi def link commentBlock NonText + +That's it! You should now see syntax highlighting when opening files with names +ending in `*.pvvs`. + +If you don't any see syntax highlighting, check your `.vimrc` and other config +locations for conflicting options. Try manually enabling syntax highlighting +with the `syntax on` configuration directive. + + +NeatVI Highlighting +------------------- + +Open the file `conf.h` for editing. + +Add the following line to the `filetypes` array, matching all `*.pvvs` files. + + {"pvvs", "\\.pvvs$"}, /* VVhitespace */ + +Add the following block to the `highlights` array. + + {"pvvs", {2}, "^[sS][sS]"}, /* IMP: Stack Manipulation */ + {"pvvs", {2}, "^[sS][nN][sStTnN]"}, /* IMP: Stack Manipulation */ + {"pvvs", {8}, "^[tT][sS][sStT][sStTnN]"}, /* IMP: Arithmetic */ + {"pvvs", {5}, "^[tT][tT][sStT]"}, /* IMP: Heap Access */ + {"pvvs", {1}, "^[nN][nNsStT][nNsStT]"}, /* IMP: Control Flow */ + {"pvvs", {4}, "^[tT][nN][sStT][sStT]"}, /* IMP: Input/Output */ + {"pvvs", {6 | SYN_BD}, "V[sStT]+N "}, /* Label Definitions */ + {"pvvs", {3 | SYN_IT}, "\\|.*$"}, /* Comments */ + {"pvvs", {3 | SYN_IT}, "\\@.*$"}, /* Comments */ + +Recompile NeatVI and syntax highlighting should work on all files with names +ending in `*.pvvs`. diff --git a/syntax_highlighting/vvhitespace.vim b/syntax_highlighting/vvhitespace.vim new file mode 100644 index 0000000..8c0e095 --- /dev/null +++ b/syntax_highlighting/vvhitespace.vim @@ -0,0 +1,42 @@ +" Vim syntax file +" Language: VVhitespace +" Based on: https://vim.fandom.com/wiki/Creating_your_own_syntax_files + +if exists("b:current_syntax") + finish +endif + +let b:current_synax = "vvhitespace" + +" Stack IMP +syn match stackIMP '^[sS][sS]' +syn match stackIMP '^[sS][nN][sStTnN]' + +" Arithmetic IMP +syn match arithIMP '^[tT][sS][sStT][sStTnN]' + +" Heap IMP +syn match heapIMP '^[tT][tT][sStT]' + +" Control Flow IMP +syn match controlIMP '^[nN][nNsStT][nNsStT]' + +" IO IMP +syn match ioIMP '^[tT][nN][sStT][sStT]' + +" Label Definition +syn match labelDef 'V[sStT]\+N' + +" Comments +syn match commentBlock '|.*$' +syn match commentBlock '@.*$' + +" Define colors using existing Highlight Groups. +" To see a list of other Highlight Groups, type `:highlight` in vim. +hi def link stackIMP Type +hi def link arithIMP ModeMsg +hi def link heapIMP Special +hi def link controlIMP Constant +hi def link ioIMP Keyword +hi def link labelDef Function +hi def link commentBlock NonText diff --git a/syntax_highlighting/vvs_syntax_highlighting.gif b/syntax_highlighting/vvs_syntax_highlighting.gif new file mode 100644 index 0000000000000000000000000000000000000000..69fdcf0410a55414458224821d543ec1ea046f90 GIT binary patch literal 102250 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 literal 0 HcmV?d00001 -- 2.20.1 From 3e7318181b224315b471f85c0ab9a8f73471075b Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Fri, 13 Mar 2020 02:12:33 -0700 Subject: [PATCH 13/16] Updated `rationale.md`, expanding previous notes. --- rationale.md | 86 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/rationale.md b/rationale.md index b19f462..2499caa 100644 --- a/rationale.md +++ b/rationale.md @@ -1,36 +1,58 @@ -VVhitespace is descended from Whitespace, adding a vertical tab to the language -along with some restrictions to ease implementation. The name is intended to -embrace the spirit of Whitespace's visual incomprehensibility. +While considering methods for further obfuscating Whitespace code, I realized +that the Whitespace language reference didn't enforce any particular alignment +or grouping for code execution. In theory this means one could create a +sequence of Whitespace trits which represents different sequences of commands +depending on exactly where execution begins. -Since Whitespace ignores [VTab] as a comment character, and since the -Whitespace VM is a superset of the VVhitespace VM, all valid VVhitespace -programs are also valid Whitespace programs. +One implementation of this idea is the creation of a 'hidden' label. For +example, the command sequence --------------------------------------------------------------------------------- + PUSH 0; DROP; PUSH 46 + +assembles as + + SSSSN SNN SSSTSTTTSN -TODO: Finish this up based on what I've found below. +which can be visually regrouped as -It seems that all (most?) WS interpreters locate labels using one of two methods. + SSSSNSN NSSSTSTTTSN + +and contains the `MARK label0` command used in the next set of examples. + +Additionally, since `PUSH 0; DROP` is effectively a `NOP`, 'hijacking' the code +at this location allows one to insert their own integer on the stack in place +of the `PUSH 46` command, sneakily substituting it as an input to any +downstream processing. + +I decided to investigate the behavior of specific Whitespace interpreters, +discovering that they broke down into two methods for locating labels. * **Method 1** Scan from the start of the file for the first occurence of the mark-label bytestring and jump. + + Example: whitespacers/c: (c) meth0dz * **Method 2** Scan from the start of the file, looking for a mark-label bytestring, but 'parsing' one bytestring at a time, and jumping to the first 'standalone' mark-label bytestring. Note that this is different than executing the program, particularly when user-input commands are present. + + Example: whitespacers/haskell: (c) 2003 Edwin Brady + whitespacers/ruby: (c) 2003 by Wayne E. Conrad + whitespacers/perl: (c) 2003 Micheal Koelbl + threeifbywhiskey/satan -Both of these methods can be broken: +Both of these methods can be broken using valid Whitespace code: - * Type 1: No 'standalone' label exists. This breaks Method 2. + * Type A: No 'standalone' label exists. This breaks Method 2. - This should print a '!' before infinite '.' lines. + By programmer's intent, this should print a '!' before infinite '.' lines. - * Type 2: Hidden label before 'standalone' label. This breaks Method 1. + * Type B: Hidden label before 'standalone' label. This breaks Method 1. - This should print an infinite chain of '.' lines. + By programmer's intent, this should print an infinite chain of '.' lines. -This is the Type 1 program: +This is the Type A program: SSSTSSSSTN | Push +33 (ASCII !) NSNSTSTTTSN | JMP>label0 @@ -43,18 +65,34 @@ This is the Type 1 program: TNSS | Output character NSNTTTTN | JMP>label2 -Append this to turn it into the Type 2 program: +Append this to turn it into the Type B program: NSSSTSTTTSN | MARK label0 (2nd time) NSNTTTTN | JMP>label2 -WS Interpreter Evaluations: +VVhitespace avoids this ambiguity by marking label definitions with a vertical +tab `[VTab]` immediately before the label. + + Old label: NSS TSTS N + New label: NSSV TSTS N + +Since Whitespace ignores [VTab] as a comment character, and since the +Whitespace VM is a superset of the VVhitespace VM, all valid VVhitespace +programs are also valid Whitespace programs, though the task of locating a +suitable Whitespace interpreter is left for the reader. + +-------------------------------------------------------------------------------- + +Quoting from the original Whitespace tutorial which I used as language reference: + + The programmer is free to push arbitrary width integers onto the stack. + +I have yet to find a Whitespace interpreter which successfully implements that +statement. + +Since I wanted to implement bitwise logic functions, this broad definition +posed a problem. For example, how many `1`s should be in the output of the +expression `NOT(0)`? Should the expression `NOT(0) == NOT(0)` always be true? - Method 1: - whitespacers/c: (c) meth0dz +VVhitespace sidesteps the problem by declaring all integers to be 64-bits wide. - Method 2: - whitespacers/ruby: (c) 2003 by Wayne E. Conrad - whitespacers/perl: (c) 2003 Micheal Koelbl - whitespacers/haskell: (c) 2003 Edwin Brady - threeifbywhiskey/satan -- 2.20.1 From 4dc56bba8fe9a2cf8eea585d56f34af277d214b9 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Fri, 13 Mar 2020 02:18:44 -0700 Subject: [PATCH 14/16] Small updates to `rationale.md` for display on Github. --- rationale.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/rationale.md b/rationale.md index 2499caa..d2c06c1 100644 --- a/rationale.md +++ b/rationale.md @@ -17,7 +17,8 @@ which can be visually regrouped as SSSSNSN NSSSTSTTTSN -and contains the `MARK label0` command used in the next set of examples. +and contains the `MARK label0` command (i.e. `NSS STSTTTS N`) used in the next +set of examples. Additionally, since `PUSH 0; DROP` is effectively a `NOP`, 'hijacking' the code at this location allows one to insert their own integer on the stack in place @@ -30,17 +31,24 @@ discovering that they broke down into two methods for locating labels. * **Method 1** Scan from the start of the file for the first occurence of the mark-label bytestring and jump. - Example: whitespacers/c: (c) meth0dz + Examples: + + whitespacers/c: (c) meth0dz * **Method 2** Scan from the start of the file, looking for a mark-label bytestring, but 'parsing' one bytestring at a time, and jumping to the first 'standalone' mark-label bytestring. Note that this is different than executing the program, particularly when user-input commands are present. - Example: whitespacers/haskell: (c) 2003 Edwin Brady - whitespacers/ruby: (c) 2003 by Wayne E. Conrad - whitespacers/perl: (c) 2003 Micheal Koelbl - threeifbywhiskey/satan + Examples: + + whitespacers/haskell: (c) 2003 Edwin Brady + + whitespacers/ruby: (c) 2003 by Wayne E. Conrad + + whitespacers/perl: (c) 2003 Micheal Koelbl + + threeifbywhiskey/satan Both of these methods can be broken using valid Whitespace code: -- 2.20.1 From 2d2ef3fec003d3a651467c7ddd72f612c7334914 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Fri, 13 Mar 2020 02:23:42 -0700 Subject: [PATCH 15/16] More minor updates to `rationale.md`. --- rationale.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/rationale.md b/rationale.md index d2c06c1..0f6de45 100644 --- a/rationale.md +++ b/rationale.md @@ -17,7 +17,7 @@ which can be visually regrouped as SSSSNSN NSSSTSTTTSN -and contains the `MARK label0` command (i.e. `NSS STSTTTS N`) used in the next +and contains the `MARK label0` command (i.e. `NSSSTSTTTSN`) used in the next set of examples. Additionally, since `PUSH 0; DROP` is effectively a `NOP`, 'hijacking' the code @@ -43,40 +43,37 @@ discovering that they broke down into two methods for locating labels. Examples: whitespacers/haskell: (c) 2003 Edwin Brady - whitespacers/ruby: (c) 2003 by Wayne E. Conrad - whitespacers/perl: (c) 2003 Micheal Koelbl - threeifbywhiskey/satan Both of these methods can be broken using valid Whitespace code: - * Type A: No 'standalone' label exists. This breaks Method 2. + * **Type A**: No 'standalone' label exists. This breaks Method 2. By programmer's intent, this should print a '!' before infinite '.' lines. - * Type B: Hidden label before 'standalone' label. This breaks Method 1. + * **Type B**: Hidden label before 'standalone' label. This breaks Method 1. By programmer's intent, this should print an infinite chain of '.' lines. This is the Type A program: - SSSTSSSSTN | Push +33 (ASCII !) - NSNSTSTTTSN | JMP>label0 - NSSTTTTN | MARK label2 - SSSSN | PUSH +0 + SSSTSSSSTN | Push +33 (ASCII '!') + NSNSTSTTTSN | JMP > 0101110 (label0) + NSSTTTTN | MARK: 1111 (label2) + SSSSN | PUSH 0 SNN | DROP - SSSTSTTTSN | Push +46 (ASCII .) + SSSTSTTTSN | Push +46 (ASCII '.') TNSS | Output character - SSSTSTSN | Push +10 (ASCII newline) + SSSTSTSN | Push +10 (ASCII '\n') TNSS | Output character - NSNTTTTN | JMP>label2 + NSNTTTTN | JMP > 1111 (label2) Append this to turn it into the Type B program: - NSSSTSTTTSN | MARK label0 (2nd time) - NSNTTTTN | JMP>label2 + NSSSTSTTTSN | MARK: 0101110 (label0) (2nd time) + NSNTTTTN | JMP > 1111 (label2) VVhitespace avoids this ambiguity by marking label definitions with a vertical tab `[VTab]` immediately before the label. -- 2.20.1 From e5ba12f2147dfdc42ed1531dfacb2565f6f660a3 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Fri, 13 Mar 2020 02:33:16 -0700 Subject: [PATCH 16/16] Final (?) set of small changes to `rationale.md`. --- rationale.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rationale.md b/rationale.md index 0f6de45..c847fab 100644 --- a/rationale.md +++ b/rationale.md @@ -51,11 +51,11 @@ Both of these methods can be broken using valid Whitespace code: * **Type A**: No 'standalone' label exists. This breaks Method 2. - By programmer's intent, this should print a '!' before infinite '.' lines. + By programmer's intent, this should print a `!` before infinite `.` lines. * **Type B**: Hidden label before 'standalone' label. This breaks Method 1. - By programmer's intent, this should print an infinite chain of '.' lines. + By programmer's intent, this should print an infinite chain of `.` lines. This is the Type A program: -- 2.20.1