home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------
- * filename - realcvt.cas
- *
- * function(s)
- * TrimTrailing - suppresses trailing zeroes if needed
- * __realcvt - converts a double value to an ASCIIZ string
- *-----------------------------------------------------------------------*/
-
- /*[]------------------------------------------------------------[]*/
- /*| |*/
- /*| Turbo C Run Time Library - Version 3.0 |*/
- /*| |*/
- /*| |*/
- /*| Copyright (c) 1987, 1990 by Borland International |*/
- /*| All Rights Reserved. |*/
- /*| |*/
- /*[]------------------------------------------------------------[]*/
-
- #pragma inline
- #include <asmrules.h>
-
- #include <_printf.h>
-
- # define EXTPROC(x) (x)
-
- #define I asm
-
- /*--------------------------------------------------------------------------*
-
- Name TrimTrailing - suppresses trailing zeroes if needed
-
- Usage void near TrimTrailing (void);
-
- Prototype in local function
-
- Description Following rules derived from combining the altFormat and
- formCh parameters, backspace to remove unwanted trailing
- characters.
-
- (altFormat || ~(G or g)) == trailing zeroes are wanted
- altFormat == trailing decimal point is wanted
-
- Upon entry DL must contain formCh, CX the starting offset
- of strP, and ES_:[bx] points at the end of the string.
-
- Return value Upon exit ES_:[bx-1] points to the last byte of the
- (trimmed ?) string.
-
- *---------------------------------------------------------------------------*/
- static void near TrimTrailing (void)
- {
- I mov al, 5Fh /* "upper case only" mask */
- I and al, dl /* dl is formCh */
- I cmp al, 'G'
- I jne tt_trimPoint /* only G/g removes trailing zeroes */
-
- tt_trimLoop:
- I cmp BY0 (ES_ [bx-1]), '0'
- I jne tt_trimPoint
- I dec bx
- I cmp bx, cx
- I ja tt_trimLoop
- I jmp short tt_trimmed
-
- tt_trimPoint:
- I cmp BY0 (ES_ [bx-1]), '.'
- I jne tt_trimmed
- I dec bx
-
- tt_trimmed:
- return;
- }
-
- /*--------------------------------------------------------------------------*
-
- Name __realcvt - converts a double value to an ASCIIZ string
-
- Usage void pascal __realcvt (double *valueP, int ndec,
- char *strP, char formCh,
- char altFormat);
-
- Prototype in _printf.h
-
- Description The double (value) is converted to a decimal ASCIIZ string
- (*bufP) either the F format:
-
- [sign] [zeroes] [digit]* ['.'] [digit]
-
- or the E format:
-
- [sign] digit ['.'] [digit]* 'E' sign [digit] digit digit
-
- Where the leading sign is inserted only if negative, and
- the decimal point is suppressed if ndec == 0, and the total
- count of digits <= MIN (1, ndec).
-
- The F format is used if formCh == 'f', or if 'g' or 'G' and
- there would be three or fewer leading zeroes and no
- trailing zeroes, otherwise the E format is used.
-
- If formCh == 'E' or 'G' then the character 'E' is used to
- separate fraction from exponent, otherwise 'e' is used.
-
- Trailing decimal points are usually suppressed, as also are
- trailing fraction zeroes in the G/g format. If altFormat is
- true (not zero) then trailing decimals remain, and G/g
- format will not trim zeroes.
-
- If ndec > 18 the effect is as if ndec == 18.
-
- If the value was infinite then the string is "9E+999" or
- "-9E+999".
-
- The basic conversion to a numeral string is done by
- __xcvt(), with this function responsible for "customizing"
- the simple format which __xcvt() returns.
-
- Return value There is no return value
-
- *---------------------------------------------------------------------------*/
- #pragma warn -use
- static void pascal near __realcvt (void *valueP, int ndec,
- char *strP, char formCh, char altFormat, int type)
- {
- char buf [__XCVTDIG__ + 4];
- int sign, realcnt;
-
- /* caller expects ES to be preserved! */
- I push ES
-
- #if (! LDATA)
- I push DS
- I pop ES
- #endif
-
- I mov ax, ndec
- I cmp ax, __XCVTDIG__
- I jna meaningful
- I mov ax, __XCVTDIG__ /* IEEE double is meaningless */
- I mov ndec, ax /* beyond "__XCVTDIG__" digits. */
-
- meaningful:
- I mov realcnt,ax /* save *real* requested count */
- I mov dl, formCh
- I and dl, 0DFh /* not ('a' - 'A'), mask case difference */
- I cmp dl, 'F' /* F- or f-format. */
- I jne overallDigits
-
- /* F-format works with digits right of the decimal point, specified */
- /* to __xcvt() as a negative number. */
-
- I neg ax
- I jng bounded /* note "ndec" itself still +ve */
- I sub ax, ax /* no decimals if -ve requested. */
- I mov ndec, ax
- I jmp short bounded
-
- /* E-format counts overall digits, as does g-format sometimes. There */
- /* must be at least one. */
-
- overallDigits:
- I or ax, ax
- I jg bounded
- I mov ax, 1
- I jmp short decimalsDecided /* that one is left of '.' */
-
- bounded:
- I cmp dl, 'E' /* e format has digit left of '.' */
- I jne decimalsDecided
- I inc ax /* so ask __xcvt() for one more. */
- I inc W0 (ndec)
-
- decimalsDecided:
-
- /* __xcvt will do the difficult part, conversion to decimal, but the */
- /* format it returns must be worked on before it can be passed */
- /* to the caller. Therefore put it into a temporary buffer. */
-
- #if LDATA
- I push W1 (valueP)
- #endif
- I push W0 (valueP) /* (valueP */
-
- #if 0
- why was this here??
- /*RSS xx */
- I cmp ax,0
- I jle ok_cnt
- asm mov ax,realcnt
- ok_cnt:
- #endif
-
- I push ax /* , realcnt */
- #if LDATA
- I push SS
- #endif
- I lea bx, sign
- I push bx /* , &sign */
- #if LDATA
- I push SS
- #endif
- I lea si, buf
- I push si /* , buf */
-
- I mov ax, W0 (type) /* , type ) */
- I push ax
-
- I call EXTPROC (__xcvt)/* returns exponent in AX */
-
- I xchg bx, ax /* BX = exponent */
-
- /* Set up pointer to destination string */
-
- I LES_ di, strP
- I cld /* forward string direction */
-
- /* Check for Infinity and NAN numbers right up front */
-
- I cmp bx, INF_number
- I je infinities
- I cmp bx, NAN_number
- I je NANs
- I jmp short regular_number
-
- /*+---------------------------------------------------------------------+
- | We have a NAN or Infinity here. Do +INF/-INF or +NAN/-NAN |
- +---------------------------------------------------------------------+*/
- infinities:
- I mov ax, 492BH /* 'I+' stosw reverses */
- I cmp W0 (sign), 0
- I je store_Isign
- I inc ax /* Change 'I+' to 'I-' */
- I inc ax
-
- store_Isign:
- I stosw
-
- I mov ax, 464EH /* 'FN' stosw reverses */
- I stosw
- goto end;
-
- /* Print a NAN */
- NANs:
- I mov ax, 4E2BH /* 'N+' stosw reverses */
- I cmp W0 (sign), 0
- I je store_Nsign
- I inc ax /* Change 'N+' to 'N-' */
- I inc ax
-
- store_Nsign:
- I stosw
-
- I mov al, 'A' /* AX = 'NA' stosw reverses */
- I stosw
- goto end;
-
- /*+---------------------------------------------------------------------+
- | We're printing a regular number |
- +---------------------------------------------------------------------+*/
-
- regular_number:
-
- /* Either format begins with optional sign. */
-
- I cmp BY0 (sign), 0
- I je signSet
- I mov al, '-'
- I stosb
- signSet:
-
- /* Now that we have the basic string, decide what format the caller */
- /* wants it to be put into. */
-
- I mov dl, formCh /* possibilities are e, E, f, g, or G */
- I and dl, 5Fh /* convert to upper case */
- I cmp dl, 'F'
- I je F_format
- I cmp dl, 'E'
- I je E_format
-
- /* G format can be either E or F, depending on circumstances. */
-
- I cmp bx, -3 /* Now decide between E and F formats. */
- I jl E_format
- I mov ax, ndec /* Zero digits in G format are taken as one */
- I or ax, ax
- I jnz NotZero
- I inc ax
- NotZero:
- I cmp bx, ax
- I jg E_format
-
- /* The F format has no written exponent and the decimal point can be at */
- /* the left edge or any interior position, but not the right edge. */
-
- F_format:
- I cmp bx, __XCVTDIG__ /* refuse to do F format if more than */
- I jg E_format /* __XCVTDIG__ integral digits can result. */
-
- I or bx, bx
- I jg FdigitStart
-
- /* No integral digits, begin with '0.'. */
-
- I mov ax, ('.' SHL 8) + '0'
- I stosw
- I mov cx,1
- I je Fdigits
-
- /* If the exponent is negative then leading zeroes are required. */
-
- I mov al, '0'
- FleadZeroes:
- I stosb
- I inc bx
- I jnz FleadZeroes
-
- /* Now write the regular digits, inserting a '.' if it is somewhere */
- /* in the middle of the numeral. */
-
- FdigitStart:
- I mov cx,0
- Fdigits:
- I lods BY0 (SS_ [si])
- I or al, al
- I jz Fend
-
- I stosb
- I dec bx
- I jnz Fdigits
- I mov al, '.'
- I stosb
- I inc cx
- I jmp short Fdigits
-
- /* remove any trailing zeroes, unless there is an implied decimal at */
- /* the right edge. */
-
- Fend:
- I mov ax,ndec
- I add cx,realcnt
- I cmp ax,cx
- I jbe no_zero_pad
-
- I sub ax,cx
- I mov cx,ax
- I add bx,ax
- I mov al,'0'
- I rep stosb
-
- I dec bx
- I jz Fz
-
- no_zero_pad:
- I dec bx
- I jz Fz
-
- I cmp BY0 (altFormat), 0
- I jne Fz /* altFormat implies no trimming */
- I mov dl, formCh
- I mov cx, W0 (strP)
- I xchg bx, di
- TrimTrailing (); /* backspaces BX to effect trimming */
- I xchg di, bx
-
- Fz: /* is the result an empty string ? */
- I cmp di, W0 (strP)
- I jne goto_end
- I mov al, '0'
- I stosb /* if so, it is a form of zero. */
- goto_end:
- I jmp end /* the string will be zero terminated */
-
-
- /* The E format always places one digit to the left of the decimal
- point, followed by fraction digits, and then an 'E' followed
- by a decimal exponent. The exponent is always 2 digits unless
- it is of magnitude > 99.
- */
- E_format:
- I lods W0 (SS_ [si]) /* get two bytes together */
- I stosb
-
- /* The decimal point appears only if followed by a digit. */
-
- I mov al, '.'
- I or ah, ah
- I jz Eexp
-
- /* If arrived here then there are at least two digits so put in */
- /* the decimal point and copy the fraction digits. */
-
- I stosb
- I mov al, ah
-
- Edigits:
- I stosb
- I lods BY0 (SS_ [si])
- I or al, al
- I jnz Edigits
-
- /* Trailing zeroes are removed from the fraction. */
-
- I cmp BY0 (altFormat), 0
- I jne Eexp /* altFormat implies no trimming */
- I mov dl, formCh
- I mov cx, W0 (strP)
- I xchg bx, di
- TrimTrailing (); /* backspaces BX to effect trimming */
- I xchg di, bx
-
- /* Now put in the exponent. Note that the exponent returned from __xcvt */
- /* is one digit different, since __xcvt places the decimal point at */
- /* the left edge. */
-
- Eexp:
- I mov al, 20h /* the bit which distinguishes lower case */
- I and al, formCh /* e f g E G -- cause */
- I or al, 'E' /* e e e E E -- effect */
- I stosb
-
- I mov ax, 2D2Bh /* AX = "-+" load up both possibilities */
- I dec bx /* BX is the exponent returned from __xcvt */
- I jnl EexpSigned
- I xchg al, ah /* It was negative, switch signs */
- I neg bx /* Make it positive for later */
-
- EexpSigned:
- I stosb
- I xchg ax, bx
-
- /*
- ANSI says that "The exponent always contains AT LEAST two digits".
- If the exponent is bigger than 99 then we will use as many as are
- required.
- */
- I mov cx, 3030h /* "00" this'll be handy later */
-
- I cmp ax, 99
- I jna EtwoDigits
-
- I cmp ax, 999
- I jna EthreeDigits
-
- EfourDigits:
- I cwd /* Sign extend AX through DX */
- I mov bx, 1000
- I div bx
- I add al, cl
- I stosb
- I xchg ax, dx
-
- EthreeDigits:
- I mov bl, 100
- I div bl
- I add al, cl
- I stosb
- I xchg al, ah
- I cbw
-
- EtwoDigits:
- I mov bl, 10
- I div bl
- I add ax, cx /* '00' */
- I stosw
-
- /* Both formats are terminated with \0. */
-
- end:
- I xor al,al
- I stosb
-
- I pop ES
-
- return;
- }
- #pragma warn .use
-
-
- /*--------------------------------------------------------------------------*
-
- Description __realcvt is essentially used by printf family functions. In
- order to include the real conversion function only if
- needed (using %f, %e or %g format), __realcvt is not called
- directly but via a pointer to function which is declared in
- the startup code in an extra segment _CVTSEG.
-
- This module is included at link time only if there is at
- least one floating point number, because in that case there
- are external references to the fixups FI?RQQ which force
- the emulator to be linked in. The emulator has an external
- reference to the value __turboCvt which forces this module
- to be linked in.
-
- *---------------------------------------------------------------------------*/
-
- #define CodeSeg _TEXT
- #define cPtr dw
-
- #pragma warn -rvl
- #pragma warn -use
- #pragma warn -asm
- static void near turboCvt(void)
- {
- I CodeSeg ENDS
- I PUBLIC __turboCvt
- I __turboCvt equ 0
- I _CVTSEG SEGMENT PUBLIC WORD 'DATA'
- I cPtr __realcvt
- I _CVTSEG ENDS
- I CodeSeg SEGMENT
- }
-