[英]AT&T Assembly carry flag
我再次需要AT&T组装的帮助,已将一些数据加载到内存中,如下所示(十六进制和十进制)。
(gdb) x/8xb &buffer_in
0x8049096: 0x03 0x02 0x10 0x27 0xe8 0x03 0x64 0x00
(gdb) x/8db &buffer_in
0x8049096: 3 2 16 39 -24 3 100 0
假设第一个字节=数字计数,第二个=每个数字长度(以字节为单位),然后得到(第一个*第二个)字节数字。 对于此示例,3个数字,每个2个字节,第一个数字是16 39,以此类推。 我想将每个数字相加,因此在这种情况下,它会将0x10 + 0xe8(低字节)添加到result [0],然后将0x27 + 0x03添加到result [1],然后再次添加result [0] = result [0] + 0x64,最后是result [1] = result [1] + 0x00。
当我在已经包含0xf8的result [0]中添加0x64时,将设置CF(进位标志),这当然很不错,因为我想在下一个加法中使用进位结果[1]。 但是问题是 ,在下一条CMP指令之后(我将在下面的代码中对其进行标记),该进位标志被清除,因此最终结果为0x5C2A(当我合并两个字节的结果时),并且应为0x5C2B(但进位标志没有影响由于cmp指令而导致的添加)。
%eax-求和的数字量
%ecx-每个数字的长度(以字节为单位)
%esi-循环开始之前指向“实际”数据的第一个字节(在这种情况下为0x10)
loop1:
movl $0, %ebx
loop2:
leal (%esi, %ebx, 1), %edi
movb (%edi), %dl # %dl contain now next byte to add
adc %dl, result(%ebx) # adding to result
inc %ebx
cmp %ebx, %ecx # this comparsion clears CF flag and that's the problem
JG loop2
leal (%esi, %ecx, 1), %esi
dec %al
cmp $0, %al
JG loop1
这通常可以通过调整算法逻辑来解决,以避免在add
和adc
之间进行任何CF更改指令,而当您想要循环动态字节数时,乍一看似乎实际上是不可能的。
但是,如果您要阅读有关INC
和DEC
指令的详细信息,则有一件有趣的事情,看起来像是奇怪的不一致。 它们不影响CF! (实际上是因为类似的用例,所以才这样设计的)。
因此,您的代码可能看起来像这样(对不起,Intel + NASM语法,我不喜欢AT&T,所以请自行转换,至少您肯定会理解)(加上我没有调试它,所以它可能有一些错误,如果有问题,请告诉我):
; zero the result data first
movzx edx,byte [buffer_in+1] ; element length
zero_result:
dec edx
mov [result+edx],byte 0
jnz zero_result
; now sum all elements
movzx ecx,byte [buffer_in+0] ; number of elements
lea esi,[buffer_in+2] ; source data ptr
elements_loop:
movzx edx,byte [buffer_in+1] ; element length
xor ebx,ebx ; offset of byte of element = 0 AND CF=0 (!)
element_byte_loop:
mov al,[esi] ; read source byte (no CF change)
inc esi ; ++ptr (CF preserved)
adc [result+ebx],al ; add it to result with CF
inc ebx ; next offset of byte inside element (CF preserved)
dec edx ; do all bytes of element (CF preserved)
jnz element_byte_loop
; single element added to result, now repeat it for all elements
dec ecx
jnz elements_loop
如果只想保存进位标志,则有一些技巧。
推标志
pushf //save the flags
...... do stuff
popf //restore the flags
将CF保存在寄存器中
//save CF in eax
sbb eax,eax //CF=1 -> CF=1, regx=-1; CF=0 -> CF=0, regx=0, clobbers other flags
//note that sbb reg, reg preserves! CF, how cool is that!
.... do stuff, do not alter eax
add eax,1 //restore CF
重写循环,因此很重要 下 最高为零
loop1:
mov ebx,ecx //ebx = count
lea esi,[esi+ecx] //esi = end of buffer
neg ebx //ebx = -count
loop2:
//no need for the lea (the mov can do complex addressing)
mov dl,[esi+ebx] # %dl contain now next byte to add
adc [ecx+ebx+result],dl adding to result
inc ebx //ebx will be zero when done :-)
//no need for cmp
jnz loop2 //we only need ZF
以防万一您错过了它。 该技巧的工作原理如下。
首先,我们将计数添加到基本指针。
接下来,我们对计数取反。
因此,循环从basepointer+count-count = basepointer
在每次迭代中,我们都增加-count
。
这在循环迭代n
产生以下效果: address = base+count-count+n
ergo: adr = base + n
。
完成后, -count+n
将为零,并且无需执行cmp
因为inc
将根据需要将ZF
调整为,而不会破坏CF
请注意,我原则上仅使用Intel语法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.