Search code examples
phpccrccrc16

Convert given CRC 16 code written in C to PHP


I have this code written in C language, Which is representing CRC-16 algorithm using polynom 0x8005, I need to convert that code to PHP, I got stuck at the memcpypgm2ram part, I don't know what that function supposed to do and what's the equivalent to it in PHP. I have done this so far:

C code

    const rom unsigned far int CRC16tbl [ 256 ] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 };

unsigned short addCRC(unsigned short crc, unsigned char dataByte)
{
    unsigned int crc16int;

    memcpypgm2ram(&crc16int, &CRC16tbl[ ( crc & 0xFF ) ^ dataByte ], 2); // Can't get idea behind this line

    return ( ( crc >> 8 ) ^ crc16int ); //neither this line
}

unsigned short crc16_calc(char *pBuffer, unsigned short length)
{
    unsigned short crc16 = 0;

    while (length--)
    crc16 = addCRC( crc16, *pBuffer++);

    return crc16;
}

PHP Code:

    public static function crc16Calc($buffer, $length){
        $crc16 = 0;
        while ($length--) {
            $crc16 = $this->addCRC($crc16, $buffer++);
        }
        return $crc16;
    }
    
    public static function addCRC($crc , $data_byte){
        $crc16;
        // Need some help here 
    }

Solution

  • The first line in question does the following:

    1. (crc & 0xFF) zeroes all but the 8 least significant bits of the CRC, effectively truncating it to one byte (i.e. 0xAABB becomes 0xBB).
    2. (...) ^ dataByte takes the XOR (exclusive or) of that with the new data byte.
    3. &CRC16tbl[...] treats the result of the above as an index into the table, and produces a pointer to that location in the table.
    4. The call to memcpypgm2ram copies two bytes starting at the pointer from above into the variable crc16int, effectively assigning to crc16int a value from the table.

    From point 4, we can assume that the C environment that the code was written for uses 16-bit integers, and that it has separate memory for code and for data. memcpypgm2ram(a, b, len) can be assumed to copy len bytes from from code space (at pointer b) to data space (at pointer a). Most probably some sort of embedded platform was used to run the code.

    1. Finally, (crc >> 8) ^ crc16int shifts the 16-bit integer 8 places to the right, effectively taking the most significant byte (i.e. 0xAABB becomes 0xAA), then takes the XOR with the value that was read from the table.

    In PHP, this could be written as:

    // values are integers between 0 and 0xFFFF
    $crc16table = array(
    0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
    // ... copy from question ...
    0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 );
    
    // $crc is an integer between 0 and 0xFFFF
    // $dataByte is an integer between 0 and 0xFF
    // The result is an integer between 0 and 0xFFFF
    function addCRC($crc, $dataByte) {
        global $crc16table;
        $index = ($crc & 0xFF) ^ $dataByte;
        $crc16int = $crc16table[$index];
        return ($crc >> 8) ^ $crc16int;
    }
    
    // $buffer is a string containing the binary data
    // The result is an integer between 0 and 0xFFFF
    function crc16Calc($buffer) {
        $crc16 = 0;
        $length = strlen($buffer);
        for ($i = 0; $i < $length; $i++) {
            // Use ord() to go from a length-1 string to an integer between 0 and 0xFF
            $dataByte = ord($buffer[$i]);
            $crc16 = addCRC($crc16, $dataByte);
        }
        return $crc16;
    }
    

    In case you need more explanation on the bitwise operators, it can be found on the PHP website.

    Notice also how I changed the iteration in crc16Calc. While in C it may be common to iterate by moving a pointer, PHP does not work that way. Instead, the buffer is passed in as a string with a known length strlen($buffer). *pBuffer++ cannot be directly translated to $buffer++.