寄存器与数据传送指令
64位的处理器中,原来8个16位寄存器扩展成了64位,还增加了8个
不同寄存器扮演不同的角色,相应的编程规范规定了如何使用这些寄存器
rax
用来保存函数的返回值rsp
用来保存程序栈的结束位置rdi, rsi, rdx, rcx, r8, r9
可以用来传递函数参数
指令(Instruction)
包括操作码(Operation Code)和操作数(Operands)
- 操作码决定了CPU执行操作的类型
- 大多数指令具有一个或多个操作数
操作数可以分为3类:
- 立即数(Immediate):以
$
开头,后面跟一个整数- 寄存器(Register):
%rax
,64位,32位,16位,8位寄存器都可以作为操作数- 内存引用(Memory Reference):寄存器加上小括号,如
(%rdi)
MOV指令
有两个操作数,源操作数(Source operand)和目的操作数(Destination operand)
- 源操作数可以是:立即数,寄存器,内存引用
- 目的操作数是用来存放源操作数的内容,所以只能是寄存器或内存引用
所以,放需要将一个数从内存的一个位置复制到另一个位置,需要两条mov
指令来完成
mov memory, register // 将内存源位置的数值加载到寄存器
mov register, memory // 再将该寄存器的值写入目标位置
mov
指令的后缀与寄存器的大小一定得是匹配的
movl $0x4050, %eax // 立即数->寄存器 32位
movw %bp, %sp // 寄存器->寄存器 16位
movb (%rdi, %rcx), %al // 内存->寄存器 8位
movb $-17, (%rsp) // 立即数->内存 8位
movq %rax, -12(%rbp) // 寄存器->内存 64位
特殊情况
当movq
的源操作数是立即数时,该立即数只能是32位的补码表示,然后对该数值进行符号位扩展,将得到的64位数传送到目标位置
movabsq
指令的源操作数可以是任意的64位立即数,目的操作数只能是寄存器
例子:
使用movabsq
将一个64位立即数复制到寄存器rax
movabsq $0x0011223344556677, %rax
rax
内的数值如图所示
接下来,使用movb
指令将立即数-1复制到低8位的寄存器al
此时,rax
的低8位发生了变化
然后,再用指令movw
将-1复制给16位寄存器ax
此时,rax
的低16位发生了变化
再用movl
将立即数-1复制到32位寄存器eax
此时,rax
的低32位发生了变化,而且高32位也被置0
- 64位处理器的规定:
movl
的目的操作数是寄存器时,它会把寄存器的高4字节设置为0
当源操作数的数位小于目的操作数时,需要对目的操作数剩余的字节进行零扩展或符号位扩展
零扩展数据传送指令有5条,z表示zero,最后两个字符都是大小指示符
movzbw 8->16
movzbl 8->32
movzwl 16->32
movzbq 8->64
movzwq 16->64
符号位扩展传送指令有6条,s表示sign
movsbw 8->16
movsbl 8->32
movswl 16->32
movsbq 8->64
movswq 16->64
movslq 32->64
对比发现,零扩展没有32位到64位的扩展指令,这种情况可以用movl
来实现,本身就是可以将高位置0
另外,符号位扩展还有一条没有操作数的特殊指令cltq
,源操作数总是eax
,目的操作数总是rax
,就是对寄存器rax
的高32位进行符号位扩展,等价于movslq %eax, %rax