Using MMX for Encryption

In this section we demonstrate a potential use of MMX for the encryption of large files. The procedures for loading from file and saving to file are quick because each uses a single instruction to transfer all the contents at once. Notice our use of open array parameters in the ArrayToFile and Conversion procedures and the suitability of this situation for our dynamic array MMXByteArray. The previous section demonstrated arithmetic with simultaneous processing of four two-byte words. This program demonstrates the processing of 64 bits with the XOR operation. We showed in the encryption section of our In-Line Assembler tutorial how the application of the XOR operation twice with the same key regenerates the original "plain text".

Some of you will notice a technicality in the code. Our target file for encryption is unlikely to have a multiple of 64 bits. FileBytes DIV 8 gives us the number of complete 64-bit MMXBytes in the file. To this we add one to size our array and so the final MMXByte will contain from 0 to 7 bytes of actual data. We encrypt all of the eight bytes in the MMXByte for convenience, but we use the same file size as before when writing to file. We do not therefore use the converted few bytes that were not part of the input file.

You need a large test file to test the speed of the program. We used a file named Programs.txt and were impressed that it encrypted our 4.5 MB array in about 8 ms (as an average from 100 loops). A text file is suitable for checking quickly the encryption and decryption, but you do not need to use a text file. You need to change the constants INFILE, ENCRYPTED and DECRYPTED as necessary for your tests. (The procedure GetTickCount in the Windows unit outputs times in ms correct to the nearest 16 ms or so, therefore you must run the code many times if you want a more precise measure of the time taken per encryption).

program Encrypt_MMX;
  {$APPTYPE CONSOLE}
uses
  SysUtils, MMX, Windows, Classes;
type
  TMMXBytes = array of TMMXByte;
const
  KEYS : array[0 .. 3] of TMMXByte = (($85, $A8, $FA, $D3, $C7, $8B, $E2, $9B),
                                      ($81, $A2, $F6, $D0, $C2, $8A, $E4, $9C),
                                      ($86, $A4, $F8, $D6, $CA, $82, $EB, $92),
                                      ($84, $AB, $F3, $D9, $C0, $81, $E2, $96));
  INFILE = 'Programs.txt';
  ENCRYPTED = 'Encrypted.txt';
  DECRYPTED = 'Decrypted.txt';

var
  MMXBytes : TMMXBytes;
  FileBytes,  NumOfMMXbytes : integer;

procedure FileToArray(const strSource : string;
                      var iFileBytes, iMMXbytes  : integer);
var
  fsCurrent : TFileStream;
  iBytesRead : integer;
begin
  fsCurrent := TFileStream.Create(strSource, fmOpenRead);
  iFileBytes := fsCurrent.Size;
  iMMXbytes := (iFileBytes div 8) + 1;
  SetLength(MMXBytes, iMMXbytes);
  iBytesRead := fsCurrent.Read(MMXBytes[0], iFileBytes);
  if iBytesRead <> iFileBytes then
    writeln('Error reading from file');
  fsCurrent.Destroy;
end;

procedure ArrayToFile(const strDestination : string; var arraySource : TMMXBytes;
                      const iFileBytes : integer);
var
  fsCurrent : TFileStream;
  iBytesWritten : integer;
begin
  fsCurrent := TFileStream.Create(strDestination, fmCreate);
  iBytesWritten := fsCurrent.Write(arraySource[0], iFileBytes);
  if iBytesWritten <> iFileBytes then
    writeln('Error writing to file');
  fsCurrent.Destroy;
end;

procedure Convert(var MMXBytes : TMMXBytes; const iBytes : integer);
var
  Ticks, StartTicks, i, j : integer;
begin
  {$MMX+}
  StartTicks := GetTickCount;
  for i := 0 to iBytes - 1 do
    begin
      j := i MOD 4;
      MMXBytes[i] := MMXBytes[i] XOR KEYS[j];
    end;
  Ticks := GetTickCount - StartTicks;
  writeln('Array converted in ', Ticks, ' ms. ');
  {$MMX-}
end;

begin
  FileToArray(INFILE, FileBytes, NumOfMMXBytes);
  Convert(MMXBytes, NumOfMMXBytes);
  ArrayToFile(ENCRYPTED, MMXBytes, FileBytes);
  writeln('File encrypted. Press Enter to decrypt.');
  readln;
  FileToArray(ENCRYPTED, FileBytes, NumOfMMXBytes);
  Convert(MMXBytes, NumOfMMXBytes);
  ArrayToFile(DECRYPTED, MMXBytes, FileBytes);
  writeln('File decrypted. Press Enter to exit.');
  readln;
end.
Programming - a skill for life!

How to use the MMX capabilities of Pentium processors