--- /dev/null
+! Praxix - a Z-Machine unit test
+! by Zarf and Dannii
+! Public domain, but please share your changes so we can improve the tests
+
+! The indirect test is Copyright (C) 2003, Amir Karger
+
+! TODO:
+! @encode_text
+! @erase_line
+! @get_cursor
+! @set_font
+! @call 0
+! @random - check it's a good distribution
+! @print_table - check line breaks are only printed between lines
+! Consider making this test suite non-interactive, and have another test suite for interactive tests
+! Move too any tests which are manually confirmed by eye, so that all of Praxix can be summed up by "All tests passed"
+! Maybe add tests for @loadw/@storew etc to check they are casting array+word correctly
+! 1.1 spec unicode texts
+
+Constant Story "Praxix";
+Constant Headline "A Z-code interpreter unit test^";
+Release 1;
+
+! To compile this canonically, do "inform praxix.inf". No other
+! options.
+
+Constant HDR_GAMERELEASE $02; ! word
+Constant HDR_GAMEFLAGS $10; ! word
+Constant HDR_GAMESERIAL $12; ! six ASCII characters
+Constant HDR_SPECREVISION $32; ! word
+
+Array buffer -> 123; ! Buffer for parsing main line of input
+Array parse buffer 63; ! Parse table mirroring it
+
+Global failures = 0;
+Global total_failures = 0;
+
+[ Main ix;
+ ! deal with some compiler warnings
+ ix = PrintShortName;
+
+ new_line;
+ Banner();
+ new_line;
+
+ LookSub();
+ TestLoop();
+
+ print "^Goodbye.^";
+];
+
+[ Keyboard;
+ while (true) {
+ print ">";
+ buffer->0 = 120;
+ parse->0 = 15;
+ read buffer parse;
+ if (parse->1)
+ break;
+ }
+];
+
+[ Banner i;
+ if (Story ~= 0) {
+ #IfV5; style bold; #Endif;
+ print (string) Story;
+ #IfV5; style roman; #Endif;
+ }
+ if (Headline ~= 0) print ": ", (string) Headline;
+ print "Release ", (HDR_GAMERELEASE-->0) & $03ff, " / Serial number ";
+ for (i=0 : i<6 : i++) print (char) HDR_GAMESERIAL->i;
+ print " / Inform v"; inversion;
+ print ", compiler options ";
+ #Ifdef STRICT_MODE;
+ print "S";
+ #Endif; ! STRICT_MODE
+ #Ifdef INFIX;
+ print "X";
+ #Ifnot;
+ #Ifdef DEBUG;
+ print "D";
+ #Endif; ! DEBUG
+ #Endif; ! INFIX
+ new_line;
+];
+
+[ Version;
+ ! Print the version number
+ print HDR_SPECREVISION->0, ".", HDR_SPECREVISION->1;
+];
+
+[ TestLoop wd paddr plen ix obj found;
+ while (true) {
+ new_line;
+ if (failures) {
+ print failures, " uncounted test failures!^^";
+ failures = 0;
+ }
+
+ Keyboard();
+ wd = parse-->1;
+
+ if (wd == 'quit' or 'q//')
+ return;
+
+ found = nothing;
+
+ objectloop (obj ofclass TestClass) {
+ paddr = obj.&name;
+ plen = obj.#name / WORDSIZE;
+ for (ix=0 : ix<plen : ix++) {
+ if (paddr-->ix == wd) {
+ found = obj;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ if (~~found) {
+ print "I don't understand that command.^";
+ continue;
+ }
+
+ found.testfunc();
+ }
+];
+
+Attribute meta;
+
+Class TestClass
+ with
+ short_name 0,
+ testfunc TestNothing,
+ fail_count;
+
+[ PrintShortName obj addr;
+ if (obj provides short_name && obj.short_name) {
+ print (string) obj.short_name;
+ rtrue;
+ }
+ if (obj ofclass TestClass) {
+ addr = obj.&name;
+ print (address) (addr-->0);
+ rtrue;
+ }
+ print (object) obj;
+];
+
+! Print out in base 16 a byte or word
+[ Hex val byte nibble i;
+ print "$";
+ for (i=0 : i<2 : i++)
+ {
+ @log_shift val (-8) -> byte;
+ @log_shift val (8) -> val;
+ if (byte == 0 && i == 0)
+ continue;
+ nibble = byte & $0F;
+ @log_shift byte (-4) -> byte;
+ if (byte <= 9)
+ print (char) (byte+'0');
+ else
+ print (char) (byte-10+'A');
+ if (nibble <= 9)
+ print (char) (nibble+'0');
+ else
+ print (char) (nibble-10+'A');
+ }
+];
+
+[ check val wanted;
+ if (val == wanted) {
+ print val;
+ rtrue;
+ }
+ failures++;
+ print val, " (should be ", wanted, " FAIL)";
+ rfalse;
+];
+
+[ check_unspecified val wanted;
+ if (val == wanted) {
+ print val, " (Unspecified)";
+ rtrue;
+ }
+ print val, " (Unspecified but ", wanted, " is suggested)";
+ rfalse;
+];
+
+[ check_arr_3 arr v0 v1 v2;
+ if (arr->0 == v0 && arr->1 == v1 && arr->2 == v2) {
+ print v0, " ", v1, " ", v2;
+ rtrue;
+ }
+ failures++;
+ print arr->0, " ", arr->1, " ", arr->2;
+ print " (should be ", v0, " ", v1, " ", v2, " FAIL)";
+ rfalse;
+];
+
+[ check_hex val wanted;
+ if (val == wanted) {
+ print (Hex) val;
+ rtrue;
+ }
+ failures++;
+ print (Hex) val, " (should be ", (Hex) wanted, " FAIL)";
+ rfalse;
+];
+
+[ check_hex_unspecified val wanted;
+ if (val == wanted) {
+ print (Hex) val, " (Unspecified)";
+ rtrue;
+ }
+ print (Hex) val, " (Unspecified but ", wanted, " is suggested)";
+ rfalse;
+];
+
+[ check_hex_min val min;
+ if (val >= min) {
+ print (Hex) val;
+ rtrue;
+ }
+ failures++;
+ print (Hex) val, " (should be >= ", (Hex) min, " FAIL)";
+ rfalse;
+];
+
+[ check_array test gold len func
+ ix testch goldch;
+ for (ix=0 : ix<len : ix++) {
+ goldch = gold->ix;
+ testch = test->ix;
+ if (testch ~= goldch) {
+ func(goldch, ix);
+ check(testch, goldch);
+ new_line;
+ }
+ }
+];
+
+[ count_failures val;
+ print "^";
+ if (failures) {
+ val = failures;
+ total_failures = total_failures + failures;
+ failures = 0;
+ print_ret val, " tests failed.";
+ }
+ else {
+ "Passed.";
+ }
+];
+
+TestClass LookAction
+ with name 'look' 'l//' 'help' '?//',
+ testfunc LookSub,
+ has meta;
+
+TestClass AllAction
+ with name 'all',
+ testfunc [ obj startfail res ix;
+ print "All tests:^";
+ startfail = total_failures;
+ objectloop (obj ofclass TestClass) {
+ if (obj has meta)
+ continue;
+
+ res = total_failures;
+ print "^";
+ obj.testfunc();
+ obj.fail_count = total_failures - res;
+ }
+ res = total_failures - startfail;
+ if (res == 0) {
+ "^All tests passed.";
+ }
+ else {
+ print "^", res, " tests failed overall: ";
+ ix = 0;
+ objectloop (obj ofclass TestClass) {
+ if (obj.fail_count) {
+ if (ix)
+ print ", ";
+ print (name) obj, " (", obj.fail_count, ")";
+ ix++;
+ }
+ }
+ ".";
+ }
+ ],
+ has meta;
+
+[ TestNothing;
+ "Nothing happens.";
+];
+
+[ LookSub obj ix;
+ print "A voice booooms out: Welcome to the test chamber.^^";
+ print "Type ~help~ to repeat this message, ~quit~ to exit,
+ ~all~ to run all tests, or one of the following test options: ";
+
+ ix = 0;
+ objectloop (obj ofclass TestClass) {
+ if (obj has meta)
+ continue;
+ if (ix) print ", ";
+ print "~", (name) obj, "~";
+ ix++;
+ }
+ print ".";
+
+ print "^(Some tests check unspecified behaviour, and their results will be marked by (Unspecified).)^";
+
+ new_line;
+ if (total_failures) {
+ print "^", total_failures, " tests have failed so far in this run.^";
+ }
+];
+
+Global testglobal;
+Global testglobal2;
+
+TestClass OperandTest
+ with name 'operand',
+ testfunc [ ix val;
+ print "Basic operand values:^^";
+
+ testglobal = 1;
+ ix = 1;
+ val = (ix == testglobal);
+ print "(1==1)="; check(val, 1); print ", ";
+ val = (1 == testglobal);
+ print "(1==1)="; check(val, 1); print ", ";
+ val = (1 == ix);
+ print "(1==1)="; check(val, 1); print ", ";
+ @push 1;
+ val = 1;
+ @je 1 sp ?jump1;
+ val = 0;
+ .jump1;
+ print "(1==1)="; check(val, 1); print "^";
+
+ testglobal = -2;
+ ix = -2;
+ val = (ix == testglobal);
+ print "(-2==-2)="; check(val, 1); print ", ";
+ val = (-2 == testglobal);
+ print "(-2==-2)="; check(val, 1); print ", ";
+ val = (-2 == ix);
+ print "(-2==-2)="; check(val, 1); print ", ";
+ @push (-2);
+ val = 1;
+ @je (-2) sp ?jump2;
+ val = 0;
+ .jump2;
+ print "(-2==-2)="; check(val, 1); print "^";
+
+ count_failures();
+ ];
+
+
+TestClass ArithTest
+ with name 'arith',
+ testfunc [ val;
+ print "Integer arithmetic:^^";
+
+ @add 2 2 val;
+ print "2+2="; check(val, 4); print ", ";
+ @add (-2) (-3) val;
+ print "-2+-3="; check(val, -5); print ", ";
+ @add 3 (-4) val;
+ print "3+-4="; check(val, -1); print ", ";
+ @add (-4) 5 val;
+ print "-4+5="; check(val, 1); print ", ";
+ @add $7FFF $7FFE val;
+ print "$7FFF+$7FFE="; check(val, -3); print ", ";
+ @add $8000 $8000 val;
+ print "$8000+$8000="; check(val, 0); print "^";
+ testglobal = 6; testglobal2 = 8;
+ @add testglobal testglobal2 val;
+ print "Globals 6+8="; check(val, 14); print ", ";
+ testglobal = $7FFE; testglobal2 = $7FFD;
+ @add testglobal testglobal2 val;
+ print "$7FFE+$7FFD="; check(val, -5); print "^";
+
+ @sub 2 2 val;
+ print "2-2="; check(val, 0); print ", ";
+ @sub (-2) 3 val;
+ print "-2-3="; check(val, -5); print ", ";
+ @sub 3 4 val;
+ print "3-4="; check(val, -1); print ", ";
+ @sub (-4) (-5) val;
+ print "-4-(-5)="; check(val, 1); print ", ";
+ @sub $7FFF $7FFE val;
+ print "$7FFF-$7FFE="; check(val, 1); print ", ";
+ @sub $8000 $8001 val;
+ print "$8000-$8001="; check(val, -1); print ", ";
+ @sub $7FFF $8001 val;
+ print "$7FFF-$8001="; check(val, -2); print "^";
+ testglobal = 6; testglobal2 = 8;
+ @sub testglobal testglobal2 val;
+ print "Globals 6-8="; check(val, -2); print ", ";
+ testglobal = $7FFD; testglobal2 = $7FFE;
+ @sub testglobal testglobal2 val;
+ print "$7FFD-$7FFE="; check(val, -1); print "^";
+
+ @mul 2 2 val;
+ print "2*2="; check(val, 4); print ", ";
+ @mul (-2) (-3) val;
+ print "-2*-3="; check(val, 6); print ", ";
+ @mul 3 (-4) val;
+ print "3*-4="; check(val, -12); print ", ";
+ @mul (-4) 5 val;
+ print "-4*5="; check(val, -20); print ", ";
+ @mul $100 $100 val;
+ print "$100*$100 (trunc)="; check(val, 0); print ", ";
+ @mul 311 373 val;
+ print "311*373 (trunc)="; check_hex(val, -15069); print "^";
+ testglobal = -6; testglobal2 = -8;
+ @mul testglobal testglobal2 val;
+ print "Globals -6*-8="; check(val, 48); print ", ";
+ testglobal = -311; testglobal2 = 373;
+ @mul testglobal testglobal2 val;
+ print "Globals -311*373="; check(val, 15069); print "^";
+
+ @div 12 3 val;
+ print "12/3="; check(val, 4); print ", ";
+ @div 11 2 val;
+ print "11/2="; check(val, 5); print ", ";
+ @div (-11) 2 val;
+ print "-11/2="; check(val, -5); print ", ";
+ @div 11 (-2) val;
+ print "11/-2="; check(val, -5); print ", ";
+ @div (-11) (-2) val;
+ print "-11/-2="; check(val, 5); print ", ";
+ @div $7fff 2 val;
+ print "$7fff/2="; check_hex(val, $3fff); print ", ";
+ @div $7fff (-2) val;
+ print "$7fff/-2="; check_hex(val, -$3fff); print ", ";
+ @div (-$7fff) 2 val;
+ print "-$7fff/2="; check_hex(val, -$3fff); print ", ";
+ @div (-$7fff) (-2) val;
+ print "-$7fff/-2="; check_hex(val, $3fff); print ", ";
+ @div $8000 2 val;
+ print "$8000/2="; check_hex(val, $C000); print ", ";
+ @div $8000 (-2) val;
+ print "$8000/(-2)="; check_hex(val, $4000); print ", ";
+ @div $8000 1 val;
+ print "$8000/1="; check_hex(val, $8000); print "^";
+
+ testglobal = -48; testglobal2 = -8;
+ @div testglobal testglobal2 val;
+ print "Globals -48/-8="; check(val, 6); print ", ";
+ testglobal = 48; testglobal2 = 7;
+ @div testglobal testglobal2 val;
+ print "48/7="; check(val, 6); print ", ";
+ testglobal = 48; testglobal2 = -7;
+ @div testglobal testglobal2 val;
+ print "48/-7="; check(val, -6); print ", ";
+ testglobal = -48; testglobal2 = 7;
+ @div testglobal testglobal2 val;
+ print "-48/7="; check(val, -6); print ", ";
+ testglobal = -48; testglobal2 = -7;
+ @div testglobal testglobal2 val;
+ print "-48/-7="; check(val, 6); print "^";
+
+ @mod 12 3 val;
+ print "12%3="; check(val, 0); print ", ";
+ @mod 13 5 val;
+ print "13%5="; check(val, 3); print ", ";
+ @mod (-13) 5 val;
+ print "-13%5="; check(val, -3); print ", ";
+ @mod 13 (-5) val;
+ print "13%-5="; check(val, 3); print ", ";
+ @mod (-13) (-5) val;
+ print "-13%-5="; check(val, -3); print ", ";
+ @mod $7fff 11 val;
+ print "$7fff%11="; check(val, 9); print ", ";
+ @mod (-$7fff) 11 val;
+ print "-$7fff%11="; check(val, -9); print ", ";
+ @mod $7fff (-11) val;
+ print "$7fff%-11="; check(val, 9); print ", ";
+ @mod (-$7fff) (-11) val;
+ print "-$7fff%-11="; check(val, -9); print ", ";
+ @mod $8000 7 val;
+ print "$8000%7="; check(val, -1); print ", ";
+ @mod $8000 (-7) val;
+ print "$8000%-7="; check(val, -1); print ", ";
+ @mod $8000 2 val;
+ print "$8000%2="; check(val, 0); print ", ";
+ @mod $8000 (-2) val;
+ print "$8000%-2="; check(val, 0); print ", ";
+ @mod $8000 1 val;
+ print "$8000%1="; check(val, 0); print "^";
+
+ testglobal = 49; testglobal2 = 8;
+ @mod testglobal testglobal2 val;
+ print "Globals 49%8="; check(val, 1); print ", ";
+ testglobal = 49; testglobal2 = -8;
+ @mod testglobal testglobal2 val;
+ print "49%-8="; check(val, 1); print ", ";
+ testglobal = -49; testglobal2 = 8;
+ @mod testglobal testglobal2 val;
+ print "-49%8="; check(val, -1); print ", ";
+ testglobal = -49; testglobal2 = -8;
+ @mod testglobal testglobal2 val;
+ print "-49%-8="; check(val, -1); print "^";
+
+ count_failures();
+ ];
+
+TestClass CompoundArithTest
+ with name 'comarith' 'comparith',
+ testfunc [ val xloc yloc zloc;
+ print "Compound arithmetic expressions:^^";
+
+ testglobal = 7;
+ yloc = 2;
+ zloc = -4;
+ val = (testglobal + yloc) * zloc;
+ print "(7+2)*-4="; check(val, -36); print "^";
+
+ xloc = $7FFF;
+ yloc = 2;
+ zloc = 16;
+ val = (xloc + yloc) / zloc;
+ print "($7FFF+2)/16="; check(val, -$7FF); print "^";
+
+ xloc = -$7FFF;
+ yloc = -2;
+ zloc = 16;
+ val = (xloc + yloc) / zloc;
+ print "(-$7FFF+-2)/16="; check(val, $7FF); print "^";
+
+ xloc = -26103;
+ yloc = -32647;
+ val = (xloc + yloc) / 9;
+ print "(-26103+-32647)/9="; check(val, 754); print "^";
+
+ xloc = -$7FFF;
+ yloc = 2;
+ zloc = 16;
+ val = (xloc - yloc) / zloc;
+ print "(-$7FFF-2)/16="; check(val, $7FF); print "^";
+
+ xloc = $7FFF;
+ yloc = -2;
+ zloc = 16;
+ val = (xloc - yloc) / zloc;
+ print "($7FFF--2)/16="; check(val, -$7FF); print "^";
+
+ xloc = -26103;
+ yloc = 32647;
+ val = (xloc - yloc) / 9;
+ print "(-26103-32647)/9="; check(val, 754); print "^";
+
+ xloc = $100;
+ yloc = $100;
+ zloc = 16;
+ val = (xloc * yloc) / zloc + 1;
+ print "($100*$100)/16+1="; check(val, 1); print "^";
+
+ xloc = 311;
+ yloc = 373;
+ zloc = 16;
+ val = (xloc * yloc) / zloc;
+ print "(311*373)/16="; check(val, -941); print "^";
+
+ xloc = 311;
+ zloc = 16;
+ val = (xloc * -373) / zloc;
+ print "(311*-373)/16="; check(val, 941); print "^";
+
+ yloc = 373;
+ val = (111 * yloc) / 16;
+ print "(111*373)/16="; check(val, -1508); print "^";
+
+ yloc = -373;
+ val = (111 * yloc) / 16;
+ print "(111*-373)/16="; check(val, 1508); print "^";
+
+ count_failures();
+ ];
+
+TestClass BitwiseTest
+ with name 'bitwise' 'bits' 'bit',
+ testfunc [ val;
+ print "Bitwise arithmetic:^^";
+
+ @and 0 0 val;
+ print "0&0="; check_hex(val, 0); print ", ";
+ @and $FFFF 0 val;
+ print "$FFFF&0="; check_hex(val, 0); print ", ";
+ @and $FFFF $FFFF val;
+ print "$FFFF&$FFFF="; check_hex(val, $FFFF); print ", ";
+ @and $013F $F310 val;
+ print "$013F&$F310="; check_hex(val, $0110); print ", ";
+ @and $F731 $137F val;
+ print "$F731&$137F="; check_hex(val, $1331); print ", ";
+ @and $35 $56 val;
+ print "$35&56="; check_hex(val, $14); print "^";
+
+ @or 0 0 val;
+ print "0|0="; check_hex(val, 0); print ", ";
+ @or $FFFF 0 val;
+ print "$FFFF|0="; check_hex(val, $FFFF); print ", ";
+ @or $FFFF $FFFF val;
+ print "$FFFF|$FFFF="; check_hex(val, $FFFF); print ", ";
+ @or $3700 $0012 val;
+ print "$3700|$0012="; check_hex(val, $3712); print ", ";
+ @or $35 $56 val;
+ print "$35|56="; check_hex(val, $77); print "^";
+
+ @not 0 val;
+ print "!0="; check_hex(val, $FFFF); print ", ";
+ @not 1 val;
+ print "!1="; check_hex(val, $FFFE); print ", ";
+ @not $F val;
+ print "!$F="; check_hex(val, $FFF0); print ", ";
+ @not $7FFF val;
+ print "!$7FFF="; check_hex(val, $8000); print ", ";
+ @not $8000 val;
+ print "!$8000="; check_hex(val, $7FFF); print ", ";
+ @not $FFFD val;
+ print "!$FFFD="; check_hex(val, $2); print "^";
+
+ count_failures();
+ ];
+
+TestClass ShiftTest
+ with name 'shift',
+ testfunc [ val res ix;
+ print "Bit shifts:^^";
+
+ @log_shift $11 0 val;
+ print "$11u<<0="; check_hex(val, $11); print ", ";
+ @log_shift $11 1 val;
+ print "$11u<<1="; check_hex(val, $22); print ", ";
+ @log_shift $11 4 val;
+ print "$11u<<4="; check_hex(val, $110); print ", ";
+ @log_shift $11 10 val;
+ print "$11u<<10="; check_hex(val, $4400); print ", ";
+ @log_shift $11 15 val;
+ print "$11u<<15="; check_hex(val, $8000); print ", ";
+ @log_shift $11 16 val;
+ print "$11u<<16="; check_hex_unspecified(val, 0); print ", ";
+ @log_shift (-2) 0 val;
+ print "-2u<<0="; check(val, -2); print ", ";
+ @log_shift (-2) 1 val;
+ print "-2u<<1="; check(val, -4); print ", ";
+ @log_shift (-2) 7 val;
+ print "-2u<<7="; check(val, -256); print ", ";
+ @log_shift (-2) 15 val;
+ print "-2u<<15="; check(val, 0); print "^";
+
+ testglobal = 1;
+ res = 1;
+ for (ix=0 : ix<16 : ix++) {
+ @log_shift testglobal ix val;
+ print "1u<<", ix, "="; check_hex(val, res); print ", ";
+ res = res+res;
+ }
+ @log_shift testglobal ix val;
+ print "1u<<", ix, "="; check_hex_unspecified(val, 0); print "^";
+
+ @log_shift $4001 (0) val;
+ print "$4001u>>-0="; check_hex(val, $4001); print ", ";
+ @log_shift $4001 (-1) val;
+ print "$4001u>>-1="; check_hex(val, $2000); print ", ";
+ @log_shift $4001 (-6) val;
+ print "$4001u>>-6="; check_hex(val, $100); print ", ";
+ @log_shift $4001 (-11) val;
+ print "$4001u>>-11="; check_hex(val, $8); print ", ";
+ @log_shift $4001 (-15) val;
+ print "$4001u>>-15="; check_hex(val, $0); print ", ";
+ @log_shift $4001 (-16) val;
+ print "$4001u>>-16="; check_hex_unspecified(val, $0); print "^";
+
+ @log_shift $7FFF (0) val;
+ print "$7FFFu>>-0="; check_hex(val, $7FFF); print ", ";
+ @log_shift $7FFF (-1) val;
+ print "$7FFFu>>-1="; check_hex(val, $3FFF); print ", ";
+ @log_shift $7FFF (-2) val;
+ print "$7FFFu>>-2="; check_hex(val, $1FFF); print ", ";
+ @log_shift $7FFF (-6) val;
+ print "$7FFFu>>-6="; check_hex(val, $1FF); print ", ";
+ @log_shift $7FFF (-12) val;
+ print "$7FFFu>>-12="; check_hex(val, $7); print ", ";
+ @log_shift $7FFF (-15) val;
+ print "$7FFFu>>-15="; check_hex(val, $0); print ", ";
+ @log_shift $7FFF (-16) val;
+ print "$7FFFu>>-16="; check_hex_unspecified(val, $0); print "^";
+
+ @log_shift (-1) (0) val;
+ print "-1u>>-0="; check_hex(val, -1); print ", ";
+ @log_shift (-1) (-1) val;
+ print "-1u>>-1="; check_hex(val, $7FFF); print ", ";
+ @log_shift (-1) (-2) val;
+ print "-1u>>-2="; check_hex(val, $3FFF); print ", ";
+ @log_shift (-1) (-6) val;
+ print "-1u>>-6="; check_hex(val, $3FF); print ", ";
+ @log_shift (-1) (-12) val;
+ print "-1u>>-12="; check_hex(val, $F); print ", ";
+ @log_shift (-1) (-13) val;
+ print "-1u>>-13="; check_hex(val, $7); print ", ";
+ @log_shift (-1) (-15) val;
+ print "-1u>>-15="; check_hex(val, $1); print ", ";
+ @log_shift (-1) (-16) val;
+ print "-1u>>-16="; check_hex_unspecified(val, $0); print ", ";
+ @log_shift (-1) (-17) val;
+ print "-1u>>-17="; check_hex_unspecified(val, $0); print "^";
+
+ testglobal = -1;
+ res = $7fff;
+ for (ix=-1 : ix>-16 : ix--) {
+ @log_shift testglobal ix val;
+ print "-1u>>", ix, "="; check_hex(val, res); print ", ";
+ res = res / 2;
+ }
+ @log_shift testglobal ix val;
+ print "-1u>>", ix, "="; check_hex_unspecified(val, 0); print "^";
+
+ @art_shift $11 0 val;
+ print "$11s<<0="; check_hex(val, $11); print ", ";
+ @art_shift $11 1 val;
+ print "$11s<<1="; check_hex(val, $22); print ", ";
+ @art_shift $11 4 val;
+ print "$11s<<4="; check_hex(val, $110); print ", ";
+ @art_shift $11 10 val;
+ print "$11s<<10="; check_hex(val, $4400); print ", ";
+ @art_shift $11 15 val;
+ print "$11s<<15="; check_hex(val, $8000); print ", ";
+ @art_shift $11 16 val;
+ print "$11s<<16="; check_hex_unspecified(val, 0); print ", ";
+ @art_shift (-2) 0 val;
+ print "-2s<<0="; check(val, -2); print ", ";
+ @art_shift (-2) 1 val;
+ print "-2s<<1="; check(val, -4); print ", ";
+ @art_shift (-2) 7 val;
+ print "-2s<<7="; check(val, -256); print ", ";
+ @art_shift (-2) 15 val;
+ print "-2s<<15="; check(val, 0); print "^";
+
+ testglobal = 1;
+ res = 1;
+ for (ix=0 : ix<16 : ix++) {
+ @art_shift testglobal ix val;
+ print "1s<<", ix, "="; check_hex(val, res); print ", ";
+ res = res+res;
+ }
+ @art_shift testglobal ix val;
+ print "1s<<", ix, "="; check_hex_unspecified(val, 0); print "^";
+
+ @art_shift $4001 (0) val;
+ print "$4001s>>-0="; check_hex(val, $4001); print ", ";
+ @art_shift $4001 (-1) val;
+ print "$4001s>>-1="; check_hex(val, $2000); print ", ";
+ @art_shift $4001 (-6) val;
+ print "$4001s>>-6="; check_hex(val, $100); print ", ";
+ @art_shift $4001 (-11) val;
+ print "$4001s>>-11="; check_hex(val, $8); print ", ";
+ @art_shift $4001 (-15) val;
+ print "$4001s>>-15="; check_hex(val, $0); print ", ";
+ @art_shift $4001 (-16) val;
+ print "$4001s>>-16="; check_hex_unspecified(val, $0); print "^";
+
+ @art_shift $7FFF (0) val;
+ print "$7FFFs>>-0="; check_hex(val, $7FFF); print ", ";
+ @art_shift $7FFF (-1) val;
+ print "$7FFFs>>-1="; check_hex(val, $3FFF); print ", ";
+ @art_shift $7FFF (-2) val;
+ print "$7FFFs>>-2="; check_hex(val, $1FFF); print ", ";
+ @art_shift $7FFF (-6) val;
+ print "$7FFFs>>-6="; check_hex(val, $1FF); print ", ";
+ @art_shift $7FFF (-12) val;
+ print "$7FFFs>>-12="; check_hex(val, $7); print ", ";
+ @art_shift $7FFF (-13) val;
+ print "$7FFFs>>-13="; check_hex(val, $3); print ", ";
+ @art_shift $7FFF (-14) val;
+ print "$7FFFs>>-14="; check_hex(val, $1); print ", ";
+ @art_shift $7FFF (-15) val;
+ print "$7FFFs>>-15="; check_hex(val, $0); print ", ";
+ @art_shift $7FFF (-16) val;
+ print "$7FFFs>>-16="; check_hex_unspecified(val, $0); print "^";
+
+ @art_shift (-1) (0) val;
+ print "-1s>>-0="; check(val, -1); print ", ";
+ @art_shift (-1) (-1) val;
+ print "-1s>>-1="; check(val, -1); print ", ";
+ @art_shift (-1) (-15) val;
+ print "-1s>>-15="; check(val, -1); print ", ";
+ @art_shift (-1) (-16) val;
+ print "-1s>>-16="; check_hex_unspecified(val, -1); print ", ";
+ @art_shift (-1) (-17) val;
+ print "-1s>>-17="; check_hex_unspecified(val, -1); print "^";
+
+ @art_shift (-1000) (0) val;
+ print "-1000s>>-0="; check(val, -1000); print ", ";
+ @art_shift (-1000) (-1) val;
+ print "-1000s>>-1="; check(val, -500); print ", ";
+ @art_shift (-1000) (-2) val;
+ print "-1000s>>-2="; check(val, -250); print ", ";
+ @art_shift (-1000) (-4) val;
+ print "-1000s>>-4="; check(val, -63); print ", ";
+ @art_shift (-1000) (-6) val;
+ print "-1000s>>-6="; check(val, -16); print ", ";
+ @art_shift (-1000) (-9) val;
+ print "-1000s>>-9="; check(val, -2); print ", ";
+ @art_shift (-1000) (-15) val;
+ print "-1000s>>-15="; check(val, -1); print ", ";
+ @art_shift (-1000) (-16) val;
+ print "-1000s>>-16="; check_hex_unspecified(val, -1); print ", ";
+ @art_shift (-1000) (-17) val;
+ print "-1000s>>-17="; check_hex_unspecified(val, -1); print "^";
+
+ testglobal = -1;
+ for (ix=0 : ix>-16 : ix--) {
+ @art_shift testglobal ix val;
+ print "-1s>>", ix, "="; check(val, -1); print ", ";
+ }
+ @art_shift testglobal ix val;
+ print "-1s>>", ix, "="; check_hex_unspecified(val, -1); print "^";
+
+ count_failures();
+ ];
+
+TestClass IncrementTest
+ with name 'inc' 'dec' 'increment' 'decrement',
+ testfunc [ val;
+ print "Increment/decrement:^^";
+
+ val = 0;
+ @inc val;
+ print "0++="; check(val, 1); print ", ";
+ val = 1;
+ @inc val;
+ print "1++="; check(val, 2); print ", ";
+ val = -1;
+ @inc val;
+ print "-1++="; check(val, 0); print ", ";
+ val = -10;
+ @inc val;
+ print "-10++="; check(val, -9); print ", ";
+ val = $7FFF;
+ @inc val;
+ print "$7FFF++="; check_hex(val, $8000); print ", ";
+ val = $C000;
+ @inc val;
+ print "$C000++="; check_hex(val, $C001); print "^";
+
+ testglobal = 0;
+ @inc testglobal;
+ print "0++="; check(testglobal, 1); print ", ";
+ testglobal = 1;
+ @inc testglobal;
+ print "1++="; check(testglobal, 2); print ", ";
+ testglobal = -1;
+ @inc testglobal;
+ print "-1++="; check(testglobal, 0); print ", ";
+ testglobal = -10;
+ @inc testglobal;
+ print "-10++="; check(testglobal, -9); print ", ";
+ testglobal = $7FFF;
+ @inc testglobal;
+ print "$7FFF++="; check_hex(testglobal, $8000); print ", ";
+ testglobal = $C000;
+ @inc testglobal;
+ print "$C000++="; check_hex(testglobal, $C001); print "^";
+
+ @push 0;
+ @inc sp;
+ @pull val;
+ print "0++="; check(val, 1); print ", ";
+ @push 1;
+ @inc sp;
+ @pull val;
+ print "1++="; check(val, 2); print ", ";
+ @push -1;
+ @inc sp;
+ @pull val;
+ print "-1++="; check(val, 0); print ", ";
+ @push -10;
+ @inc sp;
+ @pull val;
+ print "-10++="; check(val, -9); print ", ";
+ @push $7FFF;
+ @inc sp;
+ @pull val;
+ print "$7FFF++="; check_hex(val, $8000); print ", ";
+ @push $C000;
+ @inc sp;
+ @pull val;
+ print "$C000++="; check_hex(val, $C001); print "^";
+
+ val = 0;
+ @dec val;
+ print "0--="; check(val, -1); print ", ";
+ val = 1;
+ @dec val;
+ print "1--="; check(val, 0); print ", ";
+ val = -1;
+ @dec val;
+ print "-1--="; check(val, -2); print ", ";
+ val = 10;
+ @dec val;
+ print "10--="; check(val, 9); print ", ";
+ val = $8000;
+ @dec val;
+ print "$8000--="; check_hex(val, $7FFF); print ", ";
+ val = $C000;
+ @dec val;
+ print "$C000--="; check_hex(val, $BFFF); print "^";
+
+ testglobal = 0;
+ @dec testglobal;
+ print "0--="; check(testglobal, -1); print ", ";
+ testglobal = 1;
+ @dec testglobal;
+ print "1--="; check(testglobal, 0); print ", ";
+ testglobal = -1;
+ @dec testglobal;
+ print "-1--="; check(testglobal, -2); print ", ";
+ testglobal = 10;
+ @dec testglobal;
+ print "10--="; check(testglobal, 9); print ", ";
+ testglobal = $8000;
+ @dec testglobal;
+ print "$8000--="; check_hex(testglobal, $7FFF); print ", ";
+ testglobal = $C000;
+ @dec testglobal;
+ print "$C000--="; check_hex(testglobal, $BFFF); print "^";
+
+ @push 0;
+ @dec sp;
+ @pull val;
+ print "0--="; check(val, -1); print ", ";
+ @push 1;
+ @dec sp;
+ @pull val;
+ print "1--="; check(val, 0); print ", ";
+ @push -1;
+ @dec sp;
+ @pull val;
+ print "-1--="; check(val, -2); print ", ";
+ @push 10;
+ @dec sp;
+ @pull val;
+ print "10--="; check(val, 9); print ", ";
+ @push $8000;
+ @dec sp;
+ @pull val;
+ print "$8000--="; check_hex(val, $7FFF); print ", ";
+ @push $C000;
+ @dec sp;
+ @pull val;
+ print "$C000--="; check_hex(val, $BFFF); print "^";
+
+ count_failures();
+ ];
+
+TestClass IncrementBranchTest
+ with name 'incchk' 'decchk' 'inccheck' 'deccheck',
+ testfunc [ val res;
+ print "Increment/decrement and branch:^^";
+
+ res = 0;
+ val = 1;
+ @inc_chk res 0 ?jump1a;
+ val = 0;
+ .jump1a;
+ print "++0="; check(res, 1); print ","; check(val, 1); print ", ";
+
+ res = 1;
+ val = 1;
+ @inc_chk res 0 ?jump1b;
+ val = 0;
+ .jump1b;
+ print "++1="; check(res, 2); print ","; check(val, 1); print ", ";
+
+ res = -1;
+ val = 1;
+ @inc_chk res 0 ?jump1c;
+ val = 0;
+ .jump1c;
+ print "++-1="; check(res, 0); print ","; check(val, 0); print ", ";
+
+ res = 100;
+ val = 1;
+ @inc_chk res 0 ?jump1d;
+ val = 0;
+ .jump1d;
+ print "++100="; check(res, 101); print ","; check(val, 1); print ", ";
+
+ res = -10;
+ val = 1;
+ @inc_chk res 0 ?jump1e;
+ val = 0;
+ .jump1e;
+ print "++-10="; check(res, -9); print ","; check(val, 0); print ", ";
+
+ res = $7FFF;
+ val = 1;
+ @inc_chk res 0 ?jump1f;
+ val = 0;
+ .jump1f;
+ print "++$7FFF="; check_hex(res, $8000); print ","; check(val, 0); print ", ";
+
+ res = $C000;
+ val = 1;
+ @inc_chk res 0 ?jump1g;
+ val = 0;
+ .jump1g;
+ print "++$C000="; check_hex(res, $C001); print ","; check(val, 0); print "^";
+
+
+ testglobal2 = 0;
+ testglobal = 1;
+ @inc_chk testglobal2 0 ?jump2a;
+ testglobal = 0;
+ .jump2a;
+ print "++0="; check(testglobal2, 1); print ","; check(testglobal, 1); print ", ";
+
+ testglobal2 = 1;
+ testglobal = 1;
+ @inc_chk testglobal2 0 ?jump2b;
+ testglobal = 0;
+ .jump2b;
+ print "++1="; check(testglobal2, 2); print ","; check(testglobal, 1); print ", ";
+
+ testglobal2 = -1;
+ testglobal = 1;
+ @inc_chk testglobal2 0 ?jump2c;
+ testglobal = 0;
+ .jump2c;
+ print "++-1="; check(testglobal2, 0); print ","; check(testglobal, 0); print ", ";
+
+ testglobal2 = 100;
+ testglobal = 1;
+ @inc_chk testglobal2 0 ?jump2d;
+ testglobal = 0;
+ .jump2d;
+ print "++100="; check(testglobal2, 101); print ","; check(testglobal, 1); print ", ";
+
+ testglobal2 = -10;
+ testglobal = 1;
+ @inc_chk testglobal2 0 ?jump2e;
+ testglobal = 0;
+ .jump2e;
+ print "++-10="; check(testglobal2, -9); print ","; check(testglobal, 0); print ", ";
+
+ testglobal2 = $7FFF;
+ testglobal = 1;
+ @inc_chk testglobal2 0 ?jump2f;
+ testglobal = 0;
+ .jump2f;
+ print "++$7FFF="; check_hex(testglobal2, $8000); print ","; check(testglobal, 0); print ", ";
+
+ testglobal2 = $C000;
+ testglobal = 1;
+ @inc_chk testglobal2 0 ?jump2g;
+ testglobal = 0;
+ .jump2g;
+ print "++$C000="; check_hex(testglobal2, $C001); print ","; check(testglobal, 0); print "^";
+
+
+ @push 0;
+ val = 1;
+ @inc_chk sp 0 ?jump3a;
+ val = 0;
+ .jump3a;
+ @pull res;
+ print "++0="; check(res, 1); print ","; check(val, 1); print ", ";
+
+ @push 1;
+ val = 1;
+ @inc_chk sp 0 ?jump3b;
+ val = 0;
+ .jump3b;
+ @pull res;
+ print "++1="; check(res, 2); print ","; check(val, 1); print ", ";
+
+ @push -1;
+ val = 1;
+ @inc_chk sp 0 ?jump3c;
+ val = 0;
+ .jump3c;
+ @pull res;
+ print "++-1="; check(res, 0); print ","; check(val, 0); print ", ";
+
+ @push 100;
+ val = 1;
+ @inc_chk sp 0 ?jump3d;
+ val = 0;
+ .jump3d;
+ @pull res;
+ print "++100="; check(res, 101); print ","; check(val, 1); print ", ";
+
+ @push -10;
+ val = 1;
+ @inc_chk sp 0 ?jump3e;
+ val = 0;
+ .jump3e;
+ @pull res;
+ print "++-10="; check(res, -9); print ","; check(val, 0); print ", ";
+
+ @push $7FFF;
+ val = 1;
+ @inc_chk sp 0 ?jump3f;
+ val = 0;
+ .jump3f;
+ @pull res;
+ print "++$7FFF="; check_hex(res, $8000); print ","; check(val, 0); print ", ";
+
+ @push $C000;
+ val = 1;
+ @inc_chk sp 0 ?jump3g;
+ val = 0;
+ .jump3g;
+ @pull res;
+ print "++$C000="; check_hex(res, $C001); print ","; check(val, 0); print "^";
+
+
+ res = 0;
+ val = 1;
+ @dec_chk res 0 ?jump4a;
+ val = 0;
+ .jump4a;
+ print "--0="; check(res, -1); print ","; check(val, 1); print ", ";
+
+ res = 1;
+ val = 1;
+ @dec_chk res 0 ?jump4b;
+ val = 0;
+ .jump4b;
+ print "--1="; check(res, 0); print ","; check(val, 0); print ", ";
+
+ res = -1;
+ val = 1;
+ @dec_chk res 0 ?jump4c;
+ val = 0;
+ .jump4c;
+ print "---1="; check(res, -2); print ","; check(val, 1); print ", ";
+
+ res = 100;
+ val = 1;
+ @dec_chk res 0 ?jump4d;
+ val = 0;
+ .jump4d;
+ print "--100="; check(res, 99); print ","; check(val, 0); print ", ";
+
+ res = -10;
+ val = 1;
+ @dec_chk res 0 ?jump4e;
+ val = 0;
+ .jump4e;
+ print "---10="; check(res, -11); print ","; check(val, 1); print ", ";
+
+ res = $8000;
+ val = 1;
+ @dec_chk res 0 ?jump4f;
+ val = 0;
+ .jump4f;
+ print "--$8000="; check_hex(res, $7FFF); print ","; check(val, 0); print ", ";
+
+ res = $C000;
+ val = 1;
+ @dec_chk res 0 ?jump4g;
+ val = 0;
+ .jump4g;
+ print "--$C000="; check_hex(res, $BFFF); print ","; check(val, 1); print "^";
+
+
+ testglobal2 = 0;
+ testglobal = 1;
+ @dec_chk testglobal2 0 ?jump5a;
+ testglobal = 0;
+ .jump5a;
+ print "--0="; check(testglobal2, -1); print ","; check(testglobal, 1); print ", ";
+
+ testglobal2 = 1;
+ testglobal = 1;
+ @dec_chk testglobal2 0 ?jump5b;
+ testglobal = 0;
+ .jump5b;
+ print "--1="; check(testglobal2, 0); print ","; check(testglobal, 0); print ", ";
+
+ testglobal2 = -1;
+ testglobal = 1;
+ @dec_chk testglobal2 0 ?jump5c;
+ testglobal = 0;
+ .jump5c;
+ print "---1="; check(testglobal2, -2); print ","; check(testglobal, 1); print ", ";
+
+ testglobal2 = 100;
+ testglobal = 1;
+ @dec_chk testglobal2 0 ?jump5d;
+ testglobal = 0;
+ .jump5d;
+ print "--100="; check(testglobal2, 99); print ","; check(testglobal, 0); print ", ";
+
+ testglobal2 = -10;
+ testglobal = 1;
+ @dec_chk testglobal2 0 ?jump5e;
+ testglobal = 0;
+ .jump5e;
+ print "---10="; check(testglobal2, -11); print ","; check(testglobal, 1); print ", ";
+
+ testglobal2 = $8000;
+ testglobal = 1;
+ @dec_chk testglobal2 0 ?jump5f;
+ testglobal = 0;
+ .jump5f;
+ print "--$8000="; check_hex(testglobal2, $7FFF); print ","; check(testglobal, 0); print ", ";
+
+ testglobal2 = $C000;
+ testglobal = 1;
+ @dec_chk testglobal2 0 ?jump5g;
+ testglobal = 0;
+ .jump5g;
+ print "--$C000="; check_hex(testglobal2, $BFFF); print ","; check(testglobal, 1); print "^";
+
+
+ @push 0;
+ val = 1;
+ @dec_chk sp 0 ?jump6a;
+ val = 0;
+ .jump6a;
+ @pull res;
+ print "--0="; check(res, -1); print ","; check(val, 1); print ", ";
+
+ @push 1;
+ val = 1;
+ @dec_chk sp 0 ?jump6b;
+ val = 0;
+ .jump6b;
+ @pull res;
+ print "--1="; check(res, 0); print ","; check(val, 0); print ", ";
+
+ @push -1;
+ val = 1;
+ @dec_chk sp 0 ?jump6c;
+ val = 0;
+ .jump6c;
+ @pull res;
+ print "---1="; check(res, -2); print ","; check(val, 1); print ", ";
+
+ @push 100;
+ val = 1;
+ @dec_chk sp 0 ?jump6d;
+ val = 0;
+ .jump6d;
+ @pull res;
+ print "--100="; check(res, 99); print ","; check(val, 0); print ", ";
+
+ @push -10;
+ val = 1;
+ @dec_chk sp 0 ?jump6e;
+ val = 0;
+ .jump6e;
+ @pull res;
+ print "---10="; check(res, -11); print ","; check(val, 1); print ", ";
+
+ @push $8000;
+ val = 1;
+ @dec_chk sp 0 ?jump6f;
+ val = 0;
+ .jump6f;
+ @pull res;
+ print "--$8000="; check_hex(res, $7FFF); print ","; check(val, 0); print ", ";
+
+ @push $C000;
+ val = 1;
+ @dec_chk sp 0 ?jump6g;
+ val = 0;
+ .jump6g;
+ @pull res;
+ print "--$C000="; check_hex(res, $BFFF); print ","; check(val, 1); print "^";
+
+
+ res = 50;
+ val = 1;
+ @inc_chk res 60 ?jumpxa;
+ val = 0;
+ .jumpxa;
+ print "++50="; check(res, 51); print ","; check(val, 0); print ", ";
+
+ res = 70;
+ val = 1;
+ @inc_chk res 60 ?jumpxb;
+ val = 0;
+ .jumpxb;
+ print "++70="; check(res, 71); print ","; check(val, 1); print ", ";
+
+ res = -50;
+ val = 1;
+ @inc_chk res (-60) ?jumpxc;
+ val = 0;
+ .jumpxc;
+ print "++-50="; check(res, -49); print ","; check(val, 1); print ", ";
+
+ res = -70;
+ val = 1;
+ @inc_chk res (-60) ?jumpxd;
+ val = 0;
+ .jumpxd;
+ print "++-70="; check(res, -69); print ","; check(val, 0); print ", ";
+
+ res = -50;
+ val = 1;
+ @inc_chk res 60 ?jumpxe;
+ val = 0;
+ .jumpxe;
+ print "++-50="; check(res, -49); print ","; check(val, 0); print ", ";
+
+ res = 50;
+ val = 1;
+ @inc_chk res (-60) ?jumpxf;
+ val = 0;
+ .jumpxf;
+ print "++50="; check(res, 51); print ","; check(val, 1); print "^";
+
+
+ res = 50;
+ val = 1;
+ @dec_chk res 60 ?jumpya;
+ val = 0;
+ .jumpya;
+ print "--50="; check(res, 49); print ","; check(val, 1); print ", ";
+
+ res = 70;
+ val = 1;
+ @dec_chk res 60 ?jumpyb;
+ val = 0;
+ .jumpyb;
+ print "--70="; check(res, 69); print ","; check(val, 0); print ", ";
+
+ res = -50;
+ val = 1;
+ @dec_chk res (-60) ?jumpyc;
+ val = 0;
+ .jumpyc;
+ print "---50="; check(res, -51); print ","; check(val, 0); print ", ";
+
+ res = -70;
+ val = 1;
+ @dec_chk res (-60) ?jumpyd;
+ val = 0;
+ .jumpyd;
+ print "---70="; check(res, -71); print ","; check(val, 1); print ", ";
+
+ res = -50;
+ val = 1;
+ @dec_chk res 60 ?jumpye;
+ val = 0;
+ .jumpye;
+ print "---50="; check(res, -51); print ","; check(val, 1); print ", ";
+
+ res = 50;
+ val = 1;
+ @dec_chk res (-60) ?jumpyf;
+ val = 0;
+ .jumpyf;
+ print "--50="; check(res, 49); print ","; check(val, 0); print "^";
+
+
+ count_failures();
+ ];
+
+Array array1 --> $1357 $FDB9 $0011 $FFEE;
+Array array2 --> 4;
+
+TestClass ArrayTest
+ with name 'array' 'loadw' 'loadb' 'storew' 'storeb',
+ testfunc [ val ix addr;
+ print "Array loads and stores:^^";
+
+ addr = array1;
+ @loadw array1 0 -> val;
+ print "a-->0="; check_hex(val, $1357); print ", ";
+ @loadw addr 0 -> val;
+ print "a-->0="; check_hex(val, $1357); print ", ";
+
+ ix = 1;
+ @loadw array1 ix -> val;
+ print "a-->1="; check_hex(val, $FDB9); print ", ";
+ @loadw addr ix -> val;
+ print "a-->1="; check_hex(val, $FDB9); print ", ";
+
+ testglobal = 2;
+ @loadw array1 testglobal -> val;
+ print "a-->2="; check_hex(val, $0011); print ", ";
+ @loadw addr testglobal -> val;
+ print "a-->2="; check_hex(val, $0011); print ", ";
+
+ @push 3;
+ @loadw array1 sp -> val;
+ print "a-->3="; check_hex(val, $FFEE); print ", ";
+ @push 3;
+ @loadw addr sp -> val;
+ print "a-->3="; check_hex(val, $FFEE); print "^";
+
+ addr = array1+3;
+ @loadw addr (-1) -> val;
+ print "a+3-->-1="; check_hex(val, $57FD); print ", ";
+ @loadw addr 0 -> val;
+ print "a+3-->0="; check_hex(val, $B900); print ", ";
+ @loadw addr 1 -> val;
+ print "a+3-->1="; check_hex(val, $11FF); print ", ";
+ testglobal = array1+3;
+ @loadw testglobal (-1) -> val;
+ print "a+3-->-1="; check_hex(val, $57FD); print ", ";
+ @loadw testglobal 0 -> val;
+ print "a+3-->0="; check_hex(val, $B900); print ", ";
+ @loadw testglobal 1 -> val;
+ print "a+3-->1="; check_hex(val, $11FF); print "^";
+
+
+ addr = array1;
+ @loadb array1 0 -> val;
+ print "a->0="; check_hex(val, $13); print ", ";
+ @loadb addr 0 -> val;
+ print "a->0="; check_hex(val, $13); print ", ";
+
+ ix = 1;
+ @loadb array1 ix -> val;
+ print "a->1="; check_hex(val, $57); print ", ";
+ @loadb addr ix -> val;
+ print "a->1="; check_hex(val, $57); print ", ";
+
+ testglobal = 2;
+ @loadb array1 testglobal -> val;
+ print "a->2="; check_hex(val, $FD); print ", ";
+ @loadb addr testglobal -> val;
+ print "a->2="; check_hex(val, $FD); print ", ";
+
+ @push 3;
+ @loadb array1 sp -> val;
+ print "a->3="; check_hex(val, $B9); print ", ";
+ @push 3;
+ @loadb addr sp -> val;
+ print "a->3="; check_hex(val, $B9); print "^";
+
+ addr = array1+3;
+ @loadb addr (-1) -> val;
+ print "a+3->-1="; check_hex(val, $FD); print ", ";
+ @loadb addr 0 -> val;
+ print "a+3->0="; check_hex(val, $B9); print ", ";
+ @loadb addr 1 -> val;
+ print "a+3->1="; check_hex(val, $00); print ", ";
+ testglobal = array1+3;
+ @loadb testglobal (-1) -> val;
+ print "a+3->-1="; check_hex(val, $FD); print ", ";
+ @loadb testglobal 0 -> val;
+ print "a+3->0="; check_hex(val, $B9); print ", ";
+ @loadb testglobal 1 -> val;
+ print "a+3->1="; check_hex(val, $00); print "^";
+
+
+ addr = array2;
+ @storew array2 0 $1201;
+ @loadw array2 0 -> val;
+ print "a-->0="; check_hex(val, $1201); print ", ";
+ @storew addr 0 $2302;
+ @loadw array2 0 -> val;
+ print "a-->0="; check_hex(val, $2302); print ", ";
+
+ ix = 1;
+ @storew array2 ix $3403;
+ @loadw array2 1 -> val;
+ print "a-->1="; check_hex(val, $3403); print ", ";
+ @storew addr ix $4504;
+ @loadw array2 1 -> val;
+ print "a-->1="; check_hex(val, $4504); print ", ";
+
+ testglobal = 2;
+ @storew array2 testglobal $5605;
+ @loadw array2 2 -> val;
+ print "a-->2="; check_hex(val, $5605); print ", ";
+ @storew addr testglobal $6706;
+ @loadw array2 2 -> val;
+ print "a-->2="; check_hex(val, $6706); print ", ";
+
+ @push 3;
+ @storew array2 sp $7807;
+ @loadw array2 3 -> val;
+ print "a-->3="; check_hex(val, $7807); print ", ";
+ @push 3;
+ @storew addr sp $8908;
+ @loadw array2 3 -> val;
+ print "a-->3="; check_hex(val, $8908); print "^";
+
+ addr = array2+4;
+ @storew addr (-1) $AB0A;
+ @loadw array2 1 -> val;
+ print "a-->-1="; check_hex(val, $AB0A); print ", ";
+ @storew addr 0 $BC0B;
+ @loadw array2 2 -> val;
+ print "a-->0="; check_hex(val, $BC0B); print ", ";
+ @storew addr 1 $CD0C;
+ @loadw array2 3 -> val;
+ print "a-->1="; check_hex(val, $CD0C); print ", ";
+
+ testglobal = array2+4;
+ @storew testglobal (-1) $BA1B;
+ @loadw array2 1 -> val;
+ print "a-->-1="; check_hex(val, $BA1B); print ", ";
+ @storew testglobal 0 $CB1C;
+ @loadw array2 2 -> val;
+ print "a-->0="; check_hex(val, $CB1C); print ", ";
+ @storew testglobal 1 $DC1D;
+ @loadw array2 3 -> val;
+ print "a-->1="; check_hex(val, $DC1D); print "^";
+
+
+ addr = array2;
+ @storeb array2 0 $12;
+ @loadb array2 0 -> val;
+ print "a->0="; check_hex(val, $12); print ", ";
+ @storeb addr 0 $23;
+ @loadb array2 0 -> val;
+ print "a->0="; check_hex(val, $23); print ", ";
+
+ ix = 1;
+ @storeb array2 ix $34;
+ @loadb array2 1 -> val;
+ print "a->1="; check_hex(val, $34); print ", ";
+ @storeb addr ix $45;
+ @loadb array2 1 -> val;
+ print "a->1="; check_hex(val, $45); print ", ";
+
+ testglobal = 2;
+ @storeb array2 testglobal $56;
+ @loadb array2 2 -> val;
+ print "a->2="; check_hex(val, $56); print ", ";
+ @storeb addr testglobal $67;
+ @loadb array2 2 -> val;
+ print "a->2="; check_hex(val, $67); print ", ";
+
+ @push 3;
+ @storeb array2 sp $78;
+ @loadb array2 3 -> val;
+ print "a->3="; check_hex(val, $78); print ", ";
+ @push 3;
+ @storeb addr sp $89;
+ @loadb array2 3 -> val;
+ print "a->3="; check_hex(val, $89); print "^";
+
+ addr = array2+4;
+ @storeb addr (-1) $AB;
+ @loadb array2 3 -> val;
+ print "a->-1="; check_hex(val, $AB); print ", ";
+ @storeb addr 0 $BC;
+ @loadb array2 4 -> val;
+ print "a->0="; check_hex(val, $BC); print ", ";
+ @storeb addr 1 $CD;
+ @loadb array2 5 -> val;
+ print "a->1="; check_hex(val, $CD); print ", ";
+
+ testglobal = array2+4;
+ @storeb testglobal (-1) $BA;
+ @loadb array2 3 -> val;
+ print "a->-1="; check_hex(val, $BA); print ", ";
+ @storeb testglobal 0 $CB;
+ @loadb array2 4 -> val;
+ print "a->0="; check_hex(val, $CB); print ", ";
+ @storeb testglobal 1 $DC;
+ @loadb array2 5 -> val;
+ print "a->1="; check_hex(val, $DC); print "^";
+
+ ix = $F1;
+ @storeb array2 0 ix;
+ ix = $E2;
+ @storeb array2 1 ix;
+ @loadw array2 0 -> val;
+ print "$F1 concat $E2 = "; check_hex(val, $F1E2); print "^";
+
+ ix = $9876;
+ @storew array2 1 ix;
+ @loadb array2 2 -> val;
+ print "$9876 = "; check_hex(val, $98); print " ";
+ @loadb array2 3 -> val;
+ print "concat "; check_hex(val, $76); print "^";
+
+ count_failures();
+ ];
+
+TestClass UndoTest
+ with name 'undo',
+ testfunc [ val loc;
+ print "Undo:^^";
+
+ val = HDR_GAMEFLAGS-->0; ! "Flags 2" byte
+ if (val & 16)
+ print "Interpreter claims to support undo.^^";
+ else
+ print "Interpreter claims to not support undo. (Continuing test anyway...)^^";
+
+ print "Using a local variable for @@64save_undo result:^";
+ loc = 99;
+ testglobal = 999;
+ @save_undo val;
+ if (val == -1) {
+ print "Undo is not available on this interpreter.^";
+ count_failures();
+ return;
+ }
+ if (val == 0) {
+ print "@@64save_undo failed!^";
+ failures++;
+ count_failures();
+ return;
+ }
+ if (val == 1) {
+ print "Undo saved...^";
+ ! The following changes will be undone.
+ loc = 77;
+ testglobal = 777;
+ failures++;
+ print "Restoring undo...^";
+ @restore_undo val;
+ if (val == 0) {
+ print "@@64restore_undo failed (value 0)!^";
+ }
+ else {
+ print "@@64restore_undo failed with unknown return value: ", val, "^";
+ }
+ failures++;
+ count_failures();
+ return;
+ }
+ else if (val ~= 2) {
+ print "Unknown @@64save_undo return value: ", val, "^";
+ failures++;
+ count_failures();
+ return;
+ }
+ print "Undo succeeded, return value "; check(val, 2); print ".^";
+ print "loc="; check(loc, 99); print " glob="; check(testglobal, 999); print "^";
+
+ print "Using a global variable for @@64save_undo result:^";
+ loc = 98;
+ testglobal = 998;
+ @save_undo testglobal2;
+ if (testglobal2 == -1) {
+ print "Undo is not available on this interpreter.^";
+ count_failures();
+ return;
+ }
+ if (testglobal2 == 0) {
+ print "@@64save_undo failed!^";
+ failures++;
+ count_failures();
+ return;
+ }
+ if (testglobal2 == 1) {
+ print "Undo saved...^";
+ ! The following changes will be undone.
+ loc = 77;
+ testglobal = 777;
+ failures++;
+ print "Restoring undo...^";
+ @restore_undo testglobal2;
+ if (testglobal2 == 0) {
+ print "@@64restore_undo failed (value 0)!^";
+ }
+ else {
+ print "@@64restore_undo failed with unknown return value: ", testglobal2, "^";
+ }
+ failures++;
+ count_failures();
+ return;
+ }
+ else if (testglobal2 ~= 2) {
+ print "Unknown @@64save_undo return value: ", testglobal2, "^";
+ failures++;
+ count_failures();
+ return;
+ }
+ print "Undo succeeded, return value "; check(testglobal2, 2); print ".^";
+ print "loc="; check(loc, 98); print " glob="; check(testglobal, 998); print "^";
+
+ print "Calling @@64save_undo within a function or two:^";
+ loc = 97;
+ testglobal = 997;
+ val = undo_depth_check();
+ if (val == -1) {
+ print "Undo is not available on this interpreter.^";
+ count_failures();
+ return;
+ }
+ if (val == 0) {
+ print "@@64save_undo failed!^";
+ failures++;
+ count_failures();
+ return;
+ }
+ if (val == 1) {
+ print "Undo saved...^";
+ ! The following changes will be undone.
+ loc = 77;
+ testglobal = 777;
+ failures++;
+ print "Restoring undo...^";
+ @restore_undo val;
+ if (val == 0) {
+ print "@@64restore_undo failed (value 0)!^";
+ }
+ else {
+ print "@@64restore_undo failed with unknown return value: ", val, "^";
+ }
+ failures++;
+ count_failures();
+ return;
+ }
+ else if (val ~= 2) {
+ print "Unknown @@64save_undo return value: ", val, "^";
+ failures++;
+ count_failures();
+ return;
+ }
+ print "Undo succeeded, return value "; check(val, 2); print ".^";
+ print "loc="; check(loc, 97); print " glob="; check(testglobal, 997); print "^";
+ @check_arg_count 3 ?~ok;
+ print "Error: test method wrongly got argument 3!^";
+ failures++;
+ .ok;
+
+ print "Using the stack for @@64save_undo result:^";
+ loc = 98;
+ testglobal = 998;
+ testglobal2 = -99;
+ @save_undo sp;
+ @pull testglobal2;
+ if (testglobal2 == -1) {
+ print "Undo is not available on this interpreter.^";
+ count_failures();
+ return;
+ }
+ if (testglobal2 == 0) {
+ print "@@64save_undo failed!^";
+ failures++;
+ count_failures();
+ return;
+ }
+ if (testglobal2 == 1) {
+ print "Undo saved...^";
+ ! The following changes will be undone.
+ loc = 77;
+ testglobal = 777;
+ testglobal2 = -99;
+ failures++;
+ print "Restoring undo...^";
+ @restore_undo sp;
+ @pull testglobal2;
+ if (testglobal2 == 0) {
+ print "@@64restore_undo failed (value 0)!^";
+ }
+ else {
+ print "@@64restore_undo failed with unknown return value: ", testglobal2, "^";
+ }
+ failures++;
+ count_failures();
+ return;
+ }
+ else if (testglobal2 ~= 2) {
+ print "Unknown @@64save_undo return value: ", testglobal2, "^";
+ failures++;
+ count_failures();
+ return;
+ }
+ print "Undo succeeded, return value "; check(testglobal2, 2); print ".^";
+ print "loc="; check(loc, 98); print " glob="; check(testglobal, 998); print "^";
+
+ print "Checking @@64save_undo saves the stack correctly:^";
+ @push 9;
+ loc = 99;
+ testglobal = 999;
+ testglobal2 = -999;
+ @save_undo val;
+ if (val == -1) {
+ print "Undo is not available on this interpreter.^";
+ count_failures();
+ return;
+ }
+ if (val == 0) {
+ print "@@64save_undo failed!^";
+ failures++;
+ count_failures();
+ return;
+ }
+ if (val == 1) {
+ print "Undo saved...^";
+ ! The following changes will be undone.
+ loc = 77;
+ testglobal = 777;
+ testglobal2 = -777;
+ @pull val;
+ print "guard="; check(val, 9); print "^";
+ val = 7;
+ failures++;
+ print "Restoring undo...^";
+ @restore_undo testglobal2;
+ if (testglobal2 == 0) {
+ print "@@64restore_undo failed (value 0)!^";
+ }
+ else {
+ print "@@64restore_undo failed with unknown return value: ", testglobal2, "^";
+ }
+ failures++;
+ count_failures();
+ return;
+ }
+ else if (val ~= 2) {
+ print "Unknown @@64save_undo return value: ", val, "^";
+ failures++;
+ count_failures();
+ return;
+ }
+ print "Undo succeeded, return value "; check(val, 2); print ".^";
+ print "loc="; check(loc, 99); print " glob="; check(testglobal, 999); print " glob2="; check(testglobal2, -999); print "^";
+
+ @pull val;
+ print "guard="; check(val, 9); print "^";
+
+ count_failures();
+ ];
+
+[ undo_depth_check;
+ return undo_depth_check2(11, 22, 33);
+];
+
+[ undo_depth_check2 foo bar baz val;
+ bar = 1;
+ foo = bar;
+ baz = foo;
+ @save_undo val;
+ @check_arg_count 3 ?ok;
+ print "Error: undo_depth_check2 did not get argument 3!^";
+ failures++;
+ .ok;
+ return val;
+];
+
+TestClass MultiUndoTest
+ with name 'multiundo',
+ testfunc [ val loc;
+ print "Multi-level undo:^(Note: this capability is not required by the spec.)^^";
+
+ val = HDR_GAMEFLAGS-->0; ! "Flags 2" byte
+ if (val & 16)
+ print "Interpreter claims to support undo.^^";
+ else
+ print "Interpreter claims to not support undo. (Continuing test anyway...)^^";
+
+ loc = 99;
+ testglobal = 999;
+ @save_undo val;
+ if (val == -1) {
+ print "Undo is not available on this interpreter.^";
+ count_failures();
+ return;
+ }
+ if (val == 0) {
+ print "First @@64save_undo failed!^";
+ failures++;
+ count_failures();
+ return;
+ }
+ if (val == 1) {
+ print "Undo 1 saved...^";
+ ! The following changes will be undone.
+ loc = 77;
+ testglobal = 777;
+ failures++;
+
+ @save_undo val;
+ if (val == -1) {
+ print "Undo returned ~unavailable~, even though it was available the first time.^";
+ count_failures();
+ return;
+ }
+ if (val == 0) {
+ print "Second @@64save_undo failed! This interpreter apparently doesn't support multi-level undo. This should not be considered a bug.^";
+ failures--; ! cancel the previous failure
+ count_failures();
+ return;
+ }
+ if (val == 1) {
+ print "Undo 2 saved...^";
+ ! The following changes will be undone.
+ loc = 55;
+ testglobal = 555;
+ failures++;
+
+ print "Restoring undo 2...^";
+ @restore_undo val;
+ if (val == 0) {
+ print "Second @@64restore_undo failed (value 0)!^";
+ }
+ else {
+ print "Second @@64restore_undo failed with unknown return value: ", val, "^";
+ }
+ failures++;
+ count_failures();
+ return;
+ }
+ else if (val ~= 2) {
+ print "Unknown @@64save_undo return value: ", val, "^";
+ failures++;
+ count_failures();
+ return;
+ }
+
+ print "Undo 2 succeeded, return value "; check(val, 2); print ".^";
+ print "loc="; check(loc, 77); print " glob="; check(testglobal, 777); print "^";
+
+ print "Restoring undo 1...^";
+ @restore_undo val;
+ if (val == 0) {
+ print "First @@64restore_undo failed (value 0)!^";
+ }
+ else {
+ print "First @@64restore_undo failed with unknown return value: ", val, "^";
+ }
+ failures++;
+ count_failures();
+ return;
+ }
+ else if (val ~= 2) {
+ print "Unknown @@64save_undo return value: ", val, "^";
+ failures++;
+ count_failures();
+ return;
+ }
+ print "Undo 1 succeeded, return value "; check(val, 2); print ".^";
+ print "loc="; check(loc, 99); print " glob="; check(testglobal, 999); print "^";
+
+ count_failures();
+ ];
+
+! The Indirect test is copied more-or-less whole from czech.inf
+! (as of Oct 1 2010). Comments copied also:
+!
+! Indirect-able opcodes: inc, dec, inc_chk, dec_chk, store, pull, load
+! Spec Version 1.1 (draft7): "an indirect reference to the stack
+! pointer does not push or pull the top item of the stack - it is read
+! or written in place."
+! Based on my tests (see rec.arts.int-fiction 20031028), this seems to mean
+! that, e.g., for load, you NEVER pop the stack, for all cases
+! (a) load sp; (b) load [sp]; (c) i=0; load [i]; (d) sp=0; load [sp];
+!
+! Overall rules:
+! - Do NOT push/pop for "foo sp": write in place
+! - DO pop for "foo [sp]". However, if top of stack is 0, only pop ONCE.
+! - "bar = 0; foo [bar]" yields EXACTLY the same results as "foo sp"
+! ("push 0; foo [sp] is also identical to "foo sp".)
+!
+TestClass IndirectTest
+ with name 'indirect',
+ testfunc [ ix;
+ print "Indirect opcodes:^^";
+
+ for (ix = 0: ix < 100: ix++) {
+ do_indirect_test(ix);
+ }
+
+ count_failures();
+ ];
+
+[ do_indirect_test which
+ result local2 spointer lpointer rpointer
+ top_of_stack which_str expectr expect1 expect2;
+
+ ! First, set up everything we're going to need.
+
+ local2 = 51;
+ ! Gtemp = 61;
+ result = 71;
+ spointer = 0; ! stack
+ rpointer = 2; ! points to 'result'
+ lpointer = 3; ! local2
+ ! gpointer = 21; ! '21' means 6th global, which is (hopefully!) Gtemp
+ expectr = 999; ! don't test 'result' unless we change this value
+
+ @push 41; @push 42; @push 43; @push 44; @push 45;
+ switch (which) {
+
+ ! load -> result
+ 0:
+ @load sp -> result; ! compiles as 'load 0 -> result'
+ expectr = 45; expect1 = 45; expect2 = 44;
+ which_str = "load sp -> result";
+ 1:
+ @load [spointer] -> result;
+ expectr = 45; expect1 = 45; expect2 = 44;
+ which_str = "load [spointer] -> result";
+ 2:
+ @push lpointer; @load [sp] -> result;
+ expectr = 51; expect1 = 45; expect2 = 44;
+ which_str = "load [sp=lpointer] -> result";
+ 3:
+ @push spointer; @load [sp] -> result;
+ expectr = 45; expect1 = 45; expect2 = 44;
+ which_str = "load [sp=spointer] -> result";
+
+ ! load -> sp
+ 4:
+ @load sp -> sp;
+ expect1 = 45; expect2 = 45;
+ which_str = "load sp -> sp";
+ 5:
+ @push lpointer; @load [sp] -> sp;
+ expect1 = 51; expect2 = 45;
+ which_str = "load [sp=lpointer] -> sp";
+ 6:
+ @push spointer; @load [sp] -> sp;
+ expect1 = 45; expect2 = 45;
+ which_str = "load [sp=spointer] -> sp";
+
+ ! store
+ 10:
+ @store sp 83;
+ expect1 = 83; expect2 = 44;
+ which_str = "store sp 83";
+ 11:
+ @store [spointer] 83;
+ expect1 = 83; expect2 = 44;
+ which_str = "store [spointer] 83";
+ 12:
+ @push spointer; @store [sp] 83;
+ expect1 = 83; expect2 = 44;
+ which_str = "store [sp=spointer] 83";
+
+ 13:
+ @store [rpointer] 83;
+ expectr = 83; expect1 = 45; expect2 = 44;
+ which_str = "store [rpointer] 83";
+ 14:
+ @push rpointer; @store [sp] 83;
+ expectr = 83; expect1 = 45; expect2 = 44;
+ which_str = "store [sp=rpointer] 83";
+
+ 15:
+ @store result sp;
+ expectr = 45; expect1 = 44; expect2 = 43;
+ which_str = "store result sp";
+ 16:
+ @store sp sp;
+ expect1 = 45; expect2 = 43;
+ which_str = "store sp sp";
+ 17:
+ @push spointer; @store [sp] sp;
+ expect1 = 45; expect2 = 43;
+ which_str = "store [sp=spointer] sp";
+
+ 18:
+ @store [rpointer] sp;
+ expectr = 45; expect1 = 44; expect2 = 43;
+ which_str = "store [rpointer] sp";
+ 19:
+ @push rpointer; @store [sp] sp;
+ expectr = 45; expect1 = 44; expect2 = 43;
+ which_str = "store [sp=rpointer] sp";
+
+ ! pull
+ 20:
+ @pull result;
+ expectr = 45; expect1 = 44; expect2 = 43;
+ which_str = "pull result";
+ 21:
+ @pull [rpointer];
+ expectr = 45; expect1 = 44; expect2 = 43;
+ which_str = "pull [rpointer]";
+ 22:
+ @push rpointer; @pull [sp];
+ expectr = 45; expect1 = 44; expect2 = 43;
+ which_str = "pull [sp=rpointer]";
+
+ 23:
+ @pull sp;
+ expect1 = 45; expect2 = 43;
+ which_str = "pull sp";
+ 24:
+ @push spointer; @pull [sp];
+ expect1 = 45; expect2 = 43;
+ which_str = "pull [sp=spointer]";
+ 25:
+ @pull [spointer];
+ expect1 = 45; expect2 = 43;
+ which_str = "pull [spointer]";
+
+ ! inc
+ 30:
+ @inc result;
+ expectr = 72; expect1 = 45; expect2 = 44;
+ which_str = "inc result";
+ 31:
+ @inc [rpointer];
+ expectr = 72; expect1 = 45; expect2 = 44;
+ which_str = "inc [rpointer]";
+ 32:
+ @push rpointer; @inc [sp];
+ expectr = 72; expect1 = 45; expect2 = 44;
+ which_str = "inc [sp=rpointer]";
+
+ 33:
+ @inc sp;
+ expect1 = 46; expect2 = 44;
+ which_str = "inc sp";
+ 34:
+ @inc [spointer];
+ expect1 = 46; expect2 = 44;
+ which_str = "inc [spointer]";
+ 35:
+ @push spointer; @inc [sp];
+ expect1 = 46; expect2 = 44;
+ which_str = "inc [sp=spointer]";
+
+ ! dec
+ 40:
+ @dec result;
+ expectr = 70; expect1 = 45; expect2 = 44;
+ which_str = "dec result";
+ 41:
+ @dec [rpointer];
+ expectr = 70; expect1 = 45; expect2 = 44;
+ which_str = "dec [rpointer]";
+ 42:
+ @push rpointer; @dec [sp];
+ expectr = 70; expect1 = 45; expect2 = 44;
+ which_str = "dec [sp=rpointer]";
+
+ 43:
+ @dec sp;
+ expect1 = 44; expect2 = 44;
+ which_str = "dec sp";
+ 44:
+ @dec [spointer];
+ expect1 = 44; expect2 = 44;
+ which_str = "dec [spointer]";
+ 45:
+ @push spointer; @dec [sp];
+ expect1 = 44; expect2 = 44;
+ which_str = "dec [sp=spointer]";
+
+ ! inc_chk
+ 50:
+ which_str = "inc_chk result";
+ @inc_chk result 72 ?bad_indirect_inc;
+ expectr = 72; expect1 = 45; expect2 = 44;
+ 51:
+ which_str = "inc_chk [rpointer]";
+ @inc_chk [rpointer] 72 ?bad_indirect_inc;
+ expectr = 72; expect1 = 45; expect2 = 44;
+ 52:
+ which_str = "inc_chk [sp=rpointer]";
+ @push rpointer; @inc_chk [sp] 72 ?bad_indirect_inc;
+ expectr = 72; expect1 = 45; expect2 = 44;
+
+ 53:
+ which_str = "inc_chk sp";
+ @inc_chk sp 46 ?bad_indirect_inc;
+ expect1 = 46; expect2 = 44;
+ 54:
+ which_str = "inc_chk [spointer]";
+ @inc_chk [spointer] 46 ?bad_indirect_inc;
+ expect1 = 46; expect2 = 44;
+ 55:
+ which_str = "inc_chk [sp=spointer]";
+ @push spointer; @inc_chk [sp] 46 ?bad_indirect_inc;
+ expect1 = 46; expect2 = 44;
+
+ ! dec_chk
+ 60:
+ which_str = "dec_chk result";
+ @dec_chk result 70 ?bad_indirect_inc;
+ expectr = 70; expect1 = 45; expect2 = 44;
+ 61:
+ which_str = "dec_chk [rpointer]";
+ @dec_chk [rpointer] 70 ?bad_indirect_inc;
+ expectr = 70; expect1 = 45; expect2 = 44;
+ 62:
+ which_str = "dec_chk [sp=rpointer]";
+ @push rpointer; @dec_chk [sp] 70 ?bad_indirect_inc;
+ expectr = 70; expect1 = 45; expect2 = 44;
+
+ 63:
+ which_str = "dec_chk sp";
+ @dec_chk sp 44 ?bad_indirect_inc;
+ expect1 = 44; expect2 = 44;
+ 64:
+ which_str = "dec_chk [spointer]";
+ @dec_chk [spointer] 44 ?bad_indirect_inc;
+ expect1 = 44; expect2 = 44;
+ 65:
+ which_str = "dec_chk [sp=spointer]";
+ @push spointer; @dec_chk [sp] 44 ?bad_indirect_inc;
+ expect1 = 44; expect2 = 44;
+
+ default:
+ rfalse; ! no test here; do nothing.
+ }
+
+ ! Test results
+ print (string) which_str, ": ";
+ @je expectr 999 ?skip_expectr;
+ check(result, expectr); print ", ";
+ .skip_expectr;
+ @pull top_of_stack;
+ check(top_of_stack, expect1); print ", ";
+ @pull top_of_stack;
+ check(top_of_stack, expect2);
+
+ new_line;
+ rtrue;
+
+ ! If you got here, inc_chk/dec_chk broke
+ .bad_indirect_inc;
+ print (string) which_str, ": ";
+ check(result, 123);
+ new_line;
+ rfalse;
+];
+
+! The lengths here must exceed the number of characters printed (below)
+! by at least 3.
+Array streambuf -> 170;
+Array streambufcmp -> 170;
+
+! The StreamTrip test sends every legal, printable ZSCII character to
+! a memory stream, and then reads them back. They should all survive
+! the journey unscathed.
+!
+! Note that character 9 (tab) and 10 (unix newline) are not legal for
+! output. (Despite the fact that Zork 1 uses tabs for indenting
+! the temple book's text.) (Tab is legal in V6, but this test is for
+! v5/8.)
+!
+! Printing character 0 is legal, but doesn't generate output. It should
+! not count towards the stream count either. (See 3.8.2.1.)
+!
+TestClass StreamTripTest
+ with name 'streamtrip',
+ short_name "streamtrip",
+ testfunc [ ix len pos;
+ print "Memory stream round-trip:^^";
+
+ @output_stream 3 streambuf;
+ pos = 0;
+ @print_char 13; ! newline
+ streambufcmp->pos = 13; pos++;
+ for (ix=32 : ix <= 126 : ix++) {
+ streambufcmp->pos = ix; pos++;
+ @print_char ix;
+ }
+ for (ix=155 : ix <= 223 : ix++) {
+ streambufcmp->pos = ix; pos++;
+ @print_char ix;
+ }
+ ! no streambufcmp output for the no-op
+ @print_char 0; ! no-op
+ @print_char 64; ! @-sign
+ streambufcmp->pos = 64; pos++;
+ @output_stream -3;
+
+ ix = streambuf;
+ len = ix-->0;
+ print "Number of characters written: "; check(pos, 166); new_line;
+ print "Number of characters read: "; check(len, 166); new_line;
+
+ check_array( streambuf+2, streambufcmp, len, Streamtripprint );
+
+ count_failures();
+ ];
+
+[ Streamtripprint ch;
+ print "Mismatch for ", ch, " '";
+ @print_char ch;
+ print "': ";
+];
+
+TestClass StreamOpTest
+ with name 'streamop',
+ short_name "streamop",
+ testfunc [ len;
+ print "Memory stream opcodes:^^";
+
+ @output_stream 3 streambuf;
+ @print_paddr "abc";
+ @output_stream -3;
+
+ len = (streambuf+0)-->0;
+ print "@@64print_paddr: ";
+ check(len, 3); print " characters written: ";
+ check_arr_3(streambuf+2, 'a', 'b', 'c'); new_line;
+
+ @output_stream 3 streambuf;
+ @print_num 789;
+ @output_stream -3;
+
+ len = (streambuf+0)-->0;
+ print "@@64print_num: ";
+ check(len, 3); print " characters written: ";
+ check_arr_3(streambuf+2, '7', '8', '9'); new_line;
+
+ @output_stream 3 streambuf;
+ @print_char 'x';
+ @output_stream -3;
+
+ len = (streambuf+0)-->0;
+ print "@@64print_char: ";
+ check(len, 1); print " characters written: ";
+ check_arr_3(streambuf+2, 'x', '8', '9'); new_line;
+
+ count_failures();
+ ];
+
+TestClass Throwcatch
+ with name 'throwcatch' 'throw' 'catch',
+ short_name "throwcatch",
+ testfunc [ val;
+ print "@@64throw/@@64catch:^^";
+
+ testglobal = 0;
+ testglobal2 = 0;
+
+ val = Throwfunc1();
+
+ print "The function with @@64catch will be returned with the value of @@64throw: "; check(val, 1); print "^";
+ print "Intermediate functions should not set their storers.^testglobal="; check(testglobal, 0); print "^";
+ print "testglobal2="; check(testglobal2, 0); print "^";
+
+ count_failures();
+ ];
+
+[ Throwfunc1 val;
+ print "Throwfunc1^";
+ val = Throwfunc2();
+ print "Returning from Throwfunc1^^";
+ return val;
+];
+
+[ Throwfunc2 val;
+ print "Throwfunc2^";
+ @catch -> val;
+ val = Throwfunc3(val);
+ print "Returning from Throwfunc2^";
+ return 2;
+];
+
+[ Throwfunc3 val;
+ print "Throwfunc3^";
+ testglobal = Throwfunc4(val);
+ print "Returning from Throwfunc3^";
+ return 3;
+];
+
+[ Throwfunc4 val;
+ print "Throwfunc4^";
+ testglobal2 = Throwfunc5(val);
+ print "Returning from Throwfunc4^";
+ return 4;
+];
+
+[ Throwfunc5 val;
+ print "Throwfunc5^About to @@64throw - should then return from Throwfunc1^";
+ @throw 1 val;
+ print "Returning from Throwfunc5^";
+ return 5;
+];
+
+Array tables_data -> 256;
+Array copy_table_reference ->
+$00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0a $0b $0c $0d $0e $0f
+$00 $01 $02 $03 $04 $05 $06 $07 $18 $19 $1a $1b $1c $1d $1e $1f
+$30 $31 $32 $33 $34 $35 $36 $37 $28 $29 $2a $2b $2c $2d $2e $2f
+$30 $31 $32 $33 $34 $35 $36 $37 $38 $39 $3a $3b $3c $3d $3e $3f
+$40 $41 $42 $43 $40 $41 $42 $43 $44 $45 $46 $47 $4c $4d $4e $4f
+$54 $55 $56 $57 $58 $59 $5a $5b $58 $59 $5a $5b $5c $5d $5e $5f
+$60 $61 $62 $63 $60 $61 $62 $63 $60 $61 $62 $63 $6c $6d $6e $6f
+$00 $00 $00 $00 $00 $00 $00 $00 $78 $79 $7a $7b $7c $7d $7e $7f;
+
+TestClass Tables
+ with name 'tables' 'table',
+ testfunc [ val val2 ix;
+ print "*_table tests:^^";
+
+ for (ix=0 : ix<256 : ix++) {
+ tables_data->ix = ix;
+ }
+
+ print "@@64print_table - should print the alphabet in upper then lower case:^";
+ val = tables_data + $41;
+ @print_table val 26 2 6;
+
+ print "^^@@64scan_table:";
+ print "^Default form, first word: ";
+ @scan_table $0001 tables_data 128 -> val ?~bad_scan_table;
+ check(val, tables_data);
+ print "^Default form, another word: ";
+ @scan_table $8081 tables_data 128 -> val ?~bad_scan_table;
+ check(val, tables_data + $80);
+ print "^Manually specified default form: ";
+ @scan_table $8081 tables_data 128 $82 -> val ?~bad_scan_table;
+ check(val, tables_data + $80);
+ print "^Default form, nonexistent word: ";
+ @scan_table $0102 tables_data 128 -> val ?bad_scan_table;
+ check(val, 0);
+
+ print "^Byte form, first byte: ";
+ @scan_table $00 tables_data 256 $01 -> val ?~bad_scan_table;
+ check(val, tables_data);
+ print "^Byte form, another byte: ";
+ @scan_table $80 tables_data 256 $01 -> val ?~bad_scan_table;
+ check(val, tables_data + $80);
+ print "^Byte form, nonexistent byte: ";
+ @scan_table $100 tables_data 256 $01 -> val ?bad_scan_table;
+ check(val, 0);
+
+ print "^Longer form, first word: ";
+ @scan_table $0001 tables_data 64 $84 -> val ?~bad_scan_table;
+ check(val, tables_data);
+ print "^Longer form, another word: ";
+ @scan_table $8081 tables_data 64 $84 -> val ?~bad_scan_table;
+ check(val, tables_data + $80);
+ print "^Longer form, a word which will be skipped: ";
+ @scan_table $0203 tables_data 64 $84 -> val ?bad_scan_table;
+ check(val, 0);
+
+ print "^Longer byte form, first byte: ";
+ @scan_table $00 tables_data 64 $04 -> val ?~bad_scan_table;
+ check(val, tables_data);
+ print "^Longer byte form, another byte: ";
+ @scan_table $80 tables_data 64 $04 -> val ?~bad_scan_table;
+ check(val, tables_data + $80);
+ print "^Longer byte form, a byte which will be skipped: ";
+ @scan_table $02 tables_data 64 $04 -> val ?bad_scan_table;
+ check(val, 0);
+
+ print "^Default form, word after length of table: ";
+ @scan_table $8081 tables_data 63 -> val ?bad_scan_table;
+ check(val, 0);
+ print "^Longer form, word after length of table: ";
+ @scan_table $8081 tables_data 31 $84 -> val ?bad_scan_table;
+ check(val, 0);
+
+ .copy_table;
+ print "^^@@64copy_table:^";
+ print "Copying forward, non-overlapping.^";
+ val = tables_data + $10;
+ @copy_table tables_data val 8;
+ val2 = copy_table_reference + $10;
+ check_array( val, val2, 16, Copytableprint );
+
+ print "Copying backwards, non-overlapping.^";
+ val = tables_data + $20;
+ val2 = tables_data + $30;
+ @copy_table val2 val 8;
+ val2 = copy_table_reference + $20;
+ check_array( val, val2, 16, Copytableprint );
+
+ print "Copying forward, overlapping, non-corrupting.^";
+ val = tables_data + $40;
+ val2 = tables_data + $44;
+ @copy_table val val2 8;
+ val2 = copy_table_reference + $40;
+ check_array( val, val2, 16, Copytableprint );
+
+ print "Copying backward, overlapping, non-corrupting.^";
+ val = tables_data + $50;
+ val2 = tables_data + $54;
+ @copy_table val2 val 8;
+ val2 = copy_table_reference + $50;
+ check_array( val, val2, 16, Copytableprint );
+
+ print "Copying forward, overlapping, corrupting.^";
+ val = tables_data + $60;
+ val2 = tables_data + $64;
+ @copy_table val val2 (-8);
+ val2 = copy_table_reference + $60;
+ check_array( val, val2, 16, Copytableprint );
+
+ print "Using @@64copy_table to zero out an array.^";
+ val = tables_data + $70;
+ @copy_table val 0 8;
+ val2 = copy_table_reference + $70;
+ check_array( val, val2, 16, Copytableprint );
+
+ val = failures;
+
+ print "Checking final table (failures are not counted twice).^";
+ check_array( tables_data, copy_table_reference, 128, Copytableprint );
+
+ failures = val;
+ count_failures();
+ return;
+
+ .bad_scan_table;
+ print "^Bad @@64scan_table branch.";
+ failures++;
+ jump copy_table;
+ ];
+
+[ Copytableprint ch ix;
+ print "Mismatch for index ", ix, ": ";
+];
+
+TestClass specfixes
+ with name 'specfixes' 'fixes',
+ testfunc [ val ;
+ print "Z-Machine 1.1 Updates/Clarifications:^^";
+
+ ! Operand evalution
+ ! Opcode operands are always evaluated from first to last
+ print "Operand evalution: ";
+ @push 2;
+ @push 4;
+ @sub sp sp -> val;
+ print "4-2="; check(val, 2); print "^";
+
+ ! Indirect variable references
+ ! An indirect reference to the stack pointer does not push or pull the top item of the stack - it is read or written in place.
+ print "Indirect variable references:^";
+ @push 9;
+ @push 5;
+ @dec sp;
+ @pull val;
+ @pull val;
+ print "@@64dec: guard="; check(val, 9); print "^";
+ @push 9;
+ @push 7;
+ @dec_chk sp 2 ?jumpfail;
+ @pull val;
+ @pull val;
+ print "@@64dec_chk: guard="; check(val, 9); print "^";
+ @push 9;
+ @push 5;
+ @inc sp;
+ @pull val;
+ @pull val;
+ print "@@64inc: guard="; check(val, 9); print "^";
+ @push 9;
+ @push 2;
+ @inc_chk sp 7 ?jumpfail;
+ @pull val;
+ @pull val;
+ print "@@64inc_chk: guard="; check(val, 9); print "^";
+ ! Push an extra value in case @load fails - we don't want a stack underflow
+ @push 1;
+ @push 9;
+ @load sp -> val;
+ @pull val;
+ print "@@64load: guard="; check(val, 9); print "^";
+ @push 9;
+ @pull sp;
+ @pull val;
+ print "@@64pull: guard="; check(val, 9); print "^";
+ @push 9;
+ @push 1;
+ @store sp 2;
+ @pull val;
+ @pull val;
+ print "@@64store: guard="; check(val, 9); print "^";
+
+ ! @je
+ ! @je can take between 2 and 4 operands.
+ print "@@64je operands: ";
+ val = 1;
+ @je 1 0 1 ?je1;
+ val = 0;
+ .je1;
+ print "3: "; check(val, 1);
+ val = 1;
+ @je 1 0 0 1 ?je2;
+ val = 0;
+ .je2;
+ print ", 4: "; check(val, 1); print "^";
+
+ ! @get_prop_len
+ ! @get_prop_len 0 must return 0.
+ val = 1;
+ print "@@64get_prop_len 0: ";
+ @get_prop_len 0 -> val;
+ check(val, 0); print "^";
+
+ !### @set_cursor
+ !### @output_stream
+ !### Mouse
+ !### Sound
+
+ count_failures();
+ return;
+
+ .jumpfail;
+ failures++;
+ count_failures();
+ ];
+
+Array basic_color_set --> $0000 $001D $0340 $03BD $59A0 $7C1F $77A0 $7FFF $5AD6 $4631 $2D6B;
+
+Array rainbow_set --> $001f $007f $00bf $00ff $013f $019f $01df $023f $027f $02bf $02ff $035f $039f $03df $03fd $03fb $03f9 $03f6 $03f4 $03f2 $03ef $03ed $03eb $03e8 $03e6 $03e4 $03e1 $07e0 $0fe0 $1be0 $23e0 $2be0 $33e0 $3fe0 $47e0 $53e0 $5be0 $63e0 $6be0 $77e0 $7fe0 $7f80 $7f40 $7f00 $7ec0 $7e60 $7e20 $7dc0 $7d80 $7d40 $7d00 $7ca0 $7c60 $7c00 $7c01 $7c04 $7c06 $7c09 $7c0b $7c0d $7c0f $7c12 $7c14 $7c17 $7c19 $7c1b $7c1d $781f $701f $641f $5c1f $541f $4c1f $401f $381f $2c1f $241f $1c1f $141f $081f;
+
+TestClass spec11
+ with name 'spec11',
+ testfunc [ i val;
+ print "Z-Machine 1.1 tests:^^";
+
+ ! Check we're in a 1.1 version interpreter
+ val = HDR_SPECREVISION-->0;
+ if (val < $0101)
+ {
+ print "Stopping, interpreter is only version "; Version(); print ".^";
+ return;
+ }
+
+ print "Ok, interpreter is version "; Version(); print ".^^";
+
+ ! Test @set_true_colour by printing a rainbow
+ print "Checking @@64set_true_colour by printing a pretty rainbow (ultimate prettiness of rainbow depends on the observer's tastes):^^";
+
+ @set_text_style 8;
+ for (i=0 : i<80 : i++)
+ {
+ val = rainbow_set-->i;
+ @"EXT:13" -1 val;
+ print " ";
+ }
+ @set_colour 1 1;
+ @set_text_style 0;
+ print "^^";
+
+ ! Check the recommended colour set
+ print "Checking if the basic colour set uses the recommended true colours:^^";
+
+ @set_text_style 8;
+ print "Recommended: ";
+ for (i=0 : i<11 : i++)
+ {
+ val = basic_color_set-->i;
+ @"EXT:13" -1 val;
+ print " ";
+ }
+ @set_colour 1 1;
+ print "^Interpreter: ";
+ for (i=2 : i<13 : i++)
+ {
+ @set_colour 1 i;
+ print " ";
+ }
+ @set_colour 1 1;
+ @set_text_style 0;
+ print "^";
+
+ !count_failures();
+ return;
+ ];
+
+TestClass gestalt
+ with name 'spec12' 'gestalt',
+ testfunc [ val ;
+ print "Z-Machine 1.2 (@@64gestalt):^^";
+
+ ! Check we're in a 1.2 version interpreter
+ val = HDR_SPECREVISION-->0;
+ if (val < $0102)
+ {
+ print "Stopping, interpreter is only version "; Version(); print ".^";
+ return;
+ }
+
+ print "Ok, interpreter is version "; Version(); print ".^^";
+
+ ! Checking non-existant selector
+ @"EXT:30S" 0 0 -> val;
+ print "Selector 0 (non-existant): 0="; check(val, 0); print "^";
+
+ ! Checking Standard Revision selector
+ @"EXT:30S" 1 0 -> val;
+ print "Selector 1 (Standard Revision): $0102<= "; check_hex_min(val, $0102); print "^";
+
+ count_failures();
+ return;
+ ];