CRC.DYL: A DYL for calculating common CRCs

This source code is placed in the public domain.

CRC.CAT - The Category File

! CRC Library - see SIBO SDK Vol 3, Page 3-4
! This DYL supplies its own root class - saves an external reference to OLIB
LIBRARY crc

INCLUDE p_std.h
INCLUDE p_object.h

CLASS root {
    ADD destroy
    PROPERTY
    {
        P_OBJECT                pc ;
    }
}

The root class we provide just eliminates the need for an external references. It could also be subsumed into the crc class below.
! Abstract base class
CLASS crc root
{
    DEFER crc_init              ! initializes a CRC object
    DEFER crc_calc              ! calculate (update) a CRC for a single byte
    DEFER crc_calc_len          ! calculate (update) a CRC for a stream of bytes
    PROPERTY
    {
        unsigned short          crctab[256] ;   ! table of values: crctab[n] is just crc_fn(n, polynomial, 0)
    }
}
The crc base class is an abstract base class which has just two purposes:
! Concrete CCITT CRC - used in X/YMODEM
CLASS ccitt crc
{
    REPLACE crc_init
    REPLACE crc_calc
    REPLACE crc_calc_len
}
The class ccitt inherits from the crc base class and replaces all methods. The CCITT CRC is the CRC used in the X-MODEM and Y-MODEM protocols. Psion provides an implementation for this CRC in the PLIB library, but this DYL also provides it as an example.
! Concrete CRC16 CRC - used in DEX/UCS
CLASS crc16 crc
{
    REPLACE crc_init
    REPLACE crc_calc
    REPLACE crc_calc_len
}
The class crc16 inherits from the crc base class and replaces all methods. The CRC-16 is used specifically in the DEX (DEX/UCS) protocol for data interchange used by supermarkets, retailers and vending machines.

CRC_O.C - The Source Code

Most of this code is based on Joe Campbell's "C Programmer's Guide to Serial Communications" published by Sam's. I highly recommend this book if you ever want to know anything about serial comms, modems, CRCs, etc. It will earn its keep after just one reference...

You can get it from Amazon.com, which I also highly recommend if you don't want to be tempted by going into a real bookstore.

// CRC generator for inclusion in an EPOC DYL 961105 JCRoux 73733.1014@compuserve.com

#include <crc.g>

// Code from C Programmer's Guide to Serial Communications, Joe Campbell, Sam's
typedef unsigned short USHORT ;
#define CRCCCITT        0x1021          // CCITT polynomial (for XMODEM etc.)
#define CRC16           0x8005          // CRC16 polynomial
#define CRC16_REV       0xA001          // Reverse CRC16 polynomial (for DEX/UCS etc.)
The polynomials are bit masks, where the bits in the mask identify which coefficients are used.
#define crcupdate(d, a, t) *(a) = (*(a) << 8) ^ (t)[(*(a) >> 8) ^ (d)]
#define crcupdate16(d, a, t) *(a) = ((*(a)>>8) ^ (t)[(*(a) ^ (d)) & 0x00FF])
These extremely noxious macros do most of the dirty work in getting the table results.
// "Classical" Hardware bitshift simulation of CRC
USHORT crc_hware(USHORT data, USHORT genpoly, USHORT accum) {
    int i ;

    data <<= 8 ;
    for ( i = 8 ; i > 0 ; i-- )  {
        if ( (data ^ accum) & 0x8000 ) {
            accum = (accum << 1) ^ genpoly ;
        }
        else {
            accum <<= 1 ;
        }
        data <<= 1 ;
    }

    return(accum) ;
}

// "Reverse" Hardware bitshift simulation of CRC
USHORT crc_revhware(USHORT data, USHORT genpoly, USHORT accum) {
    int i ;

    data <<= 1 ;
    for ( i = 8 ; i > 0 ; i-- )  {
        data >= 1 ;
        if ( (data ^ accum) & 0x0001 ) {
            accum = (accum > 1) ^ genpoly ;
        }
        else {
            accum >>= 1 ;
        }
    }

    return(accum) ;
}
The two routines above actually do the CRC calculations and are only used to build the tables in the crc ojects during initialization.
// Create a table
VOID make_crctbl(USHORT *ptable, USHORT poly, USHORT (*crcfn)(USHORT, USHORT, USHORT)) {
    int i ;
    for ( i = 0 ; i < 256 ; i++ )  {
        ptable[i] = (*crcfn)(i, poly, 0) ;
    }
}
The make_crctbl() will be used during initialization and will call one of the bitshift simulations to calculate the CRCs for each byte in the table.
#pragma METHOD_CALL

// Initialize the table stored internally
METHOD VOID ccitt_crc_init(PR_CCITT *self) {
    // Create the CRC table - DYLs cannot have static data
    make_crctbl(self->crc.crctab, CRCCCITT, crc_hware) ;
}

// Calculate the CRC for just one byte
METHOD VOID ccitt_crc_calc(PR_CCITT *self, char *data, USHORT *crc) {
    crcupdate(*data, crc, self->crc.crctab) ;
}

// Calculate the CRC for a string of bytes
METHOD VOID ccitt_crc_calc_len(PR_CCITT *self, char *data, USHORT *crc, int len) {
    for (  ; len > 0 ; data++, len-- )  {
        crcupdate(*data, crc, self->crc.crctab) ;
    }
}
The init method implementations simply use the appropriate polynomials and bitshift simulators to build the tables. The crc_calc and crc_calc_len implementations use the macros to perform the necessary bitshifts on the current CRC, the current data (which gives us the new table entry to be used)
// Initialize the table stored internally
METHOD VOID crc16_crc_init(PR_CRC16 *self) {
    // Create the CRC table - DYLs cannot have static data
    make_crctbl(self->crc.crctab, CRC16_REV, crc_revhware) ;
}

// Calculate the CRC for just one byte
METHOD VOID crc16_crc_calc(PR_CRC16 *self, char *data, USHORT *crc) {
    crcupdate16(*data, crc, self->crc.crctab) ;
}

// Calculate the CRC for a string of bytes
METHOD VOID crc16_crc_calc_len(PR_CRC16 *self, char *data, USHORT *crc, int len) {
    for (  ; len > 0 ; data++, len-- )  {
        crcupdate16(*data, crc, self->crc.crctab) ;
    }
}
Download crc.cat and crc_o.c.

Back to My home page
© 1997 J. C. Roux