A recent Delphi project i've inherited has a procedure in ASM. I'm a complete ASM newbie, so i dont understand it. I've read up on the various ASM instructions to try and decipher the procedures flow, but i still dont get it.
Could someone with ASM experience assist my understanding and translate the following procedure to English (then i can translate back to Delphi so the code is easier to read in the future!!!)
The declaration of Mem1 is an array [0..15] of Byte;. And Mem2 is a LongInt.
Here's the procedure:
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;
edit: Thanks to Roman R i've converted the ASM back to 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;
The function accepts two pointers (to arrays whatever) and their lengths in bytes. The function performs byte-to-byte XOR
operation on first array bytes (Mem1) using second array bytes (Mem2). Pseudo-code:
for Index = 0 to Count - 1
(Mem1 as Byte Array) [Index] = (Mem1 as Byte Array) [Index] Xor (Mem2 as Byte Array) [Index]
Here is a working and simple pure pascal version:
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;
Here is an optimized version using DWORD reading:
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;
IMHO the 2nd version is not mandatory. Reading a DWORD at once makes sense if data is DWORD aligned. Otherwise, it can be effectively slower. For small amount of data, a small Delphi loop will be fast enough, and clear to read. Latest CPUs (like Core i5 or i7) make wonders when using a small code loop (unrolling a loop is not necessary any more).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With