diff --git a/toolbox/CMakeLists.txt b/toolbox/CMakeLists.txt index 1f5cdfb..89b1066 100644 --- a/toolbox/CMakeLists.txt +++ b/toolbox/CMakeLists.txt @@ -16,9 +16,17 @@ set(TOOLBOX_SRC os_fileinfo.cpp qd.cpp sane.cpp + saneparser.cpp utility.cpp ) +add_custom_command( + OUTPUT saneparser.cpp + COMMAND ragel -p -G2 -o saneparser.cpp "${CMAKE_CURRENT_SOURCE_DIR}/saneparser.rl" + MAIN_DEPENDENCY saneparser.rl +) + + add_library(TOOLBOX_LIB ${TOOLBOX_SRC}) \ No newline at end of file diff --git a/toolbox/sane.cpp b/toolbox/sane.cpp index a0d1e3b..cebb61d 100644 --- a/toolbox/sane.cpp +++ b/toolbox/sane.cpp @@ -1,5 +1,6 @@ #include "qd.h" #include "toolbox.h" +#include "sane.h" #include #include @@ -7,6 +8,7 @@ #include #include +#include #include "stackframe.h" @@ -436,6 +438,61 @@ namespace SANE return 0; } + template + uint16_t fdecimal(const char *name) + { + uint16_t op; + uint32_t decimalPtr; + uint32_t dest; + + + StackFrame<10>(decimalPtr, dest, op); + + uint16_t sgn = memoryReadByte(decimalPtr); + uint16_t exp = memoryReadWord(decimalPtr + 2); + std::string sig; + sig = ToolBox::ReadPString(decimalPtr + 4, false); + + Log(" %s({%c %s e%d}, %08x)\n", + name, + sgn ? '-' : ' ', sig.c_str(), exp, + dest + ); + + extended tmp = 0; + if (sig.length()) + { + if (sig[0] == 'I') + { + tmp = INFINITY; + } + else if (sig[0] == 'N') + { + tmp = NAN; // todo -- nan type + } + else + { + tmp = stold(sig); + + while (exp > 0) + { + tmp = tmp * 10.0; + exp--; + } + while (exp < 0) + { + tmp = tmp / 10.0; + exp++; + } + } + } + if (sgn) tmp = -tmp; + + writenum((DestType)tmp, dest); + + return 0; + } + extern "C" void cpuSetFlagsAbs(UWO f); uint16_t fp68k(uint16_t trap) @@ -540,6 +597,12 @@ namespace SANE break; + case 0x0009: + // fdec2x + return fdecimal("FDEC2X"); + break; + + } @@ -625,10 +688,64 @@ namespace SANE uint32_t fpstr2dec() { + // void str2dec(const char *s,short *ix,decimal *d,short *vp); +#if 0 +#define SIGDIGLEN 20 /* significant decimal digits */ +#define DECSTROUTLEN 80 /* max length for dec2str output */ - printf(stderr, "fpstr2dec is not yet supported.\n"); - exit(1); +struct decimal { + char sgn; /*sign 0 for +, 1 for -*/ + char unused; + short exp; /*decimal exponent*/ + struct{ + unsigned char length; + unsigned char text[SIGDIGLEN]; /*significant digits */ + unsigned char unused; + }sig; +}; +#endif + + uint32_t stringPtr; + uint32_t indexPtr; + uint32_t decimalPtr; + uint32_t validPtr; + + uint16_t valid; + uint16_t index; + decimal d; + + StackFrame<16>(stringPtr, indexPtr, decimalPtr, validPtr); + + index = memoryReadWord(indexPtr); + + std::string str = ToolBox::ReadPString(stringPtr, false); + Log(" FPSTR2DEC(%s, %04x, %08x, %08x)\n", + str.c_str(), index, decimalPtr, validPtr); + + index--; + str2dec(str, index, d, valid); + index++; + + memoryWriteWord(index, indexPtr); + memoryWriteWord(valid, validPtr); + + if (d.sig.length() > 20) + { + // truncate and adjust the exponent + // 1234e0 -> 123e1 -> 12e2 -> 1e3 + // 1234e-1 -> 123e-0 -> 12e1 + int over = d.sig.length() - 20; + d.sig.resize(20); + d.exp += over; + } + + memoryWriteByte(d.sgn, decimalPtr); + memoryWriteByte(0, decimalPtr + 1); + memoryWriteWord(d.exp, decimalPtr + 2); + ToolBox::WritePString(decimalPtr + 4, d.sig); + + return 0; } uint32_t decstr68k(uint16_t trap) diff --git a/toolbox/sane.h b/toolbox/sane.h index 9c32c68..bcc4146 100644 --- a/toolbox/sane.h +++ b/toolbox/sane.h @@ -2,9 +2,19 @@ #define __mpw_sane_h__ #include +#include namespace SANE { + struct decimal { + decimal() : sgn(0), exp(0) {} + int sgn; + int exp; + std::string sig; + }; + + void str2dec(const std::string &s, uint16_t &index, decimal &d, uint16_t &vp); + uint32_t decstr68k(uint16_t trap); uint16_t fp68k(uint16_t trap); } diff --git a/toolbox/saneparser.rl b/toolbox/saneparser.rl new file mode 100644 index 0000000..4ee3c8a --- /dev/null +++ b/toolbox/saneparser.rl @@ -0,0 +1,268 @@ + +#include "sane.h" +#include + +namespace SANE { + +/* + * %{} is a final transition. fpc points to the next character + * %!{} is a final transition to an error state. fpc " " " " + * + */ +%%{ + + machine fpstr; + + + action check { checkpoint = fpc; } + + nantype = + '(' + digit* ${ nantype = nantype * 10 + fc - '0'; } + ')' + $!{ nantype = 0; } + %check + ; + + nan = 'NAN'i + >{ nan = true; } + %check + %!check + nantype? + ; + + infinity = 'INF'i + >{ nan = true; } + %{ nan = false; infinity = true; checkpoint = fpc; } + %!{ nan = false; infinity = true; checkpoint = fpc; }; + + exponent = + [eE] + [+\-]? ${ if (fc == '-') negative_exp = true; } + digit+ ${ exp = exp * 10 + fc - '0'; } + %check + %!check + ; + + significand = + ( + ( + digit+ ${ + // don't push leading 0s + if (fc != '0' || siga.size()) + siga.push_back(fc); + } + ( '.' digit* ${ sigb.push_back(fc); })? + ) + | + ( + '.' + digit+ ${ sigb.push_back(fc); } + ) + ) + %check + %!check + ; + + finite_number = significand exponent?; + + unsigned_decimal = finite_number | infinity | nan; + + left_decimal = + [+\-]? ${ if (fc == '-') negative = true; } + unsigned_decimal + ; + + decimal_number = [ \t]* left_decimal; + + main := decimal_number; + +}%% + + +std::string normalize(std::string &a, std::string &b, int &exponent) +{ + int pos; + + // 1 = 1e0 10 = 1e1 + // 12 = 12e0 + // 123 = 123e0 + + // 1.1 = 11e-1 + // 0.1 = 1e-1 + + // remove trailing 0s + while (b.size() && b.back() == '0') + b.pop_back(); + + int bits = 0; + if (a.length()) bits |= 0x01; + if (b.length()) bits |= 0x02; + + std::string out; + switch(bits) + { + case 0x00: + // should never happen... + break; + + case 0x01: + // a only. + // remove trailing 0s and add 1 exp for each. + while (a.length() && a.back() == '0') + { + a.pop_back(); + exponent++; + } + out = a; + break; + + case 0x02: + // b only. + //.001 = e1-3 + exponent -= b.length(); + pos = b.find_first_not_of('0'); + out = b.substr(pos); + break; + + case 0x03: + // a and b + // 10.01 = 1001e-2 + exponent -= b.length(); + out = a; + out += b; + break; + + } + if (out.empty()) out = "0"; + return out; +} + + + + +void str2dec(const std::string &s, uint16_t &index, decimal &d, uint16_t &vp) +{ +%%write data; + + bool infinity = false; + bool nan = false; + int nantype = 0; + bool negative = false; + bool negative_exp = false; + int exp = 0; + std::string siga, sigb; + + if (index >= s.size()) return; + + /* + char *p = s.c_str(); + char *pe = p + s.size(); + char *eof = pe; + char *checkpoint = p; + */ + auto p = s.begin(); + auto checkpoint = s.begin(); + + auto pe = s.end(); + auto eof = s.end(); + + int cs; + + p += index; + + %%write init; + + %%write exec; + + d.sgn = negative ? 1 : 0; + + if (infinity) + { + d.sig = "I"; + } + else if (nan) + { + d.sig = "N"; + if (nantype) + { + const char *hexstr = "0123456789abcdef"; + // 4-byte hex + d.sig.push_back(hexstr[(nantype >> 24) & 0xff]); + d.sig.push_back(hexstr[(nantype >> 16) & 0xff]); + d.sig.push_back(hexstr[(nantype >> 8) & 0xff]); + d.sig.push_back(hexstr[(nantype >> 0) & 0xff]); + } + } + else + { + d.sig = normalize(siga, sigb, exp); + d.exp = negative_exp ? -exp : exp; + } + + vp = cs != fpstr_error; + index = checkpoint - s.begin(); + + return; + #if 0 + + + printf("%s\n", str); + + bool valid = cs != fpstr_error; + int index = checkpoint - str; + + printf("infinity: %d\n", infinity); + printf(" nan: %d\n", nan); + printf(" nantype: %d\n", nantype); + + if (negative_exp) exp = -exp; + + printf(" exp: %d\n", exp); + printf("negative: %d\n", negative); + printf(" sig: %s.%s\n", siga.c_str(), sigb.c_str()); + printf(" valid: %d\n", valid); + printf(" index: %d\n", index); + printf("\n"); + + + /* + * now we need to normalize the significand / exponent + * 1.2 e0 -> 12 e-1 + * 1000 e0 -> 1 e 3 + */ + + std::string sig = normalize(siga, sigb, exp); + + printf("normalized: %s e %d\n", sig.c_str(), exp); + #endif +} + +} // namespace + +#ifdef MAIN +#include + +int main(int argc, char **argv) +{ + for (int i = 1; i < argc; ++i) + { + std::string s = argv[i]; + SANE::decimal d; + short index = 0; + short valid = 0; + + SANE::str2dec(s, index, d, valid); + + printf("index: %d\n", index); + printf("valid: %d\n", valid); + printf(" sign: %d\n", d.sgn); + printf(" exp: %d\n", d.exp); + printf(" sig: %s\n", d.sig.c_str()); + printf("\n"); + } + + +} + +#endif