将Delphi风格的汇编代码翻译成英文?

6
最近接手的Delphi项目中有一个ASM程序。我对ASM完全不了解,因此无法理解它。我已经阅读了各种ASM指令以尝试解析过程流程,但仍然无法理解。
能否有经验的ASM人员帮助我理解并将以下过程翻译成英语(然后我可以将其翻译回Delphi,以便将来更容易阅读代码!!!)
Mem1的声明是一个字节数组[0..15];而Mem2是一个LongInt。
以下是该过程:
 procedure TForm1.XorMem(var Mem1; const Mem2; Count : Cardinal); register;
 begin
 asm
   push esi
   push edi

   mov  esi, eax         //esi = Mem1
   mov  edi, edx         //edi = Mem2

   push ecx              //save byte count
   shr  ecx, 2           //convert to dwords
   jz   @Continue

   cld
 @Loop1:                 //xor dwords at a time
   mov  eax, [edi]
   xor  [esi], eax
   add  esi, 4
   add  edi, 4
   dec  ecx
   jnz  @Loop1

 @Continue:              //handle remaining bytes (3 or less)
   pop  ecx
   and  ecx, 3
   jz   @Done

 @Loop2:                 //xor remaining bytes
   mov  al, [edi]
   xor  [esi], al
   inc  esi
   inc  edi
   dec  ecx
   jnz  @Loop2

 @Done:
   pop  edi
   pop  esi
 end;

 end;

编辑:在Roman R的帮助下,我将汇编代码转换回Delphi。

procedure TForm1.XorMem2(var Mem1; const Mem2 :LongInt; Count : Cardinal);
var
  Key : array [0..3] of byte absolute Mem1;
  Num : array [0..3] of byte absolute Mem2;
  idx : byte;
begin
  for Idx := 0 to Count -1 do Key[idx] := Key[idx] Xor Num[idx];
end;

请注意,在您的代码中Mem2: Longint并不好。只有当Count <= 4时才可以使用。如果您使用开启了范围检查的编译器进行编译,当Count > 4时该方法将会失败。 - Arnaud Bouchez
1
阅读汇编代码(或任何代码)的最佳方法是先知道它应该做什么。知道它应该做什么可以让你了解如何实现它,因此你可以认识到代码的各个部分并将它们适合于自己关于代码应该长成什么样子的头脑模型中。在这种情况下,函数签名几乎告诉你代码具体是做什么的,这使得从汇编转换变得容易。 - Rob Kennedy
@Rob 我相信这就是 OP 所问的。有谁懂 ASM 可以告诉我这个例程应该做什么? - PA.
1
你不需要知道汇编语言,@PA。只需查看函数签名。它接受两个未定义类型的内存缓冲区,其中一个是可修改的,并且还有一个大小参数。函数名称告诉我们它将对它们应用异或操作。知道这些,很容易猜测Mem1接收了使用某些东西Mem2异或的结果。我们还不能确定另一个操作数是否为Mem1,但这是一个合理的猜测,并且在阅读代码时给我们一些提示。 - Rob Kennedy
@Arnaud 是的,好观点;我可能应该完全删除计数器,因为似乎只有前4个字节会影响原始数组。 - Rucia
2个回答

9
该函数接受两个指针(指向任何类型的数组)和它们以字节为单位的长度。该函数对第一个数组字节(Mem1)执行逐字节的XOR操作,使用第二个数组字节(Mem2)。 伪代码:
for Index = 0 to Count - 1
  (Mem1 as Byte Array) [Index] = (Mem1 as Byte Array) [Index] Xor (Mem2 as Byte Array) [Index]

哇,谢谢!这在您的伪代码中很有意义;如果这只是一个粗略的Pascal版本,我不认为使用原始ASM版本会有多少速度优势。 - Rucia
汇编版本倾向于一次处理4个字节,然后逐个字节处理剩余的字节。我不确定性能提升是否真的值得麻烦。请注意,使用SIMD指令可以一次处理更多字节,这可能会更快。 - Roman R.
1
对于这么小的值,速度提升不会太明显。该函数尝试一次异或4个字节,所以速度可能会更快。但是你需要多次调用它才能注意到差别。 - GolezTrol
2
@Roman 如果数据是DWORD对齐的话,一次读取DWORD是有意义的。否则,它可能会变得更慢。汇编代码很奇怪:例如,有一个cld操作码,看起来像是使用lodsd / stosd的某个旧版本的遗物。简而言之:提供的汇编代码远非优化。对于少量数据,一个小的Delphi循环就足够快,并且转换为64位也要快得多(例如)。 - Arnaud Bouchez

4
这是一个可运行且简单的纯Pascal版本:
procedure TForm1.XorMem(var Mem1; const Mem2; Count : Cardinal);
var i: integer;
    M1: TByteArray absolute Mem1;
    M2: TByteArray absolute Mem2;
begin
  for i := 0 to Count-1 do
     M1[i] := M1[i] xor M2[i];
end;

以下是使用DWORD读取的优化版本:

这里是一个使用DWORD读取进行优化的版本:

procedure TForm1.XorMem(var Mem1; const Mem2; Count : Cardinal);
var i, n: integer;
    M1: TByteArray absolute Mem1;
    M2: TByteArray absolute Mem2;
    I1: TIntegerArray absolute Mem1;
    I2: TIntegerArray absolute Mem2;
begin
  n := Count shr 2;
  for i := 0 to n-1 do
     I1[i] := I1[i] xor I2[i];
  n := n shl 2;
  for i := 0 to (Count and 3)-1 do
     M1[n+i] := M1[n+i] xor M2[n+i];
end;

我认为第二个版本并非必需。如果数据是DWORD对齐的,则一次读取DWORD是有意义的。否则,这可能会导致效率降低。对于少量数据,一个小的Delphi循环足够快且易于阅读。最新的CPU(如Core i5或i7)在使用小型代码循环时表现出色(不再需要展开循环)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接