如何将 eax 寄存器中的值,ah 和 al 左移 2 个字节? x86 汇编

How to mov values in eax register, ah and al left by 2 bytes? x86 Assembly

我有一个关于如何在 x86 汇编 eax 寄存器中移动值的问题。我知道 32 位寄存器分解为更小的组件寄存器,低 16 位为 ax,16 位进一步分解为 8 位寄存器 ah 和 al。

我目前正在为 x86 汇编语言作业编写一个程序,该程序希望我仅使用 mov、add 和 sub 命令在寄存器中移动四个 8 位十六进制值。该程序首先让您通过加减变量来移动变量值,这没问题。

第二部分(phase2)是将每个值放入每个eax 8位位置。但是,我知道您只能访问较低的两个 8 位位置(“ah”和“al”。)我需要以某种方式将 ah 和 al 一起移动到 eax 的前导 16 位,将添加的值推送到 ah 和al 留下两个字节的位置? (问号,因为我不知道。)我相当确定我可以将正确的值添加回 ah 和 al 以完成解决方案。

我相信这样做的方法是将 'some hex value' 添加到 ah 并保留溢出,但我似乎无法理解它的逻辑。 “从逻辑上讲,”我想说这似乎是最好的行动方案,但我不确定如何实施。而且,由于我不能全神贯注,我找不到我应该找到的隐藏算法。 Phase2 应该只有 aprx 21 行,所以我知道它不是大量的添加指令列。

任何关于如何思考这个问题的指导都将不胜感激。感谢任何人。

.386
.model flat,stdcall
.stack 4096
ExitProcess proto,dwExitCode:dword

.data
    var1 BYTE 'A'
    var2 BYTE 'B'
    var3 BYTE 'C'
    var4 BYTE 'D'
    
.code
main proc
;phase1
mov al, var1; store 'A'
mov ah, var4; store 'D'
mov var1, ah; move 'D' to var1
sub ah, 1; make ah 'C'
mov var4, ah; move 'C' to var4
sub ah, 1; make ah 'B'
mov var3, ah; move 'B' to var3
mov var2, al; 'mov al to var2 

    ;var1 BYTE 'D'
    ;var2 BYTE 'A'
    ;var3 BYTE 'B'
    ;var4 BYTE 'C'


;phase2
mov ah, var1; store 'D'
mov al, var2; store 'A'

; this is where I want to shift al and ah left two bytes 
; once the first two bytes of eax equal 'DA' move 'B' 'C' 
; into ah and al

mov ah, var3; store 'B'
mov al, var4; store 'C'

;eax should read 'DABC' = 44414243
    
    invoke ExitProcess,0
main endp
end main

如果您不能像正常人一样使用shl eax, 16,您的其他选择包括:

  • add eax,eax 重复 16 次(糟糕,慢),循环部分展开或完全展开。
  • 以偏移量存储/重新加载:也很慢,但仅用于延迟(存储转发停顿)。吞吐量还可以,而延迟非常接近与典型现代 x86 上的 16x add 方式相同的 16 个周期。
    sub  esp, 16             ; reserve some stack space.

    ...
    mov  [esp+2], ax         ; 2 byte store
    mov  eax, [esp]          ; 4-byte reload with previous AX in the top half
    
    mov  ah, ...             ; overwrite whatever garbage in the low 2 bytes
    mov  al, ...

x86 是小端 ,因此 load/store 的 EAX 到 addr loads/stores AL 到相同的 addr,和 AH 到 addr+1.,高 2 个字节来自 addr+2 和 +3.

在写入 AH 和 AL 后读取 EAX 也会强制 CPU 合并部分寄存器,如果它从完整的 EAX 中分离重命名 AH(可能还有 AL),但很明显,如果你只限制自己ISA 的一小部分然后高性能不是您的首要目标。 (有关详细信息,请参阅 and 。)

store-forwarding stall部分见Can modern x86 implementations store-forward from more than one prior store?


取决于你对新的低部分(新的 AH 和 AL)做了多少,你实际上可以在一个单独的寄存器(比如 DH 和 DL)中做它们,所以无序的 exec 可以得到开始了这项工作,没有对存储转发重新加载的错误依赖,尤其是在 CPUs 上,它们没有从 EAX 中单独重命名 AL(甚至 AH)。 (即 CPU 不是 Intel P6 系列的,例如顽皮的老 Nehalem)。

所以你会这样做

    mov  [esp+2], ax         ; 2 byte store
    mov  eax, [esp]          ; 4-byte reload with previous AX in the top half
    
    mov  dl, ...
    mov  dh, ...
    ... more computation with these two

    mov  ax, dx              ; replace low 2 bytes of EAX

mov ax,dx 可能需要等待旧的 EAX 值“准备好”,即重新加载完成,因此它可以作为 运行 该指令的一部分合并到其中。 (在 Intel Sandybridge 系列和所有非 Intel CPUs 上。)所以这让 DL/DH 上的计算与存储转发延迟重叠。

明确一点,所有这些关于权衡的讨论都是关于性能,而不是正确性;我在这里展示的所有方法都是完全正确的。(除非我弄错了 :P)