Reputation: 39
the purpose of the MACRO I'm trying to write is to update a char passing a new value defined by Value, position and length. Let me give you an example to better explain:
Original value : 0b11000011 Value to update : 0b11 Position to put : 4 lenght to new value : 2
Basically I want to put two bits (0b11) in the middle of the byte (0b11000011) in order to have a new byte (0b11011011)
the MACRO I have right now is something like
#define UPDATE_REG(REG, MASK, POS, LEN, VAL) ((REG) = (((REG) & (MASK)) | (VAL<<(POS +1 -LEN))))
and it works well. For example, in my header I have definitions like
#define CP_BYP_MODE_SAD 0x38
#define CP_BYP_MODE_ADR 0xD4
#define CP_BYP_MODE_POS 4
#define CP_BYP_MODE_LEN 3
#define CP_BYP_MODE_DEF 0b00
#define CP_BYP_MODE_MSK 0b11100111
and to use the MACRO I write the following code
uint8_t Reg = 0b11000011;
UPDATE_REG(Reg, CP_BYP_MODE_MSK, CP_BYP_MODE_POS, CP_BYP_MODE_LEN, 0b11);
This solution works well but my point is that I'd like to reduce the arguments (if possible) with some tricks, considering that I'll use, for each call, three defines that are composed by name_MSK name_POS name_LEN
Just to be clear, my target is to have a MACRO something like
#define UPDATE_REG(REG, NAME, VAL) ((REG) = (((REG) & (**NAME_MSK**)) | (VAL<<(**NAME_POS** +1 - **NAME_LEN**))))
able to get, for example, CP_BYP_MODE_POS passing only CP_BYP_MODE and able to add automatically _POS suffix.
Upvotes: 0
Views: 170
Reputation: 213720
First of all, there's really no need to write obscure macros for this, you'll only make the code harder to read. It is unfortunately common that people try to hide simple bit-wise arithmetic behind abstraction layers, just because they find bit-wise arithmetic tricky.
What you should do instead is to keep it simple:
uint8_t reg = 0xC3;
uint8_t mask = 0x18;
uint8_t new_val = 0x18;
reg &= ~mask; // clear values with mask
reg |= new_val; // write new value to the cleared position
Notable, if you know the mask, that means you already know the size and bit position. You can calculate the mask based of size and position like this:
mask = ((1 << size)-1) << pos;
size = 2, pos = 3 gives:
1 << 2
= 0000 0100b = 0x04
0x04-1
= 0000 0011b = 0x03
0x03 << 3
= 0001 1000b = 0x18
Upvotes: 1
Reputation: 2898
Maybe this is what you want:
/* this bitfield may appear in more places: */
#define TWO_BITS_FOR_ALICE 4,2 /*starting bit, length in bits*/
#define REG1_TWO_BITS_FOR_ALICE REG1,TWO_BITS_FOR_ALICE
/* this is a global bitfield, e.g. some unique hardware register: */
#define REG1_TWO_BITS_FOR_BOB REG1,0,2 /*destination,starting bit,length in bits */
#define MASK_OUT(start,length) ~(MASK_IN((start),(length)))
#define MASK_IN(start,length) ((-1u<<(start))+(1u<<((start)+(length))))
#define SET_GBFLD(bfld,value) _SET_BFLD(bfld,value)
#define SET_BFLD(target,bfld,value) _SET_BFLD(target,bfld,value)
#define _SET_BFLD(target,start,length,value) ((target) = ((target)&MASK_OUT((start),(length))) | (((value) & MASK_IN(0,(length)))<<(start)))
#include <stdio.h>
int main(void)
{
unsigned REG1 = 0;
SET_GBFLD(REG1_TWO_BITS_FOR_ALICE,7);
SET_GBFLD(REG1_TWO_BITS_FOR_BOB,7);
printf("%04x ",REG1);
unsigned REG2 = 0;
SET_BFLD(REG2,TWO_BITS_FOR_ALICE,7);
printf("%04x ",REG2);
}
Upvotes: 0