home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / MISC / REC.ZIP / FLT86F.ASM < prev    next >
Encoding:
Assembly Source File  |  1985-12-02  |  24.8 KB  |  947 lines

  1. ;    ==============================================================
  2. ;
  3. ;    REC module for some of the operators and predicates concerning
  4. ;    numeric operands.  These comprise:
  5. ;
  6. ;    conversion, including compilation of numbers:
  7. ;
  8. ;        [-]{d}.{d} | [-]d{d} | -{d}.{d}<E|D>[-|+]{d} |
  9. ;        [-]d{d}<E|D>[-|+]{d} :  numeric constant
  10. ;        O :    decimal ascii string to number
  11. ;        # :    number to decimal ascii string
  12. ;
  13. ;    arithmetic:
  14. ;
  15. ;        ~ :    complement or negative
  16. ;
  17. ;    --------------------------------------------------------------
  18. ;
  19. ;            FLT - Copyright (c) 1984
  20. ;            Universidad Autonoma de Puebla
  21. ;             All Rights Reserved
  22. ;
  23. ;            [Gerardo Cisneros, 11 April 1984]
  24. ;
  25. ;    ==============================================================
  26.  
  27. ;    Compile a decimal number, which requires reading any
  28. ;    further digits that follow including decimal point and
  29. ;    exponent, and saving the terminator.
  30.  
  31. RECDD:    mov    FRST,al        ;save first character
  32.     push    dx        ;save compilation address
  33.     push    cx        ;save execution address
  34.     call    word ptr read    ;fetch next character
  35.     call    recds        ;build string
  36.     pop    cx        ;recover execution address
  37. DD1:    pop    dx        ;recover compilation pointer
  38.     push    ax        ;save terminating character
  39.     call    recop        ;compile subroutine call
  40.     mov    al,NSIZ        ;get final constant size
  41.     mov    di,dx
  42.     mov    cs:[di],al    ;save in calling sequence
  43.     inc    di
  44.     mov    si,(offset ARG1)
  45.     mov    cl,al
  46.     mov    ch,0
  47.     mov    bp,cs
  48.     call    xf2
  49.     mov    dx,di        ;updated exec ptr back to DX
  50.     pop    ax        ;recover terminating character
  51.     jmp    skp86        ;skip over character read call
  52.  
  53. ;    (O) Transform an ASCII character string on the PDL into a
  54. ;    two or four byte integer or a single or double precision
  55. ;    floating point number.  Predicate - false if the argument
  56. ;    is not a digit string or null, leaving the argument unchanged.
  57.  
  58. UCO:    mov    byte ptr NSIZ,2    ;assume two-byte digit will be produced
  59.     mov    read,(offset pty)    ;make buffer out of arg
  60.     mov    bx,PX    ;start of the string
  61.     mov    RX,bx
  62.     mov    bx,PY    ;end of the string, plus 1
  63.     mov    (byte ptr[bx]),00    ;add a NUL at the end
  64.     inc    bx
  65.     mov    RY,bx
  66.     mov    RSEG,ds
  67.     call    word ptr read
  68.     or    al,al
  69.     jnz    nnul    ;skip if string not null
  70.     mov    word ptr ARG1,0000    ;null string, make 0
  71.     jmp    short o1
  72.  
  73. nnul:    mov    FRST,al    ;save first character
  74.     call    word ptr read    ;get next character
  75.     call    recds    ;and gather rest of number
  76.     test    al,al    ;returned character must be NUL
  77.     jz    o1
  78.     ret        ;return FALSE if not
  79. o1:    mov    cl,NSIZ    ;else we have a number of size (nsiz)
  80.     mov    ch,00    ;set up (BC) to allocate space on PDL
  81.     call    OARG    ;get it
  82.     mov    PY,bx
  83.     sub    bx,cx    ;recompute PX
  84.     mov    di,bx
  85.     mov    si,(offset ARG1)
  86.     call    xf1    ;move to PDL from arg1 onward
  87.     jmp    SKP    ;take TRUE exit
  88.  
  89. ;    The heart of number parsing and conversion
  90.  
  91. recds:    push    ax    ;save second character
  92.     mov    byte ptr NSIZ,2    ;assume 2 byte integer
  93.     call    dsinit    ;initialize number gathering areas and flags
  94.     mov    al,FRST    ;start parsing
  95.     cmp    al,'-'
  96.     jz    ds1
  97.     cmp    al,'0'    ;leading 0 may mean 4 byte integer
  98.     jz    ds4
  99.     cmp    al,'.'    ;floating point implied by period
  100.     jz    ds5
  101.     pop    bx    ;get second char, foreseeing ret in rnd
  102.     call    RND    ;return if not a digit at this point
  103.     mov    byte ptr ARG1,al    ;put in the digit-gathering buffer
  104.     mov    al,bl    ;get second character
  105.     jmp    short ds6    ;go ahead with rest
  106.  
  107. ds1:    pop    ax    ;negative number, examine next char.
  108.     cmp    al,'.'
  109.     jnz    ds3
  110. ds2:    mov    DCPT,al    ;period after minus or zero; record fact,
  111.     call    word ptr read    ;get next character
  112.     jmp    short ds5a    ;and go indicate floating point size
  113.  
  114. ds3:    call    RND    ;return if not period and not digit after sign
  115.     jnz    ds3b    ;if not zero, restore to ascii
  116. ds3a:    mov    byte ptr NSIZ,4    ;set to gather a 4-byte integer
  117. ds3b:    add    al,'0'    ;restore to ascii before continuing to gather
  118.     jmp    short ds6
  119.  
  120. ds4:    pop    ax    ;character following 0 may be:
  121.     cmp    al,'E'    ;    E, single precision exponent
  122.     jz    ds6
  123.     cmp    al,'D'    ;    D, double precision exponent
  124.     jz    ds6
  125.     cmp    al,'.'    ;    ., decimal point
  126.     jz    ds2
  127.     call    RND    ;    or digit; in the latter case
  128.     jmp    ds3a    ;we set up for a 4-byte integer
  129.  
  130. ds5:    mov    DCPT,al    ;period as first character, record found
  131.     pop    ax    ;fetch 2nd char before going on
  132. ds5a:    mov    byte ptr NSIZ,5    ;record size of single precision operand
  133.  
  134. ds6:    call    dsgath    ;go gather rest
  135.     push    ax    ;save terminating character
  136.     call    dsend    ;do final number-building
  137.     pop    ax    ;put terminating character back in A
  138.     ret        ;done
  139.  
  140. ;    digit-gathering loop
  141.  
  142. dsg1:    call    word ptr read
  143. dsgath: cmp    al,'.'    ;check decimal point first
  144.     jnz    dsg2
  145.     mov    ah,DCPT
  146.     test    ah,ah
  147.     jz    dsg1a
  148.     ret        ;period found twice, return
  149. dsg1a:    mov    DCPT,al        ;first one, record fact
  150.     mov    byte ptr NSIZ,5    ;set single precision size
  151.     jmp    short dsg1
  152.  
  153. dsg2:    cmp    al,'E'    ;check S.P. exponent
  154.     jnz    dsg3
  155.     mov    ah,5
  156. dsg2a:    mov    NSIZ,ah    ;S.P. size
  157.     jmp    short dsxpt    ;go gather exponent
  158.  
  159. dsg3:    cmp    al,'D'    ;check D.P. exponent
  160.     mov    ah,8
  161.     jz    dsg2a
  162.  
  163.     call    RND    ;finally, check for digit
  164.     mov    cx,ax    ;save digit in b
  165.     mov    al,ARG1H    ;high order byte of significand
  166.     and    al,0F0H    ;check highest nibble
  167.     jz    dsg3a    ;skip if high nibble = 0
  168.  
  169.     xor    al,al    ;else drop, but check if digit dropped
  170.     or    al,DCPT    ;belongs to integer or fractional part:
  171.     jnz    dsg1    ;continue gathering if fractional part
  172.  
  173.     inc    DDCT    ;else add 1 to partial exponent due to dropping
  174.     jmp    short dsg1    ;of integer part digit and continue gathering
  175.  
  176. dsg3a:    xor    al,al
  177.     or    al,DCPT    ;if decimal point not recorded
  178.     jz    dsg4    ;proceed to tack on this digit,
  179.     dec    DDCT    ;else decr.partl expt due to incl of fract dig
  180.  
  181. dsg4:    push    cx    ;save digit
  182.     call    txp    ;multiply current mantissa by 10
  183.     mov    di,(offset ARG2)    ;set up alternate buffer
  184.     call    zarg    ;to receive the next digit
  185.     pop    ax    ;retrieve digit into ax
  186.     mov    byte ptr ARG2,al
  187.     call    add8    ;add it to previous mantissa
  188.     jmp    short dsg1    ;and continue gathering
  189.  
  190. ;    Exponent-gathering
  191.  
  192. dsxpt:    call    word ptr read    ;get next character
  193.     mov    DXSG,al    ;save as indicator of decimal exponent sign
  194.     mov    bx,0    ;exponent will be put together in HL
  195.     cmp    al,'-'    ;negative?
  196.     jz    dsx2    ;yes, go to next char
  197.     cmp    al,'+'    ;explicit positive sign?
  198.     jnz    dsx3    ;no, go check if digit
  199.  
  200. dsx2:    call    word ptr read    ;fetch next character
  201. dsx3:    mov    DCXPT,bx    ;save partially gathered exponent
  202.     call    RND    ;terminate if not a digit
  203.     mov    cx,bx    ;copy HL into BC
  204.     sal    bx,1    ;multiply HL by 4
  205.     sal    bx,1
  206.     add    bx,cx    ;make it 5
  207.     sal    bx,1    ;twice again, to make it times 10
  208.     add    bx,ax    ;add current digit
  209.     test    bh,0FCH    ;check for exponent overflow
  210.     jz    dsx2
  211.     mov    bx,03FFH    ;set large decimal exponent
  212.     jmp    dsx2
  213.  
  214. ;    Final number buildup
  215.  
  216. dsend:    cmp    byte ptr NSIZ,5
  217.     jc    dsn0
  218.     call    dnd0    ;put together F.P. numbers
  219. dsn0:    cmp    byte ptr FRST,'-'    ;take care of initial sign
  220.     jz    dsn1
  221.     ret
  222. dsn1:    mov    al,NSIZ
  223.     mov    bx,(offset ARG1)
  224.  
  225. ;    Subroutine for negation of numeric arguments
  226.  
  227. negn:    cmp    al,5
  228.     mov    cl,al
  229.     mov    ch,0
  230.     jnc    negr    ;negate F.P. numbers
  231. ngn1:    clc        ;clear carry
  232.     shr    cx,1    ;divide count by 2 to do it by words
  233. ngn0:    mov    ax,0000    ;negate multi-byte integer
  234.     sbb    ax,[bx]
  235.     mov    [bx],ax
  236.     inc    bx
  237.     inc    bx
  238.     loop    ngn0
  239.     ret
  240.  
  241. negr:    dec    cx    ;find exponent byte
  242.     add    bx,cx    ;got it
  243. negr1:    mov    al,[bx]
  244.     mov    cl,al    ;save it
  245.     dec    bx
  246.     or    al,[bx]
  247.     jz    negr2    ;return if operand is zero
  248.     xor    cl,80H    ;complement sign bit
  249.     inc    bx    ;point back to high byte
  250.     mov    [bx],cl    ;restore exponent with changed mantissa sign
  251. negr2:    ret        ;done
  252.  
  253. ;    check if argument has size 0, 1, 2, 4, 5 or 8.
  254. ;    zero flag is returned if size is 5 or 8
  255.  
  256. numchk: mov    bx,PX
  257.     mov    cx,PY
  258.     mov    dx,cx    ;a copy of PY into DX
  259.     sub    cx,bx
  260.     test    ch,ch
  261.     jnz    nch1    ;no large arguments
  262.     cmp    cl,8
  263.     jz    nch0
  264.     jnc    nch1    ;no args of size gt 8
  265.     cmp    cl,5
  266.     jz    nch0
  267.     jnc    nch1    ;no size 6 or 7 args
  268.     cmp    cl,3
  269.     jz    nch1    ;no size 3 args
  270. nch0:    ret
  271. nch1:    jmp    RER
  272.  
  273. ;    (~)  Complement or negate the top of the PDL
  274.  
  275. comp:    call    numchk
  276.     jz    negr    ;negate F.P. argument
  277.     test    cl,cl
  278.     jz    cmp0    ;leave null string as is
  279.     cmp    cl,2
  280.     jnc    ngn1    ;negate 2 or 4-byte integer
  281.     not    byte ptr[bx]    ;1-byte argument, do a log. complement
  282. cmp0:    ret
  283.  
  284. ;    Final assembly of floating point operands
  285.  
  286. dnd0:    cmp    byte ptr DXSG,'-'    ;set proper decimal exponent sign
  287.     jnz    dnd1    ;skip if not negative
  288.     neg    DCXPT
  289.  
  290. dnd1:    mov    al,DDCT    ;fetch partial exponent due to digit-gathering
  291.     cbw        ;extend its sign into ah
  292.     add    ax,DCXPT    ;compute final decimal exponent in HL
  293.     mov    DCXPT,ax    ;and save it
  294.     call    zach    ;check if arg1=0
  295.     jnz    dnd1a
  296.     ret        ;done if mantissa is zero
  297. dnd1a:    mov    dx,043EH    ;else compute biased binary exponent
  298.  
  299. dnd3:    push    dx    ;save binexpt
  300.     jmp    short dnd3b
  301. dnd3a:    call    div10b    ;mantissa will be divided by 10 if DCXPT<0
  302. dnd3b:    call    norg1    ;normalize arg1 (shift until high bit = 1)
  303.     jz    dnd3c
  304.     pop    bx
  305.     add    dx,bx    ;reduce binexpt by amount shifted
  306.     jnc    ufl
  307.     push    dx
  308. dnd3c:    mov    bx,DCXPT    ;check DCXPT sign
  309.     test    bx,bx
  310.     js    dnd3a    ;go divide by 10 if dec. expt. negative
  311.     jz    dspack    ;zero, do final packing
  312.     call    m58thb    ;mult by 10/16 if positive
  313.     jnc    dnd3    ;beware of bin. expt. overflow
  314.     jmp    short ovf
  315.  
  316. ;    Pack up exponent and mantissa
  317.  
  318. dspack: pop    word ptr BINXPT    ;retrieve binary exponent
  319.     mov    di,(offset ARG2)    ;first do rounding of the mantissa
  320.     call    zarg
  321.     mov    bx,(offset ARG2B)
  322.     cmp    byte ptr NSIZ,8    ;set rounding bit according to size
  323.     jnz    dsp0
  324.     shr    al,1    ;make it a 4 to be the
  325.     dec    bx    ; next to high bit of next to low nibble
  326.     jmp    short dsp0a
  327. dsp0:    mov    al,80H    ;high bit
  328.     inc    bx    ; of 5th mantissa byte for SP
  329. dsp0a:    mov    (byte ptr[bx]),al    ;store
  330.     call    add8    ;round
  331.     jnc    dsp1    ;skip if rounding produced no carry
  332.     mov    byte ptr ARG1H,080H    ;else set MSbit of mantissa
  333.     inc    word ptr BINXPT    ;and adjust bin. expt.
  334.     jz    ovf    ;skip to overflow if it became 0
  335.  
  336. dsp1:    cmp    byte ptr NSIZ,5    ;which size
  337.     jnz    dsp2        ;skip if D.P.
  338.     mov    ax,0FC80H    ;constant to adjust S.P. bias
  339.     add    ax,word ptr BINXPT
  340.     jz    ufl    ;0 means underflow
  341.     jnc    ufl
  342.     test    ah,ah    ;(HL) must end up between 1 and 0FEH
  343.     jnz    ovf
  344.     mov    ah,al
  345.     inc    al
  346.     jz    ovf    ;0FFH also invalid
  347.     mov    al,byte ptr ARG1H    ;MSByte to al
  348.     rol    al,1    ;get rid of MSbit
  349.     shr    ax,1    ;binexpt LSbit into its place, 0 to sign
  350.     mov    word ptr ARG1H,ax
  351.     mov    di,(offset ARG1)    ;shift S.P. number down 4 bytes
  352.     mov    si,(offset ARG1M)
  353.     mov    cx,5
  354.     jmp    xf1    ;shift the upper 5 bytes
  355.  
  356. ;    Handle underflow in binary exponent
  357.  
  358. ufl:    call    zarg1    ;make it all zero
  359.     mov    FRST,al    ;including the sign
  360.     ret
  361.  
  362. ;    Handle overflow in binary exponent
  363.  
  364. ovf:    call    zarg1        ;set up infinite operand
  365.     mov    cx,7F80H    ;first two bytes of SP infinite
  366.     cmp    byte ptr NSIZ,5        ;set rightmost bits of exponent
  367.     jnz    ov1        ;according to size of operand
  368.     mov    word ptr(ARG1M-1),cx
  369.     ret
  370.  
  371. ov1:    or    cl,70H        ;D.P. handled here
  372.     mov    word ptr ARG1B,cx    ;set next byte
  373.     ret
  374.  
  375. dsp2:    mov    dx,word ptr BINXPT
  376.     cmp    dx,07FFH
  377.     jnb    ovf    ;07FFH or bigger is an overflow
  378.     call    halve    ;shift mantissa down 3 bits
  379.     call    halve
  380.     call    halve
  381.     mov    cl,4
  382.     sal    dx,cl    ;and shift exponent up 4 bits
  383.     mov    al,0FH
  384.     and    al,ARG1H    ;mask implicit bit off
  385.     or    dl,al        ;insert lower 4 bits of exponent
  386.     mov    word ptr ARG1H,dx    ;store it
  387.     mov    di,(offset ARG1)    ;shift one byte down
  388.     mov    si,di
  389.     inc    si
  390.     mov    cx,8
  391.     jmp    xf1    ;done when finished shifting
  392.  
  393. ;    (#)  Change binary number into a decimal-based ASCII
  394. ;    string as follows:
  395. ;        size of     max. size    form
  396. ;        number      of string
  397. ;         0         1        0
  398. ;         1         3        d{d}
  399. ;         2         5        d{d}
  400. ;         4        12        [-]0d{d}
  401. ;         5        15        [-]d{d}.{d}[E[s]d{d}]
  402. ;         8        21        [-]d{d}.{d}D[s]d{d}
  403.  
  404. ns:    call    numchk    ;check for numerical argument
  405.     mov    NSIZ,cl    ;record size in memory
  406.     mov    al,cl    ;and in AL
  407.     shl    cl,1    ;compute size of maximum string
  408.     inc    cl
  409.     cmp    cl,11    ;is it FP?
  410.     jc    nsaa
  411.     inc    cl    ;yes, make it 1 longer
  412. nsaa:    cmp    cl,9    ;is it 4 bytes or longer?
  413.     jc    nsbb
  414.     add    cl,3    ;yes, make it 3 longer
  415. nsbb:    call    OARG    ;and find out whether there is enough space for it
  416.     mov    si,PY    ;load source index before modifying py
  417.     sub    bx,cx    ;recompute PX
  418.     mov    PY,bx    ;close interval before string production
  419.     cmp    al,4
  420.     jnc    nslrg    ;jump on long operands
  421.     mov    cl,al
  422.     mov    ax,0000    ;put zero in DE for default
  423.     jcxz    ns1    ;load nothing
  424.     mov    al,[bx]    ;load low byte
  425.     dec    cx    ;test for one byte
  426.     jcxz    ns1    ;only byte and it's loaded
  427.     mov    ah,1[bx]    ;load high byte
  428.  
  429. ;    The following code is also used to convert exponents of
  430. ;    floating point operands and long integers whose high
  431. ;    word is null.
  432.  
  433. ns1:    mov    bp,bx        ;save pointer for ASCII string
  434.     mov    cl,'0'        ;prepare to write a zero
  435.     mov    bx,-10000    ;will there be 5 digits?
  436.     add    bx,ax        ;
  437.     jb    ns2
  438.     mov    bx,-1000        ;will there be 4 digits?
  439.     add    bx,ax        ;
  440.     jb    ns3
  441.     mov    bx,-100        ;will there be 3 digits?
  442.     add    bx,ax        ;
  443.     jb    ns4
  444.     mov    bx,-10        ;will there be 2 digits?
  445.     add    bx,ax        ;
  446.     jb    ns5
  447.     jmp    ns6        ;write one no matter what
  448. ns2:    mov    bx,10000    ;ten thousands digit
  449.     call    nsa        ;
  450. ns3:    mov    bx,1000        ;thousands digit
  451.     call    nsa        ;
  452. ns4:    mov    bx,100        ;hundreds digit
  453.     call    nsa        ;
  454. ns5:    mov    bx,10        ;tens digit
  455.     call    nsa        ;
  456. ns6:    add    cl,al        ;units digit
  457.     mov    ds:[bp],cl    ;store the digit
  458.     inc    bp        ;position pointer for next byte
  459.     mov    PY,bp        ;done, store it as terminator
  460.     ret
  461.  
  462. nsa:    mov    dx,0000        ;clear extension for div
  463.     div    bx        ;div bx into axdx
  464.     add    cl,al        ;form ASCII digit
  465.     mov    ax,dx        ;put remainder in ax
  466.     mov    ds:[bp],cl    ;store new digit
  467.     inc    bp        ;advance pointer
  468.     mov    cl,'0'        ;load a fresh ASCII zero
  469.     ret
  470.  
  471. ;    Long number conversion to ASCII starts here.
  472. ;    HL contains (px) on entry.
  473.  
  474. nslrg:    call    dsinit        ;clear all number buffers
  475.     mov    cl,NSIZ
  476.     mov    di,(offset arghh)    ;get destination address +1
  477.     call    mduc        ;move by decrement until count
  478.     cld            ;note: es=ds by call to mduc
  479.     mov    di,PY        ;get ptr to next available byte for string
  480.     cmp    byte ptr NSIZ,4        ;do we have an integer?
  481.     jnz    nsflt        ;no, jump to F.P. processor
  482.     mov    al,ARG1H    ;yes, check its sign
  483.     test    al,al
  484.     jns    nsl2
  485.     mov    bx,(offset ARG1M)    ;negate the 4-byte operand
  486.     mov    cx,4
  487.     call    ngn1
  488.     mov    al,'-'
  489.     stosb            ;record the negative sign
  490. nsl2:    mov    al,'0'
  491.     stosb            ;long integers have a leading 0
  492.     mov    PY,di        ;save
  493.     mov    bx,word ptr ARG1B    ;get high word of operand
  494.     test    bx,bx
  495.     jnz    nsl3        ;greater than 2**16 - 1?
  496.     mov    ax,word ptr ARG1M    ;no, get it into HL
  497.     mov    bx,di        ;put PDL pointer in bx
  498.     jmp    ns1        ;and treat it as a 2 byte operand
  499.  
  500. nsl3:    mov    dx,20H        ;make it look like a floating point number
  501.     call    nsdnor        ;normalize decimal
  502.     mov    al,byte ptr DCXPT    ;get dec. exponent (=# of dec. digits)
  503.     call    mkstr        ;go make the string
  504.     mov    bx,PY        ;pointer to start of string
  505.     add    bx,word ptr BINXPT    ;length of produced string
  506.     mov    PY,bx        ;make address of next free PDL byte
  507.     ret            ;done
  508.  
  509. ;    Real number strings produced here
  510.  
  511. nsflt:    mov    al,ARG1H    ;examine sign of operand
  512.     or    al,al
  513.     jns    nsf1
  514.     mov    al,'-'
  515.     stosb            ;insert sign right away
  516. nsf1:    mov    PY,di        ;save pointer to the string
  517.     call    unpak        ;unpack the operand
  518.     call    nsdnor        ;normalize decimal
  519.     mov    al,NSIZ        ;compute how many digits to produce
  520.     sal    al,1
  521.     dec    al
  522.     call    mkstr        ;produce them
  523.     mov    ax,DCXPT    ;the decimal exponent
  524.     test    ah,ah
  525.     jnz    insxp        ;force exponent insertion if >255 or <0
  526.     cmp    al,7        ;and also
  527.     jnc    insxp        ;if >6
  528.     mov    bx,py
  529.     add    bx,ax        ;determine where to insert dec. point
  530.     call    shstr        ;insert point, shift string, drop trailing 0s
  531.     inc    bx        ;update pointer
  532.     mov    PY,bx
  533.     cmp    al,'.'        ;see if the last character was the period
  534.     jnz    nsfdp        ;if not, go insert D0 if DP number
  535.     mov    ax,bx        ;else make sure we have at least one digit
  536.     mov    dx,PX
  537.     sub    ax,dx
  538.     cmp    al,3
  539.     jnc    nsfdp        ;we do, insert DP expt if necessary
  540.     mov    bx,dx        ;make bx point at start of string
  541.     mov    al,(byte ptr[bx])    ;we don't, fix it
  542.     cmp    al,'.'        ;is the first character a period?
  543.     jnz    nsf4
  544.     mov    (byte ptr[bx]),'0'    ;yes, insert 0 in its place
  545.     inc    bx        ;and put the period after it
  546.     mov    (byte ptr[bx]),al
  547.     dec    bx
  548. nsf4:    cmp    al,'-'        ;was it a -?
  549.     jnz    nsf5
  550.     mov    (byte ptr[bx]),'0'    ;yes, but next is sure to be a period
  551. nsf5:    inc    bx
  552.     inc    bx    ;keep PDL pointer updated
  553.     mov    PY,bx
  554.  
  555. nsfdp:    cmp    byte ptr NSIZ,5        ;was this a DP operand?
  556.     jnz    nsf6
  557.     ret            ;no, we're done
  558. nsf6:    mov    (word ptr[bx]),'0D'    ;yes, insert D0
  559.     inc    bx
  560.     inc    bx
  561.     mov    PY,bx    ;update pointer
  562.     ret        ;and quit
  563.  
  564. ;    FP exponent insertion
  565.  
  566. insxp:    dec    ax        ;decrement dec. expt., we will insert
  567.     mov    DCXPT,ax    ;dec. point after first digit
  568.     mov    bx,PY        ;get start of string
  569.     inc    bx        ;point it to start of move
  570.     mov    al,1        ;bytes NOT to move
  571.     call    shstr        ;insert period, shift, drop trailing zeros
  572.     inc    bx        ;advance pointer
  573.     mov    ch,'E'        ;prepare to insert exponent
  574.     cmp    byte ptr NSIZ,5        ;but first ch kind to insert
  575.     jz    insx1
  576.     mov    ch,'D'        ;DP exponent
  577. insx1:    mov    (byte ptr[bx]),ch    ;insert the letter
  578.     inc    bx        ;advance the pointer
  579.     mov    ax,DCXPT    ;get the decimal exponent
  580.     test    ah,ah        ;examine its sign
  581.     js    insx2
  582.     jmp    ns1        ;positive, insert it and quit
  583. insx2:    mov    (byte ptr[bx]),'-'    ;insert sign
  584.     inc    bx        ;keep pointer updated
  585.     neg    ax        ;negate the exponent
  586.     jmp    ns1        ;insert it and quit
  587.  
  588. ;    Insert period, shift string, drop trailing zeros
  589.  
  590. shstr:    mov    cx,word ptr BINXPT    ;total length of digit string
  591.     sub    cl,al        ;minus digits to be left in place
  592.     mov    al,'.'        ;prepare period
  593. shst1:    mov    ah,(byte ptr[bx])    ;start moving
  594.     mov    (byte ptr[bx]),al
  595.     mov    al,ah
  596.     inc    bx    ;next
  597.     loop    shst1
  598.     mov    (byte ptr[bx]),al    ;last
  599. shst2:    cmp    al,'0'    ;while last character is zero, drop it
  600.     jz    shst3
  601.     ret
  602. shst3:    dec    bx    ;back up
  603.     mov    al,(byte ptr[bx])
  604.     jmp    shst2
  605.  
  606. ;    Unpack floating point number
  607.  
  608. unpak:    mov    bx,(offset ARG1H)    ;get address of high byte
  609. unpk1:    dec    bx
  610.     mov    dx,[bx]        ;check for zero
  611.     mov    DXSG,dh        ;save sign-containing byte
  612.     test    dx,dx
  613.     jnz    up0
  614.     ret
  615.  
  616. up0:    mov    cl,NSIZ
  617.     cmp    cl,5
  618.     jnz    updp        ;jump if DP
  619.     dec    cl        ;set up count for shlby1
  620.     sal    dx,1        ;move LSbit of exponent to MSBit of AH
  621.     stc            ;set "implicit" bit
  622.     rcr    dl,1        ;got full mantissa byte and LSbit of
  623.     mov    (byte ptr[bx]),dl    ; exponent in Carry
  624.     inc    bx    ;point to MSbyte
  625.     mov    dl,dh    ;exponent to LSbyte of AX
  626.     mov    dh,0    ;zero to high byte of AX
  627.     mov    (byte ptr[bx]),dh    ;and to high byte of arg1
  628.     mov    ax,0FF82H    ;bias to subtract (-07EH)
  629.     jmp    short up2
  630.  
  631. updp:    mov    al,dl        ;save lower byte in al
  632.     and    dl,0FH        ;select mantissa nibble
  633.     or    dl,10H        ;and set "implicit" bit
  634.     mov    (byte ptr[bx]),dl    ;put it back
  635.     inc    bx
  636.     mov    (byte ptr[bx]),0    ;clear highest byte
  637.     and    dh,07FH        ;clear high bit
  638.     mov    dl,al        ;restore low byte
  639.     mov    cl,4        ;set shift count
  640.     shr    dx,cl        ;and divide by 16
  641.     mov    cl,7        ;set up count for shlby1
  642.     mov    ax,0FC05H    ;bias to subtract (-3FFH + 1/2 byte)
  643. up2:    add    dx,ax        ;subtract bias
  644.     mov    di,bx
  645.     mov    ch,0        ;clear upper half of count reg.
  646.     call    shlby1        ;move mantissa up one byte
  647.     stc            ;set carry to indicate nonzero operand
  648.     ret            ;and quit
  649.  
  650. ;    Decimal normalization: reduce binary exponent to zero
  651. ;    while computing decimal exponent and keeping mantissa
  652. ;    between 0.1 and 1.
  653.  
  654. nsdnor: mov    word ptr BINXPT,dx    ;save the unbiased binary exponent
  655.     jmp    short nsdn1a
  656. nsdn1:    call    div10a        ;divide by 10 while BINXPT>0
  657. nsdn1a: call    norg1        ;keep mantissa normalized
  658.     add    dx,word ptr BINXPT    ;and binary expt correct
  659.     mov    word ptr BINXPT,dx    ;but test it
  660.     test    dx,dx
  661.     jnz    nsdn1b
  662.     ret            ;return when bin. expt. is zero
  663. nsdn1b: jns    nsdn1        ;divide by 10 while positive
  664.     push    dx
  665.     add    dx,3        ;else see if number between 0.1 and 1.
  666.     jc    nsdn3        ;if not less than -3, almost there
  667.     call    m58tha        ;else multiply by 10/16 and
  668.     jmp    short nsdnor        ;keep at it
  669.  
  670. nsdn3:    test    dx,dx        ;almost there
  671.     pop    dx
  672.     jnz    nsdn4        ;done if -3<BINXPT<0 (# between .125 and 1.)
  673.     push    dx        ;save binexpt in stack for m58tha
  674.     mov    di,PY    ;save mantissa in PDL, we may have to restore it
  675.     mov    si,(offset ARG1)
  676.     mov    cx,8
  677.     call    xf1
  678.     call    m58tha        ;try product by 10/16 one last time
  679.     mov    ah,arg1h    ;get highest mantissa byte
  680.     test    ah,ah        ;see if we overflowed
  681.     jns    nsdnor        ;if not, normalize again
  682.     mov    si,PY        ;if it did, we must undo it and quit:
  683.     mov    di,(offset ARG1)    ;retrieve mantissa from PDL
  684.     mov    cx,8
  685.     call    xf1
  686.     inc    DCXPT        ;restore DCXPT to what it was
  687.     mov    dx,0FFFDH    ;value of l when we came in at nsdn3 (-3)
  688. nsdn4:    neg    dx        ;final value of -(BINXPT)
  689.     mov    di,(offset ARG1H)    ; mantissa to the right
  690.     call    shr1
  691.     ret
  692.  
  693. ;    Generate (A) decimal digits from mantissa at arg1
  694.  
  695. mkstr:    mov    bx,PY
  696.     mov    (byte ptr[bx]),'0'
  697. mkstr2: inc    al    ;one extra digit to use for rounding
  698.     mov    cl,al
  699.     mov    ch,0
  700. mkstr3: inc    bx        ;point to next byte on PDL
  701.     push    cx        ;save counter
  702.     push    bx        ;and pointer
  703.     push    bx        ;once more for the benefit of m58thc
  704.     call    m58thc
  705.     pop    bx        ;retrieve pointer
  706.     mov    al,ARG1H    ;high byte, whose high nibble
  707.     mov    cl,4
  708.     shr    al,cl        ;contains the next decimal digit
  709.     add    al,'0'        ;which we translate to ASCII
  710.     mov    (byte ptr[bx]),al    ;save on the PDL
  711.     mov    bp,bx
  712.     mov    dl,4    ;and drop from the mantissa
  713. mk4:    call    twice
  714.     dec    dl
  715.     jnz    mk4
  716.     mov    bx,bp
  717.     pop    cx    ;retrieve counter
  718.     loop    mkstr3    ;and keep at it till we're through
  719.     mov    word ptr BINXPT,bx    ;use BINXPT to point at the last char
  720.     mov    ch,5
  721. mk5:    mov    al,(byte ptr[bx])    ;do a decimal round on the string
  722.     add    al,ch
  723.     cmp    al,'9'+1
  724.     jc    mk6
  725.     sub    al,10    ;decimal carry ocurred, propagate it
  726.     mov    (byte ptr[bx]),al
  727.     mov    ch,1
  728.     dec    bx
  729.     jmp    mk5
  730.  
  731. mk6:    mov    [bx],al    ;return the last decimal digit rounded
  732.     mov    bx,PY
  733.     mov    al,[bx]
  734.     sub    word ptr BINXPT,bx    ;compute string length
  735.     cmp    al,'0'
  736.     jz    mk7        ;all the way
  737.     inc    word ptr DCXPT    ;if it did, adjust the decimal exponent
  738.     ret
  739.  
  740. mk7:    dec    word ptr BINXPT    ;no carry propagated, get rid of extra 0
  741.     mov    cx,word ptr BINXPT    ;adjust length and use it to shift:
  742.     mov    si,PY        ;prepare to shift string down one digit
  743.     mov    di,si        ;get start of digit string
  744.     inc    si        ;point source index to 1st nonzero digit
  745. ;                ;run into xf1 to do the transfer
  746.  
  747. ;    --------------------------------------------------------------
  748. ;    Service routines for the preceding conversion operators
  749. ;    --------------------------------------------------------------
  750.  
  751. ;    transfer in increasing memory direction
  752.  
  753. xf1:    mov    bp,ds
  754. xf2:    mov    es,bp
  755.     cld
  756.     repnz    movsb
  757.     ret
  758.  
  759. ;    transfer in decreasing memory direction
  760.  
  761. mduc:    mov    bp,ds
  762.     mov    es,bp
  763. mduc1:    dec    di
  764.     dec    si
  765.     std
  766.     repnz    movsb
  767.     ret
  768.  
  769. ;    Clear number buffers
  770.  
  771. dsinit: mov    di,(offset ARG1)    ;starting byte to clear
  772.     mov    cx,22    ;number of bytes to clear
  773.     jmp    short zar1
  774.  
  775. ;    Clear 8 bytes or (CX) bytes starting at (DI)
  776.  
  777. zarg1:    mov    di,(offset ARG1)
  778. zarg:    mov    cx,8
  779. zar1:    mov    ax,ds
  780.     mov    es,ax
  781.     mov    ax,0000
  782.     cld
  783.     repnz    stosb
  784.     ret
  785.  
  786. ;    arg1 times 10
  787.  
  788. txp:    call    cop    ;copy to arg2
  789.     call    twice    ;multiply by fos
  790.     call    twice
  791.     call    add8    ;add it to make 5 and run into twice
  792.  
  793. ;    arg1 times 2
  794.  
  795. twice:    mov    cx,4
  796. twi0:    mov    bx,(offset ARG1)
  797. twi1:    clc
  798. tw1:    rcl    word ptr [bx],1
  799.     inc    bx
  800.     inc    bx
  801.     loop    tw1
  802.     ret
  803.  
  804. ;    copy arg1 to arg2
  805.  
  806. cop:    mov    si,(offset ARG1)
  807.     mov    di,(offset ARG2)
  808.     mov    cx,8
  809.     jmp    xf1
  810.  
  811. ;    shift right one nibble argument pointed to by DE
  812.  
  813. shrnib: mov    dl,4
  814. shr1:    mov    bx,di
  815.     call    halv2
  816.     dec    dl
  817.     jnz    shr1
  818.     ret
  819.  
  820. ;    halve arg1
  821.  
  822. halve:    mov    bx,(offset ARG1H)
  823. halv2:    clc
  824. halvc:    mov    cx,8    ;this entry to shift right with initial carry
  825. hal1:    rcr    byte ptr [bx],1
  826.     dec    bx
  827.     loop    hal1
  828.     ret
  829.  
  830. ;    add arg2 to arg1
  831.  
  832. add8:    mov    cx,4
  833. adda:    mov    bx,(offset ARG1)
  834. addb:    mov    bp,(offset ARG2)
  835. addc:    clc
  836. ad1:    mov    ax,ds:[bp]
  837.     adc    [bx],ax
  838.     inc    bx
  839.     inc    bx
  840.     inc    bp
  841.     inc    bp
  842.     loop    ad1
  843.     ret
  844.  
  845. ;    shift left arg1 a full byte
  846.  
  847. shlby:    mov    di,(offset ARG1H)
  848. shlby0: mov    cx,7
  849. shlby1: mov    si,di
  850.     dec    si
  851.     std
  852.     mov    bp,ds
  853.     mov    es,bp
  854.     repnz    movsb
  855.     mov    al,ch        ;clear AL
  856.     stosb            ;clear LSbyte
  857.     ret
  858.  
  859. ;    multiply arg1 by 10/16
  860.  
  861. m58tha: mov    bx,DCXPT
  862. m58thb: dec    bx    ;subtract one from dec. exponent
  863.     mov    DCXPT,bx
  864. m58thc: call    halve
  865.     call    cop
  866.     call    halve
  867.     call    halve
  868.     call    add8
  869.     pop    bp
  870.     pop    dx
  871.     add    dx,4    ;add 4 to bin. exponent
  872.     jmp    bp
  873.  
  874. ;    divide arg1 by 10
  875.  
  876. div10a: mov    bx,DCXPT
  877. div10b: inc    bx    ;add 1 to decimal exponent
  878.     mov    DCXPT,bx
  879.     call    halve
  880.     call    cop
  881.     call    halve
  882.     call    add8    ;here we have 3/4 of original mantissa
  883.     call    cop    ;which we copy into arg2
  884.     mov    cx,15    ;nibbles in 8 bytes, minus one
  885. dv1:    push    cx    ;this loop multiplies arg1 by 16/15 (approx)
  886.     mov    di,(offset ARG2H)
  887.     call    shrnib
  888.     call    add8
  889.     pop    cx
  890.     loop    dv1    ;when done, we have 4/5 of original arg1
  891.     call    halve    ;divide by 8 to make 1/10
  892.     call    halve
  893.     call    halve
  894.     ret
  895.  
  896. ;    normalize arg1
  897.  
  898. norg1:    mov    dx,0
  899. nr0:    mov    al,ARG1H
  900.     test    al,al
  901.     jnz    nr2    ;determine whether a byte shift is needed
  902.     mov    al,dl    ;it is
  903.     sub    al,38H    ;max number of shifts (7 bytes)
  904.     jnz    nr1
  905.     mov    dl,al    ;arg1 was 0
  906.     ret
  907. nr1:    add    al,40H    ;restore the subtracted 38H, add 8 more
  908.     mov    dl,al    ; and save in c
  909.     call    shlby    ;shift left a full byte
  910.     jmp    nr0    ;start over
  911.  
  912. nr2:    js    nr3    ;high bit on means we're done
  913.     call    twice    ;otherwise shift left one bit
  914.     inc    dl    ;record the fact
  915.     jns    nr0    ;and test again
  916.  
  917. nr3:    neg    dx    ;negate the shift count
  918.     ret
  919.  
  920. ;    Return if not decimal. A unchanged if not decimal, else
  921. ;    reduced to binary.
  922.  
  923. RND:    cmp    al,':'        ;colon follows 9 in ASCII alphabet
  924.     jnb    RTN
  925.     cmp    al,'0'        ;ASCII zero is lower limit
  926.     jb    RTN
  927.     sub    al,'0'        ;normalize to get binary values
  928.     mov    ah,00        ;zero for uncomplicated arithmetic
  929.     ret
  930. RTN:    inc    sp
  931.     inc    sp
  932.     ret
  933.  
  934. ;    Check if arg1=0
  935.  
  936. zach:    xor    ax,ax
  937.     mov    bx,(offset ARG1)
  938.     mov    cx,4
  939. zch0:    or    ax,(word ptr[bx])    ;pile up mantissa bytes on A
  940.     inc    bx
  941.     inc    bx
  942.     loop    zch0
  943.     test    ax,ax
  944.     ret
  945.  
  946. ;    end
  947.