Add Bocfel interpreter
[projects/chimara/chimara.git] / interpreters / bocfel / math.c
diff --git a/interpreters/bocfel/math.c b/interpreters/bocfel/math.c
new file mode 100644 (file)
index 0000000..925391d
--- /dev/null
@@ -0,0 +1,166 @@
+/*-
+ * Copyright 2010-2012 Chris Spiegel.
+ *
+ * This file is part of Bocfel.
+ *
+ * Bocfel is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version
+ * 2 or 3, as published by the Free Software Foundation.
+ *
+ * Bocfel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Bocfel.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+
+#include "math.h"
+#include "branch.h"
+#include "process.h"
+#include "stack.h"
+#include "util.h"
+#include "zterp.h"
+
+void zinc(void)
+{
+  store_variable(zargs[0], variable(zargs[0]) + 1);
+}
+
+void zdec(void)
+{
+  store_variable(zargs[0], variable(zargs[0]) - 1);
+}
+
+void znot(void)
+{
+  store(~zargs[0]);
+}
+
+void zdec_chk(void)
+{
+  int16_t new;
+  int16_t val = zargs[1];
+
+  zdec();
+
+  /* The z-spec 1.1 requires indirect variable references to the stack not to push/pop */
+  if(zargs[0] == 0) new = *stack_top_element();
+  else              new = variable(zargs[0]);
+
+  branch_if(new < val);
+}
+
+void zinc_chk(void)
+{
+  int16_t new;
+  int16_t val = zargs[1];
+
+  zinc();
+
+  /* The z-spec 1.1 requires indirect variable references to the stack not to push/pop */
+  if(zargs[0] == 0) new = *stack_top_element();
+  else              new = variable(zargs[0]);
+
+  branch_if(new > val);
+}
+
+void ztest(void)
+{
+  branch_if( (zargs[0] & zargs[1]) == zargs[1] );
+}
+
+void zor(void)
+{
+  store(zargs[0] | zargs[1]);
+}
+
+void zand(void)
+{
+  store(zargs[0] & zargs[1]);
+}
+
+void zadd(void)
+{
+  store(zargs[0] + zargs[1]);
+}
+
+void zsub(void)
+{
+  store(zargs[0] - zargs[1]);
+}
+
+void zmul(void)
+{
+  store(zargs[0] * zargs[1]);
+}
+
+void zdiv(void)
+{
+  ZASSERT(zargs[1] != 0, "divide by zero");
+  store((int16_t)zargs[0] / (int16_t)zargs[1]);
+}
+
+void zmod(void)
+{
+  ZASSERT(zargs[1] != 0, "divide by zero");
+  store((int16_t)zargs[0] % (int16_t)zargs[1]);
+}
+
+void zlog_shift(void)
+{
+  int16_t places = zargs[1];
+
+  /* Shifting more than 15 bits is undefined (as of Standard 1.1), but
+   * do the most sensible thing.
+   */
+  if(places < -15 || places > 15)
+  {
+    store(0);
+    return;
+  }
+
+  if(places < 0) store(zargs[0] >> -places);
+  else           store(zargs[0] <<  places);
+}
+
+void zart_shift(void)
+{
+  int16_t number = zargs[0], places = zargs[1];
+
+  /* Shifting more than 15 bits is undefined (as of Standard 1.1), but
+   * do the most sensible thing.
+   */
+  if(places < -15 || places > 15)
+  {
+    store(number < 0 ? -1 : 0);
+    return;
+  }
+
+  /* Shifting a negative value in C has some consequences:
+   * • Shifting a negative value left is undefined.
+   * • Shifting a negative value right is implementation defined.
+   *
+   * Thus these are done by hand.  The Z-machine requires a right-shift
+   * of a negative value to propagate the sign bit.  This is easily
+   * accomplished by complementing the value (yielding a positive
+   * number), shifting it right (zero filling), and complementing again
+   * (flip the shifted-in zeroes to ones).
+   *
+   * For a left-shift, the result should presumably be the same as a
+   * logical shift, so do that.
+   */
+  if(number < 0)
+  {
+    if(places < 0) store(~(~number >> -places));
+    else           store(zargs[0]  <<  places);
+  }
+  else
+  {
+    if(places < 0) store(zargs[0] >> -places);
+    else           store(zargs[0] <<  places);
+  }
+}