#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);
}
Vorgefundene Kodierung: UTF-8 | 0
|