ron%hcr@uunet.UU.NET (05/04/90)
The following is a crc16 routine for the 32000 family, note that it is faster than the single bit loop version and the table used fits into cache. The C code has been tested on a 386 and a VAX. The results have also been compared to the vax CRC instruction results. Give it a try. CAUTION - This is a true crc16 routine, it has been used for many years on various communication protocols - it works. The CCITT routine posted is NOT a valid CCITT crc (it does not use an initial value of 0xfff ...). The interesting property of the crc16 routine is that a buffer of zeros has a crc value of 0, this is NOT true for the CCITT crc. ; ; crc16.s ; ; Declare all routines as exports ; ; ; *** unsigned short crc16(out,buffer,size) ; char *out; ; char *buffer; ; unsigned short size; ; This routine returns the crc16 for 'buffer' of 'size'. ; If 'out' is non-zero, result is also stored there. ; Registers: r0 = result ; r7 = buffer pointer ; r6 = size remaining ; r5,r4 intermediate results ; crc16: enter [r4,r5,r6,r7],0x00 ; Enter frame movd 12(fp),r7 ; Point to buffer movzwd 16(fp),r6 ; Buffer size movd 0,r0 ; start result ; ; buffer loop ; crc_1: movzbd 0(r7),r5 ; Get next character xorb r0,r5 ; Xor low byte with previous movd r5,r4 ; Copy andb 0x0f,r5 ; Extract low nibble movw crc16l[r5:w],r5 ; Table entry for low nibble andb 0xf0,r4 ; Extract hi nibble lshb -3,r4 ; Index [w] to table xorw crc16h[r4:b],r5 ; Xor table entry for hi nibble lshw -8,r0 ; Old hi byte to low byte xorw r5,r0 ; Xor table entries addd 1,r7 ; Update pointer acbw -1,r6,crc_1 ; Dec size and repeat ; cmpd 0,8(fp) ; Output pointer specified? beq crc_2 ; No movw r0,0(8(fp)) ; Yes - save result there crc_2: exit [r4,r5,r6,r7] ; Restore ret 0 ; and return ; ; crc16 table: low and hi halves, each 16 words ; .align 4 ; word align for speed crc16l: .word 0x0000,0xc0c1,0xc181,0x0140 .word 0xc301,0x03c0,0x0280,0xc241 .word 0xc601,0x06c0,0x0780,0xc741 .word 0x0500,0xc5c1,0xc481,0x0440 crc16h: .word 0x0000,0xcc01,0xd801,0x1400 .word 0xf001,0x3c00,0x2800,0xe401 .word 0xa001,0x6c00,0x7800,0xb401 .word 0x5000,0x9c01,0x8801,0x4400 ------------------------------ /* C version */ static int crc(); int crc_table(); /* crc16 - DDCMP and Bisync (16 bit) * x^16 + x^15 + x^2 + 1 */ #define TEST 1 /* set to 1 for testing */ #define SINGLE 0 /* single bit version */ /* crc16 - crc16 routine * * R.K. Irvine * * This routine returns the crc16 for the buffer pointed * to by "in" of size "size". * crc16 is given by: x^16 + x^15 + x^2 + 1 */ unsigned short crc16_l[] = { 0x0000,0xc0c1,0xc181,0x0140, 0xc301,0x03c0,0x0280,0xc241, 0xc601,0x06c0,0x0780,0xc741, 0x0500,0xc5c1,0xc481,0x0440, }; unsigned short crc16_h[] = { 0x0000,0xcc01,0xd801,0x1400, 0xf001,0x3c00,0x2800,0xe401, 0xa001,0x6c00,0x7800,0xb401, 0x5000,0x9c01,0x8801,0x4400, }; unsigned short crc16(in, size) unsigned char *in; int size; { unsigned short c, n, crc; crc = 0; while (size--) { n = *in++ ^ crc; crc = crc16_l[n&0x0f] ^ crc16_h[(n>>4)&0x0f] ^ (crc>>8); } return(crc); } #if TEST /* test routine */ main() { printf("CRC16 test routine\n"); test("Hello world.", 0x33f9); test("12345678901234567890", 0x9b37); test("if else while case", 0xdb7f); printf("done\n"); } test(s, n) unsigned char *s; int n; { static t=0; int r; #if SINGLE /* test with single bit version */ r = crc_ok(s); #else /* test nibble version */ r = crc16(s, strlen(s)); #endif printf("test %d: '%s' = %x; ", t++, s, r); if (r != n) { printf("BAD, should be %x\n", n); } else { printf("OK\n"); } } /* single bit version of crc16 */ unsigned crc_byte(crc, new_ch) register unsigned short crc; int new_ch; { register unsigned short bit; register int i; for (i=1; i <= 0x80; i<<=1) { bit = ((new_ch&i)?0x8000:0); if (crc & 1) bit ^= 0x8000; crc >>= 1; if (bit) crc ^= (int)(0x2001); crc |= bit; } return crc; } crc_ok(p) char *p; { unsigned crc = 0; while (*p) { crc = crc_byte(crc, *p++); } return(crc); } #endif
culberts@hplwbc.hpl.hp.com (Bruce Culbertson) (05/04/90)
When I implemented the monitor download command, I considered the various popular CRC codes. I chose the one I did because I thought it was compatible with both Minix and CCITT. CCITT is definitely the wave of the future -- the organizations behind it have far more clout than the people using CRC16. I am disappointed to hear that the Minix people blew it when they implemented their CRC -- that it is not really CCITT. My preference, however, is to try to convince them to fix their code and bring it into conformance. By the way, does anyone know what POSIX specifies for the "sum" command? Is there a POSIX "crc" command? I used a bit serial algorithm to save ROM space (no table required) and because download time was dominated by the serial port throughput. I like the small table size used by the posted CRC16 routine. Bruce Culbertson