I have the following code that " functions " in that I can write a 32 bit counter value and place it in the High and Low words of the 32 bit register in a dsPic. I am using MPLAB X IDE and the XC16 compiler.
int main()
{
unsigned long My_Number; // 32 bit
unsigned int High_Word; // 16 bit
unsigned int Low_Word; // 16 bit
My_Number = 0x6000C000; // Decimal 1610661888
High_Word = (My_Number & (0x1FFFF << 16)) >> (16); // Returns 6000
Low_Word = (My_Number & 0xFFFF ) ; // Returns C000
return 0;
}
What I am not understanding is why I need to use 1FFFF to mask the High_Word.
I have tried different values to find what gets returned to find an explanation but something is not clicking and I don't like having anything that works without understanding why as that always leads to issues down the line.
Edit: Having read all the replies and done some re-thinking I have realised that rather than using 1FFFF I could have used FFFFUL to force it to an unsigned long integer and that works. Going further I can see that rather than shifting left to then shift right I could just use
High_Word = (My_Number >> 16);
Low_Word = My_Number;
This code returns the expected values and I believe is more compliant.
The comments seem to indicate that int and unsigned int are 16 bits in the targeted C implementation, and that long int and unsigned long int are 32 bits.
In such a C implementation 0xFFFF is an unsigned int per the table in C 2018 6.4.4.1 5. This means that 0xFFFF << 16 will take FFFF16 as a 16-bit item and ask to shift it 16 bits. Then the behavior of the program is not defined because C 2018 6.5.7 3, about shifts, says:
… If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
However, if we write 0x1FFFF, this is too large for an unsigned int. Then the table tells us it is a long int, which is 32 bits.
Now 0x1FFFF << 16 attempts to shift a 32-bit item by 16 bits.
It may be the author of the code thought this code would have defined behavior, since it no longer falls afoul of the rule in 6.5.7 3.
Unfortunately, it does fall afoul of a different rule in 6.5.7 4:
… If
E1has a signed type and nonnegative value, andE1× 2E2is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
In other words, while 0x1FFFF is 32 bits, it is a signed type, and the signed type can only represent values up 7FFFFFFF16. The mathematical product for the shift would be 1FFFF000016, which does not fit. So the behavior is not defined.
Had the author written 0x1FFFFu to request an unsigned type or 0xFFFFul to request an unsigned long int, then the behavior would be defined. That is because, for shifting an unsigned operand, the result is defined even if the product is not representable: Excess bits are discarded (as specified in the C standard, the result is wrapped modulo a modulus for the type).
I conjecture that, in the targeted C implementation 0x1FFFF << 16 produces FFFF000016 in the bits of a long int, and that is why the author used that code. Even though the C standard does not define the behavior, it may work. In contrast, 0xFFFF << 16 likely produces only a 16-bit result, which cannot be correct since FFFF16 is needed in the high bits.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With