Source file: /~heha/hsn/crc16.zip/crc16.cpp

#include <avr/io.h>

typedef unsigned char byte;
typedef unsigned short word, size_t;	// es steht frei, size_t auch kleiner zu machen

class CRC16CCITT{
 word crc;				// Die aktuelle CRC ist versteckt (private)
public:
 CRC16CCITT():crc(0xFFFF) {}		// Der Konstruktor setzt den Startwert
 inline void operator()(byte v);	// Dieser Funktionsoperator lässt das Objekt wie eine Funktion aussehen
 operator word() const {return crc;}	// Dieser Cast-Operator lässt das Objekt wie eine 16-Bit-Zahl aussehen
};
// Mit template<word STARTVAL,word XORVAL> usw. ließe sich das Ganze auch universell
// für verschiedene CRC16-Regeln ausbauen, aber das halte ich für Overkill.

// Die byteweise CRC-Routine, die eine Memberfunktion sein muss, weil sie in "crc" einen Status speichert.
// In C löst man das gern (und auch ohne Optimizer performant) mit "static word crc16add(word crc, byte v)"
// und lässt sich die CRC „durchpipen“.
void CRC16CCITT::operator()(byte v) {
 word w(crc^v), xorval(0xA001);		// die XOR-Verknüpfung mit dem CRC-Wert erfolgt hier
 byte cnt(8);				// man hätte C-mäßig auch "byte cnt = 8" schreiben können
 asm(
"1:	lsr	%B0	\n"		// Da man in Assembler Flags kennt, kann man zuerst rechtsschieben
"	ror	%A0	\n"		// A meint Low-Teil, B meint High-Teil des Registerpaars "%0", also "w"
"	brcc	2f	\n"		// "2f" heißt: Springe zu Label "2" vorwärts. Diese lokalen Labels sind zu bevorzugen.
"	eor	%B0,%B1	\n"		// Da es keinen "eori"-Befehl gibt (XOR mit Konstante) muss man die Konstante
"	eor	%A0,%A1	\n"		// ohnehin im Voraus in Register (hier "%1") laden. Das überlässt man C.
"2:	dec	%2	\n"		// Auch %2 ist von C vorinitialisiert
"	brne	1b	\n"		// "1b" heißt: Springe zu Label "1" rückwärts.
:"+r"(w):"r"(xorval),"r"(cnt));		// Statt "xorval" hätte man ohne Nachteil auch "0xA001" schreiben können.
// Für "cnt" gilt das nicht, eine "8" stattdessen lässt C 2 Register reservieren, weil "8" vom Typ "int" ist.
// "+" meint "Input/Output Operand" und erspart die fummelige, ältere Schweibweise mit dem "0"-Rückbezug
 crc=w;	// Registerpaar zurückschreiben
}

// Sollte man in einem realen Mikrocontrollerprogramm nicht brauchen:
// CRC über einen Speicherbereich ziehen.
// Im Mikrocontroller wird man CRC16CCITT in einem ostream oder istream einbauen.
static word crc16(const byte*buf, size_t len) {
 CRC16CCITT crc;			// Das sind 2 Byte, die mit 0xFFFF initialisiert werden
 if (len) do crc(*buf++); while(--len);	// Konstrukt zur zwangsweisen Schleifenoptimierung
 return crc;				// Das ruft den Cast-Operator auf den Plan
/* In C wäre das, für alte Hasen gut leserlich:
 word crc = 0xFFFF;
 if (len) do crc = crc16add(crc, *buf++); while(--len);
 return crc;
Ganz verrückte verzichten auf den zweiten Parameter von crc16add und schreiben:
 if (len) do crc = crc16add(crc ^ *buf++); while(--len);
*/
}

// Testaufruf. Es geht nur ums Kompilat von CRC16CCITT.
extern const byte __data_start;		// Das Linkerskript generiert dieses Symbol. Es verweist auf den RAM-Anfang.
int main() {
 return crc16(&__data_start,32);
}
Detected encoding: UTF-80