aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--LICENSE1
-rw-r--r--README1
-rw-r--r--tests/Makefile34
-rw-r--r--tests/float.expect56
-rw-r--r--tests/float.nxc141
-rw-r--r--tests/term.h55
-rw-r--r--tests/term.py78
7 files changed, 366 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
index f5814a9..fb590f9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -12,6 +12,7 @@ Details:
- startup/*: see header (provided "as is").
- armdebug/*: see armdebug/README (dual, LEGO Open Source License or GPLv2).
- contrib/*: see header.
+- tests/*: Expat license, see header.
LEGO(R) is a trademark of the LEGO Group of companies which does not sponsor,
authorize or endorse NXT Improved Firmware.
diff --git a/README b/README
index 29b5f61..8dcca98 100644
--- a/README
+++ b/README
@@ -32,6 +32,7 @@ Details:
- startup/*: see header (provided "as is").
- armdebug/*: see armdebug/README (dual, LEGO Open Source License or GPLv2).
- contrib/*: see header.
+- tests/*: Expat license, see header.
LEGO(R) is a trademark of the LEGO Group of companies which does not sponsor,
authorize or endorse NXT Improved Firmware.
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..9e7d788
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,34 @@
+NBC = nbc
+OPTIMIZE = -Z2
+FIRMWARE =
+PYTHON = python3
+# or PYTHON = poetry -C path/to/nxt-python run python
+
+TESTS = float
+
+tests: $(TESTS:%=%.diff)
+
+%.rxe: %.nxc
+ $(NBC) $(OPTIMIZE) $(FIRMWARE) -O=$@ $<
+
+%.load: %.rxe
+ $(NBC) $(OPTIMIZE) $(FIRMWARE) -d -b $<
+
+%.out: %.load
+ $(PYTHON) term.py -qos $* > $@
+
+%.diff: %.out %.expect
+ diff $^ > $@
+
+%.S: %.nxc
+ $(NBC) $(OPTIMIZE) $(FIRMWARE) -nbc=$@ $<
+
+clean-out:
+ rm -f $(TESTS:%=%.out) $(TESTS:%=%.diff)
+
+clean: clean-out
+ rm -f $(TESTS:%=%.rxe)
+
+.SECONDARY:
+
+.DELETE_ON_ERROR:
diff --git a/tests/float.expect b/tests/float.expect
new file mode 100644
index 0000000..4777fb1
--- /dev/null
+++ b/tests/float.expect
@@ -0,0 +1,56 @@
+move -12.9
+ char -12 244
+ short -12 65524
+ long -12 4294967284
+move -12.34
+ char -12 244
+ short -12 65524
+ long -12 4294967284
+move 12.34
+ char 12 12
+ short 12 12
+ long 12 12
+move 12.9
+ char 12 12
+ short 12 12
+ long 12 12
+move[] -12.9
+ char -12 244
+ short -12 65524
+ long -12 4294967284
+move[] -12.34
+ char -12 244
+ short -12 65524
+ long -12 4294967284
+move[] 12.34
+ char 12 12
+ short 12 12
+ long 12 12
+move[] 12.9
+ char 12 12
+ short 12 12
+ long 12 12
+neg -12.9
+ char 12 12
+ short 12 12
+ long 12 12
+neg -12.34
+ char 12 12
+ short 12 12
+ long 12 12
+neg 12.34
+ char -12 244
+ short -12 65524
+ long -12 4294967284
+neg 12.9
+ char -12 244
+ short -12 65524
+ long -12 4294967284
+out -12.9
+ char -13 243
+out -12.34
+ char -12 244
+out 12.34
+ char 12 12
+out 12.9
+ char 13 13
diff --git a/tests/float.nxc b/tests/float.nxc
new file mode 100644
index 0000000..4edbb29
--- /dev/null
+++ b/tests/float.nxc
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 Nicolas Schodet
+ *
+ * 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.
+ */
+
+/*
+ * Convert floating point values to integer, check conversion is right.
+ *
+ * Actual check is done on the attached computer, see float.expect.
+ *
+ * When converting a float to an integer, if the integer is not able to
+ * represent the value, the behavior is implementation defined.
+ *
+ * The original firmware source code depends on this implementation defined
+ * behavior from the C compiler.
+ *
+ * The LEGO MINDSTORMS NXT Executable File Specification does not define what
+ * is the expected behavior for the firmware because it was released for
+ * version 1.03, before float was supported. However it says: "At the scalar
+ * level, data type conversions behave identically to type casts in ANSI C.".
+ *
+ * For NXT Improved firmware, I decided to follow the behavior of the original
+ * firmware binary, which is:
+ *
+ * - Negative values to unsigned numbers: large positive integer (two's
+ * complement).
+ * - Round to nearest for SETOUT instruction, truncate for other operations.
+ *
+ * There are so many code paths inside the firmware to do the same thing that
+ * I may have missed some other cases. If you find such cases, please update
+ * this test.
+ */
+
+#include "term.h"
+
+task main()
+{
+ float a[4] = {-12.9, -12.34, 12.34, 12.9};
+ int i;
+
+ Wait(1000);
+
+ /*
+ * Test conversion using MOV instruction.
+ */
+ for (i = 0; i < 4; i++) {
+ float af = a[i];
+ char ac = af;
+ unsigned char auc = af;
+ short as = af;
+ unsigned short aus = af;
+ long al = af;
+ unsigned long aul = af;
+
+ TermText("move "); TermNumNl(af);
+ TermText(" char "); TermNum(ac); TermText(" "); TermNumNl(auc);
+ TermText(" short "); TermNum(as); TermText(" "); TermNumNl(aus);
+ TermText(" long "); TermNum(al); TermText(" "); TermNumNl(aul);
+
+ Wait(100);
+ }
+
+ /*
+ * Test conversion using MOV instruction with an array.
+ */
+ while (1) {
+ char ac[4];
+ ac = a;
+ unsigned char auc[4];
+ auc = a;
+ short as[4];
+ as = a;
+ unsigned short aus[4];
+ aus = a;
+ long al[4];
+ al = a;
+ unsigned long aul[4];
+ aul = a;
+ for (i = 0; i < 4; i++) {
+ TermText("move[] "); TermNumNl(a[i]);
+ TermText(" char "); TermNum(ac[i]); TermText(" "); TermNumNl(auc[i]);
+ TermText(" short "); TermNum(as[i]); TermText(" "); TermNumNl(aus[i]);
+ TermText(" long "); TermNum(al[i]); TermText(" "); TermNumNl(aul[i]);
+
+ Wait(100);
+ }
+ break;
+ }
+
+ /*
+ * Test conversion using NEG instruction.
+ */
+ for (i = 0; i < 4; i++) {
+ float af = a[i];
+ char ac = -af;
+ unsigned char auc = -af;
+ short as = -af;
+ unsigned short aus = -af;
+ long al = -af;
+ unsigned long aul = -af;
+
+ TermText("neg "); TermNumNl(af);
+ TermText(" char "); TermNum(ac); TermText(" "); TermNumNl(auc);
+ TermText(" short "); TermNum(as); TermText(" "); TermNumNl(aus);
+ TermText(" long "); TermNum(al); TermText(" "); TermNumNl(aul);
+
+ Wait(100);
+ }
+
+ /*
+ * Test conversion using SETOUT/GETOUT instructions.
+ */
+ for (i = 0; i < 4; i++) {
+ float af = a[i];
+ SetOutput(OUT_A, PowerField, af, RegPValueField, af);
+ char ac = GetOutput(OUT_A, PowerField);
+ unsigned char auc = GetOutput(OUT_A, RegPValueField);
+
+ TermText("out "); TermNumNl(af);
+ TermText(" char "); TermNum(ac); TermText(" "); TermNumNl(auc);
+
+ Wait(100);
+ }
+}
diff --git a/tests/term.h b/tests/term.h
new file mode 100644
index 0000000..967dccf
--- /dev/null
+++ b/tests/term.h
@@ -0,0 +1,55 @@
+#ifndef term_h
+#define term_h
+/*
+ * Copyright (C) 2024 Nicolas Schodet
+ *
+ * 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.
+ */
+
+/**
+ * Terminal like output using messages.
+ */
+
+int term_screen_line = LCD_LINE1;
+string term_line = "";
+
+inline void
+TermTextSub(string s, bool nl)
+{
+ term_line = StrCat(term_line, s);
+ if (nl) {
+ TextOut(0, term_screen_line, term_line);
+ if (term_screen_line)
+ term_screen_line -= 8;
+ else
+ term_screen_line = LCD_LINE1;
+ ClearLine(term_screen_line);
+ SendMessage(10, term_line);
+ Wait(2);
+ term_line = "";
+ }
+}
+
+#define TermText(s) TermTextSub(s, FALSE)
+#define TermTextNl(s) TermTextSub(s, TRUE)
+#define TermNum(num) TermText(NumToStr(num))
+#define TermNumNl(num) TermTextNl(NumToStr(num))
+#define TermNl(num) TermTextNl("")
+
+#endif
diff --git a/tests/term.py b/tests/term.py
new file mode 100644
index 0000000..c1a5bb6
--- /dev/null
+++ b/tests/term.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+"""Display terminal output from NXT program."""
+#
+# Copyright (C) 2024 Nicolas Schodet
+#
+# 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.
+#
+import argparse
+import nxt.locator
+import nxt.error
+import os.path
+import time
+
+p = argparse.ArgumentParser(description=__doc__)
+p.add_argument("-s", "--start", metavar="PROGRAM",
+ help="start program (with .rxe extension if not given")
+p.add_argument("-q", "--quiet", action="store_true", help="output nothing while waiting")
+p.add_argument("-o", "--once", action="store_true", help="exit after program end")
+nxt.locator.add_arguments(p)
+options = p.parse_args()
+
+spinner = "-\\|/"
+spinning = False
+seen = False
+
+def out(message):
+ print(message.rstrip(b"\0").decode("windows-1252"))
+
+with nxt.locator.find_with_options(options) as b:
+ try:
+ if options.start:
+ prog = options.start
+ if not os.path.splitext(prog)[1]:
+ prog = prog + ".rxe"
+ b.start_program(prog)
+ while True:
+ try:
+ (inbox, message) = b.message_read(10, 0, True)
+ if spinning:
+ print("\b", end="")
+ out(message)
+ seen = True
+ except nxt.error.EmptyMailboxError as e:
+ if spinning:
+ print("\b", end="")
+ if not options.quiet:
+ print("_", end="", flush=True)
+ spinning = True
+ time.sleep(0.05)
+ seen = True
+ except nxt.error.NoActiveProgramError:
+ if spinning:
+ print("\b", end="")
+ if seen and options.once:
+ break
+ if not options.quiet:
+ print(spinner[0], end="", flush=True)
+ spinner = spinner[1:] + spinner[0]
+ spinning = True
+ time.sleep(0.2)
+ except KeyboardInterrupt:
+ pass