since it's not typecast to anything, gets the default type of int. Then when it's shifted right, the sign bit is propagated to the right.
You can test this by typecasting that expression to uint16_t before shifting the whole thing right by 3, and seeing if it does what you expect.
To avoid having to worry about all that, you might consider shifting the reg[8] to the left by 8 bits, OR'ing with reg[9], and AND'ing the whole thing with a mask that clears the most significant bits. I think it would run faster, too.
The compiler expands the byte values to 16-bits before performing the shifts. I haven't examined the assembly code, but the optimizer might be fooling with the shifts also. Does this give the expected result?
Thank you for this explanation. That's what I was looking for. I will try to find more to read to be able to apply it.
Whandall:
Uncasted expressions are treated as signed ints.
Signed fields are filled with the sign bit on arithmetic right shifts.
Unsigned fields are filled with zeros on arithmetic right shifts.
But, looking closer and trying to understand your explanation, why doesn't the first uncast test return a 0x1FFF? "Signed fields are filled with the sign bit on arithmetic right shifts." The sign bit would be '0' in the case of 0x7F, no? When is the sign bit "checked", before the left shift or before the right shift? My tests are appearing to indicate "right shift of signed values is undefined."
Val - simple shift and add to combine two uint8_t into an uint16_t
Chr - Christensen's shift scheme
Cst - Christensen's shift scheme with the cast to uint16_t
The library's author, Jack Christensen has helped with the issue I posted to the github library. He speculated it wasn't a core issue but the ARM's native type of int32_t as opposed to the AVR's int16_t. A simple cast of int16_t fixed everything up.
Whandall:
All this behaviour and how to fix it was explained in #1.
Is this the fix?
tmd3:
To avoid having to worry about all that, you might consider shifting the reg[8] to the left by 8 bits, OR'ing with reg[9], and AND'ing the whole thing with a mask that clears the most significant bits. I think it would run faster, too.
This is the original code that works on AVRs and not on ARMs:
( ( regs[0] << 11 ) + ( regs[1] << 3 ) ) >> 3;
tmd3's (and your recommendation):
(( test_regs[0] << 8 ) | test_regs[1] ) & 0x1FFF;
Doesn't achieve the same end, I don't think. The goal isn't merely to mask the 3 most significant bits. It is, to take a signed 13 bit int that is embedded between bits 0 and 12, convert it to an int16_t and subtract it from 256 if it is negative. jchristensen achieved all of that with the single line... IF the native type is int16_t as in the AVR. When compiling for the ARM, the line has to be cast from the native int32_t to int16_t for the code to work.
jecottrell:
From the datasheet, it confirms that the objective of that line is to combine two bytes into an uint16_t and mask the three uppermost bits (to zeros).
jecottrell:
The goal isn't merely to mask the 3 most significant bits. It is, to take a signed 13 bit int that is embedded between bits 0 and 12, convert it to an int16_t and subtract it from 256 if it is negative.
Last night after working on it most of the day yesterday, that was what I thought to be the total purpose of that line. However, since then I've learned it does significantly more.