汇编学习----基本数学功能

 一、整数运算

       汇编语言程序中执行数学操作的基本过程是整数运算。

   1、加法

        1)ADD指令

       ADD指令用于把两个整数相加,ADD指令的格式如下:

add source,destination

/*其中source可以是立即值、内存位置或者寄存器。destination参数可以是寄存器或者内存位置中存储
  的值(但是不能同时使用内存位置作为源和目标)。加法的结果存放在目标位置*/
.section .data
  data:
    .int 40

.section .text
  .globl _start

_start:
  movl $0, %eax
  movl $0, %ebx
  movl $0, %ecx
  movb $20, %al
  addb $10, %al
  movsx %al, %ebx
  addw %cx, %bx
  movsx %bx, %ebx
  movl $100, %edx
  addl %edx, %edx
  addl data, %eax
  addl %eax, data
  movl $1, %eax
  movl $0, %ebx
  int $0x80
  
.section .data
  data:
    .int -40

.section .text
  .globl _start

_start:
  movl $-10, %eax
  movl $-200, %ebx
  movl $80, %ecx
  addl data, %eax
  addl %ecx, %eax
  addl %ebx, %eax
  addl %eax, data
  addl $210, data
  movl $1, %eax
  movl $0, %ebx
  int $0x80

         2)检测进位或者溢出情况

         对于无符号整数,当二进制加法造成进位情况时(结果大于允许的最大值),进位标志

(carry flag)就会被设置为1.对于带符号整数,当出现溢出情况时(结果值小于允许的最小负

值或大于允许的最大正值),溢出标志(overflow flag)就会被设置为1。

          下面是无符号数加法进位的例子:

.section .text
  .globl _start

_start:
  movl $0, %ebx
  movb $190, %bl
  movb $100, %al
  addb %al, %bl
  jc over
  movl $1, %eax
  
  int $0x80

over:
  movl $1, %eax
  movl $0, %ebx
  int $0x80

            下面是有符号数加法进位的例子:

.section .data
  output:
    .asciz "The result is %d\n"

.section .text
  .globl _start

_start:
  movl $-1590876934, %ebx
  movl $-1259230143, %eax
  addl %eax, %ebx
  jo over
  pushl %ebx
  pushl $output
  call printf
  add $8, %esp
  pushl $0
  call exit

over:
  pushl $0
  pushl $output
  call printf
  add $8, %esp
  pushl $0
  call exit

         3)ADC指令

         如果必须处理非常大,不能存放在双字数据长度(ADD指令可以使用的最大长度)中的带

符号或这无符号整数,可以把值分割为多个双字数据元素,并且对每个元素执行独立的加法操

作。Intel提供的方案如下:

     为了执行多组字节的加法操作,可以把多个ADC指令链接到一起。

    ADC指令的格式如下:

adc source, destination
 
/*其中source可以是立即数或者8位、16位或者32位寄存器或内存位置,destination可以是
  8位、16位或者32位寄存器或内存位置值(和ADD指令类似,source和destination不能
  同时是内存位置)*/
.section .data
  data1:
    .quad 7252051615
  data2:
    .quad 5732348928
  output:
    .asciz "The result is %qd\n"

.section .text
  .globl _start

_start:
  movl data1, %ebx
  movl data1+4, %eax
  movl data2, %edx
  movl data2+4, %ecx
  addl %ebx, %edx
  adcl %eax, %ecx
  pushl %ecx
  pushl %edx
  push $output
  call printf
  addl $12, %esp
  pushl $0
  call exit

   2、减法    

   1)SUB指令

    SUB指令可以用于无符号和带符号整数。SUB指令的格式如下:

sub source, destination

/*其中从destination的值中减去source的值,结果存储在destination操作数的位置。*/
  

    下面程序演示了SUB指令:

.section .data
  data:
    .int 40

.section .text
  .globl _start

_start:
  movl $0, %eax
  movl $0, %ebx
  movl $0, %ecx
  movb $20, %al
  subb $10, %al
  movsx %al, %eax
  movw $100, %cx
  subw %cx, %bx
  movsx %bx, %ebx
  movl $100, %edx
  subl %eax, %edx
  subl data, %eax
  movl $1, %eax
  movl $0, %ebx
  int $0x80

    与SUB指令密切相关的是NEG指令。它生成值的补码。  

    2)减法操作中的进位和溢出

     对于无符号整数。subtest2.s程序演示了这个问题:

.section .text
  .globl _start
  
_start:
  movl $5, %eax
  movl $2, %ebx
  subl %eax, %ebx
  jc under
  movl $1, %eax
  int $0x80

under:
  movl $1, %eax
  movl $0, %ebx
  int $0x80

     使用进位标志确定无符号整数的减法产生负数结果的情况。

      下面是带符号减法的例子:

.section .data
  output:
    .asciz "The result is %d\n"

.section .text
  .globl _start

_start:
  movl $-1590876934, %ebx
  movl $1259230143, %eax
  subl %eax, %ebx
  jo over
  pushl %ebx
  pushl $output
  call printf
  add $8, %esp
  pushl $0
  call exit

over:
  pushl $0
  pushl $output
  call printf
  add $8, %esp
  pushl $0
  call exit

      带符号减法超出范围则溢出标志被设为1。 

    3)SBB指令

      SBB指令在多字节减法操作中利用进位和溢出标志实现跨越数据边界的借位特性。

      格式如下:

sbb source, destination

/*其中进位位被添加到source值,然后从destination值中减去source值得到结果。*/

      

      

.section .data
  data1:
    .quad 7252051515
  data2:
    .quad 5732348928
  output:
    .asciz "The result is %qd\n"

.section .text
  .globl _start
 
_start:
  movl data1, %ebx
  movl data1+4, %eax
  movl data2, %edx
  movl data2+4, %ecx
  subl %ebx, %edx
  sbbl %eax, %ecx
  pushl %ecx
  pushl %edx
  push $output
  call printf
  add $12, %esp
  pushl $0
  call exit

   3、递增和递减

    INC和DEC指令用于对无符号整数值进行递增(INC)和递减(DEC)。这两个指令不会影响

进位标志,所以可以递增或递减计数器的值。

INC  destination
DEC  destination

/*其中destination可以是8位、16位或者32位寄存器,或者内存中的值 */

   4、乘法

   1)使用MUL进行无符号整数乘法

    MUL指令用于两个无符号整数乘法。MUL指令的格式如下:

mul source

/*其中source可以是8位、16位或者32位寄存器或内存值。
  目标位置总是使用EAX寄存器的某种形式,这取决于源操作数的长度。
  MUL指令的目标位置必须是源操作数的两倍长度。*/

     不过,当和16位源操作数相乘时,EAX寄存器不被用于保存在32位结果。为了向下兼容老式

的处理器。Intel使用DX:AX寄存器对保存32位乘法结果值。对于32位源值,目标位置使用64位

EDX:EAX寄存器对。

   

   2)MUL指令范例   

.section .data
  data1:
    .int 315814
  data2:
  .int 165432
  result:
    .quad 0
  output:
    .asciz "The result is %qd\n"

.section .text
  .globl _start

_start:
  movl data1, %eax
  mull data2
  movl %eax, result
  movl %edx, result+4
  pushl %edx
  pushl %eax
  pushl $output
  call printf
  add $12, %esp
  pushl $0
  call exit

   3)使用IMUL进行带符号整数乘法

    MUL指令只能用于无符号整数,而IMUL指令可以用于带符号和无符号整数。

    IMUL有三种指令格式:

imul source

/*source操作数可以是8位、16位或者32位寄存器或内存中的值,它位于AL、AX或者EAX寄存器
 (取决于源操作数的长度)中的隐含操作数相乘。然后,结果被存放到AX寄存器、DX:AX寄存器对
  或者EDX:EAX寄存器对中*/

    IMUL指令的第二种格式允许指定EAX寄存器之外的目标操作数:

imul source, destination

/*其中source可以是16位或者32位寄存器或内存中的值,destination必须是16位或者32
  位通用寄存器。这种格式允许把乘法操作的结果存放到哪个位置*/

    这种格式的缺陷在于乘法操作的结果被限制为单一目标寄存器的长度。使用这种格式时必须非

常小心,不要溢出目标寄存器。

    IMUL指令的第三种格式允许指定3个操作数:

imul multiplier, source, destination

/*其中mutiplier是一个立即数,source是16位或者32位寄存器或内存中的值,destination必须是通用
  寄存器。这种格式允许执行一个值(source)和一个带符号整数(mutiplier)的快速乘法操作,把结果
  存储到通用寄存器(destination)中。*/

   4)IMUL指令范例

.section .data
  value1:
    .int 10
  value2:
    .int -35
  value3:
    .int 400

.section .text
  .globl _start

_start:
  movl value1, %ebx
  movl value2, %ecx
  imull %ebx, %ecx
  movl value3, %edx
  imull $2, %edx, %eax
  movl $1, %eax
  movl $0, %ebx
  int $0x80 

   5)检查溢出

.section .text
  .globl _start

_start:
  movw $680, %ax
  movw $100, %cx
  imulw %cx
  jo over
  movl $1, %eax
  movl $0, %ebx
  int $0x80

over:
  movl $1, %eax
  movl $1, %ebx
  int $0x80

   5、除法

    1)无符号除法

     DIV指令用于无符号整数的除法操作。格式如下:

div divisor

/*其divisor(除法)是隐含的被除数要除以的值,它可以是8位、16位或者32位寄存器或内存中的值。
  在执行DIV指令之前,被除数必须已经存储到了AX寄存器(对于16位值)、DX:AX寄存器对(对于
  32位值)或者EDX:EAX寄存器对(对于64位值)。*/

      

     这就是说,当除法操作完成时,会丢失被除数,所以要确保这不是这个值的唯一拷贝。

.section .data
  dividend:
    .quad 8335
  divisor:
    .int 25
  quotient:
    .int 0
  remainder:
    .int 0
  output:
    .asciz "The quotient is %d, and the remainder is %d\n"

.section .text
  .globl _start

_start:
  movl dividend, %eax
  movl dividend+4, %edx
  divl divisor
  movl %eax, quotient
  movl %edx, remainder
  pushl remainder
  pushl quotient
  pushl $output
  call printf
  add $12, %esp
  pushl $0
  call exit

    2)带符号除法

    IDIV用于带符号除法。只有一种格式:

idiv divisor

/*其中divisor可以是8位、16位或者32位寄存器或内存中的值*/

     对于带符号整数的除法,余数的符号总是与被除数的符号相同。

     3)检查除法错误

     除以0或者商(或者余数)溢出目标寄存器。

 二、整数运算

     乘法和除法是处理器上最为耗费时间的两种指令。但是,可以运用一些技巧帮助加

快应用程序的执行速度。移位指令提供了基于2的乘方的乘法和除法操作的快速和容易的方

式。

     1、移位乘法

      为了使整数乘以2的乘方,必须把值向左移位。可以使用下面两个命令——SAL(向左算术移

位)和SHL(向左逻辑移位)。它们有3种不同的格式:

sal destination
sal %al, destination
sal shifter, destination

/*第一种格式把destination的值向左移1位,这等同于使值乘以2。
  第二种格式把destination的值向左移动CL寄存器中指定的位数。
  第三种格式把destination的值向左移动shifter值指定的位数。
*/

      

     

.section .data
  value1:
    .int 25
  
.section .text
  .globl _start

_start:
  movl $10, %ebx
  sall %ebx
  movb $2, %cl
  sall %cl, %ebx
  sall $2, %ebx
  sall value1
  sall $2, value1
  movl $1, %eax
  movl $0, %ebx
  int $0x80

     2、移位除法

      SHR指令清空移位造成的空位,所以它只能用于对无符号整数进行移位操作。SAR指令根据

整数的符号位,要么清空,要么设置移位造成的空位。对于负数,空位被设置为1,但是对于整数

它们被设置为0。

     3、循环移位

     循环移位指令执行的功能和移位指令一样,只不过溢出位被存放回值的另一端,而不是被丢

弃。

 三、十进制运算

     1、不打包BCD的运算

      用于把二进制运算结果转换为不打包BCD格式的指令有4条:

  • AAA:调整加法操作的结果
  • AAS:调整减法操作的结果
  • AAM:调整乘法操作的结果
  • AAD:准备除法操作的被除数

      这些指令必须和一般的无符号整数指令ADD、ADC、SUB、SBB、MUL和DIV组

合在一起使用。AAA、AAS和AAM指令在它们各自的操作之后使用,把二进制结果

转换为不打包BCD格式。

       这些指令都使用一个隐含的操作数——AL寄存器。AAA、AAS和AAM指令假设

前一个操作的结果放在AL寄存器中,并且把这个值转换为不打包BCD格式。AAD指

令假设被除数以不打包BCD格式存放在AX寄存器中,并且把它转换为DIV指令要处理

的二进制格式。结果是正确的一个不打包BCD值,AL寄存器中的商和AH寄存器中的

余数。

     

.section .data
  value1:
    .byte 0x05, 0x02, 0x01, 0x08, 0x02
  value2:
    .byte 0x03, 0x03, 0x09, 0x02, 0x05

.section .bss
  .lcomm sum, 6

.section .text
  .globl _start

_start:
  xor %edi, %edi
  movl $5, %ecx
  clc

loop1:
  movb value1( , %edi, 1 ), %al
  adcb value2( , %edi, 1 ), %al
  aaa
  movb %al, sum( , %edi, 1 )
  inc %edi
  loop loop1
  adcb $0, sum( , %edi, 4 )
  movl $1, %eax
  movl $0, %ebx
  int $0x80 

     2、打包BCD的运算

     处理打包BCD值时,可用的指令只有2条:

  •  DAA:调整ADD或者ADC指令的结果
  •  DAS:调整SUB或者SBB指令的结果

       

      

.section .data
  value1:
    .byte 0x25, 0x81, 0x02
  value2:
    .byte 0x33, 0x29, 0x05

.section .bss
  .lcomm result, 4

.section .text
  .globl _start

_start:
  xor %edi, %edi
  movl $3, %ecx
  
loop1:
  movb value2( , %edi, 1 ), %al
  sbbb value1( , %edi, 1 ), %al
  das
  movb %al, result( , %edi, 1 )
  inc %edi
  loop loop1
  sbbb $0, result( , %edi, 4 )
  movl $1, %eax
  movl $0, %ebx
  int $0x80

 四、逻辑操作

    1、布尔逻辑

     布尔操作如下:

  • AND
  • NOT
  • OR
  • XOR

      AND、OR和XOR指令使用相同的格式:

and source, destination

/*其中source可以是8位、16位或者32位立即值,寄存器或内存中的值,destination可以是8位
  、16位或者32位寄存器或内存中的值*/

       2、位测试

       TEST指令例子:

.section .data
  output_cpuid:
    .asciz "This processor supports the CPUID instruction\n"
  output_nocpuid:
    .asciz "This processor does not support the CPUID instruction\n"

.section .text
  .globl _start

_start:
  pushfl    #把EFLAGS寄存器的值保存到堆栈顶部
  popl %eax #把EFLAGS的值读到EAX寄存器中
  movl %eax, %edx  
  xor $0x00200000, %eax
  pushl %eax
  popfl  #把EAX的值储存在EFLAGS中
  pushfl
  popl %eax
  xor %edx, %eax
  test $0x00200000, %eax
  jnz cpuid
  pushl $output_nocpuid
  call printf
  add $4, %esp
  pushl $0
  call exit

cpuid:
  pushl $output_cpuid
  call printf
  add $4, %esp
  pushl $0
  call exit
  

 

转载于:https://my.oschina.net/u/2537915/blog/693523

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值