Author Topic: PIC/Hitech C - conversion of char to boolean (bit?)  (Read 12060 times)

0 Members and 1 Guest are viewing this topic.

Offline iconTopic starter

  • Regular Contributor
  • *
  • Posts: 246
PIC/Hitech C - conversion of char to boolean (bit?)
« on: April 28, 2012, 06:56:18 pm »
Hi

I'm trying to convert my meagre ASM skills into C. I had wrongly assumed that a char (or the result of a char expression) would be understood as 'false' if it equalled 0 (ie 0b00000000) or 'true' if greater than 0 (eg 0b10000000). After an entire afternoon struggling with the logic of a few lines of code, I discover this is not so. Specifically, I'm interested in whether any bits between RB4 and RB7 are low. RB0 to RB3 could be anything. So I do this:
PORTB | 0b00001111

Then I 1's complement the result:
~(PORTB | 0b00001111)

If RB4 to RB7 had been all ones, I would now have an expression that evaluated to 0b00000000; or if (say) RB7 had been 0, then I would have 0b10000000. My intention is to use it in a statement:
if (~PORTB | 0b00001111) {stuff...

In ASM, I could just use comf whatever, btfsc STATUS,Z. Am I missing an obvious construction that would work in C?

Cheers
John
 

Offline Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11714
  • Country: my
  • reassessing directives...
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #1 on: April 28, 2012, 07:08:46 pm »
Quote
After an entire afternoon struggling with the logic of a few lines of code
yeah! hacking for the one goddamn bit. now you know the fun thing when somebody else encapsulated a funny standard into their library. but i cannot see why "greater or other than zero byte" = false.
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2095
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #2 on: April 28, 2012, 07:17:18 pm »
Am I missing an obvious construction that would work in C?

You are probably missing integer promotion rules. Values in expressions are promoted to a minimum size of int before evaluation. For some expressions this makes no difference and the compiler can optimize away the inefficiency, but, not in your case.

(PORTB & 0xF0)

Will be true if any of bits 4 to 7 are set in PORTB.

If you understand assembler looking at the code the compiler generates will help you understand the language. If you are using the free version of Hitech PICC the generated code will horribly inefficient.
« Last Edit: April 28, 2012, 07:21:04 pm by Rufus »
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #3 on: April 28, 2012, 07:58:25 pm »
I think you are doing it backwards.

First mask out the bits you are interested in:

PORTB & 0xF0

Now compare with the all ones value:

(PORTB & 0xF0) == 0xF0

If this expression is true you have all ones. If this expression is false you have one or more zero bits.
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #4 on: April 28, 2012, 08:01:50 pm »
I had wrongly assumed that a char (or the result of a char expression) would be understood as 'false' if it equalled 0 (ie 0b00000000) or 'true' if greater than 0 (eg 0b10000000). After an entire afternoon struggling with the logic of a few lines of code, I discover this is not so.

It is indeed so, by the way. That is not what is going wrong. Checking intermediate values with print statements or a debugger would help you see why your code is not working. Spending an afternoon struggling is not good when you could simply debug your code line by line and immediately find where it is not doing what you expect.
 

Offline iconTopic starter

  • Regular Contributor
  • *
  • Posts: 246
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #5 on: April 28, 2012, 10:59:39 pm »
I think you are doing it backwards.

First mask out the bits you are interested in:

PORTB & 0xF0

Now compare with the all ones value:

(PORTB & 0xF0) == 0xF0

If this expression is true you have all ones. If this expression is false you have one or more zero bits.

Reluctantly, and with ill grace, I have to concede that you're right. This:

RC0 = ~((PORTB & 0xF0) == 0xF0)

works (RC0 lights and goes out as expected), whereas

RC0 = (~(PORTB | 0x0F)) == 0

doesn't. It also takes 9 more bytes not to work, which seems poor value. Can you point out my blunder? I can't see for looking. All I can say is that the logic worked and made sense in the ASM version - testing for zero is built in. See below:

   movfw   PORTB
   movwf   latch
   movlw   b'00001111'
   iorwf   latch      ; make non-input bits 1
   comf   latch,1      ; and 'not'
   btfsc   STATUS,Z   ; ie skip next instruction if any switch closed
   goto   s_o         ; all switches open

I can feel myself falling back into the warm, welcoming bosom of ASM... must... resist...

John
 

alm

  • Guest
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #6 on: April 28, 2012, 11:17:25 pm »
I agree with Rufus's analysis, the result is probably promoted to a 16-bit integer. Casting the result to uint8_t might help. foo & MASK == MASK is the common pattern to check if certain bits are set. I would write PORTB & 0xF0 != 0xF0, however, or at least !(PORTB & 0xF0 == 0xF0). ~ is a binary inversion operator, so ~1 = 0xFFFE for 16-bit ints, while !1 is 0.
 

Offline amspire

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #7 on: April 28, 2012, 11:23:53 pm »

RC0 = (~(PORTB | 0x0F)) == 0
 Can you point out my blunder?
Have you tried:
RC0 = (~(PORTB | 0x0F) == 0)   ?

I am not sure of the order of precedence of "=" and "==", but it might be doing RC0 = (~(PORTB | 0x0F)) before (~(PORTB | 0x0F)) == 0. I  always surround a comparison in brackets just to make sure.
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #8 on: April 28, 2012, 11:39:41 pm »
Can you point out my blunder?

Not immediately. It is probably something like Rufus said, to do with integer promotion rules.

Your logic does seem to work, although it is not the way I would approach the problem. Here is my test program:

Code: [Select]
#include <stdio.h>

int main()
{
  unsigned char PORTB;

  PORTB = 0xA0;
  if ( ~ (PORTB | 0x0F) ) printf("%2X: zeroes are present\n", PORTB);

  PORTB = 0xF0;
  if ( ~ (PORTB | 0x0F) ) printf("%2X: no zeroes are present\n", PORTB);

  return 0;
}

And here is the output:

Code: [Select]
A0: zeroes are present
F0: no zeroes are present

To find out why it is not working for you, I think you will need to break it down into intermediate steps line by line, and then check the value at each step.
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #9 on: April 28, 2012, 11:45:39 pm »
RC0 = ~((PORTB & 0xF0) == 0xF0)

It was already mentioned above, but I will mention it also, that this should better be written as:

RC0 = ! ((PORTB & 0xF0) == 0xF0);

or even better

RC0 = (PORTB & 0xF0) != 0xF0;

"~" is the bitwise NOT operator, whereas "!" is the logical NOT operator.
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #10 on: April 29, 2012, 12:04:40 am »
For example, here is how you could set out the code line by line to match the assembly version:

Code: [Select]
  unsigned char PORTB;
  unsigned char latch;
  int RC0;

  PORTB = 0xA0;  // emulate input value

  latch = PORTB;       // read input byte
  latch |= 0x0F;       // set low order bits
  latch = ~latch;      // one's complement
  RC0 = latch ? 1 : 0; // set RC0 if any switches are closed

With the code in this form you can then step through it with the debugger and check the result of each operation. You should quickly see where something is going astray. Once it works, you can condense it back into a single expression.
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #11 on: April 29, 2012, 12:12:46 am »
Reluctantly, and with ill grace, I have to concede that you're right.

Sorry about the afternoon struggling comment, by the way. I have been there myself, so I speak from experience. In such situations the debugger is your friend. When programs do mysterious things you just have to get in there with the debugger and see what is happening.
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2095
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #12 on: April 29, 2012, 12:21:06 am »
I agree with Rufus's analysis, the result is probably promoted to a 16-bit integer.

In this

~(PORTB | 0b00001111)

it is PORTB that gets promoted to int or unsigned int (16 bits) with the high order 8 bits getting sign extension or 0s depending on PORTB being signed or unsigned. Assuming PORTB is unsigned the high 8 0 bits get complemented and the result is always non-zero or true. The complier should optimize away the whole expression, except PORTB is probably volatile so the compiler is required to read it. That is my understanding without reading up on promotion rules again.

~(PORTB | 0xFF0F) would do what I think the OP intended.

Not considering the effect of promotions is an easy mistake to make but it doesn't often make a difference.
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #13 on: April 29, 2012, 12:29:49 am »
Incidentally, when you create your constant 0b00001111 you are not doing it the best way. It is far better to write ~0b11110000 instead as then you will guarantee to put 1's everywhere they might be needed, and not just in the lower four bits.

If you do this you will then have PORTB | ~0b11110000

Next you will 1's complement the result:

~ (PORTB | ~0b11110000)

Now if you apply De Morgan's laws to this expression you get the equivalent form

~PORTB & 0b11110000

Now you can write

  if (~PORTB & 0b11110000) { /* a switch is closed */ }

Since "0b" is non-standard, good style would suggest

  if (~PORTB & 0xF0) { ... }

or perhaps

  RC0 = (~PORTB & 0xF0) ? 1 : 0;
 

Offline iconTopic starter

  • Regular Contributor
  • *
  • Posts: 246
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #14 on: April 29, 2012, 05:08:09 pm »
Hi

Well thanks for all the replies! Having compounded my spending an afternoon on this, by adding a further evening and afternoon, I can confirm that this

 ((PORTB & 0xF0) != 0xF0)

works as expected, as does

RC0 = (~PORTB & 0xF0) ? 1 : 0

There's enough other material in this thread to spend another day playing with with other ways of expressing the logic, and exploring how the compiler treats different objects, but I gotta get on. Besides, how many programming cycles can one PIC take before dying?

I'm going to start another thread about debugging, since there's clearly lots I'm missing out on.

Thanks again.

John
 

Offline iconTopic starter

  • Regular Contributor
  • *
  • Posts: 246
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #15 on: April 29, 2012, 06:29:57 pm »
OK, I know what I said, but look at this.

RC0 = (PORTB | 0x0F) ? 1 : 0;

Produces this code:

BCF 0x3, 0x5
BCF 0x3, 0x6
MOVF 0x6, W
IORLW 0xf
BTFSC 0x3, 0x2
GOTO 0x7fc
BCF 0x3, 0x5
BCF 0x3, 0x6
BSF 0x7, 0
GOTO 0x7de
BCF 0x3, 0x5
BCF 0x3, 0x6
BCF 0x7, 0

Basically what you'd expect - it doesn't do what I need, of course, because I need a 'COMF' somehow after the IORLW. So lets stick in a bitwise operator to do that:

RC0 = ~(PORTB | 0x0F) ? 1 : 0;

Produces this:

BCF 0x3, 0x5
BCF 0x3, 0x6
BSF 0x7, 0

That's it! Just complete bollocks. No testing, no nothing, just sets RC0 to 0, thank you very much. No wonder the sodding code doesn't work.

(~PORTB & 0xF0) also (obviously) produces a working version of code.

Thanks for listening.

John
 

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 14126
  • Country: gb
    • Mike's Electric Stuff
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #16 on: April 29, 2012, 06:33:55 pm »
Quote
RC0 = (PORTB | 0x0F) ? 1 : 0;
This should always just set RC0 regardless of PORTB, as anything | 0x0f is going to be nonzero
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 

Offline iconTopic starter

  • Regular Contributor
  • *
  • Posts: 246
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #17 on: April 29, 2012, 07:11:14 pm »
Quote
RC0 = (PORTB | 0x0F) ? 1 : 0;
This should always just set RC0 regardless of PORTB, as anything | 0x0f is going to be nonzero

Yes that's right - but the code is correctly generated to *test* whether it's true - I suppose if anything the compiler could in theory optimise it out. My point is that when you stick a ~ in front of it (so now FALSE if PORTB | 0x0F had been all ones, or TRUE if there had been any zeros - which is what I'm trying to test) the compiler optimisation *does* suddenly decide to creak into action - only wrongly.

John
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #18 on: April 29, 2012, 07:25:48 pm »
OK, try this:

RC0 = ~(PORTB | ~0xF0) ? 1 : 0;

Remember my note previously, you should always tell the compiler what bits you are interested in, not what bits you are not interested in. You are interested in bits 4-7, so tell the compiler that explicitly, don't leave it to assume your intent.
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2095
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #19 on: April 29, 2012, 07:50:14 pm »
RC0 = ~(PORTB | 0x0F) ? 1 : 0;

That's it! Just complete bollocks. No testing, no nothing, just sets RC0 to 0, thank you very much. No wonder the sodding code doesn't work.

I already posted about integer promotions. Both values are promoted to 16 bit unsigned ints. If PORTB contains 0xF0 for example what you are saying is

RC0 = ~(0x00F0 | 0x000F) ? 1 : 0;

Which unconditionally assigns 1 to RC0.

As an aside the Hi-Tech compiler is bugged, the compiler should generate code to read PORTB regardless because PORTB is qualified volatile. Sometimes it optimizes away these required accesses.
 

Offline iconTopic starter

  • Regular Contributor
  • *
  • Posts: 246
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #20 on: April 29, 2012, 07:54:59 pm »
OK, try this:

RC0 = ~(PORTB | ~0xF0) ? 1 : 0;

Remember my note previously, you should always tell the compiler what bits you are interested in, not what bits you are not interested in. You are interested in bits 4-7, so tell the compiler that explicitly, don't leave it to assume your intent.

Again, gritted teeth, you're quite right; it works. That was one of the options I didn't try. Interestingly, it does go all 16 bit, and uses incf to see whether the register rolls over to zero, rather than comf. The code is longer and (because it's 16bit) uses more registers than your other version, so it's of academic interest only!

Thanks
Jon
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 12570
  • Country: us
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #21 on: April 29, 2012, 07:55:36 pm »
Both values are promoted to 16 bit unsigned ints.

Which is the technical reason why you should write ~0xF0 rather than 0x0F. The compiler should convert ~0xF0 to 0xFF0F in 16 bit cases, or 0xFFFFFF0F in 32 bit cases (see my debugger window in the other thread).
 

Offline iconTopic starter

  • Regular Contributor
  • *
  • Posts: 246
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #22 on: April 29, 2012, 08:01:06 pm »
I already posted about integer promotions.

You're quite right, you did. I didn't follow through the implication that the compiler might do all that 'internally' without any sign of it making its way into the code.

Cheers
John
 

Offline Rufus

  • Super Contributor
  • ***
  • Posts: 2095
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #23 on: April 29, 2012, 08:07:54 pm »
You're quite right, you did. I didn't follow through the implication that the compiler might do all that 'internally' without any sign of it making its way into the code.

That is what an optimizing compiler does. You appear to be using the free version of the Hi-Tech compiler which has severely crippled optimization. I was a little surprised to see it optimize away the whole expression.
 

Offline iconTopic starter

  • Regular Contributor
  • *
  • Posts: 246
Re: PIC/Hitech C - conversion of char to boolean (bit?)
« Reply #24 on: April 29, 2012, 08:34:54 pm »
You appear to be using the free version of the Hi-Tech compiler which has severely crippled optimization.

I can't speak for the optimisation, but I can say that the code appears to be stuffed full of redundant 'GOTO's. The algorithm seems to be - wherever there's a real GOTO needed, stuff a fake one after it that's never called, but pads out the code. In Pro mode (I've turned on the trial) these aren't there. "Omniscient Code Generation" seems to = "Don't pad the code out with redundant GOTOs". Why, it's almost like making a 100MHz scope and then crippling some of them and selling them as 50MHz. Call me suspicious.

John
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf