Assembly 如何在汇编中添加和减去大型变量?

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

所以这应该是一个很容易回答的问题

假设给我两个非常大的变量ab,我想从a中减去b。假设每个变量都有30个单词长。我不能只使用sub教练,可以吗?我被告知它将默认为sub.w,并且只从a的第一个单词中减去b的第一个单词


那我该怎么做呢

要执行多字减法,从低阶字开始,从两个变量中减去相应的字。使用
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
来说是常见的情况)。