user2819934
user2819934

Reputation: 39

C MACRO that dynamically use different defines

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

Answers (2)

Lundin
Lundin

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

Vroomfondel
Vroomfondel

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

Related Questions