I'm having trouble simplifying a code (Bitwise Operations)

Hi everyone, im trying to make this code shorter and simpler, but I am not that good with bitwise operations, I have a 16 bit variable called OutputModeByte1 this variable stores 8 - 2bit "sub variables", so their range is from 0 to 3, I need to make three functions to set their bits according to the mode variable.

x is telling which from the 8 "sub variable" (from 0 to 7) is to be changed.

This is the code:

switch(x)
        {
            case 0:
                
                switch(mode)
                {
                    case 0: OutputModeByte1 = OutputModeByte1 & 0b1111111111111100; break;
                    case 1:
                        
                        OutputModeByte1 = OutputModeByte1 & 0b1111111111111100;
                        OutputModeByte1 = OutputModeByte1 | 0b0000000000000001;
                        
                        break;
                        
                    case 2:
                        
                        OutputModeByte1 = OutputModeByte1 & 0b1111111111111100;
                        OutputModeByte1 = OutputModeByte1 | 0b0000000000000010;
                        
                    break;
                }
            
                break;
                
            case 1:
                
                switch(mode)
            {
                    
                case 0: OutputModeByte1 = OutputModeByte1 & 0b1111111111110011; break;
                case 1:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111111111110011;
                    OutputModeByte1 = OutputModeByte1 | 0b0000000000000001;
                    
                    break;
                    
                case 2:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111111111110011;
                    OutputModeByte1 = OutputModeByte1 | 0b0000000000000010;
                    
                    break;
            }
                
                break;
                
            case 2:
                
                switch(mode)
            {
                    
                case 0: OutputModeByte1 = OutputModeByte1 & 0b1111111111001111; break;
                case 1:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111111111001111;
                    OutputModeByte1 = OutputModeByte1 | 0b0000000000010000;
                    
                    break;
                    
                case 2:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111111111001111;
                    OutputModeByte1 = OutputModeByte1 | 0b0000000000100000;
                    
                    break;
            }
                
                break;
                
            case 3:
                
                switch(mode)
            {
                    
                case 0: OutputModeByte1 = OutputModeByte1 & 0b1111111100111111; break;
                case 1:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111111100111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0000000001000000;
                    
                    break;
                    
                case 2:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111111100111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0000000010000000;
                    
                    break;
            }
                
                break;
                
            case 4:
                
                switch(mode)
            {
                    
                case 0: OutputModeByte1 = OutputModeByte1 & 0b1111110011111111; break;
                case 1:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111110011111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0000000100000000;
                    
                    break;
                    
                case 2:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111110011111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0000001000000000;
                    
                    break;
            }
                
                break;
                
            case 5:
                
                switch(mode)
            {
                    
                case 0: OutputModeByte1 = OutputModeByte1 & 0b1111001111111111; break;
                case 1:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111001111111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0000010000000000;
                    
                    break;
                    
                case 2:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1111001111111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0000100000000000;
                    
                    break;
            }
                
                break;
                
            case 6:
                
                switch(mode)
            {
                    
                case 0: OutputModeByte1 = OutputModeByte1 & 0b1100111111111111; break;
                case 1:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1100111111111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0001000000000000;
                    
                    break;
                    
                case 2:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b1100111111111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0010000000000000;
                    
                    break;
            }
                
                break;
                
            case 7:
                
                switch(mode)
            {
                    
                case 0: OutputModeByte1 = OutputModeByte1 & 0b0011111111111111; break;
                case 1:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b0011111111111111;
                    OutputModeByte1 = OutputModeByte1 | 0b0100000000000000;
                    
                    break;
                    
                case 2:
                    
                    OutputModeByte1 = OutputModeByte1 & 0b0011111111111111;
                    OutputModeByte1 = OutputModeByte1 | 0b1000000000000000;
                    
                    break;
            }
                
            break;
                
            default: break;
        }

I only worked out how to do the mapping to set both bits to 00

OutputModeByte1 = OutputModeByte1 & (0b1111111111111111 ^ (3 << x * 2));
 
// Xor 0b1111111111111111 with 3 (binary 11) moved to the left x spaces times 2 
// (because each variable is 2 bit long) to set both bits to 0.

With the other two functions the code could be simplified to something like this:

switch(mode)
                {
                    case 0: OutputModeByte1 = OutputModeByte1 & (0b1111111111111111 ^ (3 << x * 2));    break;
                    case 1: ??????????????     break;
                    case 2: ??????????????     break;
                    default: break;
                }

The only idea I've had is to first, shift both to 0 with the same function as for mode 0, and then turn on the respective bit, any thoughts? :slight_smile: :slight_smile: :slight_smile: :slight_smile: :slight_smile:

You can use a bitfield.

struct MyData{
  uint16_t value1 : 2;
  uint16_t value2 : 2;
  uint16_t value3 : 2;
  uint16_t value4 : 2;
  uint16_t value5 : 2;
  uint16_t value6 : 2;
  uint16_t value7 : 2;
  uint16_t value8 : 2;
};

MyData example;

example.value1 = 3;

Each value is two bits, and will be interpreted as an unsigned int.

Edited bits was bytes. Sorry.

BulldogLowell:
You can use a bitfield.

struct MyData{

uint16_t value1 : 2;
  uint16_t value2 : 2;
  uint16_t value3 : 2;
  uint16_t value4 : 2;
  uint16_t value5 : 2;
  uint16_t value6 : 2;
  uint16_t value7 : 2;
  uint16_t value8 : 2;
};

MyData example;

example.value1 = 3;




Each value is two bytes, and will be interpreted as an unsigned int.

Sure I can, but I forgot to say this is running inside a Library and I want it as memory efficient as possible, thats why I used a 16 bit variable to store the 8 variables inside instead of using 16 - 8 bit variables.

this way I'm using 16 bits instead of 128 bits. So this way I'm saving 112 bits of memory :o :slight_smile:

that's why I must change only those bits without affecting tho other ones.

You can do it the way that BulldogLowell has suggested, but you will have to use an array to make use of the x variable.

You did not explain why you are using an int ( a two-byte variable) with a name that suggests that it holds a single byte using the name OutputModeByte1.

You did not explain what three out of four possibilities you want, so I did all four. You can use the three that are of interest to you...

Another way:

int twox = 2 * x ;
OutputModeByte1 &= ~(3<<twox) ;
switch (mode) {
case 0:  //  Nothing to do here.  The appropriate two bits of OutputModeByte1 have been zeroed out.
  break ;
case 1:
  OutputModeByte1 |= (1<<twox) ;
  break ;
case 2:
  OutputModeByte1 |= (2<<twox) ;
  break ;
case 3:
  OutputModeByte1 |= (3<<twox) ;
  break ;

x is telling which from the 8 "sub variable" (from 0 to 7) is to be changed.

set_bitfield(byte x, byte val) {
/*
 * OutputModeByte1 is a 16but unsigned integer
 *  x is the number (0 to 7) of a two-bit bitfield withing the integer
 *   (0 means bits 0 and 1, 1 means bits 2 and 3, etc.)
 *  val is the two-bit value to put in the bitfield.
 */

   // create a two bit bitmask in the proper place
   unsigned int bitmask = 0b11 << (x*2);

   // shift the new value into the proper position.
   val = val << (x*2);

   OutputModeByte1 &= ~bitmask;  // zero the destination bits
   OutputModeByte1 |= val;	 // or in the new bits
}

I hope that wasn't homework.

westfw:
I hope that wasn't homework.

I'm just learning to properly do bit math. It's the first time I come across this kind of problem.

so let me make the question simpler.

Depending on the mode ,that could be from 0 to 2, I need to map 00, 01 or 10 respectively.

The position on the 16 bit variable depends on the x variable.

So for example for x = 0 and mode = 0, I would need to map 00 in the 16 bit variable (aka "OutputModeByte1") this way xxxxxxxxxxxxxx00 (Keep in mind that the x's must stay as they were)

let me put a few more examples

x = 2 , mode = 1 --> xxxxxxxxxx01xxxx
x = 5 , mode = 2 --> xxxx10xxxxxxxxxx
x = 3 , mode = 0 --> xxxxxxxx00xxxxxx
x = 1 , mode = 1 --> xxxxxxxxxxxx01xx

I hope with this my question is easier to understand.

I am trying to do the bitfield right now, thanks =)

Edit: That was just what I was trying to do, thanks a lot, learned a lot from it, thanks westfw. :slight_smile:

cheche_romo:
Sure I can, but I forgot to say this is running inside a Library and I want it as memory efficient as possible

Bitfields are 100% memory efficient, at least as far as storage for the variables. The compiler may need a few temporary variables to insert and extract bits at run time.

In this code, LSB is the position of the least significant bit in the field (the amount of left shift) and length is the number of bits.

const uint16_t MASK = ((1<<length)-1) << LSB;

packed_field &= ~MASK; // remove old values from the field.
packed_field |= new_value << LSB; // Insert new values.

Make sense?

Yes, I corrected my post. The struct uses 16 bits

Just as efficient as your proposed bitshift idea, without all the hassle of shifting.

cheche_romo:
let me put a few more examples

x = 2 , mode = 1 --> xxxxxxxxxx01xxxx
x = 5 , mode = 2 --> xxxx10xxxxxxxxxx
x = 3 , mode = 0 --> xxxxxxxx00xxxxxx
x = 1 , mode = 1 --> xxxxxxxxxxxx01xx

I hope with this my question is easier to understand.

Are you saying...... if your bit pattern is b15 b14 b13..... b1 b0,

then x = 2 accesses b5 b4

and x = 5 accesses b11 b10

and x = 3 accesses b7 b6

So, whatever the mode is.... you defined your own pair of bit positions to access, right?

And the 'mode' value is a decimal value, converted to 2 bits (binary) - to be inserted into whatever pair of bit positions being accessed, right?

example using bitfield...

struct MyData{
  uint8_t value1 : 2;
  uint8_t value2 : 2;
  uint8_t value3 : 2;
  uint8_t value4 : 2;
  uint8_t value5 : 2;
  uint8_t value6 : 2;
  uint8_t value7 : 2;
  uint8_t value8 : 2;
}data;

void setup() 
{
  Serial.begin(9600);
  Serial.println(sizeof(data));  // you can see the 16bit data size...
  data.value1 = 5;
  Serial.println(data.value1);  // you can see the overflow at data.value1 = 4...
}

void loop() 
{

}

This:

OutputModeByte1 = (OutputModeByte1 & ~(3 << (x*2))) | (mode << (x*2));

Or:

OutputModeByte1 &= ~(3 << (x*2));
OutputModeByte1 |= mode << (x*2);

PaulMurrayCbr:
This:

OutputModeByte1 = (OutputModeByte1 & ~(3 << (x*2))) | (mode << (x*2));

Or:

OutputModeByte1 &= ~(3 << (x*2));

OutputModeByte1 |= mode << (x*2);

I prefer generating the masks using this formula:

((1<<length)-1) << offset

Shifting + subtraction creates correct number of contiguous bits for the mask (for example for a 4 bit mask, 1<<4 = 0x10, 0x10-1 = 0x0F), and offset shifts it up to the right location.

Thanks all for your replies, I've got it working now, this is what I did:

void BPM_Clock::OutputMode_bitMask(bool n, uint8_t x, uint8_t mode)
{
    uint16_t bitmask = 0b11 << (x*2);
    uint16_t _mode = mode << (x*2);
    
    switch(n)
    {
        case 0:
            
            OutputModeByte1 &= ~bitmask;  // zero the destination bits
            OutputModeByte1 |= _mode;	 // or in the new bits
            
        break;
            
        case 1:
            
            OutputModeByte2 &= ~bitmask;  // zero the destination bits
            OutputModeByte2 |= _mode;	 // or in the new bits
            
        break;
    }
}

im trying to make this code shorter and simpler

Sigh...