[ Home | 6502 | 6502 (CRC-8/SMBUS) | ARM | Python | 8080/Z80 | Generic ARM | 8051 | Disclaimer ]
Map of common 16-bit CRC algorithms.
"123456789" (UTF-8) |
Polynomial | 1021 | 8005 | ||
---|---|---|---|---|---|
Reflected? | False | True | False | ||
Initial value | Final XOR | ||||
0000 | 0000 | 31C3 (XMODEM) |
2189 (KERMIT) |
BB3D (ARC) |
FEE8 (UMTS) |
FFFF | CE3C (GSM) |
DE76 (–) |
44C2 (MAXIM) |
0117 (–) |
|
FFFF | D64E (GENIBUS) |
906E (SDLC) |
B4C8 (USB) |
5118 (–) |
|
0000 | 29B1 (IBM 3740) |
6F91 (MCRF4XX) |
4B37 (MODBUS) |
AEE7 (CMS) |
|
6502 code | 6502 | – | |||
ARM code | ARM | Generic ARM | |||
8080 / Z80 code | 8080/Z80 | – | |||
8051 / 8052 code | 8051 | – |
[ Top of page ]
Sample 6502 assembly code to implement the eleven major 16-bit algorithms in constant time, without the use of lookup tables.
*= $0070 crclo: .DB $FF crchi: .DB $FF *= $1900 .START * test: CLD LDY #$FF ; for GENIBUS, SDLC, USB, IBM 3740, MCRF4XX, MODBUS ; LDY #$00 ; for XMODEM, KERMIT, ARC, GSM, MAXIM STY crclo ; store init value STY crchi LDY #$00 byloop: LDA data,Y JSR crc16_xmodem_f INY CPY #lendata BMI byloop ; LDA crclo ; complement result ; EOR #$FF ; for GSM, MAXIM, GENIBUS, SDLC, USB ; STA crclo ; LDA crchi ; EOR #$FF ; STA crchi BRK ; result is in $0070 and $0071 ; Add byte to XMODEM, GSM, GENIBUS or IBM 3740 CRC ; On entry: ; A = byte to add ; crclo = low byte of XMODEM CRC ; crchi = high byte of XMODEM CRC ; (on the first call, crclo and crchi should be set ; according to the algorithm in use) ; On exit: ; crclo,crchi = New XMODEM CRC with byte added ; On exit, when using alternative ending 1: ; S,V,B,D,I = preserved (subject to interrupts) ; A,X,Y,N,Z,C = undefined ; On exit, when using alternative ending 2: ; Y,S,V,B,D,I = preserved (subject to interrupts) ; A,X,N,Z,C = undefined ; Relocatable, non-re-entrant crc16_xmodem_f: ; add byte to XMODEM CRC EOR crchi ; A contained the data STA crchi ; XOR it into high byte LSR ; right shift A 4 bits LSR ; to make top of x^12 term LSR ; ($1...) LSR TAX ; save it ASL ; then make top of x^5 term EOR crclo ; and XOR that with low byte STA crclo ; and save TXA ; restore partial term EOR crchi ; and update high byte STA crchi ; and save ASL ; left shift three ASL ; the rest of the terms ASL ; have feedback from x^12 TAX ; save bottom of x^12 ASL ; left shift two more ASL ; watch the carry flag EOR crchi ; bottom of x^5 ($..2.) ; ; alternative ending 1 ; TAY ; save high byte ; TXA ; fetch temp value ; ROL ; bottom of x^12, middle of x^5! ; EOR crclo ; finally update low byte ; STA crchi ; then swap high and low bytes ; STY crclo ; RTS ; 37 bytes, 68 cycles, AXYP undefined ; alternative ending 2 STA crchi ; save high byte TXA ; fetch temp value ROL ; bottom of x^12, middle of x^5! EOR crclo ; finally update low byte LDX crchi ; then swap high and low bytes STA crchi STX crclo RTS ; 40 bytes, 72 cycles, AXP undefined ; Add byte to KERMIT, SDLC or MCRF4XX CRC ; This is a straightforward reflection of the XMODEM algorithm ; On entry: ; A = byte to add ; crclo = low byte of KERMIT CRC ; crchi = high byte of KERMIT CRC ; (on the first call, crclo and crchi should be set ; according to the algorithm in use) ; On exit: ; crclo,crchi = New KERMIT CRC with byte added ; On exit, when using alternative ending 1: ; S,V,B,D,I = preserved (subject to interrupts) ; A,X,Y,N,Z,C = undefined ; On exit, when using alternative ending 2: ; Y,S,V,B,D,I = preserved (subject to interrupts) ; A,X,N,Z,C = undefined ; Relocatable, non-re-entrant kermit_f: ; add byte to KERMIT CRC EOR crclo ; A contained the data STA crclo ; XOR into low byte ASL ; create top of x^12 term ASL ASL ASL TAX ; save it LSR ; then make top of x^5 term EOR crchi ; XOR into high byte STA crchi TXA ; restore x^12 EOR crclo ; apply it to low byte EOR crclo LSR ; create bottom of x^12 LSR ; (with feedback from top) LSR ; watch the carry flag TAX ; save it LSR ; make bottom of x^5 LSR EOR crclo ; apply to low byte ; ; alternative ending 1 ; TAY ; save in Y ; TXA ; restore bottom of x^12 ; ROR ; rotate in middle of x^5 ; EOR crchi ; apply to high byte ; STA crclo ; and swap bytes ; STY crchi ; RTS ; 37 bytes, 68 cycles, AXYP undefined ; alternative ending 2 STA crclo ; save low byte TXA ; restore bottom of x^12 ROR ; rotate in middle of x^5 EOR crchi ; apply to high byte LDX crclo ; pick up low byte STA crclo ; and swap bytes STX crchi RTS ; 40 bytes, 72 cycles, AXP undefined ; Add byte to ARC, MAXIM, USB or MODBUS CRC ; On entry: ; A = byte to add ; crclo = low byte of ARC CRC ; crchi = high byte of ARC CRC ; (on the first call, crclo and crchi should = $00, $00) ; temp = unused byte of memory ; D = 0 ; On exit: ; crclo,crchi = New ARC CRC with byte added ; Y,S,V,B,D,I = preserved (subject to interrupts) ; A,X,N,Z,C,temp = undefined ; Relocatable, non-re-entrant crc16_arc_f: ; add byte to ARC-style CRC EOR crclo STA crclo ; parity_adc (thanks bogax at 6502.org) ASL EOR crclo AND #$AA ADC #$66 ; C has no effect AND #$88 ADC #$78 ASL ; C contains even parity LDA #0 ROR crclo ; push parity in b7 ROR STA temp ; save crclo B0 LDA crclo LSR ROR temp ; save crclo B1 EOR crclo ; 'blur' crclo TAX ; keep final crchi ASL ; C contains parity again LDA temp ; reload mask ROL ; parity in b0, B0 in b7 EOR temp ; add B0 in b6, B1 in b7 EOR crchi ; apply completed mask STA crclo ; and store, swapping bytes STX crchi RTS ; 44 bytes, 73 cycles, AXP undefined ; other parity algorithms can be used ; their speed and size vary greatly data: .DB $31,$32,$33,$34,$35,$36,$37,$38 .DB $39 lendata = *-data .END
[ Top of page ]
Sample 6502 assembly code to implement the "CRC-8/SMBUS"
algorithm in constant
time, without the use of lookup tables.
*= $0070 crc: .DB $00 *= $1900 .START * test: CLD LDY #$00 ; init value STY crc byloop: LDA data,Y JSR crc8_f INY CPY #lendata BMI byloop BRK ; result is in $0070 ; Add byte to CRC-8 ; On entry: ; A = byte to add ; crc = CRC-8 value ; (on the first call, crc should = $00) ; On exit: ; crc = New CRC-8 value with byte added ; X,Y,S,V,B,D,I = preserved (subject to interrupts) ; A,N,Z,C,temp = undefined ; Relocatable, non-re-entrant crc8_f: ; add byte to CRC-8 EOR crc ; A contained the data STA crc ; XOR it with the byte ASL ; current contents of A will become x^2 term BCC crc8_f_apply_1 ; if b7 = 1 EOR #$07 ; then apply polynomial with feedback crc8_f_apply_1: BCC *+2 ; ballast to ensure constant time EOR crc ; apply x^1 ASL ; C contains b7 ^ b6 BCC crc8_f_apply_2 EOR #$07 crc8_f_apply_2: BCC *+2 ; ballast to ensure constant time EOR crc ; apply unity term STA crc ; save result RTS ; 25 bytes, 37 cycles, AP undefined data: .DB $31,$32,$33,$34,$35,$36,$37,$38 .DB $39 lendata = *-data .END
[ Top of page ]
Sample ARM assembly code to implement the eleven major 16-bit algorithms in constant time, without the use of lookup tables. Thanks to Viktor Gottschald for correspondence and a 15-instruction routine for ARC/Modbus, leading to this updated version.
.crc16_test STMDB (sp)!,{R1-R4,R8,R9,lr} \preserve registers ADR R8,data \set pointer to string MOV R9,#dataend - data \set counter to string length MOV R0,#0 \set Init = 0 (XMODEM, KERMIT, ARC) \ MOV R0,#&FF00 \or set Init = 0xffff (others) \ ORR R0,R0,#&FF MOV R2,#&FF000000 \R2 = 0xff0000ff ORR R2,R2,#&FF MOV R3,#&A000000A \R3 = 0xa006000a ORR R3,R3,#&60000 MVN R4,#&2D00 \R4 = 0x2d00d2ff EOR R4,R4,R4,LSL #16 .crc16_test_loop LDRB R1,[R8],#1 \get character from string BL crc16_xmodem_f \update CRC using XMODEM SUBS R9,R9,#1 \decrement counter BNE crc16_test_loop \loop until end of string AND R0,R0,R2,ROR #24 \clear high bytes of result \ EOR R0,R0,R2,ROR #24 \complement it for GENIBUS/SDLC/USB ADDS R0,R0,#0 \BASIC V/RISC OS needs flags clear LDMIA (sp)!,{R1-R4,R8,R9,pc} \at end restore regs and return CRC \fast XMODEM engine \on entry R0=old CRC, R1=data byte, \R2 = 0xff0000ff \on exit R0=new CRC (bits 0..15), \R0 bits 16..31 undefined, \R1 undefined .crc16_xmodem_f EOR R0,R0,R1,LSL #8 \merge new byte into top byte AND R0,R2,R0,ROR #8 \old CRC to top and bottom byte EOR R1,R0,R0,LSR #4 \'blur' low byte in new register EOR R0,R0,R1,LSL #21 \apply feedback to polynomial EOR R0,R0,R1,LSL #12 \and again EOR R0,R0,R0,LSR #16 \fold top half of word to bottom MOV pc,lr \return; 6 (7) instructions \fast KERMIT / SDLC engine \on entry R0=old CRC, R1=data byte, \R2 = 0xff0000ff \on exit R0=new CRC (bits 0..15), \R0 bits 16..31 undefined, \R1 undefined .kermit_f AND R0,R2,R0,ROR #8 \merge new byte into bottom byte EOR R0,R0,R1,LSL #24 \old CRC to top and bottom byte EOR R1,R0,R0,LSL #4 \'blur' low byte in new register EOR R0,R0,R1,LSR #21 \apply feedback to polynomial EOR R0,R0,R1,LSR #12 \and again EOR R0,R0,R0,LSR #16 \fold top half of word to bottom MOV pc,lr \return; 6 (7) instructions \fast ARC / MODBUS engine \on entry R0=old CRC, R1=data byte, \R2 = 0xff0000ff, R3 = 0xa006000a, \R4 & 0x7f807f80 = 0x2d005280 \on exit R0=new CRC (bits 0..15), \R0 bits 16..31 undefined, \R1 undefined .crc16_arc_f AND R0,R2,R0,ROR #8 \old CRC to top and bottom byte EOR R0,R0,R1,LSL #24 \merge new byte into top byte EOR R1,R0,R0,LSR #1 \'blur' top byte in new register EOR R0,R0,R1,LSR #17 \merge blurred byte into new top ORR R1,R3,R1,ROR #26 \and rotate it and mask with 1s ADDS R1,R1,R4,ROR R1 \put parity into N using table EORMI R0,R0,R3,LSR #3 \if odd parity invert three bits MOV pc,lr \return; 7 (8) instructions .data EQUS "123456789" .dataend ALIGN
[ Top of page ]
A demonstration of selected 16-bit algorithms in Python. Reproduced by kind permission of James Luscher.
#!/usr/bin/env Python def CharToBinary(char): n = ord(char) bits = '' for y in range(8): if ((n & (1 << y)) == 0): bits = '0' + bits else: bits = '1' + bits return bits def StringToBinary(message, reflect): bytes = '' print 'message = "' + message + '"' if reflect: print ' binary hex reflected' else: print ' binary hex' for x in range(len(message)): char = message[x:x+1] bits = CharToBinary(char) rbits = Reflect(bits) if reflect: print "char = '"+char+"' -> "+bits+" (0x"+BinaryToHex(bits)+') <=> '+rbits+' (0x'+BinaryToHex(rbits)+')' bits = rbits else: print "char = '"+char+"' -> "+bits+" (0x"+BinaryToHex(bits)+')' if bytes == '': bytes = bytes + '{' + bits else: bytes = bytes + ',' + bits bytes = bytes + '}' if len(bytes) > 57: print "bytes = "+bytes[0:57]+' ...' else: print "bytes = " + bytes return bytes def XOR(s1,s2): if len(s1) != len(s2): print "XOR(): ERROR, unequal length strings: ["+s1+"]["+s2+"]" return r = '' for i in range(len(s1)): if (s1[i:i+1] == '1' and s2[i:i+1] == '1'): r = r + '0' elif(s1[i:i+1] == '0' and s2[i:i+1] == '0'): r = r + '0' else: r = r + '1' return r def Reflect(s): r = '' for i in range(len(s)): r = s[i:i+1] + r return r def NOT(s): r = '' for i in range(len(s)): if (s[i:i+1] == '1'): r = r + '0' else: r = r + '1' return r def HexToBinary(n): h = '' for inx in range(16): if n & 0x1 == 0x1: h = '1' + h else: h = '0' + h n = n / 2 return h def BinaryToHex(s): h = '' for x in range((len(s)+3)/4): n = 0 i = 1 for y in range(4): if s[-1:] == '1': n = n + i s = s[:-1] i = (i * 2) if n <= 9: h = (chr(ord('0') + n )) + h else: h = (chr(ord('a') + (n - 10))) + h return h def MessageStrip(M): while len(M) > 0 and M[0:1] != '0' and M[0:1] != '1': M = M[1:] return M #============================================ # author: James Luscher (owns all errors ;-) # <jluscher-gmail-com> # corrections: Greg Cook (thanks Greg!) # copyright: 2007 James Luscher # licensed under: GNU General Public License # details at http://www.gnu.org/copyleft/ # # This program was written for self-education. # I hope others may find it informative also. #============================================= def crc(poly, register, reflect, message, invert): #----------------------------------- # CRC-16 polynomial is 0x8005 # CRC-16/CCITT polynomial is 0x1021 P = HexToBinary(poly) #----------------------------------- # register (initial value) R = HexToBinary(register) #----------------------------------- # reflect in/out ?? if reflect != 0: print "reflect = True" else: print "reflect = False" #----------------------------------- # M -> message (ASCII string) M = StringToBinary(message, reflect) length = len(M) print if len(M) > 53: print "message = "+M[0:53]+' ...' else: print "message = "+M #----------------------------------- print "register = "+R print "polynomial= "+P+" (0x" + BinaryToHex(P) + ')' print M = MessageStrip(M) print " register message..." print " ---------------- -------..." if len(M) > 40: print " "+R+' '+M[0:40]+' ...' elif len(M) > 0: print " "+R+' '+M else: print " "+R while len(M)>0: Rc = R[0:1] Mc = M[0:1] C = XOR(Rc,Mc) R = R[1:]+'0' M = M[1:] M = MessageStrip(M) if len(M) > 40: print " ("+Rc+") < "+R+' ('+Mc+') < '+M[0:40]+' ...' else: print " ("+Rc+") < "+R+' ('+Mc+') < '+M if C == '1': print "["+Rc+'^'+Mc+"]=> "+P+" (xor by 0x" + BinaryToHex(P) + ')' print " "+('-' * 16 ) R = XOR(R, P) print " "+R+" (0x" + BinaryToHex(R) + ')' print print if reflect: R = Reflect(R) print " reflected" print "<=> => " + R if invert != 0: R = NOT(R) print "NOT => " + R print " " + R + " = CRC" + " (0x" + BinaryToHex(R) + ")" def d1(): crcdemo(0x1021, 0x0000, 1, 0) print print "demo: example: poly reg r i expect" print "----- ------------------- ----------------------------- ------" print "d1() KERMIT: crcdemo(0x1021, 0x0000, 1, 0) // 0x2189" print "===================================================================" crcdemo_help() def d2(): crcdemo(0x8408, 0x0000, 1, 0) print print "demo: example: poly reg r i expect" print "----- ------------------- ----------------------------- ------" print "d2() X-KERMIT: crcdemo(0x8408, 0x0000, 1, 0) // 0x0c73" print "===================================================================" crcdemo_help() def d3(): crcdemo(0x1021, 0xffff, 1, 1) print print "demo: example: poly reg r i expect" print "----- ------------------- ----------------------------- ------" print "d3() X-25: crcdemo(0x1021, 0xffff, 1, 1) // 0x906e" print "===================================================================" crcdemo_help() def d4(): crcdemo(0x8005, 0x0000, 1, 0) print print "demo: example: poly reg r i expect" print "----- ------------------- ----------------------------- ------" print "d4() CRC-16/ARC: crcdemo(0x8005, 0x0000, 1, 0) // 0xbb3d" print "===================================================================" crcdemo_help() def d5(): crcdemo(0x1021, 0xffff, 0, 0) print print "demo: example: poly reg r i expect" print "----- ------------------- ----------------------------- ------" print "d5() CRC-16/CCITT-FALSE: crcdemo(0x1021, 0xffff, 0, 0) // 0x29b1" print "===================================================================" crcdemo_help() def d6(): crcdemo(0x1021, 0x0000, 0, 0) print print "demo: example: poly reg r i expect" print "----- ------------------- ----------------------------- ------" print "d6() CRC-16/XMODEM: crcdemo(0x1021, 0x0000, 0, 0) // 0x31c3" print "===================================================================" crcdemo_help() def crcdemo_help(): print """ crcdemo() - show the detailed calculation for various kinds of 16 bit CRCs - ALL demo examples applied to the canonical message "123456789" - use function 'crc()' to apply to your own message string. crcdemo(poly, register, reflect, append, invert) poly <- hex value for (truncated) generator polynomial register <- hex initial value for remainder register reflect <- 0 == don't reflect message bytes & output CRC invert <- 0 == don't invert remainder after calculation demo: examples: poly reg r i expect ----- ------------------- ----------------------------- ------ d1() KERMIT: crcdemo(0x1021, 0x0000, 1, 0) // 0x2189 d2() X-KERMIT: crcdemo(0x8408, 0x0000, 1, 0) // 0x0c73 d3() X-25: crcdemo(0x1021, 0xffff, 1, 1) // 0x906e d4() CRC-16/ARC: crcdemo(0x8005, 0x0000, 1, 0) // 0xbb3d d5() CRC-16/CCITT-FALSE: crcdemo(0x1021, 0xffff, 0, 0) // 0x29b1 d6() CRC-16/XMODEM: crcdemo(0x1021, 0x0000, 0, 0) // 0x31c3 """ def crcdemo(poly, register, reflect, invert): crc(poly, register, reflect, '123456789', invert) return crcdemo_help() // To run a demonstration of each algorithm, uncomment any of the next 6 lines. - GJC // d1() // d2() // d3() // d4() // d5() // d6() // End of crc16demo.py
[ Top of page ]
Sample 8080/Z80 assembly code to implement the eleven major 16-bit algorithms in constant time, without the use of lookup tables. One of the routines is for faster calculation on a Z80 only.
; Main loop, for 8080/Z80 ORG 100H Start: LD SP,1000H LD HL,0 ; for XMODEM, KERMIT and ARC ; LD HL,FFFFH ; for GENIBUS, SDLC and USB LD DE,data ; set pointer to test string LD C,dataend-data ; set counter to string length tloop: LD A,(DE) ; get character of string INC DE ; increment pointer PUSH BC ; save counter CALL crc16_xmodem_f ; do CRC on character POP BC ; restore counter DEC C ; decrement it JP NZ,tloop ; and loop until string done ; LD A,H ; complement result ; CPL ; for GENIBUS, SDLC and USB ; LD H,A ; LD A,L ; CPL ; LD L,A HALT data: DEFB "123456789" ; test string dataend:NOP ; label for calculating length ; CRC-16/XMODEM for 8080/Z80 ; On entry HL = old CRC, A = byte ; On exit HL = new CRC, A,B,C undefined ; Ts M/code 8080 assembly crc16_xmodem_f: XOR H ; 4 AC XRA H LD B,A ; 4 47 MOV B,A LD C,L ; 4 4D MOV C,L RRCA ; 4 0F RRC RRCA ; 4 0F RRC RRCA ; 4 0F RRC RRCA ; 4 0F RRC LD L,A ; 4 6F MOV L,A AND 0FH ; 7 E6 0F ANI 0FH LD H,A ; 4 67 MOV H,A XOR B ; 4 A8 XRA B LD B,A ; 4 47 MOV B,A XOR L ; 4 AD XRA L AND F0H ; 7 E6 F0 ANI F0H LD L,A ; 4 6F MOV L,A XOR C ; 4 A9 XRA C ADD HL,HL ; 11 29 DAD H XOR H ; 4 AC XRA H LD H,A ; 4 67 MOV H,A LD A,L ; 4 7D MOV A,L XOR B ; 4 A8 XRA B LD L,A ; 4 6F MOV L,A RET ; 10 C9 RET ; 115 T-states, 25 bytes ; CRC-16/XMODEM for 8080/Z80 ; On entry HL = old CRC, A = byte ; On exit HL = new CRC, A,B undefined ; Ts M/code 8080 assembly crc16_xmodem_c_f: XOR H ; 4 AC XRA H LD H,A ; 4 67 MOV H,A AND F0H ; 7 E6 F0 ANI F0H RRCA ; 4 0F RRC RRCA ; 4 0F RRC RRCA ; 4 0F RRC RRCA ; 4 0F RRC XOR H ; 4 AC XRA H LD H,A ; 4 67 MOV H,A RRCA ; 4 0F RRC RRCA ; 4 0F RRC RRCA ; 4 0F RRC LD B,A ; 4 47 MOV B,A AND E0H ; 7 E6 E0 ANI E0H XOR H ; 4 AC XRA H LD H,A ; 4 67 MOV H,A LD A,B ; 4 78 MOV A,B AND 1FH ; 7 E6 1F ANI 1FH XOR L ; 4 AD XRA L LD L,A ; 4 6F MOV L,A LD A,B ; 4 78 MOV A,B RRCA ; 4 0F RRC AND F0H ; 7 E6 F0 ANI F0H XOR L ; 4 AD XRA L LD L,H ; 4 6C MOV L,H LD H,A ; 4 67 MOV H,A RET ; 10 C9 RET ; 126 T-states, 31 bytes ; KERMIT for 8080/Z80 ; On entry HL = old CRC, A = byte ; On exit HL = new CRC, A,B undefined ; Ts M/code 8080 assembly kermit_f: XOR L ; 4 AD XRA L LD L,A ; 4 6F MOV L,A ADD A,A ; 4 87 ADD A ADD A,A ; 4 87 ADD A ADD A,A ; 4 87 ADD A ADD A,A ; 4 87 ADD A XOR L ; 4 AD XRA L LD L,A ; 4 6F MOV L,A RLCA ; 4 07 RLC RLCA ; 4 07 RLC RLCA ; 4 07 RLC LD B,A ; 4 47 MOV B,A AND 07H ; 7 E6 07 ANI 07H XOR L ; 4 AD XRA L LD L,A ; 4 6F MOV L,A LD A,B ; 4 78 MOV A,B AND F8H ; 7 E6 F8 ANI F8H XOR H ; 4 AC XRA H LD H,A ; 4 67 MOV H,A LD A,B ; 4 78 MOV A,B RLCA ; 4 07 RLC AND 0FH ; 7 E6 0F ANI 0FH XOR H ; 4 AC XRA H LD H,L ; 4 65 MOV H,L LD L,A ; 4 6F MOV L,A RET ; 10 C9 RET ; 119 T-states, 29 bytes ; CRC-16/ARC for 8080/Z80 ; On entry HL = old CRC, A = byte ; On exit HL = new CRC, A,B undefined ; Ts M/code 8080 assembly crc16_arc_f: XOR L ; 4 AD XRA L LD L,A ; 4 6F MOV L,A RRCA ; 4 0F RRC RRCA ; 4 0F RRC JP PO,blur ; 10 E2 nn nn JPO blur AND A ; 4 A7 ANA A blur: JP PE,blur1 ; 10 EA nn nn JPE blur1 SCF ; 0 37 STC blur1: RRA ; 4 1F RAR AND E0H ; 7 E6 E0 ANI E0H RLA ; 4 17 RAL LD B,A ; 4 47 MOV B,A RLA ; 4 17 RAL XOR B ; 4 A8 XRA B XOR H ; 4 AC XRA H LD B,A ; 4 47 MOV B,A XOR H ; 4 AC XRA H RRA ; 4 1F RAR LD A,L ; 4 7D MOV A,L RRA ; 4 1F RAR LD L,A ; 4 6F MOV L,A AND A ; 4 A7 ANA A RRA ; 4 1F RAR XOR L ; 4 AD XRA L LD L,B ; 4 68 MOV L,B LD H,A ; 4 67 MOV H,A RET ; 10 C9 RET ; 125 T-states, 31 bytes ; CRC-16/ARC for Z80 only ; On entry HL = old CRC, A = byte ; On exit HL = new CRC, A,B undefined ; Ts M/code crc16_arc_z80_f: LD B,0 ; 7 06 00 XOR L ; 4 AD JP PO,blur80 ; 10 E2 nn nn AND A ; 4 A7 blur80: JP PE,blur81 ; 10 EA nn nn SCF ; 0 37 blur81: RRA ; 4 1F RR B ; 8 CB 18 LD L,A ; 4 6F SRL A ; 8 CB 3F RR B ; 8 CB 18 XOR L ; 4 AD LD L,A ; 4 6F ADD A,A ; 4 87 LD A,B ; 4 78 RLA ; 4 17 XOR B ; 4 A8 XOR H ; 4 AC LD H,L ; 4 65 LD L,A ; 4 6F RET ; 10 C9 ; 113 T-states, 29 bytes END
[ Top of page ]
Parametrised, table-driven CRC algorithm in ARM assembler. The main loop takes just 8 instructions per byte, or 6 if inlined.
REM >Table3 REM Greg Cook 12 January 2020 REM Assemble algorithm-independent code DIM code 1024+1024-1 sp=13:lr=14:pc=15 FOR pass=0 TO 3 STEP 3 P%=code [OPT pass .main \ do a CRC of the test string \ to demonstrate the algorithm stmdb (sp)!,{r1-r4,lr} bl doinit adr r3,string ldr r4,strlen .mloop ldrb r1,[r3],#1 bl byte subs r4,r4,#1 bne mloop bl finish adds r0,r0,#0 \for BASIC V/RISC OS ldmia (sp)!,{r1-r4,pc} .doinit stmdb (sp)!,{r1,r3-r5,lr} \ set up registers adr r2,table ldr r1,poly bic r1,r1,#&FF0000 bic r1,r1,#&FF000000 \ set up table mov r3,#&FF .itbyte \ copy table offset to dividend mov r0,r3,lsl #8 mov r5,r3 mov r4,#&01000000 \bit counter \ do pure polynomial division .itbit tst r0,#&8000 bic r0,r0,#&8000 mov r0,r0,lsl #1 eorne r0,r0,r1 \ shift bit counter and reverse offset movs r5,r5,lsr #1 adcs r4,r4,r4 bcc itbit \ test RefIn ldr r5,refin tst r5,#1 beq split \ if RefIn = true reverse remainder \ (without swapping bytes) mov r5,#&18000 .revrem movs r0,r0,lsr #1 adcs r5,r5,r5 bcc revrem movs r0,r5,ror #8 \clear Z .split \split remainder to top and bottom byte orr r5,r0,r0,lsl #16 bic r5,r5,#&FF00 bic r5,r5,#&FF0000 \if RefIn = false store at direct offset orreq r5,r5,r4,lsl #8 streq r5,[r2,r3,lsl #2] \if RefIn = true store at reflected offset orrne r5,r5,r3,lsl #8 strne r5,[r2,r4,lsl #2] subs r3,r3,#1 bcs itbyte \ set up shift register. test RefIn ldr r5,refin tst r5,#1 ldr r0,init \ move Init to top mov r0,r0,lsl #16 \ split Init and quit if RefIn = false orreq r0,r0,r0,lsr #16 beq doneinit \ else split and reflect Init (bytes not swapped) mov r5,r0,lsr #24 and r0,r0,#&FF0000 ldr r0,[r2,r0,lsr #14] ldr r5,[r2,r5,lsl #2] mov r5,r5,lsl #16 orr r0,r5,r0,lsr #8 .doneinit bic r0,r0,#&FF00 bic r0,r0,#&FF0000 ldmia (sp)!,{r1,r3-r5,pc} .byte \ merge byte in R1 into the CRC in R0 eor r0,r0,r1,lsl #24 ldr r1,[r2,r0,lsr #22] eor r0,r1,r0,lsl #24 mov pc,lr .finish \ test RefIn and RefOut ldr r1,refout movs r1,r1,lsr #1 ldr r1,refin adc r1,r1,#0 tst r1,#1 \ C = RefOut, Z = !(RefIn ^ RefOut) \ Bytes to top of r0 and r1 \ Swap if RefOut = true movcc r1,r0,lsl #24 andcs r1,r0,#&FF000000 movcs r0,r0,lsl #24 \ Reflect bytes if RefIn != RefOut ldrne r0,[r2,r0,lsr #22] ldrne r1,[r2,r1,lsr #22] \ Join bytes in r0 bic r0,r0,#&FF orr r0,r0,r1,lsr #8 \ Move to top if RefIn != RefOut movne r0,r0,lsl #16 \ Apply XorOut to result ldr r1,xorout eor r0,r0,r1,lsl #16 \ Shift result to bottom of r0 mov r0,r0,lsr #16 mov pc,lr .poly equd 0 .init equd 0 .refin equd 0 .refout equd 0 .xorout equd 0 .strlen equd 0 .string equs STRING$(255," ") align .table ] P%+=1024 NEXT REM Set parameters for this run REM This is a RockSoft(TM) Model record REM with adjusted syntax Width = 16 : REM not used Poly = &1021 Init = &FFFF RefIn = TRUE RefOut = TRUE XorOut = &FFFF REM Set the string to test String$ = "123456789" REM Store the parameters for the routine !poly = Poly !init = Init !refin = RefIn !refout = RefOut !xorout = XorOut !strlen = LEN(String$) $string = String$ REM Call code and print computed CRC PRINT '"Result = ";~USR(main) REM Dump table contents PRINT "Contents of table:" FOR T% = 0 TO 1023 STEP 4 IF T% MOD 32 = 0 THEN PRINT PRINT ~table!T%; NEXT PRINT REM Regression test: 16 results from Ross Williams' crcmodel.c REM and values from REM https://reveng.sourceforge.io/crc-catalogue/all.htm#appendix.a $string = "123456789" !strlen=9 FOR T%=1 TO 30 READ !poly,!init,!refin,!refout,!xorout,expect%,name$ actual%=USR(main) IF actual%<>expect% THEN PRINT ~!poly,~!init,~!refin,~!refout,~!xorout,~expect%,~actual%:END ENDIF NEXT PRINT "Regression test passed" END DATA &1021,&0000, 0, 0,&0000,&31C3,"CRC-16/XMODEM" DATA &1021,&0000, 0,-1,&0000,&C38C,"" DATA &1021,&0000,-1, 0,&0000,&9184,"" DATA &1021,&0000,-1,-1,&0000,&2189,"CRC-16/KERMIT" DATA &1021,&0000, 0, 0,&2357,&1294,"" DATA &1021,&0000, 0,-1,&2357,&E0DB,"" DATA &1021,&0000,-1, 0,&2357,&B2D3,"" DATA &1021,&0000,-1,-1,&2357,&02DE,"" DATA &1021,&1234, 0, 0,&0000,&EDEB,"" DATA &1021,&1234, 0,-1,&0000,&D7B7,"" DATA &1021,&1234,-1, 0,&0000,&4DAC,"" DATA &1021,&1234,-1,-1,&0000,&35B2,"" DATA &1021,&1234, 0, 0,&2357,&CEBC,"" DATA &1021,&1234, 0,-1,&2357,&F4E0,"" DATA &1021,&1234,-1, 0,&2357,&6EFB,"" DATA &1021,&1234,-1,-1,&2357,&16E5,"" DATA &8005,&0000,-1,-1,&0000,&BB3D,"CRC-16/ARC" DATA &8005,&0000, 0, 0,&0000,&FEE8,"CRC-16/UMTS" DATA &1021,&0000, 0, 0,&FFFF,&CE3C,"CRC-16/GSM" DATA &1021,&0000,-1,-1,&FFFF,&DE76,"" DATA &8005,&0000,-1,-1,&FFFF,&44C2,"CRC-16/MAXIM" DATA &8005,&0000, 0, 0,&FFFF,&0117,"" DATA &1021,&FFFF, 0, 0,&FFFF,&D64E,"CRC-16/GENIBUS" DATA &1021,&FFFF,-1,-1,&FFFF,&906E,"CRC-16/IBM-SDLC" DATA &8005,&FFFF,-1,-1,&FFFF,&B4C8,"CRC-16/USB" DATA &8005,&FFFF, 0, 0,&FFFF,&5118,"" DATA &1021,&FFFF, 0, 0,&0000,&29B1,"CRC-16/IBM-3740" DATA &1021,&FFFF,-1,-1,&0000,&6F91,"CRC-16/MCRF4XX" DATA &8005,&FFFF,-1,-1,&0000,&4B37,"CRC-16/MODBUS" DATA &8005,&FFFF, 0, 0,&0000,&AEE7,"CRC-16/CMS" REM End of Table3
NB: When RefIn
and RefOut
are constants, the finish
subroutine can be condensed to one of the following:
\RefIn = FALSE, RefOut = FALSE .finish and r1,r0,#&FF orr r0,r1,r0,lsr #16 ldr r1,xorout eor r0,r0,r1 mov pc,lr \RefIn = FALSE, RefOut = TRUE .finish and r1,r0,#&FF ldr r0,[r2,r0,lsr #22] ldr r1,[r2,r1,lsl #2] and r0,r0,#&FF00 and r1,r1,#&FF00 orr r0,r1,r0,lsr #8 ldr r1,xorout eor r0,r0,r1 mov pc,lr \RefIn = TRUE, RefOut = FALSE .finish and r1,r0,#&FF ldr r0,[r2,r0,lsr #22] ldr r1,[r2,r1,lsl #2] and r0,r0,#&FF00 and r1,r1,#&FF00 orr r0,r0,r1,lsr #8 ldr r1,xorout eor r0,r0,r1 mov pc,lr \RefIn = TRUE, RefOut = TRUE .finish mov r0,r0,ror #24 bic r0,r0,#&FF0000 ldr r1,xorout eor r0,r0,r1 mov pc,lr
[ Top of page ]
Sample 8051/8052 assembly code to implement the eleven major 16-bit algorithms in constant time, without the use of lookup tables.
; Main loop mov r0,#00h ; for XMODEM, KERMIT and ARC mov r1,#00h ;mov r0,#0ffh ; for GENIBUS, SDLC and USB ;mov r1,#0ffh mov dptr,#data mov r3,#0 char: mov a,r3 movc a,@a+dptr acall xmodem inc r3 cjne r3,#datalen,char finish: ;xch a,r0 ; complement result ;cpl a ; for GENIBUS, SDLC and USB ;xch a,r1 ;cpl a ;xch a,r1 ;xch a,r0 sjmp $ ; CRC-16/XMODEM for 8051/2 ; On entry A = byte ; R0 = old CRC low byte ; R1 = old CRC high byte ; On exit R0 = new CRC low byte ; R1 = new CRC high byte ; A,R2 = undefined ; Cs M/code xmodem: xrl a,r1 ; 1 69 mov r1,a ; 1 f9 swap a ; 1 c4 anl a,#0fh ; 1 54 0f xrl a,r1 ; 1 69 mov r1,a ; 1 f9 swap a ; 1 c4 mov r2,a ; 1 fa anl a,#0f0h ; 1 54 f0 xrl a,r0 ; 1 68 xch a,r2 ; 1 ca rl a ; 1 23 mov r0,a ; 1 f8 anl a,#0e0h ; 1 54 e0 xrl a,r1 ; 1 69 xch a,r0 ; 1 c8 anl a,#1fh ; 1 54 1f xrl a,r2 ; 1 6a mov r1,a ; 1 f9 ret ; 2 22 ;21 cycles, 24 bytes ; KERMIT for 8051/2 ; On entry A = byte ; R0 = old CRC low byte ; R1 = old CRC high byte ; On exit R0 = new CRC low byte ; R1 = new CRC high byte ; A,R2 = undefined ; Cs M/code kermit: xrl a,r0 ; 1 68 mov r0,a ; 1 f8 swap a ; 1 c4 anl a,#0f0h ; 1 54 f0 xrl a,r0 ; 1 68 mov r0,a ; 1 f8 swap a ; 1 c4 mov r2,a ; 1 fa anl a,#0fh ; 1 54 0f xrl a,r1 ; 1 69 xch a,r2 ; 1 ca rr a ; 1 03 mov r1,a ; 1 f9 anl a,#07h ; 1 54 07 xrl a,r0 ; 1 68 xch a,r1 ; 1 c9 anl a,#0f8h ; 1 54 f8 xrl a,r2 ; 1 6a mov r0,a ; 1 f8 ret ; 2 22 ;21 cycles, 24 bytes ; ARC for 8051/2 ; On entry A = byte ; R0 = old CRC low byte ; R1 = old CRC high byte ; On exit R0 = new CRC low byte ; R1 = new CRC high byte ; A,R2,C = undefined ; Cs M/code arc: xrl a,r0 ; 1 68 mov r0,a ; 1 f8 rr a ; 1 03 rr a ; 1 03 anl a,#0c0h ; 1 54 c0 mov r2,a ; 1 fa mov a,r0 ; 1 e8 mov c,p ; 1 a2 d0 rrc a ; 1 13 mov r0,a ; 1 f8 clr c ; 1 c3 rrc a ; 1 13 xrl a,r0 ; 1 68 mov r0,a ; 1 f8 rlc a ; 1 33 mov a,r2 ; 1 ea rlc a ; 1 33 xrl a,r2 ; 1 6a xrl a,r1 ; 1 69 xch a,r0 ; 1 c8 mov r1,a ; 1 f9 ret ; 2 22 ;23 cycles, 24 bytes datalen equ 9 data: db "123456789" end
Every effort has been made to ensure accuracy, however there may be occasional errors or omissions. All trademarks and registered trademarks are the intellectual property of their respective owners. The code and documentation included in this document are supplied without warranty, not even the implied warranties of merchantability or fitness for a particular purpose. In no event shall the author or his suppliers be liable for any loss, damage, injury or death, of any nature and howsoever caused, arising from the use of, or failure, inability or unwillingness to use, this software or documentation.
Greg Cook,[ Top of page ]