Advertisement

汇编总结:无符号除法,有符号除法,取余,无符号乘法,有符号乘法指令

阅读量:

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

本文分为3个模块。

  1. 示例 ---该指令的示例

  2. 解释 ---为指令不好理解的地方

  3. 练习 ---为了更熟悉该指令

1.1 有符号除法指令及取余example:

在c语言里要完成 8 / 2的汇编指令如下:

在c语言里要完成 8 % 2的汇编指令如下:

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movl $8, %eax   #被除数是%edx:%eax 是这两个寄存器拼起来的%eax存放低位%edx存储高位
    
     movl %eax, %edx
    
     shrl $31, %edx  #根据符号位填充%edx寄存器
    
     movl $2, %ecx
    
     idivl %ecx      #%eax保存商 %edx保存余数

上面的也是4个字节除法及取余运算示列,跟据所使用的类型不同(c语言有同类概念)还有如下变种:

1个字节的除法及取余运算示例如下:

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movw $8, %ax #被除数是%ax寄存器
    
     movb $2, %cl 
    
     idivb %cl    #除数可以是通用寄存器,这里的demo是%cl。%al存放商。%ah存放余数

2个字节的除法及取余运算示例如下:

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movw $8, %ax   #被除数是%dx:%ax 是这两个寄存器拼起来的%ax存放低位%dx存储高位
    
     movw %ax, %dx
    
     shrw $15, %dx
    
     movw $2, %cx
    
     idivw %cx      #%ax保存商 %dx保存余数

8个字节的除法及取余运算示例如下:

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movq $8, %rax  #被除数是%rdx:%rax 是这两个寄存器拼起来的%rax存放低位%rdx存储高位
    
     movq %rax, %rdx
    
     shrq $63, %rdx
    
     movq $5, %rcx
    
     idivq %rcx     #%rax保存商 %rdx保存余数

1.2 下面的除数指令里为什么用右移指令操作%edx寄存器?

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movl $8, %eax   #1
    
     movl %eax, %edx #2
    
     shrl $31, %edx  #3
    
     movl $2, %ecx   #4
    
     idivl %ecx      #5

上面的例子第2+3行其实根据被除数%eax里的符号将%edx设置为全零或者全一。这样一来,两个寄存器就拼成了64位的寄存器(%edx:%eax--被除数)

1.3 无符号除法指令用法

无符号除法指令和有符号除法指令差不多,只要把idiv换成div就行。

2.1 有符号乘法指令example:

在c语言里要完成 4 * 3的汇编指令如下:

根据imul的操作数的个数不同可分为两种,一种是两个操作数。这时执行imull指令,结果存在第二个操作数里。另一种是一个操作数。这时候的结果保存到%edx:%eax两个寄存器。

2.1.1 双操作数imul指令example:

c语言要完成a = 3; a *= 4;可用如下指令完成同等效果

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movw $3, %cx
    
     imulw $4, %cx
    
  
    
     movl  $3, %ecx
    
     imull $4, %ecx
    
  
    
     movq  $3, %rcx
    
     imulq $4, %rcx

注意:双操作数imul指令没有imulb版本的

2.1.2 单操作数imul示例如下:

1个字节的乘法运算示例如下:

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movb $0x7f, %al #被乘数需放至%al寄存器中
    
     movb $4, %cl    #乘数可为通用寄存器或者内存地址
    
     imulb %cl       #ah存储高位 al存储低位,可用ax引用结果

2个字节的乘法运算示例如下:

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movw $0x7fff, %ax #被乘数需放至%ax寄存器中
    
     movw $4, %cx      #乘数可为通用寄存器或者内存地址
    
     imulw %cx         #dx存储高位 ax存储低位。结果保存在dx:ax两个寄存器中

4个字节的乘法运算示例如下:

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movl $0x7fffffff, %eax #被乘数需放至%eax寄存器中
    
     movl $4, %ecx          #乘数可为通用寄存器或者内存地址
    
     imull %ecx             #edx存储高位 eax存储低位 结果保存在edx:eax两个寄存器中

8个字节的乘法运算示例如下:

复制代码
 .section .text

    
 .global _start
    
 _start:
    
     movq $0x7fffffffffffffff, %rax #被乘数需放至%rax寄存器中
    
     movq $4, %rcx                  #乘数可为通用寄存器或者内存地址
    
     imulq %rcx                     #rdx存储高位 rax存储低位 结果保存在rdx:rax两个寄存器中

2.2 无符号乘法指令用法

无符号乘法和有符号乘法指令差不多,只要把imul换成mul就行。

3.1 编写函数,实现字符串转数字。函数原型如下:

复制代码
    int str2int(const char *str, int base);

base范围为2-36,可把str里的数组解释为2-36进制的数

需要提供一定的进制猜测能力。当base为0时打开进制猜测能力。str以0b或0B开头,默认为2进制。以0开头默认为8进制。以0x或0X开头,默认为16进制,其余默认为10进制。

str以'-'开头。base 为10时,认为是10进制的负数。其余进制?

汇编code:

复制代码
 .equ SIGN_SIZE, 4

    
 .equ SIGN, -4
    
  
    
 #str2int(const char *str, int base)
    
 .section .text
    
 .global str2int
    
 .type str2int, @function
    
 str2int:
    
     pushq %rbp
    
     movq %rsp, %rbp
    
  
    
     subq $SIGN_SIZE, %rsp
    
     movl $1, SIGN(%rbp)
    
  
    
     #初始化返回值
    
     movl $0, %eax
    
     #判断base的值
    
     cmpl $0, %esi
    
     je str2int_init_base
    
     cmpl $2, %esi
    
     jl str2int_exit
    
     cmpl $36, %esi
    
     jg str2int_exit
    
     jmp str2int_skip_space
    
  
    
 str2int_init_base:
    
     movl $10, %r9d
    
  
    
 str2int_skip_space:
    
  
    
     movb (%rdi), %r8b
    
     cmpb $0, %r8b
    
     je str2int_exit
    
  
    
     cmpb $' ', %r8b
    
     je str2int_skip_space_next
    
     cmpb $'\r', %r8b
    
     je str2int_skip_space_next
    
     cmpb $'\n', %r8b
    
     je str2int_skip_space_next
    
     cmpb $'\t', %r8b
    
     je str2int_skip_space_next
    
  
    
     jmp str2int_sign
    
 str2int_skip_space_next:
    
     incq %rdi
    
     jmp str2int_skip_space
    
  
    
 str2int_sign:
    
     cmpb $'-', %r8b
    
     jne str2int_guess_hex
    
     incq %rdi
    
     movl $-1, SIGN(%rbp)
    
  
    
 str2int_guess_hex:
    
     cmpb $'0', %r8b
    
     jne str2int_check_base
    
     movl $8, %r9d
    
     incq %rdi
    
     movb (%rdi), %r8b
    
     cmpb $0, %r8b
    
     je str2int_exit
    
   64.     cmpb $'b', %r8b
    
     je str2int_guess_hex_2
    
     cmpb $'B', %r8b
    
     je str2int_guess_hex_2
    
     cmpb $'x', %r8b
    
     je str2int_guess_hex_16
    
     cmpb $'X', %r8b
    
     je str2int_guess_hex_16
    
     jmp str2int_check_base
    
   74. str2int_guess_hex_2:
    
     movl $2, %r9d
    
     incq %rdi
    
     jmp str2int_check_base
    
   79. str2int_guess_hex_16:
    
     movl $16, %r9d
    
     incq %rdi
    
   83. str2int_check_base:
    
     cmpl $0, %esi
    
     jne str2int_cal
    
     movl %r9d, %esi
    
   88.   89. str2int_cal:
    
     xorl %r9d, %r9d
    
     movb (%rdi), %r9b
    
     cmpb $0, %r9b
    
     je str2int_exit
    
   95.     orb $0x20, %r9b
    
   97.     #if (c >= 'a' && c <= 'z')
    
     cmpb $'a', %r9b
    
     jl str2int_cal_10
    
     cmpb $'z', %r9b
    
     jg str2int_exit
    
     subb $'a', %r9b
    
     addb $10, %r9b
    
     jmp str2int_cal_next
    
  
    
 str2int_cal_10:
    
     #if (c >= '0' && c <= '9')
    
     cmpb $'0', %r9b
    
     jl str2int_exit
    
     cmpb $'9', %r9b
    
     jg str2int_exit
    
     subb $'0', %r9b
    
  
    
 str2int_cal_next:
    
     imull %esi, %eax
    
     addl %r9d, %eax
    
     incq %rdi
    
     jmp str2int_cal
    
  
    
 str2int_exit:
    
     imull SIGN(%rbp), %eax
    
     movq %rbp, %rsp
    
     popq %rbp
    
     ret

3.2 编写函数,实现数字转字符串,函数原型如下:

复制代码
    char *int2str(int val, char *str, int base);

base范围为2-36,可把val里的数字解释为2-36进制的字符串,并存入str中。

val是输入。str是输出。

汇编code:

复制代码

转载于:https://my.oschina.net/guonaihong/blog/513984

全部评论 (0)

还没有任何评论哟~