曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 5685|回复: 3
打印 上一主题 下一主题

减法及指令

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2010-1-20 23:42:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1、SUB 指令
指令格式:
sub source, destination
源操作数和目标操作数可以是8位、16位或者32位寄存器或存储在内存中的值。注意,两者不能同为内存位置!源值也可以是立即数。

测试程序
.section .data
data:
    .int 40
.section .text
.global _start
_start:
    nop
    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
    subl %eax, data
    movl $1, %eax
    movl $0, %ebx
    int $0x80
调试器中可以看到:
(gdb) print $eax
$1 = -30
(gdb) x/d &data
0x80490b4 <data>:    70

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
沙发
 楼主| 发表于 2010-1-21 00:13:24 | 只看该作者

减法操作中的进位和溢出

和 ADD 指令类似,执行减法操作后,SUB 指令会修改 EFLAGS 寄存器的几个位。但是,在减法操作中,进位溢出的概念是不同的!

对于无符号整数,一个小数减去一个大数,此时进位标志被设置:
.section .text
.global _start
_start:
    nop
    movl $5, %eax
    movl $2, %ebx
    subl %eax, %ebx   
    jc under
    movl $1, %eax
    int $0x80
under:
    movl $0, %ebx
    movl $1, %eax
    int $0x80
运行及输出:
$ ./sub2
$ echo $?
0
由输出结果可见,程序发生跳转,进位标志位被设置了。从调试器里可以看到:
(gdb) print $ebx
$1 = -3
EBX 寄存器里包含的值是正确的,尽管它被“认为是”无符号整数,但处理器不知道所使用的是无符号整数还是带符号的数,这由程序负责确定值是否超出了无符号(或者带符号)值的范围。

上面的情况是使用进位标志确定无符号整数减法是否产生了负数(如果被置位则产生负数,否则是整数)。

但是,在带符号整数的减法中,进位标志是没有用的!因为结果常常可能是负值。替换的做法是,必须依靠溢出标志来判断是否到达了数据长度的界限。下面是测试代码:
.section .data
output:
    .asciz "The result is %d\\n"

.section .text
.global _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
编译连接与运行输出:
$ as -gstabs -o sub3.o sub3.s
$ ld -dynamic-linker /lib/ld-linux.so.2 -lc -o sub3 sub3.o
$ ./sub3
The result is 0
由输出结果可知,发生了 JO 指令的跳转,这是因为 EBX 减去 EAX 后生成了一个超过 32 位范围的值。
如果把 EAX 寄存器中的值改为:
movl $-1259230143, %eax
这时再运行程序,看到:
$ ./sub3
The result is -331646791
这一次没有发生溢出。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
板凳
 楼主| 发表于 2010-1-22 00:22:32 | 只看该作者

SBB 指令

SBB 指令在多字节减法操作中利用进位和溢出标志实现跨越数据边界的借位特性。指令格式如下:
sbb source, destination
其中,进位位被添加到 source 值,然后从 destination 值中减去 source 值得到结果,结果存储在 destination 位置中。

SBB 指令最常用于从前一条 SUB 指令 “挖出” 进位标志。当前一条 SUB 指令被执行并造成进位时,进位位被 SBB 指令 “借走” ,以便继续下一个数据的减法。

下面程序进行测试
.section .data
data1:
    .quad 7252051615
data2:
    .quad 5732348928
output:
    .asciz "The result is %qd\\n"

.section .text
.global _start
_start:
    nop
    movl data1, %ebx
    movl data1+4, %eax
    movl data2, %edx
    movl data2+4, %ecx
   
    subl %ebx, %edx
    sbbl %eax, %ecx
    pushl %ecx
    pushl %edx
    pushl $output
    call printf
   
    add $12, %esp
    pushl $0
    call exit
运行输出
$ ./sbb
The result is -1519702687
在 pushl %ecx 处下断,然后运行至此,可以从调试器中观察到:
eax            0x1    1
ecx            0xffffffff    -1
edx            0xa56b2d61    -1519702687
ebx            0xb041869f    -1337882977
由上可见,在执行指令 subl %ebx, %edx 时,由于 edx 中的内容比 ebx 中的小,所以从 ecx 中借了 1 位(CF=1),使 %ecx 中的值变为 0,然后 ecx - eax ,从而 ecx 中的值变为 0xffffffff (-1)。所以,在调用 printf 时用 %qd 打印出 64 位带符号整数时,由于 ecx:edx 组成的数为 ffffffffa56b2d61 ,这相当于带符号数的扩展(扩展成 64 位),所以这表示的是一个负数,换算成 10 进制即为: -1519702687 。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
地板
 楼主| 发表于 2010-1-24 17:35:35 | 只看该作者

递增与递减

在汇编程序里,经常会遇到遍历数组以处理每个元素的情况,这时候比较有用的两个指令是递增 INC 和递减 DEC 。

INC 和 DEC 指令不会影响进位标志,所以可以递增或者递减计数器的值,并且不会影响程序循环中涉及进位标志的任何其他加法或者减法的操作。

两个指令的语法格式如下:
dec destination
inc destination
其中,destination 可以是 8 位,16位或者 32位寄存器,或者是内存中的值。

注意,INC 和 DEC 指令主要用于无符号整数。如果从 0 - 1 寄存器变成 0xFFFFFFFF,看上去像是 -1 的数,但实际上是被当作无符号整数 4 294 967 295 处理。如果把他们用于带符号整数,那么就要小心符号的变化。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-4 01:56 , Processed in 0.084602 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表