目录 Table of Contents
地址计算指令与其他-1
刚才说了两种寻址方式, 现在讲第三种方式
变址寻址
常见形式
D(RB, Ri, S) Mem[Reg[Rb] + S*Reg[Ri] + D]
D : Displacement 常量 (地址偏移量)
Rb : 基址寄存器 : 8 个通用寄存器之一
Ri : 索引寄存器 : %esp 不作为索引寄存器, 一般 %ebp 也不用作这个用途
S : Scale 比例因子 1, 2, 4, or 8
我把这个基址, 加上你的索引*scale, 这个 scale 是 1 2 4 8, 这个算出来之后, 再加上一个常量的地址偏移.
这个实际上很像 C 里面访问的数组. 数组可以想象, 数组应该有一个开始的一个地址, 可以把它放在寄存器里面, 访问的时候是用 index, 可以想象把这个 index 放到 index 寄存器里面去, 然后再乘上 sizeof(每个数组元素的宽度, 一般来说 1 2 4 8 够用了), 最后加一个总的偏移量.
变址寻址模式, 用的时候可以漏掉一些, 可以两个要素, 甚至一个要素 :
其他变形 :
(Rb, Ri) Mem[Reg[Rb] + Reg[Ri]]
D(Rb, Ri) Mem[Reg[Rb] + Reg[Ri] + D]
(Rb, Ri, S) Mem[Reg[Rb] + S*Reg[Ri]]
反正总的算法是 index 乘上你的 scale, 再加上基址, 再加上 displacement
寻址模式实例
初始值 : %edx = 0xF000; %ecx = 0x100
Expression 表达式 | Computation 运算 | Address 最终地址 |
---|---|---|
0x8(%edx) | 0xF000 + 0x8 | 0xF008 |
(%edx, %ecx) | 0xF000 + 0x100 | 0xF100 |
(%edx, %ecx, 4) | 0xF000 + 4 * 0x100 | 0xF400 |
0x80 (, %edx, 2) | 2 * 0xF000 + 0x80 | 0x1E080 |
第一行 0x8 括号里面是个 edx, 就是把 edx 的值取出来, 加上 displacement.
第二个是括号里面是两个寄存器, 那反正是把这两个寄存器加起来的和作为地址.
第三个括号里面三个参数, 就是 edx 作为基址, ecx 是作为 index 乘上 4 (就是 Scale), 乘完之后再加 edx
最后一个, 括号里面第一个漏掉了, 没有基址这是可以的, 然后就是 edx 乘 2, 再加上括号外面的 D 0x80
地址计算指令
讲完了 mov
指令, 接下来就是地址计算指令
leal Src, Dest
计算出来的地址赋给 Dest
l 后缀就是, 我算的目的操作数是双字类型
注意, Src 是地址计算表达式. 地址计算表达式就是刚才变址寻址的式子.
Destination 一般来说是个寄存器.
它和
mov
很像, 但是有本质的不同.mov
指令如果你的 Src 是地址表达式的话, 它是把这个算出来地址, 这个地址里面的内容取出来再移动过去. 而leal
很简单, 我算出来的这个地址, 就是我所需要的东西, 算完之后这个地址本身, 给挪到 Destination 里面去.
它的一大用途就是地址计算, 但是不访问内存
它还可以完成 x + k * y
这一种类型的整数计算. 这里面 x 和 y 可以是寄存器, 也就是说 x y 是可变的. 如果你能把一个整数计算表达成这种形式的话, 那么可以用 leal
指令很方便地进行计算. 这个比你单独地用加减乘除指令计算要快.
整数计算指令
- 双操作数指令
加减乘
指令格式 | 计算过程 |
---|---|
addl Src, Dest |
Dest = Dest + Src |
subl Src, Dest |
Dest = Dest - Src |
imull Src, Dest |
Dest = Dest * Src |
稍微说下 32 位乘法, 注意看, 这里面加减乘, 乘法是只取 32 位结果的, 因为它是 l
没有说取到 64 位结果.
在这种情况下, 加减乘实际上不区分它们操作数的类型是带符号或者不带符号.
指令格式 | 计算过程 |
---|---|
sall Src, Dest |
Dest = Dest << Src 与 shll 等价, 数据左移 |
sarl Src, Dest |
Dest = Dest >> Src 数据的算术右移 |
shrl Src, Dest |
Dest = Dest >> Src 数据的逻辑右移 |
算术右移与逻辑右移不一样的地方在于, 因为你数据整个往右挪动了, 你高位得把别的数据补充进来. 补什么呢, 逻辑右移就是单纯地补充 0, 算术右移就是补充被移动数据的最高位, 就是符号位.
指令格式 | 计算过程 |
---|---|
xorl Src, Dest |
Dest = Dest ^ Src |
andl Src, Dest |
Dest = Dest & Src |
orl Src, Dest |
Dest = Dest | Src |
这些就是一些逻辑操作了
- 单操作数指令
指令格式 | 计算过程 |
---|---|
incl Dest |
Dest = Dest + 1 加 |
decl Dest |
Dest = Dest -1 减 |
negl Dest |
Dest = - Dest 取非 |
notl Dest |
Dest = ~ Dest 取反 |