summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2007-09-18 16:39:03 -0700
committerH. Peter Anvin <hpa@zytor.com>2007-09-18 16:39:03 -0700
commit141d7cf68d60f6c77c078fea7ff85526db668c6f (patch)
treee5605799603d43038a2a4d095471a2e3f3a35a76
parent510a2508e6cb1298fa4640d6083e73c1c7ead06e (diff)
downloadnasm-141d7cf68d60f6c77c078fea7ff85526db668c6f.tar.gz
Support 16-bit IEEE floating point; used in SSE5
SSE5 supports standard IEEE 16-bit floating point, so we should support that too.
-rw-r--r--float.c71
-rw-r--r--parser.c24
2 files changed, 82 insertions, 13 deletions
diff --git a/float.c b/float.c
index 099e23f2..afa84d2e 100644
--- a/float.c
+++ b/float.c
@@ -213,6 +213,7 @@ static int ieee_round(uint16_t *mant, int i)
#define put(a,b) ( (*(a)=(b)), ((a)[1]=(b)>>8) )
+/* 64-bit format with 52-bit mantissa and 11-bit exponent */
static int to_double(char *str, int32_t sign, uint8_t *result,
efunc error)
{
@@ -275,6 +276,7 @@ static int to_double(char *str, int32_t sign, uint8_t *result,
return 1; /* success */
}
+/* 32-bit format with 23-bit mantissa and 8-bit exponent */
static int to_float(char *str, int32_t sign, uint8_t *result,
efunc error)
{
@@ -330,6 +332,64 @@ static int to_float(char *str, int32_t sign, uint8_t *result,
return 1;
}
+/* 16-bit format with 10-bit mantissa and 5-bit exponent.
+ Defined in IEEE 754r. Used in SSE5. See the AMD SSE5 manual, AMD
+ document number 43479. */
+static int to_float16(char *str, int32_t sign, uint8_t *result,
+ efunc error)
+{
+ uint16_t mant[MANT_WORDS];
+ int32_t exponent;
+
+ sign = (sign < 0 ? 0x8000L : 0L);
+
+ ieee_flconvert(str, mant, &exponent, error);
+ if (mant[0] & 0x8000) {
+ /*
+ * Non-zero.
+ */
+ exponent--;
+ if (exponent >= -14 && exponent <= 16) {
+ /*
+ * Normalised.
+ */
+ exponent += 15;
+ ieee_shr(mant, 5);
+ ieee_round(mant, 1);
+ if (mant[0] & 0x800) /* did we scale up by one? */
+ ieee_shr(mant, 1), exponent++;
+ mant[0] &= 0x3FF; /* remove leading one */
+ put(result + 0, (exponent << 7) | mant[0] | sign);
+ } else if (exponent < -14 && exponent >= -24) {
+ /*
+ * Denormal.
+ */
+ int shift = -(exponent + 8);
+ int sh = shift % 16, wds = shift / 16;
+ ieee_shr(mant, sh);
+ if (ieee_round(mant, 1 - wds)
+ || (sh > 0 && (mant[0] & (0x8000 >> (sh - 1))))) {
+ ieee_shr(mant, 1);
+ if (sh == 0)
+ mant[0] |= 0x8000;
+ exponent++;
+ }
+ put(result + 0, (wds == 0 ? mant[0] : 0) | sign);
+ } else {
+ if (exponent > 0) {
+ error(ERR_NONFATAL, "overflow in floating-point constant");
+ return 0;
+ } else
+ memset(result, 0, 2);
+ }
+ } else {
+ memset(result, 0, 2);
+ }
+ return 1;
+}
+
+/* 80-bit format with 64-bit mantissa *including an explicit integer 1*
+ and 15-bit exponent. */
static int to_ldoub(char *str, int32_t sign, uint8_t *result,
efunc error)
{
@@ -394,13 +454,16 @@ static int to_ldoub(char *str, int32_t sign, uint8_t *result,
int float_const(char *number, int32_t sign, uint8_t *result, int bytes,
efunc error)
{
- if (bytes == 4)
+ switch (bytes) {
+ case 2:
+ return to_float16(number, sign, result, error);
+ case 4:
return to_float(number, sign, result, error);
- else if (bytes == 8)
+ case 8:
return to_double(number, sign, result, error);
- else if (bytes == 10)
+ case 10:
return to_ldoub(number, sign, result, error);
- else {
+ default:
error(ERR_PANIC, "strange value %d passed to float_const", bytes);
return 0;
}
diff --git a/parser.c b/parser.c
index ca12a097..69ae3790 100644
--- a/parser.c
+++ b/parser.c
@@ -230,30 +230,36 @@ insn *parse_line(int pass, char *buffer, insn * result,
if (i == TOKEN_FLOAT) {
eop->type = EOT_DB_STRING;
result->eops_float = TRUE;
- if (result->opcode == I_DD)
+ switch (result->opcode) {
+ case I_DW:
+ eop->stringlen = 2;
+ break;
+ case I_DD:
eop->stringlen = 4;
- else if (result->opcode == I_DQ)
+ break;
+ case I_DQ:
eop->stringlen = 8;
- else if (result->opcode == I_DT)
+ break;
+ case I_DT:
eop->stringlen = 10;
- else if (result->opcode == I_DO)
- eop->stringlen = 16;
- else {
+ break;
+ default:
error(ERR_NONFATAL, "floating-point constant"
- " encountered in `D%c' instruction",
- result->opcode == I_DW ? 'W' : 'B');
+ " encountered in `d%c' instruction"
+ ? (result->opcode == I_DO) ? 'o' : 'b');
/*
* fix suggested by Pedro Gimeno... original line
* was:
* eop->type = EOT_NOTHING;
*/
eop->stringlen = 0;
+ break;
}
eop = nasm_realloc(eop, sizeof(extop) + eop->stringlen);
tail = &eop->next;
*fixptr = eop;
eop->stringval = (char *)eop + sizeof(extop);
- if (eop->stringlen < 4 ||
+ if (!eop->stringlen ||
!float_const(tokval.t_charptr, sign,
(uint8_t *)eop->stringval,
eop->stringlen, error))