在学校,我们用 MIPS 汇编语言编程已经有一段时间了。我有兴趣深入研究 x86 程序集,我听说这有点难(甚至我的 MIPS 教科书也这么说)。
作为一名 MIPS 程序员,在进入 x86 世界之前,我应该了解哪些核心信息?
要记住的最重要的事情是:
除此之外,x86 非常简单。当您学会滥用诸如“lea”和“test”之类的指令时,您就会学会喜欢它。另外,提示:英特尔将免费向您发送指令集手册的副本,甚至不必支付运费。在他们的网站上查看履行电子邮件并按 SKU 索取书籍。
与大多数其他架构相比,x86 的可用寄存器集非常有限。这并没有真正使汇编语言更难学习,但有时会使在实践中实现代码变得更加困难。
此外,由于 x86 具有强大的向后兼容性的历史,指令集并不是非常对称(绝对是 RISC 之前的版本),并且可能存在许多规则例外情况和需要注意的极端情况。
我一直在学习 x86 和 x86_64 自己编写汇编程序。如果您不打算自己编写汇编程序,那么我将讲述的一些内容几乎毫无用处。不过,我自己并不了解 MIPS。
x86 间接寻址是一件复杂的事情。在一条指令中,您可以执行以下操作:
mov reg, [reg+offset]
mov reg, [reg*scale+base register+offset] # in where scale can be 1, 2, 4 or 8.
因此,它们的指令编码很复杂,但是对于以这种方式编码的每条指令来说都是一致的。您可能想从sandpile.org阅读此内容。如果您想了解有关编码的更多信息,可以随时向我询问。另一个指令编码相关的烦人细节是前缀。它们极大地改变了指令的含义。例如,前面的 0x66(如果我没记错的话)和一些指令变为 16 位 GPR 而不是 32 位 GPR。
32 位 GPR(按顺序):eax、ecx、edx、ebx、esp、ebp、esi、edi
64 位 GPR:rax、rcx、rdx、rbx、rsp、rbp、rsi、rdi、r8、r9、r10、r11、r12、r13、r14、r15
注意通用寄存器有多么少,这将迫使大多数软件或多或少地以堆栈机器方式使用它。一个痛苦的细节。rsp 用于堆栈(pop、push 指令),而 rbp 也倾向于保留。x86_64 有更多的寄存器,但人们采用它需要时间,即使每个消费者都有一个能够支持它的处理器。
浮点运算有两种不同的指令集。XMM 是较新的。在 x86_64 中有 16 个 128 位寄存器可用,而在 x86 中只有 8 个。较旧的指令集将寄存器作为堆栈处理。你只是没有交换、咬合或腐烂,所以使用它是令人费解的。
在使用中,x86 趋向于简化为 RISC 机器。其中一些复杂的指令在较新的机器上没有好处,甚至速度更慢。根据您阅读或写作的内容,您将了解大约 30-150 条指令。您也可以完全忽略一些旧指令和 AL/HL -stuff。请记住,这一切都是 1978 年之后的混乱起源,令人惊讶的是,它并没有更糟,从那以后 31 年,距离 IA-32 首次引入还有 24 年。很多事情在那段时间改变了它们的相关性。
直接跳转和调用似乎与 x86 中的下一条指令相关。所以:
jmp nowhere # or call, jz, jg whatever...
nowhere:
nop
最终编码为“JMP imm:0, NOP”。确实执行绝对跳转的寄存器间接 jmp。注意到没有寄存器间接条件跳转也很好,它也困扰着我。
这不是你应该知道的所有可能的事情,而是你的问题让我想到的第一件事。但也许你现在可以和这些相处。
x86 的指令比 MIPS 更复杂。因此,MIPS 中的常见序列可能只有一条指令(最值得注意的是内存寻址)。缺少大量寄存器当然是一个缺点,但在这两种架构中都有一些约定,几乎将您可以自由使用的数量限制在 4-5 个。在 x86 中更明显。x86 比 MIPS 有更多的寄存器使用例外,你必须记住,但没有什么值得不断抱怨的。
从经验来看,任何一种语言的学习难度都差不多,包括约定。考虑到丰富的在线资源及其受欢迎程度,也许 x86 更容易一些。
x86 的难点在于生成二进制文件,因为它具有可变长度的指令和多种寻址模式。大多数情况下,无论如何您都不需要这样做。
我当然可以推荐你学习比 MIPS 更复杂的指令架构。
而且,这很重要,不要参与 RISC 与 CISC 之间的宗教战争……