汇编语言(零)——基本概念

0.相关背景

这里讨论的是与运行 Microsoft Windows 32 位和 64 位系统的 Intel 和 AMD 处理器相兼容的微处理器编程。教程中使用了 Microsoft 宏汇编器(称为 MASM)的最新版本。Microsoft Visual Studio 的大多数版本(专业版,旗舰版,精简版……)都包含 MASM。在运行 Microsoft Windows 的 x86 系统中,其他一些有名的汇编器包括:TASM(Turbo 汇编器),NASM(Netwide 汇编器)和 MASM32(MASM 的一种变体)。GAS(GNU 汇编器)和 NASM 是两种基于 Linux 的汇编器。在这些汇编器中,NASM 的语法与 MASM 的最相似。汇编语言是最古老的编程语言,在所有的语言中,它与原生机器语言最为接近。它能直接访问计算机硬件,要求用户了解计算机架构和操作系统。如果小伙伴计划成为 C 或 C++ 开发者,就需要理解内存、地址和指令是如何在底层工作的。在高级语言层次上,很多编程错误不容易被识别。因此,程序员经常会发现需要“深入”到程序内部,才能找出程序不工作的原因。

汇编器(assembler)是一种工具程序,用于将汇编语言源程序转换为机器语言。链接器(linker)也是一种工具程序,它把汇编器生成的单个文件组合为一个可执行程序。还有一个相关的工具,称为调试器(debugger),使程序员可以在程序运行时,单步执行程序并检查寄存器和内存状态。

MASM 能创建32 位保护模式(32-Bit Protected Mode):32 位保护模式程序运行于所有的 32 位和 64 位版本的 Microsoft Windows 系统。它们通常比实模式程序更容易编写和理解。从现在开始,将其简称为 32 位模式。64 位模式(64-Bit Mode):64 位程序运行于所有的 64 位版本 Microsoft Windows 系统。16 位实地址模式(16-Bit Real-Address Mode):16 位程序运行于 32 位版本 Windows 和嵌入式系统。 64 位 Windows 不支持这类程序。

机器语言(machine language)是一种数字语言, 专门设计成能被计算机处理器(CPU)理解。所有 x86 处理器都理解共同的机器语言。汇编语言(assembly language)包含用短助记符如 ADD、MOV、SUB 和 CALL 书写的语句。汇编语言与机器语言是一对一(one-to-one)的关系:每一条汇编语言指令对应一条机器语言指令。高级语言如 Python、C++ 和 Java 与汇编语言和机器语言的关系是一对多(one-to-many)。比如,C++ 的一条语句就会扩展为多条汇编指令或机器指令。大多数人无法阅读原始机器代码,因此,这里探讨的是与之最接近的汇编语言。例如,下面的 C++ 代码进行了两个算术操作,并将结果赋给一个变量。假设 X 和 Y 是 整数:

int Y;
int X = ( Y + 4 ) * 3;

与之等价的汇编语言程序如下所示。这种转换需要多条语句,因为每条汇编语句只对应一条机器指令:

mov eax,Y  ;Y 送入 EAX 寄存器
add eax,4  ;EAX 寄存器内容加 4
mov ebx,3  ;3 送入 EBX 寄存器
imul ebx   ;EAX 与 EBX 相乘
mov x,eax  ;EAX 的值送入 X

寄存器(register)是 CPU 中被命名的存储位置,用于保存操作的中间结果。这个例子的重点不是说明 C++ 与汇编语言哪个更好,而是展示它们的关系。一种语言,如果它的源程序能够在各种各样的计算机系统中进行编译和运行,那么这种语言被称为是可移植的(portable)。例如,一个 C++ 程序,除非需要特别引用某种操作系统的库函数,否则它就几乎可以在任何一台计算机上编译和运行。Java 语言的一大特点就是,其编译好的程序几乎能在所有计算机系统中运行。汇编语言不是可移植的,因为它是为特定处理器系列设计的。目前广泛使用的有多种不同的汇编语言,每一种都基于一个处理器系列。对于一些广为人知的处理器系列如 Motorola 68x00、x86、SUN Sparc、Vax 和 IBM-370,汇编语言指令会直接与该计算机体系结构相匹配,或者在执行时用一种被称为微代码解释器(microcode interpreter)的处理器内置程序来进行转换。

大多数汇编语言规则都是以目标处理器及其机器语言的物理局限性为基础的。比如,CPU 要求两个指令操作数的大小相同。与C++ 或 Java 相比,汇编语言的规则较少,因为,前者是用语法规则来减少意外的逻辑错误,而这是以限制底层。汇编语言程序员可以很容易地绕过高级语言的限制性特征。例如,Java 就不允许访问特定的内存地址。程序员可以使用 JNI(Java Native Interface)类来调用 C 函数绕过这个限制,可结果程序不容易维护。反之,汇编语言可以访问所有的内存地址。但这种自由的代价也很高:汇编语言程序员需要花费大量的时间进行调试。

1.应用

早期在编程时,大多数应用程序部分或全部用汇编语言编写。它们不得不适应小内存,并尽可能在慢速处理器上有效运行。随着内存容量越来越大,以及处理器速度急速提高,程序变得越来越复杂。程序员也转向高级语言如 C语言、FORTRAN COBOL,这些语言具有很多结构化能力。最近,PythonC++C# 和 Java 等面向对象语言已经能够编写含数百万行代码的复杂程序了。很少能看到完全用汇编语言编写的大型应用程序,因为它们需要花费大量的时间进行编写和维护。不过,汇编语言可以用于优化应用程序的部分代码来提升速度,或用于访问计算机硬件。下表比较了汇编语言和高级语言对各种应用类型的适应性。
 

应用类型 高级语言 汇编语言
商业或科学应用程序,为单一的中型或大型平台编写 规范结构使其易于组织和维护大量代码 最小规范结构,因此必须由具有不同程度经验的程序员来维护结构。这导致对已有代码的维护困难
硬件设备驱动程序 语言不一定提供对硬件的直接访问。 即使提供了,可能也需要难以控制的编码技术,这导致维护困难 对硬件的访问直接且简单。当程序较短且文档良好时易于维护
为多个平台(不同的操作系统)编写的商业或科学应用程序 通常可移植。在每个目标操作系统上, 源程序只做少量修改就能重新编译 需要为每个平台单独重新编写代码, 每个汇编器都使用不同的语法。维护困难
需要直接访问硬件的嵌入式系统和电脑游戏 可能生成很大的可执行文件,以至于超出设备的内存容量 理想,因为可执行代码小,运行速度快

C 和 C++ 语言具有一个独特的特性,能够在高级结构和底层细节之间进行平衡。直接访问硬件是可能的,但是完全不可移植。大多数 C 和 C++ 编译器都允许在其代码中嵌入汇编语句,以提供对硬件细节的访问。

2.数据

汇编语言程序员处理的是物理级数据,因此他们必须善于检查内存和寄存器。通常,二进制数被用于描述计算机内存的内容;有时也使用十进制和十六进制数。所以必须熟练掌握数字格式,以便快速地进行数字的格式转换。每一种数制格式或系统,都有一个基数(base),也就是可以分配给单一数字的最大符号数。下表给岀了数制系统内可能的数字,这些系统是硬件和软件手册中最常使用的。

系统 基数 可能的数字
二进制 2 01
八进制 8 01234567
十进制 10 0123456789
十六进制 16 0123456789ABCDEF

在表的最后一行,十六进制使用的是数字 0 到 9,然后字母 A 到 F 表示十进制数 10 到 15。在展示计算机内存的内容和机器级指令时,使用十六进制是相当常见的。

3.布尔表达式(NOT、AND、OR)

一个布尔表达式(boolean expression)包括一个布尔运算符以及一个或多个操作数。每个布尔表达式都意味着一个为真或假的值。以下为运算符集合:

  • 非(NOT):标记为 ¬ 或 ~ 或 '
  • 与(AND):标记为^或 ·
  • 或(OR):标记为 ∨ 或 +
发布了363 篇原创文章 · 获赞 32 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_35789421/article/details/104113250