UNIT crc32;

{$I platform.inc}
{$I mkglobal.inc}

INTERFACE
{ Use a type LONGINT variable to store the crc value.                     }
{ Initialise the variable to $FFFFFFFF before running the crc routine.    }
{ VERY IMPORTANT!!!! -> This routine was developed for data communications}
{ and returns the crc bytes in LOW to HIGH order, NOT byte reversed!      }
{ To turn the value into a 'normal' LONGINT, you must reverse the bytes!  }
{ e.g.                                                                    }
{ VAR                                                                     }
{    l, crc: LONGINT;                                                     }
{    list: ARRAY[0..1023] OF BYTE;                                        }
{    counter: INTEGER;                                                    }
{                                                                         }
{ BEGIN                                                                   }
{    crc := $FFFFFFFF;                           (* initialise  *)        }
{    FillChar(list,SizeOf(list),1);              (* dummy array *)        }
{    FOR counter := 0 TO (Pred(SizeOf(list))) DO (* run thru    *)        }
{       crc := UpdC32(buf[counter],crc);         (* finding crc *)        }
{    FOR counter := 1 TO 4 DO                    (* reverse     *)        }
{       l := (l SHL 8) OR BYTE(crc);             (* the bytes   *)        }
{    (* l now contains the 'normalized' crc *)                            }
{                                                                         }

FUNCTION UpdC32(octet: BYTE; crc: LONGINT) : LONGINT;

Function NormalizeCRC(Orig:Longint):Longint;

IMPLEMENTATION
(* Converted to Turbo Pascal (tm) V4.0 March, 1988 by J.R.Louvau       *)
(* Copyright (C) 1986 Gary S. Brown.  You may use this program, or     *)
(* code or tables extracted from it, as desired without restriction.   *)
(*                                                                     *)
(* First, the polynomial itself and its table of feedback terms.  The  *)
(* polynomial is                                                       *)
(* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 *)
(* Note that we take it "backwards" and put the highest-order term in  *)
(* the lowest-order bit.  The X^32 term is "implied"; the LSB is the   *)
(* X^31 term, etc.  The X^0 term (usually shown as "+1") results in    *)
(* the MSB being 1.                                                    *)
(*                                                                     *)
(* Note that the usual hardware shift register implementation, which   *)
(* is what we're using (we're merely optimizing it by doing eight-bit  *)
(* chunks at a time) shifts bits into the lowest-order term.  In our   *)
(* implementation, that means shifting towards the right.  Why do we   *)
(* do it this way?  Because the calculated CRC must be transmitted in  *)
(* order from highest-order term to lowest-order term.  UARTs transmit *)
(* characters in order from LSB to MSB.  By storing the CRC this way,  *)
(* we hand it to the UART in the order low-byte to high-byte; the UART *)
(* sends each low-bit to hight-bit; and the result is transmission bit *)
(* by bit from highest- to lowest-order term without requiring any bit *)
(* shuffling on our part.  Reception works similarly.                  *)
(*                                                                     *)
(* The feedback terms table consists of 256, 32-bit entries.  Notes:   *)
(*                                                                     *)
(*     The table can be generated at runtime if desired; code to do so *)
(*     is shown later.  It might not be obvious, but the feedback      *)
(*     terms simply represent the results of eight shift/xor opera-    *)
(*     tions for all combinations of data and CRC register values.     *)
(*                                                                     *)
(*     The values must be right-shifted by eight bits by the "updcrc"  *)
(*     logic; the shift must be unsigned (bring in zeroes).  On some   *)
(*     hardware you could probably optimize the shift in assembler by  *)
(*     using byte-swap instructions.                                   *)
(*     polynomial $edb88320                                            *)
(*                                                                     *)

{$Ifdef VirtualPascal}
Uses use32;
{$Endif}

VAR crc_32_tab: ARRAY[0..255] OF LONGINT;

FUNCTION UpdC32(octet: BYTE; crc: LONGINT) : LONGINT;
{$IFDEF PPC_BP}
Assembler;
asm
  mov cx, word ptr crc
  mov ax, cx
  mov bx, word ptr crc+2
  mov dx, bx
  shr cx, 8
  mov ch, bl
  shr bx, 8
  and cx, 0ffffh
  and bx, 00ffh
  xor al, octet
  xor ah, ah
  shl ax, 2
  mov di, ax
  mov ax, word ptr [di+crc_32_tab]
  mov dx, word ptr [di+crc_32_tab+2]
  xor ax, cx
  xor dx, bx
end;
{$ELSE}
{$IFDEF PPC_VIRTUAL}
Assembler;
{$Uses EDX}
{$Frame-}
asm
  mov eax, crc
  mov edx, eax
  shr eax, 8
  and eax, 0ffffffh
  xor edx, dword ptr octet
  and edx, 0ffh
  shl edx, 2
  xor eax, dword ptr [edx+crc_32_tab]
end;
{$ELSE}
begin
  UpdC32 := CRC_32_tab[System.Byte(crc xor System.LongInt(octet))] xor
           ((crc shr 8) and $00ffffff);

end;
{$ENDIF PPC_VIRTUAL}
{$ENDIF PPC_BP}

Function NormalizeCRC(Orig:Longint):Longint;
{$IFDEF VIRTUALPASCAL}
Assembler;
{$Uses None}
{$Frame-}
asm
  mov eax,Orig
  {$IFOPT G3+}
    xchg al,ah
    rol eax,16
    xchg al,ah
  {$ELSE}
    bswap eax   { Only 486 + Pentium }
  {$ENDIF}
end;
{$ELSE}
{$IFDEF PPC_BP}
Assembler;
asm
  mov ax,word ptr Orig+2
  mov dx,word ptr Orig
  xchg al,ah
  xchg dl,dh
end;
{$ELSE}
var
   count : Integer;
   L	 : Longint;
begin
   for count:=1 to 4 do begin
      l:=(l shl 8) or Byte(Orig);
   end;
   NormalizeCRC:=l;
end;
{$ENDIF}
{$ENDIF}

Procedure makeCRC32table;
Var
  crc : LongInt;
  i : integer;
  n : Byte;
begin
  For i := 0 to 255 do
  begin
    crc := i;
    For n := 1 to 8 do
      if odd(crc) then
        crc := (crc shr 1) xor $EDB88320
      else
        crc := crc shr 1;

    crc_32_tab[i] := crc;
  end;
end;


BEGIN
  makeCRC32Table;
END. {unit}

