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

  1.     title    BCDASM -- Copyright 1997, Morten Elling
  2.     subttl    Signed division and modulo of packed signed BCDs
  3.  
  4.     include    model.inc
  5.     include    modelt.inc
  6.     include    bcd.ash
  7.  
  8.     @CODESEG
  9.  
  10. ;//////////////////////////////////////////////////////////////////////
  11. ;//    Name    bcdIdiv
  12. ;//    Desc    Signed division and modulo of two packed signed BCDs
  13. ;//        (lo(dst) = dst / src,  hi(dst) = dst % src)
  14. ;//
  15. ;//
  16. ;//    Entry    Passed args (see note)
  17. ;//    Exit    Acc > 0:
  18. ;//          Low (destination) = packed signed BCD quotient
  19. ;//          high(destination) = packed signed BCD remainder
  20. ;//          replacing the dividend.
  21. ;//          The byte size of each of these results is half the
  22. ;//          size of the original destination operand.
  23. ;//
  24. ;//          The sign of the quotient is determined from the
  25. ;//          signs of divisor and dividend.
  26. ;//          The remainder keeps the sign of the dividend.
  27. ;//
  28. ;//        Acc = 0:
  29. ;//          Error: division by zero or overflow (result would
  30. ;//          exceed destination's size); in these cases, the
  31. ;//          dividend is unchanged.
  32. ;//
  33. ;//
  34. ;//    Note    Before calling this procedure, both operands must be
  35. ;//        scaled to double-size, for example after a call to
  36. ;//        bcdImul (dividend) or bcdSx (divisor) both of which
  37. ;//        create a double-size packed signed BCD result.
  38. ;//        The most-significant half of the divisor must not
  39. ;//        contain any significant BCD digits (i.e. all zeros,
  40. ;//        except for the sign bit).
  41. ;//
  42. ;//        The divisor is left unchanged by this procedure (but
  43. ;//        provides temporary work space used during execution).
  44. ;//
  45. ;//
  46. ;//    ToDo    Write wrapper to make 'bcdMod' procedure.
  47.  
  48. bcdIdiv proc            ; NZB = non-zero byte ('MSB')
  49. arg    dstBCD    :dataptr, \    ; Addr of dividend (size = srcsz)
  50.     srcBCD    :dataptr, \    ; Addr of divisor
  51.     srcsz    :@uint        ; Byte size of divisor (see note)
  52. local    @@signs :@uint, \    ; src's sign + (dst's sign SHL 8)
  53.     @@nzbs    :@uint, \    ; src's NZB + (dst's NZB SHL 8)
  54.     @@srcsb :@uint, \    ; # significant bytes in divisor
  55.     @@dstsb :@uint, \    ; # significant bytes in dividend
  56.     @@src2p :@uint, \    ; Addr of shifted divisor
  57.     @@quotp :@uint        ; Addr of quotient insertion point
  58. @uses    ds,es,rsi,rdi,rbx,rcx,rdx
  59. ;.
  60. ; Procedure notes
  61. ;
  62. ; The format of the operands has been chosen to keep a consistent,
  63. ; simple calling interface (no separate work buffer). As a side
  64. ; effect, we note that maximum two segment registers are required
  65. ; to access the data.
  66. ;
  67. ;
  68. ; The fact that dividend and divisor are in packed BCD format
  69. ; allows us to easily compute the byte size of the quotient and
  70. ; remainder (one byte = two digits), once we know the lengths of
  71. ; the operands.
  72. ;
  73. ; First, then, count the significant bytes in each of the operands.
  74. ; Second, do away with two special cases: division by zero (which
  75. ; can't be done) and division overflow (result exceeds destination
  76. ; size). Third, if the dividend is smaller than the divisor, the
  77. ; division loop isn't required.
  78.  
  79.  
  80. ; ----- Determine no. of significant
  81. ;    bytes in divisor
  82.     std            ; Auto-decrement index registers
  83.     mov   rbx, [srcsz]
  84.     dec   rbx        ; Size excl. sign byte
  85.     @LES  rdi, [srcBCD]
  86.     add   rdi, rbx
  87.     dec   rdi
  88.     mov   rcx, rbx
  89.     sub   rax, rax        ; Zero accumulator
  90.     repz  scasb
  91.     jz sh @@divz        ; Exit if divisor is zero
  92.     adc   rcx, rax
  93.     mov   [@@srcsb], rcx
  94.     mov   dl, @ES [rdi+1]    ; Get first non-zero byte (NZB)
  95.  
  96. ; ----- Determine no. of significant
  97. ;    bytes in dividend
  98.     @LES  rdi, [dstBCD]
  99.     add   rdi, rbx
  100.     dec   rdi
  101.     mov   rcx, rbx
  102.     repz  scasb
  103.     adc   rcx, rax
  104.     mov   [@@dstsb], rcx
  105.     mov   dh, @ES [rdi+1]
  106.         mov   [@@nzbs], rdx
  107.  
  108. ; ----- Check for possible overflow
  109.     lea   rdx, [rbx+1]    ; Quotient and remainder must
  110.     shr   rdx, 1        ;   each fit within srcsz/2-1
  111.     mov   rax, rcx        ; Dividend's significant bytes
  112.     sub   rax, [@@srcsb]    ; - divisor's ditto
  113.     jnc sh @@p1        ; (If divisor > dividend
  114.     cmp   rcx, rdx        ;  remainder = dividend
  115.     jnc sh @@divz        ;  which must also fit,
  116.     sub   rax, rax        ;  quotient = 0, i.e. 1 byte)
  117. @@p1:    inc   rax        ; + a possible odd BCD digit
  118.     cmp   rax, rdx        ; = max. signif. bytes in quotient
  119.     jnc sh @@divz        ; If not below limit then exit
  120.  
  121.     ; ToDo: Allow division when = limit and no odd BCD
  122.     ;     byte will appear. As the code is, a 36-digit
  123.     ;    number can't be divided by an 18-digit ditto.
  124.  
  125. ; ----- Get, save, and reset operands's signs
  126. ;    rbx = srcsz-1, rdx = srcsz/2
  127.     mov   rdi, @uiptr [dstBCD]
  128.     add   rdi, rbx        ; Point to sign byte
  129.     sub   rax, rax        ; Zero accumulator
  130.     xchg  ah, @ES [rdi]    ; Set dividend's sign = 0
  131.     @LDS  rsi, [srcBCD]
  132.     add   rsi, rbx        ; Point to sign byte
  133.     xchg  al, [rsi]        ; Set divisor's sign = 0
  134.     mov   [@@signs], rax    ; Save original signs
  135.  
  136. ; ----- 'Early out' condition?
  137.     mov   rbx, [@@dstsb]    ; Compare length of operands
  138.     sub   rbx, [@@srcsb]
  139.     ja sh @@loopinit
  140.     jb sh @@p2
  141.     mov   rcx, [@@nzbs]    ; Same length, compare 1st byte
  142.     cmp   ch, cl
  143.     jae sh @@loopinit
  144.  
  145. ; ----- Special case: divisor > dividend
  146. ;    Set remainder = dividend and quotient = zero
  147. ;    (direction flag is set)
  148. @@p2:    mov   [rsi], al     ; Restore divisor's sign
  149.     mov   al, ah
  150.     stosb            ; Remainder keeps dividend's sign
  151.     @LDSEGM ds, es
  152.     mov   rsi, rdi
  153.     sub   rsi, rdx
  154.     mov   rcx, rdx
  155.     dec   rcx        ; (srcsz/2 - 1)
  156.     rep   movsb        ; Transfer dividend to hi(dst)
  157.     mov   rcx, rdx
  158.     sub   rax, rax
  159.     rep   stosb        ; Reset quotient to zero
  160.     jmp   @@ok        ; Return 1
  161.  
  162. ; ----- Special cases: divisor is zero / overflow
  163. @@divz: sub   rax, rax        ; Return 0
  164.     jmp   @@ret
  165.  
  166.  
  167. ;-/////////////////////////////////////////////////////////////////
  168. ; Notes on BCD division algorithm
  169. ;
  170. ; Quotient and remainder are formed by repeated subtraction in
  171. ; each digit position. This algorithm requires two loops, the
  172. ; first (subtracting the divisor shifted left by 4 bits) creates
  173. ; the high nibble, the second (subtracting the original divisor)
  174. ; creates the low nibble.
  175. ;
  176. ; For storage efficiency, the quotient is stored in the part of
  177. ; the destination that is vacated by the diminishing remainder.
  178. ; For speed, the shifted divisor is kept in the unused part of
  179. ; source storage (alternatives: shift divisor left and right on
  180. ; each loop, or loop up to 99 times on a byte -- slow and ugly,
  181. ; but it will keep memory requirements at a minimum, i.e.
  182. ; normal-size divisor).
  183. ;
  184. ;
  185. ; Storage use:
  186. ;    Destination    Source
  187. ;    srrrrrrrrrrr    szzzzzzddddd    On entry to procedure
  188. ;    zrrrrrrrrrrr    DDDDDDzddddd    Before division
  189. ;    qqq..rrrrrrr    DDDDDDzddddd    During division
  190. ;    srrrrrsqqqqq    szzzzzzddddd    On exit from procedure
  191. ;
  192. ;    (r)emainder    (s)ign
  193. ;    (q)uotient    (z)ero
  194. ;    (d)ivisor
  195. ;    (D)ivisor shifted left by 4 bits
  196. ;
  197. ;    Note that the top bytes (signs) are used as work space.
  198. ;
  199. ;
  200. ; Division example:
  201. ;    +4+3+2+1+0 <= index into dividend (2 digits per byte)
  202. ;    0026189023 / 8038 = 3258 (rem. 1219)
  203. ;    ..080380.. (x3)  (#1 hi)
  204. ;    ..008038.. (x2)  (#1 lo)
  205. ;    ....080380 (x5)  (#2 hi)
  206. ;    ....008038 (x8)  (#2 lo)
  207. ;
  208. ;    loop #                        #1  #2
  209. ;    length of dividend    @@dstsb             4   3
  210. ;    length of divisor    @@srcsb + j         3   3
  211. ;    subtraction index    @@dstsb - @@srcsb - i     1   0
  212. ;    exit condition        index < 0
  213. ;
  214. ;    j = 1
  215. ;    i = 1 if ((@@dstsb > @@srcsb) and (dst's NZB < src's NZB))
  216. ;    (need i to keep within limits, and j to propagate carry,
  217. ;    e.g. when computing 100/9)
  218. ;-
  219.  
  220. ; ----- Compute auxiliary variables
  221. ;    for fast access
  222. @@loopinit:
  223.     ; ds = srcBCD segment, es = dstBCD segment
  224.     ; rbx = (@@dstsb - @@srcsb)
  225.     cld            ; Clear direction flag
  226.     mov   rdi, @uiptr [dstBCD]
  227.     mov   rax, [srcsz]
  228.     add   rdi, rax
  229.     dec   rdi        ; Point to top byte of dst
  230.     mov   [@@quotp], rdi    ; = Quotient insertion point
  231.     shr   rax, 1
  232.     mov   rsi, @uiptr [srcBCD]
  233.     mov   rdi, rsi
  234.     add   rdi, rax        ; Point to high half of src
  235.     mov   [@@src2p], rdi    ; = Addr of shifted divisor
  236.  
  237. ; ----- Copy divisor to high half of src
  238. ;    shifting it left by one nibble
  239.         mov   rcx, [@@srcsb]
  240.     sub   dl, dl
  241. @@svl:    lodsb
  242.     @shl  rax, 4
  243.     or    al, dl
  244.     mov   [rdi], al
  245.     and   ah, 0fh
  246.     mov   dl, ah
  247.     inc   rdi
  248.     dec   rcx
  249.     jnz   @@svl
  250.     mov   [rdi], ah     ; Store odd nibble
  251.  
  252. ; ----- Adjust subtraction point and
  253. ;    divisor's length if needed
  254.     test  rbx, rbx
  255.     jz sh @@vl1
  256.         mov   rax, [@@nzbs]
  257.     cmp   ah, al
  258.     sbb   rbx, 0        ; Subtraction point
  259. @@vl1:    mov   rdx, [@@srcsb]
  260.     inc   rdx        ; Divisor's length
  261.  
  262.  
  263. ; ///// Top of loop ////////////
  264. ;    Usage:
  265. ;    rbx = subtraction point
  266. ;    rdx = divisor's length (constant)
  267. ;    rsi = divisor pointer
  268. ;    rdi = dividend pointer
  269. ;       ah  = quotient byte (BCD)
  270. @@looptop:
  271.     add   rbx, @uiptr [dstBCD] ; Base register = subtraction point
  272.     sub   ah, ah        ; Initialize quotient digit pair
  273.  
  274. ; ----- Do 'high' subtraction (shifted divisor)
  275. @@hi1:    mov   rdi, rbx        ; Subtraction point
  276.     mov   rsi, [@@src2p]    ; Point to shifted divisor
  277.     mov   rcx, rdx        ; Loop count is divisor length
  278.     cmp   al, al        ; Clear carry
  279.     @alignn
  280. @@hi2:    mov   al, @ES [rdi]    ; Get two BCD digits of remainder
  281.     sbb   al, [rsi]        ; Subtract carry and divisor digits
  282.     das            ; Decimal adjust after subtraction
  283.     stosb            ; Store byte back
  284.     inc   rsi        ; Step divisor pointer
  285.     dec   rcx        ; Loop
  286.     jnz   @@hi2        ;   until done
  287.     jc sh @@hi3        ; If result negative, loop is done
  288.     add   ah, 10h        ; Count 'high' subtractions
  289.     jmp   @@hi1        ; Do next subtraction
  290.  
  291.     ; Add back since we went too far
  292. @@hi3:    sub   rdi, rdx        ; Reset
  293.     sub   rsi, rdx        ;   pointers
  294.     mov   rcx, rdx        ; cf=0
  295.     @alignn
  296. @@hi4:    mov   al, @ES [rdi]    ; Get byte from remainder
  297.     adc   al, [rsi]        ; Add carry and byte from divisor
  298.     daa            ; Decimal adjust after addition
  299.     stosb            ; Store byte back
  300.     inc   rsi        ; Step divisor pointer
  301.     dec   rcx        ; Loop
  302.     jnz   @@hi4        ;   until done
  303.  
  304.  
  305. ; ----- Do 'low' subtraction (unshifted divisor)
  306. @@lo1:    mov   rdi, rbx        ; Subtraction point
  307.     mov   rsi, @uiptr [srcBCD] ; Point to unshifted divisor
  308.     mov   rcx, rdx        ; Loop count is divisor's length
  309.     cmp   al, al        ; Clear carry
  310.     @alignn
  311. @@lo2:    mov   al, @ES [rdi]
  312.     sbb   al, [rsi]
  313.     das
  314.     stosb
  315.     inc   rsi
  316.     dec   rcx
  317.     jnz   @@lo2
  318.     jc sh @@lo3        ; If result negative, loop is done
  319.     inc   ah        ; Count no. of 'low' subtractions
  320.     jmp   @@lo1
  321.  
  322.     ; Add back since we went too far
  323. @@lo3:    sub   rdi, rdx
  324.     sub   rsi, rdx
  325.     mov   rcx, rdx        ; cf=0
  326.     @alignn
  327. @@lo4:    mov   al, @ES [rdi]
  328.     adc   al, [rsi]
  329.     daa
  330.     stosb
  331.     inc   rsi
  332.     dec   rcx
  333.     jnz   @@lo4
  334.  
  335. ; ----- Store the digit pair just generated
  336. ;    into the quotient
  337.     mov   rdi, [@@quotp]
  338.     mov   @ES [rdi], ah
  339.     dec   [@@quotp]        ; Decrement quotient insertion point
  340.     sub   rbx, @uiptr [dstBCD] ; Make index
  341.     jz sh @@loopend     ; Time to finish when zero
  342.     dec   rbx        ; Decrement subtraction index
  343.     jmp   @@looptop
  344.  
  345.  
  346. ; ///// End of loop ////////////
  347. @@loopend: ;
  348.  
  349. ; ----- Move and zero-extend
  350. ;    quotient to hi(dst)
  351.     mov   rbx, [srcsz]
  352.     shr   rbx, 1        ; (srcsz/2)
  353.     @LES  rdi, [dstBCD]
  354.     add   rdi, rbx
  355.     lea   rcx, [rdi+rbx]
  356.     mov   rsi, [@@quotp]
  357.     if @isDataFar
  358.     @LDSEGM ds, es
  359.     endif
  360.     inc   rsi        ; Point to quotient (rsi > rdi)
  361.     sub   rcx, rsi
  362.     mov   rdx, rcx
  363.     rep   movsb
  364.     mov   rcx, rdx
  365.     neg   rcx
  366.     add   rcx, rbx
  367.     sub   rax, rax
  368.     rep   stosb
  369.  
  370. ; ----- Swap lo(dst) and hi(dst)
  371.     mov   rsi, @uiptr [dstBCD]
  372.     lea   rdi, [rsi+rbx]
  373.     mov   rcx, rbx
  374.     sub   rdx, rdx
  375.     @alignn
  376. @@swp:    mov   al, [rsi]
  377.     mov   ah, [rdi]
  378.     mov   [rdi], al     ; Remainder's bytes go into hi(dst)
  379.     mov   [rsi], ah     ; Quotient's bytes go into lo(dst)
  380.     or    rdx, rax        ; OR bytes together for zero check
  381.     inc   rsi
  382.     inc   rdi
  383.     dec   rcx
  384.     jnz   @@swp
  385.  
  386. ; ----- Set result signs, adjust if zero results
  387.     xchg  dl, dh
  388.     neg   dl
  389.     sbb   dl, dl        ; 0 if quotient zero, else 0ffh
  390.     neg   dh
  391.     sbb   dh, dh        ; 0 if remainder zero, else 0ffh
  392.     mov   rax, [@@signs]    ; Get original signs
  393.     xor   al, ah        ; Quotient's sign is the complement
  394.     ;            ; Remainder keeps dividend's sign
  395.     and   rax, 8080h    ; Isolate sign bits
  396.     and   rax, rdx        ; Remove sign if result was zero
  397.     mov   [rsi-1], al    ; Store
  398.     mov   [rdi-1], ah    ;   signs
  399.  
  400. ; ----- Restore original divisor
  401.     @LES  rdi, [srcBCD]
  402.     mov   rcx, [srcsz]
  403.     shr   rcx, 1
  404.     add   rdi, rcx
  405.     dec   rdi
  406.     sub   al, al
  407.     rep   stosb
  408.     mov   al, @bptr [@@signs]
  409.     stosb
  410.     ;
  411. @@ok:    sub   rax, rax        ; Return 1 (success)
  412.     inc   rax
  413. @@ret:    cld            ; Clear direction flag
  414.     RET            ; Back to caller
  415. bcdIdiv endp
  416.  
  417.     END
  418.