翻转时两个无符号变量之间的差异

问题描述

我被困在一些数学上。 我正在用 32 位 x86 程序集编程,当较大的数字实际上较小时,我试图找出两个数字之间的差异。

这是一个例子。

假设 EAX 为 3,EBX 为 FFFFFFFE 十六进制。

如果我想从 EBX 中减去 EAX,我可以很容易地做到:

sub EBX,EAX

我会得到 FFFFFFFB 十六进制的正确值。

但如果碰巧 EAX 是 FFFFFFFE 十六进制,而 EBX 是 3 并且我想做减法,我可以尝试:

sub EBX,EAX

但随后我会得到一个不正确的进位值。 我想得到的是值 5。我不能使用 64 位寄存器,因为我只有一个 32 位系统可以使用。

我该如何解决这个问题?

我问这个是因为我做了一个 TCP/IP 堆栈,我需要跟踪数据,它涉及永不停止累积的序列号。当我的堆栈看到一个文件正在下载时,它会拍摄当时序列号的快照,然后当新的数据包进来时,从旧的序列号中减去新的序列号。

当下载足够大并且序列号接近 32 位边界(FFFFFFFF 十六进制)时,这会变得混乱

这是一个例子:

在下载开始时,假设序列号是 FFFFFF00 十六进制。堆栈捕获这个数字。

下载 256 字节后,序列号为 00000001 十六进制。这对堆栈来说很混乱,因为从 1 中减去 FFFFFF00 十六进制会导致带有进位的错误数字。

解决方法

您不需要 64 位寄存器来计算超过 32 位。使用两个连接的 32 位寄存器或两个 DWORD 变量。 而不是

.data
Sequence DD 0xFFFFFF00
Download DD 0x00000100
.code
MOV EAX,[Sequence]  ; EAX=FFFFFF00
MOV EBX,[Download]  ; EBX=00000100
ADD EAX,EBX         ; EAX=00000000,CF=1

将变量定义为 64 位:

.data
SequenceLo DD 0xFFFFFF00
SequenceHi DD 0x00000000
DownloadLo DD 0x00000100
DownloadHi DD 0x00000000
.code
MOV EAX,[DownloadLo]  ; EAX=0x00000100
MOV EDX,[DownloadHi]  ; EDX=0x00000000
ADD [SequenceLo],EAX  ; [SequenceLo]=0x00000000,CF=1
ADC [SequenceHi],EDX  ; [SequenceHi]=0x00000001,CF=0

但如果偶然 EAX 是 FFFFFFFE 十六进制,而 EBX 是 3 并且我想做减法,我可以尝试 sub EBX,EAX 但随后我会得到一个不正确的进位值。我想得到的是值 5。

同理,减法时使用寄存器对EDX:EAXECX:EBX和指令对SUBSBB

MOV EAX,0xFFFFFFFE
CDQ                 ; EDX:EAX=0xFFFFFFFF:0xFFFFFFFE
MOV EBX,0x00000003
XOR ECX,ECX         ; ECX:EBX=0x00000000:0x00000003 
SUB EBX,EAX         ; ECX:EBX=0x00000000:0x00000005,CF=1
SBB ECX,EDX         ; ECX:EBX=0x00000000:0x00000005,CF=0