注释/说明
L : 字面量
R: 内存变量
M: 寄存器
S: 标号
寄存器
在 masm 汇编中, 一般有以下几种寄存器
通用目的寄存器 | 段寄存器 | 指令指针寄存器 | 标志位寄存器 |
---|---|---|---|
eax | cs | eip | OF |
ebx | ds | SF | |
ecx | es | ZF | |
edx | ss | CF | |
esi | AF | ||
edi | PF | ||
ebp | DF | ||
esp | IF | ||
TF |
通用目的寄存器
eax : 全称为 Accumulator Register(累加器). 在进行乘法和除法时会将其中一部分操作结果自动放在 eax 中, 调用函数时也需要把返回值保存在 eax 中. 因此在执行这些操作时不要用 eax 存储数据
ebx : 全称为 Base Register(基地址寄存器). 一般没什么专门的用途, 常用来充当临时变量或地址
ecx : 全称为 Counter Register(计数寄存器). 一般用来充当计数器, 在循环中的使用较多.
edx : 全称为 Data Register(数据寄存器). edx 常被 eax 用来扩展数位 (例如进行有符号除法时, edx : eax 共同组成被除数, edx 的最高位充当符号位)
esi 和 edi : 全称为 Source Index (源变址寄存器) / The Destination Index (目的变址寄存器). 这两者的区别不是很大, 不过在 CPU 中往往是 esi 读取数据, 而 edi 写入数据. 一般在字符串操作上面的用途较多, 且往往和循环指令 STOS, MOVSB, SCASB 一起出现来完成大量数据的保存, 加载与扫描工作.
ebp : 全称为 Base Pointer (栈基指针寄存器). 在函数调用时常用来作栈底指向栈中的数据, 在整个函数运行的过程中其内部存放的地址不会发生改变. 该寄存器为特殊寄存器, 不能对其进行修改.
esp : 全称为 Stack Pointer (栈顶指针寄存器). 与 ebp 一样与函数调用有关, 且每一次的 push, pop, ret, call 操作时其内部的地址都会发生改变. 该寄存器为特殊寄存器, 不能对其进行修改.
段寄存器
cs : 全称是Code Segement (代码段寄存器).
ds : 全称是Data Segment (数据段寄存器).
es : 全称是Extra(Data) Segment (扩展数据段寄存器)
ss : 全称是Stack Segment (栈段寄存器)
由于我还没能了解段寄存器的作用, 因此只能将完整的名称写上, 后续会补
指令指针寄存器
eip : 全称是Extend Instruction Pointer (扩展指令指针寄存器). 主要用来指向内存中的地址, 以表示接下来应该获取, 解码并执行的指令. 该寄存器也是一种特殊寄存器, 不能对其进行修改.
标志位寄存器
寄存器 | 功能 | 全称 | 标志形式 |
---|---|---|---|
OF | 溢出标志 | overflow flag | 溢出置1, 否则置0 |
SF | 符号标志 | sign flag | 正数置0, 负数置1 |
ZF | 零标志 | zero flag | 结果为0置1, 否则置0 |
CF | 进位标志 | carry flag | 无符号数最高有效位进位, 或有符号数最高有效位需要借位, 则置1. 否则置0 |
AF | 辅助进位标志 | auxiliary carry flag | 用以表示加减法做到一半时有没有形成进位/借位,如果有则置1, 否则置0 |
PF | 奇偶标志 | parity flag | 若结果为1的二进制位个数是偶数则置1, 为奇数置0 |
DF | 方向标志 | direction flag | 用以代表esi和edi的增减, 置1递减, 指令递增 |
IF | 中断标志 | interrupt flag | IF置1,响应外部中断,置0则屏蔽外部中断; |
TF | 陷进标志 | trap flag | TF置1时,处理器每次只执行一条指令,即单步执行. 置0反之 |
mov 和 movl
将源操作数的内容复制到目标操作数, 两者必须有一个是寄存器
mov M/R, L/M/R
xchg
交换两个操作数的内容, 两者必须有一个是寄存器
xchg M/R, M/R
add 和 sub
add 为加法指令, sub 为减法指令. 两个操作数必须有一个是寄存器
add M/R, L/M/R
sub M/R, L/M/R
neg
切换操作数的正负号, 即求补码
neg M/R
inc 和 dec
inc 自加指令, dec 自减指令
inc M/R
dec M/R
mul 和 imul
mul 为无符号乘法, imul 为有符号乘法.
mul 的积存储在 edx : eax 中, 其中eax作被乘数, 只支持单个操作数. imul 的积存储在自定义的寄存器中, 被乘数和乘数也可存储在自定义的寄存器中, 最多支持三个操作数.
mul M/R
imul M/R
imul R, L/M/R
imul R, M/R, L
div 和 idiv
div 为无符号除法, idiv 为有符号除法.
div 计算的被除数存储在 edx : eax 中, 除数为自定义的内存变量或寄存器, 商存储在 eax 中, 余数存储在 edx 中. 只支持单操作数
idiv 与 div 差不多. 不过在进行有关负数除法运算时, 需要将 edx 和 eax 全部求补才可进行运算, 否则会出错.
div M/R
idiv M/R
SHL 和 SHR
SHL 和 SHR 为逻辑左移 / 逻辑右移. 每移动 n 位二进制数, 结果扩大 / 缩小原来的 \(2^{n}\) 倍, 注意如果值为负数, 进行逻辑右移后其可能会出错.
shl M/R, L
shr M/R, L
SAL 和 SAR
SAL 和 SLR 为算数左移 / 算术右移. 每移动 n 位二进制数, 结果扩大 / 缩小原来的 \(2^{n}\) 倍, 负数进行算术右移不会像逻辑右移一样出现错误.
sal M/R, L
sar M/R, L
数位扩展
指令 | 寄存器 | 大小 | 扩展到 | 寄存器 | 大小 |
---|---|---|---|---|---|
cwd | ax | 16 | → | dx : ax | 32 |
cdq | eax | 32 | → | edx : eax | 64 |
cqo | rax | 64 | → | rdx : rax | 128 |
有了数位扩展指令, 就可以很方便的进行负数的有符号除法.
xor edx, edx
xor eax, eax
mov eax, -1
cdq
mov ebx, 1
idiv ebx
寄存器结果
eax = FFFFFFFF
edx = 00000000
lea
加载有效地址, 将源操作数的地址加载到目标操作数中. 由于实际地址要在程序运行时才能知道, 因此涉及这些地址的操作应该用 lea 来完成. 该指令的目标操作数必须是寄存器
lea R, M
ptr
用来将数据移动到与之大小不符的另一个区域所需要使用的指令, 例如一个为 4 个字节的 int 内存变量, 若移动到一个其大小为 2 个字节的 ax 寄存器中, 则需要 ptr 进行转换; 或一个为 2 个字节的 int 内存变量, 若移动到一个其大小为 4 个字节的 eax 寄存器中, 也需要 ptr 进行转换. size 必须与 R 的大小相符, 源操作数必须是内存变量
mov R, size ptr M
int x = -1;
__asm
{
xor eax, eax
mov ax, word ptr x
}
寄存器结果
;转换前的eax
eax = 00000000
;转换后的eax
eax = 0000FFFF
movzx 和 movsx
与 ptr 指令一样, 用来将数据移动到与之大小不符的另一个区域所需要使用的指令, 但是限制没有 ptr 那么多. 扩展的大小视目标操作数大小而定
movzx 的 zx 表示 Zero eXtend , 用 0 来扩展数位, movsx 的 sx 表示 Sign eXtend, 用符号位来扩展数位
movzx R, M/R
movsx R, M/R
offset
offset 看上去也是和 lea 一样用来获取地址的, 不过与 lea 不同的是, offset 获取的是内存变量或标号到数据段数据段的起始地址的距离. 源操作数只能是内存变量或标号. 且 offset 只能获取编译时已知的地址 (如符号常量等)
mov M/R, offset M/S