shithub: rgbds

Download patch

ref: 023884d2b02ea11670031c8a0cf0c22f4b6bc911
parent: 3567faf395cdc93d859692eb3c0421b0cdcf702d
author: Rangi <35663410+Rangi42@users.noreply.github.com>
date: Thu Sep 29 00:57:29 EDT 2022

Redefine the trig functions to divide circles into 1.0 turns (#1060)

This makes their behavior consistent across Q settings

Fixes #1059

--- a/man/rgbasm.5
+++ b/man/rgbasm.5
@@ -57,8 +57,8 @@
 .Ql */ .
 It can be split across multiple lines, or occur in the middle of an expression:
 .Bd -literal -offset indent
-X = /* the value of x
-       should be 3 */ 3
+DEF X = /* the value of x
+           should be 3 */ 3
 .Ed
 .Pp
 Sometimes lines can be too long and it may be necessary to split them.
@@ -168,20 +168,20 @@
 Examples:
 .Bd -literal -offset indent
 SECTION "Test", ROM0[2]
-X:             ;\ This works with labels **whose address is known**
-Y = 3          ;\ This also works with variables
-SUM equ X + Y  ;\ And likewise with numeric constants
+X:                 ;\ This works with labels **whose address is known**
+DEF Y = 3          ;\ This also works with variables
+DEF SUM EQU X + Y  ;\ And likewise with numeric constants
 ; Prints "%0010 + $3 == 5"
 PRINTLN "{#05b:X} + {#x:Y} == {d:SUM}"
 
 rsset 32
-PERCENT rb 1   ;\ Same with offset constants
-VALUE = 20
-RESULT = MUL(20.0, 0.32)
+DEF PERCENT rb 1   ;\ Same with offset constants
+DEF VALUE = 20
+DEF RESULT = MUL(20.0, 0.32)
 ; Prints "32% of 20 = 6.40"
 PRINTLN "{d:PERCENT}% of {d:VALUE} = {f:RESULT}"
 
-WHO equs STRLWR("WORLD")
+DEF WHO EQUS STRLWR("WORLD")
 ; Prints "Hello world!"
 PRINTLN "Hello {s:WHO}!"
 .Ed
@@ -350,18 +350,18 @@
 .Ic SIN ,
 .Ic COS ,
 .Ic TAN ,
-etc) are defined in terms of a circle divided into 65535.0 degrees.
+etc) are defined in terms of a circle divided into 1.0 "turns" (equal to 2pi radians or 360 degrees).
 .Pp
 These functions are useful for automatic generation of various tables.
 For example:
 .Bd -literal -offset indent
-; Generate a 256-byte sine table with values in the range [0, 128]
-; (shifted and scaled from the range [-1.0, 1.0])
-ANGLE = 0.0
-    REPT 256
-        db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
-ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
-    ENDR
+; Generate a table of sine values from sin(0.0) to sin(1.0), with
+; amplitude scaled from [-1.0, 1.0] to [0.0, 128.0]
+DEF turns = 0.0
+REPT 256
+    db MUL(64.0, SIN(turns) + 1.0) >> 16
+    DEF turns += 1.0 / 256
+ENDR
 .Ed
 .Ss String expressions
 The most basic string expression is any number of characters contained in double quotes
@@ -1011,7 +1011,7 @@
 DEF COUNT = 2
 DEF COUNT = 3
 DEF COUNT = ARRAY_SIZE + COUNT
-COUNT = COUNT*2
+DEF COUNT *= 2
 ;\ COUNT now has the value 14
 .Ed
 .Pp
@@ -1753,13 +1753,12 @@
 .Ic REPT
 to generate tables on the fly:
 .Bd -literal -offset indent
-; Generate a 256-byte sine table with values in the range [0, 128]
-; (shifted and scaled from the range [-1.0, 1.0])
-ANGLE = 0.0
-    REPT 256
-        db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
-ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
-    ENDR
+; Generate a table of square values from 0**2 = 0 to 100**2 = 10000
+DEF x = 0
+REPT 101
+    dw x * x
+    DEF x += 1
+ENDR
 .Ed
 .Pp
 As in macros, you can also use the escape sequence
--- a/src/asm/fixpoint.c
+++ b/src/asm/fixpoint.c
@@ -24,9 +24,9 @@
 #define fix2double(i)	((double)((i) / fix_PrecisionFactor()))
 #define double2fix(d)	((int32_t)round((d) * fix_PrecisionFactor()))
 
-// pi*2 radians == 2**fixPrecision fixed-point "degrees"
-#define fdeg2rad(f)	((f) * (M_PI * 2) / fix_PrecisionFactor())
-#define rad2fdeg(r)	((r) * fix_PrecisionFactor() / (M_PI * 2))
+// 2*pi radians == 1 turn
+#define turn2rad(f)	((f) * (M_PI * 2))
+#define rad2turn(r)	((r) / (M_PI * 2))
 
 uint8_t fixPrecision;
 
@@ -51,37 +51,37 @@
 
 int32_t fix_Sin(int32_t i)
 {
-	return double2fix(sin(fdeg2rad(fix2double(i))));
+	return double2fix(sin(turn2rad(fix2double(i))));
 }
 
 int32_t fix_Cos(int32_t i)
 {
-	return double2fix(cos(fdeg2rad(fix2double(i))));
+	return double2fix(cos(turn2rad(fix2double(i))));
 }
 
 int32_t fix_Tan(int32_t i)
 {
-	return double2fix(tan(fdeg2rad(fix2double(i))));
+	return double2fix(tan(turn2rad(fix2double(i))));
 }
 
 int32_t fix_ASin(int32_t i)
 {
-	return double2fix(rad2fdeg(asin(fix2double(i))));
+	return double2fix(rad2turn(asin(fix2double(i))));
 }
 
 int32_t fix_ACos(int32_t i)
 {
-	return double2fix(rad2fdeg(acos(fix2double(i))));
+	return double2fix(rad2turn(acos(fix2double(i))));
 }
 
 int32_t fix_ATan(int32_t i)
 {
-	return double2fix(rad2fdeg(atan(fix2double(i))));
+	return double2fix(rad2turn(atan(fix2double(i))));
 }
 
 int32_t fix_ATan2(int32_t i, int32_t j)
 {
-	return double2fix(rad2fdeg(atan2(fix2double(i), fix2double(j))));
+	return double2fix(rad2turn(atan2(fix2double(i), fix2double(j))));
 }
 
 int32_t fix_Mul(int32_t i, int32_t j)
--- /dev/null
+++ b/test/asm/trigonometry.asm
@@ -1,0 +1,38 @@
+for Q, 2, 31
+	OPT Q.{d:Q}
+
+	assert sin(0.25) == 1.0
+	assert asin(1.0) == 0.25
+
+	assert sin(0.0) == 0.0
+	assert asin(0.0) == 0.0
+
+	assert cos(0.0) == 1.0
+	assert acos(1.0) == 0.0
+
+	if Q > 2 ; can't represent 0.125 in Q.2
+		assert tan(0.125) == 1.0
+		assert atan(1.0) == 0.125
+	else
+		assert tan(0.0) == 0.0
+		assert atan(0.0) == 0.0
+	endc
+endr
+
+SECTION "sine", ROM0[0]
+OPT Q.16
+; Generate a table of sine values from sin(0.0) to sin(1.0), with
+; amplitude scaled from [-1.0, 1.0] to [0.0, 128.0]
+DEF turns = 0.0
+REPT 256
+    db MUL(64.0, SIN(turns) + 1.0) >> 16
+    DEF turns += 1.0 / 256
+ENDR
+
+
+SECTION "cosine", ROM0[256]
+OPT Q.8
+; 32 samples of cos(x) from x=0 to x=pi radians=0.5 turns
+for x, 0.0, 0.5, 0.5 / 32
+    dw cos(x)
+endr
binary files /dev/null b/test/asm/trigonometry.out.bin differ