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:
Rangi
2022-09-04 18:47:32 -04:00
committed by GitHub
parent 889302a9e2
commit 98a6dffbca
20 changed files with 253 additions and 56 deletions

View File

@@ -17,17 +17,24 @@
#include "asm/symbol.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
#define M_PI 3.14159265358979323846
#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)
{
uint32_t u = i;
@@ -38,7 +45,7 @@ void fix_Print(int32_t i)
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);
}

View File

@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <string.h>
#include "asm/fixpoint.h"
#include "asm/format.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') {
// 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;
if (fracWidth > 255) {
@@ -234,7 +235,8 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
fracWidth = 255;
}
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)fracWidth, value / 65536.0);
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)fracWidth,
value / fix_PrecisionFactor());
} else {
char const *spec = fmt->type == 'd' ? "%" PRId32
: fmt->type == 'u' ? "%" PRIu32

View File

@@ -27,6 +27,7 @@
#include "platform.h" // For `ssize_t`
#include "asm/lexer.h"
#include "asm/fixpoint.h"
#include "asm/format.h"
#include "asm/fstack.h"
#include "asm/macro.h"
@@ -1125,39 +1126,66 @@ static uint32_t readNumber(int radix, uint32_t baseValue)
return value;
}
static uint32_t readFractionalPart(int32_t integer)
static uint32_t readFractionalPart(uint32_t integer)
{
uint32_t value = 0, divisor = 1;
uint8_t precision = 0;
enum {
READFRACTIONALPART_DIGITS,
READFRACTIONALPART_PRECISION,
READFRACTIONALPART_PRECISION_DIGITS,
} state = READFRACTIONALPART_DIGITS;
for (;; shiftChar()) {
int c = peek();
if (c == '_')
continue;
else if (c < '0' || c > '9')
break;
if (divisor > (UINT32_MAX - (c - '0')) / 10) {
warning(WARNING_LARGE_CONSTANT,
"Precision of fixed-point constant is too large\n");
// Discard any additional digits
shiftChar();
while (c = peek(), (c >= '0' && c <= '9') || c == '_')
if (state == READFRACTIONALPART_DIGITS) {
if (c == '_') {
continue;
} else if (c == 'q' || c == 'Q') {
state = READFRACTIONALPART_PRECISION;
continue;
} else if (c < '0' || c > '9') {
break;
}
if (divisor > (UINT32_MAX - (c - '0')) / 10) {
warning(WARNING_LARGE_CONSTANT,
"Precision of fixed-point constant is too large\n");
// Discard any additional digits
shiftChar();
break;
while (c = peek(), (c >= '0' && c <= '9') || c == '_')
shiftChar();
break;
}
value = value * 10 + (c - '0');
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');
}
value = value * 10 + (c - '0');
divisor *= 10;
}
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");
// Cast to unsigned avoids UB if shifting discards bits
integer = (uint32_t)integer << 16;
// 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];
@@ -1741,6 +1769,8 @@ static int yylex_SKIP_TO_ENDC(void); // forward declaration for yylex_NORMAL
static int yylex_NORMAL(void)
{
uint32_t num = 0;
for (;;) {
int c = nextChar();
char secondChar;
@@ -1912,10 +1942,12 @@ static int yylex_NORMAL(void)
case '7':
case '8':
case '9':
yylval.constValue = readNumber(10, c - '0');
num = readNumber(10, c - '0');
if (peek() == '.') {
shiftChar();
yylval.constValue = readFractionalPart(yylval.constValue);
yylval.constValue = readFractionalPart(num);
} else {
yylval.constValue = num;
}
return T_NUMBER;

View File

@@ -19,6 +19,7 @@
#include <time.h>
#include "asm/charmap.h"
#include "asm/fixpoint.h"
#include "asm/format.h"
#include "asm/fstack.h"
#include "asm/lexer.h"
@@ -86,7 +87,7 @@ static char *make_escape(char const *str)
}
// 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
static int depType; // Variants of `-M`
@@ -116,6 +117,7 @@ static struct option const longopts[] = {
{ "MQ", required_argument, &depType, 'Q' },
{ "output", required_argument, NULL, 'o' },
{ "pad-value", required_argument, NULL, 'p' },
{ "q-precision", required_argument, NULL, 'Q' },
{ "recursion-depth", required_argument, NULL, 'r' },
{ "version", no_argument, NULL, 'V' },
{ "verbose", no_argument, NULL, 'v' },
@@ -128,7 +130,8 @@ static void print_usage(void)
fputs(
"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"
" [-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"
" -E, --export-all export all labels\n"
" -M, --dependfile <path> set the output dependency file\n"
@@ -170,6 +173,7 @@ int main(int argc, char *argv[])
opt_B("01");
opt_G("0123");
opt_P(0);
opt_Q(16);
haltnop = true;
warnOnHaltNop = true;
optimizeLoads = true;
@@ -250,17 +254,34 @@ int main(int argc, char *argv[])
out_SetFileName(musl_optarg);
break;
unsigned long fill;
unsigned long padByte;
case 'p':
fill = strtoul(musl_optarg, &ep, 0);
padByte = strtoul(musl_optarg, &ep, 0);
if (musl_optarg[0] == '\0' || *ep != '\0')
errx("Invalid argument for option 'p'");
if (fill > 0xFF)
if (padByte > 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;
case 'r':

View File

@@ -14,6 +14,7 @@
#include <stdlib.h>
#include <string.h>
#include "asm/fixpoint.h"
#include "asm/fstack.h"
#include "asm/lexer.h"
#include "asm/main.h"
@@ -23,7 +24,8 @@
struct OptStackEntry {
char binary[2];
char gbgfx[4];
int32_t fillByte;
uint8_t fixPrecision;
uint8_t fillByte;
bool haltnop;
bool warnOnHaltNop;
bool optimizeLoads;
@@ -47,9 +49,14 @@ void opt_G(char const chars[4])
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)
@@ -103,18 +110,41 @@ void opt_Parse(char *s)
case 'p':
if (strlen(&s[1]) <= 2) {
int result;
unsigned int fillchar;
unsigned int padByte;
result = sscanf(&s[1], "%x", &fillchar);
if (result != EOF && result != 1)
result = sscanf(&s[1], "%x", &padByte);
if (result != 1)
error("Invalid argument for option 'p'\n");
else if (padByte > 0xFF)
error("Argument for option 'p' must be between 0 and 0xFF\n");
else
opt_P(fillchar);
opt_P(padByte);
} else {
error("Invalid argument for option 'p'\n");
}
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': {
++s; // Skip 'r'
while (isblank(*s))
@@ -231,6 +261,8 @@ void opt_Push(void)
entry->gbgfx[2] = gfxDigits[2];
entry->gbgfx[3] = gfxDigits[3];
entry->fixPrecision = fixPrecision; // Pulled from fixpoint.h
entry->fillByte = fillByte; // Pulled from section.h
entry->haltnop = haltnop; // Pulled from main.h
@@ -259,6 +291,7 @@ void opt_Pop(void)
opt_B(entry->binary);
opt_G(entry->gbgfx);
opt_P(entry->fillByte);
opt_Q(entry->fixPrecision);
opt_H(entry->warnOnHaltNop);
opt_h(entry->haltnop);
opt_L(entry->optimizeLoads);