为什么没有包含更高字节EAX的寄存器?

时间:2008-10-23 01:31:10

标签: x86 assembly

%AX = (%AH + %AL)

那么为什么%EAX = (%SOME_REGISTER + %AX)注册%SOME_REGISTER

3 个答案:

答案 0 :(得分:130)

只是为了澄清一下。在1970年代早期的微处理器时代,CPU只有少量的寄存器和非常有限的指令集。通常,算术单元只能在单个CPU寄存器上操作,通常称为“累加器”。 8位8080& 8上的累加器Z80处理器被称为“A”。还有6个其他通用8位寄存器:B,C,D,E,H&这六个寄存器可以配对形成3个16位寄存器:BC,DE& HL。在内部,累加器与Flags寄存器组合在一起形成AF 16位寄存器。

当英特尔开发出16位8086系列时,他们希望能够移植8080代码,因此它们保持相同的基本寄存器结构:

8080/Z80  8086
A         AX
BC        BX
DE        CX
HL        DX
IX        SI    
IY        DI

由于需要移植8位代码,因此需要能够参考AX,BX,CX和CD的各个8位部分。 DX。这些被称为AL,AH为低和低。 BL / BH,CL / CH&的高字节AX等等DL / DH。 IX& Z80上的IY仅用作16位指针寄存器,因此无需访问SI和EX的两半。 DI。

当80386在20世纪80年代中期发布时,他们创建了所有寄存器的“扩展”版本。因此,AX成为EAX,BX成为EBX等。不需要访问这些新扩展寄存器的前16位,因此它们没有创建EAXH伪寄存器。

AMD在生产第一批64位处理器时应用了相同的技巧。 AX寄存器的64位版本称为RAX。所以,现在你有了这样的东西:

|63..32|31..16|15-8|7-0|
               |AH.|AL.|
               |AX.....|
       |EAX............|
|RAX...................|

答案 1 :(得分:27)

这里有很多答案,但没有一个真正回答给定的问题:为什么没有一个寄存器直接编码高16位的EAX或高32位的RAX?答案归结为x86指令编码本身的局限性。

16位历史课

当英特尔设计8086时,他们对许多指令使用了可变长度编码方案。这意味着某些非常常见的指令(如POP AX)可以表示为单个字节(58),而MOV CX, [BX*4+BP+1023]之类的罕见(但仍然可能有用)指令仍然可以表示,即使它花了几个字节来存储它们(在这个例子中,8B 8C FF 03)。

这似乎是一个合理的解决方案,但是当他们设计它时,他们填写了大部分可用空间。因此,例如,八个单独寄存器(AX,CX,DX,BX,SP,BP,SI,DI)有八条POP指令,它们填写了操作码58到5F,操作码60是完全不同的东西(PUSHA),操作码57(PUSH DI)也是如此。在那之后或之前没有任何余地留下任何余地。甚至推送和弹出段寄存器 - 这在概念上几乎与推送和弹出通用寄存器完全相同 - 必须在不同的位置编码(低于06 / 0E / 16 / 1E),因为它没有其余推/弹指示旁边的房间。

同样," mod r / m"用于复杂指令的字节如MOV CX, [BX*4+BP+1023]只有三位用于编码寄存器,这意味着它总共只能代表8个寄存器。如果您只有8个寄存器,这很好,但如果您想要更多寄存器则会出现真正的问题。

(这里有x86架构中所有这些字节分配的优秀地图:http://i.imgur.com/xfeWv.png。注意主映射中没有剩余空间,一些指令重叠字节,由于MMX和SSE指令,现在使用了多少次级" 0F"地图。)

走向32位和64位

因此,为了甚至允许CPU设计从16位扩展到32位,它们已经存在设计问题,并且他们用前缀字节解决了这个问题:通过添加特殊的" 66"在所有标准16位指令前面的字节,CPU知道你想要相同的指令但是32位版本(EAX)而不是16位版本(AX)。设计的其余部分保持不变:整体CPU架构中仍然只有8个通用寄存器。

必须采取类似的措施将架构扩展到64位(RAX和朋友);在那里,通过添加另一组前缀代码(REX,40-4F)来解决问题,这意味着" 64位" (并且有效地将另外两位添加到" mod r / m"字段),并且还丢弃了没有人使用过的奇怪的旧指令,并将其字节代码重新用于更新的东西。

8位寄存器旁边

要问的一个更大的问题是,如果只有8个寄存器的设计真正有空间,那么像AH和AL这样的东西是如何起作用的。答案的第一部分是没有" PUSH AL" - 一些指令根本无法对字节大小的寄存器进行操作!唯一可以是一些特殊的怪物(如AADXLAT)和特殊版本的" mod r / m"说明:通过在" mod r / m"中翻转一个非常具体的位。字节,那些"扩展指令"可以翻转以操作8位寄存器而不是16位寄存器。恰好恰好有8个8位寄存器:AL,CL,DL,BL,AH,CH,DH和BH(按此顺序),并且与8个可用的寄存器插槽非常吻合在" mod r / m"字节。

英特尔当时指出,8086的设计应该是"源兼容"使用8080/8085:8086中的每条8080/8085指令都有一条等效指令,但它没有使用相同的字节代码(它们甚至没有关闭),而你&#39 ; d必须重新编译(重新组装)您的程序,以使其使用新的字节代码。但是"源兼容"这是旧软件的前进方向,它允许8085的个人A,B,C等以及组合" BC"和" DE"注册仍然可以在新处理器上工作,即使它们现在被称为" AL"和" BL"和" BX"和" DX" (或任何映射)。

这真的是真正的答案:并非英特尔或AMD故意"遗漏"用于EAX的高16位寄存器或用于RAX的高32位寄存器:高8位寄存器是一个奇怪的剩余历史异常,并且以更高的位大小复制它们的设计将非常困难鉴于要求架构向后兼容。

绩效考虑

还有另外一个考虑因为那些"高寄存器"从那以后也被添加了:现代处理器架构内部,出于性能原因,可变大小的寄存器实际上并不重叠:AH和AL不是AX和AX的一部分不是EAX的一部分,EAX不是RAX的一部分:它们是引擎盖下的所有独立寄存器,当你操纵其中一个时,处理器会在其他寄存器上设置一个无效标志,以便它知道当你从别人那里读取数据时需要复制数据。

(例如:如果设置AL = 5,则处理器不会更新AX。但是如果您从AX读取,则处理器会快速将AL中的5个复制到AX的底部位。)

通过保持寄存器分离,CPU可以执行各种聪明的操作,例如隐形寄存器重命名,以使代码运行得更快,但这意味着如果使用旧代码,代码运行较慢将小寄存器视为较大寄存器的一部分的模式,因为处理器必须停止并更新它们。为了防止所有内部簿记失控,CPU设计人员明智地选择在较新的处理器上添加单独的寄存器,而不是添加更多重叠的寄存器。

(是的,这意味着在现代处理器上明确地#34; MOVZX EAX, value"实际上要快于#34; {{1}的旧的,更加笨拙的方式}"。)

<强>结论

尽管如此,英特尔和AMD可以添加更多&#34;重叠&#34;注册,如果他们真的真的想?当然。如果有足够的需求,有办法让他们陷入困境。但鉴于重要的历史包袱,当前的架构限制,显着的性能限制,以及这些天大多数代码都是由针对非重叠寄存器优化的编译器生成的事实,它们极不可能添加这样的事情很快就会发生。

答案 2 :(得分:25)

在旧的8位日里,有一个A寄存器。

在16位时间内,有16位AX寄存器,当你还想使用8位值的时候,它被分成两个8位部分AH和AL。

在32位时间内,引入了32位EAX寄存器,但是AX,AH和AL寄存器都被保留了。设计人员认为没有必要引入一个新的16位寄存器来寻址EAX的16到31位。