指令目的是什么?

7

les指令在汇编语言中的作用是什么?

为什么我们需要加载es一个寄存器?书中给出了以下示例:

les    bx, p           ; Load p into ES:BX
mov    es:[bx], al     ; Store away AL

为什么在这种情况下需要加载 es bx

还有,为什么要使用 es:[bx]?如果 p 指向内存中的 100h,那么 esbx 都不是 100h,而是 200h(bx+es)吗?

3个回答

10
很遗憾,您正在学习一个架构混乱的微处理器的汇编语言。这会让您接触到一些令人困惑的概念,比如LES指令。
传统的微处理器有足够大的寄存器来容纳完整的内存地址。您只需将内存位置的地址加载到寄存器中,然后通过寄存器访问该位置(通常使用索引还可以访问附近的位置)。
某些机器(特别是实模式下的Intel 286,似乎是您正在编程的),只有16位寄存器,但可以寻址1MB的内存。在这种情况下,一个寄存器没有足够的位数:您需要20位,但寄存器只有16位。
解决方案是有第二个寄存器来包含缺失的位。一个简单的方案是需要2个寄存器,其中一个具有低16位,另一个具有高16位,以产生32位地址。然后引用两个寄存器的指令就有意义了:您需要两个寄存器才能获得完整的内存地址。

英特尔选择了一个更混乱的segment:offset方案:普通寄存器(在您的情况下为bx)包含低16位(偏移量),而特殊寄存器(称为ES)包含左移4位的16位,加到偏移量上,以获得结果线性地址。 ES被称为“段”寄存器,但是如果您不去阅读1968年左右的Multics操作系统,这将毫无意义。

(x86允许对内存地址的“有效地址”或“偏移量”部分使用其他寻址模式,例如es:[bx + si + 1234],但始终只有一个段寄存器用于内存地址。)

[在Multics中,段和段寄存器的实现方式确实是一个有趣的想法。如果你不知道这是什么,并且对计算机和/或信息架构有任何兴趣,请找到Elliot Organick关于Multics的书并从头到尾阅读。你会对我们在60年代末拥有的东西感到沮丧,并似乎在50年的“进步”中失去了它。如果你想更详细地讨论这个问题,请参见我对FS和GS段寄存器目的的讨论]
[x86中这个想法剩下的部分在“现代”操作系统中使用的方式基本上是一个笑话。你并不真正关心;当一些硬件设计师向你展示一台机器时,你必须接受它的现状。]
对于Intel 286,您只需加载一个段寄存器和一个索引寄存器即可获得完整的地址。每个机器指令必须引用一个索引寄存器和一个段寄存器,以便形成完整的地址。对于Intel 286,有4个这样的段寄存器:DS、SS、ES和CS。每个指令类型都显式地指定一个索引寄存器,并隐含地选择其中的一个段寄存器,除非您提供一个明确的覆盖,说明要使用哪一个。JMP指令使用CS,除非您另有说明。MOV指令使用DS,除非您另有说明。PUSH指令使用SS,除非您另有说明(在这种情况下最好不要)。ES是“额外”的段;您只能通过在指令中明确引用它来使用它(除了块移动[MOVB]指令,它隐含地使用DS和ES)。
希望这有所帮助。
最好使用更现代的微处理器进行工作,这样段寄存器的愚蠢就不是问题了。(例如,32位模式x86,其中主流操作系统使用平面内存模型,所有段基址= 0。因此,您可以忽略分段,并将单个寄存器作为指针,只关心地址的“偏移”部分。)

你的答案“大部分”是正确的。然而,所有现代x86处理器都使用段寄存器。即使在大多数是平坦的64位模式中,仍然有GS和FS不是平坦的。如果说有什么区别的话,这种段寄存器的荒唐比你在帖子中描述的要复杂得多。 - Nathan Fellman
3
是的,他们确实这样做,但OP不需要听到这个复杂情况。当前使用的(x64)也只是真正段寄存器的最基本痕迹。很遗憾,可以参考Multics。安迪·格鲁夫在80年代中期的一次演讲中大发雷霆...英特尔设计了386段寄存器真正做Multics的功能,但被Unix爱好者忽视了。我们应该接受什么就得到了什么。 - Ira Baxter
2
非常感谢!“你需要20位,但是寄存器只有16位。”我完全忘记了我正在使用16位的CPU! - tina nyaa
1
三个小问题:1)这里是8086;2)隐式段寄存器取决于操作数,例如SI获取DS,DI获取ES;3)8086(实模式)段与Multics无关(它们只用于寻址高达1MB的内存而不需要分段),您可能在考虑286(保护模式)段。 - ninjalj
1
无论段寄存器如何工作,它们都会影响逻辑地址映射到物理内存的方式,并控制访问是否合法。事实上,8086段寄存器非常简单,没有与之关联的实际保护位,这使它们成为Multics和最终的32位Intel CPU版本的极其原始的版本。英特尔能够意识到这一点是他们的天才之处;而世界其他地方太愚蠢以至于无法理解这一点则是纯粹的愚蠢行为。因此,我们得到了扁平地址空间的“Eunuchs”,而不是Multics。咳。 - Ira Baxter
一些机器(特别是您正在编程的英特尔286),只有16位寄存器,但可以寻址1MB的内存。实际上,286在保护模式下可以寻址高达16 MiB的内存。在该模式下,段寄存器的值不直接用于计算基地址。只有在实地址模式下(始终在8086/186上)段寄存器“包含16位,这些位左移4位”以形成段基址。 - ecm

6
8086段寄存器cs、ds、es和ss是16位寄存器可以寻址超过64K内存的原始机制。在8086/8088中,需要产生20位地址(1024K)。x86处理器的后续版本添加了新方案以寻址更多内存,但从一对16位值生成20+位地址是基本原因。
在所谓的“实模式”(本地化于8086/8088/80186)中,地址通过将段寄存器的内容乘以16(或等效地向左移动四个位置)并添加偏移量来计算。
在保护模式(适用于80286及更高版本),段寄存器选择包含基本物理地址的“描述符”。例如,操作数es:[bx]将bx加到该物理地址上以生成操作数地址。

保护模式是在286时引入的。 - ecm

3

p指向一个32位的FAR指针,具有段和偏移部分(与仅为偏移部分的NEAR指针相对)。 LES将加载段:偏移量到ES:BX中。

否则,您将需要使用三个指令。 一个用于加载BX,两个用于加载ES(段寄存器不能直接从内存中加载,必须先加载到通用寄存器中,然后再加载到段寄存器中)。

哦,是的,wallyk提到了保护模式是一个好观点(尽管这与您的问题无关)。 在这里,ES将被解释为选择器,而不是实际的段。

在此上下文中,段(地址)是物理地址的一部分:
将段左移4位(即乘以2^4 = 16),然后加上偏移量,以从段:偏移量获取物理地址。

相比之下,选择器是指向所谓的描述符表中条目的指针(即选择器指向描述符),并且在保护模式下使用。 描述符表(例如GDT)可能包含有关内存块的信息条目,包括有关物理内存地址,块大小,访问权限等的信息(还有一些稍微不同的用法)。


3
“segment registers cannot be loaded directly from memory”这句话是不正确的。你可以像这样使用mov es, word [1234h]进行加载。唯一的限制是你不能在计算中使用段寄存器(如incaddand等指令),也不能在嵌入指令的立即值中加载(如mov es,0ABCDh)。 - ecm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接