为了迂腐,你用来直接与 CPU 对话的真正语言是机器码。这意味着找出必须用于某些指令的实际字节值。这显然太乏味且容易出错,因此人们改用汇编程序。汇编程序将机器代码的文本表示形式转换为机器代码本身,并处理各种繁琐的细节,例如计算相对地址等。
对于特定的机器代码,可以有许多不同的汇编程序,每个汇编程序都有自己的关于如何编写程序集的想法。对于 x86 处理器来说尤其如此——从广义上讲,有两种风格:英特尔和 AT&T。然后在其中,不同的汇编器可以有不同的宏和指令集等等。
为了说明,这里是从一些 C 代码生成的程序集示例gcc -S -masm=intel
:
cmp eax, ebx
jl .L63
mov eax, DWORD PTR inbuffd
mov DWORD PTR [esp+8], 8192
mov DWORD PTR [esp+4], OFFSET FLAT:inbuf
mov DWORD PTR [esp], eax
call read
cmp eax, -1
mov ebx, eax
mov DWORD PTR inbytes, eax
je .L64
test eax, eax
je .L36
mov eax, 1
xor edx, edx
jmp .L33
这是使用生成的相同代码段gcc -S -masm=att
:
cmpl %ebx, %eax
jl .L63
movl inbuffd, %eax
movl $8192, 8(%esp)
movl $inbuf, 4(%esp)
movl %eax, (%esp)
call read
cmpl $-1, %eax
movl %eax, %ebx
movl %eax, inbytes
je .L64
testl %eax, %eax
je .L36
movl $1, %eax
xorl %edx, %edx
jmp .L33
这两个片段产生相同的机器代码——不同之处仅在于汇编语法。特别注意参数的顺序是如何不同的(英特尔是目标优先,AT&T 是源优先),指令名称的细微差别,%
在 AT&T 中指定寄存器的使用等等。
然后是不同的 CPU。CPU具有一定的架构。这意味着它将执行该架构的指令集。对于该架构,将有一个核心指令集,可能还有用于增强功能或特殊应用的额外指令组。x86 就是一个很好的例子——你有浮点指令,MMx,3DNow!和 SSE 1 到 5。该架构的不同 CPU 可能会也可能不会理解额外的指令;通常有一些方法可以询问 CPU 它支持什么。
当您说“x86 程序集”时,人们理解您的意思是“将在 x86 架构的任何 CPU 上运行的程序集”。
更复杂的 CPU - 特别是那些具有内存管理功能的 CPU(包括 x86)不仅仅执行指令。从80286开始,x86 架构有两种主要模式——实模式和保护模式。核心指令集可以在任何一种模式下按原样使用,但内存在每种模式下的工作方式完全不同,以至于尝试编写可以在任何一种模式下工作的真实世界代码是不切实际的。
后来的 CPU 引入了更多模式。386 引入了虚拟 8086 模式又名 v86 模式,以允许保护模式操作系统运行实模式程序,而无需实际将 CPU 切换到实模式。AMD64 处理器以长模式运行 64 位代码。
一个 CPU 可以支持多种架构——安腾架构被认为是一个独立的架构,英特尔发布的所有支持安腾的 CPU 也都支持 x86,并且可以在它们之间切换。
x86 系列可能是汇编语言的一个过于复杂的例子——它的历史可以追溯到 33 多年前。(32 位)应用程序中使用的核心指令的机器代码与 1978 年发布的 8086 相同。经过多次修订,每次都添加了更多指令。
如果您想正确学习 x86 汇编,请考虑:
汇编语言编程的艺术,并有一个适用于 DOS、Windows 和 Linux 的版本。Windows 和 Linux 版本使用作者发明的一种称为 High Level Assembly 或 HLA 的语言,它有点像 x86 汇编,但不是真的。这可能是也可能不是你的一杯茶 - 它不是严格意义上的真正组装,但概念都在那里,之后学习编写正确的组装不会有太大的努力。值得称赞的是,它还包含大量与组装相关的材料,例如有关处理器架构、BIOS、视频等的信息。DOS 版本教授直接 MASM(英特尔)组装。
从头开始编程在 Linux 中教授 AT&T 风格的汇编
对于实际的汇编程序(免费的),请在 Windows 或Linux上尝试MASM32 (intel 风格)。as
碰巧的是,Linuxas
将组装 Intel 或 AT&T 样式的程序集。
如果您对 x86 架构感到 畏惧,并且愿意为其他架构学习汇编,请考虑从较小的.