summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2007-09-18 18:31:26 -0700
committerH. Peter Anvin <hpa@zytor.com>2007-09-18 18:31:26 -0700
commitfe2177fe4287bbfa9205bcd362694f47870a3c30 (patch)
tree8121152ecea8ed93fe0aafd127d02e796240a085
parente31747e95bba75c7e27d0a76f0e385c6d12351e2 (diff)
downloadnasm-fe2177fe4287bbfa9205bcd362694f47870a3c30.tar.gz
Support C99-style hexadecimal floating point.
Add support for C99-style hexadecimal floating point. The format is 0x <hexadecimal mantissa> p <binary exponent>. 0x1.0e+1 thus is the same as 2.0.
-rw-r--r--float.c91
-rw-r--r--stdscan.c4
2 files changed, 94 insertions, 1 deletions
diff --git a/float.c b/float.c
index a6ad3936..08dfdb47 100644
--- a/float.c
+++ b/float.c
@@ -8,6 +8,7 @@
* initial version 13/ix/96 by Simon Tatham
*/
+#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -56,6 +57,91 @@ static int ieee_multiply(uint16_t *to, uint16_t *from)
}
}
+static int hexval(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c-'0';
+ else if (c >= 'a' && c <= 'f')
+ return c-'a'+10;
+ else
+ return c-'A'+10;
+}
+
+static void ieee_flconvert_hex(char *string, uint16_t *mant,
+ int32_t *exponent, efunc error)
+{
+ static const int log2tbl[16] =
+ { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
+ uint16_t mult[MANT_WORDS+1], *mp;
+ int ms;
+ int32_t twopwr;
+ int seendot, seendigit;
+ unsigned char c;
+
+ twopwr = 0;
+ seendot = seendigit = 0;
+
+ memset(mult, 0, sizeof mult);
+
+ while ((c = *string++) != '\0') {
+ if (c == '.') {
+ if (!seendot)
+ seendot = TRUE;
+ else {
+ error(ERR_NONFATAL,
+ "too many periods in floating-point constant");
+ return;
+ }
+ } else if (isxdigit(c)) {
+ int v = hexval(c);
+
+ if (!seendigit && v) {
+ int l = log2tbl[v];
+
+ seendigit = 1;
+ mp = mult;
+ ms = 15-l;
+
+ twopwr = seendot ? twopwr-4+l : l-3;
+ }
+
+ if (seendigit) {
+ if (ms <= 0) {
+ *mp |= v >> -ms;
+ mp++;
+ if (mp > &mult[MANT_WORDS])
+ mp = &mult[MANT_WORDS]; /* Guard slot */
+ ms += 16;
+ }
+ *mp |= v << ms;
+ ms -= 4;
+
+ if (!seendot)
+ twopwr += 4;
+ } else {
+ if (seendot)
+ twopwr -= 4;
+ }
+ } else if (c == 'p' || c == 'P') {
+ twopwr += atoi(string);
+ break;
+ } else {
+ error(ERR_NONFATAL,
+ "floating-point constant: `%c' is invalid character",
+ *string);
+ return;
+ }
+ }
+
+ if (!seendigit) {
+ memset(mant, 0, 2*MANT_WORDS); /* Zero */
+ *exponent = 0;
+ } else {
+ memcpy(mant, mult, 2*MANT_WORDS);
+ *exponent = twopwr;
+ }
+}
+
static void ieee_flconvert(char *string, uint16_t *mant,
int32_t *exponent, efunc error)
{
@@ -66,6 +152,11 @@ static void ieee_flconvert(char *string, uint16_t *mant,
int32_t tenpwr, twopwr;
int extratwos, started, seendot;
+ if (string[0] == '0' && (string[1] == 'x' || string[1] == 'X')) {
+ ieee_flconvert_hex(string+2, mant, exponent, error);
+ return;
+ }
+
p = digits;
tenpwr = 0;
started = seendot = FALSE;
diff --git a/stdscan.c b/stdscan.c
index d4ad696d..b6a4ee8f 100644
--- a/stdscan.c
+++ b/stdscan.c
@@ -130,7 +130,9 @@ int stdscan(void *private_data, struct tokenval *tv)
stdscan_bufptr++;
while (isnumchar(*stdscan_bufptr) ||
((stdscan_bufptr[-1] == 'e'
- || stdscan_bufptr[-1] == 'E')
+ || stdscan_bufptr[-1] == 'E'
+ || stdscan_bufptr[-1] == 'p'
+ || stdscan_bufptr[-1] == 'P')
&& (*stdscan_bufptr == '-' || *stdscan_bufptr == '+'))) {
stdscan_bufptr++;
}