From 56fb5d9743dd626d4efb30d601f2f23dc92b5c33 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 25 Feb 2015 15:33:59 -0500 Subject: [PATCH] Implement FX2DEC. Not perfect, but good enough I think. --- test/makefile | 15 +++- test/nan.asm | 35 ++++++++ test/test_sane.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++ toolbox/sane.cpp | 171 +++++++++++++++++++++++++++++++---- 4 files changed, 431 insertions(+), 19 deletions(-) create mode 100644 test/nan.asm create mode 100644 test/test_sane.c diff --git a/test/makefile b/test/makefile index 3b6d662..0c5771b 100644 --- a/test/makefile +++ b/test/makefile @@ -5,7 +5,8 @@ MAKEFLAGS += --no-builtin-rules .SUFFIXES: .SECONDARY: -MPW ?= mpw +MPWFLAGS = -DMPWVersion=3.2 +MPW ?= mpw LIBS = \ {Libraries}Stubs.o \ @@ -25,7 +26,7 @@ LDFLAGS = -w -c 'MPS ' -t MPST \ SCFLAGS = -p TARGETS = test_new_handle test_new_handle_2 test_new_pointer test_volumes \ - test_createresfile test_hwpriv + test_createresfile test_hwpriv test_sane all : $(TARGETS) @@ -44,10 +45,16 @@ clean : # test_volumes : o/test_volumes.o # mpw Link $(LDFLAGS) -o $@ $^ $(LIBS) +test_sane: o/nan.o o/test_sane.o + $(MPW) $(MPWFLAGS) Link $(LDFLAGS) -o $@ $^ $(LIBS) % : o/%.o - $(MPW) Link $(LDFLAGS) -o $@ $^ $(LIBS) + $(MPW) $(MPWFLAGS) Link $(LDFLAGS) -o $@ $^ $(LIBS) o/%.o : %.c - $(MPW) SC $(SCFLAGS) $< -o $@ + $(MPW) $(MPWFLAGS) SC $(SCFLAGS) $< -o $@ + +o/%.o : %.asm + $(MPW) $(MPWFLAGS) Asm $(ASMFLAGS) $< -o $@ + diff --git a/test/nan.asm b/test/nan.asm new file mode 100644 index 0000000..906a0e2 --- /dev/null +++ b/test/nan.asm @@ -0,0 +1,35 @@ + + case on + + export nan, __inf, inf + +nan proc + ; The MPW 3.2 version of nan is broken. This what it's supposed to do. + ; extended (80-bit) returns via d0, d1, a0 + + MOVEM.L data,D0/D1/A0 + MOVE.W #$4000,D1 + MOVE.B $0004(A7),D1 + BNE.S swap + MOVE.B #$15,D1 +swap + SWAP D1 + RTS + + entry inf + entry __inf + +inf +__inf + + MOVEM.L data,D0/D1/A0 + RTS + +data + dc.l $00007fff + dc.l $00000000 + dc.l $00000000 + + end + endp + diff --git a/test/test_sane.c b/test/test_sane.c new file mode 100644 index 0000000..786d3fe --- /dev/null +++ b/test/test_sane.c @@ -0,0 +1,229 @@ +#include +#include + +#include + +//extern pascal extended NAN(short x); + +void dump_decimal(const decimal *d) +{ + fprintf(stdout, "%d : %d : %.*s\n", + d->sgn, d->exp, d->sig.length, d->sig.text); +} + + +void test_fxc2dec(void) +{ + decimal d; + decform df; + + df.style = 0; + df.digits = 100; + num2dec(&df, 1.125, &d); + dump_decimal(&d); + // 0 : -18 : 1125000000000000000 + + df.style = 0; + df.digits = 10; + num2dec(&df, 1.125, &d); + dump_decimal(&d); + // 0 : -9 : 1125000000 + + df.style = 0; + df.digits = 2; + num2dec(&df, 1.125, &d); + dump_decimal(&d); + // 0 : -1 : 11 + + + + df.style = 1; + df.digits = 100; + num2dec(&df, 1.125, &d); + dump_decimal(&d); + // 0 : -18 : 1125000000000000000 + + df.style = 1; + df.digits = 10; + num2dec(&df, 1.125, &d); + dump_decimal(&d); + // 0 : -10 : 11250000000 + + + df.style = 1; + df.digits = 2; + num2dec(&df, 1.125, &d); + dump_decimal(&d); + //0 : -2 : 112 + + // + + df.style = 0; + df.digits = 100; + num2dec(&df, 1e25, &d); + dump_decimal(&d); + // 0 : 7 : 1000000000000000000 + + df.style = 0; + df.digits = 10; + num2dec(&df, 1e25, &d); + dump_decimal(&d); + // 0 : 16 : 1000000000 + + df.style = 0; + df.digits = 2; + num2dec(&df, 1e25, &d); + dump_decimal(&d); + // 0 : 24 : 10 + + + + df.style = 1; + df.digits = 100; + num2dec(&df, 1e25, &d); + dump_decimal(&d); + // 0 : 7 : 1000000000000000000 + + df.style = 1; + df.digits = 10; + num2dec(&df, 1e25, &d); + dump_decimal(&d); + // 0 : 7 : 1000000000000000000 + + + df.style = 1; + df.digits = 2; + num2dec(&df, 1e25, &d); + dump_decimal(&d); + //0 : 7 : 1000000000000000000 + + + + df.style = 1; + df.digits = 10; + num2dec(&df, 0, &d); + dump_decimal(&d); + // + + df.style = 0; + df.digits = 10; + num2dec(&df, 0, &d); + dump_decimal(&d); + // + + + df.style = 1; + df.digits = 0; + num2dec(&df, -0.0, &d); + dump_decimal(&d); + // + + df.style = 0; + df.digits = 0; + num2dec(&df, -0.0, &d); + dump_decimal(&d); + // + + + df.style = 1; + df.digits = 0; + num2dec(&df,nan(1), &d); + dump_decimal(&d); + // + + df.style = 0; + df.digits = 0; + num2dec(&df, nan(2), &d); + dump_decimal(&d); + // + + + + df.style = 1; + df.digits = 10; + num2dec(&df,-nan(3), &d); + dump_decimal(&d); + // + + df.style = 0; + df.digits = 10; + num2dec(&df, nan(4), &d); + dump_decimal(&d); + // + + + df.style = 1; + df.digits = 10; + num2dec(&df,-inf(), &d); + dump_decimal(&d); + // + + df.style = 0; + df.digits = 10; + num2dec(&df, -inf(), &d); + dump_decimal(&d); + + + df.style = 1; + df.digits = 0; + num2dec(&df, inf(), &d); + dump_decimal(&d); + // + + df.style = 0; + df.digits = 0; + num2dec(&df, inf(), &d); + dump_decimal(&d); + + + df.style = 0; + df.digits = 10; + num2dec(&df, 0.125, &d); + dump_decimal(&d); + // + + df.style = 1; + df.digits = 10; + num2dec(&df, 0.125, &d); + dump_decimal(&d); + // + + df.style = 0; + df.digits = 4; + num2dec(&df, 0.00000125, &d); + dump_decimal(&d); + // s/b -9 1250 + + df.style = 1; + df.digits = 4; + num2dec(&df, 0.00000125, &d); + dump_decimal(&d); + // s/b -4 0 + + + + +} + +int main(int argc, char **argv) +{ + extended x; + decimal d; + + short index; + short valid; + + (void)argc; + (void)argv; + + index = 0; + valid = 0; + str2dec("1.125", &index, &d, &valid); + x = dec2num(&d); + + + + test_fxc2dec(); + + return 0; +} diff --git a/toolbox/sane.cpp b/toolbox/sane.cpp index f68adce..9dbca98 100644 --- a/toolbox/sane.cpp +++ b/toolbox/sane.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "stackframe.h" @@ -50,6 +51,7 @@ namespace SANE using std::to_string; using std::fpclassify; using std::signbit; +using std::abs; using its_complicated::to_string; using its_complicated::fpclassify; @@ -310,6 +312,72 @@ using its_complicated::signbit; ToolBox::WritePString(address + _sig, sig); } + void format_f(extended x, int precision, std::string &mm, std::string &nn) + { + char *buffer = nullptr; + + mm.clear(); + nn.clear(); + + if (precision < 0) precision = 0; + int length = asprintf(&buffer, "%.*Lf", precision, x); + std::string tmp(buffer, length); + free(buffer); + + auto dot = tmp.find('.'); + if (dot == tmp.npos) + { + mm = std::move(tmp); + } + else + { + mm = tmp.substr(0, dot); + nn = tmp.substr(dot + 1); + } + + // skip mm if it's 0 + if (mm.length() == 1 && mm.front() == '0') mm.clear(); + + // skip nn if it's 0000... + if (std::all_of(nn.begin(), nn.end(), [](char c){ return c == '0'; })) + nn.clear(); + } + + void format_e(extended x, int precision, std::string &mm, std::string &nn, int &exp) + { + char *buffer = nullptr; + + exp = 0; + mm.clear(); + nn.clear(); + + if (precision < 0) precision = 0; + if (precision > 19) precision = 19; + + int length = asprintf(&buffer, "%.*Le", precision, x); + // output mm . nn e[+-]exp + // mm e[+-]exp + std::string tmp(buffer, length); + free(buffer); + + auto dot = tmp.find('.'); + auto e = tmp.find('e'); + + if (dot == tmp.npos) + { + mm = tmp.substr(0, e); + } + else + { + mm = tmp.substr(0, dot); + nn = tmp.substr(dot + 1, e - dot - 1); + } + + char sign = tmp[e+1]; + tmp = tmp.substr(e + 2); + exp = std::stoi(tmp); + if (sign == '-') exp = -exp; + } uint16_t fx2dec() { @@ -331,8 +399,6 @@ using its_complicated::signbit; Log(" FX2DEC(%08x, %08x, %08x, %04x)\n", f_adr, a_adr, d_adr, op); - fprintf(stderr, "warning: FX2DEC not yet implemented\n"); - extended s = readnum(a_adr); df = decform::read(f_adr); @@ -353,18 +419,20 @@ using its_complicated::signbit; * [-]mmm[.nnn] */ - // this doesn't entirely apply for fx2dec, though. if (df.digits < 0) df.digits = 0; + if (df.digits > 19) df.digits = 19; fpinfo fpi(s); - //fprintf(stderr, "%02x %02x %08x %016llx\n", fpi.sign, fpi.one, fpi.exp, fpi.sig); + //fprintf(stderr, "%02x %02x %d %016llx\n", fpi.sign, fpi.one, fpi.exp, fpi.sig); + + + d.sgn = signbit(s); // handle infinity, nan as a special case. switch (fpclassify(s)) { case FP_ZERO: - d.sgn = signbit(s); d.sig = "0"; d.write(d_adr); return 0; @@ -374,7 +442,6 @@ using its_complicated::signbit; { char buffer[20]; // 16 + 2 needed snprintf(buffer, 20, "N%016llx", fpi.sig); - d.sgn = signbit(s); d.sig = buffer; d.write(d_adr); } @@ -382,7 +449,6 @@ using its_complicated::signbit; case FP_INFINITE: - d.sgn = signbit(s); d.sig = "I"; d.write(d_adr); return 0; @@ -394,20 +460,95 @@ using its_complicated::signbit; // normal and subnormal handled here.... - #if 0 - if (df.style == decform::FIXEDDECIMAL) + // float decimal: df.digits refers to the total length + // fixed decimal: df.digits refers to the fractional part only. + + s = abs(s); + + + if (s < 1.0 && df.style == decform::FLOATDECIMAL) { - char buffer[decimal::SIGDIGLEN]; - snprintf(buffer, sizeof(buffer), "%.*Lg", df.digits, s); + std::string mm; + std::string nn; + format_e(s, df.digits - 1, mm, nn, d.exp); + d.sig = mm + nn; + + // better be < 0... + if (d.exp < 0) + d.exp -= nn.length(); + + d.write(d_adr); + return 0; } - #endif + else // s > 1 + { - // ugh, really don't want to write this code right now. + std::string mm; + std::string nn; - d.write(d_adr); - return 0; + format_f(s, df.digits, mm, nn); + + if (mm.empty() && nn.empty()) + { + // very large 0. + d.sig = "0"; + d.write(d_adr); + return 0; + } + + // if nn is empty (or 0s), this is a large number, + // and we don't have to worry about the fraction. + if (nn.empty()) + { + d.exp = 0; + + if (df.style == decform::FIXEDDECIMAL) df.digits = 19; + + // limit the length. + if (mm.length() > df.digits) + { + d.exp = mm.length() - df.digits; + mm.resize(df.digits); + } + d.sig = std::move(mm); + + } + else + { + if (df.style == decform::FIXEDDECIMAL) + { + // digits is the total size, mm + nn + // re-format with new precision. + // this is getting repetitive... + + if (mm.length()) + { + int precision = df.digits - mm.length(); + if (precision < 0) precision = 1; + + format_f(s, precision, mm, nn); + } + } + // todo -- if mm is empty and nn has leading 0s, + // drop the leading 0s and adjust the exponent + // accordingly. + + d.sig = mm + nn; + d.exp = -nn.length(); + + if (d.sig.length() > 19) + { + d.exp += (d.sig.length() - 19); + d.sig.resize(19); + } + } + + + d.write(d_adr); + return 0; + } }