For bitfield elements > 8 bits vales are stored low part at low address.
Example:
// RTC_SHIFTR
volatile __at(ADR_RTC + 0x1A) union {
uint16_t r;
struct {
uint16_t
subfs: 15,
add1s: 1;
};
} rtc_shift;
//...
// code in some function:
rtc_shift.r = 0x1002;
/...
rtc_shift.subfs = 0x1002;
compiles to:
; rtc_shift.r = 0x1002;
ldw x, #_rtc_shift+0
ldw y, #0x1002
ldw (x), y ; (_rtc_shift + 0) = 0x10, (_rtc_shift + 1) = 0x02
; rtc_shift.subfs = 0x1002;
ldw x, #_rtc_shift+0
ld a, #0x02
ld (x), a ; (_rtc_shift + 0) = 0x02
ld a, #0x10
and a, #0x7f
push a
ld a, #0x80
and a, (0x1, x)
or a, (1, sp)
ld (0x1, x), a ; (_rtc_shift + 1) && 0x7F = 0x10
pop a
SDCC : mcs51/z80/z180/r2k/r3ka/gbz80/tlcs90/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8 3.5.2 #9280 (MINGW32)
I do not think this is a bug. Since endianess only defines the byte order within an int/long, not for a struct / union / array / bit_field. So
struct {
char a1;
char b2;
}
can only result a1 is stored in lower byte and b2 in upper byte. And for your case, add1s is stored in the upper byte.
I guess you expect rtc_shift.r = 0x1002 has the same effect with rtc_shift.subfs = 0x1002, right ? Unfortunately that is impossible given add1s is stored in the upper byte.
Consider rtc_shift.r = 0x7FFF, you will actually get rtc_shift.subfs equals to 0x7F7F, if subfs is also in big endian.
I don't think this is a bug. I think you are assuming things that the standard leaves for the implementation to define.
In the C standard (I have ISO/IEC 9899:1999 here; may be a different section in earlier or later versions) 6.7.2.1 paragraph 10:
And then 6.2.6.1 paragraph 4
Bolding is mine. My interpretation is that the bit-fields are not required to have the same representation as a non-bit-field element, even if they hold the same value.
For the specific case of SDCC, the bit-fields are always allocated and processed in a little endian representation, regardless of any particular endianness of the underlying processor.
I don't see a bug here either.
Using big-endian might be more efficient for some-use cases (since we could use ldw instructions in more scenarios), but less efficient for others (since little-endian storage makes more bytes of each bitfield byte-aligned, reducing the number of shifts necessary, for the common case of a bigger bitfield variable being aligned to a byte boundary - the code in the original post is an example : little-endianness allows us to just sign the lower byte of subfs).
Which one is better for which scenario might need more investigation, using bitfield-using code as benchmark. Unless sdcc users complain about bitfield-access efficiency in the stm8 port I would not consider that high priority for now though.
Philipp
I agree this is not a bug against the standard. But it still feels weird that sdcc uses a different endianness for bitfields from what it uses for normal ints. And I fail to see the point. I also don't see how it requires less shifts. In this particular case I would expect (rtcshift + 0) = 0x10 in both cases. I wonder what happens when you change subfs to a 16 bits bitfield.
According to my understanding:
Store an n > 8 bit bitfield in a byte-aligned little-endian bitfield: All bytes except for the most significant can be just copied, most significant byte needs masking, no shifting necessary.
Store an n > 8 bitfield in a byte-aligned big-ednidan bitfield: The most signifiant 8 bits need to go to the lowest address. The least significant byte needs masking. This means all bytes need some shifting.
Philipp
P.S.: I didn't think much about this when implementing bitfields for the stm8. I gues I was just too used to little-endian from working on the z80-related ports earlier.
Oops, I'm sorry. Shifting is indeed required in such a case. And thus in the bitfield case it would be (rtcshift + 0) = 0x20 following the big-endian format.
i would vote for keeping current situation, since it neither violate any standard nor generate inefficient assembly.
Closed. Since