home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c082_122 / 2.ddi / MATHSRC.ZIP / REALCVT.CAS < prev    next >
Encoding:
Text File  |  1992-06-10  |  15.7 KB  |  520 lines

  1. /*------------------------------------------------------------------------
  2.  * filename - realcvt.cas
  3.  *
  4.  * function(s)
  5.  *      TrimTrailing  - suppresses trailing zeroes if needed
  6.  *      __realcvt       - converts a double value to an ASCIIZ string
  7.  *-----------------------------------------------------------------------*/
  8.  
  9. /*
  10.  *      C/C++ Run Time Library - Version 5.0
  11.  *
  12.  *      Copyright (c) 1987, 1992 by Borland International
  13.  *      All Rights Reserved.
  14.  *
  15.  */
  16.  
  17. #pragma  inline
  18. #include <asmrules.h>
  19.  
  20. #include <_printf.h>
  21.  
  22. #undef   EXTPROC
  23. #define  EXTPROC(x)  (x)
  24.  
  25. #define I asm
  26.  
  27. /*--------------------------------------------------------------------------*
  28.  
  29. Name            TrimTrailing - suppresses trailing zeroes if needed
  30.  
  31. Usage           void near TrimTrailing (void);
  32.  
  33. Prototype in    local function
  34.  
  35. Description     Following  rules derived  from combining  the altFormat and
  36.                 formCh  parameters, backspace  to remove  unwanted trailing
  37.                 characters.
  38.  
  39.                         (altFormat || ~(G or g)) == trailing zeroes are wanted
  40.                         altFormat == trailing decimal point is wanted
  41.  
  42.                 Upon entry DL  must contain formCh, CX the  starting offset
  43.                 of strP, and ES_:[bx] points at the end of the string.
  44.  
  45. Return value    Upon  exit  ES_:[bx-1]  points  to  the  last  byte  of the
  46.                 (trimmed ?) string.
  47.  
  48. *---------------------------------------------------------------------------*/
  49. static  void    near    TrimTrailing (void)
  50. {
  51. I       mov     al, 5Fh         /* "upper case only" mask */
  52. I       and     al, dl          /* dl is formCh */
  53. I       cmp     al, 'G'
  54. I       jne     tt_trimPoint            /* only G/g removes trailing zeroes */
  55.  
  56. tt_trimLoop:
  57. I       cmp     BY0 (ES_ [bx-1]), '0'
  58. I       jne     tt_trimPoint
  59. I       dec     bx
  60. I       cmp     bx, cx
  61. I       ja      tt_trimLoop
  62. I       jmp     short   tt_trimmed
  63.  
  64. tt_trimPoint:
  65. I       cmp     BY0 (ES_ [bx-1]), '.'
  66. I       jne     tt_trimmed
  67. I       dec     bx
  68.  
  69. tt_trimmed:
  70.         return;
  71. }
  72.  
  73. /*--------------------------------------------------------------------------*
  74.  
  75. Name            __realcvt - converts a double value to an ASCIIZ string
  76.  
  77. Usage           void pascal __realcvt (double *valueP, int ndec,
  78.                                         char *strP, char formCh,
  79.                                         char altFormat);
  80.  
  81. Prototype in    _printf.h
  82.  
  83. Description     The double (value) is converted  to a decimal ASCIIZ string
  84.                 (*bufP) either the F format:
  85.  
  86.                 [sign] [zeroes] [digit]* ['.'] [digit]
  87.  
  88.                 or the E format:
  89.  
  90.                 [sign] digit ['.'] [digit]* 'E' sign [digit] digit digit
  91.  
  92.                 Where the  leading sign is  inserted only if  negative, and
  93.                 the decimal point is suppressed if ndec == 0, and the total
  94.                 count of digits <= MIN (1, ndec).
  95.  
  96.                 The F format is used if formCh == 'f', or if 'g' or 'G' and
  97.                 there  would  be  three  or  fewer  leading  zeroes  and no
  98.                 trailing zeroes, otherwise the E format is used.
  99.  
  100.                 If formCh ==  'E' or 'G' then the character  'E' is used to
  101.                 separate fraction from exponent, otherwise 'e' is used.
  102.  
  103.                 Trailing decimal points are usually suppressed, as also are
  104.                 trailing fraction zeroes in the G/g format. If altFormat is
  105.                 true  (not zero)  then  trailing  decimals remain,  and G/g
  106.                 format will not trim zeroes.
  107.  
  108.                 If ndec > 18 the effect is as if ndec == 18.
  109.  
  110.                 If the  value was infinite  then the string  is "9E+999" or
  111.                 "-9E+999".
  112.  
  113.                 The  basic  conversion  to  a  numeral  string  is  done by
  114.                 __xcvt(), with  this function responsible  for "customizing"
  115.                 the simple format which __xcvt() returns.
  116.  
  117. Return value    There is no return value
  118.  
  119. *---------------------------------------------------------------------------*/
  120. #pragma warn -use
  121. static void pascal near __realcvt (void *valueP, int ndec,
  122.                         char *strP, char formCh, char altFormat, int type)
  123. {
  124.         char    buf [__XCVTDIG__ + 4];
  125.         int     sign, realcnt;
  126.  
  127. /* caller expects ES to be preserved! */
  128. I       push    ES
  129.  
  130. #if (! LDATA)
  131. I       push    DS
  132. I       pop     ES
  133. #endif
  134.  
  135. I       mov     ax, ndec
  136. I       cmp     ax, __XCVTDIG__
  137. I       jna     meaningful
  138. I       mov     ax, __XCVTDIG__ /* IEEE double is meaningless */
  139. I       mov     ndec, ax        /*      beyond "__XCVTDIG__" digits. */
  140.  
  141. meaningful:
  142. I       mov     realcnt,ax      /* save *real* requested count */
  143. I       mov     dl, formCh
  144. I       and     dl, 0DFh        /* not ('a' - 'A'), mask case difference */
  145. I       cmp     dl, 'F'         /* F- or f-format. */
  146. I       jne     overallDigits
  147.  
  148. /* F-format works with digits right of the decimal point, specified */
  149. /*      to __xcvt() as a negative number. */
  150.  
  151. I       neg     ax
  152. I       jng     bounded         /* note "ndec" itself still +ve */
  153. I       sub     ax, ax          /* no decimals if -ve requested. */
  154. I       mov     ndec, ax
  155. I       jmp     short bounded
  156.  
  157. /* E-format counts overall digits, as does g-format sometimes.  There */
  158. /*      must be at least one. */
  159.  
  160. overallDigits:
  161. I       or      ax, ax
  162. I       jg      bounded
  163. I       mov     ax, 1
  164. I       jmp     short   decimalsDecided /* that one is left of '.'      */
  165.  
  166. bounded:
  167. I       cmp     dl, 'E'         /* e format has digit left of '.' */
  168. I       jne     decimalsDecided
  169. I       inc     ax                      /* so ask __xcvt() for one more. */
  170. I       inc     W0 (ndec)
  171.  
  172. decimalsDecided:
  173.  
  174. /* __xcvt will do the difficult part, conversion to decimal, but the    */
  175. /*      format it returns must be worked on before it can be passed     */
  176. /*      to the caller.  Therefore put it into a temporary buffer.       */
  177.  
  178. #if LDATA
  179. I       push    W1 (valueP)
  180. #endif
  181. I       push    W0 (valueP)     /* (valueP              */
  182.  
  183. #if 0
  184. why was this here??
  185. /*RSS xx */
  186. I       cmp     ax,0
  187. I       jle     ok_cnt
  188. asm     mov     ax,realcnt
  189. ok_cnt:
  190. #endif
  191.  
  192. I       push    ax                      /*      , realcnt       */
  193. #if LDATA
  194. I       push    SS
  195. #endif
  196. I       lea     bx, sign
  197. I       push    bx                      /*      , &sign */
  198. #if LDATA
  199. I       push    SS
  200. #endif
  201. I       lea     si, buf
  202. I       push    si                      /*      , buf  */
  203.  
  204. I       mov     ax, W0 (type)   /*      , type )        */
  205. I       push    ax
  206.  
  207. I       call    EXTPROC (__xcvt)/* returns exponent in AX */
  208.  
  209. I       xchg    bx, ax  /* BX = exponent */
  210.  
  211. /* Set up pointer to destination string */
  212.  
  213. I       LES_    di, strP
  214. I       cld                     /* forward string direction */
  215.  
  216. /* Check for Infinity and NAN numbers right up front */
  217.  
  218. I       cmp     bx, INF_number
  219. I       je      infinities
  220. I       cmp     bx, NAN_number
  221. I       je      NANs
  222. I       jmp     short regular_number
  223.  
  224. /*+---------------------------------------------------------------------+
  225.   | We have a NAN or Infinity here. Do +INF/-INF or +NAN/-NAN           |
  226.   +---------------------------------------------------------------------+*/
  227. infinities:
  228. I       mov     ax, 492BH               /* 'I+' stosw reverses          */
  229. I       cmp     W0 (sign), 0
  230. I       je      store_Isign
  231. I       inc     ax                      /* Change 'I+' to 'I-'          */
  232. I       inc     ax
  233.  
  234. store_Isign:
  235. I       stosw
  236.  
  237. I       mov     ax, 464EH               /* 'FN' stosw reverses          */
  238. I       stosw
  239.         goto end;
  240.  
  241.                         /* Print a NAN  */
  242. NANs:
  243. I       mov     ax, 4E2BH               /* 'N+' stosw reverses          */
  244. I       cmp     W0 (sign), 0
  245. I       je      store_Nsign
  246. I       inc     ax                      /* Change 'N+' to 'N-'          */
  247. I       inc     ax
  248.  
  249. store_Nsign:
  250. I       stosw
  251.  
  252. I       mov     al, 'A'                 /* AX = 'NA' stosw reverses     */
  253. I       stosw
  254.         goto end;
  255.  
  256. /*+---------------------------------------------------------------------+
  257.   |                     We're printing a regular number                 |
  258.   +---------------------------------------------------------------------+*/
  259.  
  260. regular_number:
  261.  
  262. /* Either format begins with optional sign. */
  263.  
  264. I       cmp     BY0 (sign), 0
  265. I       je      signSet
  266. I       mov     al, '-'
  267. I       stosb
  268. signSet:
  269.  
  270. /* Now that we have the basic string, decide what format the caller */
  271. /*      wants it to be put into. */
  272.  
  273. I       mov     dl, formCh      /* possibilities are e, E, f, g, or G */
  274. I       and     dl, 5Fh         /* convert to upper case */
  275. I       cmp     dl, 'F'
  276. I       je      F_format
  277. I       cmp     dl, 'E'
  278. I       je      E_format
  279.  
  280. /* G format can be either E or F, depending on circumstances. */
  281.  
  282. I       cmp     bx, -3          /* Now decide between E and F formats. */
  283. I       jl      E_format
  284. I   mov ax, ndec    /* Zero digits in G format are taken as one */
  285. I   or  ax, ax
  286. I   jnz NotZero
  287. I   inc ax
  288. NotZero:
  289. I       cmp     bx, ax
  290. I       jg      E_format
  291.  
  292. /* The F format has no written exponent and the decimal point can be at */
  293. /*      the left edge or any interior position, but not the right edge. */
  294.  
  295. F_format:
  296. I       cmp     bx, __XCVTDIG__ /* refuse to do F format if more than */
  297. I       jg      E_format        /* __XCVTDIG__ integral digits can result. */
  298.  
  299. I       or      bx, bx
  300. I       jg      FdigitStart
  301.  
  302. /* No integral digits, begin with '0.'. */
  303.  
  304. I       mov     ax, ('.' SHL 8) + '0'
  305. I       stosw
  306. I       mov     cx,1
  307. I       je      Fdigits
  308.  
  309. /* If the exponent is negative then leading zeroes are required. */
  310.  
  311. I       mov     al, '0'
  312. FleadZeroes:
  313. I       stosb
  314. I       inc     bx
  315. I       jnz     FleadZeroes
  316.  
  317. /* Now write the regular digits, inserting a '.' if it is somewhere */
  318. /*      in the middle of the numeral. */
  319.  
  320. FdigitStart:
  321. I       mov     cx,0
  322. Fdigits:
  323. I       lods    BY0 (SS_ [si])
  324. I       or      al, al
  325. I       jz      Fend
  326.  
  327. I       stosb
  328. I       dec     bx
  329. I       jnz     Fdigits
  330. I       mov     al, '.'
  331. I       stosb
  332. I       inc     cx
  333. I       jmp     short   Fdigits
  334.  
  335. /* remove any trailing zeroes, unless there is an implied decimal at */
  336. /*      the right edge. */
  337.  
  338. Fend:
  339. I       mov     ax,ndec
  340. I       add     cx,realcnt
  341. I       cmp     ax,cx
  342. I       jbe     no_zero_pad
  343.  
  344. I       sub     ax,cx
  345. I       mov     cx,ax
  346. I       add     bx,ax
  347. I       mov     al,'0'
  348. I       rep     stosb
  349.  
  350. I       dec     bx
  351. I       jz      Fz
  352.  
  353. no_zero_pad:
  354. I       dec     bx
  355. I       jz      Fz
  356.  
  357. I       cmp     BY0 (altFormat), 0
  358. I       jne     Fz              /* altFormat implies no trimming */
  359. I       mov     dl, formCh
  360. I       mov     cx, W0 (strP)
  361. I       xchg    bx, di
  362.         TrimTrailing ();                /* backspaces BX to effect trimming */
  363. I       xchg    di, bx
  364.  
  365. Fz:                             /* is the result an empty string ? */
  366. I       cmp     di, W0 (strP)
  367. I       jne     goto_end
  368. I       mov     al, '0'
  369. I       stosb                   /* if so, it is a form of zero. */
  370. goto_end:
  371. I       jmp     end             /* the string will be zero terminated */
  372.  
  373.  
  374. /* The E format always places one digit to the left of the decimal
  375.         point, followed by fraction digits, and then an 'E' followed
  376.         by a decimal exponent.  The exponent is always 2 digits unless
  377.         it is of magnitude > 99.
  378. */
  379. E_format:
  380. I       lods    W0 (SS_ [si])           /* get two bytes together */
  381. I       stosb
  382.  
  383. /* The decimal point appears only if followed by a digit. */
  384.  
  385. I       mov     al, '.'
  386. I       or      ah, ah
  387. I       jnz     Edecimal                /* two digits so normal case */
  388.  
  389. /* Only one digit so check whether alternate format requested */
  390.  
  391. I       cmp     BY0 (altFormat), 0
  392. I       je      Eexp                    /* no altFormat, no decimal point */
  393. I       stosb
  394. I       jmp     Eexp                    /* altFormat, do decimal
  395.  
  396. /* If arrived here then there are at least two digits so put in */
  397. /*      the decimal point and copy the fraction digits. */
  398.  
  399. Edecimal:
  400. I       stosb
  401. I       mov     al, ah
  402.  
  403. Edigits:
  404. I       stosb
  405. I       lods    BY0 (SS_ [si])
  406. I       or      al, al
  407. I       jnz     Edigits
  408.  
  409. /* Trailing zeroes are removed from the fraction. */
  410.  
  411. I       cmp     BY0 (altFormat), 0
  412. I       jne     Eexp            /* altFormat implies no trimming */
  413. I       mov     dl, formCh
  414. I       mov     cx, W0 (strP)
  415. I       xchg    bx, di
  416.         TrimTrailing ();                /* backspaces BX to effect trimming */
  417. I       xchg    di, bx
  418.  
  419. /* Now put in the exponent.  Note that the exponent returned from __xcvt */
  420. /*      is one digit different, since __xcvt places the decimal point at */
  421. /*      the left edge. */
  422.  
  423. Eexp:
  424. I       mov     al, 20h         /* the bit which distinguishes lower case */
  425. I       and     al, formCh      /*      e f g E G               -- cause */
  426. I       or      al, 'E'         /*      e e e E E               -- effect */
  427. I       stosb
  428.  
  429. I       mov     ax, 2D2Bh       /* AX = "-+"    load up both possibilities  */
  430. I       dec     bx              /* BX is the exponent returned from __xcvt */
  431. I       jnl     EexpSigned
  432. I       xchg    al, ah          /* It was negative, switch signs */
  433. I       neg     bx              /* Make it positive for later */
  434.  
  435. EexpSigned:
  436. I       stosb
  437. I       xchg    ax, bx
  438.  
  439. /*
  440.         ANSI says that "The exponent always contains AT LEAST two digits".
  441.         If the exponent is bigger than 99 then we will use as many as are
  442.         required.
  443. */
  444. I       mov     cx, 3030h               /* "00" this'll be handy later */
  445.  
  446. I       cmp     ax, 99
  447. I       jna     EtwoDigits
  448.  
  449. I       cmp     ax, 999
  450. I       jna     EthreeDigits
  451.  
  452. EfourDigits:
  453. I       cwd                     /* Sign extend AX through DX */
  454. I       mov     bx, 1000
  455. I       div     bx
  456. I       add     al, cl
  457. I       stosb
  458. I       xchg    ax, dx
  459.  
  460. EthreeDigits:
  461. I       mov     bl, 100
  462. I       div     bl
  463. I       add     al, cl
  464. I       stosb
  465. I       xchg    al, ah
  466. I       cbw
  467.  
  468. EtwoDigits:
  469. I       mov     bl, 10
  470. I       div     bl
  471. I       add     ax, cx          /* '00' */
  472. I       stosw
  473.  
  474. /* Both formats are terminated with \0. */
  475.  
  476. end:
  477. I       xor     al,al
  478. I       stosb
  479.  
  480. I       pop     ES
  481.  
  482.         return;
  483. }
  484. #pragma warn .use
  485.  
  486.  
  487. /*--------------------------------------------------------------------------*
  488.  
  489. Description     __realcvt is essentially used by printf family functions. In
  490.                 order  to  include  the  real  conversion  function only if
  491.                 needed (using %f, %e or  %g format), __realcvt is not called
  492.                 directly but via a pointer to function which is declared in
  493.                 the startup code  in an extra segment _CVTSEG.
  494.  
  495.                 This module  is included at link  time only if there  is at
  496.                 least one floating point number, because in that case there
  497.                 are external  references to the  fixups FI?RQQ which  force
  498.                 the emulator to be linked  in. The emulator has an external
  499.                 reference to the value  __turboCvt which forces this module
  500.                 to be linked in.
  501.  
  502. *---------------------------------------------------------------------------*/
  503.  
  504. #define CodeSeg _TEXT
  505. #define cPtr    dw
  506.  
  507. #pragma warn -rvl
  508. #pragma warn -use
  509. #pragma warn -asm
  510. static void near turboCvt(void)
  511. {
  512. I       CodeSeg ENDS
  513. I       PUBLIC  __turboCvt
  514. I       __turboCvt      equ     0
  515. I       _CVTSEG SEGMENT PUBLIC WORD 'DATA'
  516. I       cPtr    __realcvt
  517. I       _CVTSEG ENDS
  518. I       CodeSeg SEGMENT
  519. }
  520.