home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 23 / IOPROG_23.ISO / SOFT / ASM / BCDASM.ZIP / BCDASM / SRC / BCDFMT.ASM < prev    next >
Encoding:
Assembly Source File  |  1997-06-03  |  9.9 KB  |  418 lines

  1.     title    BCDASM -- Copyright 1997, Morten Elling
  2.     subttl    Format a packed signed BCD for Ascii output
  3.  
  4.     include model.inc
  5.     include modelt.inc
  6.     include bcd.ash
  7.  
  8.     @CODESEG
  9.  
  10. ;//////////////////////////////////////////////////////////////////////
  11. ;//    Name    bcdFmt
  12. ;//    Desc    Format a packed signed BCD for Ascii output.
  13. ;//        (Write BCD to string.)
  14. ;//
  15. ;//
  16. ;//    Entry    Passed args
  17. ;//        - pass (-1) to use default value (see below)
  18. ;//        - width defaults to string length (may expand
  19. ;//          to strsz-1)
  20. ;//        - numDec indicates how many decimals the BCD has
  21. ;//          (decimal dot moves left by numDec positions)
  22. ;//        - prec indicates how many decimals to output
  23. ;//        - for positive numbers/zero, signCh can be
  24. ;//          ' ' to replace '+', or 00h to suppress '+'
  25. ;//        - fillCh can be in range 21h..7Fh to replace
  26. ;//          ' ' as string fill character
  27. ;//        - sepMCh can be 00h to suppress grouping by 1000's
  28. ;//
  29. ;//    Exit    Zero-ended Ascii string returned to destination buffer.
  30. ;//        Acc = string length (0 if bad args or string exceeds
  31. ;//        destination's size)
  32. ;//
  33. ;//    Note    Here's the number -123456789012 in various formats:
  34. ;//        ;0----+----1----+----2    (w=width, nD=numDec)
  35. ;//        "    123,456,789,012-"    w 20
  36. ;//        "   1,234,567,890.12-"    w 20, nD 2
  37. ;//        "1,234,567,890.12-"    w -1, nD 2
  38. ;//        "1234567890.12-"    w -1, nD 2, sepMCh 0
  39. ;//        "1234.56789012-   "    w 17, nD 8, sepMCh 0, rtJust 0
  40. ;//        "        1,234.57-"    w 17, nD 8, prec 2
  41. ;//        "        0.001235-"    w 17, nD 14, prec 6
  42. ;//        "12.3456789012000-"     w 17, nD 10, prec 13
  43. ;//        " 0.0123456789012000-"  w 20, nD 13, prec 16
  44.  
  45. bcdFmt    proc
  46. arg    pStr    :dataptr, \    ; Addr of Ascii result buffer
  47.     strsz    :@uint, \     ; Byte size of buffer   (min. width+1)
  48.     pBCD    :dataptr, \    ; Addr of packed signed BCD
  49.     bcdsz    :@uint, \    ; Byte size of BCD
  50.     width    :@uint, \    ; Field width        (default: length)
  51.     numDec    :@uint, \    ; Number of decimals   (default: zero)
  52.     prec    :@uint, \    ; Decimals to output    (default: all)
  53.     rtJust    :byte, \    ; Right justify     (default: yes)
  54.     signCh    :byte, \    ; Sign character (default: '+' or '-')
  55.     fillCh    :byte, \    ; Fill character    (default: ' ')
  56.     sepCh    :byte, \    ; Decimal separator    (default: '.')
  57.     sepMCh    :byte        ; Thousands separator    (default: ',')
  58. local    @@numDigits :@uint, \    ; (Local variables)
  59.     @@intDigits :@uint, \
  60.     @@decDig    :@uint, \
  61.     @@decLtZ    :@uint, \
  62.     @@decRtZ    :@uint, \
  63.     @@fillLen   :@uint, \
  64.     @@strLen    :@uint, \
  65.     @@rndNib    :byte
  66. @uses    ds,es,rsi,rdi,rbx,rcx,rdx
  67. ;.
  68. ; ----- Count significant bytes in BCD
  69.     @LES  rdi, [pBCD]
  70.     if @isDataFar
  71.     @LDSEGM ds, es
  72.     endif
  73.     mov   rcx, [bcdsz]
  74.     dec   rcx
  75.     add   rdi, rcx
  76.     mov   dh, [rdi]     ; Sign byte
  77.     dec   rdi
  78.     sub   rax, rax
  79.     std            ; Set direction flag
  80.     repz  scasb
  81.     adc   rcx, rax        ; Byte count
  82.     mov   dl, [rdi+1]    ; Non-zero byte (if count > 0)
  83.  
  84. ; ----- Fill in default values
  85. ;    (don't know width yet)
  86.     dec   rax        ; Acc = -1
  87.     cmp   [numDec], rax
  88.     jne sh @@d1
  89.     inc   [numDec]        ; Default numDec = 0
  90. @@d1:    cmp   [prec], rax
  91.     jne sh @@d2
  92.     push  [numDec]
  93.     pop   [prec]        ; Default precision = all
  94. @@d2:    inc   [fillCh]
  95.     jz sh @@d2a
  96.     dec   [fillCh]
  97.     jnz sh @@d3
  98. @@d2a:    mov   [fillCh], ' '
  99. @@d3:    inc   [sepCh]
  100.     jz sh @@d3a
  101.     dec   [sepCh]
  102.     jnz sh @@d4
  103. @@d3a:    mov   [sepCh], '.'
  104. @@d4:    cmp   [sepMCh], al
  105.     jne sh @@d5
  106.     mov   [sepMCh], ','
  107. @@d5:    ; signCh
  108.     test  dh, 80h        ; Force '-' for signed numbers
  109.     mov   dh, '-'
  110.     jnz sh @@d6
  111.     mov   dh, [signCh]
  112.     test  dh, dh
  113.     jz sh @@d6
  114.     cmp   dh, ' '
  115.     jz sh @@d6
  116.     mov   dh, '+'
  117. @@d6:    mov   [signCh], dh
  118.  
  119. ; ----- Compute no. of integers
  120.     add   rcx, rcx        ; 2 BCD digits per byte
  121.     jc sh @@err        ; Do away with biggies
  122.         jz sh @@sl1
  123.     cmp   dl, 10h        ; Decrement if no high digit
  124.     sbb   rcx, 0
  125. @@sl1:    ; integers = max(1, digits - numDec)
  126.     mov   [@@numDigits], rcx
  127.     sub   rcx, [numDec]
  128.     ja sh @@sl2
  129.     mov   rcx, 1
  130. @@sl2:    mov   [@@intDigits], rcx
  131.  
  132. ; ----- Compute no. of 1000's separators
  133.     sub   rax, rax        ; None
  134.     cmp   [sepMCh], al    ;  if suppressed
  135.     je sh @@sl3
  136.     mov   rax, rcx        ; Else (integers - 1) / 3
  137.     dec   rax
  138.     sub   rdx, rdx
  139.     mov   rbx, 3
  140.     div   BX
  141.  
  142. ; ----- Compute total string length
  143. ;    less blank-fill
  144. @@sl3:    mov   rcx, rax        ; 1000's separators
  145.     sub   rax, rax
  146.     cmp   al, [signCh]
  147.     adc   rcx, rax        ; + sign char (if non-zero)
  148.     cmp   rax, [prec]
  149.     adc   rcx, [prec]    ; + dot + decimals (if non-zero)
  150.     jc sh @@err
  151.     add   rcx, [@@intDigits]; + integers
  152.     jc sh @@err
  153.  
  154. ; ----- Check against boundaries
  155.     cmp   rcx, [strsz]
  156.     jae sh @@err
  157.     mov   rax, [width]
  158.     cmp   rax, -1
  159.     jne sh @@sl4
  160.     mov   [width], rcx    ; This is the default width
  161.     mov   rax, rcx
  162. @@sl4:    cmp   rax, [strsz]
  163.     jae sh @@err
  164.  
  165. ; ----- Get size of blank-fill
  166.     sub   rax, rcx
  167.     jnc sh @@sl5
  168.     sub   rax, rax
  169. @@sl5:    mov   [@@fillLen], rax
  170.     add   rcx, rax
  171.     mov   [@@strLen], rcx
  172.     jmp sh @@nd0
  173.  
  174.  
  175. ; ----- Error: return empty string
  176. @@err:    sub   rax, rax
  177.     @LES  rdi, [pStr]    ; df=1
  178.     stosb            ; Make AsciiZ
  179.     jmp   @@ret        ; Return 0
  180.  
  181.  
  182. ; //////////////////////////////////////////////////////////////
  183. ; Before building the string, let's compute a few more variables
  184. ; to make things easier later on, figure out where to get the
  185. ; first BCD digit (with 2 digits per byte, it could be at an odd
  186. ; position), and get the digit needed to do rounding.
  187.  
  188.  
  189. ; ----- The digits in the fractional part may consist
  190. ;    of up to 3 parts: zero-fill on right, zero-fill
  191. ;    on left, and the decimal digits.
  192. ;    The length of the fractional part = [prec]
  193.     ;
  194. @@nd0:    ; Rightmost zerofill = max(0, prec-numDec)
  195.     mov   rax, [prec]
  196.     mov   rdx, rax
  197.     sub   rax, [numDec]
  198.     ja sh @@nd1
  199.     sub   rax, rax
  200. @@nd1:    mov   [@@decRtZ], rax
  201.     ; Leftmost zerofill = min(prec, max(0,numDec-digits))
  202.     mov   rax, [numDec]
  203.     sub   rax, [@@numDigits]
  204.     ja sh @@nd2
  205.     sub   rax, rax
  206. @@nd2:    cmp   rax, rdx
  207.     jb sh @@nd3
  208.     mov   rax, rdx
  209. @@nd3:    mov   [@@decLtZ], rax
  210.     ; Decimal digits = prec - rightfill - leftfill
  211.     sub   rdx, rax
  212.     sub   rdx, [@@decRtZ]
  213.     mov   [@@decDig], rdx
  214.  
  215. ; ----- Get nibble required for rounding
  216.     mov   rsi, @uiptr [pBCD]
  217.     sub   al, al        ; Default to no rounding
  218.     sub   dh, dh
  219.     mov   rbx, [numDec]
  220.     sub   rbx, [prec]
  221.     jbe sh @@fr2
  222.     mov   ah, bl
  223.     dec   rbx
  224.     shr   rbx, 1        ; Two BCD digits per byte
  225.     cmp   rbx, [bcdsz]
  226.     jae sh @@fr2
  227.     mov   al, [rsi+rbx]
  228.     inc   dh
  229.     test  ah, 1        ; If (numDec - prec) odd,
  230.     jnz sh @@fr1        ;  get low nibble
  231.     @shr  al, 4        ;  else get high nibble
  232.     inc   rbx
  233.     dec   dh
  234. @@fr1:    and   al, 0fh
  235.     add   rsi, rbx
  236. @@fr2:    mov   [@@rndNib], al
  237.     ; dh = odd nibble flag, rsi digit ptr
  238.  
  239.  
  240. ; //////////////////////////////////
  241. ; ----- Build string in destination
  242. ;    from right (hi) to left (lo)
  243. ;    (direction flag is set)
  244.     @LES  rdi, [pStr]
  245.     add   rdi, [@@strLen]
  246.     sub   al, al        ; Zero-
  247.     stosb            ;  terminate the string
  248.  
  249. ; ----- Left justify if appr.
  250.     test  [rtJust], -1
  251.     jnz sh @@s1
  252.     mov   rcx, [@@fillLen]
  253.     mov   al, [fillCh]
  254.     rep   stosb
  255.  
  256. ; ----- Sign character (if non-zero)
  257. @@s1:    mov   al, [signCh]
  258.     test  al, al
  259.     jz sh @@s2
  260.     stosb
  261.  
  262. ; ----- Rightmost '0' fill (if any)
  263. @@s2:    mov   rcx, [@@decRtZ]
  264.     mov   al, '0'
  265.     rep   stosb
  266.  
  267. ; ----- Decimal digits
  268.     mov   rbx, rdi        ; Remember start of digits
  269.         mov   ah, [rsi]
  270.     mov   rcx, [prec]
  271.     test  rcx, rcx
  272.     jz sh @@s5        ; Branch if no fraction part
  273.     mov   rcx, [@@decDig]
  274.     test  rcx, rcx
  275.     jz sh @@s4
  276.     test  dh, 1
  277.     jnz sh @@s3a        ; Start at odd nibble
  278. @@s3:    mov   al, [rsi]
  279.     mov   ah, al
  280.     and   al, 0fh
  281.     or    al, '0'
  282.     stosb
  283.     inc   dh        ; Next nibble is high
  284.     dec   rcx
  285.     jz sh @@s4
  286. @@s3a:    mov   al, ah
  287.     @shr  al, 4
  288.     or    al, '0'
  289.     inc   rsi
  290.     stosb
  291.     dec   dh        ; Next nibble is low
  292.     dec   rcx
  293.     jnz   @@s3
  294.  
  295. ; ----- Leftmost '0' fill (if any)
  296. @@s4:    mov   rcx, [@@decLtZ]
  297.     mov   al, '0'
  298.     rep   stosb
  299.  
  300. ; ----- Decimal dot
  301.     mov   al, [sepCh]
  302.     stosb
  303.  
  304. ; ----- Integers + 1000's separators
  305. @@s5:    mov   rcx, [@@intDigits]
  306.     test  rcx, rcx
  307.     jz sh @@sz
  308.     test  dh, 1
  309.     jnz sh @@s8
  310. @@s6:    mov   dl, 3
  311. @@s7:    mov   al, [rsi]
  312.     mov   ah, al
  313.     and   al, 0fh
  314.     or    al, '0'
  315.     stosb
  316.     dec   rcx
  317.     jz sh @@sz
  318.     dec   dl
  319.     jnz sh @@s9
  320.     mov   al, [sepMCh]
  321.     test  al, al
  322.     jz sh @@s8
  323.     stosb
  324. @@s8:    mov   dl, 3
  325. @@s9:    mov   al, ah
  326.     @shr  al, 4
  327.     or    al, '0'
  328.     stosb
  329.     inc   rsi
  330.     dec   rcx
  331.     jz sh @@sz
  332.     dec   dl
  333.     jnz   @@s7
  334.     mov   al, [sepMCh]
  335.     test  al, al
  336.     jz    @@s6
  337.     stosb
  338.     jmp   @@s6
  339.  
  340. ; ----- Right justify if appr.
  341. @@sz:    test  [rtJust], -1
  342.     jz sh @@rd1
  343.     mov   rcx, [@@fillLen]
  344.     mov   al, [fillCh]
  345.     rep   stosb
  346.  
  347. ; //////////////////////////////
  348. ; ----- String done, now adjust
  349. ;    if we're to round up
  350. @@rd1:    cmp   [@@rndNib], 5
  351.     jb sh @@end
  352.     mov   rdi, rbx        ; Recall start of digits
  353.     mov   rsi, rbx
  354.     if @isDataFar
  355.     @LDSEGM ds, es
  356.     endif
  357.     mov   rcx, [@@decDig]
  358.     add   rcx, [@@decLtZ]
  359.     jz sh @@rd3
  360. @@rd2:    lodsb
  361.     inc   al
  362.     cmp   al, '9'
  363.     stosb
  364.     jbe sh @@end
  365.     sub   @bptr [rsi+1], 0Ah
  366.     dec   rcx
  367.     jnz   @@rd2
  368.     cmpsb            ; Skip decimal dot
  369. @@rd3:    mov   rcx, [@@intDigits]
  370.     test  rcx, rcx
  371.     jz sh @@rd6
  372.     mov   ah, [sepMCh]
  373.     jmp sh @@rd5
  374. @@rd4:    cmpsb            ; Skip thousands separator
  375. @@rd5:    cmp   ah, [rsi]
  376.     je    @@rd4
  377.     lodsb
  378.     inc   al
  379.     cmp   al, '9'
  380.     stosb
  381.     jbe sh @@end
  382.     sub   @bptr [rsi+1], 0Ah
  383.     dec   rcx
  384.     jnz   @@rd5
  385.  
  386. ; ----- Special case: The rounding carry has
  387. ;    snowballed past the leftmost digit.
  388. @@rd6:    mov   rsi, @uiptr [pStr]
  389.     cmp   rdi, rsi
  390.     jae sh @@rd9        ; OK to overlay left-side blank-fill
  391.     mov   rdi, rsi
  392.     mov   rcx, [@@strLen]
  393.     mov   rax, [@@fillLen]
  394.     sub   rcx, rax
  395.     add   rdi, rcx
  396.     test  rax, rax
  397.     jnz sh @@rd8        ; OK to overlay right-side blank-fill
  398.     inc   rcx        ; Include trailing zero
  399.     jz sh @@rde
  400.     cmp   rcx, [strsz]    ; Ok to expand?
  401.     jb sh @@rd7        ; Yes
  402. @@rde:    jmp   @@err        ; No, must return error
  403. @@rd7:    inc   rdi
  404.     inc   [@@strLen]
  405. @@rd8:    lea   rsi, [rdi-1]
  406.     rep   movsb        ; Move string right (up mem)
  407. @@rd9:    mov   al, '1'
  408.     stosb            ; Store the rounding carry
  409.     ; ToDo:    Insert sepMCh if carry
  410.     ;    begins a new 1000's group
  411.  
  412. ; ----- Return string length
  413. @@end:    mov   rax, [@@strLen]
  414. @@ret:    cld            ; Clear direction flag
  415.     RET
  416. bcdFmt    endp
  417.  
  418.     END