Subversion Repositories Kolibri OS

Rev

Rev 9090 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ;    mpint.inc - Multi precision integer procedures
  2. ;
  3. ;    Copyright (C) 2015-2024 Jeffrey Amelynck
  4. ;
  5. ;    This program is free software: you can redistribute it and/or modify
  6. ;    it under the terms of the GNU General Public License as published by
  7. ;    the Free Software Foundation, either version 3 of the License, or
  8. ;    (at your option) any later version.
  9. ;
  10. ;    This program is distributed in the hope that it will be useful,
  11. ;    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ;    GNU General Public License for more details.
  14. ;
  15. ;    You should have received a copy of the GNU General Public License
  16. ;    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  
  18. ; Note:
  19. ;
  20. ; These procedures have been designed to work with unsigned integers.
  21. ; For compatibility reasons, the highest bit must always be 0.
  22. ;
  23. ; You have been warned!
  24.  
  25. MPINT_MAX_LEN = MAX_BITS/8
  26.  
  27.  
  28. ;;===========================================================================;;
  29. proc mpint_to_little_endian uses esi edi ecx, dst, src ;/////////////////////;;
  30. ;;---------------------------------------------------------------------------;;
  31. ;? Convert big endian MPINT to little endian MPINT.                          ;;
  32. ;;---------------------------------------------------------------------------;;
  33. ;> src = pointer to big endian MPINT                                         ;;
  34. ;> dst = pointer to buffer for little endian MPINT                           ;;
  35. ;;---------------------------------------------------------------------------;;
  36. ;< eax = MPINT number length                                                 ;;
  37. ;;===========================================================================;;
  38.  
  39.         mov     esi, [src]
  40.         mov     edi, [dst]
  41. ; Load length dword
  42.         lodsd
  43. ; Convert to little endian
  44.         bswap   eax
  45.         stosd
  46.         test    eax, eax
  47.         jz      .zero
  48. ; Copy data, convert to little endian meanwhile
  49.         push    eax
  50.         add     esi, eax
  51.         dec     esi
  52.         mov     ecx, eax
  53.         std
  54.   @@:
  55.         lodsb
  56.         mov     byte[edi], al
  57.         inc     edi
  58.         dec     ecx
  59.         jnz     @r
  60.  
  61.         cld
  62.         pop     eax
  63.   .zero:
  64.         ret
  65.  
  66. endp
  67.  
  68. ;;===========================================================================;;
  69. proc mpint_to_big_endian uses esi edi ecx, dst, src ;////////////////////////;;
  70. ;;---------------------------------------------------------------------------;;
  71. ;? Convert little endian MPINT to big endian MPINT.                          ;;
  72. ;;---------------------------------------------------------------------------;;
  73. ;> src = pointer to little endian MPINT                                      ;;
  74. ;> dst = pointer to buffer for big endian MPINT                              ;;
  75. ;;---------------------------------------------------------------------------;;
  76. ;< eax = MPINT number length                                                 ;;
  77. ;;===========================================================================;;
  78.  
  79.         mov     esi, [src]
  80.         mov     edi, [dst]
  81. ; Load length dword
  82.         lodsd
  83.         test    eax, eax
  84.         jz      .zero
  85.         mov     ecx, eax
  86.         add     esi, eax
  87.         dec     esi
  88.         push    eax     ; we'll return length to the caller later
  89.         bswap   eax
  90.         stosd
  91. ; Copy data, convert to big endian meanwhile
  92.         std
  93.   @@:
  94.         lodsb
  95.         mov     byte[edi], al
  96.         inc     edi
  97.         dec     ecx
  98.         jnz     @r
  99.         cld
  100.         pop     eax
  101.         ret
  102.  
  103.   .zero:
  104.         stosd           ; Number 0 has 0 data bytes
  105.         ret
  106.  
  107. endp
  108.  
  109. ;;===========================================================================;;
  110. proc mpint_print uses ecx esi eax, src ;/////////////////////////////////////;;
  111. ;;---------------------------------------------------------------------------;;
  112. ;? Print MPINT to the debug board.                                           ;;
  113. ;;---------------------------------------------------------------------------;;
  114. ;> src = pointer to little endian MPINT                                      ;;
  115. ;;---------------------------------------------------------------------------;;
  116. ;< -                                                                         ;;
  117. ;;===========================================================================;;
  118.  
  119.         DEBUGF  1, "0x"
  120.         mov     esi, [src]
  121.         mov     ecx, [esi]
  122.         test    ecx, ecx
  123.         jz      .zero
  124.         lea     esi, [esi + ecx + 4 - 1]
  125.         pushf
  126.         std
  127.   .loop:
  128.         lodsb
  129.         DEBUGF  1, "%x", eax:2
  130.         dec     ecx
  131.         jnz     .loop
  132.         DEBUGF  1, "\n"
  133.         popf
  134.  
  135.         ret
  136.  
  137.   .zero:
  138.         DEBUGF  1, "00\n"
  139.         ret
  140.  
  141. endp
  142.  
  143. ;;===========================================================================;;
  144. proc mpint_bits uses esi ecx, dst ;//////////////////////////////////////////;;
  145. ;;---------------------------------------------------------------------------;;
  146. ;? Count the number of bits in the MPINT                                     ;;
  147. ;;---------------------------------------------------------------------------;;
  148. ;> dst = pointer to little endian MPINT                                      ;;
  149. ;;---------------------------------------------------------------------------;;
  150. ;< eax = highest order bit number + 1                                        ;;
  151. ;;===========================================================================;;
  152.  
  153.         DEBUGF  1, "mpint_bits(0x%x): ", [dst]
  154.  
  155.         mov     esi, [dst]
  156.         mov     eax, [esi]
  157.         test    eax, eax
  158.         jz      .zero
  159.         add     esi, 4-1
  160. ; Find highest order byte
  161.   .byteloop:
  162.         cmp     byte[esi+eax], 0
  163.         jne     .nz
  164.         dec     eax
  165.         jnz     .byteloop
  166.   .zero:
  167.         DEBUGF  1, "%u\n", eax
  168.         ret
  169.   .nz:
  170.         mov     cl, byte[esi+eax]
  171. ; multiply (eax - 1) by 8 to get nr of bits before this byte
  172.         dec     eax
  173.         shl     eax, 3
  174. ; Now shift bits of the highest order byte right, until the byte reaches zero, counting bits meanwhile
  175.   .bitloop:
  176.         inc     eax
  177.         shr     cl, 1
  178.         jnz     .bitloop
  179.         DEBUGF  1, "%u\n", eax
  180.         ret
  181.  
  182. endp
  183.  
  184. ;;===========================================================================;;
  185. proc mpint_bytes uses esi, dst ;/////////////////////////////////////////////;;
  186. ;;---------------------------------------------------------------------------;;
  187. ;? Count the number of bytes in the MPINT                                    ;;
  188. ;;---------------------------------------------------------------------------;;
  189. ;> dst = pointer to little endian MPINT                                      ;;
  190. ;;---------------------------------------------------------------------------;;
  191. ;< eax = highest order byte number + 1                                       ;;
  192. ;;===========================================================================;;
  193.  
  194.         DEBUGF  1, "mpint_bytes(0x%x): ", [dst]
  195.  
  196.         mov     esi, [dst]
  197.         mov     eax, [esi]
  198.         test    eax, eax
  199.         jz      .done
  200.         add     esi, 4-1
  201. ; Find highest order byte
  202.   .byteloop:
  203.         cmp     byte[esi+eax], 0
  204.         jne     .done
  205.         dec     eax
  206.         jnz     .byteloop
  207.   .done:
  208.         DEBUGF  1, "%u\n", eax
  209.         ret
  210.  
  211. endp
  212.  
  213. ;;===========================================================================;;
  214. proc mpint_cmp uses esi edi edx ecx ebx eax, src, dst ;//////////////////////;;
  215. ;;---------------------------------------------------------------------------;;
  216. ;? Compare two MPINTS.                                                       ;;
  217. ;;---------------------------------------------------------------------------;;
  218. ;> dst = pointer to little endian MPINT                                      ;;
  219. ;> src = pointer to little endian MPINT                                      ;;
  220. ;;---------------------------------------------------------------------------;;
  221. ;< flags are set as for single precision CMP instruction                     ;;
  222. ;;===========================================================================;;
  223.  
  224.         DEBUGF  1, "mpint_cmp(0x%x, 0x%x)\n", [dst], [src]
  225.  
  226. ; First, check the size of both numbers
  227.         stdcall mpint_bytes, [dst]
  228.         mov     ecx, eax
  229.         stdcall mpint_bytes, [src]
  230. ; If one number has more bytes, it is bigger
  231.         cmp     eax, ecx
  232.         jne     .got_answer
  233. ; If both numbers have 0 bytes, they are equal
  234.         test    ecx, ecx
  235.         jz      .got_answer
  236. ; Numbers have equal amount of bytes
  237. ; Start comparing from the MSB towards the LSB
  238.         mov     esi, [src]
  239.         mov     edi, [dst]
  240.         lea     esi, [esi + 4 + ecx]
  241.         lea     edi, [edi + 4 + ecx]
  242. ; If remaining bytes is not divisible by 4, compare only one byte at a time
  243.   .loop_1:
  244.         test    ecx, 111b
  245.         jz      .done_1
  246.         dec     esi
  247.         dec     edi
  248.         mov     al, byte[esi]
  249.         cmp     al, byte[edi]
  250.         jne     .got_answer
  251.         dec     ecx
  252.         jmp     .loop_1
  253. ; Remaining bytes is divisable by 8, compare dwords
  254.   .done_1:
  255.         shr     ecx, 3
  256.         jz      .got_answer
  257.  
  258. align 8
  259.   .loop_8:
  260.         lea     esi, [esi-8]
  261.         lea     edi, [edi-8]
  262.         mov     eax, [esi+04]
  263.         mov     ebx, [esi+00]
  264.         cmp     eax, [edi+04]
  265.         jne     .got_answer
  266.         cmp     ebx, [edi+00]
  267.         jne     .got_answer
  268.         dec     ecx
  269.         jnz     .loop_8
  270.  
  271.   .got_answer:
  272.         ret
  273.  
  274. endp
  275.  
  276. ;;===========================================================================;;
  277. proc mpint_mov uses esi edi edx ecx ebx eax, dst, src ;//////////////////////;;
  278. ;;---------------------------------------------------------------------------;;
  279. ;? Copy MPINT.                                                               ;;
  280. ;;---------------------------------------------------------------------------;;
  281. ;> dst = pointer to buffer for little endian MPINT                           ;;
  282. ;> src = pointer to little endian MPINT                                      ;;
  283. ;;---------------------------------------------------------------------------;;
  284. ;< dst = src                                                                 ;;
  285. ;;===========================================================================;;
  286.  
  287.         DEBUGF  1, "mpint_mov(0x%x, 0x%x)\n", [dst], [src]
  288.  
  289.         mov     esi, [src]
  290.         mov     edi, [dst]
  291.         mov     ecx, [esi]      ; Get dword count + 1
  292.         add     ecx, 7          ;
  293.         shr     ecx, 2          ;
  294.         mov     edx, ecx
  295.  
  296.         shr     ecx, 3
  297.         test    ecx, ecx
  298.         jz      .no32
  299. align 8
  300.   .loop32:
  301.         mov     eax, [esi+00]
  302.         mov     ebx, [esi+04]
  303.         mov     [edi+00], eax
  304.         mov     [edi+04], ebx
  305.  
  306.         mov     eax, [esi+08]
  307.         mov     ebx, [esi+12]
  308.         mov     [edi+08], eax
  309.         mov     [edi+12], ebx
  310.  
  311.         mov     eax, [esi+16]
  312.         mov     ebx, [esi+20]
  313.         mov     [edi+16], eax
  314.         mov     [edi+20], ebx
  315.  
  316.         mov     eax, [esi+24]
  317.         mov     ebx, [esi+28]
  318.         mov     [edi+24], eax
  319.         mov     [edi+28], ebx
  320.  
  321.         lea     esi, [esi+32]
  322.         lea     edi, [edi+32]
  323.         dec     ecx
  324.         jnz     .loop32
  325.   .no32:
  326.  
  327.         test    edx, 100b
  328.         jz      .no16
  329.  
  330.         mov     eax, [esi+00]
  331.         mov     ebx, [esi+04]
  332.         mov     [edi+00], eax
  333.         mov     [edi+04], ebx
  334.  
  335.         mov     eax, [esi+08]
  336.         mov     ebx, [esi+12]
  337.         mov     [edi+08], eax
  338.         mov     [edi+12], ebx
  339.  
  340.         lea     esi, [esi+16]
  341.         lea     edi, [edi+16]
  342.   .no16:
  343.  
  344.         test    edx, 010b
  345.         jz      .no8
  346.  
  347.         mov     eax, [esi+00]
  348.         mov     ebx, [esi+04]
  349.         mov     [edi+00], eax
  350.         mov     [edi+04], ebx
  351.  
  352.         lea     esi, [esi+08]
  353.         lea     edi, [edi+08]
  354.   .no8:
  355.  
  356.         test    edx, 001b
  357.         jz      .no4
  358.  
  359.         mov     eax, [esi+00]
  360.         mov     [edi+00], eax
  361.   .no4:
  362.  
  363.         ret
  364.  
  365. endp
  366.  
  367. ;;===========================================================================;;
  368. proc mpint_shl1 uses edi ecx, dst ;//////////////////////////////////////////;;
  369. ;;---------------------------------------------------------------------------;;
  370. ;? Shift little endian MPINT one bit to the left.                            ;;
  371. ;;---------------------------------------------------------------------------;;
  372. ;> dst = pointer to little endian MPINT                                      ;;
  373. ;;---------------------------------------------------------------------------;;
  374. ;< dst = dst SHL 1                                                           ;;
  375. ;;===========================================================================;;
  376.  
  377.         DEBUGF  1, "mpint_shl1(0x%x)\n", [dst]
  378.  
  379.         mov     edi, [dst]
  380.         mov     ecx, [edi]
  381.         test    ecx, 11b
  382.         jnz     .adjust_needed
  383.         shr     ecx, 2
  384.         jz      .done
  385.  
  386.   .length_ok:
  387.         add     edi, 4
  388. ; Do the lowest order dword first
  389.         shl     dword[edi], 1
  390.         lea     edi, [edi+4]
  391.         dec     ecx
  392.         jz      .done
  393. ; And the remaining dwords
  394.   .loop:
  395.         rcl     dword[edi], 1
  396.         lea     edi, [edi+4]
  397.         dec     ecx
  398.         jnz     .loop
  399.   .done:
  400.         jc      .carry
  401.         test    dword[edi-4], 0x10000000
  402.         jnz     .add0
  403.         ret
  404.  
  405.   .carry:
  406.         mov     ecx, [dst]
  407.         cmp     dword[ecx], MPINT_MAX_LEN
  408.         je      .ovf
  409.         add     dword[ecx], 4
  410.         mov     dword[edi], 1
  411.         ret
  412.  
  413.   .add0:
  414.         mov     ecx, [dst]
  415.         cmp     dword[ecx], MPINT_MAX_LEN
  416.         je      .ovf
  417.         add     dword[ecx], 4
  418.         mov     dword[edi], 0
  419.         ret
  420.  
  421.   .ovf:
  422.         int3
  423. ;;;;
  424.         ret
  425.  
  426.   .adjust_needed:
  427.         add     ecx, 3
  428.         and     ecx, not 3
  429.         stdcall mpint_grow, edi, ecx
  430.         jmp     .length_ok
  431.  
  432. endp
  433.  
  434. ;;===========================================================================;;
  435. proc mpint_shr1 uses edi ecx, dst ;//////////////////////////////////////////;;
  436. ;;---------------------------------------------------------------------------;;
  437. ;? Shift little endian MPINT one bit to the right.                           ;;
  438. ;;---------------------------------------------------------------------------;;
  439. ;> dst = pointer to little endian MPINT                                      ;;
  440. ;;---------------------------------------------------------------------------;;
  441. ;< dst = dst SHR 1                                                           ;;
  442. ;;===========================================================================;;
  443.  
  444.         DEBUGF  1, "mpint_shr1(0x%x)\n", [dst]
  445.  
  446.         mov     edi, [dst]
  447.         mov     ecx, [edi]
  448.         test    ecx, 11b
  449.         jnz     .adjust_needed
  450.  
  451.   .length_ok:
  452.         shr     ecx, 2
  453.         jz      .done
  454.         lea     edi, [edi+ecx*4]
  455. ; Do the highest order dword first
  456.         shr     dword[edi], 1
  457.         lea     edi, [edi-4]
  458.         dec     ecx
  459.         jz      .done
  460. ; And the remaining dwords
  461.   .loop:
  462.         rcr     dword[edi], 1
  463.         lea     edi, [edi-4]
  464.         dec     ecx
  465.         jnz     .loop
  466.   .done:
  467.         ret
  468.  
  469.   .adjust_needed:
  470.         add     ecx, 3
  471.         and     ecx, not 3
  472.         stdcall mpint_grow, edi, ecx
  473.         jmp     .length_ok
  474.  
  475. endp
  476.  
  477. ;;===========================================================================;;
  478. proc mpint_shl uses eax ebx ecx edx esi edi, dst, shift ;////////////////////;;
  479. ;;---------------------------------------------------------------------------;;
  480. ;? Left shift little endian MPINT by x bits.                                 ;;
  481. ;;---------------------------------------------------------------------------;;
  482. ;> dst = pointer to little endian MPINT                                      ;;
  483. ;> shift = number of bits to shift the MPINT                                 ;;
  484. ;;---------------------------------------------------------------------------;;
  485. ;< -                                                                         ;;
  486. ;;===========================================================================;;
  487.  
  488.         DEBUGF  1, "mpint_shl(0x%x, %u)\n", [dst], [shift]
  489.  
  490. ; Calculate new size
  491.         stdcall mpint_bits, [dst]
  492.         add     eax, [shift]
  493.         shr     eax, 3
  494.         cmp     eax, MPINT_MAX_LEN
  495.         jae     .overflow     ;;
  496.         inc     eax
  497.         mov     esi, [dst]
  498.         mov     [esi], eax
  499.  
  500.         mov     ecx, [shift]
  501.         shr     ecx, 3                  ; 8 bits in one byte
  502.         add     esi, MPINT_MAX_LEN+4-4
  503.         mov     edi, esi
  504.         and     ecx, not 11b
  505.         sub     esi, ecx
  506.         mov     edx, MPINT_MAX_LEN/4-1
  507.         shr     ecx, 2                  ; 4 bytes in one dword
  508.         push    ecx
  509.         sub     edx, ecx
  510.         mov     ecx, [shift]
  511.         and     ecx, 11111b
  512.         std
  513.   .loop:
  514.         lodsd
  515.         mov     ebx, [esi]
  516.         shld    eax, ebx, cl
  517.         stosd
  518.         dec     edx
  519.         jnz     .loop
  520.         lodsd
  521.         shl     eax, cl
  522.         stosd
  523.  
  524.         ; fill the LSBs with zeros
  525.         pop     ecx
  526.         test    ecx, ecx
  527.         jz      @f
  528.         xor     eax, eax
  529.         rep stosd
  530.   @@:
  531.         cld
  532.         ret
  533.  
  534.   .zero:
  535.         mov     eax, [dst]
  536.         mov     dword[eax], 0
  537.         ret
  538.  
  539.   .overflow:
  540.         int3
  541.         ret
  542.  
  543. endp
  544.  
  545. ;;===========================================================================;;
  546. proc mpint_shlmov uses eax ebx ecx edx esi edi, dst, src, shift ;////////////;;
  547. ;;---------------------------------------------------------------------------;;
  548. ;? Left shift by x bits and copy little endian MPINT.                        ;;
  549. ;;---------------------------------------------------------------------------;;
  550. ;> src = pointer to little endian MPINT                                      ;;
  551. ;> dst = pointer to little endian MPINT                                      ;;
  552. ;> shift = number of bits to shift the MPINT to the left                     ;;
  553. ;;---------------------------------------------------------------------------;;
  554. ;< dst = src SHL shift                                                       ;;
  555. ;;===========================================================================;;
  556.  
  557.         DEBUGF  1, "mpint_shlmov(0x%x, 0x%x, %u)\n", [dst], [src], [shift]
  558.  
  559.         stdcall mpint_bits, [src]
  560.         test    eax, eax
  561.         jz      .zero
  562.         add     eax, [shift]
  563.         shr     eax, 3
  564.         inc     eax
  565.         mov     edi, [dst]
  566.         mov     [edi], eax
  567.  
  568.         cmp     eax, MPINT_MAX_LEN
  569.         jae     .overflow
  570.  
  571.         mov     esi, [src]
  572.         add     esi, MPINT_MAX_LEN+4-4
  573.         add     edi, MPINT_MAX_LEN+4-4
  574.         mov     ecx, [shift]
  575.         shr     ecx, 3                  ; 8 bits in one byte
  576.         and     ecx, not 11b
  577.         sub     esi, ecx
  578.         mov     edx, MPINT_MAX_LEN/4-1
  579.         shr     ecx, 2                  ; 4 bytes in one dword
  580.         push    ecx
  581.         sub     edx, ecx
  582.         mov     ecx, [shift]
  583.         and     ecx, 11111b
  584.         std
  585.   .loop:
  586.         lodsd
  587.         mov     ebx, [esi]
  588.         shld    eax, ebx, cl
  589.         stosd
  590.         dec     edx
  591.         jnz     .loop
  592.         lodsd
  593.         shl     eax, cl
  594.         stosd
  595.  
  596.         ; fill the lsb bytes with zeros
  597.         pop     ecx
  598.         test    ecx, ecx
  599.         jz      @f
  600.         xor     eax, eax
  601.         rep stosd
  602.   @@:
  603.         cld
  604.         ret
  605.  
  606.   .zero:
  607.         mov     eax, [dst]
  608.         mov     dword[eax], 0
  609.         ret
  610.  
  611.   .overflow:
  612.         int3
  613.         ret
  614.  
  615. endp
  616.  
  617. ;;===========================================================================;;
  618. proc mpint_add uses esi edi edx ecx ebx eax, dst, src ;//////////////////////;;
  619. ;;---------------------------------------------------------------------------;;
  620. ;? Add a little endian MPINT to another little endian MPINT.                 ;;
  621. ;;---------------------------------------------------------------------------;;
  622. ;> src = pointer to little endian MPINT                                      ;;
  623. ;> dst = pointer to little endian MPINT                                      ;;
  624. ;;---------------------------------------------------------------------------;;
  625. ;< dst = dst + src                                                           ;;
  626. ;;===========================================================================;;
  627.  
  628. locals
  629.         dd_cnt  dd ?
  630. endl
  631.  
  632.         DEBUGF  1, "mpint_add(0x%x, 0x%x)\n", [dst], [src]
  633.  
  634. ; Grow both numbers to same 4-byte boundary, if not already the case
  635.         mov     esi, [src]
  636.         mov     edi, [dst]
  637.         mov     ecx, [esi]
  638.         test    ecx, 11b
  639.         jnz     .adjust_needed
  640.         cmp     ecx, [edi]
  641.         jne     .adjust_needed
  642.  
  643. ; Do the additions
  644.   .length_ok:
  645.         add     esi, 4
  646.         add     edi, 4
  647.         shr     ecx, 2
  648.         jz      .done
  649.         mov     eax, ecx
  650.         and     eax, 111b
  651.         mov     [dd_cnt], eax
  652.         shr     ecx, 3
  653.         test    ecx, ecx                ; Clear carry flag
  654.         jz      .no32
  655.  
  656.   .loop32:
  657.         mov     eax, [esi+00]
  658.         mov     edx, [esi+04]
  659.         adc     [edi+00], eax
  660.         adc     [edi+04], edx
  661.  
  662.         mov     eax, [esi+08]
  663.         mov     edx, [esi+12]
  664.         adc     [edi+08], eax
  665.         adc     [edi+12], edx
  666.  
  667.         mov     eax, [esi+16]
  668.         mov     edx, [esi+20]
  669.         adc     [edi+16], eax
  670.         adc     [edi+20], edx
  671.  
  672.         mov     eax, [esi+24]
  673.         mov     edx, [esi+28]
  674.         adc     [edi+24], eax
  675.         adc     [edi+28], edx
  676.  
  677.         lea     esi, [esi + 32]
  678.         lea     edi, [edi + 32]
  679.         dec     ecx
  680.         jnz     .loop32
  681.  
  682.   .no32:
  683.         mov     ecx, [dd_cnt]
  684.         dec     ecx
  685.         js      .check_ovf
  686.         inc     ecx
  687.   .dword_loop:
  688.         mov     eax, [esi+0]
  689.         adc     [edi+0], eax
  690.         lea     esi, [esi + 4]
  691.         lea     edi, [edi + 4]
  692.         dec     ecx
  693.         jnz     .dword_loop
  694.  
  695.   .check_ovf:
  696.         jc      .add_1                  ; Carry
  697.         test    byte[edi-1], 0x80
  698.         jnz     .add_0                  ; Currently highest bit set
  699.   .done:
  700.         ret
  701.  
  702. ; Highest bit was set, add a 0 byte as MSB if possible
  703.   .add_0:
  704.         mov     ecx, [dst]
  705.         cmp     dword[ecx], MPINT_MAX_LEN
  706.         jae     .ovf_0
  707.         mov     byte[edi], 0
  708.         inc     dword[ecx]
  709.         ret
  710.  
  711.   .ovf_0:
  712.         int3
  713.         clc
  714. ; TODO: set overflow flag?
  715.         ret
  716.  
  717. ; Carry bit was set, add a 1 byte as MSB if possible
  718.   .add_1:
  719.         mov     ecx, [dst]
  720.         cmp     dword[ecx], MPINT_MAX_LEN
  721.         jae     .ovf_1
  722.         mov     byte[edi], 1
  723.         inc     dword[ecx]
  724.         ret
  725.  
  726.   .ovf_1:
  727.         int3
  728.         stc
  729. ; TODO: set overflow flag?
  730.         ret
  731.  
  732.   .adjust_needed:
  733. ;        mov     ecx, [esi]
  734.         mov     eax, [edi]
  735. ; find the maximum of the two in ecx
  736.         mov     edx, ecx
  737.         sub     edx, eax
  738.         sbb     ebx, ebx
  739.         and     ebx, edx
  740.         sub     ecx, ebx
  741. ; align to 4 byte boundary
  742.         add     ecx, 3
  743.         and     ecx, not 3
  744. ; adjust both mpints
  745.         stdcall mpint_grow, esi, ecx
  746.         stdcall mpint_grow, edi, ecx
  747.         jmp     .length_ok
  748.  
  749. endp
  750.  
  751. ;;===========================================================================;;
  752. proc mpint_sub uses esi edi edx ecx ebx eax, dst, src ;//////////////////////;;
  753. ;;---------------------------------------------------------------------------;;
  754. ;? Subtract a little endian MPINT to another little endian MPINT.            ;;
  755. ;;---------------------------------------------------------------------------;;
  756. ;> src = pointer to little endian MPINT                                      ;;
  757. ;> dst = pointer to little endian MPINT                                      ;;
  758. ;;---------------------------------------------------------------------------;;
  759. ;< dst = dst - src                                                           ;;
  760. ;;===========================================================================;;
  761.  
  762. locals
  763.         dd_cnt  dd ?
  764. endl
  765.  
  766.         DEBUGF  1, "mpint_sub(0x%x, 0x%x)\n", [dst], [src]
  767.  
  768. ; Grow both numbers to same 4-byte boundary, if not already the case
  769.         mov     esi, [src]
  770.         mov     edi, [dst]
  771.         mov     ecx, [esi]
  772.         test    ecx, 11b
  773.         jnz     .adjust_needed
  774.         cmp     ecx, [edi]
  775.         jne     .adjust_needed
  776.  
  777. ; Do the subtractions
  778.   .length_ok:
  779.         add     esi, 4
  780.         add     edi, 4
  781.         shr     ecx, 2
  782.         jz      .done
  783.         mov     eax, ecx
  784.         and     eax, 111b
  785.         mov     [dd_cnt], eax
  786.         shr     ecx, 3
  787.         test    ecx, ecx                ; Clear carry flag
  788.         jz      .no32
  789.  
  790.   .loop32:
  791.         mov     eax, [esi+00]
  792.         mov     edx, [esi+04]
  793.         sbb     [edi+00], eax
  794.         sbb     [edi+04], edx
  795.  
  796.         mov     eax, [esi+08]
  797.         mov     edx, [esi+12]
  798.         sbb     [edi+08], eax
  799.         sbb     [edi+12], edx
  800.  
  801.         mov     eax, [esi+16]
  802.         mov     edx, [esi+20]
  803.         sbb     [edi+16], eax
  804.         sbb     [edi+20], edx
  805.  
  806.         mov     eax, [esi+24]
  807.         mov     edx, [esi+28]
  808.         sbb     [edi+24], eax
  809.         sbb     [edi+28], edx
  810.  
  811.         lea     esi, [esi + 32]
  812.         lea     edi, [edi + 32]
  813.         dec     ecx
  814.         jnz     .loop32
  815.  
  816.   .no32:
  817.         mov     ecx, [dd_cnt]
  818.         dec     ecx
  819.         js      .done
  820.         inc     ecx
  821.   .dword_loop:
  822.         mov     eax, [esi+0]
  823.         sbb     [edi+0], eax
  824.         lea     esi, [esi + 4]
  825.         lea     edi, [edi + 4]
  826.         dec     ecx
  827.         jnz     .dword_loop
  828.  
  829.   .done:
  830.         ret
  831.  
  832.   .adjust_needed:
  833. ;        mov     ecx, [esi]
  834.         mov     eax, [edi]
  835. ; find the maximum of the two in ecx
  836.         mov     edx, ecx
  837.         sub     edx, eax
  838.         sbb     ebx, ebx
  839.         and     ebx, edx
  840.         sub     ecx, ebx
  841. ; align to 4 byte boundary
  842.         add     ecx, 3
  843.         and     ecx, not 3
  844. ; adjust both mpints
  845.         stdcall mpint_grow, esi, ecx
  846.         stdcall mpint_grow, edi, ecx
  847.         jmp     .length_ok
  848.  
  849. endp
  850.  
  851.  
  852. ;;===========================================================================;;
  853. proc mpint_shrink uses eax edi, dst ;////////////////////////////////////////;;
  854. ;;---------------------------------------------------------------------------;;
  855. ;? Get rid of unnescessary leading zeroes on a little endian MPINT.          ;;
  856. ;;---------------------------------------------------------------------------;;
  857. ;> src = pointer to little endian MPINT                                      ;;
  858. ;;---------------------------------------------------------------------------;;
  859. ;<                                                                           ;;
  860. ;;===========================================================================;;
  861.  
  862.         DEBUGF  1, "mpint_shrink(0x%x)\n", [dst]
  863.  
  864.         stdcall mpint_bits, [dst]
  865.         shr     eax, 3
  866.         inc     eax
  867.         mov     edi, [dst]
  868.         mov     [edi], eax
  869.  
  870.         ret
  871.  
  872. endp
  873.  
  874. ;;===========================================================================;;
  875. proc mpint_grow uses eax edi ecx, dst, length ;//////////////////////////////;;
  876. ;;---------------------------------------------------------------------------;;
  877. ;? Add leading zeroes on a little endian MPINT.                              ;;
  878. ;;---------------------------------------------------------------------------;;
  879. ;> src = pointer to little endian MPINT                                      ;;
  880. ;> length = total length of the new MPINT in bytes                           ;;
  881. ;;---------------------------------------------------------------------------;;
  882. ;<                                                                           ;;
  883. ;;===========================================================================;;
  884.  
  885.         DEBUGF  1, "mpint_grow(0x%x, %u): ", [dst], [length]
  886.  
  887.         mov     edi, [dst]
  888.         mov     eax, [edi]
  889.         mov     ecx, [length]
  890.         sub     ecx, eax
  891.         jbe     .dontgrow
  892.         lea     edi, [edi + 4 + eax]
  893.         xor     al, al
  894.         rep stosb
  895.         mov     eax, [length]
  896.         mov     edi, [dst]
  897.         mov     [edi], eax
  898.         DEBUGF  1, "ok\n"
  899.         ret
  900.  
  901.   .dontgrow:
  902.         DEBUGF  1, "already large enough!\n"
  903.         ret
  904.  
  905. endp
  906.  
  907. ;;===========================================================================;;
  908. proc mpint_mul uses eax ebx ecx edx esi edi, dst, a, b ;/////////////////////;;
  909. ;;---------------------------------------------------------------------------;;
  910. ;? Multiply a little endian MPINT with another little endian MPINT and store ;;
  911. ;? in a third one.                                                           ;;
  912. ;;---------------------------------------------------------------------------;;
  913. ;> dst = pointer to little endian MPINT                                      ;;
  914. ;> a = pointer to little endian MPINT                                        ;;
  915. ;> b = pointer to little endian MPINT                                        ;;
  916. ;;---------------------------------------------------------------------------;;
  917. ;< dst = a * b                                                               ;;
  918. ;;===========================================================================;;
  919.  
  920. locals
  921.         asize   dd ?
  922.         bsize   dd ?
  923.         counter dd ?
  924.         esp_    dd ?
  925. endl
  926.  
  927.         DEBUGF  1, "mpint_mul(0x%x, 0x%x, 0x%x)\n", [dst], [a], [b]
  928.  
  929. ; Grow both numbers to individual 4-byte boundary, if not already the case
  930.         mov     esi, [a]
  931.         mov     edx, [b]
  932.         mov     ecx, [esi]
  933.         mov     ebx, [edx]
  934.         test    ecx, 11b
  935.         jnz     .adjust_needed
  936.         test    ebx, 11b
  937.         jnz     .adjust_needed
  938.   .length_ok:
  939.  
  940. ; Must have a size >= b size.
  941.         cmp     ebx, ecx
  942.         ja      .swap_a_b
  943.   .conditions_ok:
  944.  
  945. ; dst size will be a size + b size
  946.         lea     eax, [ebx + ecx]
  947.         cmp     eax, MPINT_MAX_LEN
  948.         ja      .ovf
  949.  
  950. ; [asize] = number of dwords in a
  951.         shr     ecx, 2
  952.         jz      .zero
  953.         mov     [asize], ecx
  954. ; esi = x ptr
  955.         add     esi, 4
  956.  
  957. ; [bsize] = number of dwords in b
  958.         shr     ebx, 2
  959.         jz      .zero
  960.         mov     [bsize], ebx
  961. ; edx = b ptr (temporarily)
  962.         add     edx, 4
  963.  
  964. ; store dst size
  965.         mov     edi, [dst]
  966.         mov     [edi], eax
  967. ; edi = dst ptr
  968.         add     edi, 4
  969.  
  970. ; Use esp as frame pointer instead of ebp
  971. ; ! Use the stack with extreme caution from here on !
  972.         mov     [esp_], esp
  973.         mov     esp, ebp
  974.  
  975. ; ebp = b ptr
  976.         mov     ebp, edx
  977.  
  978. ; Do the first multiplication
  979.         mov     eax, [esi]              ; load a[0]
  980.         mul     dword[ebp]              ; multiply by b[0]
  981.         mov     [edi], eax              ; store to dest[0]
  982. ;        mov     ecx, [asize]            ; asize
  983.         dec     ecx                     ; if asize = 1, bsize = 1 too
  984.         jz      .done
  985.  
  986. ; Prepare to enter loop1
  987.         mov     eax, [asize-ebp+esp]
  988.  
  989.         mov     ebx, edx
  990.         lea     esi, [esi + eax * 4]    ; make a ptr point at end
  991.         lea     edi, [edi + eax * 4]    ; offset dst ptr by asize
  992.         neg     ecx                     ; negate j size/index for inner loop
  993.         xor     eax, eax                ; clear carry
  994.  
  995. align 8
  996.   .loop1:
  997.         adc     ebx, 0
  998.         mov     eax, [esi + ecx * 4]    ; load next dword at a[j]
  999.         mul     dword[ebp]
  1000.         add     eax, ebx
  1001.         mov     [edi + ecx * 4], eax
  1002.         inc     ecx
  1003.         mov     ebx, edx
  1004.         jnz     .loop1
  1005.  
  1006.         adc     ebx, 0
  1007.         mov     eax, [bsize-ebp+esp]
  1008.         mov     [edi], ebx              ; most significant dword of the product
  1009.         add     edi, 4                  ; increment dst
  1010.         dec     eax
  1011.         jz      .skip
  1012.         mov     [counter-ebp+esp], eax  ; set index i to bsize
  1013.  
  1014.   .outer:
  1015.         add     ebp, 4                  ; make ebp point to next b dword
  1016.         mov     ecx, [asize-ebp+esp]
  1017.         neg     ecx
  1018.         xor     ebx, ebx
  1019.  
  1020.   .loop2:
  1021.         adc     ebx, 0
  1022.         mov     eax, [esi + ecx * 4]
  1023.         mul     dword[ebp]
  1024.         add     eax, ebx
  1025.         mov     ebx, [edi + ecx * 4]
  1026.         adc     edx, 0
  1027.         add     ebx, eax
  1028.         mov     [edi + ecx * 4], ebx
  1029.         inc     ecx
  1030.         mov     ebx, edx
  1031.         jnz     .loop2
  1032.  
  1033.         adc     ebx, 0
  1034.  
  1035.         mov     [edi], ebx
  1036.         add     edi, 4
  1037.         mov     eax, [counter-ebp+esp]
  1038.         dec     eax
  1039.         mov     [counter-ebp+esp], eax
  1040.         jnz     .outer
  1041.  
  1042.   .skip:
  1043. ; restore esp, ebp
  1044.         mov     ebp, esp
  1045.         mov     esp, [esp_]
  1046.  
  1047.         ret
  1048.  
  1049.   .done:
  1050.         mov     [edi+4], edx    ; store to dst[1]
  1051. ; restore esp, ebp
  1052.         mov     ebp, esp
  1053.         mov     esp, [esp_]
  1054.  
  1055.         ret
  1056.  
  1057.   .ovf:
  1058.         int3
  1059.  
  1060.   .zero:
  1061.         mov     eax, [dst]
  1062.         mov     dword[eax], 0
  1063.  
  1064.         ret
  1065.  
  1066.   .adjust_needed:
  1067. ; align to 4 byte boundary
  1068.         add     ecx, 3
  1069.         and     ecx, not 3
  1070.         add     ebx, 3
  1071.         and     ebx, not 3
  1072. ; adjust both mpints
  1073.         stdcall mpint_grow, esi, ecx
  1074.         stdcall mpint_grow, edx, ebx
  1075.         jmp     .length_ok
  1076.  
  1077.   .swap_a_b:
  1078.         mov     eax, esi
  1079.         mov     esi, edx
  1080.         mov     edx, eax
  1081.  
  1082.         mov     eax, ebx
  1083.         mov     ebx, ecx
  1084.         mov     ecx, eax
  1085.         jmp     .conditions_ok
  1086.  
  1087. endp
  1088.  
  1089. ;;===========================================================================;;
  1090. proc mpint_mod uses eax ebx ecx, dst, m ;////////////////////////////////////;;
  1091. ;;---------------------------------------------------------------------------;;
  1092. ;? Find the modulo (remainder after division) of dst by mod.                 ;;
  1093. ;;---------------------------------------------------------------------------;;
  1094. ;> dst = pointer to little endian MPINT                                      ;;
  1095. ;> mod = pointer to little endian MPINT                                      ;;
  1096. ;;---------------------------------------------------------------------------;;
  1097. ;< dst = dst MOD m                                                           ;;
  1098. ;;===========================================================================;;
  1099.  
  1100. locals
  1101.         mpint_tmp       rb MPINT_MAX_LEN+4
  1102. endl
  1103.  
  1104.         DEBUGF  1, "mpint_mod(0x%x, 0x%x)\n", [dst], [m]
  1105.  
  1106.         stdcall mpint_cmp, [m], [dst]
  1107.         ja      .done                           ; if mod > dst, dst = dst
  1108.         je      .zero                           ; if mod == dst, dst = 0
  1109.  
  1110.         ; left shift mod until the high order bits of mod and dst are aligned
  1111.  
  1112.         stdcall mpint_bits, [dst]
  1113.         mov     ecx, eax
  1114.         stdcall mpint_bits, [m]
  1115.         test    eax, eax
  1116.         jz      .zero                           ; if mod is zero, return
  1117.         sub     ecx, eax
  1118.         lea     ebx, [mpint_tmp]
  1119.         stdcall mpint_shlmov, ebx, [m], ecx
  1120.         inc     ecx
  1121.  
  1122.         ; For every bit in dst (starting from the high order bit):
  1123.   .bitloop:
  1124.         stdcall mpint_cmp, [dst], ebx           ; If dst > mpint_tmp
  1125.         jb      @f
  1126.         stdcall mpint_sub, [dst], ebx           ; dst = dst - mpint_tmp
  1127.   @@:
  1128.         dec     ecx
  1129.         jz      .done
  1130.  
  1131.         stdcall mpint_shr1, ebx                 ; mpint = mpint >> 1
  1132.         jmp     .bitloop
  1133.  
  1134.   .done:
  1135. ; adjust size of dst so it is no larger than mod
  1136.         mov     ebx, [dst]
  1137.         mov     ecx, [ebx]      ; current size
  1138.         mov     eax, [m]
  1139.         mov     eax, [eax]      ; size of mod
  1140.         cmp     ecx, eax
  1141.         jb      .ret
  1142.         mov     [ebx], eax
  1143.   .ret:
  1144.         ret
  1145.  
  1146.   .zero:
  1147.         mov     ebx, [dst]
  1148.         mov     dword[ebx], 0
  1149.         ret
  1150.  
  1151. endp
  1152.  
  1153. ;;===========================================================================;;
  1154. proc mpint_modexp uses edi eax ebx ecx edx, dest, b, e, m ;//////////////////;;
  1155. ;;---------------------------------------------------------------------------;;
  1156. ;? Find the modulo (remainder after division) of dst by mod.                 ;;
  1157. ;;---------------------------------------------------------------------------;;
  1158. ;> dst = pointer to buffer for little endian MPINT                           ;;
  1159. ;> base = pointer to little endian MPINT                                     ;;
  1160. ;> exp = pointer to little endian MPINT                                      ;;
  1161. ;> mod = pointer to little endian MPINT                                      ;;
  1162. ;;---------------------------------------------------------------------------;;
  1163. ;< dst = b ** e MOD m                                                        ;;
  1164. ;;===========================================================================;;
  1165.  
  1166. locals
  1167.         dst1            dd ?
  1168.         dst2            dd ?
  1169.         tmp             rb MPINT_MAX_LEN+4
  1170. endl
  1171.  
  1172.         DEBUGF  1, "mpint_modexp(0x%x, 0x%x, 0x%x, 0x%x)\n", [dest], [b], [e], [m]
  1173.  
  1174.         ; If mod is zero, return
  1175.         stdcall mpint_bytes, [m]
  1176.         test    eax, eax
  1177.         jz      .mod_zero
  1178.         test    eax, 3
  1179.         jnz     .grow_mod
  1180.   .modsize_ok:
  1181.  
  1182.         ; Find highest order byte in exponent
  1183.         stdcall mpint_bytes, [e]
  1184.         test    eax, eax
  1185.         jz      .exp_zero
  1186.         mov     ecx, eax
  1187.         mov     edi, [e]
  1188.         lea     edi, [edi + 4 + ecx - 1]
  1189.  
  1190.         ; Set up temp variables
  1191.         lea     eax, [tmp]
  1192.         mov     edx, [dest]
  1193.         mov     [dst1], eax
  1194.         mov     [dst2], edx
  1195.  
  1196.         ; Find the highest order bit in this byte
  1197.         mov     al, [edi]
  1198.         test    al, al
  1199.         jz      .invalid
  1200.         mov     bl, 9
  1201.   @@:
  1202.         dec     bl
  1203.         shl     al, 1
  1204.         jnc     @r
  1205.  
  1206.         ; Initialise result to base, to take care of the highest order bit
  1207.         stdcall mpint_mov, [dst1], [b]
  1208.         dec     bl
  1209.         jz      .next_byte
  1210.   .bit_loop:
  1211.         ; For each bit, square result
  1212.         stdcall mpint_mul, [dst2], [dst1], [dst1]
  1213.         stdcall mpint_mod, [dst2], [m]
  1214.  
  1215.         ; If the bit is set, multiply result by the base
  1216.         shl     al, 1
  1217.         jnc     .bit_zero
  1218.         stdcall mpint_mul, [dst1], [b], [dst2]
  1219.         stdcall mpint_mod, [dst1], [m]
  1220.         dec     bl
  1221.         jnz     .bit_loop
  1222.         jmp     .next_byte
  1223.  
  1224.   .bit_zero:
  1225.         mov     edx, [dst1]
  1226.         mov     esi, [dst2]
  1227.         mov     [dst2], edx
  1228.         mov     [dst1], esi
  1229.         dec     bl
  1230.         jnz     .bit_loop
  1231.  
  1232.   .next_byte:
  1233.         dec     ecx
  1234.         jz      .done
  1235.         dec     edi
  1236.         mov     al, [edi]
  1237.         mov     bl, 8
  1238.         jmp     .bit_loop
  1239.   .done:
  1240.         mov     edx, [dest]
  1241.         cmp     edx, [dst1]
  1242.         je      @f
  1243.         stdcall mpint_mov, [dest], [dst1]
  1244.   @@:
  1245.  
  1246.         ret
  1247.  
  1248.   .mod_zero:
  1249.         DEBUGF  3, "modexp with modulo 0\n"
  1250.         ; if mod is zero, result = 0
  1251.         mov     eax, [dest]
  1252.         mov     dword[eax], 0
  1253.         ret
  1254.  
  1255.   .exp_zero:
  1256.         DEBUGF  3, "modexp with exponent 0\n"
  1257.         ; if exponent is zero, result = 1
  1258.         mov     eax, [dest]
  1259.         mov     dword[eax], 1
  1260.         mov     byte[eax+4], 1
  1261.         ret
  1262.  
  1263.   .invalid:
  1264.         DEBUGF  3, "modexp: Invalid input!\n"
  1265.         ret
  1266.  
  1267.   .grow_mod:
  1268.         add     eax, 3
  1269.         and     eax, not 3
  1270.         stdcall mpint_grow, [m], eax
  1271.         jmp     .modsize_ok
  1272.  
  1273. endp
  1274.  
  1275.  
  1276.