Merge commit '0616816f8e81c24377dca6e191a82c406ab59394' into macro11-v0.8

Update to latest released version of macro-11.

Command run: git subtree pull --prefix=crossassemblers/macro11 ../macro11 macro11-v0.8
(should be equivalent to git subtree pull --prefix=crossassemblers/macro11 git://gitlab.com/Rhialto/macro11.git macro11-v0.8)
This commit is contained in:
Olaf Seibert
2022-07-06 19:00:22 +02:00
38 changed files with 2521 additions and 482 deletions

View File

@@ -30,9 +30,11 @@ TESTS="test-asciz \
test-prec \
test-psect \
test-rad50 \
test-radix \
test-reg \
test-reloc \
test-rept \
test-syntax \
test-ua-pl \
test-undef \
test-word-comma"

View File

@@ -0,0 +1,972 @@
# /*
gcc float.c -o float -lm
./float
exit $?
*/
/*
* Parsing PDP-11 floating point literals.
* Testing ground for different implementations.
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <float.h>
#include <ieeefp.h>
#define DEBUG_FLOAT 1
void
printflt(unsigned *flt, int size)
{
printf("%06o: ", flt[0]);
printf("sign: %d ", (flt[0] & 0x8000) >> 15);
printf("uexp: %x ", (flt[0] & 0x7F80) >> 7);
printf("ufrac: %02x", flt[0] & 0x007F);
for (int i = 1; i < size; i++) {
printf(" %04x", flt[i]);
}
printf("\n");
}
#if DEBUG_FLOAT
#define DF(...) printf(__VA_ARGS__)
#else
#define DF(...)
#endif
/*
* We need 56 bits of mantissa.
*
* Try to detect if it is needed, possible and useful to use
* long double instead of double, when parsing floating point numbers.
*/
#if DBL_MANT_DIG >= 56
/* plain double seems big enough */
# define USE_LONG_DOUBLE 0
/* long double exists and seems big enough */
#elif LDBL_MANT_DIG >= 56
# define USE_LONG_DOUBLE 1
#elif defined(LDBL_MANT_DIG)
/* long double exists but is probably still too small */
# define USE_LONG_DOUBLE 1
#else
/* long double does not exist and plain double is too small */
# define USE_LONG_DOUBLE 0
#endif
#if USE_LONG_DOUBLE
# define DOUBLE long double
# define SCANF_FMT "%Lf"
# define FREXP frexpl
#else
# define DOUBLE double
# define SCANF_FMT "%lf"
# define FREXP frexp
#endif
/* Parse PDP-11 64-bit floating point format. */
/* Give a pointer to "size" words to receive the result. */
/* Note: there are probably degenerate cases that store incorrect
results. For example, I think rounding up a FLT2 might cause
exponent overflow. Sorry. */
/* Note also that the full 56 bits of precision probably aren't always
available on the source platform, given the widespread application
of IEEE floating point formats, so expect some differences. Sorry
again. */
int parse_float(
char *cp,
int size,
unsigned *flt)
{
DOUBLE d; /* value */
DOUBLE frac; /* fractional value */
uint64_t ufrac; /* fraction converted to 56 bit
unsigned integer */
uint64_t onehalf; /* one half of the smallest bit
(used for rounding) */
int i; /* Number of fields converted by sscanf */
int n; /* Number of characters converted by sscanf */
int sexp; /* Signed exponent */
unsigned uexp; /* Unsigned excess-128 exponent */
unsigned sign = 0; /* Sign mask */
i = sscanf(cp, SCANF_FMT "%n", &d, &n);
if (i == 0)
return 0; /* Wasn't able to convert */
DF("LDBL_MANT_DIG: %d\n", LDBL_MANT_DIG);
DF("%Lf input: %s\n", d, cp);
cp += n;
if (d == 0.0) {
for (i = 0; i < size; i++) {
flt[i] = 0; /* All-bits-zero equals zero */
}
return 1; /* Good job. */
}
frac = FREXP(d, &sexp); /* Separate into exponent and mantissa */
DF("frac: %Lf %La sexp: %d\n", frac, frac, sexp);
if (sexp < -127 || sexp > 127)
return 0; /* Exponent out of range. */
uexp = sexp + 128; /* Make excess-128 mode */
uexp &= 0xff; /* express in 8 bits */
DF("uexp: %02x\n", uexp);
/*
* frexp guarantees its fractional return value is
* abs(frac) >= 0.5 and abs(frac) < 1.0
* Another way to think of this is that:
* abs(frac) >= 2**-1 and abs(frac) < 2**0
*/
if (frac < 0) {
sign = (1 << 15); /* Negative sign */
frac = -frac; /* fix the mantissa */
}
/*
* For the PDP-11 floating point representation the
* fractional part is 7 bits (for 16-bit floating point
* literals), 23 bits (for 32-bit floating point values),
* or 55 bits (for 64-bit floating point values).
* However the bit immediately above the MSB is always 1
* because the value is normalized. So it's actually
* 8 bits, 24 bits, or 56 bits.
* We effectively multiply the fractional part of our value by
* 2**56 to fully expose all of those bits (including
* the MSB which is 1).
* However as an intermediate step, we really multiply by
* 2**57, so we get one lsb for possible later rounding
* purposes. After that, we divide by 2 again.
*/
/* The following big literal is 2 to the 57th power: */
ufrac = (uint64_t) (frac * 144115188075855872.0); /* Align fraction bits */
DF("ufrac: %016lx\n", ufrac);
DF("56 : %016lx\n", (1UL<<57) - 2);
/*
* ufrac is now >= 2**56 and < 2**57.
* This means it's normalized: bit [56] is 1
* and all higher bits are 0.
*/
/* Round from 57-bits to 56, 24, or 8.
* We do this by:
* + first adding a value equal to one half of the
* least significant bit (the value 'onehalf')
* + (possibly) dealing with any carrying that
* causes the value to no longer be normalized
* (with bit [56] = 1 and all higher bits = 0)
* + shifting right by 1 bit (which throws away
* the 0 bit). Note this step could be rolled
* into the next step.
* + taking the remaining highest order 8,
* 24, or 56 bits.
*
* +--+--------+-------+ +--------+--------+
* |15|14 7|6 0| |15 | 0|
* +--+--------+-------+ +--------+--------+
* | S|EEEEEEEE|MMMMMMM| |MMMMMMMM|MMMMMMMM| ...maybe 2 more words...
* +--+--------+-------+ +--------+--------+
* Sign (1 bit)
* Exponent (8 bits)
* Mantissa (7 bits)
*/
onehalf = 1ULL << (16 * (4-size));
ufrac += onehalf;
DF("onehalf=%016lx, ufrac+onehalf: %016lx\n", onehalf, ufrac);
/* Did it roll up to a value 2**56? */
if ((ufrac >> 57) > 0) { /* Overflow? */
if (uexp < 0xFF) {
ufrac >>= 1; /* Normalize */
uexp++;
DF("ufrac: %016lx uexp: %02x (normalized)\n", ufrac, uexp);
} else {
/*
* If rounding and then normalisation would cause the exponent to
* overflow, just don't round: the cure is worse than the disease.
* We could detect ahead of time but the conditions for all size
* values may be a bit complicated, and so rare, that it is more
* readable to just undo it here.
*/
ufrac -= onehalf;
DF("don't round: exponent overflow\n");
}
}
ufrac >>= 1; /* Go from 57 bits to 56 */
flt[0] = (unsigned) (sign | (uexp << 7) | ((ufrac >> 48) & 0x7F));
if (size > 1) {
flt[1] = (unsigned) ((ufrac >> 32) & 0xffff);
if (size > 2) {
flt[2] = (unsigned) ((ufrac >> 16) & 0xffff);
flt[3] = (unsigned) ((ufrac >> 0) & 0xffff);
}
}
return 1;
}
/*
* Several of these functions assume that "unsigned" contains more
* than 16 bits. Bit 16 is sometimes used to store a carry.
*/
#define DUMP DF("exp: %d. %d %04x %04x %04x %04x\n", \
float_dec_exponent, float_bin_exponent, \
float_buf[0], float_buf[1], float_buf[2], float_buf[3])
#define DUMP4(b, t) DF("%04x %04x %04x %04x%s", \
b[0], b[1], b[2], b[3], t)
void float_copy( /* XMIT4 at FLTGSV, more or less */
unsigned *to,
unsigned *from)
{
to[0] = from[0] & 0xFFFF;
to[1] = from[1] & 0xFFFF;
to[2] = from[2] & 0xFFFF;
to[3] = from[3] & 0xFFFF;
}
int float_right_shift( /* FLTGRS */
unsigned *buf)
{
int carry1, carry2;
carry1 = buf[0] & 0x0001; buf[0] >>= 1;
carry2 = buf[1] & 0x0001; buf[1] >>= 1; buf[1] |= (carry1 << 15);
carry1 = buf[2] & 0x0001; buf[2] >>= 1; buf[2] |= (carry2 << 15);
carry2 = buf[3] & 0x0001; buf[3] >>= 1; buf[3] |= (carry1 << 15);
return carry2;
}
int float_left_shift( /* FLTGLS */
unsigned *buf)
{
int carry1, carry2;
carry1 = buf[3] >> 15; buf[3] <<= 1;
carry2 = buf[2] >> 15; buf[2] <<= 1; buf[2] |= (carry1 & 0x0001);
carry1 = buf[1] >> 15; buf[1] <<= 1; buf[1] |= (carry2 & 0x0001);
carry2 = buf[0] >> 15; buf[0] <<= 1; buf[0] |= (carry1 & 0x0001);
float_copy(buf, buf); /* Clean up carries */
return carry2 & 0x01;
}
void float_add( /* FLTGAD */
unsigned *to,
unsigned *from)
{
//DF("float_add: "); DUMP4(to, ""); DF(" += "); DUMP4(from, "\n");
to[3] += from[3];
to[2] += from[2] + ((to[3] >> 16) & 0x0001);
to[1] += from[1] + ((to[2] >> 16) & 0x0001);
to[0] += from[0] + ((to[1] >> 16) & 0x0001);
//DF(" = "); DUMP4(to, "\n");
float_copy(to, to); /* Clean up carries */
}
void float_mult_by_5( /* FLTM50 */
unsigned *buf,
unsigned *workspace)
{
float_copy(workspace, buf); /* Save 1 x original value */
float_left_shift(buf); /* 2 x */
float_left_shift(buf); /* 4 x */
float_add(buf, workspace); /* 5 x */
}
void float_mult_by_5_4th( /* FLTM54 */
int *bin_exp,
unsigned *buf,
unsigned *workspace)
{
if (buf[0] >= 0146314) {
float_right_shift(buf);
++*bin_exp;
}
float_copy(workspace, buf);
float_right_shift(buf); /* half of the original */
float_right_shift(buf); /* a quarter */
float_add(buf, workspace); /* add the original to the quarter */
}
int parse_float_m2(
char *cp,
int size,
unsigned *flt)
{
int float_sign = 0;
int float_dot = 0;
int float_dec_exponent = 0;
int float_bin_exponent = 0;
unsigned float_buf[4] = { 0 };
unsigned float_save[4];
float_bin_exponent = 65;
if (*cp == '+') {
cp++;
} else if (*cp == '-') {
float_sign = 0100000;
cp++;
}
DF("float_sign: %d\n", float_sign);
for (;;) {
if (isdigit(*cp)) {
/* Can we multiply by 10? */
DF("digit: %c\n", *cp);
if (float_buf[0] & 0174000) {
/* No, that would overflow */
float_dec_exponent++; /* no, compensate for the snub */
/*
* Explanation of the above comment:
* - after the decimal separator, we should ignore extra digits
* completely. Since float_dot == -1, the exponent will be
* decremented below, and compensate for that here.
* - before the decimal separator, we don't add the digit
* (which would overflow) so we lose some lesser significant
* bits. float_dot == 0 in this case, and we do want the
* exponent increased to compensate for the ignored digit.
* So in both cases the right thing happens.
*/
} else {
/* Multiply by 10 */
float_mult_by_5(float_buf, float_save);
float_left_shift(float_buf);
/* Add digit */
float_buf[3] += *cp - '0';
/* Ripple carry */
float_buf[2] += (float_buf[3] >> 16) & 0x0001;
float_buf[1] += (float_buf[2] >> 16) & 0x0001;
float_buf[0] += (float_buf[1] >> 16) & 0x0001;
float_copy(float_buf, float_buf); /* Clean up carries */
}
float_dec_exponent += float_dot;
DUMP;
cp++;
} else if (*cp == '.') {
DF("dot: %c\n", *cp);
if (float_dot < 0) {
// error...
printf("Error: repeated decimal separator\n");
return 0;
}
float_dot = -1;
cp++;
} else {
DF("Other char: %c\n", *cp);
break;
}
}
if (toupper(*cp) == 'E') {
cp++;
int exp = strtol(cp, &cp, 10);
float_dec_exponent += exp;
DF("E%d -> dec_exp %d\n", exp, float_dec_exponent);
}
/* FLTG3 */
if (float_buf[0] | float_buf[1] | float_buf[2] | float_buf[3]) {
/* Non-zero */
DF("Non-zero: decimal exponent: %d\n", float_dec_exponent);
while (float_dec_exponent > 0) { /* 31$ */
DUMP;
if (float_buf[0] <= 031426) { /* 13078, 0x3316, 65390 / 5 */
/* Multiply by 5 and 2 */
DF("Multiply by 5 and 2\n");
float_mult_by_5(float_buf, float_save);
float_bin_exponent++;
DUMP;
} else {
/* Multiply by 5/4 and 8 32$ */
DF("Multiply by 5/4 and 8\n");
float_mult_by_5_4th(&float_bin_exponent, float_buf, float_save);
float_bin_exponent += 3;
DUMP;
}
float_dec_exponent--;
}
while (float_dec_exponent < 0) { /* 41$ ish */
DUMP;
DF("Prepare for division by left-shifting the bits\n");
/* Prepare for division by left-shifting the bits */
while ((float_buf[0] & 0x8000) == 0) { /* 41$ */
float_bin_exponent--; /* 40$ */
float_left_shift(float_buf);
DUMP;
}
/* Divide by 2 */
float_right_shift(float_buf);
float_copy(float_save, float_buf);
DUMP;
DF("float_save: "); DUMP4(float_save, "\n");
/* Divide by 5:
* multiplying by (2**32 - 1) / 5 = 0x333333333
* while dropping the 32 least significant bits.
* This is nearly exact but exact enough?
* 2**32 - 1 is a multiple of 5.
*/
for (int i = 16 * 2; i > 0; i--) {
if ((i & 1) == 0) { /* 42$ */
float_right_shift(float_buf);
float_right_shift(float_buf);
}
float_right_shift(float_buf); /* 43$ */
float_add(float_buf, float_save);
DF("Loop i=%2d: ", i); DUMP;
}
float_bin_exponent -= 3;
float_dec_exponent++;
DUMP;
}
/* Normalize the mantissa: shift a single 1 out to the left */
DF("Normalize the mantissa: shift a single 1 out to the left\n");
int carry;
do {
/* FLTG5 */
float_bin_exponent--;
carry = float_left_shift(float_buf);
DUMP;
} while (carry == 0);
/* Set excess 128. */
DF("Set excess 128.\n");
float_bin_exponent += 0200;
DUMP;
if (float_bin_exponent & 0xFF00) {
/* Error N. Underflow. 2$ */
printf("Error N (underflow)\n");
return 0;
}
/* Shift right 9 positions to make room for sign and exponent 3$ */
DF("Shift right 9 positions to make room for sign and exponent\n");
int round = (float_buf[3] >> 8) & 0x0001;
float_buf[3] >>= 9; float_buf[3] |= (float_buf[2] << (16-9)) & 0xff80;
float_buf[2] >>= 9; float_buf[2] |= (float_buf[1] << (16-9)) & 0xff80;
float_buf[1] >>= 9; float_buf[1] |= (float_buf[0] << (16-9)) & 0xff80;
float_buf[0] >>= 9;
float_buf[0] |= float_bin_exponent << 7;
DUMP;
/*
* float_bin_exponent is included because of the location of
* FLTBEX ;BINARY EXPONENT (MUST PRECEED FLTBUF)
*/
/* Round (this is optional, really) */
if (1) {
if (size < 4) {
round = float_buf[size] & 0x8000;
}
/* The rounding bit is the lesser significant bit that's just
* outside the returned result. If we round, we add it to the
* returned value.
*
* If there is a carry-out of the mantissa, it gets added to
* the exponent (increasing it by 1).
*
* If that also overflows, we truely have overflow.
*/
if (round) {
for (int i = size - 1 ; round && i >= 0; i--) {
float_buf[i]++; /* 5$ */
round = float_buf[i] & 0x10000; /* carry */
DF("round, i=%d: ", i); DUMP;
}
}
DF("After rounding\n");
DUMP;
}
if (float_buf[0] & 0x8000) {
// 6$ error T: exponent overflow
printf("Error T\n");
return 0;
}
/* 7$ */
float_buf[0] |= float_sign;
DF("Put in float_sign: "); DUMP;
}
/* Now put together the result from the parts */
flt[0] = float_buf[0] & 0xFFFF;
if (size > 1) {
flt[1] = float_buf[1] & 0xFFFF;
if (size > 2) {
flt[2] = float_buf[2] & 0xFFFF;
flt[3] = float_buf[3] & 0xFFFF;
}
}
return 1;
}
static void
mult64to128(uint64_t op1, uint64_t op2, uint64_t *hi, uint64_t *lo)
{
#if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ >= 16
__uint128_t result = (__uint128_t)op1 * op2;
*lo = result;
*hi = result >> 64;
#else
uint64_t u1 = (op1 & 0xffffffff);
uint64_t v1 = (op2 & 0xffffffff);
uint64_t t = (u1 * v1);
uint64_t w3 = (t & 0xffffffff);
uint64_t k = (t >> 32);
op1 >>= 32;
t = (op1 * v1) + k;
k = (t & 0xffffffff);
uint64_t w1 = (t >> 32);
op2 >>= 32;
t = (u1 * op2) + k;
k = (t >> 32);
*hi = (op1 * op2) + w1 + k;
*lo = (t << 32) + w3;
#endif
}
#define DUMP3 DF("exp: %d. %d %016llx\n", \
float_dec_exponent, float_bin_exponent, float_buf)
int parse_float_m3(
char *cp,
int size,
unsigned *flt)
{
int float_sign = 0;
int float_dot = 0;
int float_dec_exponent = 0;
int float_bin_exponent = 0;
uint64_t float_buf = 0;
float_bin_exponent = 65;
if (*cp == '+') {
cp++;
} else if (*cp == '-') {
float_sign = 1;
cp++;
}
DF("float_sign: %d\n", float_sign);
for (;;) {
if (isdigit(*cp)) {
/* Can we multiply by 10? */
DF("digit: %c\n", *cp);
if (float_buf & 0xF800000000000000ULL) { /* [0] & 0174000, 0xF800 */
/* No, that would overflow */
float_dec_exponent++; /* no, compensate for the snub */
/*
* Explanation of the above comment:
* - after the decimal separator, we should ignore extra digits
* completely. Since float_dot == -1, the exponent will be
* decremented below, and compensate for that here.
* - before the decimal separator, we don't add the digit
* (which would overflow) so we lose some lesser significant
* bits. float_dot == 0 in this case, and we do want the
* exponent increased to compensate for the ignored digit.
* So in both cases the right thing happens.
*/
} else {
/* Multiply by 10 */
float_buf *= 10;
/* Add digit */
float_buf += *cp - '0';
}
float_dec_exponent -= float_dot;
DUMP3;
cp++;
} else if (*cp == '.') {
DF("dot: %c\n", *cp);
if (float_dot) {
// error...
printf("Error: repeated decimal separator\n");
return 0;
}
float_dot = 1;
cp++;
} else {
DF("Other char: %c\n", *cp);
break;
}
}
if (toupper(*cp) == 'E') {
cp++;
int exp = strtol(cp, &cp, 10);
float_dec_exponent += exp;
DF("E%d -> dec_exp %d\n", exp, float_dec_exponent);
}
/* FLTG3 */
if (float_buf) {
/* Non-zero */
DF("Non-zero: decimal exponent: %d\n", float_dec_exponent);
while (float_dec_exponent > 0) { /* 31$ */
DUMP3;
if (float_buf <= 0x3316000000000000ULL) { /* 031426, 13078, 0x3316, 65390 / 5 */
/* Multiply by 5 and 2 */
DF("Multiply by 5 and 2\n");
float_buf *= 5;
float_bin_exponent++;
DUMP3;
} else {
/* Multiply by 5/4 and 8 32$ */
DF("Multiply by 5/4 and 8\n");
if (float_buf >= 0xCCCC000000000000ULL) {
float_buf >>= 1;
float_bin_exponent++;
}
float_buf += (float_buf >> 2);
float_bin_exponent += 3;
DUMP3;
}
float_dec_exponent--;
}
while (float_dec_exponent < 0) { /* 41$ ish */
DUMP3;
DF("Prepare for division by left-shifting the bits\n");
/* Prepare for division by left-shifting the bits */
while (((float_buf >> 63) & 1) == 0) { /* 41$ */
float_bin_exponent--; /* 40$ */
float_buf <<= 1;
DUMP3;
}
/* Divide by 2 */
float_buf >>= 1;
uint64_t float_save = float_buf;
DUMP3;
DF("float_save: %016llx\n", float_save);
/* Divide by 5:
* multiplying by (2**66 - 4) / 5 = 0xCCCCCCCCCCCCCCCC
* while dropping the 64 least significant bits.
* This is nearly exact but exact enough?
* Somehow there is another doubling in here, so the
* final result = start * 8 / 5.
*/
for (int i = 16 * 2; i > 0; i--) {
if ((i & 1) == 0) { /* 42$ */
float_buf >>= 2;
}
float_buf >>= 1; /* 43$ */
float_buf += float_save;
DF("Loop i=%2d: ", i); DUMP3;
}
#if 1
{ /* Method 3 */
uint64_t hi, lo;
mult64to128(float_save, 0x9999999999999999ULL, &hi, &lo);
/*
* Really multiply with the 65-bit number
* 0x19999999999999999
* 1 1001 1001 1001 ...
* which is 8 * 0011 0011 0011 ... aka 0x333...
* which is (2**64 - 1) / 5 aka 0xFFF... / 5.
*/
uint64_t result = hi + float_save;
if (result == float_buf) {
printf("Same 333 loop and *9999: %016llx vs %016llx\n", float_buf, result);
printf(" was : %016llx\n", float_save);
} else {
printf("Difference between 333 loop and *3333: %016llx vs %016llx\n", float_buf, result);
printf(" : %016llx %016llx\n", hi, lo);
printf(" was : %016llx\n", float_save);
}
}
#endif
#if 0 /* Try other methods to calculate the same thing more directly */
{
uint64_t result = float_save / 5 * 8;
/* Try to fill in some of the lesser significant bits */
int round = float_save % 5;
if (round) {
// If round == 2, sometimes 2 needs to be added instead,
// to match the original calculation.
result += round * 8 / 5;
} else {
// decrement to match the original calculation, but
// the end result is ok without that.
//result--;
}
# if 0
if (result == float_buf) {
printf("Same 333 loop and /5 *8: %016llx vs %016llx\n", float_buf, result);
} else {
printf("Difference between 333 loop and /5 *8: %016llx vs %016llx\n", float_buf, result);
printf(" was : %016llx %d %d\n", float_buf, float_save % 20, (float_save % 20) * 2 / 5);
}
# endif
float_buf = result;
printf("after / 5 * 8: ");
DUMP3;
}
#endif
#if 1
{
__uint128_t big = (__uint128_t)float_save << 3;
uint64_t result = big / 5;
if (float_save % 5 == 0)
result--;
if (result == float_buf) {
printf("Same 333 loop and *8 /5: %016llx vs %016llx\n", float_buf, result);
} else {
printf("Difference between 333 loop and *8 /5: %016llx vs %016llx\n", float_buf, result);
}
/*
* Rounding is slightly different, in particular for start
* values that are multiples of 5 but some other cases too.
*/
}
#endif
/* It's not simply dividing by 5, it also multiplies by 8,
* so we need to adjust the exponent here. */
float_bin_exponent -= 3;
float_dec_exponent++;
DUMP3;
}
/* Normalize the mantissa: shift a single 1 out to the left */
DF("Normalize the mantissa: shift a single 1 out to the left\n");
int carry;
do {
/* FLTG5 */
float_bin_exponent--;
carry = (float_buf >> 63) & 1;
float_buf <<= 1;
DUMP3;
} while (carry == 0);
/* Set excess 128. */
DF("Set excess 128.\n");
float_bin_exponent += 0200;
DUMP3;
if (float_bin_exponent & 0xFF00) {
/* Error N. Underflow. 2$ */
printf("Error N (underflow)\n");
return 0;
}
/* Shift right 9 positions to make room for sign and exponent 3$ */
DF("Shift right 9 positions to make room for sign and exponent\n");
int round = (float_buf >> 8) & 0x0001;
float_buf >>= 9;
float_buf |= (uint64_t)float_bin_exponent << (64-9);
DUMP3;
/*
* This rounding step seems always needed to make the result the same
* as the implementation with long doubles.
* This may be because of the slight imprecision of the divisions by 10?
* However there is something to be said for the argument that when you
* want a 1 or 2 word result, rounding twice is wrong.
* The reference implementation omits this rounding for size != 4.
*/
float_buf += round;
DF("round: size = 4, round = %d\n", round);
/* Round (there is a truncation option to omit this step) */
if (1) {
if (size < 4) {
/* 1 << 31 or 1 << 47 */
uint64_t onehalf = 1ULL << ((16 * (4-size)) - 1);
DUMP3;
DF("round: size = %d, onehalf = %016llx\n", size, onehalf);
float_buf += onehalf;
DUMP3;
/* The rounding bit is the lesser significant bit that's just
* outside the returned result. If we round, we add it to the
* returned value.
*
* If there is a carry-out of the mantissa, it gets added to
* the exponent (increasing it by 1).
*
* If that also overflows, we truely have overflow.
*/
}
DF("After rounding\n");
DUMP3;
}
if (float_buf & 0x8000000000000000ULL) {
// 6$ error T: exponent overflow
printf("Error T: exponent overflow\n");
return 0;
}
/* 7$ */
float_buf |= (uint64_t)float_sign << 63;
DF("Put in float_sign: "); DUMP3;
}
/* Now put together the result from the parts */
flt[0] = (float_buf >> 48) & 0xFFFF;
if (size > 1) {
flt[1] = (float_buf >> 32) & 0xFFFF;
if (size > 2) {
flt[2] = (float_buf >> 16) & 0xFFFF;
flt[3] = (float_buf >> 0) & 0xFFFF;
}
}
return 1;
}
void
test_one(char *input, unsigned expected0, unsigned expected1, unsigned expected2, unsigned expected3)
{
unsigned result[4];
unsigned result2[4];
printf("------------------------\n");
parse_float_m3(input, 4, result);
if (result[0] != expected0 ||
result[1] != expected1 ||
result[2] != expected2 ||
result[3] != expected3) {
printf("Unexpected result: %04x %04x %04x %04x from %s\n", result[0], result[1], result[2], result[3], input);
printf(" expected : %04x %04x %04x %04x\n", expected0, expected1, expected2, expected3);
printflt(result, 4);
}
#if 0
parse_float_m2(input, 4, result2);
if (result2[0] != expected0 ||
result2[1] != expected1 ||
result2[2] != expected2 ||
result2[3] != expected3) {
printf("Unexpected result2: %04x %04x %04x %04x from %s\n", result2[0], result2[1], result2[2], result2[3], input);
printf(" expected : %04x %04x %04x %04x\n", expected0, expected1, expected2, expected3);
printflt(result2, 4);
}
#endif
}
int
main(int argc, char **argv)
{
/* Should print 64 */
DF("LDBL_MANT_DIG: %d\n", LDBL_MANT_DIG);
test_one("100E-2", 040200, 0000000, 0000000, 0000000);
#if 1
test_one("10000000000E-10", 040200, 0000000, 0000000, 0000000);
test_one("1.0", 040200, 0000000, 0000000, 0000000);
test_one("0.1", 037314, 0146314, 0146314, 0146315);
test_one("1.0E5", 0044303, 0050000, 0000000, 0000000);
test_one("1.0E10", 0050425, 0001371, 0000000, 0000000);
test_one("1.0E20", 0060655, 0074353, 0142654, 0061000);
test_one("1.0E30", 0071111, 0171311, 0146404, 0063517);
test_one("1.0E38", 0077626, 0073231, 0050265, 0006611);
test_one("1.701411834604692307e+38", 077777, 0177777, 0177777, 0177777);
#endif
test_one("0.994140625", 0040176, 0100000, 000000, 000000);
// 407e 8000 0000 0000
#if 1
test_one("0.998046875", 0040177, 0100000, 000000, 000000);
test_one("1.00390625", 0040200, 0100000, 000000, 000000);
test_one("1.01171875", 0040201, 0100000, 000000, 000000);
test_one("0.999999910593032836914062", 0040177, 0177776, 0100000, 0000000);
test_one("0.999999970197677612304687", 0040177, 0177777, 0100000, 0000000);
test_one("1.00000005960464477539062", 0040200, 0000000, 0100000, 0000000);
test_one("1.000000178813934326171875", 0040200, 0000001, 0100000, 0000000);
test_one("1.0000000000000000138777878078144567552953958511353", 0040200, 0000000, 0000000, 0000000);
test_one("1.0000000000000000416333634234433702658861875534058", 0040200, 0000000, 0000000, 0000001);
test_one("0.99999999999999997918331828827831486705690622329712", 0040177, 0177777, 0177777, 0177776);
test_one("0.99999999999999999306110609609277162235230207443237", 0040177, 0177777, 0177777, 0177777);
//test_one("", 0, 0, 0, 0);
test_one("6.66666", 040725, 052507, 055061, 0122276);
test_one("170141183460469230846243588177825628160", 0100000, 0000000, 0000000, 0000000); // T error
test_one("170141183460469230564930741053754966016", 0077777, 0177777, 0177777, 0177777);
//test_one("3.1415926535897932384626433", 0, 0, 0, 0);
//test_one("", 0, 0, 0, 0);
//
/* First a number that should just fit in a 56 bit mantissa. */
/* 1 << 56 */
test_one("72057594037927936", 056200, 000000, 000000, 000000);
/* A bit more requires more precision, so they are rounded. */
test_one("72057594037927937", 056200, 000000, 000000, 000001);
test_one("72057594037927938", 056200, 000000, 000000, 000001);
/* Lower numbers should all be represented exactly */
test_one("72057594037927935", 056177, 0177777, 0177777, 0177777);
test_one("72057594037927934", 056177, 0177777, 0177777, 0177776);
test_one("72057594037927933", 056177, 0177777, 0177777, 0177775);
/* 1 << 57 should also be exactly representable */
test_one("144115188075855872", 056400, 000000, 000000, 000000);
/* 1 less lacks one significant bit so will be rounded up */
test_one("144115188075855871", 056400, 000000, 000000, 000000);
/* Same for 1 more, rounded down */
test_one("144115188075855873", 056400, 000000, 000000, 000000);
/* but 2 more should show up in the lsb */
/* This one seems most clearly problematic */
test_one("144115188075855874", 056400, 000000, 000000, 000001);
// Some numbers around some of the magic numbers in the parser
test_one("3681129745421959167", 0057514, 0054000, 0, 0);
test_one("3681129745421959168", 0057514, 0054000, 0, 0); // 0x3316000000000000
test_one("3681129745421959169", 0057514, 0054000, 0, 0);
test_one("3681129745421959170", 0057514, 0054000, 0, 0);
test_one("14757170078986272767", 0060114, 0146000, 0000000, 0000000);
test_one("14757170078986272768", 0060114, 0146000, 0000000, 0000000); // 0xCCCC000000000000
test_one("14757170078986272769", 0060114, 0146000, 0000000, 0000000);
test_one("14757170078986272780", 0060114, 0146000, 0000000, 0000000);
#endif
return 0;
}

View File

@@ -97,19 +97,19 @@
82 ; V05.06
83
84 000240 040177 .word ^F 0.994140625 ; (2**9-3)/2**9 040176 040177
85 000242 040176 100000 000000 .flt4 0.994140625
85 000242 040176 100000 000000 .flt4 0.994140625 ; same-> 040176 100000 0 0
000250 000000
86
87 000252 040200 .word ^F 0.998046875 ; (2**9-1)/2**9 040177 040200
88 000254 040177 100000 000000 .flt4 0.998046875
88 000254 040177 100000 000000 .flt4 0.998046875 ; same-> 040177 100000 0 0
000262 000000
89
90 000264 040201 .word ^F 1.00390625 ; (2**8+1)/2**8 040200 040201
91 000266 040200 100000 000000 .flt4 1.00390625
91 000266 040200 100000 000000 .flt4 1.00390625 ; same-> 040200 100000 0 0
000274 000000
92
93 000276 040202 .word ^F 1.01171875 ; (2**8+3)/2**8 040201 040202
94 000300 040201 100000 000000 .flt4 1.01171875
94 000300 040201 100000 000000 .flt4 1.01171875 ; same-> 040201 100000 0 0
000306 000000
95
96 000310 077777 177777 177777 .flt4 1.701411834604692307e+38 ; 077777 177777 177777 177777
@@ -121,90 +121,178 @@
99 000340 077777 177777 177777 .FLT4 170141183460469230564930741053754966016 ; 2**127-(2**70-2**64+2**62+2)
000346 177777
100
101 ; Several ways to define a name for the fpp registers
102
103 000000 ac0 = r0
104 000001 ac1 = %1
105 000002 f2 = %2
106
107 000350 171003 mulf r3,ac0
108 000352 171102 mulf r2,ac1
109 000354 172227 041040 ADDF #^O41040,F2
110 000360 172127 040200 addf #1,ac1
111
112 000364 171003 mulf r3,ac0
113 000366 171102 mulf r2,ac1
114 000370 172227 041040 addf #^O41040,F2 ; taken literally
115 000374 172127 040200 addf #1,ac1 ; as float
116 000400 172127 040200 addf #1.,ac1 ; as float
117 000404 172127 040200 addf #1.0,ac1 ; as float
118 000410 172127 000001 addf #^D1,ac1 ; literally
119 000414 173027 000001 subf #<1>,ac0 ; literally
120 000420 172127 000002 addf #<1+1>,ac1 ; literally
test-float.mac:121: ***ERROR Invalid addressing mode (1st operand, fsrc: Invalid expression after '#')
121 subf #<1.0>,ac0 ; error
122 000424 172127 040300 addf #1.5,ac1 ; as float
123 000430 172127 140263 addd #-1.4,ac1 ; as float
124 000434 173027 040200 subf #<^F 1.0>,ac0 ; as float
test-float.mac:125: ***ERROR Invalid addressing mode (1st operand, fsrc: Invalid expression after '#')
125 subf #<^D 1.0>,ac0 ; error
126 000440 173027 000001 subf #<^D 1>,ac0 ; literally
127 000444 173027 000002 subf #^D<1+1>,ac0 ; literally
128 000450 173027 000002 subf #^D 1+1 ,ac0 ; literally
129 000454 173027 042572 subf #1e3,ac0 ; as float
test-float.mac:130: ***ERROR Invalid syntax (comma expected)
130 subf #1e 3,ac0 ; TODO: accepted by MACRO11 as 1E3 (but not 1 e3, 1 e 3)
131 000001 a = 1
132 000003 e3 = 3
133 000460 173027 000001 subf #a,ac0 ; a interpreted as bit pattern
134 000464 173027 000001 subf #<a>,ac0 ; a interpreted as bit pattern
135 000470 173027 000003 subf #e3,ac0 ; e3 is the label
test-float.mac:136: ***ERROR Invalid addressing mode (1st operand, fsrc: Invalid expression after '#')
136 subf #<1e3>,ac0 ; error N
137
test-float.mac:138: ***ERROR Junk at end of line ('5 ; bad: ')
138 000474 170627 000002 absf #2.5 ; bad: operand is destination
test-float.mac:139: ***ERROR Junk at end of line ('5 ; bad: ')
139 000500 170527 000002 tstd #2.5 ; bad: operand is considered FDST by the arch handbook
test-float.mac:140: ***ERROR Junk at end of line ('5 ; bad: junk')
140 000504 174027 000002 stf ac0,#2.5 ; bad: junk at end of line
141 000510 174027 000002 stf ac0,#2 ; doesn't makes sense but MACRO11 allows it
142
143 ; Test immediate source argument for instructions that have one (src or fsrc)
144
145 000514 172027 040200 addd #1,ac0 ; float
146 000520 172027 040200 addf #1,ac0 ; float
147 000524 173427 040200 cmpd #1,ac0 ; float
148 000530 173427 040200 cmpf #1,ac0 ; float
149 000534 174427 040200 divd #1,ac0 ; float
150 000540 174427 040200 divf #1,ac0 ; float
151 000544 177427 040200 ldcdf #1,ac0 ; float
152 000550 177427 040200 ldcfd #1,ac0 ; float
153 000554 177027 000001 ldcid #1,ac0 ; integer
154 000560 177027 000001 ldcif #1,ac0 ; integer
155 000564 177027 000001 ldcld #1,ac0 ; integer
156 000570 177027 000001 ldclf #1,ac0 ; integer
157 000574 172427 040200 ldd #1,ac0 ; float
158 000600 172427 040200 ldf #1,ac0 ; float
159 000604 176427 000001 ldexp #1,ac0 ; integer
160 000610 171427 040200 modd #1,ac0 ; float
161 000614 171427 040200 modf #1,ac0 ; float
162 000620 171027 040200 muld #1,ac0 ; float
163 000624 171027 040200 mulf #1,ac0 ; float
164 000630 173027 040200 subd #1,ac0 ; float
165 000634 173027 040200 subf #1,ac0 ; float
166
167 .end
167
101 000350 040200 000000 000000 .flt4 1.0000000000000000138777878078144567552953958511353 ; 0040200 0000000 0000000 0000000
000356 000000
102 000360 040200 000000 000000 .flt4 1.0000000000000000416333634234433702658861875534058 ; 0040200 0000000 0000000 0000001
000366 000001
103 000370 040177 177777 177777 .flt4 0.99999999999999997918331828827831486705690622329712 ; 0040177 0177777 0177777 0177776
000376 177776
104 000400 040177 177777 177777 .flt4 0.99999999999999999306110609609277162235230207443237 ; 0040177 0177777 0177777 0177777
000406 177777
105
106 000410 040200 000000 000000 .flt4 100E-2 ; 040200 000000 000000 000000
000416 000000
107 000420 044303 050000 000000 .flt4 1.0E5 ; 044303 050000 000000 000000
000426 000000
108 000430 050425 001371 000000 .flt4 1.0E10 ; 050425 001371 000000 000000
000436 000000
109 000440 060655 074353 142654 .flt4 1.0E20 ; 060655 074353 142654 061000
000446 061000
110 000450 071111 171311 146404 .flt4 1.0E30 ; 071111 171311 146404 063517
000456 063517
111 000460 077626 073231 050265 .flt4 1.0E38 ; 077626 073231 050265 006611
000466 006611
112
113 000470 034047 142654 043433 .flt4 1.0E-5 ; 034047 142654 043433 043604
000476 043604
114 000500 027733 163376 147275 .flt4 1.0E-10 ; 027733 163376 147275 166726
000506 166726
115 000510 017474 162410 062222 .flt4 1.0E-20 ; 017474 162410 062222 010433
000516 010433
116 000520 007242 041137 173536 .flt4 1.0E-30 ; 007242 041137 173536 012374
000526 012374
117 000530 000531 143734 166523 .flt4 1.0E-38 ; 000531 143734 166523 143442
000536 143442
118
119 000540 057514 054000 000000 .flt4 3681129745421959167 ; 057514 054000 000000 000000
000546 000000
120 000550 057514 054000 000000 .flt4 3681129745421959168 ; 0x3316000000000000 057514 054000 000000 000000
000556 000000
121 000560 057514 054000 000000 .flt4 3681129745421959169 ; 057514 054000 000000 000000
000566 000000
122 000570 057514 054000 000000 .flt4 3681129745421959170 ; 057514 054000 000000 000000
000576 000000
123
124 000600 060114 146000 000000 .flt4 14757170078986272767 ; 060114 146000 000000 000000
000606 000000
125 000610 060114 146000 000000 .flt4 14757170078986272768 ; 0xCCCC000000000000 060114 146000 000000 000000
000616 000000
126 000620 060114 146000 000000 .flt4 14757170078986272769 ; 060114 146000 000000 000000
000626 000000
127 000630 060114 146000 000000 .flt4 14757170078986272780 ; 060114 146000 000000 000000
000636 000000
128
129 000640 040511 007732 121041 .flt4 3.1415926535897932384626433 ; 040511 007732 121041 064302
000646 064302
130
131 ; Try some possibly incomplete numbers
132
test-float.mac:133: ***ERROR Bad floating point format
133 000650 000000 000000 000000 .flt4 + ; bad
000656 000000
134 000660 040200 000000 000000 .flt4 +1 ; ok
000666 000000
test-float.mac:135: ***ERROR Bad floating point format
135 000670 000000 000000 000000 .flt4 +E1 ; bad
000676 000000
test-float.mac:136: ***ERROR Bad floating point format
136 000700 000000 000000 000000 .flt4 - ; bad
000706 000000
137 000710 140200 000000 000000 .flt4 -1 ; ok
000716 000000
138 000720 140200 000000 000000 .flt4 -1. ; ok
000726 000000
test-float.mac:139: ***ERROR Bad floating point format
139 000730 000000 000000 000000 .flt4 -1.. ; bad
000736 000000
test-float.mac:140: ***ERROR Bad floating point format
140 000740 000000 000000 000000 .flt4 -E1 ; bad
000746 000000
141 000750 000000 000000 000000 .flt4 +. ; bad
000756 000000
142 000760 000000 000000 000000 .flt4 -. ; bad
000766 000000
143 000770 000000 000000 000000 .flt4 . ; bad
000776 000000
test-float.mac:144: ***ERROR Bad floating point format
144 001000 000000 000000 000000 .flt4 .. ; bad
001006 000000
145 001010 000000 000000 000000 .flt4 .E10 ; bad
001016 000000
146
147 ; Several ways to define a name for the fpp registers
148
149 000000 ac0 = r0
150 000001 ac1 = %1
151 000002 f2 = %2
152
153 001020 171003 mulf r3,ac0
154 001022 171102 mulf r2,ac1
155 001024 172227 041040 ADDF #^O41040,F2
156 001030 172127 040200 addf #1,ac1
157
158 001034 171003 mulf r3,ac0
159 001036 171102 mulf r2,ac1
160 001040 172227 041040 addf #^O41040,F2 ; taken literally
161 001044 172127 040200 addf #1,ac1 ; as float
162 001050 172127 040200 addf #1.,ac1 ; as float
163 001054 172127 040200 addf #1.0,ac1 ; as float
164 001060 172127 000001 addf #^D1,ac1 ; literally
165 001064 173027 000001 subf #<1>,ac0 ; literally
166 001070 172127 000002 addf #<1+1>,ac1 ; literally
test-float.mac:167: ***ERROR Invalid addressing mode (1st operand, fsrc: Invalid expression after '#')
167 subf #<1.0>,ac0 ; error
168 001074 172127 040300 addf #1.5,ac1 ; as float
169 001100 172127 140263 addd #-1.4,ac1 ; as float
170 001104 173027 040200 subf #<^F 1.0>,ac0 ; as float
test-float.mac:171: ***ERROR Invalid addressing mode (1st operand, fsrc: Invalid expression after '#')
171 subf #<^D 1.0>,ac0 ; error
172 001110 173027 000001 subf #<^D 1>,ac0 ; literally
173 001114 173027 000002 subf #^D<1+1>,ac0 ; literally
174 001120 173027 000002 subf #^D 1+1 ,ac0 ; literally
175 001124 173027 042572 subf #1e3,ac0 ; as float
176 001130 173027 042572 subf #1e 3,ac0 ; accepted by MACRO11 as 1E3 (but not 1 e3, 1 e 3)
177 000001 a = 1
178 000003 e3 = 3
179 001134 173027 000001 subf #a,ac0 ; a interpreted as bit pattern
180 001140 173027 000001 subf #<a>,ac0 ; a interpreted as bit pattern
181 001144 173027 000003 subf #e3,ac0 ; e3 is the label
test-float.mac:182: ***ERROR Invalid addressing mode (1st operand, fsrc: Invalid expression after '#')
182 subf #<1e3>,ac0 ; error N
183
test-float.mac:184: ***ERROR Junk at end of line ('5 ; bad: ')
184 001150 170627 000002 absf #2.5 ; bad: operand is destination
test-float.mac:185: ***ERROR Junk at end of line ('5 ; bad: ')
185 001154 170527 000002 tstd #2.5 ; bad: operand is considered FDST by the arch handbook
test-float.mac:186: ***ERROR Junk at end of line ('5 ; bad: junk')
186 001160 174027 000002 stf ac0,#2.5 ; bad: junk at end of line
187 001164 174027 000002 stf ac0,#2 ; doesn't makes sense but MACRO11 allows it
188
189 ; Test immediate source argument for instructions that have one (src or fsrc)
190
191 001170 172027 040200 addd #1,ac0 ; float
192 001174 172027 040200 addf #1,ac0 ; float
193 001200 173427 040200 cmpd #1,ac0 ; float
194 001204 173427 040200 cmpf #1,ac0 ; float
195 001210 174427 040200 divd #1,ac0 ; float
196 001214 174427 040200 divf #1,ac0 ; float
197 001220 177427 040200 ldcdf #1,ac0 ; float
198 001224 177427 040200 ldcfd #1,ac0 ; float
199 001230 177027 000001 ldcid #1,ac0 ; integer
200 001234 177027 000001 ldcif #1,ac0 ; integer
201 001240 177027 000001 ldcld #1,ac0 ; integer
202 001244 177027 000001 ldclf #1,ac0 ; integer
203 001250 172427 040200 ldd #1,ac0 ; float
204 001254 172427 040200 ldf #1,ac0 ; float
205 001260 176427 000001 ldexp #1,ac0 ; integer
206 001264 171427 040200 modd #1,ac0 ; float
207 001270 171427 040200 modf #1,ac0 ; float
208 001274 171027 040200 muld #1,ac0 ; float
209 001300 171027 040200 mulf #1,ac0 ; float
210 001304 173027 040200 subd #1,ac0 ; float
211 001310 173027 040200 subf #1,ac0 ; float
212
213 .end
213
Symbol table
. 000640R 001 AC0 =%000000 E3 = 000003
. 001314R 001 AC0 =%000000 E3 = 000003
A = 000001 AC1 =%000001 F2 =%000002
Program sections:
. ABS. 000000 000 (RW,I,GBL,ABS,OVR,NOSAV)
000640 001 (RW,I,LCL,REL,CON,NOSAV)
001314 001 (RW,I,LCL,REL,CON,NOSAV)

View File

@@ -82,22 +82,68 @@
; V05.06
.word ^F 0.994140625 ; (2**9-3)/2**9 040176 040177
.flt4 0.994140625
.flt4 0.994140625 ; same-> 040176 100000 0 0
.word ^F 0.998046875 ; (2**9-1)/2**9 040177 040200
.flt4 0.998046875
.flt4 0.998046875 ; same-> 040177 100000 0 0
.word ^F 1.00390625 ; (2**8+1)/2**8 040200 040201
.flt4 1.00390625
.flt4 1.00390625 ; same-> 040200 100000 0 0
.word ^F 1.01171875 ; (2**8+3)/2**8 040201 040202
.flt4 1.01171875
.flt4 1.01171875 ; same-> 040201 100000 0 0
.flt4 1.701411834604692307e+38 ; 077777 177777 177777 177777
.FLT4 170141183460469230551095682998472802304 ; 2**127-2**70
.FLT4 170141183460469230564930741053754966015 ; 2**127-(2**70-2**64+2**62+1)
.FLT4 170141183460469230564930741053754966016 ; 2**127-(2**70-2**64+2**62+2)
.flt4 1.0000000000000000138777878078144567552953958511353 ; 0040200 0000000 0000000 0000000
.flt4 1.0000000000000000416333634234433702658861875534058 ; 0040200 0000000 0000000 0000001
.flt4 0.99999999999999997918331828827831486705690622329712 ; 0040177 0177777 0177777 0177776
.flt4 0.99999999999999999306110609609277162235230207443237 ; 0040177 0177777 0177777 0177777
.flt4 100E-2 ; 040200 000000 000000 000000
.flt4 1.0E5 ; 044303 050000 000000 000000
.flt4 1.0E10 ; 050425 001371 000000 000000
.flt4 1.0E20 ; 060655 074353 142654 061000
.flt4 1.0E30 ; 071111 171311 146404 063517
.flt4 1.0E38 ; 077626 073231 050265 006611
.flt4 1.0E-5 ; 034047 142654 043433 043604
.flt4 1.0E-10 ; 027733 163376 147275 166726
.flt4 1.0E-20 ; 017474 162410 062222 010433
.flt4 1.0E-30 ; 007242 041137 173536 012374
.flt4 1.0E-38 ; 000531 143734 166523 143442
.flt4 3681129745421959167 ; 057514 054000 000000 000000
.flt4 3681129745421959168 ; 0x3316000000000000 057514 054000 000000 000000
.flt4 3681129745421959169 ; 057514 054000 000000 000000
.flt4 3681129745421959170 ; 057514 054000 000000 000000
.flt4 14757170078986272767 ; 060114 146000 000000 000000
.flt4 14757170078986272768 ; 0xCCCC000000000000 060114 146000 000000 000000
.flt4 14757170078986272769 ; 060114 146000 000000 000000
.flt4 14757170078986272780 ; 060114 146000 000000 000000
.flt4 3.1415926535897932384626433 ; 040511 007732 121041 064302
; Try some possibly incomplete numbers
.flt4 + ; bad
.flt4 +1 ; ok
.flt4 +E1 ; bad
.flt4 - ; bad
.flt4 -1 ; ok
.flt4 -1. ; ok
.flt4 -1.. ; bad
.flt4 -E1 ; bad
.flt4 +. ; bad
.flt4 -. ; bad
.flt4 . ; bad
.flt4 .. ; bad
.flt4 .E10 ; bad
; Several ways to define a name for the fpp registers
ac0 = r0
@@ -127,7 +173,7 @@ f2 = %2
subf #^D<1+1>,ac0 ; literally
subf #^D 1+1 ,ac0 ; literally
subf #1e3,ac0 ; as float
subf #1e 3,ac0 ; TODO: accepted by MACRO11 as 1E3 (but not 1 e3, 1 e 3)
subf #1e 3,ac0 ; accepted by MACRO11 as 1E3 (but not 1 e3, 1 e 3)
a = 1
e3 = 3
subf #a,ac0 ; a interpreted as bit pattern

View File

@@ -4,13 +4,13 @@
4 ;
5
6 ; This should get listed (list_level 1)
7 .nlist
9 .list
10 ; This should get listed (list_level 1)
11 .list
12 ; This should get listed (list_level 2)
13 .nlist
14 ; This should get listed (list_level 1)
15 .nlist
21 .list
22 ; This should get listed (list_level 1)
22

View File

@@ -49,15 +49,69 @@
1 ; <bro<ken>
35 000000 braket <bro<ken> string
1 ; <bro<ken>
35
36
37 ;
38 ; Test that commas work with default arguments
39 ;
40 .macro tstarg a1,a2=default,a3,a4
41 .narg label ; second arg is "a2"
42 .endm
43
44 000000 start: tstarg ; 0 args
1 000000 .narg label ; second arg is "default"
45 000000 tstarg 123 ; 1 arg
1 000001 .narg label ; second arg is "default"
46 000000 tstarg 1, ; 2 args
1 000002 .narg label ; second arg is ""
47 000000 tstarg ,2 ; 2 args
1 000002 .narg label ; second arg is "2"
48 000000 tstarg , ; 2 args
1 000002 .narg label ; second arg is ""
49 000000 tstarg ,, ; 3 args
1 000003 .narg label ; second arg is "default"
50 000000 tstarg 1,, ; 3 args
1 000003 .narg label ; second arg is "default"
51 000000 tstarg ,,3 ; 3 args
1 000003 .narg label ; second arg is "default"
52 000000 tstarg 1,,3 ; 3 args
1 000003 .narg label ; second arg is "default"
53 000000 tstarg 1,2,3 ; 3 args
1 000003 .narg label ; second arg is "2"
54
55 ;
56 ; Test default args and strange commas
57 ;
58 .macro tstdef a=1,b=2
59 .word a,b
60 .endm
61
62 000000 tstdef
1 000000 000001 000002 .word 1,2
63 000004 tstdef 4,5
1 000004 000004 000005 .word 4,5
64 000010 tstdef 4,5,6 ; Excess argument is ignored
1 000010 000004 000005 .word 4,5
65 000014 tstdef b=42
1 000014 000001 000042 .word 1,42
66 000020 tstdef a=73
1 000020 000073 000002 .word 73,2
67 000024 tstdef ,b=11
1 000024 000001 000011 .word 1,11
68 000030 tstdef a=5,b=4
1 000030 000005 000004 .word 5,4
69 000034 tstdef ,a=5,b=4 ; Strange case seen in some sources
1 000034 000005 000004 .word 5,4
70 000040 tstdef a=5,a=4 ; Duplicate keyword argument -- legal!
1 000040 000004 000002 .word 4,2
70
Symbol table
. 000000R 001 LABEL = 000003 START 000000R 001
. 000044R 001 LABEL = 000003 START 000000R 001
Program sections:
. ABS. 000000 000 (RW,I,GBL,ABS,OVR,NOSAV)
000000 001 (RW,I,LCL,REL,CON,NOSAV)
000044 001 (RW,I,LCL,REL,CON,NOSAV)

View File

@@ -33,3 +33,38 @@ start: tstarg ; 0 args
braket ^/broken
braket <bro<ken>
braket <bro<ken> string
;
; Test that commas work with default arguments
;
.macro tstarg a1,a2=default,a3,a4
.narg label ; second arg is "a2"
.endm
start: tstarg ; 0 args
tstarg 123 ; 1 arg
tstarg 1, ; 2 args
tstarg ,2 ; 2 args
tstarg , ; 2 args
tstarg ,, ; 3 args
tstarg 1,, ; 3 args
tstarg ,,3 ; 3 args
tstarg 1,,3 ; 3 args
tstarg 1,2,3 ; 3 args
;
; Test default args and strange commas
;
.macro tstdef a=1,b=2
.word a,b
.endm
tstdef
tstdef 4,5
tstdef 4,5,6 ; Excess argument is ignored
tstdef b=42
tstdef a=73
tstdef ,b=11
tstdef a=5,b=4
tstdef ,a=5,b=4 ; Strange case seen in some sources
tstdef a=5,a=4 ; Duplicate keyword argument -- legal!

View File

@@ -22,16 +22,14 @@ test-operands.mac:15: ***ERROR Junk at end of line (',0 ; bad')
19 000020 104377 emt 255.
test-operands.mac:20: ***ERROR Literal operand too large (256. > 255.)
20 000022 104377 emt 256. ; too large
test-operands.mac:21: ***ERROR Instruction requires simple literal operand
21 000024 104000 emt . ; must be literal
21 000024 024' 210 emt . ; allowed though strange
22
23 000026 104400 trap 0
24 000030 104401 trap 1
25 000032 104777 trap 255.
test-operands.mac:26: ***ERROR Literal operand too large (256. > 255.)
26 000034 104777 trap 256. ; too large
test-operands.mac:27: ***ERROR Instruction requires simple literal operand
27 000036 104400 trap . ; must be literal
27 000036 036' 211 trap . ; allowed though strange
28
29 ; OC_1GEN
30

View File

@@ -18,13 +18,13 @@
emt 1
emt 255.
emt 256. ; too large
emt . ; must be literal
emt . ; allowed though strange
trap 0
trap 1
trap 255.
trap 256. ; too large
trap . ; must be literal
trap . ; allowed though strange
; OC_1GEN

View File

@@ -0,0 +1,38 @@
1 ;;;;;
2 ;
3 ; Test .radix directive
4 ;
5 000010 .RADIX 8
6 000000 000010 .WORD 10
7 000012 .RADIX 10
8 000002 000012 .WORD 10
9 .RADIX ;Default is 8
10 000004 000010 .WORD 10
11 000002 .RADIX 2
12 000006 000002 .WORD 10
13 000020 .RADIX 16
14 000010 000020 .WORD 10
15 ; Odd but valid
16 000010 .RADIX ^O10 ;Octal 10, i.e., 8
17 000012 000010 .WORD 10
18 000012 .RADIX 5+5
19 000014 000012 .WORD 10
20 ; Errors
test-radix.mac:21: ***ERROR Argument to .RADIX must be 2, 8, 10, or 16
21 000007 .RADIX 7
22 000016 000012 .WORD 10 ;Radix is unchanged after error
test-radix.mac:23: ***ERROR Argument to .RADIX must be constant
23 .RADIX .
24 000020 000012 .WORD 10
24
Symbol table
. 000022R 001
Program sections:
. ABS. 000000 000 (RW,I,GBL,ABS,OVR,NOSAV)
000022 001 (RW,I,LCL,REL,CON,NOSAV)

View File

@@ -0,0 +1,24 @@
;;;;;
;
; Test .radix directive
;
.RADIX 8
.WORD 10
.RADIX 10
.WORD 10
.RADIX ;Default is 8
.WORD 10
.RADIX 2
.WORD 10
.RADIX 16
.WORD 10
; Odd but valid
.RADIX ^O10 ;Octal 10, i.e., 8
.WORD 10
.RADIX 5+5
.WORD 10
; Errors
.RADIX 7
.WORD 10 ;Radix is unchanged after error
.RADIX .
.WORD 10

View File

@@ -0,0 +1,64 @@
1 ;;;;
2 ;
3 ; Test some aspects of syntax.
4 ;
5 ; Reference MACRO11 does very weird things with these errors.
6 ; Fortunately I don't plan to produce exactly the same results in case of errors.
7 ; It seems to recognise an operand where there is none (or none yet)...
8 ;
9 ; AQ 37 000022 012767 000004 177772 mov #4..,r0
10 ; AQ 38 000030 012767 000011 177772 mov #9..,r0
11 ; AQ 39 000036 012767 000004 000000G mov #4.$,r0
12 ; AQU 40 000044 012767 000000 177772 mov #4$.,r0
13 ; 41
14 ; A 42 000052 012767 000004 177772 mov #4..
15 ; A 43 000060 012767 000004 000000G mov #4.$
16 ; AU 44 000066 012767 000000 177772 mov #4$.
17
test-syntax.mac:18: ***ERROR Invalid syntax (comma expected)
18 mov #4..,r0 ; bad syntax
test-syntax.mac:19: ***ERROR Invalid syntax (comma expected)
19 mov #4$.,r0 ; bad syntax
test-syntax.mac:20: ***ERROR Invalid syntax (comma expected)
20 mov #4.$,r0 ; bad syntax
test-syntax.mac:21: ***ERROR Invalid syntax (comma expected)
21 mov #4$$,r0 ; bad syntax
22
test-syntax.mac:23: ***ERROR Invalid syntax (comma expected)
23 mov #4.. ; bad syntax
test-syntax.mac:24: ***ERROR Invalid syntax (comma expected)
24 mov #4$. ; bad syntax
test-syntax.mac:25: ***ERROR Invalid syntax (comma expected)
25 mov #4.$ ; bad syntax
test-syntax.mac:26: ***ERROR Invalid syntax (comma expected)
26 mov #4$$ ; bad syntax
27
28 ;; page 2-4:
29 ;; Multiple expressions used in the operand field of a MACRO-11 statement
30 ;; must be separated by a comma;
31 ;; multiple symbols similarly must be delimited by a valid separator
32 ;; (a comma, tab, and/or space).
33 ;; When the operator field contains an op code, associated operands are
34 ;; always expressions, ...
35
36 000001 a=1
test-syntax.mac:37: ***ERROR Invalid syntax (comma expected)
37 mov #4 r0
test-syntax.mac:38: ***ERROR Invalid syntax (comma expected)
38 mov a r0
39
40 ;; page 2-3:
41 ;; An operator is terminated by a space, tab, or any non-Radix-50 character,
42 000000 017700 000001' mov@a,r0
42
Symbol table
. 000004R 001 A = 000001
Program sections:
. ABS. 000000 000 (RW,I,GBL,ABS,OVR,NOSAV)
000004 001 (RW,I,LCL,REL,CON,NOSAV)

View File

@@ -0,0 +1,42 @@
;;;;
;
; Test some aspects of syntax.
;
; Reference MACRO11 does very weird things with these errors.
; Fortunately I don't plan to produce exactly the same results in case of errors.
; It seems to recognise an operand where there is none (or none yet)...
;
; AQ 37 000022 012767 000004 177772 mov #4..,r0
; AQ 38 000030 012767 000011 177772 mov #9..,r0
; AQ 39 000036 012767 000004 000000G mov #4.$,r0
; AQU 40 000044 012767 000000 177772 mov #4$.,r0
; 41
; A 42 000052 012767 000004 177772 mov #4..
; A 43 000060 012767 000004 000000G mov #4.$
; AU 44 000066 012767 000000 177772 mov #4$.
mov #4..,r0 ; bad syntax
mov #4$.,r0 ; bad syntax
mov #4.$,r0 ; bad syntax
mov #4$$,r0 ; bad syntax
mov #4.. ; bad syntax
mov #4$. ; bad syntax
mov #4.$ ; bad syntax
mov #4$$ ; bad syntax
;; page 2-4:
;; Multiple expressions used in the operand field of a MACRO-11 statement
;; must be separated by a comma;
;; multiple symbols similarly must be delimited by a valid separator
;; (a comma, tab, and/or space).
;; When the operator field contains an op code, associated operands are
;; always expressions, ...
a=1
mov #4 r0
mov a r0
;; page 2-3:
;; An operator is terminated by a space, tab, or any non-Radix-50 character,
mov@a,r0