/* Bit-exact core conversions and comparisons for Borland's software Real48. */ #include "tdkpin_real48.h" #include "tdkpin_borland_runtime.h" #include enum { REAL48_EXPONENT_BIAS = 129, REAL48_EXPLICIT_FRACTION_BITS = 39, REAL48_SIGN = 0x80, BORLAND_RUNTIME_ERROR_DIVIDE_BY_ZERO = 200, BORLAND_RUNTIME_ERROR_FLOAT_OVERFLOW = 205, BORLAND_RUNTIME_ERROR_INVALID_FLOAT = 207, }; /* Code5 1020:1418..1435 — ArcTan odd-series coefficients, highest first. */ static const BorlandReal48 g_arctan_coefficients[5] = { {{0x7d, 0xe8, 0xa2, 0x8b, 0x2e, 0xba}}, /* -1/11 */ {{0x7d, 0x8e, 0xe3, 0x38, 0x8e, 0x63}}, /* +1/9 */ {{0x7e, 0x49, 0x92, 0x24, 0x49, 0x92}}, /* -1/7 */ {{0x7e, 0xcd, 0xcc, 0xcc, 0xcc, 0x4c}}, /* +1/5 */ {{0x7f, 0xab, 0xaa, 0xaa, 0xaa, 0xaa}}, /* -1/3 */ }; /* Code5 1020:13e8..1417 — ArcTan reduction thresholds and tan/angle pairs. */ static const BorlandReal48 g_arctan_direct_limit = {{0x7e, 0x4a, 0x8e, 0xe9, 0x6f, 0x0c}}; static const BorlandReal48 g_arctan_tan_pi_over_8 = {{0x7f, 0xe7, 0xcf, 0xcc, 0x13, 0x54}}; static const BorlandReal48 g_arctan_tan_pi_over_12 = {{0x7f, 0xf6, 0xf4, 0xa2, 0x30, 0x09}}; static const BorlandReal48 g_pi_over_12 = {{0x7f, 0x6a, 0xc1, 0x91, 0x0a, 0x06}}; static const BorlandReal48 g_arctan_second_threshold = {{0x80, 0xb5, 0x9e, 0x8a, 0x6f, 0x44}}; static const BorlandReal48 g_arctan_tan_pi_over_6 = {{0x80, 0x82, 0x2c, 0x3a, 0xcd, 0x13}}; static const BorlandReal48 g_pi_over_6 = {{0x80, 0x6a, 0xc1, 0x91, 0x0a, 0x06}}; static const BorlandReal48 g_real48_one = {{0x81, 0, 0, 0, 0, 0}}; static const BorlandReal48 g_pi_over_4 = {{0x80, 0x21, 0xa2, 0xda, 0x0f, 0x49}}; static const BorlandReal48 g_pi_over_2 = {{0x81, 0x21, 0xa2, 0xda, 0x0f, 0x49}}; /* Code5 1020:1240..1263 and constants used by Ln at 11bb. */ static const BorlandReal48 g_ln_coefficients[6] = { {{0x7d, 0x8a, 0x9d, 0xd8, 0x89, 0x1d}}, /* 1/13 */ {{0x7d, 0xe9, 0xa2, 0x8b, 0x2e, 0x3a}}, /* 1/11 */ {{0x7d, 0x8e, 0xe3, 0x38, 0x8e, 0x63}}, /* 1/9 */ {{0x7e, 0x49, 0x92, 0x24, 0x49, 0x12}}, /* 1/7 */ {{0x7e, 0xcd, 0xcc, 0xcc, 0xcc, 0x4c}}, /* 1/5 */ {{0x7f, 0xab, 0xaa, 0xaa, 0xaa, 0x2a}}, /* 1/3 */ }; static const BorlandReal48 g_sqrt_one_half = {{0x80, 0xfb, 0x33, 0xf3, 0x04, 0x35}}; static const BorlandReal48 g_half_ln_two = {{0x7f, 0xd2, 0xf7, 0x17, 0x72, 0x31}}; static const BorlandReal48 g_ln_two = {{0x80, 0xd2, 0xf7, 0x17, 0x72, 0x31}}; /* Code5 1020:12d7..1306 — Exp polynomial, highest coefficient first. */ static const BorlandReal48 g_exp_coefficients[8] = { {{0x6d, 0x2e, 0x1d, 0x11, 0x60, 0x31}}, {{0x70, 0x46, 0x2c, 0xfe, 0xe5, 0x7f}}, {{0x74, 0x36, 0x7c, 0x89, 0x84, 0x21}}, {{0x77, 0x53, 0x3c, 0xff, 0xc3, 0x2e}}, {{0x7a, 0xd2, 0x7d, 0x5b, 0x95, 0x1d}}, {{0x7c, 0x25, 0xb8, 0x46, 0x58, 0x63}}, {{0x7e, 0x16, 0xfc, 0xef, 0xfd, 0x75}}, {{0x80, 0xd2, 0xf7, 0x17, 0x72, 0x31}}, }; static const BorlandReal48 g_sqrt_two = {{0x81, 0xfb, 0x33, 0xf3, 0x04, 0x35}}; /* Code5 1020:1191..11ba — Sin odd-series coefficients, highest first. */ static const BorlandReal48 g_sin_coefficients[7] = { {{0x58, 0x9d, 0x39, 0x9f, 0x3f, 0xd7}}, /* -1/15! */ {{0x60, 0x43, 0x9d, 0x30, 0x92, 0x30}}, /* +1/13! */ {{0x67, 0xaa, 0x3f, 0x28, 0x32, 0xd7}}, /* -1/11! */ {{0x6e, 0xb6, 0x2a, 0x1d, 0xef, 0x38}}, /* +1/9! */ {{0x74, 0x0d, 0xd0, 0x00, 0x0d, 0xd0}}, /* -1/7! */ {{0x7a, 0x88, 0x88, 0x88, 0x88, 0x08}}, /* +1/5! */ {{0x7e, 0xab, 0xaa, 0xaa, 0xaa, 0xaa}}, /* -1/3! */ }; static const BorlandReal48 g_two_pi = {{0x83, 0x21, 0xa2, 0xda, 0x0f, 0x49}}; static uint8_t real48_exponent(BorlandReal48 value) { return value.bytes[0]; } static bool real48_negative(BorlandReal48 value) { return (value.bytes[5] & REAL48_SIGN) != 0; } static uint64_t real48_fraction(BorlandReal48 value) { return (uint64_t)value.bytes[1] | ((uint64_t)value.bytes[2] << 8) | ((uint64_t)value.bytes[3] << 16) | ((uint64_t)value.bytes[4] << 24) | ((uint64_t)(value.bytes[5] & 0x7f) << 32); } static uint64_t real48_significand(BorlandReal48 value) { return (UINT64_C(1) << REAL48_EXPLICIT_FRACTION_BITS) | real48_fraction(value); } static BorlandReal48 make_real48( uint8_t exponent, bool negative, uint64_t fraction) { BorlandReal48 result = {{0, 0, 0, 0, 0, 0}}; result.bytes[0] = exponent; result.bytes[1] = (uint8_t)fraction; result.bytes[2] = (uint8_t)(fraction >> 8); result.bytes[3] = (uint8_t)(fraction >> 16); result.bytes[4] = (uint8_t)(fraction >> 24); result.bytes[5] = (uint8_t)((fraction >> 32) | (negative ? REAL48_SIGN : 0)); return result; } static BorlandReal48Result real48_pack_internal( uint64_t internal, int16_t exponent, bool negative) { internal += UINT64_C(0x80); if ((internal & (UINT64_C(1) << 48)) != 0) { internal >>= 1; exponent++; } if (exponent <= 0) { return (BorlandReal48Result){ borland_real48_zero_registers(), false, false}; } if (exponent > UINT8_MAX) { return (BorlandReal48Result){ borland_real48_zero_registers(), true, false}; } uint64_t significand = internal >> 8; uint64_t fraction = significand - (UINT64_C(1) << REAL48_EXPLICIT_FRACTION_BITS); return (BorlandReal48Result){ make_real48((uint8_t)exponent, negative, fraction), false, false}; } static BorlandReal48Result real48_add_with_signs( BorlandReal48 left, BorlandReal48 right, bool right_negative) { uint8_t left_exponent = real48_exponent(left); uint8_t right_exponent = real48_exponent(right); if (right_exponent == 0) { return (BorlandReal48Result){left, false, false}; } if (left_exponent == 0) { right.bytes[5] = (uint8_t)( (right.bytes[5] & 0x7f) | (right_negative ? REAL48_SIGN : 0)); return (BorlandReal48Result){right, false, false}; } BorlandReal48 high = left; BorlandReal48 low = right; bool high_negative = real48_negative(left); bool low_negative = right_negative; if (right_exponent > left_exponent) { high = right; low = left; uint8_t temporary_exponent = left_exponent; left_exponent = right_exponent; right_exponent = temporary_exponent; high_negative = right_negative; low_negative = real48_negative(left); } uint8_t difference = (uint8_t)(left_exponent - right_exponent); if (difference >= 41) { high.bytes[5] = (uint8_t)( (high.bytes[5] & 0x7f) | (high_negative ? REAL48_SIGN : 0)); return (BorlandReal48Result){high, false, false}; } uint64_t high_internal = real48_significand(high) << 8; uint64_t low_internal = (real48_significand(low) << 8) >> difference; uint64_t magnitude; bool result_negative; int16_t result_exponent = left_exponent; if (high_negative == low_negative) { magnitude = high_internal + low_internal; result_negative = high_negative; if ((magnitude & (UINT64_C(1) << 48)) != 0) { magnitude >>= 1; result_exponent++; if (result_exponent > UINT8_MAX) { return (BorlandReal48Result){ borland_real48_zero_registers(), true, false}; } } } else { if (high_internal == low_internal) { return (BorlandReal48Result){ borland_real48_zero_registers(), false, false}; } if (high_internal > low_internal) { magnitude = high_internal - low_internal; result_negative = high_negative; } else { magnitude = low_internal - high_internal; result_negative = low_negative; } while ((magnitude & (UINT64_C(1) << 47)) == 0) { magnitude <<= 1; result_exponent--; if (result_exponent == 0) { return (BorlandReal48Result){ borland_real48_zero_registers(), false, false}; } } } return real48_pack_internal( magnitude, result_exponent, result_negative); } /* 1020:0cd4 — register-form software Real48 addition. */ BorlandReal48Result borland_real48_add_registers( BorlandReal48 left, BorlandReal48 right) { return real48_add_with_signs(left, right, real48_negative(right)); } /* 1020:0cd0 — toggle DI's sign bit, then enter the shared addition body. */ BorlandReal48Result borland_real48_subtract_registers( BorlandReal48 left, BorlandReal48 right) { return real48_add_with_signs(left, right, !real48_negative(right)); } typedef struct { uint64_t low; uint16_t high; } Real48Product80; static Real48Product80 multiply_40_by_40(uint64_t left, uint64_t right) { uint64_t low_product = (uint64_t)(uint32_t)left * (uint32_t)right; uint64_t cross = (left >> 32) * (uint32_t)right + (right >> 32) * (uint32_t)left; uint64_t low = low_product + (cross << 32); uint32_t high = (uint32_t)( (left >> 32) * (right >> 32) + (cross >> 32) + (low < low_product)); return (Real48Product80){low, (uint16_t)high}; } /* 1020:0d97 — exact 40x40 product followed by Borland's eight guard bits. */ BorlandReal48Result borland_real48_multiply_registers( BorlandReal48 left, BorlandReal48 right) { if (real48_exponent(left) == 0 || real48_exponent(right) == 0) { return (BorlandReal48Result){ borland_real48_zero_registers(), false, false}; } Real48Product80 product = multiply_40_by_40( real48_significand(left), real48_significand(right)); bool top_bit_79 = (product.high & 0x8000) != 0; uint8_t shift = top_bit_79 ? 32 : 31; uint64_t internal = (product.low >> shift) | ((uint64_t)product.high << (64 - shift)); int16_t exponent = (int16_t)real48_exponent(left) + real48_exponent(right) - (top_bit_79 ? 128 : 129); return real48_pack_internal( internal, exponent, real48_negative(left) != real48_negative(right)); } /* 1020:0e93 — shared underflow/zero result: AX=BX=DX=0. */ BorlandReal48 borland_real48_zero_registers(void) { return (BorlandReal48){{0, 0, 0, 0, 0, 0}}; } /* 1020:0e9a — restoring division generating 40 result plus eight guard bits. */ BorlandReal48Result borland_real48_divide_registers( BorlandReal48 left, BorlandReal48 right) { if (real48_exponent(right) == 0) { return (BorlandReal48Result){ borland_real48_zero_registers(), false, true}; } if (real48_exponent(left) == 0) { return (BorlandReal48Result){ borland_real48_zero_registers(), false, false}; } uint64_t numerator = real48_significand(left); uint64_t denominator = real48_significand(right); int16_t exponent = (int16_t)real48_exponent(left) - real48_exponent(right) + 129; if (numerator < denominator) { numerator <<= 1; exponent--; } uint64_t internal = 0; uint64_t remainder = numerator; for (uint8_t bit = 0; bit < 48; bit++) { bool quotient_bit = remainder >= denominator; if (quotient_bit) { remainder -= denominator; } internal = (internal << 1) | quotient_bit; remainder <<= 1; } return real48_pack_internal( internal, exponent, real48_negative(left) != real48_negative(right)); } /* * 1020:1031/103b/1045/104f — near arithmetic entries that save and restore * CX:SI:DI around the corresponding core. C by-value operands express that * register-preservation contract directly. */ BorlandReal48Result borland_real48_add_preserving_right( BorlandReal48 left, BorlandReal48 right) { return borland_real48_add_registers(left, right); } BorlandReal48Result borland_real48_subtract_preserving_right( BorlandReal48 left, BorlandReal48 right) { return borland_real48_subtract_registers(left, right); } BorlandReal48Result borland_real48_multiply_preserving_right( BorlandReal48 left, BorlandReal48 right) { return borland_real48_multiply_registers(left, right); } BorlandReal48Result borland_real48_divide_preserving_right( BorlandReal48 left, BorlandReal48 right) { return borland_real48_divide_registers(left, right); } /* 1020:1059 — Pascal Int: clear fractional significand bits toward zero. */ BorlandReal48 borland_real48_integer_part(BorlandReal48 value) { uint8_t exponent = real48_exponent(value); if (exponent >= 168) { return value; } if (exponent <= 128) { return borland_real48_zero_registers(); } uint8_t fractional_bits = (uint8_t)(168 - exponent); uint64_t significand = real48_significand(value); uint64_t mask = ~((UINT64_C(1) << fractional_bits) - 1); uint64_t integer_significand = significand & mask; uint64_t fraction = integer_significand - (UINT64_C(1) << REAL48_EXPLICIT_FRACTION_BITS); return make_real48(exponent, real48_negative(value), fraction); } /* 1020:10aa — Pascal Frac: original minus Int(original). */ BorlandReal48 borland_real48_fractional_part(BorlandReal48 value) { return borland_real48_subtract_registers( value, borland_real48_integer_part(value)) .value; } /* * 1020:10be — Borland Sqrt Newton iteration. The initial exponent transform, * retained input mantissa, divide/add/exponent-decrement step, and convergence * test on the exponent of new-old follow the register routine exactly. */ BorlandReal48 borland_real48_sqrt(BorlandReal48 value) { if (real48_exponent(value) == 0) { return value; } if (real48_negative(value)) { borland_runtime_error(BORLAND_RUNTIME_ERROR_INVALID_FLOAT); } BorlandReal48 guess = value; int8_t halved = (int8_t)(uint8_t)(real48_exponent(value) + 0x80); halved >>= 1; guess.bytes[0] = (uint8_t)((uint8_t)halved + 0x80); uint8_t convergence_exponent = (uint8_t)(guess.bytes[0] - 0x14); while (true) { BorlandReal48 quotient = borland_real48_divide_preserving_right(value, guess).value; BorlandReal48 next = borland_real48_add_preserving_right(quotient, guess).value; next.bytes[0]--; BorlandReal48 difference = borland_real48_subtract_registers(next, guess).value; guess = next; if (difference.bytes[0] < convergence_exponent) { return guess; } } } /* * 1020:1455 — Horner evaluator used by the transcendental routines: * result = 1 + x*(c[n-1] + x*(... + x*c[0])). The target multiplies once * before advancing to each later coefficient, so table order is significant. */ BorlandReal48 borland_real48_polynomial_plus_one( BorlandReal48 argument, const BorlandReal48 *coefficients, uint16_t count) { BorlandReal48 result = coefficients[0]; for (uint16_t index = 0; index < count; index++) { if (index != 0) { result = borland_real48_add_registers( result, coefficients[index]) .value; } result = borland_real48_multiply_registers(result, argument).value; } return borland_real48_add_registers( result, make_real48(0x81, false, 0)) .value; } /* 1020:143c — x * polynomial_plus_one(x*x, table, count). */ BorlandReal48 borland_real48_odd_polynomial( BorlandReal48 argument, const BorlandReal48 *coefficients, uint16_t count) { BorlandReal48 squared = borland_real48_multiply_registers(argument, argument).value; BorlandReal48 polynomial = borland_real48_polynomial_plus_one( squared, coefficients, count); return borland_real48_multiply_registers(polynomial, argument).value; } /* 1020:1436 — fixed-table ArcTan series entry sharing the 143c tail. */ BorlandReal48 borland_real48_arctan_series(BorlandReal48 argument) { return borland_real48_odd_polynomial( argument, g_arctan_coefficients, (uint16_t)(sizeof(g_arctan_coefficients) / sizeof(g_arctan_coefficients[0]))); } /* * 1020:1307 — ArcTan with Borland's fixed argument reductions. Values above * one are inverted; three tan-addition ranges reduce to the direct odd series; * then the reciprocal and original-sign transforms are applied in that order. */ BorlandReal48 borland_real48_arctan(BorlandReal48 argument) { if (real48_exponent(argument) == 0) { return argument; } bool negative = real48_negative(argument); argument.bytes[5] &= 0x7f; bool reciprocal = borland_real48_compare_registers(argument, g_real48_one) >= 0; if (reciprocal) { argument = borland_real48_divide_registers( g_real48_one, argument) .value; } BorlandReal48 result; if (borland_real48_compare_registers( argument, g_arctan_direct_limit) < 0) { result = borland_real48_arctan_series(argument); } else { const BorlandReal48 *tangent; const BorlandReal48 *angle; if (borland_real48_compare_registers( argument, g_arctan_tan_pi_over_8) < 0) { tangent = &g_arctan_tan_pi_over_12; angle = &g_pi_over_12; } else if (borland_real48_compare_registers( argument, g_arctan_second_threshold) < 0) { tangent = &g_arctan_tan_pi_over_6; angle = &g_pi_over_6; } else { tangent = &g_real48_one; angle = &g_pi_over_4; } BorlandReal48 numerator = borland_real48_subtract_preserving_right( argument, *tangent) .value; BorlandReal48 denominator = borland_real48_add_registers( borland_real48_multiply_registers(argument, *tangent).value, g_real48_one) .value; BorlandReal48 reduced = borland_real48_divide_registers(numerator, denominator).value; result = borland_real48_add_registers( borland_real48_arctan_series(reduced), *angle) .value; } if (reciprocal) { result = borland_real48_subtract_registers(g_pi_over_2, result).value; } if (negative && real48_exponent(result) != 0) { result.bytes[5] ^= REAL48_SIGN; } return result; } /* * 1020:11bb — natural logarithm. Normalize the input exponent, center its * mantissa with sqrt(1/2), evaluate the atanh odd series, double it, and add * the signed exponent displacement times ln(2). Results below exponent 67 are * flushed to canonical zero exactly like the final CMP/JAE sequence. */ BorlandReal48 borland_real48_ln(BorlandReal48 argument) { if (real48_exponent(argument) == 0 || real48_negative(argument)) { borland_runtime_error(BORLAND_RUNTIME_ERROR_INVALID_FLOAT); } int8_t exponent_displacement = (int8_t)(uint8_t)(real48_exponent(argument) - 0x81); argument.bytes[0] = 0x81; BorlandReal48 centered = borland_real48_multiply_registers(argument, g_sqrt_one_half).value; BorlandReal48 numerator = borland_real48_subtract_registers(centered, g_real48_one).value; BorlandReal48 denominator = borland_real48_add_registers(centered, g_real48_one).value; BorlandReal48 ratio = borland_real48_divide_registers(numerator, denominator).value; BorlandReal48 mantissa_log = borland_real48_odd_polynomial( ratio, g_ln_coefficients, (uint16_t)(sizeof(g_ln_coefficients) / sizeof(g_ln_coefficients[0]))); mantissa_log.bytes[0]++; mantissa_log = borland_real48_add_registers( mantissa_log, g_half_ln_two) .value; BorlandReal48 exponent_term = borland_real48_multiply_registers( borland_i32_to_real48_registers(exponent_displacement), g_ln_two) .value; BorlandReal48 result = borland_real48_add_registers(mantissa_log, exponent_term).value; return real48_exponent(result) < 0x67 ? borland_real48_zero_registers() : result; } /* * 1020:1264 — exponential. Reduce |x| by ln(2), round twice the quotient, * evaluate the fixed 2^r polynomial, apply sqrt(2) for an odd reduction, and * scale through the Real48 exponent byte. A negative original input takes the * reciprocal only after the positive path is complete. */ BorlandReal48 borland_real48_exp(BorlandReal48 argument) { bool negative = real48_negative(argument); argument.bytes[5] &= 0x7f; BorlandReal48 quotient = borland_real48_divide_registers(argument, g_ln_two).value; if (real48_exponent(quotient) >= 0x88) { borland_runtime_error(BORLAND_RUNTIME_ERROR_FLOAT_OVERFLOW); } BorlandReal48 doubled_quotient = quotient; doubled_quotient.bytes[0]++; BorlandReal48IntegerResult rounded = borland_real48_to_i32_registers(doubled_quotient, true); if (rounded.overflow) { borland_runtime_error(BORLAND_RUNTIME_ERROR_FLOAT_OVERFLOW); } uint16_t reduction = (uint16_t)rounded.value; BorlandReal48 half_reduction = borland_i32_to_real48_registers(rounded.value); if (real48_exponent(half_reduction) != 0) { half_reduction.bytes[0]--; } BorlandReal48 reduced = borland_real48_subtract_registers( quotient, half_reduction) .value; BorlandReal48 result = borland_real48_polynomial_plus_one( reduced, g_exp_coefficients, (uint16_t)(sizeof(g_exp_coefficients) / sizeof(g_exp_coefficients[0]))); if ((reduction & 1) != 0) { result = borland_real48_multiply_registers(result, g_sqrt_two).value; } uint8_t exponent_increment = (uint8_t)(reduction >> 1); uint16_t scaled_exponent = (uint16_t)real48_exponent(result) + exponent_increment; if (scaled_exponent > UINT8_MAX) { borland_runtime_error(BORLAND_RUNTIME_ERROR_FLOAT_OVERFLOW); } result.bytes[0] = (uint8_t)scaled_exponent; if (negative) { result = borland_real48_divide_registers(g_real48_one, result).value; } return result; } /* * 1020:1130 — Sin shared entry inside the containing Cos routine. Reduce by * 2*pi through Frac, fold [0,2*pi) through pi and pi/2, evaluate the odd * series only above exponent 6c, then restore the half-cycle sign. */ BorlandReal48 borland_real48_sin(BorlandReal48 argument) { if (real48_exponent(argument) < 0x6c) { return argument; } BorlandReal48 reduced = argument; BorlandReal48 absolute = argument; absolute.bytes[5] &= 0x7f; if (borland_real48_compare_registers(absolute, g_two_pi) >= 0) { BorlandReal48 quotient = borland_real48_divide_preserving_right(argument, g_two_pi).value; reduced = borland_real48_multiply_preserving_right( borland_real48_fractional_part(quotient), g_two_pi) .value; } if (real48_negative(reduced)) { reduced = borland_real48_add_preserving_right(reduced, g_two_pi).value; } BorlandReal48 pi = g_two_pi; pi.bytes[0]--; bool positive_half_cycle = borland_real48_compare_registers(reduced, pi) < 0; if (!positive_half_cycle) { reduced = borland_real48_subtract_preserving_right(reduced, pi).value; } BorlandReal48 half_pi = pi; half_pi.bytes[0]--; if (borland_real48_compare_registers(reduced, half_pi) >= 0) { reduced.bytes[5] |= REAL48_SIGN; reduced = borland_real48_add_registers(reduced, pi).value; } if (real48_exponent(reduced) >= 0x6c) { reduced = borland_real48_odd_polynomial( reduced, g_sin_coefficients, (uint16_t)(sizeof(g_sin_coefficients) / sizeof(g_sin_coefficients[0]))); } if (!positive_half_cycle && real48_exponent(reduced) != 0) { reduced.bytes[5] ^= REAL48_SIGN; } return reduced; } /* 1020:111d — Cos prelude computes pi/2-x, then enters Sin at 1130. */ BorlandReal48 borland_real48_cos(BorlandReal48 argument) { BorlandReal48 minus_half_pi = g_pi_over_2; minus_half_pi.bytes[5] |= REAL48_SIGN; BorlandReal48 shifted = borland_real48_add_registers(argument, minus_half_pi).value; if (real48_exponent(shifted) != 0) { shifted.bytes[5] ^= REAL48_SIGN; } return borland_real48_sin(shifted); } /* * 1020:0f28 — unsigned Real48 magnitude ordering materialized from the flags. * Equal zero exponents compare equal without inspecting the remaining bytes, * exactly matching OR AL,AL followed by the early RET. */ int16_t borland_real48_compare_magnitude_registers( BorlandReal48 left, BorlandReal48 right) { uint8_t left_exponent = real48_exponent(left); uint8_t right_exponent = real48_exponent(right); if (left_exponent != right_exponent) { return left_exponent < right_exponent ? -1 : 1; } if (left_exponent == 0) { return 0; } uint64_t left_fraction = real48_fraction(left); uint64_t right_fraction = real48_fraction(right); if (left_fraction == right_fraction) { return 0; } return left_fraction < right_fraction ? -1 : 1; } /* 1020:0f11 — sign-aware comparison, reversing magnitude order if negative. */ int16_t borland_real48_compare_registers( BorlandReal48 left, BorlandReal48 right) { bool left_negative = real48_negative(left); bool right_negative = real48_negative(right); if (left_negative != right_negative) { return left_negative ? -1 : 1; } int16_t magnitude = borland_real48_compare_magnitude_registers(left, right); return left_negative ? (int16_t)-magnitude : magnitude; } /* * 1020:0f3b — signed DX:AX integer to register-form Real48. Every int32_t is * exact because Real48 carries forty significand bits. */ BorlandReal48 borland_i32_to_real48_registers(int32_t value) { if (value == 0) { return borland_real48_zero_registers(); } bool negative = value < 0; uint32_t magnitude = negative ? (uint32_t)(0U - (uint32_t)value) : (uint32_t)value; uint8_t highest_bit = 0; for (uint32_t scan = magnitude; scan > 1; scan >>= 1) { highest_bit++; } uint64_t significand = (uint64_t)magnitude << (REAL48_EXPLICIT_FRACTION_BITS - highest_bit); uint64_t fraction = significand - (UINT64_C(1) << REAL48_EXPLICIT_FRACTION_BITS); return make_real48( (uint8_t)(REAL48_EXPONENT_BIAS + highest_bit), negative, fraction); } /* * 1020:0f77 — Real48 to signed DX:AX. `round` is the entry's CH byte: zero * truncates, nonzero adds the first discarded bit before the signed-range * check. Thus exact halves round away from zero, as Borland Round requires. */ BorlandReal48IntegerResult borland_real48_to_i32_registers( BorlandReal48 value, bool round) { uint8_t exponent = real48_exponent(value); if (exponent == 0) { return (BorlandReal48IntegerResult){0, false}; } int16_t highest_bit = (int16_t)exponent - REAL48_EXPONENT_BIAS; if (highest_bit >= 32) { return (BorlandReal48IntegerResult){0, true}; } uint64_t significand = real48_significand(value); int16_t shift = REAL48_EXPLICIT_FRACTION_BITS - highest_bit; uint64_t magnitude = shift >= 64 ? 0 : significand >> shift; if (round && shift > 0 && shift <= 40 && ((significand >> (shift - 1)) & 1) != 0) { magnitude++; } bool negative = real48_negative(value); uint64_t limit = negative ? UINT64_C(0x80000000) : (uint64_t)INT32_MAX; bool overflow = magnitude > limit; if (!negative) { int32_t candidate = magnitude == UINT64_C(0x80000000) ? INT32_MIN : (int32_t)(uint32_t)magnitude; return (BorlandReal48IntegerResult){candidate, overflow}; } uint32_t twos_complement = 0U - (uint32_t)magnitude; int32_t candidate = twos_complement == UINT32_C(0x80000000) ? INT32_MIN : (int32_t)twos_complement; return (BorlandReal48IntegerResult){candidate, overflow}; } /* 1020:1007 — far comparison entry around the near flags-only core. */ int16_t borland_real48_compare( BorlandReal48 left, BorlandReal48 right) { return borland_real48_compare_registers(left, right); } static BorlandReal48 require_real48_arithmetic( BorlandReal48Result result) { if (result.divide_by_zero) { borland_runtime_error(BORLAND_RUNTIME_ERROR_DIVIDE_BY_ZERO); } if (result.overflow) { borland_runtime_error(BORLAND_RUNTIME_ERROR_FLOAT_OVERFLOW); } return result.value; } /* 1020:0fe5 — far Add wrapper; carry maps to Runtime Error 205. */ BorlandReal48 borland_real48_add( BorlandReal48 left, BorlandReal48 right) { return require_real48_arithmetic( borland_real48_add_registers(left, right)); } /* 1020:0feb — far Subtract wrapper around the sign-toggle entry. */ BorlandReal48 borland_real48_subtract( BorlandReal48 left, BorlandReal48 right) { return require_real48_arithmetic( borland_real48_subtract_registers(left, right)); } /* 1020:0ff1 — copy AX:BX:DX to CX:SI:DI, then share Multiply. */ BorlandReal48 borland_real48_square(BorlandReal48 value) { return require_real48_arithmetic( borland_real48_multiply_registers(value, value)); } /* 1020:0ff7 — far Multiply wrapper; carry maps to Runtime Error 205. */ BorlandReal48 borland_real48_multiply( BorlandReal48 left, BorlandReal48 right) { return require_real48_arithmetic( borland_real48_multiply_registers(left, right)); } /* 1020:0ffd — denominator zero is Error 200; overflow is Error 205. */ BorlandReal48 borland_real48_divide( BorlandReal48 left, BorlandReal48 right) { return require_real48_arithmetic( borland_real48_divide_registers(left, right)); } /* 1020:100b — far integer-to-Real48 entry around 1020:0f3b. */ BorlandReal48 borland_i32_to_real48(int32_t value) { return borland_i32_to_real48_registers(value); } /* 1020:100f — CH=0 Trunc entry sharing the error tail at 101f with Round. */ int32_t borland_real48_truncate_to_i32(BorlandReal48 value) { BorlandReal48IntegerResult converted = borland_real48_to_i32_registers(value, false); if (converted.overflow) { borland_runtime_error(BORLAND_RUNTIME_ERROR_INVALID_FLOAT); } return converted.value; } /* 1020:1017 — CH=1 Round entry; carry becomes Runtime Error 207. */ int32_t borland_real48_round_to_i32(BorlandReal48 value) { BorlandReal48IntegerResult converted = borland_real48_to_i32_registers(value, true); if (converted.overflow) { borland_runtime_error(BORLAND_RUNTIME_ERROR_INVALID_FLOAT); } return converted.value; }