EmbeddedRelated.com
Forums
Memfault Beyond the Launch

Details on compiler behaviour for 8051 bit manipulation

Started by Unknown January 20, 2007
I was investigating my 8051 compiler's (its Hitech 7.6) behaviour
regarding bit manipulation, and came up with some strange/interesting
results. Lets say we want to manipulate PX0, which is the lowest bit in
the IP register. Now, PX0 is defined with

static bit	PX0	@ 0xB8;

which is the BIT address 0xB8. And IP_BITS is defined with

static SFR_BITS	IP_BITS		@ 0xB8;

which is the BYTE address 0xB8. SFR_BITS is a special struct that is
defined with

typedef struct {
	unsigned B7:1;	/* this is the most significant bit */
	unsigned B6:1;
	unsigned B5:1;
	unsigned B4:1;
	unsigned B3:1;
	unsigned B2:1;
	unsigned B1:1;
	unsigned B0:1;
}	SFR_BITS;

Now, IP_BITS.B0 also accesses the BIT address 0xB8, that is, exactly
the same as PX0.
Then, I expected that manipulation of IP_BITS.B0 and PX0 would be
totally interchangeble, but it seems they are not. The following test
program illustrates this:

// 8051test.c
#include "8051.h"
void Test()
{
    // Self copying in 4 flavours
    IP_BITS.B0 = IP_BITS.B0;
    IP_BITS.B0 ^= 0;
    PX0 = PX0;
    PX0 ^= 0;

    // Inverting in 4 flavours
    IP_BITS.B0 = !IP_BITS.B0;
    IP_BITS.B0 ^= 1;
    PX0 = !PX0;
    PX0 ^= 1;
}

compiles to this:

	global	stack_internal
	psect	text,class=CODE
	psect	text
	global	_Test
	signat	_Test,88
_Test:
	jmp	f861
f860:
;8051test.c: 5: IP_BITS.B0 = IP_BITS.B0;
	mov	c,ip.0
	mov	ip.0,c
;8051test.c: 6: IP_BITS.B0 ^= 0;
;8051test.c: 7: PX0 = PX0;
	mov	c,ip.0
	mov	ip.0,c
;8051test.c: 8: PX0 ^= 0;
;8051test.c: 11: IP_BITS.B0 = !IP_BITS.B0;
	bnb	ip.0, u11
	jmp	u10
u11:
	mov	r1,#1
	sjmp	u20
u10:
	mov	r1,#0
u20:
	mov	r0,#0
	mov	a,r1
	mov	c,acc.0
	mov	ip.0,c
;8051test.c: 12: IP_BITS.B0 ^= 1;
	cpl	ip.0
;8051test.c: 13: PX0 = !PX0;
	cpl	ip.0
;8051test.c: 14: PX0 ^= 1;
	cpl	ip.0
;8051test.c: 15: }
l2:
	ret
f861:
	jmp	f860
f862	equ	0
	end

Note that doint ^=0;  will not read/write anything, but doing just =
assignment to itself means reading and then writing back. Question:
Does this corresponds to anything at all in the C standard or the 8051
hardware?

Note also that IP_BITS.B0 = !IP_BITS.B0; compiles to something far more
complicated than all the other 3 inverting statements. Question: Does
this corresponds to anything at all in the C standard or the 8051
hardware?

Feel free to comment on this. However, comments like "its all the same"
is not useful. The BNB and CPL instructions does NOT read the same
thing, if it had been a port pin (compiler behaves the same).

ttl_idiot@yahoo.com wrote:

> Now, IP_BITS.B0 also accesses the BIT address 0xB8, that is, exactly > the same as PX0. > Then, I expected that manipulation of IP_BITS.B0 and PX0 would be > totally interchangeble, but it seems they are not.
That expectation is based on invalid assumptions. You assume that addresses from independent memory spaces can meaningfully be compared to each other. For bit number 0 in an 8051 bit-addressable SFR, that happens to be the case, but it's just coincidence. You wouldn't expect that a char* and a double* both pointing to the same address to be interchangeable, now would you? Why should this be true for a bit* and an SFR*, then?
> Note that doint ^=0; will not read/write anything, but doing just = > assignment to itself means reading and then writing back. Question: > Does this corresponds to anything at all in the C standard or the 8051 > hardware?
It corresponds to the fact that all SFRs in an 8051 are, from the C standard's point-of-view, inherently "volatile". So a compiler is not allowed to optimize away any action that accesses that variable. The fact that your compiler complete removed the ^=0 operation is IMHO a break of standard compliance, i.e. a compiler bug.
> Note also that IP_BITS.B0 = !IP_BITS.B0; compiles to something far more > complicated than all the other 3 inverting statements. Question: Does > this corresponds to anything at all in the C standard or the 8051 > hardware?
! is a logical operator on a standard C operant, ^ is a binary operator. They differ in intended semantics. Standard C has no command over what you do with bit variables --- those are 100% non-standard.
Hans-Bernhard Br=F6ker wrote:
> ttl_idiot@yahoo.com wrote: > > > Now, IP_BITS.B0 also accesses the BIT address 0xB8, that is, exactly > > the same as PX0. > > Then, I expected that manipulation of IP_BITS.B0 and PX0 would be > > totally interchangeble, but it seems they are not. > > That expectation is based on invalid assumptions. You assume that > addresses from independent memory spaces can meaningfully be compared to > each other. For bit number 0 in an 8051 bit-addressable SFR, that > happens to be the case, but it's just coincidence. > > You wouldn't expect that a char* and a double* both pointing to the same > address to be interchangeable, now would you? Why should this be true > for a bit* and an SFR*, then?
Note that IP_BITS.B0 and PX0 both in fact compiles to accessing the BIT variable 0xB8. Do you mean that accessing IP_BITS.B0 should, in order to follow the c standard, have to access (in c standard terminology) the whole byte IP_BITS and then mask out the bit B0?
> The fact that your compiler complete removed the ^=3D0 operation is IMHO a > break of standard compliance, i.e. a compiler bug.
That is interesting.
> > Note also that IP_BITS.B0 =3D !IP_BITS.B0; compiles to something far mo=
re
> > complicated than all the other 3 inverting statements. Question: Does > > this corresponds to anything at all in the C standard or the 8051 > > hardware? > > ! is a logical operator on a standard C operant, ^ is a binary operator. > They differ in intended semantics. Standard C has no command over > what you do with bit variables --- those are 100% non-standard.
Doing '~' on the bit variable gave the same result as doing '!'. This inspired me to test the same for BYTE sfr: P0 =3D P0; compiles to mov 080h,080h but P0 ^=3D 0; compiles to nothing! However, the compiler generates a "expression generates no code" warning.

Memfault Beyond the Launch