diff --git a/include/asm/fixpoint.h b/include/asm/fixpoint.h index 6fc5ac29..b6374163 100644 --- a/include/asm/fixpoint.h +++ b/include/asm/fixpoint.h @@ -13,22 +13,22 @@ extern uint8_t fixPrecision; +uint8_t fix_Precision(void); double fix_PrecisionFactor(void); -void fix_Print(int32_t i); -int32_t fix_Sin(int32_t i); -int32_t fix_Cos(int32_t i); -int32_t fix_Tan(int32_t i); -int32_t fix_ASin(int32_t i); -int32_t fix_ACos(int32_t i); -int32_t fix_ATan(int32_t i); -int32_t fix_ATan2(int32_t i, int32_t j); -int32_t fix_Mul(int32_t i, int32_t j); -int32_t fix_Mod(int32_t i, int32_t j); -int32_t fix_Div(int32_t i, int32_t j); -int32_t fix_Pow(int32_t i, int32_t j); -int32_t fix_Log(int32_t i, int32_t j); -int32_t fix_Round(int32_t i); -int32_t fix_Ceil(int32_t i); -int32_t fix_Floor(int32_t i); +int32_t fix_Sin(int32_t i, int32_t q); +int32_t fix_Cos(int32_t i, int32_t q); +int32_t fix_Tan(int32_t i, int32_t q); +int32_t fix_ASin(int32_t i, int32_t q); +int32_t fix_ACos(int32_t i, int32_t q); +int32_t fix_ATan(int32_t i, int32_t q); +int32_t fix_ATan2(int32_t i, int32_t j, int32_t q); +int32_t fix_Mul(int32_t i, int32_t j, int32_t q); +int32_t fix_Mod(int32_t i, int32_t j, int32_t q); +int32_t fix_Div(int32_t i, int32_t j, int32_t q); +int32_t fix_Pow(int32_t i, int32_t j, int32_t q); +int32_t fix_Log(int32_t i, int32_t j, int32_t q); +int32_t fix_Round(int32_t i, int32_t q); +int32_t fix_Ceil(int32_t i, int32_t q); +int32_t fix_Floor(int32_t i, int32_t q); #endif // RGBDS_ASM_FIXPOINT_H diff --git a/man/rgbasm.5 b/man/rgbasm.5 index f688322a..7f6210b0 100644 --- a/man/rgbasm.5 +++ b/man/rgbasm.5 @@ -346,6 +346,15 @@ delim $$ delim off .EN .Pp +All of these fixed-point functions can take an optional final argument, which is the precision to use. +For example, +.Ql MUL(6.0q8, 7.0q8, 8) +will evaluate to +.Ql 42.0q8 +no matter what value is set as the current +.Cm Q +option. +.Pp The trigonometry functions ( .Ic SIN , .Ic COS , diff --git a/src/asm/fixpoint.c b/src/asm/fixpoint.c index 7c54ed0e..861fd1ce 100644 --- a/src/asm/fixpoint.c +++ b/src/asm/fixpoint.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "asm/fixpoint.h" #include "asm/symbol.h" @@ -21,8 +20,8 @@ #define M_PI 3.14159265358979323846 #endif -#define fix2double(i) ((double)((i) / fix_PrecisionFactor())) -#define double2fix(d) ((int32_t)round((d) * fix_PrecisionFactor())) +#define fix2double(i, q) ((double)((i) / pow(2.0, q))) +#define double2fix(d, q) ((int32_t)round((d) * pow(2.0, q))) // 2*pi radians == 1 turn #define turn2rad(f) ((f) * (M_PI * 2)) @@ -30,96 +29,87 @@ uint8_t fixPrecision; +uint8_t fix_Precision(void) +{ + return fixPrecision; +} + double fix_PrecisionFactor(void) { return pow(2.0, fixPrecision); } -void fix_Print(int32_t i) +int32_t fix_Sin(int32_t i, int32_t q) { - uint32_t u = i; - char const *sign = ""; - - if (i < 0) { - u = -u; - sign = "-"; - } - - printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> fixPrecision, - ((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000); + return double2fix(sin(turn2rad(fix2double(i, q))), q); } -int32_t fix_Sin(int32_t i) +int32_t fix_Cos(int32_t i, int32_t q) { - return double2fix(sin(turn2rad(fix2double(i)))); + return double2fix(cos(turn2rad(fix2double(i, q))), q); } -int32_t fix_Cos(int32_t i) +int32_t fix_Tan(int32_t i, int32_t q) { - return double2fix(cos(turn2rad(fix2double(i)))); + return double2fix(tan(turn2rad(fix2double(i, q))), q); } -int32_t fix_Tan(int32_t i) +int32_t fix_ASin(int32_t i, int32_t q) { - return double2fix(tan(turn2rad(fix2double(i)))); + return double2fix(rad2turn(asin(fix2double(i, q))), q); } -int32_t fix_ASin(int32_t i) +int32_t fix_ACos(int32_t i, int32_t q) { - return double2fix(rad2turn(asin(fix2double(i)))); + return double2fix(rad2turn(acos(fix2double(i, q))), q); } -int32_t fix_ACos(int32_t i) +int32_t fix_ATan(int32_t i, int32_t q) { - return double2fix(rad2turn(acos(fix2double(i)))); + return double2fix(rad2turn(atan(fix2double(i, q))), q); } -int32_t fix_ATan(int32_t i) +int32_t fix_ATan2(int32_t i, int32_t j, int32_t q) { - return double2fix(rad2turn(atan(fix2double(i)))); + return double2fix(rad2turn(atan2(fix2double(i, q), fix2double(j, q))), q); } -int32_t fix_ATan2(int32_t i, int32_t j) +int32_t fix_Mul(int32_t i, int32_t j, int32_t q) { - return double2fix(rad2turn(atan2(fix2double(i), fix2double(j)))); + return double2fix(fix2double(i, q) * fix2double(j, q), q); } -int32_t fix_Mul(int32_t i, int32_t j) +int32_t fix_Div(int32_t i, int32_t j, int32_t q) { - return double2fix(fix2double(i) * fix2double(j)); + return double2fix(fix2double(i, q) / fix2double(j, q), q); } -int32_t fix_Div(int32_t i, int32_t j) +int32_t fix_Mod(int32_t i, int32_t j, int32_t q) { - return double2fix(fix2double(i) / fix2double(j)); + return double2fix(fmod(fix2double(i, q), fix2double(j, q)), q); } -int32_t fix_Mod(int32_t i, int32_t j) +int32_t fix_Pow(int32_t i, int32_t j, int32_t q) { - return double2fix(fmod(fix2double(i), fix2double(j))); + return double2fix(pow(fix2double(i, q), fix2double(j, q)), q); } -int32_t fix_Pow(int32_t i, int32_t j) +int32_t fix_Log(int32_t i, int32_t j, int32_t q) { - return double2fix(pow(fix2double(i), fix2double(j))); + return double2fix(log(fix2double(i, q)) / log(fix2double(j, q)), q); } -int32_t fix_Log(int32_t i, int32_t j) +int32_t fix_Round(int32_t i, int32_t q) { - return double2fix(log(fix2double(i)) / log(fix2double(j))); + return double2fix(round(fix2double(i, q)), q); } -int32_t fix_Round(int32_t i) +int32_t fix_Ceil(int32_t i, int32_t q) { - return double2fix(round(fix2double(i))); + return double2fix(ceil(fix2double(i, q)), q); } -int32_t fix_Ceil(int32_t i) +int32_t fix_Floor(int32_t i, int32_t q) { - return double2fix(ceil(fix2double(i))); -} - -int32_t fix_Floor(int32_t i) -{ - return double2fix(floor(fix2double(i))); + return double2fix(floor(fix2double(i, q)), q); } diff --git a/src/asm/parser.y b/src/asm/parser.y index 56aea0da..ee9bb9de 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -550,6 +550,7 @@ enum { %token T_OP_BANK "BANK" %token T_OP_ALIGN "ALIGN" %token T_OP_SIZEOF "SIZEOF" T_OP_STARTOF "STARTOF" + %token T_OP_SIN "SIN" T_OP_COS "COS" T_OP_TAN "TAN" %token T_OP_ASIN "ASIN" T_OP_ACOS "ACOS" T_OP_ATAN "ATAN" T_OP_ATAN2 "ATAN2" %token T_OP_FDIV "FDIV" @@ -559,6 +560,7 @@ enum { %token T_OP_LOG "LOG" %token T_OP_ROUND "ROUND" %token T_OP_CEIL "CEIL" T_OP_FLOOR "FLOOR" +%type opt_q_arg %token T_OP_HIGH "HIGH" T_OP_LOW "LOW" %token T_OP_ISCONST "ISCONST" @@ -1466,50 +1468,50 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); } lexer_ToggleStringExpansion(true); } - | T_OP_ROUND T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_Round($3)); + | T_OP_ROUND T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Round($3, $4)); } - | T_OP_CEIL T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_Ceil($3)); + | T_OP_CEIL T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Ceil($3, $4)); } - | T_OP_FLOOR T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_Floor($3)); + | T_OP_FLOOR T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Floor($3, $4)); } - | T_OP_FDIV T_LPAREN const T_COMMA const T_RPAREN { - rpn_Number(&$$, fix_Div($3, $5)); + | T_OP_FDIV T_LPAREN const T_COMMA const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Div($3, $5, $6)); } - | T_OP_FMUL T_LPAREN const T_COMMA const T_RPAREN { - rpn_Number(&$$, fix_Mul($3, $5)); + | T_OP_FMUL T_LPAREN const T_COMMA const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Mul($3, $5, $6)); } - | T_OP_FMOD T_LPAREN const T_COMMA const T_RPAREN { - rpn_Number(&$$, fix_Mod($3, $5)); + | T_OP_FMOD T_LPAREN const T_COMMA const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Mod($3, $5, $6)); } - | T_OP_POW T_LPAREN const T_COMMA const T_RPAREN { - rpn_Number(&$$, fix_Pow($3, $5)); + | T_OP_POW T_LPAREN const T_COMMA const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Pow($3, $5, $6)); } - | T_OP_LOG T_LPAREN const T_COMMA const T_RPAREN { - rpn_Number(&$$, fix_Log($3, $5)); + | T_OP_LOG T_LPAREN const T_COMMA const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Log($3, $5, $6)); } - | T_OP_SIN T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_Sin($3)); + | T_OP_SIN T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Sin($3, $4)); } - | T_OP_COS T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_Cos($3)); + | T_OP_COS T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Cos($3, $4)); } - | T_OP_TAN T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_Tan($3)); + | T_OP_TAN T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_Tan($3, $4)); } - | T_OP_ASIN T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_ASin($3)); + | T_OP_ASIN T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_ASin($3, $4)); } - | T_OP_ACOS T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_ACos($3)); + | T_OP_ACOS T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_ACos($3, $4)); } - | T_OP_ATAN T_LPAREN const T_RPAREN { - rpn_Number(&$$, fix_ATan($3)); + | T_OP_ATAN T_LPAREN const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_ATan($3, $4)); } - | T_OP_ATAN2 T_LPAREN const T_COMMA const T_RPAREN { - rpn_Number(&$$, fix_ATan2($3, $5)); + | T_OP_ATAN2 T_LPAREN const T_COMMA const opt_q_arg T_RPAREN { + rpn_Number(&$$, fix_ATan2($3, $5, $6)); } | T_OP_STRCMP T_LPAREN string T_COMMA string T_RPAREN { rpn_Number(&$$, strcmp($3, $5)); @@ -1536,7 +1538,7 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); } uconst : const { $$ = $1; if ($$ < 0) - fatalerror("Constant mustn't be negative: %d\n", $1); + fatalerror("Constant must not be negative: %d\n", $1); } ; @@ -1549,6 +1551,17 @@ const_no_str : relocexpr_no_str { $$ = rpn_GetConstVal(&$1); } const_8bit : reloc_8bit { $$ = rpn_GetConstVal(&$1); } ; +opt_q_arg : %empty { $$ = fix_Precision(); } + | T_COMMA const { + if ($2 >= 1 && $2 <= 31) { + $$ = $2; + } else { + error("Fixed-point precision must be between 1 and 31\n"); + $$ = fix_Precision(); + } + } +; + string : T_STRING | T_OP_STRSUB T_LPAREN string T_COMMA const T_COMMA uconst T_RPAREN { size_t len = strlenUTF8($3); diff --git a/test/asm/fixed-point-specific.asm b/test/asm/fixed-point-specific.asm new file mode 100644 index 00000000..a0ef810f --- /dev/null +++ b/test/asm/fixed-point-specific.asm @@ -0,0 +1,27 @@ +MACRO compare + print "\3: " + if _NARG == 4 + def v1 = \3(\4q\1, \1) + def v2 = \3(\4q\2, \2) + elif _NARG == 5 + def v1 = \3(\4q\1, \5q\1, \1) + def v2 = \3(\4q\2, \5q\2, \2) + endc + opt Q\1 + print "{.4f:v1} == " + opt Q\2 + println "{.4f:v2}" +ENDM + + compare 8, 16, mul, 6.0, 7.0 + compare 12, 24, div, 115.625, 9.25 + compare 7, 14, pow, 3.5, 5.5 + + compare 4, 8, sin, 0.25 + compare 5, 9, cos, 0.75 + compare 6, 10, asin, 1.0 + compare 7, 11, acos, 0.0 + + compare 3, 6, round, 1.75 + compare 10, 20, ceil, 123.4 + compare 13, 17, floor, 567.8 diff --git a/test/asm/fixed-point-specific.err b/test/asm/fixed-point-specific.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/fixed-point-specific.out b/test/asm/fixed-point-specific.out new file mode 100644 index 00000000..8b4773a0 --- /dev/null +++ b/test/asm/fixed-point-specific.out @@ -0,0 +1,10 @@ +mul: 42.0000 == 42.0000 +div: 12.5000 == 12.5000 +pow: 982.5938 == 982.5943 +sin: 1.0000 == 1.0000 +cos: 0.0000 == 0.0000 +asin: 0.2500 == 0.2500 +acos: 0.2500 == 0.2500 +round: 2.0000 == 2.0000 +ceil: 124.0000 == 124.0000 +floor: 567.0000 == 567.0000