假设我在 中定义了.data
X 个字节:
db 00000000b,00100011b,11100011b,...........
假设我定义了 50 个字节。
我想抓住这 50 个字节,然后向左移动一个大位。上面的示例将如下所示:
db 00000000b,01000111b,11000110b,...........
当我们使用 4 个字节时,这很容易做到,因为您只需将它们放入EAX
并进行移位。但现在我有 50 个字节。
我怎样才能做到这一点?
将任意数量的字节左移一位:首先SHL
是最低有效字节一位。然后RCL
是所有其他字节一位,从第二个最低有效位到最高有效位。
通常有一种移位的味道,即移入或通过进位位。进位位用于从一个移位操作中取出移位,并使用它在下一个移位操作中移入。因此,如果向左移动,您将从 lsbyte 开始,它的 msbit 移出,并移入到 lsbyte 的下一个右侧,依此类推,在所有字节上产生级联效应。我将把它留给读者查找移位操作,看看哪些是做什么的。请记住您需要控制移入的第一个移位操作,因此清除进位位或使用不同的指令。通常通过进位进行的移位称为轮换。
如果只进行一位左移,您可以使用 rcl 指令将一个内存字左移一位,捕获进位,并在下一个字上重复。您想为第一次迭代重置进位。(我很确定这不是 NASM 语法,但这不应该打扰你)。
mov esi, offset data_buffer
mov ebx, data_buffer_dword_count
lea esi, [esi+4*ebx-4]
clc
loop:
rcl [esi]
lea esi, -4[esi]
dec ebx ; this kind of loop is why dec instructions don't affect carry!
jne loop
如果移位 N>1 位,这很慢。在这种情况下,请使用 shift-left-double 指令。该指令使用两个寄存器,并将这对移位指定的距离(cl 或某个立即数)。您需要跟踪部分偏移的结果;簿记有点棘手。
如果您的移位距离为 31 位或更少,则此代码应该执行此操作:(编辑:我用不那么笨拙的东西替换了以前的代码):
mov esi, offset data_buffer
mov ebx, data_buffer_dword_count
lea esi, [esi+4*ebx-4]
xor edi, edi ; bits from last time; this acts like "clc" in above loop
loop:
mov eax, [esi]
xor edx, edx ; make 64 bit value of next dword
mov ecx, shift_left_distance
shld edx, eax, cl ; shift left across <edx,eax>
add eax, edi ; form full shifted dword by combining with last partial
mov [esi], eax ; update storage with shifted result
mov edi, edx ; save shifted but unstored bits from this round
lea esi, -4[esi]
dec ebx
jne loop
如果您想移动超过 31 位,那么您可以通过使用内存偏移量(dwords 中的移动距离除以 32)与上述移位距离模 32 的方案来组合 shuffle dwords 的想法。
将字节数组左移一位最好使用 add + adc 序列来实现——您可以组合任意长度的操作,例如:
lea edi, [array]
mov eax, [edi]
mov bx, [edi + 4]
mov cl, [edi + 6]
add eax, eax ;; shift the MSB to Carry bit
adc bx, bx ;; shift the previous carry to bit 0 + produce new carry
adc cl, cl
mov [edi], eax ;; write back
mov [edi + 4], bx
mov [edi + 6], cl