如何在eax寄存器中mov值,ah和al左2字节?x86程序集



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

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

第二部分(阶段2(是将每个值放入eax 8位位置中的每个位置。但是,我知道你只能访问下面的两个8位位置("ah"one_answers"al"(。我需要以某种方式将ah和al一起移动到eax的前16位,将添加到ah和al的值向左推两个字节位置?(打个问号,因为我不知道。(我相当确定,然后我可以将正确的值添加回ah和al来完成解决方案。

我相信这样做的方法是在ah中添加"一些十六进制值",并留下溢出,但我似乎无法理解它的逻辑;从逻辑上讲;我想说这似乎是最好的做法,但我不确定如何实现它。而且,由于我无法理解它,我找不到我应该找到的隐藏算法。Phase2应该只有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上的16xadd方式非常接近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是小字节序,因此EAX到addr的加载/存储将AL加载/存储到相同的addr,将AH加载/存储在addr+1。,其中高2个字节来自addr+2和+3。

在写入AH和AL之后读取EAX也会迫使CPU合并部分寄存器,如果它将AH(可能还有AL(与完整的EAX分开重命名,但很明显,如果你只限于ISA的一小部分,那么高性能不是你的首要目标。(请参阅为什么GCC不使用部分寄存器?以及Haswell/Skylake上的部分寄存器究竟是如何执行的?写入AL似乎对RAX有错误的依赖性,而AH不一致以了解更多详细信息。(

有关存储转发暂停部分,请参阅现代x86实现是否可以从多个先前存储进行存储转发?


根据您对新的低位部分(新的AH和AL(所做的工作,您实际上可能会在一个单独的寄存器(如DH和DL(中进行这些操作,因此无序的exec可以开始这项工作,而不会错误地依赖于存储转发重载,尤其是在那些不将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

CCD_ 7可能需要等待旧EAX值为"0";准备就绪";,即重新加载完成,以便它可以作为运行该指令的一部分合并到其中。(在英特尔Sandybridge系列和所有非英特尔CPU上。(因此,这使得DL/DH上的计算与存储转发延迟重叠。

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

最新更新