diff --git a/include/asm/mymath.h b/include/asm/mymath.h index 085ba422..f7c11aa6 100644 --- a/include/asm/mymath.h +++ b/include/asm/mymath.h @@ -22,6 +22,8 @@ int32_t math_ATan(int32_t i); 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); diff --git a/include/linkdefs.h b/include/linkdefs.h index e088a511..d21f61f8 100644 --- 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 @@ enum RPNCommand { RPN_DIV = 0x03, RPN_MOD = 0x04, RPN_UNSUB = 0x05, + RPN_EXP = 0x06, RPN_OR = 0x10, RPN_AND = 0x11, diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 7edaee7a..9ac85c2a 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -182,6 +182,8 @@ static struct KeywordMapping { {"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 @@ struct KeywordDictNode { 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 @@ static int yylex_NORMAL(void) 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 @@ static int yylex_NORMAL(void) return T_COMMA; /* Handle ambiguous 1- or 2-char tokens */ - char secondChar; case '/': /* Either division or a block comment */ secondChar = peek(0); if (secondChar == '*') { diff --git a/src/asm/math.c b/src/asm/math.c index eb19e1fe..df122748 100644 --- a/src/asm/math.c +++ b/src/asm/math.c @@ -125,6 +125,22 @@ int32_t math_Div(int32_t i, int32_t j) return double2fx(fx2double(i) / fx2double(j)); } +/* + * 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 */ diff --git a/src/asm/parser.y b/src/asm/parser.y index d62524fe..5d59e876 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -353,6 +353,11 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg) %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 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg) %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 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg) %left T_OP_STRLWR %left T_OP_STRFMT -%left NEG /* negation -- unary minus */ - %token T_LABEL %token T_ID %token T_LOCAL_ID @@ -1131,6 +1136,9 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); } | 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); } @@ -1166,6 +1174,12 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); } | 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)); } diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index 2ebebf3b..cf84e247 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -131,6 +131,7 @@ A great number of operators you can use in expressions are available (listed fro .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 @@ delim $$ .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 diff --git a/src/asm/rpn.c b/src/asm/rpn.c index 112015d4..4268edc6 100644 --- a/src/asm/rpn.c +++ b/src/asm/rpn.c @@ -292,6 +292,20 @@ static int32_t shift(int32_t shiftee, int32_t amount) } } +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)) @@ -415,6 +429,15 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, 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: case RPN_UNNOT: diff --git a/src/link/patch.c b/src/link/patch.c index 3a72ccbc..86f85f14 100644 --- 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) { @@ -237,6 +251,18 @@ static int32_t computeRPNExpr(struct Patch const *patch, 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: value = popRPN() | popRPN(); diff --git a/test/asm/math.asm b/test/asm/math.asm new file mode 100644 index 00000000..3d1dace4 --- /dev/null +++ b/test/asm/math.asm @@ -0,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:: diff --git a/test/asm/math.err b/test/asm/math.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/math.out b/test/asm/math.out new file mode 100644 index 00000000..e69de29b