曲径通幽论坛

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

交换数据与数据交换指令

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2009-12-5 18:04:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
有时候在程序中必须交换数据元素的位置,MOV 指令的一个缺陷是难以在不使用临时中间寄存器的情况下交换两个寄存器的值。比如单独用 MOV 指令,交换 EAX 和 EBX 中的值,需要借助第 3 个寄存器 ECX ,如:
movl %eax, %ecx
movl %ebx, %eax
movl %ecx, %ebx
然而有几个数据交换指令却可以在这类涉及数据交换处理的情况下使用,如下表所示:
指令
描述
XCHG
在两个寄存器之间或者寄存器和内存位置之间交换值
BSWAP
反转一个 32 位寄存器中的字节顺序
XADD
交换两个值并且把总和存储在目标操作数中
CMPXCHG
把一个值和一个外部值进行比较,并且交换它和另一个值
CMPXCHG8B
比较两个 64 位值并且交换它们

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
沙发
 楼主| 发表于 2009-12-5 18:05:09 | 只看该作者

XCHG 指令

XCHG
XCHG 指令应用最为简单,它的作用是在两个通用寄存器之间或者寄存器和内存之间交换数据值,其格式如下:
xchg  openrand1, operand2
operand1 或者 operand2 可以是通用寄存器,也可以是内存位置 (但两者不能同时为内存位置) 。可以对任何通用 8位, 16位和32位寄存器使用这个命令,但是两个操作数的长度必须相同。当一个操作数是内存位置时,处理器的 LOCK信号被自动标明,防止在交换过程中任何其他处理器访问这个内存位置。
(使用 XCHG 对内存位置进行操作时要小心。LOCK 处理器非常耗费时间,并且可能对程序性能有不良影响)

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
板凳
 楼主| 发表于 2009-12-5 22:23:50 | 只看该作者

BSWAP 指令

BSWAP 指令很强大,当使用的系统具有不同的字节排列方式(或大端格式,或小端格式)时,它很有用。

BSWAP 指令反转寄存器中字节的顺序。第 0~7 位和第 24~31 位进行交换,第 8~15 位和第 16~23 位交换。交换如下图所示:

注:位的顺序并没有反转;被反转的是寄存器中包含的各个字节。这样就从小端格式(litte-endian)的值生成了大端格式(big-endian)的值,反之亦然。

测试代码
# swaptest.s - An example of using the BSWAP instruction

.section .data
output1:
  .asciz "Before BSWAP,the value in EBX is: 0x%x\\n"

output2:
  .asciz "After BSWAP, the value in EBX is: 0x%x\\n"

.section .text
.globl _start
_start:
    nop
    movl    $0x12345678, %ebx
    pushl    %ebx
    pushl    $output1
    call    printf
    addl    $8, %esp
    bswap     %ebx
    pushl    %ebx
    pushl    $output2
    call    printf
    addl    $8, %esp
    pushl    $0
    call    exit
编译
$ as -o swaptest.o swaptest.s
$ ld -dynamic-linker /lib/ld-linux.so.2 -lc -o swaptest swaptest.o
运行输出
$ ./swaptest
Before BSWAP,the value in EBX is: 0x12345678
After BSWAP, the value in EBX is: 0x78563412

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
地板
 楼主| 发表于 2009-12-6 00:41:41 | 只看该作者

XADD 指令

XADD 指令用于交换两个寄存器或者内存位置和寄存器的值,然后把两值相加,最后把结果存到目标位置 (寄存器或者内存位置),命令格式是:
xadd source, destination
其中 source 必须是寄存器,destination 可以是寄存器,也可以使内存位置,并且 destination 包含相加的结果。寄存器可以是 8 位,16位或者是32位。

测试代码
# xadd.s - An example of the XADD instruction

.section .data
output1:
  .asciz "Before XADD, the value in EAX is: %d, the value in EBX is: %d\\n"

output2:
  .asciz "After XADD, the value in EAX is: %d, the value in EBX is: %d\\n"

.section .text
.global _start

_start:
    nop
    movl $7, %eax
    movl $8, %ebx
    pushl %ebx
    pushl %eax
    pushl $output1
    call  printf
    addl  $12, %esp
   
    movl $7, %eax
    movl $8, %ebx
    xadd %eax, %ebx
    pushl %ebx
    pushl %eax
    pushl $output2
    call  printf
    addl $12, %esp

    pushl $0
    call  exit
编译
$ as -gstabs -o xadd.o xadd.s
$ ld -dynamic-linker /lib/ld-linux.so.2 -lc -o xadd xadd.o
输出
$ ./xadd
Before XADD, the value in EAX is: 7, the value in EBX is: 8
After XADD, the value in EAX is: 8, the value in EBX is: 15
说明
程序中用了 EAX 和 EBX 两个寄存器做试验,同时在 xadd 之前也用到了 printf() 函数,需要注意的是 printf() 函数是带有返回值的,而这个返回值会存在 EAX 中,所以在用 xadd 之前,还必须重新对 EAX 赋予原来的值--即 7 ,不然的话,会得到与设想不相符的交换值与相加后的和值。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
5#
 楼主| 发表于 2009-12-6 01:31:34 | 只看该作者

CMPXCHG 指令

CMPXCHG 指令比较目标操作数和 EAX, AX 或者 AL 寄存器中的值。如果两个值相等,就把源操作数的值加载到目标操作数中。如果两个值不相等,就把目标操作数加载到 EAX,AX 或者 AL 寄存器中。命令格式为:
cmpxchg source,  destination
目标操作数可以是8位,16位,或者32位寄存器,或者是内存位置。源操作数必须是长度和目标操作数匹配的寄存器。

测试程序
#cmpxchg.s - An example of the cmpxchg instruction

.section .data
data:
   .int 10

.section .text
.global _start

_start:
        nop
        movl    $10, %eax
        movl    $5,  %ebx
        cmpxchg %ebx, data
        movl    $1,  %eax
        int     $0x80
上面程序使用 CMPXCHG 指令比较 data 标签引用的内存位置的值和 EAX 寄存器中的值。因为它们相等,所以源操作数 (EBX) 中的值被加载到 data 内存位置中,并且 EBX 寄存器的值保持不变。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
6#
 楼主| 发表于 2009-12-6 12:39:39 | 只看该作者

CMPXCHG8B 指令

CMPXCHG8B 只有单一操作数,其格式为:
cmpxchg8b  destination
destination 操作数引用一个内存位置,其中的 8 个字节值会与 EDX 和 EAX 寄存器中包含的值进行逐个比较 ( EDX 是高位寄存器,EAX 是低位寄存器,EDX:EAX ) 。如果目标值和 EDX:EAX 寄存器对中包含的值匹配,就把位于 ECX:EBX 寄存器对中的 64 位值传送到目标内存置。如果不匹配,就把目标内存位置地址中的值加载到 EDX:EAX 寄存器中。

不匹配的情况
# cmpxchg8b.s - An example of the cmpxchg8b instruction

.section .data
data:
   .byte 0x11, 0x23, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88

.section .text
.globl _start

_start:
    nop
    movl $0x44332211, %eax
    movl $0x88776655, %edx
    movl $0x11111111, %ebx
    movl $0x22222222, %ecx

    cmpxchg8b data
    movl $0, %ebx
    movl $1, %eax
    int  $0x80
在使用 cmpxchg8b 指令时,EDX:EAX 中从低到高每次以 1 个字节与 data 开始的每个字节逐个比较,如果中间有不同则停止比较,然后把从 data 开始的 8 个字节的值加载到 EDX:EAX 中。这在 gdb 调试中可以看到:
(gdb) info registers
eax            0x44332311    1144202001
ecx            0x22222222    572662306
edx            0x88776655    -2005440939
ebx            0x11111111    286331153
esp            0xbffff480    0xbffff480

如果把上面程序中的 0x23 改成 0x22 ,那么从 data 开始的 8 个字节的值会被 EDA:EAX 中的值覆盖,通过 gdb 调试可以看到:
Starting program: /home/beyes/programming/assembly/movx/cmpxchg8b

Breakpoint 1, _start () at cmpxchg8b.s:12
12        movl $0x44332211, %eax
Current language:  auto
The current source language is "auto; currently asm".
(gdb) n
13        movl $0x88776655, %edx
(gdb) n
14        movl $0x11111111, %ebx
(gdb) n
15        movl $0x22222222, %ecx
(gdb) n
17        cmpxchg8b data
(gdb) n
18        movl $0, %ebx
(gdb) x/8xb &data
0x804909c <data>:    0x11    0x11    0x11    0x11    0x22    0x22    0x22    0x22

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
7#
 楼主| 发表于 2009-12-6 15:21:08 | 只看该作者

交换指令在冒泡法中的使用

程序代码
# bubble.s - An example of the XCHG instruction

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

.section .text
.global _start

_start:
    movl $values, %esi
    movl $9, %ecx
    movl $9, %ebx
loop:
    movl (%esi), %eax
    cmp  %eax, 4(%esi)
    jge  skip   
    xchg %eax, 4(%esi)
    movl %eax, (%esi)
   
skip:
    add $4, %esi
    dec %ebx
    jnz loop
    dec %ecx
    jz  end
    movl $values, %esi
    movl %ecx, %ebx
    jmp  loop

end:
    movl $1, %eax
    movl $0, %ebx
    int  $0x80
编译链接:
$ as -gstabs -o bubble.o bubble.s
$ ld -o bubble bubble.o

通过 gdb 调试,可以看到,数组被重新排序:
Breakpoint 1, end () at bubble.s:32
32        movl $1, %eax
Current language:  auto
The current source language is "auto; currently asm".
(gdb) x/9d &values
0x80490b0 <values>:    5    53    61    105
0x80490c0 <values+16>:    117    134    145    221
0x80490d0 <values+32>:    235    315
程序说明
索引数组用 ESI 寄存器,一开始 ESI 寄存器中装入 values 数组的始地址。
排序总共使用两层循环,内层循环一次要使所有遍历到的元素中最大的那个掉到最底;而每一次的遍历元素个数都要比上一次少 1 个;数组中一共有 10 个元素,外层循环需要进行 9 次才能完成最终完成排序任务。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2025-5-3 16:08 , Processed in 0.072815 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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