123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- /*
- * Fixed-Point number library
- * Contains functions for decimal numbers encoded in 16.16 format
- */
- #define fixed_point_t char
- // Fixed point arithmetic operations using 16.16 format
- #define FP_FRACT_BITS 16
- #define FP_MULTIPLY_CONST 15259
- #define FP_FRACT_MASK 0xFFFF
- // Convert a fixed-point variable to a string with a given number of decimals (truncated, not rounded)
- void FP_FPtoString(word fixedPointValue, char* outputString, word numDecimals) {
- word fractMask = FP_FRACT_MASK; // Needed to enforce a load(32) instruction, as constants have limited bits in arith operations
- word integerPart = fixedPointValue >> FP_FRACT_BITS; // Extract the integer part
- word fractionalPart = fixedPointValue & fractMask; // Extract the fractional part
- // Convert the integer part, which can be signed, to a string using itoa()
- if (integerPart < 0)
- {
- outputString[0] = '-';
- if (fractionalPart > 0)
- {
- itoa(- (integerPart + 1), &outputString[1]); // integerPart-1 to fix neg numbers
- }
- else
- {
- itoa(- integerPart, &outputString[1]);
- }
- }
- else
- {
- itoa(integerPart, outputString);
- }
- // Find the end of the integer part string
- char* endOfString = outputString;
- while (*endOfString)
- {
- endOfString++;
- }
- // Add a dot to separate the integer and fractional parts
- *endOfString = '.';
- endOfString++;
- char* decimalPosition = endOfString;
- char decbuf[10]; // 10 with current multiply constant, as number can be 9 decimal max
- // Convert the fractional part to intermediate buffer
- if (integerPart < 0 && fractionalPart > 0)
- {
- // Fix for neg numbers (2's complement)
- fractionalPart = ( (~fractionalPart) & fractMask) + 1;
- }
- fractionalPart *= FP_MULTIPLY_CONST;
- itoa(fractionalPart, decbuf);
- // Get length of decimal part string
- int declen = strlen(decbuf);
- // Prepend with 9-(length of decimal part) zeros before adding decimal part
- while (declen < 9)
- {
- *endOfString = '0';
- endOfString++;
- declen++;
- }
- // Terminate string
- *endOfString = 0;
- // Convert and append the fractional part directly to the result
- strcat(endOfString, decbuf);
- // Truncate at number of decimals given by placing string terminator
- decimalPosition[numDecimals] = 0;
- }
- // Convert a string to a fixed-point number
- fixed_point_t FP_StringToFP(char* decimalString)
- {
- // Get index of the dot, also return int part if no dot
- char* dotp = strchr(decimalString, '.');
- if (dotp == 0) return strToInt(decimalString) << FP_FRACT_BITS;
- word dotIndex = dotp - decimalString;
- // Copy integer part
- char integerPart[12];
- memcpy(integerPart, decimalString, dotIndex);
- // Terminate integerPart
- integerPart[dotIndex] = 0;
- // Copy decimal part
- char decimalPart[12];
- strcpy(decimalPart, decimalString + dotIndex + 1);
- // Add integer part to result
- word result = strToInt(integerPart) << FP_FRACT_BITS;
- // Get decimal part as an integer
- word decimalPartInt = strToInt(decimalPart);
- // Return if no or invalid decimal part
- if (decimalPartInt == 0)
- return result;
- // Get number of decimals of the decimal part string which we need later
- word lenDecimalPart = strlen(decimalPart);
- // Alternative for 10^lenDecimalPart
- word divisionNumber = 1;
- while (lenDecimalPart > 0)
- {
- divisionNumber *= 10;
- lenDecimalPart--;
- }
-
- // Calculate decimal part
- word decResult = decimalPartInt << FP_FRACT_BITS;
- decResult = MATH_div(decResult, divisionNumber);
- // Correct for negative numbers (look at string because -0.x)
- if (decimalString[0] == '-')
- {
- result -= (1 << FP_FRACT_BITS);
- decResult = (1 << FP_FRACT_BITS) - decResult;
- }
- // Add decimal part to integer part
- word fractMask = FP_FRACT_MASK; // Needed to enforce a load(32) instruction, as constants have limited bits in arith operations
- result |= (decResult & fractMask);
- return result;
- }
- // Convert an integer to a fixed-point number
- fixed_point_t FP_intToFP(word x)
- {
- return x << FP_FRACT_BITS;
- }
- // Convert a fixed-point number to an integer (truncate)
- word FP_FPtoInt(fixed_point_t x)
- {
- return x >> FP_FRACT_BITS;
- }
- // Multiply two fixed-point numbers using special instruction
- fixed_point_t FP_Mult(fixed_point_t a, fixed_point_t b)
- {
- // r4: a, r5: b
- fixed_point_t retval = 0;
- asm(
- "multfp r4 r5 r2 ; r2 = a*b (FP signed)\n"
- "write -4 r14 r2 ; write result to stack for return\n"
- );
- return retval;
- }
- // Divide two fixed-point numbers using MU (a/b)
- fixed_point_t FP_Div(fixed_point_t a, fixed_point_t b)
- {
- // r4: a, r5: b
- fixed_point_t retval = 0;
- asm(
- "load32 0xC02742 r2 ; r2 = addr fpdiv_writea\n"
- "write 0 r2 r4 ; write a to divider\n"
- "write 1 r2 r5 ; write b to divider and perform division\n"
- "read 1 r2 r2 ; read result to r2\n"
- "write -4 r14 r2 ; write result to stack for return\n"
- );
- return retval;
- }
|