mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement opt Q for fixed-point precision, and q literals (e.g. 12.34q8) (#958)
Fixes #957 Co-authored-by: ISSOtm <eldredhabert0@gmail.com>
This commit is contained in:
@@ -39,6 +39,7 @@ _rgbasm_completions() {
|
|||||||
[M]="dependfile:glob-*.mk *.d"
|
[M]="dependfile:glob-*.mk *.d"
|
||||||
[o]="output:glob-*.o"
|
[o]="output:glob-*.o"
|
||||||
[p]="pad-value:unk"
|
[p]="pad-value:unk"
|
||||||
|
[Q]="q-precision:unk"
|
||||||
[r]="recursion-depth:unk"
|
[r]="recursion-depth:unk"
|
||||||
[W]="warning:warning"
|
[W]="warning:warning"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ local args=(
|
|||||||
'*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
'*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
||||||
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
||||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||||
|
'(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:'
|
||||||
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
||||||
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern uint8_t fixPrecision;
|
||||||
|
|
||||||
|
double fix_PrecisionFactor(void);
|
||||||
void fix_Print(int32_t i);
|
void fix_Print(int32_t i);
|
||||||
int32_t fix_Sin(int32_t i);
|
int32_t fix_Sin(int32_t i);
|
||||||
int32_t fix_Cos(int32_t i);
|
int32_t fix_Cos(int32_t i);
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
|
|
||||||
void opt_B(char const chars[2]);
|
void opt_B(char const chars[2]);
|
||||||
void opt_G(char const chars[4]);
|
void opt_G(char const chars[4]);
|
||||||
void opt_P(uint8_t fill);
|
void opt_P(uint8_t padByte);
|
||||||
|
void opt_Q(uint8_t precision);
|
||||||
void opt_L(bool optimize);
|
void opt_L(bool optimize);
|
||||||
void opt_W(char const *flag);
|
void opt_W(char const *flag);
|
||||||
void opt_Parse(char const *option);
|
void opt_Parse(char const *option);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
.Op Fl MQ Ar target_file
|
.Op Fl MQ Ar target_file
|
||||||
.Op Fl o Ar out_file
|
.Op Fl o Ar out_file
|
||||||
.Op Fl p Ar pad_value
|
.Op Fl p Ar pad_value
|
||||||
|
.Op Fl Q Ar fix_precision
|
||||||
.Op Fl r Ar recursion_depth
|
.Op Fl r Ar recursion_depth
|
||||||
.Op Fl W Ar warning
|
.Op Fl W Ar warning
|
||||||
.Ar
|
.Ar
|
||||||
@@ -148,6 +149,13 @@ Write an object file to the given filename.
|
|||||||
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
|
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
|
||||||
When padding an image, pad with this value.
|
When padding an image, pad with this value.
|
||||||
The default is 0x00.
|
The default is 0x00.
|
||||||
|
.It Fl Q Ar fix_precision , Fl Fl q-precision Ar fix_precision
|
||||||
|
Use this as the precision of fixed-point numbers after the decimal point, unless they specify their own precision.
|
||||||
|
The default is 16, so fixed-point numbers are Q16.16 (since they are 32-bit integers).
|
||||||
|
The argument may start with a
|
||||||
|
.Ql \&.
|
||||||
|
to match the Q notation, for example,
|
||||||
|
.Ql Fl Q Ar .16 .
|
||||||
.It Fl r Ar recursion_depth , Fl Fl recursion-depth Ar recursion_depth
|
.It Fl r Ar recursion_depth , Fl Fl recursion-depth Ar recursion_depth
|
||||||
Specifies the recursion depth past which RGBASM will assume being in an infinite loop.
|
Specifies the recursion depth past which RGBASM will assume being in an infinite loop.
|
||||||
The default is 64.
|
The default is 64.
|
||||||
|
|||||||
29
man/rgbasm.5
29
man/rgbasm.5
@@ -208,13 +208,14 @@ section.
|
|||||||
The instructions in the macro-language generally require constant expressions.
|
The instructions in the macro-language generally require constant expressions.
|
||||||
.Ss Numeric formats
|
.Ss Numeric formats
|
||||||
There are a number of numeric formats.
|
There are a number of numeric formats.
|
||||||
.Bl -column -offset indent "Fixed point (Q16.16)" "Prefix"
|
.Bl -column -offset indent "Precise fixed-point" "Prefix"
|
||||||
.It Sy Format type Ta Sy Prefix Ta Sy Accepted characters
|
.It Sy Format type Ta Sy Prefix Ta Sy Accepted characters
|
||||||
.It Hexadecimal Ta $ Ta 0123456789ABCDEF
|
.It Hexadecimal Ta $ Ta 0123456789ABCDEF
|
||||||
.It Decimal Ta none Ta 0123456789
|
.It Decimal Ta none Ta 0123456789
|
||||||
.It Octal Ta & Ta 01234567
|
.It Octal Ta & Ta 01234567
|
||||||
.It Binary Ta % Ta 01
|
.It Binary Ta % Ta 01
|
||||||
.It Fixed point (Q16.16) Ta none Ta 01234.56789
|
.It Fixed-point Ta none Ta 01234.56789
|
||||||
|
.It Precise fixed-point Ta none Ta 12.34q8
|
||||||
.It Character constant Ta none Ta \(dqABYZ\(dq
|
.It Character constant Ta none Ta \(dqABYZ\(dq
|
||||||
.It Gameboy graphics Ta \` Ta 0123
|
.It Gameboy graphics Ta \` Ta 0123
|
||||||
.El
|
.El
|
||||||
@@ -301,9 +302,19 @@ and
|
|||||||
.Ic \&!
|
.Ic \&!
|
||||||
returns 1 if the operand was 0, and 0 otherwise.
|
returns 1 if the operand was 0, and 0 otherwise.
|
||||||
.Ss Fixed-point expressions
|
.Ss Fixed-point expressions
|
||||||
Fixed-point numbers are basically normal (32-bit) integers, which count 65536ths instead of entire units, offering better precision than integers but limiting the range of values.
|
Fixed-point numbers are basically normal (32-bit) integers, which count fractions instead of whole numbers.
|
||||||
The upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths).
|
They offer better precision than integers but limit the range of values.
|
||||||
Since they are still akin to integers, you can use them in normal integer expressions, and some integer operators like
|
By default, the upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths).
|
||||||
|
The default number of fractional bits can be changed with the
|
||||||
|
.Fl Q
|
||||||
|
command-line option.
|
||||||
|
You can also specify a precise fixed-point value by appending a
|
||||||
|
.Dq q
|
||||||
|
to it followed by the number of fractional bits, such as
|
||||||
|
.Ql 12.34q8 .
|
||||||
|
.Pp
|
||||||
|
Since fixed-point values are still just integers, you can use them in normal integer expressions.
|
||||||
|
Some integer operators like
|
||||||
.Sq +
|
.Sq +
|
||||||
and
|
and
|
||||||
.Sq -
|
.Sq -
|
||||||
@@ -317,9 +328,9 @@ delim $$
|
|||||||
.EN
|
.EN
|
||||||
.Bl -column -offset indent "ATAN2(x, y)"
|
.Bl -column -offset indent "ATAN2(x, y)"
|
||||||
.It Sy Name Ta Sy Operation
|
.It Sy Name Ta Sy Operation
|
||||||
.It Fn DIV x y Ta $x \[di] y$
|
.It Fn DIV x y Ta Fixed-point division $( x \[di] y ) \[mu] ( 2 ^ precision )$
|
||||||
.It Fn MUL x y Ta $x \[mu] y$
|
.It Fn MUL x y Ta Fixed-point multiplication $( x \[mu] y ) \[di] ( 2 ^ precision )$
|
||||||
.It Fn FMOD x y Ta $x % y$
|
.It Fn FMOD x y Ta Fixed-point modulo $( x % y ) \[di] ( 2 ^ precision )$
|
||||||
.It Fn POW x y Ta $x$ to the $y$ power
|
.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 LOG x y Ta Logarithm of $x$ to the base $y$
|
||||||
.It Fn ROUND x Ta Round $x$ to the nearest integer
|
.It Fn ROUND x Ta Round $x$ to the nearest integer
|
||||||
@@ -2034,7 +2045,7 @@ POPO
|
|||||||
The options that
|
The options that
|
||||||
.Ic OPT
|
.Ic OPT
|
||||||
can modify are currently:
|
can modify are currently:
|
||||||
.Cm b , g , p , r , h , L ,
|
.Cm b , g , p , Q , r , h , L ,
|
||||||
and
|
and
|
||||||
.Cm W .
|
.Cm W .
|
||||||
The Boolean flag options
|
The Boolean flag options
|
||||||
|
|||||||
@@ -17,17 +17,24 @@
|
|||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#define fix2double(i) ((double)((i) / 65536.0))
|
|
||||||
#define double2fix(d) ((int32_t)round((d) * 65536.0))
|
|
||||||
|
|
||||||
// pi radians == 32768 fixed-point "degrees"
|
|
||||||
#define fdeg2rad(f) ((f) * (M_PI / 32768.0))
|
|
||||||
#define rad2fdeg(r) ((r) * (32768.0 / M_PI))
|
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#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))
|
||||||
|
|
||||||
|
uint8_t fixPrecision;
|
||||||
|
|
||||||
|
double fix_PrecisionFactor(void)
|
||||||
|
{
|
||||||
|
return pow(2.0, fixPrecision);
|
||||||
|
}
|
||||||
|
|
||||||
void fix_Print(int32_t i)
|
void fix_Print(int32_t i)
|
||||||
{
|
{
|
||||||
uint32_t u = i;
|
uint32_t u = i;
|
||||||
@@ -38,7 +45,7 @@ void fix_Print(int32_t i)
|
|||||||
sign = "-";
|
sign = "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> 16,
|
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> fixPrecision,
|
||||||
((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000);
|
((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "asm/fixpoint.h"
|
||||||
#include "asm/format.h"
|
#include "asm/format.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
@@ -225,7 +226,7 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
} else if (fmt->type == 'f') {
|
} else if (fmt->type == 'f') {
|
||||||
// Special case for fixed-point
|
// Special case for fixed-point
|
||||||
|
|
||||||
// Default fractional width (C's is 6 for "%f"; here 5 is enough)
|
// Default fractional width (C's is 6 for "%f"; here 5 is enough for Q16.16)
|
||||||
size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
||||||
|
|
||||||
if (fracWidth > 255) {
|
if (fracWidth > 255) {
|
||||||
@@ -234,7 +235,8 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
fracWidth = 255;
|
fracWidth = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)fracWidth, value / 65536.0);
|
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)fracWidth,
|
||||||
|
value / fix_PrecisionFactor());
|
||||||
} else {
|
} else {
|
||||||
char const *spec = fmt->type == 'd' ? "%" PRId32
|
char const *spec = fmt->type == 'd' ? "%" PRId32
|
||||||
: fmt->type == 'u' ? "%" PRIu32
|
: fmt->type == 'u' ? "%" PRIu32
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "platform.h" // For `ssize_t`
|
#include "platform.h" // For `ssize_t`
|
||||||
|
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
|
#include "asm/fixpoint.h"
|
||||||
#include "asm/format.h"
|
#include "asm/format.h"
|
||||||
#include "asm/fstack.h"
|
#include "asm/fstack.h"
|
||||||
#include "asm/macro.h"
|
#include "asm/macro.h"
|
||||||
@@ -1125,17 +1126,28 @@ static uint32_t readNumber(int radix, uint32_t baseValue)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t readFractionalPart(int32_t integer)
|
static uint32_t readFractionalPart(uint32_t integer)
|
||||||
{
|
{
|
||||||
uint32_t value = 0, divisor = 1;
|
uint32_t value = 0, divisor = 1;
|
||||||
|
uint8_t precision = 0;
|
||||||
|
enum {
|
||||||
|
READFRACTIONALPART_DIGITS,
|
||||||
|
READFRACTIONALPART_PRECISION,
|
||||||
|
READFRACTIONALPART_PRECISION_DIGITS,
|
||||||
|
} state = READFRACTIONALPART_DIGITS;
|
||||||
|
|
||||||
for (;; shiftChar()) {
|
for (;; shiftChar()) {
|
||||||
int c = peek();
|
int c = peek();
|
||||||
|
|
||||||
if (c == '_')
|
if (state == READFRACTIONALPART_DIGITS) {
|
||||||
|
if (c == '_') {
|
||||||
continue;
|
continue;
|
||||||
else if (c < '0' || c > '9')
|
} else if (c == 'q' || c == 'Q') {
|
||||||
|
state = READFRACTIONALPART_PRECISION;
|
||||||
|
continue;
|
||||||
|
} else if (c < '0' || c > '9') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
if (divisor > (UINT32_MAX - (c - '0')) / 10) {
|
if (divisor > (UINT32_MAX - (c - '0')) / 10) {
|
||||||
warning(WARNING_LARGE_CONSTANT,
|
warning(WARNING_LARGE_CONSTANT,
|
||||||
"Precision of fixed-point constant is too large\n");
|
"Precision of fixed-point constant is too large\n");
|
||||||
@@ -1147,17 +1159,33 @@ static uint32_t readFractionalPart(int32_t integer)
|
|||||||
}
|
}
|
||||||
value = value * 10 + (c - '0');
|
value = value * 10 + (c - '0');
|
||||||
divisor *= 10;
|
divisor *= 10;
|
||||||
|
} else {
|
||||||
|
if (c == '.' && state == READFRACTIONALPART_PRECISION) {
|
||||||
|
state = READFRACTIONALPART_PRECISION_DIGITS;
|
||||||
|
continue;
|
||||||
|
} else if (c < '0' || c > '9') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
precision = precision * 10 + (c - '0');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (integer > INT16_MAX || integer < INT16_MIN)
|
if (precision == 0) {
|
||||||
|
if (state >= READFRACTIONALPART_PRECISION)
|
||||||
|
error("Invalid fixed-point constant, no significant digits after 'q'\n");
|
||||||
|
precision = fixPrecision;
|
||||||
|
} else if (precision > 31) {
|
||||||
|
error("Fixed-point constant precision must be between 1 and 31\n");
|
||||||
|
precision = fixPrecision;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integer >= ((uint32_t)1 << (precision - 1)))
|
||||||
warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large\n");
|
warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large\n");
|
||||||
|
|
||||||
// Cast to unsigned avoids UB if shifting discards bits
|
|
||||||
integer = (uint32_t)integer << 16;
|
|
||||||
// Cast to unsigned avoids undefined overflow behavior
|
// Cast to unsigned avoids undefined overflow behavior
|
||||||
uint16_t fractional = (uint16_t)round(value * 65536.0 / divisor);
|
uint32_t fractional = (uint32_t)round((double)value / divisor * pow(2.0, precision));
|
||||||
|
|
||||||
return (uint32_t)integer | (fractional * (integer >= 0 ? 1 : -1));
|
return (integer << precision) | fractional;
|
||||||
}
|
}
|
||||||
|
|
||||||
char binDigits[2];
|
char binDigits[2];
|
||||||
@@ -1741,6 +1769,8 @@ static int yylex_SKIP_TO_ENDC(void); // forward declaration for yylex_NORMAL
|
|||||||
|
|
||||||
static int yylex_NORMAL(void)
|
static int yylex_NORMAL(void)
|
||||||
{
|
{
|
||||||
|
uint32_t num = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int c = nextChar();
|
int c = nextChar();
|
||||||
char secondChar;
|
char secondChar;
|
||||||
@@ -1912,10 +1942,12 @@ static int yylex_NORMAL(void)
|
|||||||
case '7':
|
case '7':
|
||||||
case '8':
|
case '8':
|
||||||
case '9':
|
case '9':
|
||||||
yylval.constValue = readNumber(10, c - '0');
|
num = readNumber(10, c - '0');
|
||||||
if (peek() == '.') {
|
if (peek() == '.') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
yylval.constValue = readFractionalPart(yylval.constValue);
|
yylval.constValue = readFractionalPart(num);
|
||||||
|
} else {
|
||||||
|
yylval.constValue = num;
|
||||||
}
|
}
|
||||||
return T_NUMBER;
|
return T_NUMBER;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "asm/charmap.h"
|
#include "asm/charmap.h"
|
||||||
|
#include "asm/fixpoint.h"
|
||||||
#include "asm/format.h"
|
#include "asm/format.h"
|
||||||
#include "asm/fstack.h"
|
#include "asm/fstack.h"
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
@@ -86,7 +87,7 @@ static char *make_escape(char const *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Short options
|
// Short options
|
||||||
static const char *optstring = "b:D:Eg:Hhi:LlM:o:p:r:VvW:w";
|
static const char *optstring = "b:D:Eg:Hhi:LlM:o:p:Q:r:VvW:w";
|
||||||
|
|
||||||
// Variables for the long-only options
|
// Variables for the long-only options
|
||||||
static int depType; // Variants of `-M`
|
static int depType; // Variants of `-M`
|
||||||
@@ -116,6 +117,7 @@ static struct option const longopts[] = {
|
|||||||
{ "MQ", required_argument, &depType, 'Q' },
|
{ "MQ", required_argument, &depType, 'Q' },
|
||||||
{ "output", required_argument, NULL, 'o' },
|
{ "output", required_argument, NULL, 'o' },
|
||||||
{ "pad-value", required_argument, NULL, 'p' },
|
{ "pad-value", required_argument, NULL, 'p' },
|
||||||
|
{ "q-precision", required_argument, NULL, 'Q' },
|
||||||
{ "recursion-depth", required_argument, NULL, 'r' },
|
{ "recursion-depth", required_argument, NULL, 'r' },
|
||||||
{ "version", no_argument, NULL, 'V' },
|
{ "version", no_argument, NULL, 'V' },
|
||||||
{ "verbose", no_argument, NULL, 'v' },
|
{ "verbose", no_argument, NULL, 'v' },
|
||||||
@@ -128,7 +130,8 @@ static void print_usage(void)
|
|||||||
fputs(
|
fputs(
|
||||||
"Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n"
|
"Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n"
|
||||||
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||||
" [-o out_file] [-p pad_value] [-r depth] [-W warning] <file>\n"
|
" [-o out_file] [-p pad_value] [-Q precision] [-r depth]\n"
|
||||||
|
" [-W warning] <file>\n"
|
||||||
"Useful options:\n"
|
"Useful options:\n"
|
||||||
" -E, --export-all export all labels\n"
|
" -E, --export-all export all labels\n"
|
||||||
" -M, --dependfile <path> set the output dependency file\n"
|
" -M, --dependfile <path> set the output dependency file\n"
|
||||||
@@ -170,6 +173,7 @@ int main(int argc, char *argv[])
|
|||||||
opt_B("01");
|
opt_B("01");
|
||||||
opt_G("0123");
|
opt_G("0123");
|
||||||
opt_P(0);
|
opt_P(0);
|
||||||
|
opt_Q(16);
|
||||||
haltnop = true;
|
haltnop = true;
|
||||||
warnOnHaltNop = true;
|
warnOnHaltNop = true;
|
||||||
optimizeLoads = true;
|
optimizeLoads = true;
|
||||||
@@ -250,17 +254,34 @@ int main(int argc, char *argv[])
|
|||||||
out_SetFileName(musl_optarg);
|
out_SetFileName(musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
unsigned long fill;
|
unsigned long padByte;
|
||||||
case 'p':
|
case 'p':
|
||||||
fill = strtoul(musl_optarg, &ep, 0);
|
padByte = strtoul(musl_optarg, &ep, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *ep != '\0')
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
errx("Invalid argument for option 'p'");
|
errx("Invalid argument for option 'p'");
|
||||||
|
|
||||||
if (fill > 0xFF)
|
if (padByte > 0xFF)
|
||||||
errx("Argument for option 'p' must be between 0 and 0xFF");
|
errx("Argument for option 'p' must be between 0 and 0xFF");
|
||||||
|
|
||||||
opt_P(fill);
|
opt_P(padByte);
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned long precision;
|
||||||
|
const char *precisionArg;
|
||||||
|
case 'Q':
|
||||||
|
precisionArg = musl_optarg;
|
||||||
|
if (precisionArg[0] == '.')
|
||||||
|
precisionArg++;
|
||||||
|
precision = strtoul(precisionArg, &ep, 0);
|
||||||
|
|
||||||
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
|
errx("Invalid argument for option 'Q'");
|
||||||
|
|
||||||
|
if (precision < 1 || precision > 31)
|
||||||
|
errx("Argument for option 'Q' must be between 1 and 31");
|
||||||
|
|
||||||
|
opt_Q(precision);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'r':
|
case 'r':
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "asm/fixpoint.h"
|
||||||
#include "asm/fstack.h"
|
#include "asm/fstack.h"
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
@@ -23,7 +24,8 @@
|
|||||||
struct OptStackEntry {
|
struct OptStackEntry {
|
||||||
char binary[2];
|
char binary[2];
|
||||||
char gbgfx[4];
|
char gbgfx[4];
|
||||||
int32_t fillByte;
|
uint8_t fixPrecision;
|
||||||
|
uint8_t fillByte;
|
||||||
bool haltnop;
|
bool haltnop;
|
||||||
bool warnOnHaltNop;
|
bool warnOnHaltNop;
|
||||||
bool optimizeLoads;
|
bool optimizeLoads;
|
||||||
@@ -47,9 +49,14 @@ void opt_G(char const chars[4])
|
|||||||
lexer_SetGfxDigits(chars);
|
lexer_SetGfxDigits(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
void opt_P(uint8_t fill)
|
void opt_P(uint8_t padByte)
|
||||||
{
|
{
|
||||||
fillByte = fill;
|
fillByte = padByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_Q(uint8_t precision)
|
||||||
|
{
|
||||||
|
fixPrecision = precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
void opt_R(size_t newDepth)
|
void opt_R(size_t newDepth)
|
||||||
@@ -103,18 +110,41 @@ void opt_Parse(char *s)
|
|||||||
case 'p':
|
case 'p':
|
||||||
if (strlen(&s[1]) <= 2) {
|
if (strlen(&s[1]) <= 2) {
|
||||||
int result;
|
int result;
|
||||||
unsigned int fillchar;
|
unsigned int padByte;
|
||||||
|
|
||||||
result = sscanf(&s[1], "%x", &fillchar);
|
result = sscanf(&s[1], "%x", &padByte);
|
||||||
if (result != EOF && result != 1)
|
if (result != 1)
|
||||||
error("Invalid argument for option 'p'\n");
|
error("Invalid argument for option 'p'\n");
|
||||||
|
else if (padByte > 0xFF)
|
||||||
|
error("Argument for option 'p' must be between 0 and 0xFF\n");
|
||||||
else
|
else
|
||||||
opt_P(fillchar);
|
opt_P(padByte);
|
||||||
} else {
|
} else {
|
||||||
error("Invalid argument for option 'p'\n");
|
error("Invalid argument for option 'p'\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
const char *precisionArg;
|
||||||
|
case 'Q':
|
||||||
|
precisionArg = &s[1];
|
||||||
|
if (precisionArg[0] == '.')
|
||||||
|
precisionArg++;
|
||||||
|
if (strlen(precisionArg) <= 2) {
|
||||||
|
int result;
|
||||||
|
unsigned int precision;
|
||||||
|
|
||||||
|
result = sscanf(precisionArg, "%u", &precision);
|
||||||
|
if (result != 1)
|
||||||
|
error("Invalid argument for option 'Q'\n");
|
||||||
|
else if (precision < 1 || precision > 31)
|
||||||
|
error("Argument for option 'Q' must be between 1 and 31\n");
|
||||||
|
else
|
||||||
|
opt_Q(precision);
|
||||||
|
} else {
|
||||||
|
error("Invalid argument for option 'Q'\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'r': {
|
case 'r': {
|
||||||
++s; // Skip 'r'
|
++s; // Skip 'r'
|
||||||
while (isblank(*s))
|
while (isblank(*s))
|
||||||
@@ -231,6 +261,8 @@ void opt_Push(void)
|
|||||||
entry->gbgfx[2] = gfxDigits[2];
|
entry->gbgfx[2] = gfxDigits[2];
|
||||||
entry->gbgfx[3] = gfxDigits[3];
|
entry->gbgfx[3] = gfxDigits[3];
|
||||||
|
|
||||||
|
entry->fixPrecision = fixPrecision; // Pulled from fixpoint.h
|
||||||
|
|
||||||
entry->fillByte = fillByte; // Pulled from section.h
|
entry->fillByte = fillByte; // Pulled from section.h
|
||||||
|
|
||||||
entry->haltnop = haltnop; // Pulled from main.h
|
entry->haltnop = haltnop; // Pulled from main.h
|
||||||
@@ -259,6 +291,7 @@ void opt_Pop(void)
|
|||||||
opt_B(entry->binary);
|
opt_B(entry->binary);
|
||||||
opt_G(entry->gbgfx);
|
opt_G(entry->gbgfx);
|
||||||
opt_P(entry->fillByte);
|
opt_P(entry->fillByte);
|
||||||
|
opt_Q(entry->fixPrecision);
|
||||||
opt_H(entry->warnOnHaltNop);
|
opt_H(entry->warnOnHaltNop);
|
||||||
opt_h(entry->haltnop);
|
opt_h(entry->haltnop);
|
||||||
opt_L(entry->optimizeLoads);
|
opt_L(entry->optimizeLoads);
|
||||||
|
|||||||
@@ -12,3 +12,11 @@ fl = 6.283185
|
|||||||
|
|
||||||
fr = MUL(20.0, 0.32)
|
fr = MUL(20.0, 0.32)
|
||||||
println "32% of 20 = {f:fr} (~{.2f:fr}) (~~{.0f:fr})"
|
println "32% of 20 = {f:fr} (~{.2f:fr}) (~~{.0f:fr})"
|
||||||
|
|
||||||
|
q8 = 1.25q8
|
||||||
|
q16 = 1.25Q16
|
||||||
|
q24 = 1.25q.24
|
||||||
|
println "Q8 ${x:q8} Q16 ${x:q16} Q24 ${x:q24}"
|
||||||
|
|
||||||
|
qerr = 1.25q32
|
||||||
|
println qerr
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
error: fixed-point-precision.asm(21):
|
||||||
|
Fixed-point constant precision must be between 1 and 31
|
||||||
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
@@ -4,3 +4,5 @@
|
|||||||
`16.12`: 16.119995 -> $00101eb8
|
`16.12`: 16.119995 -> $00101eb8
|
||||||
`6.283185`: 6.283188 -> $0006487f
|
`6.283185`: 6.283188 -> $0006487f
|
||||||
32% of 20 = 6.40015 (~6.40) (~~6)
|
32% of 20 = 6.40015 (~6.40) (~~6)
|
||||||
|
Q8 $140 Q16 $14000 Q24 $1400000
|
||||||
|
$14000
|
||||||
|
|||||||
18
test/asm/opt-Q.asm
Normal file
18
test/asm/opt-Q.asm
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
MACRO test
|
||||||
|
PUSHO
|
||||||
|
OPT Q\1
|
||||||
|
print STRFMT("Q%4s", "\1")
|
||||||
|
def n = 1.14159
|
||||||
|
println STRFMT(" -> %032b", n)
|
||||||
|
POPO
|
||||||
|
ENDM
|
||||||
|
|
||||||
|
for x, 1, 32
|
||||||
|
if x < 16
|
||||||
|
test .{d:x}
|
||||||
|
else
|
||||||
|
test {d:x}
|
||||||
|
endc
|
||||||
|
endr
|
||||||
|
test .0 ; error
|
||||||
|
test 32 ; error
|
||||||
7
test/asm/opt-Q.err
Normal file
7
test/asm/opt-Q.err
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
warning: opt-Q.asm(10) -> opt-Q.asm::REPT~1(12) -> opt-Q.asm::test(5): [-Wlarge-constant]
|
||||||
|
Magnitude of fixed-point constant is too large
|
||||||
|
error: opt-Q.asm(17) -> opt-Q.asm::test(3):
|
||||||
|
Argument for option 'Q' must be between 1 and 31
|
||||||
|
error: opt-Q.asm(18) -> opt-Q.asm::test(3):
|
||||||
|
Argument for option 'Q' must be between 1 and 31
|
||||||
|
error: Assembly aborted (2 errors)!
|
||||||
33
test/asm/opt-Q.out
Normal file
33
test/asm/opt-Q.out
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
Q .1 -> 00000000000000000000000000000010
|
||||||
|
Q .2 -> 00000000000000000000000000000101
|
||||||
|
Q .3 -> 00000000000000000000000000001001
|
||||||
|
Q .4 -> 00000000000000000000000000010010
|
||||||
|
Q .5 -> 00000000000000000000000000100101
|
||||||
|
Q .6 -> 00000000000000000000000001001001
|
||||||
|
Q .7 -> 00000000000000000000000010010010
|
||||||
|
Q .8 -> 00000000000000000000000100100100
|
||||||
|
Q .9 -> 00000000000000000000001001001000
|
||||||
|
Q .10 -> 00000000000000000000010010010001
|
||||||
|
Q .11 -> 00000000000000000000100100100010
|
||||||
|
Q .12 -> 00000000000000000001001001000100
|
||||||
|
Q .13 -> 00000000000000000010010010001000
|
||||||
|
Q .14 -> 00000000000000000100100100010000
|
||||||
|
Q .15 -> 00000000000000001001001000100000
|
||||||
|
Q 16 -> 00000000000000010010010000111111
|
||||||
|
Q 17 -> 00000000000000100100100001111110
|
||||||
|
Q 18 -> 00000000000001001001000011111101
|
||||||
|
Q 19 -> 00000000000010010010000111111010
|
||||||
|
Q 20 -> 00000000000100100100001111110100
|
||||||
|
Q 21 -> 00000000001001001000011111101000
|
||||||
|
Q 22 -> 00000000010010010000111111010000
|
||||||
|
Q 23 -> 00000000100100100001111110011111
|
||||||
|
Q 24 -> 00000001001001000011111100111110
|
||||||
|
Q 25 -> 00000010010010000111111001111100
|
||||||
|
Q 26 -> 00000100100100001111110011111000
|
||||||
|
Q 27 -> 00001001001000011111100111110000
|
||||||
|
Q 28 -> 00010010010000111111001111100000
|
||||||
|
Q 29 -> 00100100100001111110011111000000
|
||||||
|
Q 30 -> 01001001000011111100111110000001
|
||||||
|
Q 31 -> 10010010000111111001111100000010
|
||||||
|
Q .0 -> 00000000000000010010010000111111
|
||||||
|
Q 32 -> 00000000000000010010010000111111
|
||||||
@@ -3,11 +3,13 @@ SECTION "test", ROM0
|
|||||||
opt !h, !L ; already the default, but tests parsing "!"
|
opt !h, !L ; already the default, but tests parsing "!"
|
||||||
|
|
||||||
pusho
|
pusho
|
||||||
opt p42, h, L, Wno-div
|
opt p42, Q.4, h, L, Wno-div
|
||||||
ds 1
|
ds 1
|
||||||
ld [$ff88], a
|
ld [$ff88], a
|
||||||
halt
|
halt
|
||||||
println $8000_0000 / -1
|
println $8000_0000 / -1
|
||||||
|
def n = 3.14
|
||||||
|
println "{x:n} = {f:n}"
|
||||||
popo
|
popo
|
||||||
|
|
||||||
opt H, l
|
opt H, l
|
||||||
@@ -16,3 +18,5 @@ popo
|
|||||||
ld [$ff88], a
|
ld [$ff88], a
|
||||||
halt
|
halt
|
||||||
println $8000_0000 / -1
|
println $8000_0000 / -1
|
||||||
|
def n = 3.14
|
||||||
|
println "{x:n} = {f:n}"
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
warning: opt.asm(18): [-Wdiv]
|
warning: opt.asm(20): [-Wdiv]
|
||||||
Division of -2147483648 by -1 yields -2147483648
|
Division of -2147483648 by -1 yields -2147483648
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
$80000000
|
$80000000
|
||||||
|
32 = 3.12500
|
||||||
$80000000
|
$80000000
|
||||||
|
323d7 = 3.14000
|
||||||
|
|||||||
Reference in New Issue
Block a user