Re: hexadecimal calculating: turning on the high bit



HanslH wrote:
Communicating (serial) from delphi with a device I have to conform to
some format.
One of the rules is that a two byte trailer should consist of a
checksum byte and an end byte (always 0x03)

I have to sum the value for every byte into an 'unsigned char' and
then ** turn on the high bit** to ensure that the value can never be
0x02 or 0x03 because these characters have special meaning.

I have hardly any experience with hexadecimal calculating and 'and'
'or' operators in bit calculations or whatever.

Hexadecimal doesn't really matter. A bit is a bit is a bit, no matter how you write it down.

I find that all examples in the reference *but one* will be calculated
successfully by the following algorithm
//======================================
function VetmessageChecksum(vetmessage : string) : string;
var
i:integer;
cHex : string;
aDec : arrayofinteger;
iChecksum : integer;
begin
cHex := '';

//make sure it ends with a space because of easier looping
if lastchar(vetmessage) <> ' ' then vetmessage := vetmessage + ' ';
//turning a '23 4F 45 22' type of string into an array of integer
for i := 1 to length(vetmessage) do begin
if (vetmessage[i]) = ' ' then begin
aadd(aDec,strtoint('$' + trim(cHex)));
cHex := '';
end else
cHex := cHex + vetmessage[i];
end;

Will the input string always be in that format? Groups of two hexadecimal digits separated by a single space? Then I'd process it differently. I'd also calculate the checksum along the way, instead of doing it as a separate loop. There's no need to add an extra space to the end using the code below. That also means you can declare the string parameter const.

Len := Length(vetmessage);
Assert(Succ(Len) mod 3 = 0);
i := 1;
iChecksum := 0;
while i < Length(vetmessage) do begin
cHex := Copy(vetmessage, i, 2);
value := StrToInt('$' + cHex);
aadd(aDec, value);
Inc(iChecksum, value);
Inc(i, 3);
end;

//summing the value iChecksum := 0;
for i := 0 to length(aDec) -1 do iChecksum := iChecksum + aDec[i];

/next ensures the *unsigned* char thing?
//I'm no expert but it's a trial and error assumption iChecksum := iChecksum mod 256;
result := inttohex(iChecksum,2);

"Unsigned char" is just C++'s name for Delphi's Byte type.

To get just the lowest byte of iChecksum, you can simply call the Lo function. Using "mod 256" will get the same value, but it carries a different connotation. You can also use "and $ff" or "and 255", which also do the same things.

iChecksum := Lo(iChecksum);
or
iChecksum := iChecksum and $ff;

Another way to ensure you only have one byte is the declare iChecksum as a Byte instead of an Integer. As a Byte, you're very likely to overflow when calculating the checksum, so you should turn off range checking. You should do that anyway:

{$R-}
Inc(iChecksum, value);
{$R+}

If iChecksum is supposed to have its high bit set, then do the following before converting it to a string. (More on that below.)

iChecksum := iChecksum or $80;

end;
//========================================

I find that the only example that doesnt compute right the checksum
byte is calculated '1d' but should be '9d'

That would correspond to a value that doesn't have its high bit set.

All other example checksums the checksum byte starts with a hexdigit >
7
Am I right to assume that turning on the high bit means that I have to
add 8 to the first hexdigit if it's 7 or less?

Yes. It's easier to just do this, though:

iChecksum := iChecksum or $80;

The "or" operation will set the bit, whether it was set or not. (If it was already set, then iChecksum's value won't actually change.)

--
Rob
.



Relevant Pages