ref: 895ec5564d85822289c250d115fc7fec085d4cd7
parent: 7bb6f71f0b51afd06446fe5138f5e90f996af7e4
author: Rangi <35663410+Rangi42@users.noreply.github.com>
date: Fri Jan 1 14:39:20 EST 2021
Update mathematical functions (#675) Document the existing `ROUND`, `CEIL`, and `FLOOR` functions Also update the trig function docs for searchability Implement `POW` and `LOG` Addresses part of #675 Implement ** for integer exponents ** has higher precedence than -, like Python, so -3**4 == -(3**4) == 81
--- a/include/asm/mymath.h
+++ b/include/asm/mymath.h
@@ -22,6 +22,8 @@
int32_t math_ATan2(int32_t i, int32_t j);
int32_t math_Mul(int32_t i, int32_t j);
int32_t math_Div(int32_t i, int32_t j);
+int32_t math_Pow(int32_t i, int32_t j);
+int32_t math_Log(int32_t i, int32_t j);
int32_t math_Round(int32_t i);
int32_t math_Ceil(int32_t i);
int32_t math_Floor(int32_t i);
--- a/include/linkdefs.h
+++ b/include/linkdefs.h
@@ -14,7 +14,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
#define RGBDS_OBJECT_VERSION_NUMBER 9U
-#define RGBDS_OBJECT_REV 6U
+#define RGBDS_OBJECT_REV 7U
enum AssertionType {
ASSERT_WARN,
@@ -29,6 +29,7 @@
RPN_DIV = 0x03,
RPN_MOD = 0x04,
RPN_UNSUB = 0x05,
+ RPN_EXP = 0x06,
RPN_OR = 0x10,
RPN_AND = 0x11,
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -182,6 +182,8 @@
{"FLOOR", T_OP_FLOOR},
{"DIV", T_OP_FDIV},
{"MUL", T_OP_FMUL},
+ {"POW", T_OP_POW},
+ {"LOG", T_OP_LOG},
{"SIN", T_OP_SIN},
{"COS", T_OP_COS},
{"TAN", T_OP_TAN},
@@ -488,7 +490,7 @@
uint16_t children[0x60 - ' '];
struct KeywordMapping const *keyword;
/* Since the keyword structure is invariant, the min number of nodes is known at compile time */
-} keywordDict[350] = {0}; /* Make sure to keep this correct when adding keywords! */
+} keywordDict[352] = {0}; /* Make sure to keep this correct when adding keywords! */
/* Convert a char into its index into the dict */
static inline uint8_t dictIndex(char c)
@@ -1606,13 +1608,20 @@
lexer_GetLineNo(), lexer_GetColNo());
for (;;) {
int c = nextChar();
+ char secondChar;
switch (c) {
/* Ignore whitespace and comments */
case '*':
- if (!lexerState->atLineStart)
+ if (!lexerState->atLineStart) { /* Either MUL or EXP */
+ secondChar = peek(0);
+ if (secondChar == '*') {
+ shiftChars(1);
+ return T_OP_EXP;
+ }
return T_OP_MUL;
+ }
warning(WARNING_OBSOLETE,
"'*' is deprecated for comments, please use ';' instead\n");
/* fallthrough */
@@ -1651,7 +1660,6 @@
return T_COMMA;
/* Handle ambiguous 1- or 2-char tokens */
- char secondChar;
case '/': /* Either division or a block comment */
secondChar = peek(0);
if (secondChar == '*') {
--- a/src/asm/math.c
+++ b/src/asm/math.c
@@ -126,6 +126,22 @@
}
/*
+ * Power
+ */
+int32_t math_Pow(int32_t i, int32_t j)
+{
+ return double2fx(pow(fx2double(i), fx2double(j)));
+}
+
+/*
+ * Logarithm
+ */
+int32_t math_Log(int32_t i, int32_t j)
+{
+ return double2fx(log(fx2double(i)) / log(fx2double(j)));
+}
+
+/*
* Round
*/
int32_t math_Round(int32_t i)
--- a/src/asm/parser.y
+++ b/src/asm/parser.y
@@ -353,6 +353,11 @@
%left T_OP_SHL T_OP_SHR
%left T_OP_MUL T_OP_DIV T_OP_MOD
%left T_OP_NOT
+
+%left NEG /* negation -- unary minus */
+
+%left T_OP_EXP
+
%left T_OP_DEF
%left T_OP_BANK T_OP_ALIGN
%left T_OP_SIN
@@ -364,6 +369,8 @@
%left T_OP_ATAN2
%left T_OP_FDIV
%left T_OP_FMUL
+%left T_OP_POW
+%left T_OP_LOG
%left T_OP_ROUND
%left T_OP_CEIL
%left T_OP_FLOOR
@@ -381,8 +388,6 @@
%left T_OP_STRLWR
%left T_OP_STRFMT
-%left NEG /* negation -- unary minus */
-
%token <tzSym> T_LABEL
%token <tzSym> T_ID
%token <tzSym> T_LOCAL_ID
@@ -1131,6 +1136,9 @@
| relocexpr T_OP_MOD relocexpr {
rpn_BinaryOp(RPN_MOD, &$$, &$1, &$3);
}
+ | relocexpr T_OP_EXP relocexpr {
+ rpn_BinaryOp(RPN_EXP, &$$, &$1, &$3);
+ }
| T_OP_ADD relocexpr %prec NEG { $$ = $2; }
| T_OP_SUB relocexpr %prec NEG { rpn_UNNEG(&$$, &$2); }
| T_OP_NOT relocexpr %prec NEG { rpn_UNNOT(&$$, &$2); }
@@ -1165,6 +1173,12 @@
}
| T_OP_FMUL T_LPAREN const T_COMMA const T_RPAREN {
rpn_Number(&$$, math_Mul($3, $5));
+ }
+ | T_OP_POW T_LPAREN const T_COMMA const T_RPAREN {
+ rpn_Number(&$$, math_Pow($3, $5));
+ }
+ | T_OP_LOG T_LPAREN const T_COMMA const T_RPAREN {
+ rpn_Number(&$$, math_Log($3, $5));
}
| T_OP_SIN T_LPAREN const T_RPAREN {
rpn_Number(&$$, math_Sin($3));
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -131,6 +131,7 @@
.It Sy Operator Ta Sy Meaning
.It Li \&( \&) Ta Precedence override
.It Li FUNC() Ta Built-in function call
+.It Li ** Ta Exponent
.It Li ~ + - Ta Unary complement/plus/minus
.It Li * / % Ta Multiply/divide/modulo
.It Li << >> Ta Shift left/right
@@ -190,12 +191,17 @@
.It Sy Name Ta Sy Operation
.It Fn DIV x y Ta $x \[di] y$
.It Fn MUL x y Ta $x \[mu] y$
-.It Fn SIN x Ta $sin ( x )$
-.It Fn COS x Ta $cos ( x )$
-.It Fn TAN x Ta $tan ( x )$
-.It Fn ASIN x Ta $asin ( x )$
-.It Fn ACOS x Ta $acos ( x )$
-.It Fn ATAN x Ta $atan ( x )$
+.It Fn POW x y Ta $x$ to the $y$ power
+.It Fn LOG x y Ta Logarithm of $x$ to the base $y$
+.It Fn ROUND x Ta Round $x$ to the nearest integer
+.It Fn CEIL x Ta Round $x$ up to an integer
+.It Fn FLOOR x Ta Round $x$ down to an integer
+.It Fn SIN x Ta Sine of $x$
+.It Fn COS x Ta Cosine of $x$
+.It Fn TAN x Ta Tangent of $x$
+.It Fn ASIN x Ta Inverse sine of $x$
+.It Fn ACOS x Ta Inverse cosine of $x$
+.It Fn ATAN x Ta Inverse tangent of $x$
.It Fn ATAN2 x y Ta Angle between $( x , y )$ and $( 1 , 0 )$
.El
.EQ
--- a/src/asm/rpn.c
+++ b/src/asm/rpn.c
@@ -292,6 +292,20 @@
}
}
+static int32_t exponent(int32_t base, int32_t power)
+{
+ int32_t result = 1;
+
+ while (power) {
+ if (power % 2)
+ result *= base;
+ power /= 2;
+ base *= base;
+ }
+
+ return result;
+}
+
struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
{
if (!rpn_isSymbol(expr))
@@ -414,6 +428,15 @@
expr->nVal = 0;
else
expr->nVal = src1->nVal % src2->nVal;
+ break;
+ case RPN_EXP:
+ if (src2->nVal < 0)
+ fatalerror("Exponentiation by negative power\n");
+
+ if (src1->nVal == INT32_MIN && src2->nVal == -1)
+ expr->nVal = 0;
+ else
+ expr->nVal = exponent(src1->nVal, src2->nVal);
break;
case RPN_UNSUB:
--- a/src/link/patch.c
+++ b/src/link/patch.c
@@ -21,6 +21,20 @@
#include "extern/err.h"
+static int32_t exponent(int32_t base, int32_t power)
+{
+ int32_t result = 1;
+
+ while (power) {
+ if (power % 2)
+ result *= base;
+ power /= 2;
+ base *= base;
+ }
+
+ return result;
+}
+
static int32_t asl(int32_t value, int32_t shiftamt); // Forward decl for below
static int32_t asr(int32_t value, int32_t shiftamt)
{
@@ -236,6 +250,18 @@
break;
case RPN_UNSUB:
value = -popRPN();
+ break;
+ case RPN_EXP:
+ value = popRPN();
+ if (value < 0) {
+ if (!isError)
+ error(patch->src, patch->lineNo, "Exponent by negative");
+ isError = true;
+ popRPN();
+ value = 0;
+ } else {
+ value = exponent(popRPN(), value);
+ }
break;
case RPN_OR:
--- /dev/null
+++ b/test/asm/math.asm
@@ -1,0 +1,40 @@
+X equ 0
+
+test: MACRO
+; Test RGBASM
+v equs "X +"
+ static_assert \#
+ purge v
+; Test RGBLINK
+v equs "Y +"
+ assert \#
+ purge v
+ENDM
+
+ test (v 2)*(v 10)**(v 2)*(v 2) == (v 400)
+ test -(v 3)**(v 4) == v -81
+
+ assert DIV(5.0, 2.0) == 2.5
+ assert DIV(-5.0, 2.0) == -2.5
+ assert DIV(-5.0, 0.0) == $8000_0000
+
+ assert MUL(10.0, 0.5) == 5.0
+ assert MUL(10.0, 0.0) == 0.0
+
+ assert POW(10.0, 2.0) == 100.0
+ assert POW(100.0, 0.5) == 10.0
+
+ assert LOG(100.0, 10.0) == 2.0
+ assert LOG(256.0, 2.0) == 8.0
+
+ assert ROUND(1.5) == 2.0
+ assert ROUND(-1.5) == -2.0
+
+ assert CEIL(1.5) == 2.0
+ assert CEIL(-1.5) == -1.0
+
+ assert FLOOR(1.5) == 1.0
+ assert FLOOR(-1.5) == -2.0
+
+SECTION "Y", ROM0
+Y::