(Reconstructed by hand from 10-year-old listings. This is Turbo Pascal 3.01A or older, circa 1985, modified slightly to work under Borland Pascal 7.0, 1996-04-30/tfr. Note that the "less than" character is modified to < for HTML. )
{ file crctime.pas, 85/9/15/tfr (from 7/13, 6/27-26,
showtime.pas 3/23, 1/4, 84/12/25
and showkeys 12/12-11, 11/17
and strnkey.pas, 11/17 }
{ compute crc execution times }
{ Copyright (c) 1984, 1985, T.F. Ritter; All Rights Reserved }
{ display a number of different CRC-CCITT implementations }
{ verify identical operation }
{ collect and display time statistics }
(* under 7.0 on a P90 results should be displayed in usec! *)
PROGRAM showtime;
{$R-} { Range Checks OFF }
{$C-} { Ctrl-C Checking OFF }
USES
Dos; { defines Registers type and MsDos routine in 7.0 }
PROCEDURE ownership;
BEGIN
WRITE(
^M^J, 'CRCTIME, 85/9/15',
^M^J, 'Execution times for various CRC implementations.',
^M^J, 'Copyright (c) 1985, T.F. Ritter; All Rights Reserved.',
^M^J );
END;
(* unnecessary in 7.0
TYPE
registers = RECORD
ax,bx,cx,dx,bp,si,di,ds,es,flags: INTEGER;
END;
PROCEDURE bdosch( ch: CHAR );
{ direct console output through MSDOS }
{ enables Ctrl-P printer toggle }
VAR
r: registers;
BEGIN
r.ax := $0200;
r.dx := ORD(ch);
MsDos( r );
END;
*)
{ start of time operations }
TYPE
timearty = ARRAY[0..3] of INTEGER; {originally had yr, dy}
PROCEDURE readhms( VAR dt: timearty );
{ Hi(dt[2]) = hrs (0 - 23), Lo(dt[2]) = mins (0 - 59) }
{ Hi(dt[3]) = secs (0 - 59), Lo(dt[3] = msecs (0 - 99) }
VAR
r: Registers;
BEGIN
r.ax := $2c00;
MsDos( r );
dt[2] := r.cx;
dt[3] := r.dx;
END; {rddtti}
FUNCTION timetorealsecs( x: timearty ): REAL;
BEGIN
timetorealsecs := (Hi(x[2]) * 3600.0) + (Lo(x[2])* 60.0) +
Hi(x[3]) + (Lo(x[3]) / 100.0);
END;
FUNCTION timedif( a, b: timearty ): REAL;
BEGIN
timedif := timetorealsecs( b ) - timetorealsecs( a );
END;
{ start of CRC stuff }
{ byte crc in Turbo Pascal }
{ for MSDOS }
{ METHOD 1: Pascal Bit-By-Bit }
{ bit manipulation in a high-level-language }
{ simulation of "longhand" division " }
{ mostly for verification of other methods }
PROCEDURE crcittb( VAR a: INTEGER; bit: BOOLEAN );
{ single bit computation; simulate polydiv hardware }
{ other Pascals can replace "a Shl 1" with "a + a" }
BEGIN
IF bit = (a < 0 ) THEN
a := a Shl 1
ELSE
a := (a Shl 1) XOR $1021; { CRC-CCITT }
END; {crcittb }
PROCEDURE crcittby( VAR aa: INTEGER; d: INTEGER );
{ whole byte computation }
VAR
i: INTEGER;
BEGIN
d := d Shl 7;
FOR i := 7 DOWNTO 0 DO
BEGIN
d := d Shl 1;
crcittb( aa, (d < 0) );
END;
END; { crcittby }
{ METHOD 2: Pascal Fast Bit-By-Bit }
{ eliminates procedure overhead in the loop }
{ similar to most XMODEM crc implementations }
PROCEDURE crcfbbb( VAR aa: INTEGER; d: INTEGER );
{ fast bit-by-bit whole byte computation }
VAR
i: INTEGER;
BEGIN
d := d Shl 7;
FOR i := 7 DOWNTO 0 DO
BEGIN
d := d Shl 1;
IF ((d XOR aa) < 0) THEN
aa := (aa Shl 1) XOR $1021
ELSE
aa := aa Shl 1;
END;
END; { crcfbbb }
{ METHOD 3: Pascal Byte }
{ process the data byte without loops }
{ transportable within Turbo Pascal, and fairly fast }
{ may be slower in Pascals without bit-shifting opns }
PROCEDURE crcitta( var dx: INTEGER; data: INTEGER );
{ dx := crcTransform( dx; data ) }
{ polynomial = $11021 }
{ for other Pascals, generate a Swap function }
{ probably x := (x * 256) + (x DIV 256) would work }
{ Lo(x) = x AND $00ff = x AND 255 }
{ x Shr 4 = x DIV 16 }
{ x Shl 4 = x * 16; x Shl 5 = x * 32 }
BEGIN { crcitta }
dx := Swap( dx ) XOR data;
dx := dx XOR ( Lo(dx) Shr 4 );
dx := dx XOR (Swap(Lo(dx)) Shl 4) XOR (Lo(dx) Shl 5);
END; { crcitta }
{ METHOD 4: Pascal Table }
{ pull changes from table, indexed by composite value }
{ still faster, but requires the table, and filling the table }
{ may be easier to port into a quick routine in another Pascal }
{ a slower routine is fine to fill the table }
VAR
crctab: ARRAY[0..255] of INTEGER;
PROCEDURE crctablu( VAR crcreg: INTEGER; data: INTEGER );
BEGIN
crcreg := Swap(crcreg) XOR data;
crcreg := (crcreg AND $ff00) XOR crctab[ Lo(crcreg) ];
END;
PROCEDURE initctab;
{ use method 3 to init the table }
VAR
i: INTEGER;
BEGIN
FOR i := 0 TO 255 DO
BEGIN
crctab[i] := 0;
crcitta( crctab[i], i );
END;
END; {initctab}
{ METHOD 5: Machine Code Byte }
{ method 3 ini "Inline" machine code (MSDOS) }
{ typically hidden away in an "Include" file }
{ other Pascals may need to modify the stack interface }
PROCEDURE mcrcitt1( VAR dx: INTEGER; data: INTEGER );
{ a := crcTransform( ds; data ) }
{ for MSDOS }
{ polynomial = $11021 }
BEGIN { mcrcitt1 }
INLINE (
$c4/$7e/<dx/ { es:di := [bp+ "dx"] }
$26/$8b/$05/ { ax := [es:di] }
{ dx := SWAP(dx) XOR data; }
$86/$e0/ { ah <=> al }
$33/$46/<data/ { ax := ax XOR [bp + "data"] }
$89/$c2/ { dx := ax }
{ dx := dx XOR ( LO(dx) SHR 4 ); }
$d0/$e8/ { al := al SHR 1 }
$d0/$e8/ { al := al SHR 1 }
$d0/$e8/ { al := al SHR 1 }
$d0/$e8/ { al := al SHR 1 }
$32/$d0/ { dl := dl XOR al }
{ dx := dx XOR ( (SWAP( LO(dx) ) SHL 4 ); }
$88/$d4/ { ah := dl }
$d0/$e4/ { ah := ah SHL 1 }
$d0/$e4/ { ah := ah SHL 1 }
$d0/$e4/ { ah := ah SHL 1 }
$d0/$e4/ { ah := ah SHL 1 }
$32/$f4/ { dh := dh XOR ah }
{ dx := dx XOR ( ( LO(dx) ) SHL 5); }
$88/$d0/ { al := dl }
$b4/$00/ { ah := 0 }
$d1/$e0/ { ax := ax SHL 1 }
$d1/$e0/ { ax := ax SHL 1 }
$d1/$e0/ { ax := ax SHL 1 }
$d1/$e0/ { ax := ax SHL 1 }
$d1/$e0/ { ax := ax SHL 1 }
$33/$d0/ { dx := dx XOR ax }
$26/$89/$15 { es:[di] := dx } );
END; { mcrcitt1 }
{ METHOD 6: Machine Code Table }
{ here the stack parameter interface becomes significant }
{ note the exchange notation "x :=: y" in the comments }
PROCEDURE mcrcitt3( VAR dx: INTEGER; data: INTEGER );
{ a := crcTransform( dx; data ) }
{ for MSDOS }
{ polynomial = $11021 }
BEGIN { mcrcitt3 }
INLINE (
$c4/$7e/<dx/ { es:di := [bp + "dx"] }
$26/$8b/$15/ { dx := [es:di] }
{ dx := Swap(dx) XOR data; }
$86/$d6/ { dh :=: dl }
$33/$56/<data/ { dx := dx XOR [bp + "data"] }
{ bx := Lo(dx) SHL 1; dl := 0; }
$31/$db/ { bx := bx XOR bx }
$86/$d3/ { bl :=: dl }
$d1/$e3/ { bx := bx SHL 1 }
{ dx := dx XOR crctab[ bx ]; }
$33/$97/>crctab/ { dx := dx XOR [bx + "crctab"] }
$26/$89/$15 { [es:di] := dx } );
END; { mcrcitt3 }
{ start of validation testing }
PROCEDURE fultest( beg, en: INTEGER );
TYPE
st40 = STRING[40];
VAR
adi, adr, adt: INTEGER;
PROCEDURE overall( VAR a: INTEGER; t: INTEGER );
VAR
loc: INTEGER;
data: BYTE;
BEGIN
a := adi;
FOR loc := beg TO en DO
BEGIN
data := MEM[ Cseg: loc ];
CASE t OF
1: crcittby( a, data );
2: crcfbbb( a, data );
3: crcitta( a, data );
4: crctablu( a, data );
5: mcrcitt1( a, data );
6: mcrcitt3( a, data );
END; {case}
END;
END; {overall}
PROCEDURE pbin( value: INTEGER );
VAR
i: INTEGER;
BEGIN
FOR i := 15 DOWNTO 0 DO
BEGIN
IF value < 0 THEN
WRITE( '1' )
ELSE
WRITE( '0' );
value := value SHL 1;
END;
END; {pbin}
PROCEDURE showres( s: st40 );
BEGIN
WRITE( ^M^J' ', s );
IF (adt <> adr) THEN
BEGIN
WRITE(': CRC error. ' );
WRITE( ^M^J, 'Reference: '); pbin( adr );
WRITE( ^M^J, 'Under Test: '); pbin( adt );
END
ELSE
WRITE( ': No error.' );
END; {showres}
BEGIN {fultest}
WRITE( ^M^J^J'BEGIN Validation Testing.' );
adi := -1;
initctab;
overall( adr, 1 );
WRITE( ^M^J' Reference = crcittby (Pascal Bit-By-Bit).' );
overall( adt, 2 );
showres( 'crcfbbb (Pascal Fast Bit-By-Bit)' );
overall( adt, 3 );
showres( 'crcitta (Pascal Byte)' );
overall( adt, 4 );
showres( 'crctablu (Pascal Table)' );
overall( adt, 5 );
showres( 'mcrcitt1 (Machine Code Byte)' );
overall( adt, 6 );
showres( 'mcrcitt3 (Machine Code Table)' );
WRITELN( ^M^J'END Validation Testing.' );
END; { fultest}
{ start of timing }
PROCEDURE timetest( loops: INTEGER );
{ organize the time ttesting of various routines }
VAR
a, b: timearty;
i, x: INTEGER;
by: BYTE;
empty: REAL; { time for empty loops }
calltime: REAL; { time for loops and empty procedure }
PROCEDURE crcmt( VAR dx: INTEGER; data: INTEGER );
BEGIN
END;
PROCEDURE showtimedif( a, b: timearty; c: INTEGER );
{ display a line of time results }
VAR
dif, Noloop, NoloopNocall: REAL;
BEGIN
dif := timedif( a, b );
Noloop := dif - empty;
NoloopNocall := dif - calltime;
WRITE(
Noloop:7:3, ' ', NoloopNocall:7:3, ' ',
Noloop* (1000.0 / c):7:3, ' ', NoloopNocall * (1000.0 / c):7:3 );
END; {showtimedif}
BEGIN {timetest}
calltime := 0.0; {the global}
by := $a5; {your typical data byte}
WRITELN(
^M^J^J'Turbo Pascal runs CRC-CCITT on 8088 under Bare MSDOS',
^M^J' FOR ', loops, ' OPERATIONS; 7.16 MHz CLOCK (multiply by 1.5 for 4.77 MHz) ' );
WRITE( ^M^J'Empty loop: ' );
readhms( a );
FOR i := 1 TO loops DO
;
readhms( b );
empty := timedif( a, b ); {the global}
WRITE( empty:5:3, ' secs' );
WRITE( ^M^J'Empty procedure in loop: ' );
readhms( a );
FOR i := 1 TO loops DO
crcmt( x, by );
readhms( b );
calltime := timedif( a, b ); {another global}
WRITE( calltime:5:3, ' secs' );
WRITE( ^M^J' (procedure overhead alone = ',
(calltime - empty) * (1000.0 / loops):6:3,
' msec each)' );
WRITELN(
^M^J^J' ', loops:5, ' Uses (secs) 1 Use (msec)',
^M^J' Procedure In Line Procedure In Line' );
WRITE(
^M^J'Pascal Bit-by-Bit: ' );
readhms( a );
FOR i := 1 TO loops DO
crcittby( x, by );
readhms( b );
showtimedif( a, b, loops );
WRITE(
^M^J'Pascal Fast B-B-B: ' );
readhms( a );
FOR i := 1 TO loops DO
crcfbbb( x, by );
readhms( b );
showtimedif( a, b, loops );
WRITE(
^M^J'Pascal Byte: ' );
readhms( a );
FOR i := 1 TO loops DO
crcitta( x, by );
readhms( b );
showtimedif( a, b, loops );
WRITE(
^M^J'Pascal Table: ' );
readhms( a );
FOR i := 1 TO loops DO
crctablu( x, by );
readhms( b );
showtimedif( a, b, loops );
WRITE(
^M^J'Machine Code Byte: ' );
readhms( a );
FOR i := 1 TO loops DO
mcrcitt1( x, by );
readhms( b );
showtimedif( a, b, loops );
WRITE(
^M^J'Machine Code Table: ' );
readhms( a );
FOR i := 1 TO loops DO
mcrcitt3( x, by );
readhms( b );
showtimedif( a, b, loops );
WRITELN;
END; {timetest}
PROCEDURE datatimes;
{ data character times, for comparison }
CONST
rates: ARRAY[1..7] of INTEGER =
( 12, 24, 48, 96, 192, 384, 576 );
VAR i: INTEGER;
BEGIN
WRITELN( ^M^J^J'Character Times for Various Bit Rates: ' );
WRITE( ^M^J' bits/sec char/sec msec/char' );
FOR i := 1 TO 7 DO
WRITE( ^M^J, '':4, (100.0 * rates[i]):5:0, '':8,
(10.0 * rates[i]):5:0, '':8,
(100.0 / rates[i]):5:3 );
WRITELN;
END; {datatimes}
BEGIN { main: crctime }
WRITELN;
(* not useful in 7.0
ConOutPtr := OFS( bdosch ); {output through MSDOS}
*)
ownership;
fultest( 100, 1000 );
timetest( 10000 );
datatimes;
END.
{ end file crctime.pas }
Last updated: 1996-04-30