x86汇编乘以两个32位数

时间:2017-05-09 00:09:36

标签: assembly att

我在空闲时间学习英语汇编语言(att语法),我只是想知道如何在不使用mul命令的情况下将两个数字相乘,然后将5和2组合在一起?

2 个答案:

答案 0 :(得分:1)

除非你的CPU有问题,否则你只需使用 mul命令: - )

但是,从一般意义上讲,您只需要注意乘法是重复添加的,因此4 x 7是七个中的四个加在一起:4 + 4 + 4 + 4 + 4 + 4 + 4

所以这种野兽的简单伪代码就是:

def mul(unsigned a, unsigned b):  # line 1
    res = 0                       # line 2
    while b > 0:                  # line 3
        res = res + a             # line 4
        b = b - 1                 # line 5
    return res                    # line 6

在样本干运行中使用您的测试数据显示了它的工作原理:

Line#   a    b   res
-----  ---  ---  ---
    1    5    2    ?
    2              0
    3                 (b>0, keep going)
    4              5
    5         1
    3                 (b>0, keep going)
    4             10
    5         0
    3                 (b==0, exit loop)
    6                 (returns 10)

请注意,这仅使用无符号值,您只需稍作修改即可处理有符号值:

def mul(int a, int b):
    sign = 1
    if a < 0:
        a = -a
        sign = -sign
    if b < 0:
        b = -b
        sign = -sign

    res = 0
    while a > 0:
        res = res + b
        a = a - 1

    if sign == -1:
        res = -res
    return res 

还要记住,实际上有更多的高效进行乘法的方法,包括值的位移(最小化所需的加法),而不是简单的重复加法。

我的意思是像9999 x 9999这样的计算将使用简单方法执行大约10,000次添加。通过使用班次,您可以将其中一个数字中每个数字所需的加法数量限制为另一个数字的每个数字少于一个数字,这意味着您可以为上述计算添加大约40个数字。

当您意识到可以将9999 x 9999简化为:

时,这将有意义
     9999 x 9 -> nine additions
+   99990 x 9 -> nine additions
+  999900 x 9 -> nine additions
+ 9999000 x 9 -> nine additions
                 \____________/
                       |
                       V
                 three additions

如果您想更详细地了解转移的工作方式,维基百科有article on the topic

顺便说一句,在乘以常数后,您可以获得相当好的性能,因为您事先知道需要执行哪些操作。例如,将寄存器乘以10可以用类似的东西来完成(记住我的装配日期很长):

mul_ax_by_10: push bx      ; save registers
              shl  ax      ; ax <- orig_ax * 2
              push ax      ; save for later add
              shl  ax
              shl  ax      ; ax <- orig_ax * 8
              pop  bx      ; bx <- orig_ax * 2
              add ax, bx   ; ax <- (orig_ax * 8) + (orig_ax * 2)
                           ;    <- orig_ax * (8 + 2)
                           ;    <- orig_ax * 10
              pop  bx      ; restore saved register
              ret          ; result in ax

答案 1 :(得分:0)

由于您使用标记了问题,我认为您正在与GCC的内联汇编程序进行斗争。

在旧的十进制日里,你做了一个乘法 - 例如11 * 14 - 如下:

  1. 你取乘数(11)的最右边数字= 1,然后乘以被乘数(14)= 14

  2. 你乘以乘数= 1左边的下一个数字,乘以被乘数= 14并将结果小数左移一位= 140.你也可以移动被乘数而不是结果:1 * 140 = 140。

  3. 您将结果添加到最终结果:14 + 140 = 154。

  4. 这个算法在勇敢的新二进制世界中也是有效的:

    1. 通过将乘数向右移动= 1来取乘数(1011b)最右边的。将它乘以被乘数(1110b)。这不是真正的倍增。你只有两个选择:0 * 1110b = 0和1 * 1110b = 1110b。结果是根据位0或被乘数。将其添加到最终结果中。

    2. 如果乘数大于null,则下一位等待乘法器中最右边的位。将被乘数向左移动一位(上面步骤2中的第二个选项)并转到步骤1.

    3. GCC计划:

      #include <stdio.h>
      
      unsigned mul (unsigned multiplier, unsigned multiplicand)
      {
          unsigned r = 0;
          while (multiplier)
          {
              if (multiplier & 1) r += multiplicand;
              multiplier >>= 1;
              multiplicand <<= 1;
          }
          return r;
      }
      
      unsigned mul_asm (unsigned multiplier, unsigned multiplicand)
      {
          unsigned result = 0;
      
          asm
          (
              "movl %[multiplier], %%edx;"        // EDX = multiplier
              "movl %[multiplicand], %%ecx;"      // ECX = multiplicand
              "xorl %%eax, %%eax;"                // Result = 0
      
              "L1:;"                              // While-loop
              "shrl $1, %%edx;"                   // Get rightmost bit of the multiplier
              "jnc 1f;"                           // Skip the next line if this bit == 0
              "leal (%%ecx,%%eax), %%eax;"        // Add multiplicand to result (without changing flags)
              "1:;"                               // Local jump label
              "leal (,%%ecx,2), %%ecx;"           // Shift multiplicand left by one bit (without changing flags)
              "jnz L1;"                           // While EDX != 0 (zero flag from the shrl-line)
      
              "movl %%eax, %[result];"            // Return value
      
              : [result] "=m" (result)            // Output
              : [multiplier] "m" (multiplier),    // Input
                [multiplicand] "m" (multiplicand)
              : "%eax", "%ecx", "%edx", "cc"      // Clobbered registers & flags
          );
      
          return result;
      }
      
      int main ( void )
      {
          unsigned result, multiplier, multiplicand;
      
          multiplier = 17;
          multiplicand = 23;
      
          result = multiplier * multiplicand;
          printf ("direct:  %u * %u = %u\n", multiplier, multiplicand, result);
      
          result = mul (multiplier,multiplicand);
          printf ("mul:     %u * %u = %u\n", multiplier, multiplicand, result);
      
          result = mul_asm (multiplier,multiplicand);
          printf ("mul_asm: %u * %u = %u\n", multiplier, multiplicand, result);
      
          return 0;
      }