【问题标题】:Les instruction purpose?Les 指令的目的是什么?
【发布时间】:2011-11-12 17:00:22
【问题描述】:

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)吗?

【问题讨论】:

    标签: assembly x86 masm


    【解决方案1】:

    太糟糕了,你正在为一个架构混乱的微处理器学习汇编程序。你会得到一些令人困惑的概念,例如 LES 指令。

    传统的微处理器有足够大的寄存器来包含一个完整的内存地址。您可以简单地将内存位置的地址加载到寄存器中,然后通过寄存器访问该位置(通常是附近有索引的位置)。

    一些机器(尤其是实模式下的 Intel 286,这似乎是您正在编程的)只有 16 位寄存器,但可以寻址 1MB 内存。在这种情况下,寄存器没有足够的位:您需要 20 位,但寄存器只有 16 位。

    解决方案是使用包含缺失位的第二个寄存器。一个简单的方案是需要 2 个寄存器,其中一个具有低 16 位,其中一个具有高 16 位,以产生 32 位地址。那么引用两个寄存器的指令就有意义了:你需要两个寄存器来获得一个完整的内存地址。

    英特尔选择了一个更混乱的segment:offset 方案:普通寄存器(在您的情况下为 bx)包含低 16 位(偏移量),特殊寄存器(称为 ES)包含 16 位,左移 4 位,并添加到偏移量,以获得生成的线性地址。 ES 被称为“段”寄存器,但除非您阅读有关 Multics operating system circa 1968 的信息,否则这将毫无意义。

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

    [完全实现 Multics 方式时,段和段寄存器确实是一个有趣的想法。如果您不知道这是什么,并且您对计算机和/或信息架构有任何兴趣,请查找 Elliot Organick 关于 Multics 的书并从头到尾阅读。您会对我们在 60 年代后期所拥有的东西感到沮丧,并且似乎在 50 年的“进步”中失去了。如果您想对此进行更长时间的讨论,请参阅my discussion on the purpose of FS and GS segment registers]

    x86 中剩下的想法几乎是个笑话,至少它在“现代”操作系统中使用的方式是这样。你真的不在乎;当某些硬件设计师向您展示一台机器时,您必须接受它。

    对于 Intel 286,您只需加载段寄存器和索引寄存器即可获得完整地址。每个机器指令必须引用一个索引寄存器和一个段寄存器才能形成完整的地址。对于 Intel 286,有 4 个这样的段寄存器:DS、SS、ES 和 CS。每种指令类型都明确指定一个变址寄存器并隐式选择 4 个段寄存器之一,除非您提供明确的覆盖来说明要使用哪一个。除非您另有说明,否则 JMP 指令使用 CS。除非您另有说明,否则 MOV 指令使用 DS。 PUSH 指令使用 SS,除非你另有说明(在这种情况下你最好不要)。 ES 是“额外”段;您只能通过在指令中显式引用它来使用它(除了隐式使用 DS 和 ES 的块移动 [MOVB} 指令)。

    希望对您有所帮助。

    最好使用更现代的微处理器,其中段寄存器的愚蠢不是问题。 (例如,32 位模式 x86,其中主流操作系统使用所有段基数 = 0 的平面内存模型。因此您可以忽略分段并将单个寄存器作为指针,只关心地址的“偏移”部分。 )

    【讨论】:

    • 您的答案大部分是正确的。但是,所有现代 x86 处理器都使用段寄存器。即使在大部分平坦的 64 位模式下,您仍然有不平坦的 GS 和 FS。如果有的话,现在这个段寄存器的愚蠢比你在你的帖子中描述的要复杂得多。
    • 是的,他们有,但 OP 不需要听到这种复杂情况。当前的利用率 (x64) 也不过是真实段寄存器的最基本痕迹。真可惜,请参阅 Multics 参考。 (安迪·格鲁夫(Andy Grove)在 ​​80 年代中期的一次演讲中起床并炸毁了他的堆栈......英特尔设计了 386 个段寄存器来真正实现 Multics,但他被 Unix 的小伙伴们忽略了。我们应该接受我们所接受的)。
    • 非常感谢! “你需要 20 位,但寄存器只有 16 位。”我完全忘记了我在使用 16 位 CPU!
    • 三个挑剔:1)这里是 8086 2)隐式段寄存器取决于操作数,例如SI 获得 DS,DI 获得 ES 3)8086(实模式)段与 Multics 无关(它们仅用于寻址高达 1MB 的内存而无需进行组切换),您正在考虑 286(保护模式)段。跨度>
    • 不管段寄存器如何工作,它们都会影响到物理内存的逻辑地址映射,并控制访问是否合法。事实上,8086 段寄存器是微不足道的,并且没有与它们相关的实际保护位,这只是使它们成为我们从 Multics 以及最终的 32 位 Intel CPU 获得的极其原始的版本。英特尔发现这一点的事实是他们的天才。世界其他地方太愚蠢了,无法理解这一点,这完全是白痴。因此,我们得到了平面地址空间“Eunuchs”,而不是 Multics。呸。
    【解决方案2】:

    8086 段寄存器 csdsesss 是 16 位寄存器可以寻址超过 64K 内存的原始机制。在 8086/8088 中,需要生成 20 个位地址 (1024 K)。 x86 处理器的后续版本添加了新的方案来解决更多问题,但基本原因是从一对 16 位值生成 20 位以上的地址。

    在所谓的“实模式”(原生于 8086/8088/80186)中,通过将段寄存器的内容乘以 16(或等效地,左移四位)并加上偏移量来计算地址.

    在保护模式下(适用于 80286 及更高版本),段寄存器选择包含基本物理地址的“描述符”。例如,操作数es:[bx]bx 添加到该物理地址以生成操作数地址。

    【讨论】:

    • 保护模式是在 286 中引入的。
    【解决方案3】:

    p 指向一个带有段和偏移部分的 32 位 FAR 指针(与 NEAR 指针相反,它只是偏移部分)。 LES 会将 segment:offset 加载到ES:BX

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

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

    此上下文中的段(地址)是物理地址的一部分:
    将段向左移动 4 位(即乘以 2^4 = 16)并添加偏移量以从段获得物理地址:偏移量。

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

    【讨论】:

    • "段寄存器不能直接从内存中加载" 这是不正确的。你可以像mov es, word [1234h] 一样加载。唯一的限制是您不能在计算中使用段寄存器(没有incaddand 等),并且不能加载嵌入在指令中的立即值(没有mov es, 0ABCDh)。
    猜你喜欢
    • 1970-01-01
    • 2011-02-17
    • 2012-10-05
    • 2020-11-12
    • 2021-08-29
    • 2022-11-30
    • 1970-01-01
    相关资源
    最近更新 更多