回为大家简单介绍了 Visual C++ Inline Assembly,相信已经有人想实际动手来试试了。然而,要想自由使用 Inline Assembly,你首先必须掌握 INTEL X86 体系的 32 位汇编语言。本文正是为那些已经略有 8086 汇编语言基础却没接触过 X86 体系的 32 位汇编语言的同志们准备的。我们将一起了解和深入 INTEL X86 体系的 32 位汇编语言。
因为我们的目标是“速成”,如果你能有点基础的话,那么在此之上展开讨论就能让彼此都感觉轻松很多。假若你以前完全没有学习过汇编语言,那么请务必先去找本 8086 汇编语言的教科书来补习补习之后再来阅读本文。
学习一种的汇编语言,必须了解这种 CPU 的寄存器、寻址方式以及各种指令。我们就先从寄存器开始着手吧。
g
INTEL X86 常用寄存器
通用寄存器 段寄存器
AH/AL AX (EAX) 累加器 CS 代码段
BH/BL BX (EBX) 基址 DS 数据段
CH/CL CX (ECX) 计数器 SS 堆栈段
DH/DL DX (EDX) 数据 ES 附加段
(FS) 386 新增的段寄存器
(Exx) 为 386 新增的 32 位寄存器 (GS) 386 新增的段寄存器
指针寄存器 堆栈寄存器
SI (ESI) 源索引指针 SP (ESP) 栈指针
DI (EDI) 目的索引指针 BP (EBP) 基址指针
IP 指令指针
状态寄存器
|11|10|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|
| | | | | | | | | | | | | | | | | +— CF Carry Flag
| | | | | | | | | | | | | | | | +— 1
| | | | | | | | | | | | | | | +— PF Parity Flag
| | | | | | | | | | | | | | +— 0
| | | | | | | | | | | | | +— AF Auxiliary Flag
| | | | | | | | | | | | +— 0
| | | | | | | | | | | +— ZF Zero Flag
| | | | | | | | | | +— SF Sign Flag
| | | | | | | | | +— TF Trap Flag (Single Step)
| | | | | | | | +— IF Interrupt Flag
| | | | | | | +— DF Direction Flag
| | | | | | +— OF Overflow flag
| | | | +—– IOPL I/O Privilege Level (286+ only)
| | | +—– NT Nested Task Flag (286+ only)
| | +—– 0
| +—– RF Resume Flag (386+ only)
+—— VM Virtual Mode Flag (386+ only)
怎么样,看起来大半部分都应该是我们以前很熟的了吧。现在,我们只需要侃侃那些在 386 上才
开始出现的新的寄存器就行了。
首先必须强调的是,在用 32 位汇编语言编程的时候,所有的地址偏移量都是 32 位的,在寻址时
千万不要还用原来的 16 位方式。
对于通用寄存器来说,多了种形如 (Exx) 的 32 位寄存器,它的低 16 位内容就是原来的 16 位寄
存器,而多出的高 16 位的内容,则只能通过使用 32 位寄存器来访问。
再以指针寄存器为例,在寻址时一定要用 ESI、EDI、EBP 等等,必须要把以前那种 mov ax,[si] 之
类的指令改为 mov ax,[ESI]。
从 386 开始,多出了 FS、GS 这两个新的段寄存器。由于我们学习的目的是为了今后写在线汇编,所以很多繁琐的问题都不会直接遇到。为了能更快地投入实际运用,这里就不打算去讲述保护模式的细节了,而直接给大家提出一个结论,你只管按照这个结论去做就行了。
这个简单的结论就是:你在 VC 中写在线汇编时,尽量不要去碰段寄存器!
得出这个结论的第一个原因是 VC 生成的应用程序的 DS、ES 和 SS 是相同的。换种说法,整个应用程序的数据段、附加段、堆栈段都在同一个地址,你根本就没有去改变它们的必要。第二个原因是,因为是保护模式,每个段都有 4GB 大小,所有数据都可以轻易地放进去,于是当然就用不着去改段寄存器了。最后一个原因是,保护模式下的段寄存器使用方法同实模式下完全不一样,在你没弄懂以前,最好别去乱改,否则……嘿嘿,死掉了别怨我。
至于状态寄存器,大家肯定是非常熟悉了,虽然多了几位,但这些内容我们一般都用不着,所以也可以略过。
下面将开始讲述 INTEL X86 的 32 位偏移地址构成方式。
这里给出关于 80386 寻址模式的一张列表:
基地址 + (变址 X 比例因子) + 偏移量
|无 | |无 |
|EAX| |EAX|
|EBX| |EBX| |1|
|ECX| |ECX| |2| | 无 |
|EDX| + |EDX| X |4| + | 8位|
|ESP| |—| |8| |32位|
|EBP| |EBP|
|ESI| |ESI|
|EDI| |EDI|
其中,“—”表示 ESP 不能被用作变址寄存器
注意到比例因子,这个可是从 386 才开始加入的好东西,它对处理表结构大有好处,而且还衍生出了不少技巧,详情请参考《图形程序开发人员指南 (Michael Abrash’s Graphics Programming Black Book
Special Edition)》一书第 6.3 节。
在 80386 寻址时,其默认的段寄存器取决于所选择的基地址寄存器。如果基地址寄存器是 ESP 或
者 EBP,则默认的段寄存器是 SS。对于别的基地址寄存器的选择,包括无基地址寄存器,DS 仍然是其默认的段寄存器。其实,前面已经说了,你在 VC 中写在线汇编时,可以不去碰段寄存器,但反正已经讲到这里了,所以随便就提两句。
其对应关系见下表:
基地址寄存器 默认的段寄存器
BP or SP SS
SI or DI DS
DI strings ES
SI strings DS
了解了寄存器和寻址方式以后,我们就可以使用过去已经知道的指令开始编程了。但 INTEL 在 386 以后新增了不少指令,虽然这里我不可能全部列出来,但可以稍微看看其中比较常用的几条。
BSWAP – 字节交换 (486+)
颠倒 32 位寄存器的字节排列顺序。
CDQ – 双字转换成四字 (386+)
把 EAX 中的符号数按符号扩展为 EDX:EAX,即把 EAX 中的第 32 位赋予 EDX 的每一位。
CWDE – 将字扩展转换成双字 (386+)
把 AX 中的符号数按符号扩展为 EAX,即把 AX 中的第 16 位赋予 EAX 的高 16 位。
MOVSX – 符号扩展传送 (386+)
传送数据时先作符号扩展。
MOVZX – 零扩展传送 (386+)
传送数据时先作零扩展。
SHLD/SHRD – 双精度移位 (386+)
把源操作数中的若干位移入目的操作数,源操作数中的内容保持不变。
POPA/POPAD – 所有通用寄存器出栈 (80188+)
PUSHA/PUSHAD – 所有通用寄存器入栈 (80188+)
怎么样?感觉这些指令挺爽的吧,其实还有很多的好东西(比如各种位处理指令等等)没能在这里提到。更完整和详细的资料请查阅『INTEL 汇编指令集』。
到此为止,该了解的基础知识我们差不多都已经全部提到了,很简单不是?剩下的就是该你自己去多多编程实践了。只有这样,你才能获取宝贵的经验,从而真正地掌握这门汇编语言。
- 本文固定链接: http://www.wy182000.com/2009/02/17/intel-x86-体系-32-位汇编语言速成/
- 转载请注明: wy182000 于 Studio 发表