【问题标题】:How to mov values in eax register, ah and al left by 2 bytes? x86 Assembly如何将 eax 寄存器中的值 ah 和 al 移动 2 个字节? x86 组装
【发布时间】:2021-06-12 06:43:58
【问题描述】:

我有一个关于如何在 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 以完成解决方案。

我相信这样做的方法是向 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

【问题讨论】:

  • 尝试移位。
  • 感谢 Erik 的快速回复。如果您指的是移位指令 shr 和 shl,则该赋值将我们限制为 mov add 和 sub 命令。 shift 命令是所有搜索都告诉我要做的,但我不能使用它。这是一个逻辑十六进制添加问题。
  • 左移是乘以2,可以通过加法来实现。所以 16 次加法是一种无聊但有效的 16 位移位方法。或者,您可以重新排列内存中的数据。
  • 谢谢,小丑,我会试试的。但是,会不会只是重复的```添加啊,al```之类的东西?还是你的意思是像 ``` 添加 ax, '0FFh' ``` 十六次? -编辑-或;一旦 ah 和 al 就位,然后将其移动到 bx 然后将 bx 添加到 ax 16 次...?

标签: assembly x86


【解决方案1】:

如果您不能像普通人一样使用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 是 little-endian,因此将 EAX 加载/存储到 addr 将 AL 加载/存储到相同的 addr,将 AH 加载/存储到 addr+1.,高 2 个字节来自 addr+2 和 +3。

在写入 AH 和 AL 后读取 EAX 也将强制 CPU 合并部分寄存器,如果它与完整的 EAX 分开重命名 AH(可能还有 AL),但很明显,如果你将自己限制在 ISA 的一小部分那么高性能不是你的首要目标。 (有关详细信息,请参阅 Why doesn't GCC use partial registers?How exactly do partial registers on Haswell/Skylake perform? Writing AL seems to have a false dependency on RAX, and AH is inconsistent。)

转存档部分见Can modern x86 implementations store-forward from more than one prior store?


根据您对新低部分(新的 AH 和 AL)所做的工作量,您实际上可能会在单独的寄存器中执行它们(如 DH 和 DL),因此可以开始乱序执行该工作,没有对存储转发重新加载的错误依赖,特别是在不将 AL(甚至 AH)与 EAX 分开重命名的 CPU 上。 (即不是 Intel P6 系列的 CPU,比如老顽固的 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 值“准备好”,即重新加载完成,因此它可以合并到它作为运行该指令的一部分。 (在英特尔 Sandybridge 系列和所有非英特尔 CPU 上。)因此,这使得 DL/DH 上的计算与存储转发延迟重叠。

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

【讨论】:

  • 谢谢你,彼得。你是对的,我不能像正常人一样使用 shift 。我认为因为这是我第一次介绍 Assembly,所以我的想法是强迫我使用小步骤,直到我完全理解它们。顺便说一句,这个答案很完美。其中的 16 个 ``` 添加 eax,eax ``` 完成了工作。而且,我明白如何。谢谢!
  • @DeweyWoodz:我提到shl 是为了让未来的读者能够找到标题但没有您的限制。是的,如果练习的目的是确保您了解 x86 时髦的部分寄存器和/或字节序,我不会感到惊讶。 IDK 如果他们期望内存存储/重新加载或 ADD 外观,但存储/重新加载更有效。您还可以存储其他 2 个字节以在您想要的 dword 值的内存中构建一个完整的“图像”,然后加载它。 (然后你“只是”有一个存储转发档,没有部分寄存器合并)。
  • 重复的问题,事实证明我在此之前已经回答了 2 年:How do I shift left in assembly with ADD, not using SHL?。慢路径存储转发的重新成本:What are the costs of failed store-to-load forwarding on x86?
猜你喜欢
  • 1970-01-01
  • 2013-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多