home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c065 / 2.ddi / CLIB2.ZIP / VPRINTER.CAS < prev    next >
Encoding:
Text File  |  1990-06-07  |  30.2 KB  |  1,143 lines

  1. /*-----------------------------------------------------------------------*
  2.  * filename - vprinter.cas
  3.  *
  4.  * function(s)
  5.  *      Hex4         - converts int to 4 hex digits
  6.  *      __vprinter - sends formatted output
  7.  *-----------------------------------------------------------------------*/
  8.  
  9. /*[]------------------------------------------------------------[]*/
  10. /*|                                                              |*/
  11. /*|     Turbo C Run Time Library - Version 3.0                   |*/
  12. /*|                                                              |*/
  13. /*|                                                              |*/
  14. /*|     Copyright (c) 1987,1988,1990 by Borland International    |*/
  15. /*|     All Rights Reserved.                                     |*/
  16. /*|                                                              |*/
  17. /*[]------------------------------------------------------------[]*/
  18.  
  19. #pragma  inline
  20. #include <stdio.h>
  21. #include <asmrules.h>
  22. #include <_printf.h>
  23.  
  24. #define I asm
  25.  
  26. #define BREAKPOINT    asm int 3
  27.  
  28. static    char    NullString[] = "(null)";
  29.  
  30. /*-----------------------------------------------------------------------*
  31.  
  32. Name        Hex4 - converts int to 4 hex digits
  33.  
  34. Usage        static void near pascal Hex4( void )
  35.  
  36. Description    Convert 16 bit parameter (in dx) to 4 hex digits at ES: [di].
  37.  
  38.         NOTE: TC does not realize that "stosb" implies DI, so DI is
  39.         not pushed/popped.  That is nice, but one day it may cease
  40.         to be true...
  41.  
  42.         The calling code expects di to be incremented by 4 as a
  43.         side effect of this function.
  44.  
  45. *------------------------------------------------------------------------*/
  46. I _TEXT    segment
  47. I  Hex4    proc near
  48. I          mov     al,dh
  49. I          call    Byte2Ascii
  50. I          mov     al,dl
  51.  
  52. I Byte2Ascii:                         /* convert byte in al to ASCII */
  53. I          db      0d4h,10h           /* AAM trick to separate nibbles in al */
  54. I          xchg    ah,al
  55. I          call    Nibble2Ascii
  56. I          xchg    ah,al
  57.  
  58. I Nibble2Ascii:                       /* convert hex digit in al to ASCII */
  59. I          add     al,90h
  60. I          daa
  61. I          adc     al,40h
  62. I          daa
  63. I          stosb
  64. I          ret
  65. I          endp
  66. I          ends
  67.  
  68. /*-----------------------------------------------------------------------*
  69.  
  70. __vprinter is a table-driven design, for speed and flexibility. There are
  71. two tables.  The first table classifies all 7-bit ASCII chars and then the
  72. second table is the switch table which points to the function blocks which
  73. handle the various classes of characters.
  74.  
  75. All characters with the 8th bit set are currently classed as don't cares,
  76. which is the class of character also used for normal alphabetics.  All
  77. characters less than ' ' (0x20) are also classed as don't cares.
  78.  
  79. *------------------------------------------------------------------------*/
  80.  
  81. typedef
  82.     enum
  83.     {
  84.         _si,    /* sign fill +/-    */
  85.         _af,    /* alternate form    */
  86.         _ar,    /* format (width or precision) by argument */
  87.         _lj,    /* left justify     */
  88.  
  89.         _pr,    /* precision        */
  90.         _nu,    /* numeral        */
  91.         _lo,    /* long         */
  92.         _ld,    /* long double        */
  93.         _sh,    /* short        */
  94.         _fz,    /* fill zeros        */
  95.  
  96.         _de,    /* decimal        */
  97.         _oc,    /* octal        */
  98.         _un,    /* unsigned decimal    */
  99.         _he,    /* hexadecimal        */
  100.  
  101.         _pt,    /* pointer        */
  102.         _fl,    /* float        */
  103.         _ch,    /* char         */
  104.         _st,    /* string        */
  105.  
  106.         _ns,    /* number sent        */
  107.         _zz,    /* terminator        */
  108.         _dc,    /* don't care        */
  109.         _pc,    /* percent        */
  110.  
  111.         _ne,    /* near pointer     */
  112.         _fa,    /* far pointer        */
  113.     } characterClass;
  114.  
  115.     /*  Here is the table of classes, indexed by character. */
  116.  
  117. static unsigned char printCtype [96] =
  118. {
  119. /*     SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /  */
  120.     _si,_dc,_dc,_af,_dc,_pc,_dc,_dc,_dc,_dc,_ar,_si,_dc,_lj,_pr,_dc,
  121.  
  122. /*      0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?  */
  123.     _fz,_nu,_nu,_nu,_nu,_nu,_nu,_nu,_nu,_nu,_dc,_dc,_dc,_dc,_dc,_dc,
  124.  
  125. /*      _   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O  */
  126.     _dc,_dc,_dc,_dc,_dc,_fl,_fa,_fl,_sh,_dc,_dc,_dc,_ld,_dc,_ne,_dc,
  127.  
  128. /*      P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _  */
  129.     _dc,_dc,_dc,_dc,_dc,_dc,_dc,_dc,_he,_dc,_dc,_dc,_dc,_dc,_dc,_dc,
  130.  
  131. /*      `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o  */
  132.     _dc,_dc,_dc,_ch,_de,_fl,_fl,_fl,_sh,_de,_dc,_dc,_lo,_dc,_ns,_oc,
  133.  
  134. /*      p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~ DEL  */
  135.     _pt,_dc,_dc,_st,_dc,_un,_dc,_dc,_he,_dc,_dc,_dc,_dc,_dc,_dc,_dc,
  136. };
  137.  
  138.  
  139. /*---------------------------------------------------------------------*
  140.  
  141. Name        __vprinter - sends formatted output
  142.  
  143. Usage        int   pascal __vprinter (putnF    *putter,
  144.                      void    *outP,
  145.                      const char   *formP,
  146.                      void _ss *argP)
  147.  
  148. Prototype in    _printf.h
  149.  
  150. Description    The list of arguments *argP is combined with literal text in
  151.         the format string *formP according to format specifications
  152.         inside the format string.
  153.  
  154.         The supplied procedure *putter is used to generate the output.
  155.         It is required to take the string S, which has been
  156.         constructed by __vprinter, and copy it to the destination
  157.         outP.  The destination may be a string, a FILE, or whatever
  158.         other class of construct the caller requires.  It is possible
  159.         for several calls to be made to *putter since the buffer S
  160.         is of limited size.
  161.  
  162.         *putter is required to preserve  SI, DI.
  163.  
  164.         The only purpose of the outP argument is to be passed through
  165.         to putter.
  166.  
  167.         The object at *argP is a record of unknown structure, which
  168.         structure is interpreted with the aid of the format string.
  169.         Each field of the structure may be an integer, long, double,
  170.         or string (char *).  Chars may appear but occupy integer-sized
  171.         cells.    Floats, character arrays, and structures may not
  172.         appear.
  173.  
  174. Return value    The result of the function is a count of the characters sent to
  175.         *outP.
  176.  
  177.         There is no error indication.  When an incorrect conversion
  178.         spec is encountered __vprinter copies the format as a literal
  179.         (since it is assumed that alignment with the argument list has
  180.         been lost), beginning with the '%' which introduced the bad
  181.         format.
  182.  
  183.         If the destination outP is of limited size, for example a
  184.         string or a full disk, __vprinter does not know.  Overflowing
  185.         the destination causes undefined results.  In some cases
  186.         *putter is able to handle overflows safely, but that is not
  187.         the concern of __vprinter.
  188.  
  189.         The syntax of the format string is:
  190.  
  191.         format ::=    ([literal] ['%' conversion ])* ;
  192.  
  193.         conversion ::=    '%' | [flag]* [width] ['.' precision]
  194.                       ['l'] type ;
  195.  
  196.         flag ::=    '-' | '+' | ' ' | '#' | '0' ;
  197.  
  198.         width ::=    '*' | number ;
  199.         precision ::=    '.' ('*' | number) ;
  200.  
  201.         type ::=    'd'|'i'|'o'|'u'|'x'|'n'|'X'|'f'|'e'|'E'|
  202.                 'g'|'G'|'c'|'s'|'p'|'N'|'F'
  203.  
  204. *---------------------------------------------------------------------*/
  205. int   pascal near __vprinter (putnF near *putter,
  206.                   void       *outP,
  207.                   const char *formP,
  208.                   void _ss     *argP)
  209. {
  210.  
  211. #define Ssize 80
  212.  
  213.     typedef
  214.         enum
  215.         {
  216.         flagStage, fillzStage, wideStage, dotStage, precStage,
  217.         ellStage, typeStage,
  218.         } syntaxStages;
  219.  
  220.     typedef
  221.         enum
  222.         {
  223.         altFormatBit = 1,    /* the '#' flag         */
  224.         leftJustBit  = 2,    /* the '-' flag         */
  225.         notZeroBit   = 4,    /* 0 (octal) or 0x (hex) prefix */
  226.         fillZerosBit = 8,    /* zero fill width        */
  227.         isLongBit    = 16,    /* long-type argument        */
  228.         farPtrBit    = 32,    /* far pointers         */
  229.         alt0xBit     = 64,    /* '#' confirmed for %x format    */
  230.         floatBit     = 128,    /* float arg 4 bytes not 8!    */
  231.         LongDoubleBit= 256    /* signal a long double argument*/
  232.         } flagBits;
  233.  
  234.     flagBits flagSet;
  235.  
  236.     unsigned aP;
  237.     char     fc;            /* format char, from fmt string */
  238.     char     isSigned;        /* chooses signed/unsigned ints */
  239.     int     width;
  240.     int     precision;
  241.     char     plusSign;
  242.     int     leadZ;
  243.     unsigned abandonP;        /* posn of bad syntax in fmt str*/
  244.     char     tempStr [__CVTMAX__];    /* longest _realcvt or longtoa string*/
  245.     unsigned totalSent = 0;        /* characters sent to putter    */
  246.     unsigned Scount = Ssize;     /* free space remaining in S    */
  247.     char     S [Ssize];        /* temp working result buffer    */
  248.     int     eof_error = 0;        /* flag, TRUE is EOF error    */
  249.  
  250. #if 0    /* the remaining variables are held entirely in registers    */
  251.  
  252.     char    hexCase;        /* upper/lower Hex alphabet    */
  253.     long    tempL;
  254.     syntaxStages    stage; -- CH
  255.     char        c;
  256.     char        *cP;
  257.     int        *iP;
  258. #endif
  259.  
  260.         SaveSI
  261.         SaveDI
  262. /*
  263. General outline of the method:
  264.  
  265. First the string is scanned, and conversion specifications detected.
  266.  
  267. The preliminary fields of a conversion (flags, width, precision, long)
  268. are detected and noted.
  269.  
  270. The argument is fetched and converted, under the optional guidance of
  271. the values of the preliminary fields.  With the sole exception of the
  272. 's' conversion, the converted strings are first placed in the tempStr
  273. buffer.
  274.  
  275. The contents of the tempStr (or the argument string for 's') are copied
  276. to the output, following the guidance of the preliminary fields in
  277. matters such as zero fill, field width, and justification.
  278. */
  279.  
  280. I    jmp short func_start    /* Skip over inline nested PROCs    */
  281.  
  282. /***************************************************************************
  283.         local, nested functions are placed here
  284.         ES *must* be defined in all models.
  285. ***************************************************************************/
  286.  
  287. #define RETN    db    0xC3    /* "RETN" is used to force a near ret    */
  288.  
  289. /* Get length of string pointed to by ES:DI, return result in CX.    */
  290.  
  291. I vpr_strlen LABEL NEAR     /* scan string ES: [DI] up to \0    */
  292. I    push    di
  293. I    mov    cx, -1        /*   count the string length.        */
  294. I    xor    al, al
  295. I    repne    scasb
  296. I    not    cx        /* (not CX) == (-1 -CX)         */
  297. I    dec    cx        /* scasb overshoots            */
  298. I    pop    di
  299. I    RETN
  300.  
  301.  
  302. /* call *putter to flush S */
  303. /* Put character to next position in S, check for S full */
  304.  
  305. I vpr_PutToS LABEL NEAR
  306. I    mov    BY0 (ss:[di]), al
  307. I    inc    di
  308. I    dec    BY0 (Scount)
  309. I    jle    exit_PutToS
  310.  
  311. I vpr_CallPutter LABEL NEAR
  312. I    push    bx
  313. I    push    cx
  314. I    push    dx
  315. I    push    ES
  316. I    lea    ax, S
  317. I    sub    di, ax        /* count chars in S */
  318.  
  319.     if( !putter (S, _DI, outP) )
  320.       {
  321.       eof_error = 1;
  322.       }
  323.  
  324.     Scount = Ssize;
  325.     totalSent += _DI;
  326.  
  327. I    lea    di, S
  328. I    pop    ES
  329. I    pop    dx
  330. I    pop    cx
  331. I    pop    bx
  332.  
  333. exit_PutToS :
  334. I    RETN            /* 'shared' RET for both PROCs */
  335.  
  336. /************************ end of embedded functions *******************/
  337.  
  338. func_start:
  339.  
  340. I    push    ES
  341. I    cld
  342.  
  343. I    lea    di, S
  344. I    mov    aP, di
  345.  
  346. /*
  347. This paragraph is arranged to give in-line flow to the most frequent
  348. case, literal transcription from *formP to *outP.
  349. */
  350.  
  351. vpr_NEXTap:
  352. I    mov    di, aP
  353.  
  354. vpr_NEXT:            /* loop to here when DI still valid */
  355. I    LES_    si, formP
  356.  
  357.     for (;;)        /* resume here from this literal/space */
  358.     {
  359. vpr_nextCh :
  360. I        lods    BY0 (ES_ [si])    /* if (pattern[x] == '\0')    */
  361. I        or    al, al        /*    goto exit        */
  362. I        jz    vpr_respondJmp
  363.  
  364. I        cmp    al, '%'     /* '%' char begins a conversion */
  365. I        je    vpr_CONV
  366.  
  367. vpr_literal:                /* but "%%" is just a literal '%'. */
  368. I        mov    ss:[di], al
  369. I        inc    di
  370.  
  371. I        dec    BY0 (Scount)    /* if (--Scount)    */
  372. I        jg    vpr_nextCh    /*    continue;    */
  373.  
  374. I        call    vpr_CallPutter    /* Compact way(smaller)     */
  375.     }
  376.  
  377. vpr_respondJmp:
  378. I    jmp    vpr_respond
  379.  
  380. /* If arrived here then a conversion specification has been encountered. */
  381.  
  382. vpr_CONV:
  383. I    mov    W0 (abandonP), si    /* abandon will print from here */
  384.  
  385. I    lods    BY0 (ES_ [si])        /* AL <- char at ES:SI        */
  386. I    cmp    al, '%'         /* %% really means %        */
  387. I    je    vpr_literal
  388.  
  389. I    mov    W0 (aP), di        /* keep result pointer safe    */
  390.  
  391. I    xor    cx, cx            /* CH is flagStage        */
  392. I    mov    W0 (leadZ), cx        /* leadZ     <- 0        */
  393. #if LDATA
  394. I    mov    W0 (flagSet), farPtrBit /* flagSet   <- pointers are FAR*/
  395. #else
  396. I    mov    W0 (flagSet), cx    /* flagSet   <- 0        */
  397. #endif
  398. I    mov    BY0 (plusSign), cl    /* plusSign  <- 0        */
  399. I    mov    W0 (width), -1        /* width     <- default     */
  400. I    mov    W0 (precision), -1    /* precision <- default     */
  401. I    jmp    short vpr_doSwitch
  402.  
  403.         /*==================================*/
  404.         /* loop to here when scanning flags */
  405.         /*==================================*/
  406.  
  407. vpr_nextSwitch:
  408. I    lods    BY0 (ES_ [si])        /* AL <- char at ES:SI        */
  409.  
  410. /**************************************************************************
  411.  *           Main character classification switch           *
  412.  **************************************************************************/
  413.  
  414. vpr_doSwitch:
  415. I    xor    ah, ah            /* Remove any high order trash    */
  416. I    mov    dx, ax            /* Duplicate original char in DL*/
  417. I    mov    bx, ax            /* Duplicate original char in BL*/
  418. I    sub    bl, ' '         /* Scale char in BL to 0-95    */
  419. I    cmp    bl, 128 - ' '        /* Weed out don't cares     */
  420. I    jae    vpr_jmpAbandon
  421. I    mov    bl, BY0 (printCtype [bx]) /* BL <- char type of the char*/
  422.  
  423.     switch    (_BX)            /* =-=>  clobbers AX, BX  <=-=    */
  424.     {
  425.  
  426. vpr_jmpAbandon:             /* Extend local jump range    */
  427. I    jmp    vpr_abandon
  428.  
  429. case (_af):                /* when '#' was seen        */
  430. I    cmp    ch, flagStage
  431. I    ja    vpr_jmpAbandon
  432. I    or    W0 (flagSet), altFormatBit
  433. I    jmp    vpr_nextSwitch
  434.  
  435. case (_lj):                /* when '-' was seen        */
  436. I    cmp    ch, flagStage
  437. I    ja    vpr_jmpAbandon
  438. I    or    W0 (flagSet), leftJustBit
  439. I    jmp    vpr_nextSwitch
  440.  
  441. case (_si):                /* when ' ' or '+' was seen    */
  442. I    cmp    ch, flagStage
  443. I    ja    vpr_jmpAbandon
  444. I    cmp    BY0 (plusSign), 2Bh    /* '+'                */
  445. I    je    gtvp_nxt_swit        /* ' ' ignored if '+' already    */
  446. I    mov    plusSign, dl
  447.  
  448. gtvp_nxt_swit:
  449. I    jmp    vpr_nextSwitch
  450.  
  451. case (_ne):                /* near pointer         */
  452. I    and    W0 (flagSet), NOT farPtrBit
  453. I    jmp    short ell_stagetail
  454.  
  455. case (_fa):                /* far pointer            */
  456. I    or    W0 (flagSet), farPtrBit
  457. ell_stagetail:
  458. I    mov    ch, ellStage
  459. I    jmp    vpr_nextSwitch
  460.  
  461.  
  462. case (_fz):                /* leading width '0' is a flag    */
  463. I    cmp    ch, flagStage
  464. I    ja    case_nu         /*   else it is just a digit    */
  465. I    test    W0 (flagSet), leftJustBit
  466. I    jnz    short    vpr_nextSwitchJmp
  467. I    or    W0 (flagSet), fillZerosBit
  468. I    mov    ch, fillzStage        /*   but it must be part of width */
  469. I    jmp    vpr_nextSwitch
  470.  
  471. vpr_abandonJmp:             /* Extend local jump range    */
  472. I    jmp    vpr_abandon
  473.  
  474. case (_ar):                /* when '*' was seen it causes    */
  475. I    mov    di, argP
  476. I    mov    ax, ss:[di]        /*    the next argument to be    */
  477. I    add    W0 (argP), 2        /*     taken, depending on    */
  478. I    cmp    ch, wideStage        /*        the stage, as the    */
  479. I       jnb     vpr_argPrec             /*             width, or...     */
  480.  
  481. I       or      ax,ax                   /* is the width negative?       */
  482. I       jns     vpr_pos
  483. I       neg     ax
  484. I    or    W0 (flagSet), leftJustBit
  485.  
  486. vpr_pos:
  487. I       mov     width, ax
  488. I    mov    ch, wideStage + 1
  489. vpr_nextSwitchJmp:
  490. I    jmp    vpr_nextSwitch
  491.  
  492. vpr_argPrec:
  493. I    cmp    ch, precStage
  494. I    jne    vpr_abandonJmp
  495. I    mov    precision, ax        /*           the precision.    */
  496. I    inc    ch
  497. I    jmp    vpr_nextSwitch
  498.  
  499. case (_pr):                /* when '.' is seen, precision    */
  500. I    cmp    ch, precStage
  501. I    jnb    vpr_abandonJmp
  502. I    mov    ch, precStage        /*    should follow        */
  503. I    jmp    vpr_nextSwitch
  504.  
  505. /*
  506.     When a numeral is seen, it may be either part of a width, or
  507.     part of the precision, depending on the stage.
  508. */
  509. case (_nu):                /* when 0..9 seen        */
  510. case_nu:
  511. I    xchg    ax, dx            /* move char back into AL    */
  512. I    sub    al, '0'         /* turn '0'-'9' to 0-9        */
  513. I    cbw                /* make it a word        */
  514. I    cmp    ch, wideStage        /* is it part of a width spec?    */
  515. I    ja    vpr_precNumeral     /* no, see if it's a precision    */
  516.  
  517. I    mov    ch, wideStage
  518. I    xchg    ax, width
  519. I    or    ax, ax            /* first width digit ?        */
  520. I    jl    vpr_nextSwitchJmp    /*   default width was -1    */
  521. I    shl    ax, 1            /* *2                */
  522. I    mov    dx, ax
  523. I    shl    ax, 1            /* *4                */
  524. I    shl    ax, 1            /* *8                */
  525. I    add    ax, dx            /* (*2 + *8) = *10        */
  526. I    add    width, ax        /* width = (width * 10 + num)    */
  527. I    jmp    vpr_nextSwitch
  528.  
  529. vpr_precNumeral:
  530. I    cmp    ch, precStage        /* is it part of precision spec */
  531. I    jne    vpr_abandonJmp        /* no, it's just a literal    */
  532.  
  533. I    xchg    ax, precision
  534. I    or    ax, ax            /* first precision digit ?    */
  535. I    jl    vpr_nextSwitchJmp    /*   default precision was -1    */
  536. I    shl    ax, 1            /* *2                */
  537. I    mov    dx, ax
  538. I    shl    ax, 1            /* *4                */
  539. I    shl    ax, 1            /* *8                */
  540. I    add    ax, dx            /* (*2 + *8) = *10        */
  541. I    add    precision, ax        /* prec = (prec * 10 + numeral) */
  542. I    jmp    vpr_nextSwitch
  543.  
  544.  
  545. case (_lo):                /* 'l' was seen (long)        */
  546. I    or    W0 (flagSet), isLongBit
  547. I    jmp    ell_stagetail
  548.  
  549. case (_ld):                /* 'L' was seen (long double)    */
  550. I    or    W0 (flagSet), LongDoubleBit
  551.  
  552. case (_sh):                /* 'h' or 'H' was seen (short)    */
  553. I    and    W0 (flagSet), not isLongBit
  554. I    jmp    ell_stagetail
  555.  
  556. /*--------------------------------------------------------------------------
  557. The previous cases covered all the possible flags.  Now the following
  558. cases deal with the different argument types.
  559.  
  560. Remember DL contains a copy of the original character.
  561. --------------------------------------------------------------------------*/
  562.  
  563.     /*==========================================================*/
  564.     /* The first group of cases is for the integer conversions. */
  565.     /*==========================================================*/
  566.  
  567. case (_oc):                /* octal            */
  568. I    mov    bh, 8            /* BH <- Base 8         */
  569. I    jmp    short vpr_NoSign
  570.  
  571. case (_un):                /* unsigned            */
  572. I    mov    bh, 10            /* BH <- Base 10        */
  573. I    jmp    short vpr_UINT
  574.  
  575.  
  576. case (_he):                /* hex                */
  577. I    mov    bh, 10h         /* BH <- Base 16        */
  578. I    mov    bl, 'A' - 'X'
  579. I    add    bl, dl            /* Adjust for aAbBcC etc later    */
  580.  
  581. vpr_NoSign:
  582. I    mov    BY0 (plusSign), 0    /* It's an unsigned operand    */
  583. /*    jmp    short    vpr_UINT */
  584.  
  585.  
  586. vpr_UINT:
  587. I    mov    fc, dl            /* 'fc' <- orig fmt char    */
  588. I    xor    dx, dx            /* zero extend by default    */
  589. I    mov    BY0 (isSigned), dl
  590. I    mov    di, argP
  591. I    mov    ax, ss:[di]        /* AX <- word arg at SS:DI    */
  592. I    jmp    short vpr_toAscii
  593.  
  594.  
  595. case (_de):                /* decimal            */
  596. I    mov    bh, 10            /* BH <- Base 10        */
  597. vpr_INT:
  598. I    mov    BY0 (isSigned), 1
  599. I    mov    fc, dl            /* 'fc' <- orig fmt char    */
  600.  
  601. I    mov    di, argP        /* SS:DI -> argument word[0]    */
  602. I    mov    ax, ss:[di]        /* AX <- word arg at SS:DI    */
  603. I    cwd                /* sign-extend by default    */
  604.  
  605. vpr_toAscii:
  606. I    inc    di            /* SS:DI -> next arg word    */
  607. I    inc    di
  608.  
  609. I    mov    formP, si        /* remember progress through format */
  610.  
  611. I    test    W0 (flagSet),isLongBit    /* short or long int ?        */
  612. I    je    vpr_shortInt        /* If the operand is a long    */
  613. I    mov    dx, ss:[di]        /* DX:AX holds long argument    */
  614. I    inc    di            /* SS:DI -> next arg word    */
  615. I    inc    di
  616.  
  617. vpr_shortInt:
  618. I    mov    argP, di        /* Save arg list pointer    */
  619.  
  620. I    lea    di, tempStr [1]     /* (SS) DI <- &tempStr[1]    */
  621.  
  622. I    or    ax, ax            /* Is the value zero?        */
  623. I    jnz    vpr_flag_nz        /* No,                */
  624. I    or    dx, dx            /* Is the value zero?        */
  625. I    jnz    vpr_flag_nz        /* No,                */
  626.  
  627. /*-----------------------------------------------------------------------
  628.     Check for the special ANSI case of a zero value with an explicit
  629.     precision of zero.
  630. ------------------------------------------------------------------------*/
  631.  
  632. I    cmp    W0 (precision), 0    /* Is it the special case?    */
  633. I    jne    vpr_doLtoA        /* No, continue         */
  634.  
  635. /*
  636.   Yes...this is the ANSI special case where precision is zero and the value
  637.   is zero.  This will result in NO conversion characters being produced.
  638.   The way we do this is:
  639.     1. Deal with degenerate case of 0 field width. ex. "%00.0x"
  640.     2. Determine if leading zeros are to be forced anyway. For example
  641.        this could occur with printf("%04.0x", 0);  In this case we have
  642.        a zero result with zero precision but the '0' flag char will
  643.        cause us to fill the field with 0s anyway.
  644.     3. Fill for 'width' characters with either '0' or ' ' depending on
  645.        if leading zeros were specified.
  646.     4. Terminate this conversion field.
  647. */
  648. I    mov    di, aP            /* SS:DI -> somewhere in 'S'    */
  649. I    mov    cx, width        /* If he asked for nothing,    */
  650. I    jcxz    ANSI_special_end    /*    then give him nothing.    */
  651. I    cmp    cx, -1            /* Was width defaulted?     */
  652. I    je    ANSI_special_end    /* Assume width = 0        */
  653.  
  654. I    mov    ax, W0 (flagSet)    /* fillChar =            */
  655. I    and    ax, fillZerosBit    /*   (flagSet & fillZerosBit) ? */
  656. I    jz    blankfill        /*                */
  657. I    mov    dl, '0'         /*   fillChar = '0';        */
  658. I    jmp    short fillAnother
  659.  
  660. blankfill:
  661. I    mov    dl, ' '         /*   fillChar = ' ';        */
  662.  
  663.     /* for (i=0; i<width; i++) PutToS( fillChar );    */
  664.  
  665. fillAnother:
  666. I    mov    al, dl            /* AL <- fillChar        */
  667. I    call    vpr_PutToS        /* This clobbers AX        */
  668. I    loop fillAnother        /* CX is safe though        */
  669.  
  670. ANSI_special_end:
  671. I    jmp    vpr_NEXT        /* That's the end of this field */
  672.  
  673. /*-------------------------------------------------------------------------
  674.     "Normal" integer output cases wind up down here somewhere.
  675. -------------------------------------------------------------------------*/
  676.  
  677. vpr_flag_nz:
  678. I    or    W0 (flagSet), notZeroBit /* flag non-zeroness        */
  679.  
  680. vpr_doLtoA:
  681. I    push    dx            /* Push long value (AX:DX)    */
  682. I    push    ax
  683.  
  684. #if LDATA
  685. I    push    SS
  686. #endif
  687. I    push    di            /*    ie. &tempStr[1]        */
  688.  
  689. I    mov    al, bh            /* AL <- numeric base/radix    */
  690. I    cbw                /* Convert it to a word     */
  691. I    push    ax            /* Push the radix        */
  692.  
  693. I    mov    al, isSigned        /* Push the isSigned flag,    */
  694. I    push    ax            /* AH should still be 0     */
  695.  
  696. I    push    bx            /* BL ==    , hexCase)    */
  697.  
  698. /***    __longtoa(number, string, radix, signflag, hexcase)          ***/
  699.  
  700. I    call    __longtoa        /* returns pointer to string    */
  701.  
  702. I    push    SS
  703. I    pop    ES            /* ES_ [di] = cP == 1+tempStr    */
  704.                     /* ES is needed in all models    */
  705.  
  706. I    mov    dx, precision        /* Is precision > 0 ?        */
  707. I    or    dx, dx
  708. I    jg    vpr_countActualJmp    /* Yes.             */
  709. I    jmp    vpr_testFillZeros    /* No.                */
  710.  
  711. vpr_countActualJmp:
  712. I    jmp    vpr_countActual
  713.  
  714. /*
  715. The 'p' conversion takes either a near or a far pointer and puts
  716. it out in the usual Intel xxxx:xxxx hex style.
  717. */
  718. case (_pt):                /* pointer    */
  719. I    mov    fc, dl            /* remember the type character. */
  720. I    mov    formP, si        /* remember progress through format */
  721. I    lea    di, tempStr
  722.  
  723. I    mov    bx, argP
  724. I    push    ss:[bx]         /* fetch the argument.w0 */
  725. I    inc    bx
  726. I    inc    bx
  727. I    mov    argP, bx
  728.  
  729. I    test    W0 (flagSet), farPtrBit
  730. I    jz    vpr_ptrLSW
  731.  
  732. I    mov    dx, ss:[bx]        /* fetch the argument.w1 */
  733. I    inc    bx
  734. I    inc    bx
  735. I    mov    argP, bx
  736. I    push    SS
  737. I    pop    ES
  738. I    call    Hex4
  739.  
  740. /*    add    di, 4            Hex4 does this        */
  741.  
  742. I    mov    al, ':'
  743. I    stosb
  744.  
  745. vpr_ptrLSW:
  746. I    push    SS
  747. I    pop    ES
  748. I    pop    dx
  749. I    call    Hex4
  750.  
  751. /*    add    di, 4            Hex4 does this        */
  752.  
  753. I    mov    BY0 (ss:[di]), 0
  754.  
  755. I    mov    BY0 (isSigned), 0
  756. I    and    W0 (flagSet), NOT notZeroBit
  757.  
  758. I    lea    cx, tempStr
  759. I    sub    di, cx
  760. I    xchg    cx, di        /* CX = len,  DI = tempStr    */
  761.  
  762. I    mov    dx, precision
  763. I    cmp    dx, cx
  764. I    jg    vpr_ptrEnd
  765. I    mov    dx, cx
  766.  
  767. vpr_ptrEnd:
  768. I    jmp    vpr_testFillZeros
  769.  
  770.  
  771. /*
  772. The 'c' conversion takes a character as parameter.  Note, however,
  773. that the character occupies an (int) sized cell in the argument
  774. list.
  775. */
  776.  
  777. case (_ch):            /* char     */
  778. I    mov    formP, si    /* remember progress through format */
  779. I    mov    fc, dl        /* remember the type character */
  780.  
  781. I    mov    di, argP
  782. I    mov    ax, ss:[di]
  783. I    add    W0 (argP), 2
  784.  
  785. I    push    SS
  786. I    pop    ES
  787. I    lea    di, tempStr [1]
  788. I    xor    ah, ah        /* terminate the temporary string. */
  789. I    mov    ss:[di], ax
  790. I    mov    cx, 1
  791. I    jmp    vpr_CopyLen
  792.  
  793. /*
  794. The 's' conversion takes a string (char *) as argument and copies
  795. the string to the output buffer.
  796. */
  797.  
  798. case (_st):            /* string    */
  799. I    mov    formP, si    /* remember progress through format */
  800. I    mov    fc, dl        /* remember the type character */
  801.  
  802. I    mov    di, argP
  803. I    test    W0 (flagSet), farPtrBit
  804. I    jnz    vpr_farString
  805. #ifdef __HUGE__
  806. I    jmp    vpr_abandonJmp    /* DS can't be assumed in HUGE model */
  807. #else
  808. I    mov    di, ss:[di]    /* [di] = (DS:char *) *(argP++) */
  809. I    add    W0 (argP), 2
  810. I    push    DS
  811. I    pop    ES
  812. I    or    di, di
  813. I    jmp    short vpr_countString
  814. #endif
  815. vpr_farString:
  816. I    les    di, ss:[di]    /* ES: [di] = (char far *) *(argP++) */
  817. I    add    W0 (argP), 4
  818. I    mov    ax, es
  819. I    or    ax, di
  820.  
  821. vpr_countString:
  822. I    jnz    NotaNullPtr
  823. I    push    DS
  824. I    pop    ES
  825. I    mov    di,offset NullString
  826.  
  827. NotaNullPtr:
  828. I    call    vpr_strlen        /* CX = strlen (ES: [di]) */
  829. I    cmp    cx, precision
  830. I    jna    vpr_CopyLenJmp
  831. I    mov    cx, precision        /* precision may truncate string. */
  832.  
  833. vpr_CopyLenJmp:
  834. I    jmp    vpr_CopyLen
  835.  
  836.  
  837. /* All real-number conversions are done by __realcvt. */
  838.  
  839. case (_fl):            /* float    */
  840. I    mov    formP, si    /* remember progress through format */
  841. I    mov    fc, dl        /* remember the type character */
  842.  
  843. I    mov    di, argP
  844.  
  845. I    mov    cx, precision
  846. I    or    cx, cx        /* is precision defaulted ? */
  847. I    jnl    vpr_cvtReal
  848. I    mov    cx, 6
  849.  
  850. vpr_cvtReal:
  851. #if LDATA
  852. I    push    SS
  853. #endif
  854. I    push    di        /* (valueP */
  855. I    push    cx        /*    , ndec */
  856. #if LDATA
  857. I    push    SS
  858. #endif
  859. I    lea    bx, tempStr [1]
  860. I    push    bx            /*    , cP */
  861. I    push    dx            /*    , formCh */
  862. I    mov    ax, altFormatBit
  863. I    and    ax, W0 (flagSet)
  864. I    push    ax            /*    , altFormat) */
  865.  
  866. /*
  867.     Determine the argument type double/long double.
  868.     Save the size of the argument type.
  869. */
  870. I    mov    ax, W0 (flagSet)
  871.  
  872. try_long :
  873. I    test    ax, LongDoubleBit
  874. I    jz    its_plain_double
  875. I    mov    ax, F_10byteFloat
  876. I    add    W0 (argP), 10        /* argP += sizeof(long double) */
  877. I    jmp    short push_argtype
  878.  
  879. its_plain_double :
  880. I    add    W0 (argP), 8        /* argP += sizeof(double) */
  881. I    mov    ax, F_8byteFloat
  882.  
  883. push_argtype :
  884. I    push    ax
  885.  
  886. I    call    __realcvt
  887.  
  888. I    push    SS
  889. I    pop    ES
  890. I    lea    di, tempStr [1]     /* ES_ [di] = cP == 1+tempStr */
  891.  
  892. vpr_testFillZeros:
  893. I    test    W0 (flagSet), fillZerosBit
  894. I    jz    vpr_NUMERIC
  895. I    mov    dx, width
  896. I    or    dx, dx
  897. I    jng    vpr_NUMERIC
  898.  
  899. vpr_countActual:            /* Get strlen of result string    */
  900. I    call    vpr_strlen        /* CX <- strlen (ES:[DI])    */
  901.  
  902. I    cmp    BY0 (ES: [di]), '-'    /* Is the number negative?    */
  903. I    jne    calc
  904. I    dec    cx            /* decrement the string length    */
  905. calc:
  906. I    sub    dx, cx            /* DX <- leading 0 count(if any)*/
  907. I    jng    vpr_NUMERIC        /* leadZ defaulted to 0 before    */
  908. I    mov    leadZ, dx        /* leadZ <- (width or prec-len) */
  909.  
  910.  
  911. /*
  912. If arrived here, then tempStr contains the result of a numeric
  913. conversion.  It may be necessary the prefix the number with
  914. a mandatory sign or space.
  915. */
  916.  
  917. vpr_NUMERIC:                /* ES must be well defined !    */
  918. I    mov    al, plusSign        /* Do we need a sign?        */
  919. I    or    al, al
  920. I    jz    vpr_COPY        /* No, not required        */
  921. I    cmp    BY0 (ES: [di]), '-'
  922. I    je    vpr_COPY        /* No, its there already    */
  923.  
  924. I    sub    W0 (leadZ), 1        /* Yes, leading 0's are 1 less    */
  925. I    adc    W0 (leadZ), 0        /* don't allow negative leadZ    */
  926.  
  927. I    dec    di            /* Back up 1 in the string    */
  928. I    mov    ES: [di], al        /*    and insert the sign    */
  929.  
  930. /*
  931. If arrived here then ES: [di] = cP points to the converted string,
  932. which must now be padded, aligned, and copied to the output.
  933. */
  934.  
  935. vpr_COPY:
  936. I    call    vpr_strlen        /* CX = strlen (ES: [di])    */
  937.  
  938. vpr_CopyLen:                /* comes from %c or %s section    */
  939. I    mov    si, di            /* cP <- ES: [si]        */
  940. I    mov    di, aP
  941.  
  942. I    mov    bx, width        /* BX <- width            */
  943.  
  944. I    mov    ax, notZeroBit + altFormatBit
  945. I    and    ax, W0 (flagSet)
  946. I    cmp    ax, notZeroBit + altFormatBit
  947. I    jne    goto_doLead
  948. I    mov    ah, fc            /* AH <- original format char    */
  949.  
  950. I    cmp    ah, 'o'         /* Is it alternate octal form?    */
  951. I    jne    vpr_maybeAltHex     /* No, try the next one     */
  952. I    cmp    W0 (leadZ), 0        /* Yes, alternate mode w/octal    */
  953. I    jg    goto_doLead        /*   requires at least        */
  954. I    mov    W0 (leadZ), 1        /*     one leading zero.    */
  955.  
  956. goto_doLead:
  957. I    jmp    vpr_doLead;
  958.  
  959. vpr_maybeAltHex:
  960. I    cmp    ah, 'x'         /* Is it alternate hex form?    */
  961. I    je    vpr_isAltHex        /* Yes, send            */
  962. I    cmp    ah, 'X'         /*    "0x" or "0X" prefix.    */
  963. I    jne    vpr_doLead        /* No, insert leading 0's    */
  964.  
  965. vpr_isAltHex:
  966. I    or    W0 (flagSet), alt0xBit    /* flagSet |= alt0xBit;     */
  967. I    dec    bx            /* width -= 2;            */
  968. I    dec    bx
  969. I    sub    W0 (leadZ), 2        /* leadZ -= 2;            */
  970. I    jnl    vpr_doLead        /* Still leading 0's?        */
  971. I    mov    W0 (leadZ), 0        /* No more leading 0's        */
  972.  
  973. vpr_doLead:
  974. I    add    cx, leadZ        /* CX <- len + leadZ        */
  975.  
  976.         /* Is result to be left justified? */
  977.  
  978. I    test    W0 (flagSet), leftJustBit
  979. I    jnz    vpr_check0x
  980. I    jmp    short vpr_nextJust    /* (! leftJust) == leftFill */
  981.  
  982. vpr_justLoop:
  983. I    mov    al, ' '
  984. I    call    vpr_PutToS
  985. I    dec    bx
  986.  
  987. vpr_nextJust:
  988. I    cmp    bx, cx
  989. I    jg    vpr_justLoop
  990.  
  991. vpr_check0x:
  992. I    test    W0 (flagSet), alt0xBit    /* Need alternate hex form?    */
  993. I    jz     vpr_checkLeadZ        /* No,                */
  994.  
  995. I    mov    al, '0'         /* Yes, Send "0x" or "0X"    */
  996. I    call    vpr_PutToS
  997. I    mov    al, fc            /* original 'fc' is 'x' or 'X'    */
  998. I    call    vpr_PutToS
  999.  
  1000. vpr_checkLeadZ:             /* leading zero fill required ? */
  1001. I    mov    dx, leadZ
  1002. I    or    dx, dx
  1003. I    jng    vpr_actualCopy
  1004.  
  1005. I    sub    cx, dx            /* len -= leadZ */
  1006. I    sub    bx, dx            /* width -= leadZ */
  1007.  
  1008. I    mov    al, ES: [si]        /* any leading sign must be */
  1009. I    cmp    al, '-'         /*   copied before the        */
  1010. I    je    vpr_leadSign        /*     leading zeros.        */
  1011. I    cmp    al, ' '
  1012. I    je    vpr_leadSign
  1013. I    cmp    al, '+'
  1014. I    jne    vpr_signedLead
  1015.  
  1016. vpr_leadSign:
  1017. I    lods    BY0 (ES: [si])        /* Get the 'sign'        */
  1018. I    call    vpr_PutToS        /* Send the 'sign'        */
  1019. I    dec    cx
  1020. I    dec    bx            /* anticipates actualCopy     */
  1021.  
  1022. vpr_signedLead:
  1023. I    xchg    cx, dx            /* leading zeros follow sign */
  1024. I    jcxz    vpr_leadDone
  1025.  
  1026. vpr_leadZero:
  1027. I    mov    al, '0'
  1028. I    call    vpr_PutToS
  1029. I    loop    vpr_leadZero
  1030.  
  1031. vpr_leadDone:
  1032. I    xchg    cx, dx
  1033.  
  1034.  
  1035. /* Now we copy the actual converted string from tempStr to output. */
  1036.  
  1037. vpr_actualCopy:
  1038.  
  1039. I    jcxz    vpr_copied    /* Degenerate case?    */
  1040. I    sub    bx, cx        /* No, width -= len;    */
  1041.  
  1042. vpr_copyLoop:            /* this is the high-point of __vprinter ! */
  1043. I    lods    BY0 (ES: [si])        /* ES:SI -> tempStr    */
  1044. I    mov    BY0 (SS: [di]), al    /* SS:DI -> S[xxx]    */
  1045. I    inc    di
  1046. I    dec    BY0 (Scount)
  1047. I    jg    vpr_loopTest
  1048. I    call    vpr_CallPutter
  1049. vpr_loopTest:
  1050. I    loop    vpr_copyLoop
  1051.  
  1052. vpr_copied:
  1053.  
  1054. /* Is the field to be right-filled ? */
  1055.  
  1056. I    or    bx, bx        /* any remaining width ? */
  1057. I    jng    vpr_done
  1058. I    mov    cx, bx
  1059.  
  1060. vpr_rightLoop:
  1061. I    mov    al, ' '
  1062. I    call    vpr_PutToS
  1063. I    loop    vpr_rightLoop
  1064.  
  1065.  
  1066. /* If arrive here, the conversion has been done and copied to output. */
  1067.  
  1068. vpr_done:
  1069. I    jmp    vpr_NEXT
  1070.  
  1071.  
  1072. case (_ns) :             /* number sent */
  1073. I    mov    formP, si    /* remember progress through format */
  1074.  
  1075. I    mov    di, argP
  1076. I    test    W0 (flagSet), farPtrBit
  1077. I    jnz    vpr_farCount
  1078. #ifdef __HUGE__
  1079. I    jmp    vpr_abandonJmp    /* DS can't be assumed in HUGE model */
  1080. #else
  1081. I    mov    di, ss:[di]  /* [di]=(DS:char *) *((int _ss *)argP++)*/
  1082. I    add    W0 (argP), 2
  1083. I    push    DS
  1084. I    pop    ES
  1085. I    jmp    short vpr_makeCount
  1086. #endif
  1087. vpr_farCount:
  1088. I    les    di, ss:[di]  /* ES:[di] = (char far *) *((long _ss *)argP++) */
  1089. I    add    W0 (argP), 4
  1090.  
  1091. vpr_makeCount:
  1092. I    mov    ax, Ssize
  1093. I    sub    al, Scount
  1094. I    add    ax, totalSent
  1095. I    mov    ES: [di], ax
  1096. I       test    W0(flagSet),isLongBit
  1097. I       jz      vpr_shortN
  1098. I       inc     di
  1099. I       inc     di
  1100. I       mov     word ptr ES: [di], 0
  1101. vpr_shortN:
  1102. I    jmp    vpr_NEXTap
  1103.  
  1104.  
  1105. case (_zz):    /* \0 characters, unexpected end of format string    */
  1106. case (_dc):    /* ordinary "don't care" chars in the wrong position    */
  1107. case (_pc):    /* '%' percent characters in the wrong position     */
  1108. ;        /* goto vpr_abandon    */
  1109. }        /* end switch */
  1110.  
  1111. /*
  1112. If the format goes badly wrong, then copy it literally to the output
  1113. and abandon the conversion.
  1114. */
  1115.  
  1116. vpr_abandon:
  1117. I    mov    si, abandonP
  1118. #if LDATA
  1119. I    mov    ES, W1 (formP)
  1120. #endif
  1121. I    mov    di, aP
  1122. I    mov    al, '%'
  1123.  
  1124. vpr_abandLoop:
  1125. I    call    vpr_PutToS
  1126. I    lods    BY0 (ES_ [si])
  1127. I    or    al, al
  1128. I    jnz    vpr_abandLoop
  1129.  
  1130.  
  1131. /* If arrived here then the function has finished (either correctly or not). */
  1132.  
  1133. vpr_respond:
  1134. I    cmp    BY0 (Scount), Ssize    /* anything waiting to be written ? */
  1135. I    jnl    vpr_end
  1136. I    call    vpr_CallPutter
  1137. vpr_end:
  1138. I    pop    ES
  1139.  
  1140.     if( eof_error )  return( EOF );
  1141.     else         return( totalSent );
  1142. }
  1143.