曲径通幽论坛

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

数据传送及相关指令

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2009-12-5 11:18:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
MOV 指令用来传送数据,基本格式是:
movx   source, destination
source 和 destination 的值可以是内存地址、存储在内存中的数据值、指令语句中定义的数据值,或者是寄存器。

movx 中的 x 用来要传送的数据元素的长度,可以是下面的字符:
      l  :  用于 32 位的长字值
      w  :  用于 16 位的字值
      b  :  用于 8 位的字节值
如:
movl  %eax,  %ebx    #32 位寄存器 EAX 中的内容送往 32 位寄存器 EBX
movw %ax,  %bx       #16 位寄存器
movb  %al,  %bl        # 8 位寄存器

1、把立即数传送到寄存器和内存
movl  $0,  %eax          # 0 送往 EAX 寄存器
movl  $0x80, %ebx    # 16 进制 80 送往 EBX 寄存器
movl  $100,  height   # 100 送往 height 所标识的内存位置处

2、在寄存器之间传送数据
数据在寄存器与寄存器间的传递是最快的,尽可能把数据保存在处理器寄存器中通常是明智的,这样可以减少试图访问内存位置所花费的时间。

需要注意的是,专用寄存器(控制、调试和段寄存器) 中的内容只能传给通用寄存器,或者接收从通用寄存器传来的内容。8 个通用寄存器是 (EAX, EBX, ECX, EDX, EDI, ESI, EBP 与 ESP) 。

例如:
movl   %eax,  %ecx  
movw  %ax,   %cx

下面这条指令是错误的:
movb  %al,  %bx
因为这条指令试图把 AL 寄存器中的 8 位传送给 BX 寄存器中的低 8 位。

3、在内存和寄存器之间传送数据

把数据从内存传送到寄存器
movl value, %eax
value 是个引用内存位置的标签,如下这么定义:
value:
    .int 1

把数据从寄存器传送给内存
molv  %ecx, value

使用变址的内存位置
像 C 中定义数组一样,汇编里可以在一个命令中指定把多个值存放到内存中:
values:
   .int 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60
这创建了存放在内存中的连续一系列数据值。每个数据都占用内存的一个单元 (这里是 int 整型,占4字节)。引用这些数据中其中的一个时,需要使用变址的方法来确定要访问的那个数据。完成这种操作的方式称为变址内存模式 (indexed memory mode)。内存位置由下列因素确定:
      基址
      添加到基地址上的偏移地址
      数据元素的长度
      确定选择哪个数据元素的变址
表达式的格式是:
base_address (offset_address, index, size)

获取的数据位于:
base_address + offset_address + index * size
如果其中的任何值为零,则可以忽略它们,但是仍然需要用逗号作为占位符
offset_address 和 index 的值必须是寄存器,但 size 的值可以是数字值。例如,为了引用上面给出的 values 数组中的值 20,可以使用下面的命令:
movl $2, %edi
movl values(, %edi, 4), %eax
这条指令把从 values 标签开始的第 3 个 4 字节的变址值加载到 EAX 寄存器中 (注意,数组从变址 0 开始)。大多数情况下,将使用一个寄存器计数器作为变址值,并且改变这个值来匹配要处理的数组元素。

测试程序:
.section .data
output:
   .asciz "The value is %d\n"

values:
   .int 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60

.section .text
.globl _start

_start:
     nop
     movl $0, %edi
loop:
     movl values(, %edi, 4), %eax
     pushl %eax
     pushl $output
     call printf
     addl $8, %esp
     inc %edi
     cmpl $11, %edi
     jne loop
     movl $0, %ebx
     movl $1, %eax
     int $0x80
运行输出:
$ ./movetest3
The value is 10
The value is 15
The value is 20
The value is 25
The value is 30
The value is 35
The value is 40
The value is 45
The value is 50
The value is 55
The value is 60
程序中也可以使用 offset_address ,那么在程序中可修改为:
        movl values(%edi), %eax
        pushl %eax
        pushl $output
        call printf
        addl $8, %esp
        addl $4,  %edi
        cmpl $44, %edi
        jne loop

4、使用寄存器间接寻址
当使用标签引用内存位置中包含的数据值时,可以通过指令中的标签前面加上 $ 符号获得数据值的内存位置的地址,如:
movl  $values, %edi
用于把 values 标签引用的内存地址传送给 EDI 寄存器。
又如:
movl %ebx, (%edi)
上面指令是把 EBX 中的值送到 EDI 中包含的内存地址处,也就是说 EDI 寄存器中的值是一个内存地址;如果 EDI 不加括号,则是把 EBX 中的值送到 EDI 寄存器中。

GNU 汇编器不允许把值与寄存器相加,必须把值放在括号之外,如下所示:
movl  %ebx, 4(%edi)
movl %ebx, -4(%edi)

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
沙发
 楼主| 发表于 2009-12-5 17:00:03 | 只看该作者

条件传送指令--CMOV

CMOV 是 MOV 指令的变形,它是根据特定条件进行传送的指令,其格式是:
cmovx source, destination
上面,x 是一个或者两个字母的代码,表示将触发传送操作的条件。

条件取决于 EFLAGS 寄存器的当前值。EFLAGS 寄存器中的相关条件位如下表所示:
EFLAGS位
名称
描述
CF
溢出(Carry)位
数学表达式产生了进位或者借位
OF
溢出(Overflow)位
整数值过大或过小
PF
奇偶校验(Parity)位
寄存器包含数学操作造成的错误数据
SF
符号(Sign)位
指出结果为正还是负
ZF
零(Zero)位
数学操作的结果为零

条件传送指令分为用于带符号操作的指令和用于无符号操作的指令。带符号操作涉及使用符号标志的比较,而无符号操作设计忽略符号标志的比较。

下表是无符号条件传送指令
指令对
描述
EFLAGS状态
CMOVA/CMOVNBE
大于/不小于或者等于
(CF或ZF)=0
CMOVAE/CMOVNB
大于或者等于/不小于
CF=0
CMOVNC
无进位
CF=0
CMOVC
进位
CF=1
CMOVB/CMOVNAE
小于/不大于或者等于
CF=1
CMOVBE/CMOVNA
小于或者等于/不大于
(CF或ZF)=1
CMOVE/CMOVZ
等于/零
ZF=1
CMOVNE/COMVNZ
不等于/不为零
ZF=0
CMOVP/CMOVPE
奇偶校验/偶校验
PF=1
CMOVNP/CMOVPO
非奇偶校验/奇校验
PF=0
从上表可以看出,无符号条件传送指令依靠进位、零和奇偶校验标志确定两个操作数之间的区别。
上面的指令有些是勇  / 符号隔开指令对,这两个指令具有相同的含义。比如,一个值大于另外一个值,也可以说是不小于或者等于另外一个值。这两个条件是等同的,但是二者具有个字的传送指令,如 CMOVA 和 CMOVNBE .

如果操作数是带符号值的,就必须使用不同的条件传送指令集,如下表所示
指令对
描述
EFLAGS状态
CMOVGE/CMOVNL
大于或者等于/不小于
(SF异或OF)=0
CMOVL/CMOVNGE
小于/不大于或者等于
(SF异或OF)=1
CMOVLE/CMOVNG
小于或者等于/不大于
((SF异或OF) 或 ZF)=1
CMOVO
溢出
OF=1
CMOVNO
未溢出
OF=0
CMOVS
带符号(负)
SF=1
CMOVNS
无符号(非负)
SF=0

条件传送指令需要某种类型的数学指令来设置 EFLAGS 寄存器以便进行操作,如:
movl value, %ecx
cmp %ebx, %ecx
cmova %ecx, %ebx
上面代码,先把 value 标签引用的数据值加载到 ECX 寄存器中,然后使用 CMP 指令把这个值和 EBX 寄存器中的值进行比较。CMP 指令从第 2 个操作数中减去第 1 个操作数并且适当地设置 EFLAGS 寄存器。那么,如果 ECX 寄存器中的值大于 EBX 寄存器中的原始值,就使用 CMOVA 指令把 EBX 的值替换为 ECX 中的值。(注意,AT&T 语法和 Intel 语法中的操作数顺序是相反的。)

测试程序
.section .data
output:
   .asciz "The largest value is %d\\n"

values:
   .int 105, 235, 61, 315, 134, 221, 53, 145, 117, 5

.section .text
.global _start
_start:
    nop
    movl values, %ebx
    movl $1, %edi

loop:
    movl  values(, %edi, 4), %eax
    cmp   %ebx, %eax
    cmova %eax, %ebx
    inc   %edi
    cmp   $10, %edi
    jne   loop
    pushl %ebx
    pushl $output
    call  printf
    addl  $8, %esp
    pushl $0
    call  exit
编译
$ as -gstabs  -o cmovtest.o cmovtest.s
$ ld -dynamic-linker /lib/ld-linux.so.2 -o cmovtest -lc cmovtest.o
输出
$ ./cmovtest
The largest value is 315
说明
cmovtest.s 程序查找 values 数组中定义的一系列整数中最大的一个。
EBX 寄存器用于保存当前找到的最大整数。一开始,数组的第一个值被加载到 EBX 寄存器中。然后,数组中元素被逐个加载到 EAX 寄存器中,并且和 EBX 寄存器中的值进行比较。如果 EAX 寄存器中的值更大,就把她传送给 EBX 寄存器,并且称为新的最大的值。使用 -gstabs 选项编译文件,然后在 gdb 调试里可以看到这个过程。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2024-5-15 17:17 , Processed in 0.083164 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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