Assembly 如何在汇编中添加和减去大型变量?
所以这应该是一个很容易回答的问题 假设给我两个非常大的变量a和b,我想从a中减去b。假设每个变量都有30个单词长。我不能只使用sub教练,可以吗?我被告知它将默认为sub.w,并且只从a的第一个单词中减去b的第一个单词Assembly 如何在汇编中添加和减去大型变量?,assembly,Assembly,所以这应该是一个很容易回答的问题 假设给我两个非常大的变量a和b,我想从a中减去b。假设每个变量都有30个单词长。我不能只使用sub教练,可以吗?我被告知它将默认为sub.w,并且只从a的第一个单词中减去b的第一个单词 那我该怎么做呢 要执行多字减法,从低阶字开始,从两个变量中减去相应的字。使用sbb指令处理借用,仅在第一次减法时使用sub mov dx, [ebx] ;First word of b sub [eax], dx ;Subtract from 1st word
那我该怎么做呢 要执行多字减法,从低阶字开始,从两个变量中减去相应的字。使用
sbb
指令处理借用,仅在第一次减法时使用sub
mov dx, [ebx] ;First word of b
sub [eax], dx ;Subtract from 1st word of a
mov dx, [ebx+2] ;Second word of b
sbb [eax+2], dx ;Subtract from 2nd word of a
mov dx, [ebx+4] ;Third word of b
sbb [eax+4], dx ;Subtract from 3rd word of a
...
mov dx, [ebx+58] ;Thirtieth word of b
sbb [eax+58], dx ;Subtract from 30th word of a
更实用的解决方案使用循环:
mov ecx, 30
xor esi, esi ;This clears the CF, needed for the very first SBB
Again:
mov dx, [ebx+esi]
sbb [eax+esi], dx
lea esi, [esi+2]
loop Again ; loop without clobbering CF.
有,但最佳选择因微体系结构而异。一个简单的方法是稍微展开一点,以减少系统开销
mov ecx, 15
xor esi, esi ;This clears the CF, needed for the very first SBB
Again:
mov dx, [ebx+esi]
sbb [eax+esi], dx
mov dx, [ebx+esi+2]
sbb [eax+esi+2], dx
lea esi, [esi+4]
loop Again
优化此任务的下一步是停止使用16位寄存器DX,而是使用更大的EDX寄存器。这将使完全展开版本中的指令数减半,或使循环版本中的迭代次数减半以上。我们可以这样做,因为“30个单词长的变量”可以被认为是“15个双单词长的变量” 这是完全展开的版本:
mov edx, [ebx] ;First dword of b
sub [eax], edx ;Subtract from 1st dword of a
mov edx, [ebx+4] ;Second dword of b
sbb [eax+4], edx ;Subtract from 2nd dword of a
mov edx, [ebx+8] ;Third dword of b
sbb [eax+8], edx ;Subtract from 3rd dword of a
...
mov edx, [ebx+56] ;Fifteenth dword of b
sbb [eax+56], edx ;Subtract from 15th dword of a
mov ecx, 5
clc ;This clears the CF, needed for the very first SBB
Again:
mov edx, [ebx] ; <1>
sbb [eax], edx
mov edx, [ebx+4] ; <2>
sbb [eax+4], edx
mov edx, [ebx+8] ; <3>
sbb [eax+8], edx
lea ebx, [ebx+12]
lea eax, [eax+12]
loop Again
以及部分展开的循环版本:
mov edx, [ebx] ;First dword of b
sub [eax], edx ;Subtract from 1st dword of a
mov edx, [ebx+4] ;Second dword of b
sbb [eax+4], edx ;Subtract from 2nd dword of a
mov edx, [ebx+8] ;Third dword of b
sbb [eax+8], edx ;Subtract from 3rd dword of a
...
mov edx, [ebx+56] ;Fifteenth dword of b
sbb [eax+56], edx ;Subtract from 15th dword of a
mov ecx, 5
clc ;This clears the CF, needed for the very first SBB
Again:
mov edx, [ebx] ; <1>
sbb [eax], edx
mov edx, [ebx+4] ; <2>
sbb [eax+4], edx
mov edx, [ebx+8] ; <3>
sbb [eax+8], edx
lea ebx, [ebx+12]
lea eax, [eax+12]
loop Again
mov-ecx,5
clc;这将清除第一个SBB所需的CF
再一次:
mov-edx,[ebx];
sbb[eax],edx
mov-edx,[ebx+4];
sbb[eax+4],edx
mov edx,[ebx+8];
sbb[eax+8],edx
lea ebx,[ebx+12]
lea eax,[eax+12]
再次循环
显然,在x86-64上类似地使用RDX将进一步改进此代码。请注意,30个字对应7个Q字和1个dword。读取adc和sbb指令。您使用的是什么CPU?添加和cmp缓冲标志。没有实现多精度循环的“理想”方法,因为
dec/jnz
会导致部分标志暂停,并且loop
速度较慢。稍微展开一点并使用一些笨重的东西会有帮助。@PeterCordes感谢您发现add
和cmp
撞到了标志。我编辑了答案。你的编辑并没有强调一个事实,即循环而不重击CF是非常重要的。我做了一个编辑,无耻地链接到我自己的答案。请随意查找其他比我更简洁的链接。我倾向于把我自己的答案联系起来,因为我记得它们存在,而且很容易找到它们P@PeterCordes在我的最终代码片段中使用3个不同的寄存器EDX、ESI和EDI是一个好主意吗?我用、和标记了这些线。因此mov-edx,[ebx]
mov-esi,[ebx+4]mov-edi,[ebx+8]
;所有失灵的x86 CPU都会进行寄存器重命名mov-edx,[ebx+4]
打破了对edx
先前值的依赖,因为它是只写的。(您可能已经读到,xor-edx,edx
也会破坏依赖关系。这是xor的一个特例,但对mov
来说是常见的情况)。