Files
TeslaRel410/BORLAND/BC45/SOURCE/RTL/SOURCE/MATHLIB/SCANTOD2.CAS
T
CydandClaude Fable 5 63312e07f9 source410: literal 4.10 source reconstruction + BC++ 4.52 fleet toolchain archived
- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
  is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
  Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
  (extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
  BT game source (never mixed into CODE/). Round 1-3 state:
  * 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
    (BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
    since 1996.
  * BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
    its binary-recorded line 400 exactly.
  * BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
    (Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
    TeamScore=12 flagged [T4]).
  * MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
    (VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
    the period compiler is the drift detector).
  * Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
    (per-TU verification sweep under authentic OPT.MAK flags).
  * README: corrected roadmap - MECH.HPP is the capstone grown with the mech
    TU reconstructions; BTREG.CPP green = the header-family milestone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 07:33:26 -05:00

742 lines
23 KiB
Plaintext

/*------------------------------------------------------------------------
* filename - scantod.cas
*
* function(s)
* scantod - converts a string to floating-point number
* scanpop - Clean stack after conversion error
* scanrslt - Get conversion result
*-----------------------------------------------------------------------*/
/*
* C/C++ Run Time Library - Version 6.5
*
* Copyright (c) 1987, 1994 by Borland International
* All Rights Reserved.
*
*/
#pragma inline
#include <asmrules.h>
#include <_scanf.h>
#include <ctype.h>
#include <_math.h>
#include <math.h>
#include <stdlib.h>
#include <rtldata.h>
#include <_locale.h>
#if LPROG
#define EXTPROC1(x) asm call far ptr (x)
#else
#define EXTPROC1(x) asm call near ptr (x)
#endif
/*
Internal RTL function to perform double/float truncations.
*/
#define FLT 0
#define DBL 1
double near pascal __ldtrunc(int flag, long double x, double xhuge);
/*--------------------------------------------------------------------------*
Name scantod - converts a string to floating-point number
Usage long double _scantod (int near (* Get) (void *srceP),
void near (* UnGet) (int ch, void *srceP),
const void *srceP,
int width,
int *countP,
int *statusP )
Prototype in _scanf.h
Description Convert a string to a long double precision real. The syntax
of the string must be:
float ::= [isspace]* [sign] [realnum] [exponent]
isspace ::= as per <ctype.h>:isspace
realnum ::= {digit [digit]* ['.' [digit]* ]} |
{'.' digit [digit]*}
exponent ::= 'e'|'E' [sign] digit [digit]*
"srceP" is a pointer to some kind of object from which
characters are scanned. For example, it may be a
FILE *. The functions Get() and UnGet() operate
upon srceP to get characters and possibly replace
one character, allowing LR(1) scanning rules.
The digits must be decimal.
The width is the limit on the number of digits which may be
accepted. It includes the sign character, if any, but does
not include any leading spaces.
The count value returned to the caller is a count of all
the characters consumed, including leading spaces even if
no numerals are found. It is ADDED to the existing value of
count.
The status returned is EOF if EOF was encountered before
conversion could begin, 0 if no numerals were found before
some other character occurred, 1 if the conversion
proceeded correctly, and 2 if overflow or underflow
occurred.
If the source string is not a valid floating point numeral
then the result value is zero and the next char left in the
source will be the first char encountered which could not
be part of the number. If the number is too large or too
tiny then the result is signed HUGE_VAL or zero.
Method The conversion proceeds in two stages. Firstly, the decimal
strings for fraction and exponent must be captured.
The fraction is held as a 63-bit unsigned integer (18
decimals of precision), with separate sign. Digits beyond
the 18th are truncated.
The exponent is held as a short integer in binary format,
and is adjusted to note the position of the decimal point
in the fraction so that the "fraction" is normalized as an
integer with decimal point to the right.
When both fraction and exponent have been captured, the
second stage is to combine them. This is done with the
formula:
result = 10^(exponent) * fraction * sign.
If the result overflows + or - HUGE will be returned. If
the result is an underflow, zero is returned.
The iNDP-87 is not used as much as might be optimum if the
user has a coprocessor installed. A balance is sought, so
that the routine makes strategic use of co-intructions but
not frequent use which would be quite slow if software
emulation is used in place of a chip.
The following diagram may be helpful with understanding the
relations between the variables:
000012345789012345.098765432109876E+99
|---decimals-->|
|--------------.----digits---->| not counting the '.'
Decimals are counted negative if the '.' is left of the
first digit. Digits are positive unless no non-zero digit
is ever seen.
Return value _scantod returns the converted value of the input string
*---------------------------------------------------------------------------*/
/* +/- infinity, +/- NAN */
static const float INF = 1.0/0.0;
static const float INFM = -(1.0/0.0);
static const float NAN = 0.0/0.0;
static const float NANM = -(0.0/0.0);
#pragma warn -use
#pragma warn -sus
static long double near _scantod (
int near (* Get) (void *srceP),
void near (* UnGet) (int ch, void *srceP),
const void *srceP,
int width,
int *countP,
int *statusP )
{
int decimals; /* register SI = 0x8000 */
int digits; /* register DI = -2 */
int exponent;
char sign = 0;
char FirstDigit = 1;
char saw_sign= 0;
char expSign = 0;
char ExpOflow= 0;
int ct = 0;
int status = 1;
long double frac= 0.0;
char decimal_point_char;
_QRTLDataBlock;
decimal_point_char = *(_QRTLInstanceData(_localeconvention).decimal_point);
asm mov si, 8000h
asm mov di, -2
/*
Skip leading spaces on the input numeral.
*/
std_nextBlank:
ct ++;
Get (srceP);
asm or ax, ax
asm jnl not_instantEOF /* No EOF the first time */
asm jmp std_EOF /* EOF happened first thing */
not_instantEOF:
asm cbw
asm xchg bx, ax
asm test bl, 80h
asm jnz std_notSpace
#if __HUGE__
asm mov ax, seg _ctype
asm mov ES, ax
asm test BY0 (ES: _ctype [bx+1]), _IS_SP
#else
asm test BY0 (_ctype [bx+1]), _IS_SP
#endif
asm jnz std_nextBlank
std_notSpace:
asm xchg ax, bx
asm dec W0 (width)
asm jl std_fractionLimited
/*
Is the numeral preceded by a sign ?
*/
asm cmp al, '+'
asm je std_signSeen
asm cmp al, '-'
asm jne std_fracChar /* AL must hold a fraction character. */
asm inc BY0 (sign) /* set flag to true == negative */
std_signSeen:
saw_sign++;
std_fracLoop: /* Pick up the next character of the fraction. */
asm dec W0 (width)
asm jl std_fractionLimited
ct ++;
Get (srceP);
/*-------------------------------------------------------------------------
We need to check for the special cases where +INF -INF +NAN -NAN
might be specified.
-------------------------------------------------------------------------*/
asm cmp BY0 (FirstDigit), 1
asm jne std_fracChar /* Its not 1st char, continue */
asm cmp BY0 (saw_sign), 0
asm je std_fracChar /* There was no sign, continue */
asm cmp al, 'I'
asm je relPossibleINF /* Maybe we have +/-INF */
asm cmp al, 'N'
asm je relPossibleNAN /* Maybe we have +/-NAN */
asm jmp short std_fracChar /* Its not a special case */
relPossibleINF:
asm jmp PossibleINF; /* far jmp within relative range*/
relPossibleNAN:
asm jmp PossibleNAN; /* far jmp within relative range*/
std_fracChar:
asm mov BY0 (FirstDigit), 0
asm cmp al, decimal_point_char /* Watch for decimal points */
asm je std_fracPoint
asm cmp al, '9'
asm ja std_fracEndJmp /* All other non-numeric characters .. */
asm cmp al, '0'
asm jb std_fracEndJmp /* .. are fraction terminators. */
asm sub al, '0' /* convert digit to equivalent number. */
asm cbw
/*
Keep a count of the digits seen.
*/
asm inc di
asm jg std_notFirst /* was it the first digit ? */
/*
The first digit begins the fraction. Leading zeros are noted by setting
digits -1, so that the fraction syntax is valid if no other digits
are seen, but following digits will still be treated as "firsts".
Leading non-zero digits cause digits to be set to 1.
*/
asm mov frac [0], al
asm mov di, 1
asm or al, al
asm jnz std_fracLoop
asm neg di
asm cmp si, 8000h /* has decimal point been seen ? */
asm je std_fracLoop
asm dec si /* if yes, move it to the left. */
asm jmp short std_fracLoop
/*
Arrive here when fraction is width-limited but valid.
*/
std_fractionLimited:
asm mov al, 'e' /* Behave as if exponent started. */
jmp_to_fracEnd: /* Label within relative range */
asm jmp std_fracEnd /* Width will limit exponent, too. */
/*
Error action placed here for short jump range.
*/
std_EOF:
status = -1;
asm jmp short std_noResult
std_fracEndJmp: /* extend jump range */
asm jmp std_fracEnd
/*
A decimal point has been seen
*/
std_fracPoint:
asm cmp si, 8000h /* Has a previous decimal point been seen ? */
asm jne jmp_to_fracEnd /* If so, the fraction is terminated. */
asm sub si, si /* result if '.' before any digit */
asm or di, di
asm jng std_fracLoop
asm mov si, di /* decimals = digits */
asm jmp short std_fracLoop
/*
If a digit is seen, then multiply the existing fraction by 10 and
add in the new digit. The special case of the first 5 digits is
treated separately for speed.
*/
std_notFirst:
asm cmp di, 5
asm ja std_beyond5
asm xchg bx, ax
asm mov ax, 10
asm mul W0 (frac)
asm add ax, bx
asm adc dl, dh
asm mov frac [0], ax
asm mov frac [2], dx
asm jmp std_fracLoop
/*
Digits beyond the 6th are more rare in practice (even in 6-digit
numbers, 5 will be quick), so no further special cases are
justified. Beyond 18 digits, ignore the digit values but
keep scanning.
*/
std_beyond5:
asm cmp di, 18
asm ja jmp_frac_loop
asm xchg bx, ax
asm mov ax, 10
asm mul W0 (frac [6])
asm mov (frac [6]), ax
asm mov ax, 10
asm mul W0 (frac [4])
asm mov (frac [4]), ax
asm push dx
asm mov ax, 10
asm mul W0 (frac [2])
asm mov (frac [2]), ax
asm push dx
asm mov ax, 10
asm mul W0 (frac [0])
asm add ax, bx
asm mov (frac [0]), ax
asm adc (frac [2]), dx
asm pop dx
asm adc (frac [4]), dx
asm pop dx
asm adc (frac [6]), dx
jmp_frac_loop:
asm jmp std_fracLoop
/*
error clauses placed here within short-jump range of whole routine.
Arrive here if an error occurred.
*/
std_noDigitSeen:
status = 0;
std_noResult:
if (width >= 0)
{
UnGet (_AX, srceP);
ct --;
}
asm FLDZ /* and a zero numeric result. */
asm jmp std_end
/** end of error clauses.
*/
/*
The fraction was ended. If it was valid, it must have had at least
one digit. AL must hold the character which terminated the fraction.
*/
std_fracEnd:
asm cmp di, -2
asm jz std_noDigitSeen
/*
If no decimal point was seen, then the decimal is assumed to be at
the rightmost edge.
*/
asm cmp si, 8000h
asm jne std_exponent
asm mov si, di /* decimals = digits */
/*
Now we must gather the exponent. First, check for 'E' or 'e' to
introduce it, then if found gather the short integer.
*/
std_exponent:
asm mov digits, di
asm mov decimals, si
asm sub di, di /* DI = exponent */
asm cmp al, 'E'
asm je std_present
asm cmp al, 'e'
asm jne std_combine
std_present:
asm dec W0 (width)
asm jl std_exponentLimited
ct ++;
Get (srceP);
asm cmp al, '+'
asm je std_expNext
asm cmp al, '-' /* is exponent negative ? */
asm jne std_expGotNext
expSign ++;
std_expNext:
asm dec W0 (width)
asm jl std_exponentLimited
ct ++;
Get (srceP);
std_expGotNext: /* if no leading sign, must be leading digit. */
asm cmp al, '9'
asm ja std_combine
asm sub al, '0'
asm jb std_expNonDigit
asm cbw
/*
The largest IEEE long doubles have exponents -4932 <= X <= +4932.
Numbers outside that range will be accepted as infinite or zero,
according to the sign of the exponent.
*/
std_expLoop:
asm xchg ax, di
asm mov dx, 10
asm mul dx
asm add di, ax /* DI = exponent */
asm cmp di, 4932 /* The upper limit on exponents */
asm jle std_expNext
asm xor di, di /* Exponent overflow, set flag */
asm mov BY0 (ExpOflow), 1
asm jmp short std_expNext
std_expNonDigit:
asm add al, '0' /* restore original terminator */
/*
Arrive here when a valid syntax has been terminated.
AL must still contain the terminating character, unchanged.
*/
std_combine:
UnGet (_AX, srceP);
ct--;
/*
Arrive here with valid termination but no terminator to be pushed back.
*/
std_exponentLimited:
asm test BY0 (expSign), 0FFH /* was the exponent signed ? */
asm jz skip_neg
asm neg di
/*
Normal stays normal, Infinity becomes 0 if exponent was hugely negative.
*/
asm neg BY0 (ExpOflow)
skip_neg:
/*
The special case when digits = -1 occurs when the fraction is zero.
In that case, the result is always zero, whatever the exponent.
*/
asm mov bx, digits
asm or bx, bx
asm jnl std_nonZero
asm FLDZ
asm jmp std_end
/*
Combine the decimal point position with the exponent. The exponent
begins with a value that reflects the position of the decimal point.
*/
std_nonZero:
asm mov cx, decimals
asm mov ax, cx
asm add ax, di /* 1.0E(decimals+exponent) = upper bound */
/* 0.1E(decimals+exponent) = lower bound
Convert underflows to zero and overflows to ld HUGE_VAL.
*/
asm cmp BY0 (ExpOflow), 1 /* big (+) exp -> ld HUGE_VAL */
asm je std_isInfinite
asm cmp BY0 (ExpOflow), -1 /* big (-) exp -> 0 */
asm jne std_isNormal
std_isZero:
asm FLDZ
asm jmp short status2
std_isInfinite:
/*
Make 'frac' a long double HUGE_VAL
*/
asm mov ax, -1
asm mov frac[0], ax
asm mov frac[2], ax
asm mov frac[4], ax
asm mov frac[6], ax
asm mov frac[8], 07FFEH
asm FLD LONGDOUBLE( frac )
status2:
asm mov W0 (status), 2
asm jmp std_end
std_isNormal: /* For normal numbers multiply fraction * 10^exponent */
asm mov ax, bx
asm cmp bx, 18
asm jna std_usualPoint
asm mov bx, 18 /* a maximum of 18 digits are used */
std_usualPoint:
asm add ax, cx
asm sub cx, bx
std_beginExp: /* CX = decimal point contribution to exponent */
asm add di, CX /* DI = combined exponent */
asm FILD qword ptr (frac)
asm mov ax, di /* Calculate 10^(|exponent|). */
asm or ax, ax
asm jz std_end /* no multiply required if exponent is zero. */
asm jnl std_pow
asm neg ax
std_pow:
asm push ax
EXTPROC1 (pow10) /* leaves result in iNDP-87 ST(0) TOS */
asm pop ax
asm or di, di
asm jnl std_expPlus
asm FDIV /* negative exponent --> 1 / 10^|exponent| */
asm jmp short std_end
std_expPlus:
asm FMUL /* combine the exponent with the fraction. */
std_end:
if (sign)
{
asm FCHS /* negate the result */
}
std_returnPP: /* update *(suffixPP) with the next character's position. */
/*
Finally, of course, don't forget to return the converted number !
*/
std_exit:
asm LES_ di, countP
asm mov bx, ct
asm add ES_ [di], bx
asm LES_ di, statusP
asm mov bx, status
asm mov ES_ [di], bx
#pragma noretval
return;
/*-------------------------------------------------------------------------
Special case code to scan +INF -INF +NAN -NAN
-------------------------------------------------------------------------
This special case code is positioned down here so that it won't mess up
relative jumps for the rest of the function.
One side effect here, if this ultimately isn't +INF -INF +NAN -NAN will be
that the apps input stream is now messed up because we needed to look
ahead more than 1 character to recognize INF or NAN. The 'unget' functions
are only guaranteed to be able to unget a maximum of one char. This means
on a worst case input like "+INX" there will be 3 characters we won't be
able to push back on the stream successfully. There's not much that can
be done to prevent this. The same kind of thing can happen when reading
E format numbers, for example "1.234E+Q". By the time the 'Q' is seen
"E+" has gone by.
--------------------------------------------------------------------------*/
PossibleINF:
ct ++;
Get (srceP);
asm dec W0 (width)
asm jl Didnt_pan_out
asm cmp al, 'N'
asm jne Didnt_pan_out
ct ++;
Get (srceP);
asm dec W0 (width)
asm jl Didnt_pan_out
asm cmp al, 'F'
asm jne Didnt_pan_out
if (sign)
{
asm FLD FLOAT( INFM )
}
else
{
asm FLD FLOAT( INF )
}
asm jmp std_returnPP
PossibleNAN:
ct ++;
Get (srceP);
asm dec W0 (width)
asm jl Didnt_pan_out
asm cmp al, 'A'
asm jne Didnt_pan_out
ct ++;
Get (srceP);
asm dec W0 (width)
asm jl Didnt_pan_out
asm cmp al, 'N'
asm jne Didnt_pan_out
if (sign)
{
asm FLD FLOAT( NANM )
}
else
{
asm FLD FLOAT( NAN )
}
asm jmp std_returnPP
Didnt_pan_out: /* It wasn't +/-/INF/NAN */
status = 0;
asm jmp std_noDigitSeen
#pragma warn -rvl
}
#pragma warn .rvl
#pragma warn .use
#pragma warn .sus
/*--------------------------------------------------------------------------*
Name scanpop - Clean stack after conversion error
Usage void _scanpop(void);
Description This function is used to clean the stack after a conversion
error in _scanner function
*---------------------------------------------------------------------------*/
static void near _scanpop()
{
asm FSTP ST(0) /* pop math coprocessor stack */
}
/*--------------------------------------------------------------------------*
Name scanrslt - Get conversion result
Usage void _scanrslt(void far *rsltP, int rsltType);
Description This function is used to get the result of the conversion
in _scanner function
*---------------------------------------------------------------------------*/
static void near _scanrslt(void far *rsltP, int rsltType)
{
long double temp;
asm FSTP LONGDOUBLE (temp)
if (rsltType & isLong)
*(double far *)rsltP = __ldtrunc(DBL, temp, HUGE_VAL);
else if (rsltType & isLongDouble)
*(long double far *)rsltP = temp;
else
*(float far *)rsltP = __ldtrunc(FLT, temp, 1./0.);
}
/*--------------------------------------------------------------------------*
Description The functions described above are essentially used by scanf
functions family. As for _realcvt, these functions are not
called directly, but via a pointer to function. This is
done in order to include the real conversion only if
needed.
Each time the compiler needs to build a reference to a
double or float value, it generates an external reference
to __turboFloat which forces this module to be linked in.
*---------------------------------------------------------------------------*/
#define CodeSeg _TEXT
#define cPtr dw
#pragma warn -use
#pragma warn -asm
static void turboFloat()
{
asm CodeSeg ENDS
asm PUBLIC __turboFloat
asm PUBLIC __floatconvert
asm __turboFloat equ 8087h
asm __floatconvert = __turboFloat
asm _SCNSEG SEGMENT PUBLIC WORD 'DATA'
asm cPtr _scantod
asm cPtr _scanrslt
asm cPtr _scanpop
asm _SCNSEG ENDS
asm CodeSeg SEGMENT
}