汇编语言学习[2018-05-14],第 5 天
汇编语言的条件移动数据指令
CMOV指令
指令格式
cmovx source, destination
cmov指令基于EFLAGS寄存器做条件判断,用于条件判断的位如下:
EFLAGS寄存器的位 | 数据类型指示 | 数据类型描述 |
---|---|---|
CF | Carry flag | A mathematical expression has created a carry or borrow |
OF | Overflow flag | An integer value is either too large or too small |
PF | Parity flag | The register contains corrupt data from a mathematical operation |
SF | Sign flag | Indicates whether the result is negative or positive |
ZF | Zero flag | The result of the mathematical operation is zero |
无符号的条件移动指令
指令对 | 指令描述 | EFLAGS寄存器条件 |
---|---|---|
CMOVA/CMOVNBE | 大于/不小于等于 | (CF or ZF) = 0 |
CMOVAE/CMOVNB | 大于等于/不小于 | CF=0 |
CMOVNC | Not carry | CF=0 |
CMOVB/CMOVNAE | 小于/不大于等于 | CF=1 |
CMOVC | Carry | CF=1 |
CMOVBE/CMOVNA | 小于等于/不大于 | (CF or ZF) = 1 |
CMOVE/CMOVZ | 等于/zero | ZF=1 |
CMOVNE/CMOVNZ | 不等于/zero | ZF=0 |
CMOVP/CMOVPE | Parity/parity even | PF=1 |
CMOVNP/CMOVPO | Not parity/parity odd | PF=0 |
有符号的条件移动指令
指令对 | 指令描述 | EFLAGS寄存器条件 |
---|---|---|
CMOVGE/CMOVNL | 大于等于/不小于 | (SF xor OF)=0 |
CMOVL/CMOVNGE | 小于/不大于等于 | (SF xor OF)=1 |
CMOVLE/CMOVNG | 小于等于/不大于 | CMOVLE/CMOVNG |
CMOVO | Overflow | OF=1 |
CMOVNO | Not overflow | OF=0 |
CMOVS | Sign (negative) | SF=1 |
CMOVNS | Not sign (non-negative) | SF=0 |
条件移动指令的示例代码
movl value, %ecx
cmp %ebx, %ecx
cmova %ecx, %ebx
* 注意:Remember that in AT&T syntax, the order of the operands in the CMP and CMOVA instructions are reversed from the Intel documentation. This can be confusing. *
交互数据
指令 | 指令描述 |
---|---|
XCHG | Exchanges the values of two registers, or a register and a memory location |
BSWAP | Reverses the byte order in a 32-bit register |
XADD | Exchanges two values and stores the sum in the destination operand |
CMPXCHG | Compares a value with an external value and exchanges it with another |
CMPXCHG8B | Compares two 64-bit values and exchanges it with another |
XCHG指令
指令格式
xchg operand1, operand2
指令描述
- XCHG指令可以交互两个寄存器的数据,或者是一个寄存器一个内存地址的数据;
- operand1 和 operand2都可以为通用寄存器或内存地址,两个不能同时为内存地址;
- 指令可用于8、16、32位通用寄存器,两个操作数的位数必须一致 ;
- 如果其中一个操作数是内存地址,处理器的LOCK信号会自动设置,防止数据交换期间其他处理器操作内存 ;
- 注意:当使用XCHG指令,操作内存地址时,LOCK处理会非常耗时,性能很差 ;
BSWAP指令
指令格式
bswap operand1
指令描述
- 调整寄存器的字节顺序 ;
- 地位字节(0位~7位)与高位字节(24位~31位)交换位置 ;
- 次地位字节(8位~15位)与次高位字节(16位~23位)交换位置 ;
- 只是调整字节位置,不是颠倒位 ;
指令示例
# swaptest.s – An example of using the BSWAP instruction
.section .text
.globl _start
_start:
nop
movl $0x12345678, %ebx
bswap %ebx
movl $1, %eax
int $0x80
# bswap %ebx执行后,$ebx的值为:0x78563412
XADD指令
指令格式
xadd source, destination
指令描述
- XADD指令用于交换两个寄存器,或者一个内存地址一个寄存器的值,并且将两个值相加,保存到目标操作数(寄存器或者内存地址);
- 源操作数必须是寄存器 ,目标操作数可以是寄存器或者内存地址,保存相加结果;
- 寄存器可以是8、16、32位 ;
CMPXCHG指令
指令格式
cmpxchg source, destination
指令描述
- CMPXCHG指令将目标操作数与EAX、AX、AL比较,如果值相等,源操作数的值将被载入目标操作数;如果不相等,目标操作数的值将载入EAX、AX、AL中 ;
- 目标操作数可以是8、16、32位寄存器或者内存地址 ;
- 源操作数必须是寄存器 ,位数与目标寄存器匹配;
CMPXCHG8B指令
指令格式
cmpxchg8b destination
指令描述
- cmpxchg8b 和CMPXCHG指令类似,不同的是cmpxchg8b指令用于操作8字节操作数 ;
- 目标操作数是一个内存地址 ,8字节的内存值与EDX 和 EAX寄存器保存的8字节值比较(EDX高位,EAX地位);
- 如果值相等,ECX:EBX寄存器对中的值会载入到目标操作数的内存地址中 ;
- 如果不相等,目标操作数内存地址中的8字节值会被载入到EDX:EAX寄存器对中 ;
使用数据交换指令的示例
# bubble.s - An example of the XCHG instruction
.section .data
values:
.int 105, 235, 61, 315, 134, 221, 53, 145, 117, 5
.section .text
.globl _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
#示例代码是排序算法——冒泡法,同下面的高级语言代码
for(out = array_size-1; out>0, out--)
{
for(in = 0; in < out; in++)
{
if (array[in] > array[in+1])
swap(array[in], array[in+1]);
}
}
Stack栈
栈描述
- 栈是一个特殊的颠倒的用于存放数据的区域,特殊的地方是栈的数据放入和移除的方式 ;
- 通常程序是从内存低位向内存高位保存数据,栈是相反的,从内存高位向内存低位保存数据;
PUSH入栈指令
指令格式
pushx source
x表示数据大小(l,32位;w,16位)
PUSH指令可以操作的数据元素
- 16-bit register values
- 32-bit register values
- 16-bit memory values
- 32-bit memory values
- 16-bit segment registers
- 8-bit immediate data values
- 16-bit immediate data values
- 32-bit immediate data values
# 示例
pushl %ecx # puts the 32-bit value of the ECX register on the stack
pushw %cx # puts the 16-bit value of the CX register on the stack
pushl $100 # puts the value of 100 on the stack as a 32-bit integer value
pushl data # puts the 32-bit data value referenced by the data label
pushl $data # puts the 32-bit memory address referenced by the data label
POP出栈指令
指令格式
popx destination
x表示数据大小(l,32位;w,16位)
POP指令可以操作的数据元素
- 16-bit registers
- 16-bit segment registers
- 32-bit registers
- 32-bit registers
- 32-bit memory locations
# 示例
popl %ecx # place the next 32-bits in the stack in the ECX register
popw %cx # place the next 16-bits in the stack in the CX register
popl value # place the next 32-bits in the stack in the value memory location
PUSH和POP对所有寄存器的操作指令
指令 | 描述 |
---|---|
PUSHA/POPA | Push or pop all of the 16-bit general-purpose registers |
PUSHAD/POPAD | Push or pop all of the 32-bit general-purpose registers |
PUSHF/POPF | Push or pop the lower 16 bits of the EFLAGS register |
PUSHFD/POPFD | Push or pop the entire 32 bits of the EFLAGS register |
PUSHA指令将16位压栈的顺序是:DI, SI, BP, BX, DX, CX, 和最后的 AX ;
PUSHAD指令的压栈顺序和PUSHA一样 ;
POPA和POPAD指令的出栈顺序是上面的反序 ;
POPF和POPFD指令对,依赖处理器的操作模型,如果处理器运行在受保护模型,EFLAGS寄存器中的所有非预留标识位可以被修改,除了VIP, VIF, 和 VM 标识位,VIP 和 VIF 标识位会被清掉,VM标识位不能被修改 ;
使用ESP和EBP寄存器手动压栈和出栈
PUSH和POP不是唯一的栈操作指令,你可以使用ESP作为内存地址指针,来存取数据;
通常,不只是使用ESP寄存器自己,很多程序会拷贝ESP寄存器的值到EBP;通常汇编程序过程会使用EBP指向指定过程的栈底,指令通过EBP来访问保存在栈中的参数 ;
优化内存访问
内存访问是一个比较慢的功能,影响处理器性能;写高性能的汇编程序,最好是尽量内存访问,越少越好 ;如果可能的话,进行保持变量在寄存器中;寄存器访问很快,是处理器的最佳选择;这是处理数据最快的方式 ;如果将所有应用程序数据放到寄存器中不可能实现,你应该试着去优化程序的内存访问 ,因为处理器会使用数据缓存,有顺序的访问内存,会增加缓存命中 ,因为内存块会一次性读入缓存;
另外大多数处理器(包括IA-32平台)已优化为从特殊的缓存块读写内存,从汇编程序的.data数据块定义的数据的起始位置开始,Pentium 4处理器,缓存块的大小是64位,如果你定义的数据元素跨过64位块的边界,就需要两次缓存操作去读写内存数据元素 ;Intel建议按以下规则定义数据:
- Align 16-bit data on a 16-byte boundary.
- Align 32-bit data so that its base address is a multiple of four.
- Align 64-bit data so that its base address is a multiple of eight.
- Avoid many small data transfers. Instead, use a single large data transfer.
- Avoid using larger data sizes (such as 80- and 128-bit floating-point values) in the stack.
写汇编程序时,在定义.data数据块时,尽量把相同大小的数据元素放在一起 ;如果有不规则大小的数据元素,如字符串和buffers,将它们放到.data数据块的最后 ;
gas assembler supports the .align directive,将数据元素定义在指定的边界内,17章会详细讲解