[TOC]

原文

汇编语言快速入门

内存和寻址方式

声明静态数据区

可以在x86汇编语言中使用汇编指令.DATA来声明静态数据区(类似于全局变量)。数据以单字节、双字节或双字的方式存放。(DB,DW,DD) | .DATA | | | | —– | ——– | ——————– | | var | DB 64 | 声明一个直接,并将数值64放入此字节中 | | var2 | DB ? | 声明一个未初始化的字节 | | | DB 10 | 声明一个没有label的字节,其值为10 | | X | DW ? | 声明一个双字节,未初始化 | | Y | DD 30000 | 声明一个4字节,其值为30000 |

指令详解

汇编指令通常可以分为数据传送指令、逻辑计算指令和控制流指令。本节将讲述其中最重要的指令,以下标记分别表示寄存器、内存和常数。 | 表示 | 内容 | | ——– | :————————————— | | <reg32> | 32位寄存器 (EAX, EBX, ECX, EDX, ESI, EDI, ESP, or EBP) | | <reg16> | 16位寄存器 (AX, BX, CX, or DX) | | <reg8> | 8位寄存器(AH, BH, CH, DH, AL, BL, CL, or DL) | | <reg> | 任何寄存器 | | <mem> | 内存地址 (e.g., [eax], [var + 4], or dword ptr [eax+ebx]) | | <con32> | 32位常数 | | <con16> | 16位常数 | | <con8> | 8位常数 | | <con> | 任何8位、16位或32位常数 |

数据传送指令

mov

mov指令将第二个操作数(寄存器的内容、内存中的内容或值)复制到第一个操作数(寄存器或内存)。mov不能用于直接从内存复制到内存。如下:

mov <reg>,<reg>
mov <reg>,<mem>
mov <mem>,<reg>
mov <reg>,<const>
mov <mem>,<const>

Examples
mov eax, ebx — 将ebx的值拷贝到eax
mov byte ptr [var], 5 — 将5保存找var指示内存中的一个字节中

push

push指令将操作数压入内存的栈中,其中ESP只是栈顶。在压栈前,首先将ESP值减4(x86栈增长方向与内存地址编号增长方向是相反的),然后将操作数内容压入ESP指示的位置。语法如下:

push <reg32>
push <mem>
push <con32>

Examples
push eax — 将eax内容压栈
push [var] — 将var指示的4直接内容压栈

pop

与push指令相反,pop指令执行的是出栈的操作。它首先将ESP指示的地址中的内容出栈,然后将ESP值加4.语法如下:

pop <reg32>
pop <mem>

Examples
pop edi — pop the top element of the stack into EDI.
pop [ebx] — pop the top element of the stack into memory at the four bytes starting at location EBX.

lea

lea实际上是一个载入有效地址指令,将第二个操作数表示的地址载入到第一个操作数(寄存器)中。语法如下:

Syntax
lea <reg32>,<mem>

Examples
lea eax, [var] — var指示的地址载入eax中.
lea edi, [ebx+4*esi] — ebx+4*esi表示的地址载入到edi中,这实际是上面所说的寻址模式的一种表示方式.

算术和逻辑指令

add

add指令将两个操作数相加,且将相加后的结果保存到第一个操作数中。语法如下:

add <reg>,<reg>
add <reg>,<mem>
add <mem>,<reg>
add <reg>,<con>
add <mem>,<con>
Examples
add eax, 10 — EAX ← EAX + 10
add BYTE PTR [var], 10 — 10与var指示的内存中的一个byte的值相加,并将结果保存在var指示的内存中

sub

sub指令指示第一个操作数减去第二个操作数,并将相减后的值保存在第一个操作数。语法如下:

sub <reg>,<reg>
sub <reg>,<mem>
sub <mem>,<reg>
sub <reg>,<con>
sub <mem>,<con>
Examples
sub al, ah — AL ← AL - AH
sub eax, 216 — eax中的值减26,并将计算值保存在eax中

inc,dec

inc,dec分别表示将操作数自加1,自减1,语法如下:

inc <reg>
inc <mem>
dec <reg>
dec <mem>

Examples
dec eax — eax中的值自减1.
inc DWORD PTR [var] — var指示内存中的一个4-byte值自加1

imul

整数相乘指令,有两个语法格式:第一种为两个操作数,将两个操作数的值相乘,并将结果保存在第一个操作数中,第一个操作数必须为寄存器;第二种格式为三个操作数,语义为:将第二个和第三个操作数相乘,并将结果保存在第一个操作数中,第一个操作数必须为寄存器。语法如下:

imul <reg32>,<reg32>
imul <reg32>,<mem>
imul <reg32>,<reg32>,<con>
imul <reg32>,<mem>,<con>

Examples
imul eax, [var] — eax→ eax * [var]
imul esi, edi, 25 — ESI → EDI * 25

idiv

idiv指令完成整数除法操作,idiv只有一个操作数,此操作数为除数,而被除数则为EDX:EAX中的内容(一个64位的整数),操作的结果有两部分:商和余数,其中商放在eax寄存器中,而余数则放在edx寄存器中。其语法如下所示:

Syntax
idiv <reg32>
idiv <mem>

Examples

idiv ebx
idiv DWORD PTR [var]

and,or,xor

逻辑与、逻辑或、逻辑异或操作指令,用于操作数的位操作,操作结果放在第一个操作数中。其语法如下所示:

and <reg>,<reg>
and <reg>,<mem>
and <mem>,<reg>
and <reg>,<con>
and <mem>,<con>
or <reg>,<reg>
or <reg>,<mem>
or <mem>,<reg>
or <reg>,<con>
or <mem>,<con>
xor <reg>,<reg>
xor <reg>,<mem>
xor <mem>,<reg>
xor <reg>,<con>
xor <mem>,<con>

Examples
and eax, 0fH — 将eax中的钱28位全部置为0,最后4位保持不变.
xor edx, edx — 设置edx中的内容为0.

not

位翻转指令,将操作数中的每一位翻转,即0->1, 1->0。其语法如下所示:

not <reg>
not <mem>

Example
not BYTE PTR [var] — 将var指示的一个字节中的所有位翻转.

neg

取负指令。语法为:

neg <reg>
neg <mem>

Example
neg eax — EAX → - EAX

shl,shr

位移指令,有两个操作数,第一个操作数表示被操作数,第二个操作数指示位移的数量。其语法如下所示:

shl <reg>,<con8>
shl <mem>,<con8>
shl <reg>,<cl>
shl <mem>,<cl>

shr <reg>,<con8>
shr <mem>,<con8>
shr <reg>,<cl>
shr <mem>,<cl>

Examples shl eax, 1 — Multiply the value of EAX by 2 (if the most significant bit is 0),左移1位,相当于乘以2 shr ebx, cl — Store in EBX the floor of result of dividing the value of EBX by $2^n$ where n is the value in CL.

控制转移指令

x86处理器维持着一个指示当期那执行指令的指令指针(IP),当一条指令执行后,此指针自动指向下一条指令。IP寄存器不能直接操作,但是可以通过控制流指令更新。一般用标签(label)来指示程序中的指令地址,在x86汇编代码中,可以在任何指令前加入标签。

jump

控制转移到label所指示的地址,(从label中取出执行执行),如下所示:

jmp <label>

Example
jmp begin — Jump to the instruction labeled begin.

jcondition

条件转移指令,条件转移指令依据机器状态字中的一些列条件状态转移。机器状态字中包括指示最后一个算数运算结果是否为0,运算结果是否为负数等。机器状态字具体解释请见微机原理、计算机组成等课程。语法如下所示:

je <label> (jump when equal)
jne <label> (jump when not equal)
jz <label> (jump when last result was zero)
jg <label> (jump when greater than)
jge <label> (jump when greater than or equal to)
jl <label> (jump when less than)
jle <label>(jump when less than or equal to)

Example
cmp eax, ebx
jle done  , 如果eax中的值小于ebx中的值,跳转到done指示的区域执行,否则,执行下一条指令。

cmp

cmp指令比较两个操作数的值,并根据比较结果设置机器状态字中的条件码。此指令与sub指令类似,但是cmp不用将计算结果保存在操作数中。其语法如下所示:

cmp <reg>,<reg>
cmp <reg>,<mem>
cmp <mem>,<reg>
cmp <reg>,<con>
Example
cmp DWORD PTR [var], 10
jeq loop 比较var指示的4字节内容是否为10,如果不是,则继续执行下一条指令,否则,跳转到loop指示的指令开始执行

call,set

这两条指令实现子程序(过程、函数等意思)的调用及返回。call指令首先将当前执行指令地址入栈,然后无条件转移到由标签指示的指令。与其它简单的跳转指令不同,call指令保存调用之前的地址信息(当call指令结束后,返回到调用之前的地址)。 ret指令实现子程序的返回机制,ret指令弹出栈中保存的指令地址,然后无条件转移到保存的指令地址执行。 call,ret是函数调用中最关键的两条指令。具体细节见下面一部分的讲解。语法为:

call <label>
ret