代码之家  ›  专栏  ›  技术社区  ›  nikitablack

为什么我可以访问寄存器中较低的双字/字/字节,但不能访问较高的双字/字/字节?

  •  7
  • nikitablack  · 技术社区  · 7 年前

    我开始学习汇编程序,这在我看来不符合逻辑。

    rax -&燃气轮机; eax -&燃气轮机; ax 64位寄存器。例如,我可以使用 r8 r8d ,但为什么不呢 r8dl r8dh ? 这同样适用于 r8w r8b .

    r8b 同时注册(就像我可以用 al ah 同时)。但我不能。和使用 r8b

    r8 忘掉下面的部分?

    3 回复  |  直到 7 年前
        1
  •  15
  •   Peter Cordes    7 年前

    为什么我不能在寄存器中使用多个更高的字节

    instruction     encoding    remarks
    ---------------------------------------------------------
    mov ax,value    b8 01 00    <-- whole register
    mov al,value    b4 01       <-- lower byte
    mov ah,value    b0 01       <-- upper byte
    

    由于8086是一个16位处理器,三种不同的版本涵盖了所有选项。

    以下是设计师的选择:

    instruction     encoding           remarks
    ---------------------------------------------------------
    mov eax,value    b8 01 00 00 00    (same encoding as mov ax,value!)
    mov ax,value     66 b8 01 00       (prefix 66 + encoding for mov eax,value)
    mov al,value     (same as before)
    mov ah,value     (same as before)
    

    0x66 前缀将寄存器大小从(现在)默认的32位更改为16位加a 0x67

    否则就意味着 加倍 指令编码或加法的数量
    全部的 指令字节已被占用,因此没有空间容纳新的前缀。这个操作码空间被无用的指令占用了,比如 AAA , AAD AAM , AAS , DAA , DAS SALC

    如果只想更改寄存器的较高字节,只需执行以下操作:

    movzx eax,cl     //mov al,cl, but faster   
    shl eax,24       //mov al to high byte.
    

    在最初的8086中,有8字节大小的寄存器:

    al,cl,dl,bl,ah,ch,dh,bh  <-- in this order.
    

    索引寄存器、基指针和堆栈寄存器没有字节寄存器。

    在x64中,这一点已更改。如果有 REX 前缀(表示x64寄存器),然后 al..bh al .. r15l .16个REG,包括rex前缀的1个额外编码位。这增加了 spl , dil sil , bpl ,但不包括任何 xh 不使用 rex

    使用r8b使整个r8“繁忙”

    是的,这称为“部分寄存器写入”。因为写作 r8b r8 , 现在被分成两半。一半已经改变,一半没有。CPU需要将两半连接起来。它可以通过使用额外的CPU周期来完成这项工作,也可以通过向任务添加更多电路来在单个周期内完成这项工作。
    Why doesn't GCC use partial registers? 详细介绍了不同的x86 CPU如何处理部分寄存器写入(以及稍后对完整寄存器的读取)。

    如果我使用r8b,我不能同时访问高56位,它们存在,但不可访问

    unaccessible .

    mov  rax,bignumber         //random value in eax
    mov  al,0                  //clear al
    xor  r8d,r8d               //r8=0
    mov  r8b,16                //set r8b
    or   r8,rax                //change r8 upper without changing r8b  
    

    你使用口罩加 and , or , xor not and

    真的从来没有 对于 ah ,但它确实在8086上实现了更紧凑的代码(以及更有效的可用寄存器)。有时编写EAX或RAX,然后分别读取AL和AH(例如。 movzx ecx, al movzx edx, ah )作为解包字节的一部分。

        2
  •  4
  •   Netch    7 年前

    一般的答案是,这种访问在某些意义上是昂贵的,很少需要。

    SSA 基于。因此,您永远不会在其输出中看到XCHG指令;如果您在代码中的某个地方找到它,那么它是100%手动编写的程序集插入。RCL、RCR也是如此,即使它们适用于某些特定情况(例如,将uint32除以7),也可能适用于ROL、ROR。如果AMD放弃了其x86-64设计中的RCL、RCR,那么没有人会真正哀悼这些指令。

    这不包括基于不同原理建模并与主要原理正交的矢量设施。当编译器决定对XMM寄存器执行4个并行uint32操作时,它可以使用PINS*指令替换此类寄存器的一部分,或使用PEXTR*来提取它,但在这种情况下,它会一次跟踪2-4-8-16…个值。但这种矢量化不适用于主寄存器集,至少在最先进的主ISA中是这样。

    register renaming )它们分别(例如,添加2个寄存器源并提供1个寄存器结果)而不是分别提供寄存器的每个部分,并计算(对于同一示例)获得16个单字节源并生成8个单字节结果的指令。(这就是为什么x86-64被设计为32位寄存器写入清除64位寄存器的上32位;但这不是针对8位和16位操作的,因为出于遗留原因,CPU已经需要与以前寄存器值的高位合并。)

    在彻底的CPU设计革命之前,有一些机会看到这种情况在未来发生变化,但我认为这种可能性非常小。

    如果您当前需要访问部分寄存器,例如RAX的位40-47,则可以通过复制和旋转非常容易地实现。要提取它:

    MOV RCX, RAX ; expect result in CL
    SHR RCX, 40
    MOVZX RCX, CL ; to clear all bits except 7-0
    

    要替换值:

    ROR RAX, 40
    MOV AL, CL ; provided that CL is what to insert
    ROL RAX, 40
    

    这些代码块是线性的,速度足够快。

        3
  •  3
  •   Bo Persson tox    7 年前

    历史上还有一个步骤,8086之前的8位8080。尽管它是一个8位处理器,但您可以使用8位寄存器对来执行一些16位操作。

    https://en.wikipedia.org/wiki/Intel_8080#Registers

    因此,为了更容易地将8080汇编代码转换为8086代码,新的16位寄存器被设计为可选的8位寄存器对,这在当时似乎很重要(英特尔甚至提供了一个程序来自动完成这项工作)。

    然而,在8086中,没有使用16位寄存器对进行32位操作的功能,因此当386出现时,似乎不需要将32位寄存器拆分为两个16位寄存器。

    如Johan所示,指令集仍然提供了从最低16位获取两个8位寄存器的方法。但该(mis)特征没有扩展到更高的宽度。