Z80 Cyclic Redundancy Check (CRC)

Small project to create Cyclic Redundancy Check for the ZX Spectrum in Z80 machine code.

Started with a simple CRC-8 (9bit) routine using the CCITT (Comité Consultatif International Téléphonique et Télégraphique) polynominal x8+x2+x+1. For binary data x=2, so the polynominal translates to 28+22+2+1 = 256+4+2+1 = 263 which in binary is 00000001 00000111. For this implementation the high-order bit (9th bit), which is always 1, is omitted so it becomes 00000111, 7 in decimal or 0x07 in hex.


;; =====================================================================
;; input - hl=start of memory to check, de=length of memory to check
;; returns - a=result crc
;; 20b
;; =====================================================================
_crc8b:
xor a ; 4t - initial value of crc=0 so first byte can be XORed in (CCITT)
ld c,$07 ; 7t - c=polyonimal used in loop (small speed up)
_byteloop8b:
xor (hl) ; 7t - xor in next byte, for first pass a=(hl)
inc hl ; 6t - next mem
ld b,8 ; 7t - loop over 8 bits
_rotate8b:
add a,a ; 4t - shift crc left one
jr nc,_nextbit8b ; 12/7t - only xor polyonimal if msb set (carry=1)
xor c ; 4t - CRC8_CCITT = 0x07
_nextbit8b:
djnz _rotate8b ; 13/8t
ld b,a ; 4t - preserve a in b
dec de ; 6t - counter-1
ld a,d ; 4t - check if de=0
or e ; 4t
ld a,b ; 4t - restore a
jr nz,_byteloop8b; 12/7t
ret ; 10t

As a test I ran this over the standard ZX Spectrum ROMs with the following results:


ROM

CRC Value

Sinclair 16/48k

0x7B

Sinclair 128k (UK)

0xE8

Amstrad +2 (Light Grey)

0xEE

Amstrad +2A/B (Dark Grey) & +3

0xAC


CRC-8 is optimal (unique) for a block length of 29-1 or up to 64bytes and longer blocks may produce the same CRC value.

Moving to CRC-16 again using CCITT polynominal (as used in Bluetooth) x16+x12+x5+1 = 00000001 00010000 00100001 in binary. With high-order bit omitted we get 00010000 00100001 or 4129 decimal, 0x1021 hex.


;; =====================================================================
;; input - de=start of memory to check, bc=length of memory to check
;; returns - hl=result crc
;; =====================================================================
_crc16:
ld hl,$ffff ; 10t - initial crc=$ffff
_byte16:
push bc ; 11t - preserve counter
ld a,(de) ; 7t - get byte
inc de ; 6t - next mem
xor h ; 4t - xor byte into crc high byte
ld h,a ; 4t - back into high byte
ld b,8 ; 7t - rotate 8 bits
_rotate16:
add hl,hl ; 11t - rotate crc left one
jr nc,_nextbit16 ; 12/7t - only xor polyonimal if msb set
ld a,h ; 4t
xor $10 ; 7t - high byte with $10
ld h,a ; 4t
ld a,l ; 4t
xor $21 ; 7t - low byte with $21
ld l,a ; 4t - hl now xor $1021
_nextbit16:
djnz _rotate16 ; 13/8t - loop over 8 bits
pop bc ; 10t - bring back main counter
dec bc ; 6t
ld a,b ; 4t
or c ; 4t
jr nz,_byte16 ; 12/7t
ret ; 10t

As for CRC-8 I ran this over the standard ZX Spectrum ROMs with the following results:


ROM

CRC Value

Sinclair 16/48k

0xFD5E

Sinclair 128k (UK)

0xDCEC

Amstrad +2 (Light Grey)

0xB0A2

Amstrad +2A/B (Dark Grey) & +3

0x8A9B


CRC-16 is optimal (unique) for block length of 217-1 or up to 16,384bytes.

CRC-32 was a bit more tricky