You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
460 lines
15 KiB
460 lines
15 KiB
/* (C) 1997-1998 Microsoft Corp.
|
|
*
|
|
* CBC64 - 64-bit hashing algorithm
|
|
*
|
|
* CBC64 is part of a Microsoft Research project to create a fast encryption
|
|
* algorithm for the NT5 NTFS-EFS encrypted disk subsystem. Though
|
|
* originally intended for encryption use, CBC64 makes a fast hash
|
|
* and checksum function with known probabilities for failure and known
|
|
* good variability based on input.
|
|
*
|
|
* CBC64 takes as input four seed values a, b, c, and d. The bits of a and
|
|
* c MUST correspond to the coefficients of an irreducible polynomial.
|
|
* Associated code (RandomIrreduciblePolynomial(), which is included in this
|
|
* file but ifdef-ed out) will take a random number as seed value and
|
|
* converge to a usable value (or zero if the seed cannot be converged,
|
|
* in which case a new random seed must be tried).
|
|
*
|
|
* In an encryption context, a, b, c, and d would correspond to "secrets"
|
|
* held by the encrypter and decrypter. For our purposes -- use as a hash
|
|
* function -- we can simply hardcode all four -- a and c with values from
|
|
* RandomIrreduciblePolynomial() on two random numbers, b and d with
|
|
* simple random numbers.
|
|
*
|
|
* For a set of N inputs (e.g. bitmaps) the probability of duplicate hash
|
|
* value creation has been proven to be (N^2)/(2^64).
|
|
*
|
|
* Original algorithm notes:
|
|
* From "Chain& Sum primitive and its applications to
|
|
* Stream Ciphers and MACs"
|
|
* Ramarathnam Venkatesan (Microsoft Research)
|
|
* Mariusz Jackubowski (Princeton/MS Research)
|
|
*
|
|
* To Appear in Advances in Cryptology EuroCrypt 98, Springer Verlag.
|
|
* Patent rights being applied for by Microsoft.
|
|
*
|
|
* Extracted from the algorithm for simulating CBC-Encryption and its
|
|
* message integrity properties using much faster stream ciphers. Its
|
|
* analysis appears in the above paper.
|
|
*
|
|
* Algorithm is a MAC with input preprocessing by ((Alpha)x + (Beta))
|
|
* mod 2^32, followed by forward (ax + b) and (cx + d) in field GF(2^32).
|
|
*/
|
|
|
|
#include "cbchash.h"
|
|
|
|
|
|
#define CBC_a 0xBB40E665
|
|
#define CBC_b 0x544B2FBA
|
|
#define CBC_c 0xDC3F08DD
|
|
#define CBC_d 0x39D589AE
|
|
#define CBC_bXORa (CBC_b ^ CBC_a)
|
|
#define CBC_dXORc (CBC_d ^ CBC_c)
|
|
|
|
|
|
// For removal of branches in main loop -- over twice as fast as the code
|
|
// with branches.
|
|
const UINT32 CBC_AB[2] = { CBC_b, CBC_bXORa };
|
|
const UINT32 CBC_CD[2] = { CBC_d, CBC_dXORc };
|
|
|
|
|
|
void __fastcall SHCLASS NextCBC64(
|
|
CBC64Context *pContext,
|
|
UINT32 *pData,
|
|
unsigned NumDWORDBlocks)
|
|
{
|
|
while (NumDWORDBlocks--) {
|
|
// Checksum is used to overcome known collision characteristics of
|
|
// CBC64. It is a low-tech solution that simply decreases the
|
|
// probability of collision where used.
|
|
pContext->Checksum += *pData;
|
|
|
|
pContext->Datum = CBC_RandomOddAlpha * (*pData + pContext->Datum) +
|
|
CBC_RandomBeta;
|
|
pContext->Key1 ^= pContext->Datum;
|
|
pContext->Key1 = (pContext->Key1 << 1) ^
|
|
(CBC_CD[(pContext->Key1 & 0x80000000) >> 31]);
|
|
pContext->Key2 ^= pContext->Datum;
|
|
pContext->Key2 = (pContext->Key2 << 1) ^
|
|
(CBC_AB[(pContext->Key2 & 0x80000000) >> 31]);
|
|
pData++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*
|
|
* Support functions for determining irreducible a and c.
|
|
*
|
|
*/
|
|
|
|
#if 0
|
|
|
|
|
|
// Original CBC64 from which the First()-Next() algorithm was derived.
|
|
// Returns the first part of the key value; second key part is last param.
|
|
UINT32 __fastcall SHCLASS CBC64(
|
|
UINT32 *Data,
|
|
unsigned NumDWORDBlocks,
|
|
UINT32 *pKey2)
|
|
{
|
|
int i;
|
|
UINT32 abMAC, cdMAC, Datum;
|
|
|
|
// For removal of branches in main loops -- over twice as fast as the code
|
|
// with branches.
|
|
const UINT32 AB[2] = { CBC_b, CBC_bXORa };
|
|
const UINT32 CD[2] = { CBC_d, CBC_dXORc };
|
|
|
|
// Block 0.
|
|
abMAC = cdMAC = Datum = *Data * CBC_RandomOddAlpha + CBC_RandomBeta;
|
|
abMAC = (abMAC << 1) ^ (AB[(cdMAC & 0x80000000) >> 31]);
|
|
cdMAC = (cdMAC << 1) ^ (CD[(cdMAC & 0x80000000) >> 31]);
|
|
|
|
// Blocks 1 through nblocks - 2
|
|
i = NumDWORDBlocks - 1;
|
|
while (--i) {
|
|
Data++;
|
|
Datum = CBC_RandomOddAlpha * (*Data + Datum) + CBC_RandomBeta;
|
|
cdMAC ^= Datum;
|
|
cdMAC = (cdMAC << 1) ^ (CD[(cdMAC & 0x80000000) >> 31]);
|
|
abMAC ^= Datum;
|
|
abMAC = (abMAC << 1) ^ (AB[(abMAC & 0x80000000) >> 31]);
|
|
}
|
|
|
|
// Last block (nblocks - 1)
|
|
Data++;
|
|
Datum = CBC_RandomOddAlpha * (*Data + Datum) + CBC_RandomBeta;
|
|
cdMAC ^= Datum;
|
|
cdMAC = (cdMAC << 1) ^ (CD[(cdMAC & 0x80000000) >> 31]);
|
|
abMAC ^= Datum;
|
|
*pKey2 = (abMAC << 1) ^ (AB[(abMAC & 0x80000000) >> 31]);
|
|
|
|
return cdMAC;
|
|
}
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <windows.h>
|
|
|
|
|
|
// multiply y by x mod x^32+p
|
|
__inline UINT32 Multiply(UINT32 y, UINT32 p)
|
|
{
|
|
return (y & 0x80000000) ? (y<<1)^p : y<<1;
|
|
}
|
|
|
|
|
|
|
|
// divide y by x mod x^32+p
|
|
__inline UINT32 Divide(UINT32 y, UINT32 p)
|
|
{
|
|
return (y & 1) ? 0x80000000^((y^p)>>1) : y>>1;
|
|
}
|
|
|
|
|
|
|
|
// compute (x^32+p) mod qq[i]
|
|
__inline UINT32 PolyMod(UINT32 p, UINT32 i)
|
|
{
|
|
static const UINT32 qq[2]={18<<2, 127};
|
|
|
|
static const UINT32 rt[2][256] = {
|
|
0,32,8,40,16,48,24,56,32,0,40,8,48,16,56,24,8,40,0,32,
|
|
24,56,16,48,40,8,32,0,56,24,48,16,16,48,24,56,0,32,8,40,
|
|
48,16,56,24,32,0,40,8,24,56,16,48,8,40,0,32,56,24,48,16,
|
|
40,8,32,0,32,0,40,8,48,16,56,24,0,32,8,40,16,48,24,56,
|
|
40,8,32,0,56,24,48,16,8,40,0,32,24,56,16,48,48,16,56,24,
|
|
32,0,40,8,16,48,24,56,0,32,8,40,56,24,48,16,40,8,32,0,
|
|
24,56,16,48,8,40,0,32,8,40,0,32,24,56,16,48,40,8,32,0,
|
|
56,24,48,16,0,32,8,40,16,48,24,56,32,0,40,8,48,16,56,24,
|
|
24,56,16,48,8,40,0,32,56,24,48,16,40,8,32,0,16,48,24,56,
|
|
0,32,8,40,48,16,56,24,32,0,40,8,40,8,32,0,56,24,48,16,
|
|
8,40,0,32,24,56,16,48,32,0,40,8,48,16,56,24,0,32,8,40,
|
|
16,48,24,56,56,24,48,16,40,8,32,0,24,56,16,48,8,40,0,32,
|
|
48,16,56,24,32,0,40,8,16,48,24,56,0,32,8,40,
|
|
0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,
|
|
40,42,44,46,48,50,52,54,56,58,60,62,63,61,59,57,55,53,51,49,
|
|
47,45,43,41,39,37,35,33,31,29,27,25,23,21,19,17,15,13,11,9,
|
|
7,5,3,1,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,
|
|
33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,62,60,58,56,
|
|
54,52,50,48,46,44,42,40,38,36,34,32,30,28,26,24,22,20,18,16,
|
|
14,12,10,8,6,4,2,0,2,0,6,4,10,8,14,12,18,16,22,20,
|
|
26,24,30,28,34,32,38,36,42,40,46,44,50,48,54,52,58,56,62,60,
|
|
61,63,57,59,53,55,49,51,45,47,41,43,37,39,33,35,29,31,25,27,
|
|
21,23,17,19,13,15,9,11,5,7,1,3,3,1,7,5,11,9,15,13,
|
|
19,17,23,21,27,25,31,29,35,33,39,37,43,41,47,45,51,49,55,53,
|
|
59,57,63,61,60,62,56,58,52,54,48,50,44,46,40,42,36,38,32,34,
|
|
28,30,24,26,20,22,16,18,12,14,8,10,4,6,0,2};
|
|
|
|
p ^= qq[i]<<(32-6);
|
|
|
|
p ^= rt[i][p>>24]<<16;
|
|
p ^= rt[i][(p>>16)&0xff]<<8;
|
|
p ^= rt[i][(p>>8)&0xff];
|
|
|
|
if (p&(1<<7))
|
|
p ^= qq[i]<<1;
|
|
if (p&(1<<6))
|
|
p ^= qq[i];
|
|
|
|
return p % (1<<6);
|
|
}
|
|
|
|
|
|
|
|
// do an analogue of Fermat test on x^32+p
|
|
BOOL Irreducible(UINT32 p)
|
|
{
|
|
static const UINT32 expand[256] = {
|
|
0x0, 0x1, 0x4, 0x5, 0x10, 0x11, 0x14, 0x15,
|
|
0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55,
|
|
0x100, 0x101, 0x104, 0x105, 0x110, 0x111, 0x114, 0x115,
|
|
0x140, 0x141, 0x144, 0x145, 0x150, 0x151, 0x154, 0x155,
|
|
0x400, 0x401, 0x404, 0x405, 0x410, 0x411, 0x414, 0x415,
|
|
0x440, 0x441, 0x444, 0x445, 0x450, 0x451, 0x454, 0x455,
|
|
0x500, 0x501, 0x504, 0x505, 0x510, 0x511, 0x514, 0x515,
|
|
0x540, 0x541, 0x544, 0x545, 0x550, 0x551, 0x554, 0x555,
|
|
0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
|
|
0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
|
|
0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
|
|
0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
|
|
0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
|
|
0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
|
|
0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
|
|
0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
|
|
0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
|
|
0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
|
|
0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
|
|
0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
|
|
0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
|
|
0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
|
|
0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
|
|
0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
|
|
0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
|
|
0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
|
|
0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
|
|
0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
|
|
0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
|
|
0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
|
|
0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
|
|
0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555};
|
|
|
|
// tables used for fast squaring
|
|
UINT32 pp[4], ph[16], pl[4][16];
|
|
UINT32 g, v;
|
|
int i;
|
|
|
|
pp[0] = 0;
|
|
pp[1] = p;
|
|
|
|
if (p&0x80000000)
|
|
{
|
|
pp[2] = p ^ (p<<1);
|
|
pp[3] = p<<1;
|
|
}
|
|
else
|
|
{
|
|
pp[2] = p<<1;
|
|
pp[3] = p ^ (p<<1);
|
|
}
|
|
|
|
v = 0x40000000;
|
|
for (i=0; i<16; i++)
|
|
ph[i] = v = (v<<2) ^ pp[v >> 30];
|
|
|
|
for (i=0; i<4; i++)
|
|
{
|
|
pl[i][0] = 0;
|
|
pl[i][1] = ph[4*i+0];
|
|
pl[i][2] = ph[4*i+1];
|
|
pl[i][3] = ph[4*i+0] ^ ph[4*i+1];
|
|
pl[i][4] = ph[4*i+2];
|
|
pl[i][5] = ph[4*i+2] ^ ph[4*i+0];
|
|
pl[i][6] = ph[4*i+2] ^ ph[4*i+1];
|
|
pl[i][7] = pl[i][5] ^ ph[4*i+1];
|
|
|
|
pl[i][8] = ph[4*i+3];
|
|
pl[i][9] = ph[4*i+3] ^ ph[4*i+0];
|
|
pl[i][10] = ph[4*i+3] ^ ph[4*i+1];
|
|
pl[i][11] = pl[i][9] ^ ph[4*i+1];
|
|
pl[i][12] = ph[4*i+3] ^ ph[4*i+2];
|
|
pl[i][13] = pl[i][12] ^ ph[4*i+0];
|
|
pl[i][14] = pl[i][10] ^ ph[4*i+2];
|
|
pl[i][15] = pl[i][14] ^ ph[4*i+0];
|
|
}
|
|
|
|
// compute x^(2^16) mod x^32+p
|
|
|
|
// x^32 mod x^32+p = p
|
|
g = p;
|
|
|
|
// square g
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^ pl[3][(g>>28)&0xf];
|
|
|
|
// square g 10 more times to get x^(2^16)
|
|
for (i=0; i<2; i++)
|
|
{
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^ pl[3][(g>>28)&0xf];
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^ pl[3][(g>>28)&0xf];
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^ pl[3][(g>>28)&0xf];
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^ pl[3][(g>>28)&0xf];
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^ pl[3][(g>>28)&0xf];
|
|
}
|
|
|
|
// if x^(2^16) mod x^32+p = x then x^32+p has a divisor whose degree is a power of 2
|
|
if (g==2)
|
|
return FALSE;
|
|
|
|
// compute x^(2^32) mod x^32+p
|
|
for (i=0; i<4; i++)
|
|
{
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^
|
|
pl[3][(g>>28)&0xf];
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^
|
|
pl[3][(g>>28)&0xf];
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^
|
|
pl[3][(g>>28)&0xf];
|
|
g = expand[g&0xff] ^ (expand[(g>>8)&0xff]<<16) ^
|
|
pl[0][(g>>16)&0xf] ^ pl[1][(g>>20)&0xf] ^ pl[2][(g>>24)&0xf] ^
|
|
pl[3][(g>>28)&0xf];
|
|
}
|
|
|
|
// x^32+p is irreducible iff x^(2^16) mod x^32+p != x and
|
|
// x^(2^32) mod x^32+p = x
|
|
return (g == 2);
|
|
}
|
|
|
|
|
|
|
|
// Take as input a random 32-bit value
|
|
// If successful, output is the lowest 32 bits of a degree 32 irreducible
|
|
// polynomial otherwise output is 0, in which case try again with a different
|
|
// random input.
|
|
UINT32 RandomIrreduciblePolynomial(UINT32 p)
|
|
{
|
|
#define interval (1 << 6)
|
|
BYTE sieve[interval];
|
|
UINT32 r;
|
|
int i;
|
|
|
|
memset(sieve, 0, interval);
|
|
|
|
p ^= p % interval;
|
|
|
|
r = PolyMod(p, 0);
|
|
sieve[r] = 1;
|
|
sieve[r^3] = 1;
|
|
sieve[r^5] = 1;
|
|
sieve[r^15] = 1;
|
|
sieve[r^9] = 1;
|
|
sieve[r^27] = 1;
|
|
sieve[r^29] = 1;
|
|
sieve[r^23] = 1;
|
|
sieve[r^17] = 1;
|
|
sieve[r^51] = 1;
|
|
sieve[r^53] = 1;
|
|
sieve[r^63] = 1;
|
|
sieve[r^57] = 1;
|
|
sieve[r^43] = 1;
|
|
sieve[r^45] = 1;
|
|
sieve[r^39] = 1;
|
|
sieve[r^33] = 1;
|
|
sieve[r^7] = 1;
|
|
sieve[r^21] = 1;
|
|
sieve[r^49] = 1;
|
|
sieve[r^35] = 1;
|
|
|
|
r = PolyMod(p, 1);
|
|
sieve[r] = 1;
|
|
sieve[r^11] = 1;
|
|
sieve[r^22] = 1;
|
|
sieve[r^29] = 1;
|
|
sieve[r^44] = 1;
|
|
sieve[r^39] = 1;
|
|
sieve[r^58] = 1;
|
|
sieve[r^49] = 1;
|
|
sieve[r^13] = 1;
|
|
sieve[r^26] = 1;
|
|
sieve[r^23] = 1;
|
|
sieve[r^52] = 1;
|
|
sieve[r^57] = 1;
|
|
sieve[r^46] = 1;
|
|
sieve[r^35] = 1;
|
|
|
|
for (i=1; i<interval; i+=2)
|
|
if (sieve[i]==0 && Irreducible(p^i) )
|
|
return p^i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// 16x2 similar bitmaps that come up with same CBC64 (error condition).
|
|
DWORD FailData[2][8] =
|
|
{
|
|
{ 0x00000010, 0x00000010, 0x00000001, 0x00000010,
|
|
0x61AA61AA, 0x61AA61AA, 0x6161AA61, 0xAAAA61AA },
|
|
{ 0x00000010, 0x00000010, 0x00000001, 0x00000010,
|
|
0x6AAA61AA, 0x61AA61AA, 0x6161AA61, 0x6AAA61AA }
|
|
};
|
|
|
|
|
|
|
|
int __cdecl main()
|
|
{
|
|
enum { LEN = 512 };
|
|
enum { REPS = 1000 };
|
|
UINT32 X[LEN+1];
|
|
UINT32 i;
|
|
UINT32 Key2;
|
|
double start1, end1, speed1;
|
|
|
|
for (i=0; i<LEN; X[i++]=i*486248); //junk data //
|
|
|
|
// initialize the variables a,b,c,d
|
|
// This has to be done only once
|
|
// a = RandomIrreduciblePolynomial(CBC_a);
|
|
// c = RandomIrreduciblePolynomial(CBC_c);
|
|
// Make sure these numbers are not zero; if not
|
|
// Call the routine Random...polynomial() with different seed
|
|
printf("a = %X, c = %X \n", RandomIrreduciblePolynomial(CBC_a),
|
|
RandomIrreduciblePolynomial(CBC_c));
|
|
|
|
// b = CBC_b; //put in some random number
|
|
// d = CBC_d; // put in some random number
|
|
|
|
printf("begin macing\n");
|
|
start1=clock();
|
|
for (i=0; i<REPS; i++)
|
|
CBC64(X, LEN, &Key2);
|
|
end1=clock();
|
|
printf("end macing \n");
|
|
speed1 = 32 * LEN * REPS / ((double) (end1 - start1)/CLOCKS_PER_SEC);
|
|
printf("MAC speed:%4.0lf\n %", speed1);
|
|
|
|
printf(" start1= %d end1= %d \n ", start1, end1);
|
|
scanf ("%d", &Key2); /* just hang so that I can see the output */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#endif // 0
|
|
|