汇编语言学习笔记——5

时间:2022-05-20 14:02:03

汇编语言学习[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章会详细讲解