C89 : bitfields with enumerated options - Page 1
Forum section: electronics / general

author: free_electron
author_rank: Super Contributor
timestamp: July 11, 2018, 04:16:15 pm
content: Note :this is C , not C++ !! can't switch compilers. this is microchip C compiler. No arguing over compilers please.consider this:an 8 bit register holding a 2 bit variable, a single bit variable and 5 dummy bits.Code:typedef struct {unsigned int twobits :2;unsigned int singlebit :1;unsigned int fivebits :5;} some_register;volatile some_register register_one;void test(void){register_one.singlebit =1;register_one.fivebits = 0x03;}That all works fineNow, i want to define the possible combinations for 'twobits' using an enum so it makes my life easier and the code editor can use autocompletion.Code:typedef enum {setting_a = 0x00,setting_b= 0x01 ,setting_c = 0x02 ,setting_d = 0x03) bitmodesso i can writeCode:register_one.twobits = setting_c;...if register_one.twobits = setting_a then ......case register_one.twobitssetting_asetting_bsetting_c.....how can i do this ?The key is that i want autocomplete in the editor to pick up the possible settings. i do not want to have to guess what setting goes with what register.i am trying to avoid coding errors. there may be other enums for 2 bits that have nothing to do.real life example : a configuration register for a io pinCode:typedef struct {unsigned int pinmode :2; // 3 possible settings : analog = 0, input = 2 , output= 3 . 1 is an illegal setting !unsigned int pullup :1; // 2 possible settings enabled = 0 , disabled = 1 <= note reverse logic here !unsigned int interruptmode :2; // 4 possible settings : none, rising, falling, bothunsigned int state :1; // 2 possible settings high, lowunsigned int drivestrength :2 // 4 possible settings none,weak,medium,high} iopin;volatile iopin pin1;volatile iopin pin2;i want to writeCode:pin1.strength = weak;pin2.interruptmode = rising;if pin1.state = high then pin2.pullup = enabled;how can i define this ?again : it is VERY important that the code editor automatically suggest the list of allowable settings. that is the functionality i am searching for.if i writeCode:pin1.strength = enabled...pin2.interuptmode = 0x02i want the compiler to stop and throw a warning since 'enabled' is not an allowable value for 'strength'.same for assigning values directly. i am ok if that does not stop the compiler. but using wrong constants should stop compilationideally it should be blocked. sing pin mode has only 3 possible states and 0x01 is illegal you should not be allowed to writeCode:pin1.pinmode = analog; //0pin1.pinmode = input; // 2pin1.pinmode = output; //3pin1.pinmode = 0x01; as that is NOT a valid combination. <-throws error during compilation

author: dmills
author_rank: Super Contributor
timestamp: July 11, 2018, 05:03:39 pm
content: In C an enumeration is not really a type, it is a set of integers, so there is no really good way to avoidCode:pin1.strength = enabledbecause enabled and low could translate to the same integer.This is why the microchip headers make you do things likeCode:TRISAbits.TRISA7 = 0;Note that C does not define the order of bits in a bitfield (Which are horrifically badly defined), so using this to map an IO register or such is living in sin per MISRA at the least.I can think of some UGLY, UGLY macro hackery that would sort of get you the semantics you want, but it would not get you the IDE/CTAGS behaviour that you seem to be after.

author: ajb
author_rank: Super Contributor
timestamp: July 11, 2018, 05:47:15 pm
content: Error detection like this is why MCU vendor libs require fifty lines of code and a twelve byte config struct to setup an IO pin. There simply isn't the sophisticated type safety in the C language to support the kind of checking that you're after natively, so you're stuck just checking the values at run time, or via static asserts. But even then, you won't be able to get an error on "pin1.stength = enabled" if "enabled" represents a valid numeric value for ".strength", because C enums are just integers with fancy labels.An alternative that at least indicates whether or not a value is valid is to use verbose names for your enums, likeCode:typedef enum {PIN_STRENGTH_WEAK = 0x00,PIN_STRENGTH_MEDIUM = 0x01 ,PIN_STRENGTH_STRONG = 0x02 ,PIN_STRENGTH_HULK = 0x03} pin_strength;Then at least you can tell whether or not a value is valid by inspecting the assignment:Code:pin1.strength = PIN_STRENGTH_WEAK // okay, .strength is assigned to "PIN_STRENGTH_"pin1.strength = PIN_MODE_ALTFUNCTION // not okay, because .strength is assigned to "PIN_MODE_"You'll see lots of header files that use this style. As a bonus, as long as you're consistent, when you go to set pin1.strength, you know that you can type "PIN_STRENGTH_" and autocomplete will show you the valid options. If you wanted to get fancy, you could probably write a pre-build script that scans the code and checks for violations of this pattern. The downside is that it's verbose in a way that is anathema to many C developers, but in general the time taken to bang out a few more characters is well worth the time saved in reinterpreting or debugging the code later on.

author: JPortici
author_rank: Super Contributor
timestamp: July 11, 2018, 05:55:16 pm
content: I think the most common approach to your main problem (using keywords instead of register bits) is to use a set of defines, or at least it's how i always saw it implemented in any mcu i worked with.Since i don't like typing lots and lots and lots of defines and macros and multiline statements my usual approach is to write the register value directly and comment the settings.If i can spare a few cycles during initialization i can just set each bit(s) individually and comment with what am i setting.There is also the case of a peripheral which requires a specific sequence of operations, like first you have to set one bit, then you can set the registers and so on.For the rest, Since you mention C89 i assume you are using XC8.If I remember correctly in XC8 the enum type is implemented with a char (8 bit) type, unless the number of states or explicit assignments of values forces the compiler to use larger data types.I created a small project using XC8 v1.35 and PIC18F26K22 (Simulator)Please correct me if i'm wrongQuotei don't think you can do that in C. In C enums are just fancy integers, and all enums are the same. there is no type checking. XC8 makes no difference, just tried it.Quotesame because of the reason aboveQuoteyou can't do that either. when you write to bitfields the value is masked, so if your bitfield is two bits wide and you write 5 the bitfield will assume value "1".You can however implement a function like thisCode:char SetNewSettings(enum_t newSetting, whatever_t* register) {swhitch (newSetting) {case valid1:case valid2:...case validLast:register->field = newSetting;return 0;break;default:return -1; //illegal operationbreak;}}which is... er... a tedious taskgo fetch the intern...I also tried declaring a structure with a bitfield defined as a union. i'm sure you tried it as well and you also got this error messageCode:main.c:97: error: (264) bad bitfield typemy small project..Code:// PIC18F26K22 Configuration Bit Settings// 'C' source line config statements// CONFIG1H#pragma config FOSC = ECHPIO6 // Oscillator Selection bits (EC oscillator (high power, >16 MHz))#pragma config PLLCFG = OFF // 4X PLL Enable (Oscillator used directly)#pragma config PRICLKEN = ON // Primary clock enable bit (Primary clock enabled)#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)#pragma config IESO = OFF // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)// CONFIG2L#pragma config PWRTEN = OFF // Power-up Timer Enable bit (Power up timer disabled)#pragma config BOREN = SBORDIS // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))#pragma config BORV = 190 // Brown Out Reset Voltage bits (VBOR set to 1.90 V nominal)// CONFIG2H#pragma config WDTEN = ON // Watchdog Timer Enable bits (WDT is always enabled. SWDTEN bit has no effect)#pragma config WDTPS = 32768 // Watchdog Timer Postscale Select bits (1:32768)// CONFIG3H#pragma config CCP2MX = PORTC1 // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)#pragma config PBADEN = ON // PORTB A/D Enable bit (PORTB<5:0> pins are configured as analog input channels on Reset)#pragma config CCP3MX = PORTB5 // P3A/CCP3 Mux bit (P3A/CCP3 input/output is multiplexed with RB5)#pragma config HFOFST = ON // HFINTOSC Fast Start-up (HFINTOSC output and ready status are not delayed by the oscillator stable status)#pragma config T3CMX = PORTC0 // Timer3 Clock input mux bit (T3CKI is on RC0)#pragma config P2BMX = PORTB5 // ECCP2 B output mux bit (P2B is on RB5)#pragma config MCLRE = EXTMCLR // MCLR Pin Enable bit (MCLR pin enabled, RE3 input pin disabled)// CONFIG4L#pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)#pragma config LVP = ON // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled if MCLRE is also 1)#pragma config XINST = OFF // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))// CONFIG5L#pragma config CP0 = OFF // Code Protection Block 0 (Block 0 (000800-003FFFh) not code-protected)#pragma config CP1 = OFF // Code Protection Block 1 (Block 1 (004000-007FFFh) not code-protected)#pragma config CP2 = OFF // Code Protection Block 2 (Block 2 (008000-00BFFFh) not code-protected)#pragma config CP3 = OFF // Code Protection Block 3 (Block 3 (00C000-00FFFFh) not code-protected)// CONFIG5H#pragma config CPB = OFF // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)#pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM not code-protected)// CONFIG6L#pragma config WRT0 = OFF // Write Protection Block 0 (Block 0 (000800-003FFFh) not write-protected)#pragma config WRT1 = OFF // Write Protection Block 1 (Block 1 (004000-007FFFh) not write-protected)#pragma config WRT2 = OFF // Write Protection Block 2 (Block 2 (008000-00BFFFh) not write-protected)#pragma config WRT3 = OFF // Write Protection Block 3 (Block 3 (00C000-00FFFFh) not write-protected)// CONFIG6H#pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)#pragma config WRTB = OFF // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)#pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not write-protected)// CONFIG7L#pragma config EBTR0 = OFF // Table Read Protection Block 0 (Block 0 (000800-003FFFh) not protected from table reads executed in other blocks)#pragma config EBTR1 = OFF // Table Read Protection Block 1 (Block 1 (004000-007FFFh) not protected from table reads executed in other blocks)#pragma config EBTR2 = OFF // Table Read Protection Block 2 (Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks)#pragma config EBTR3 = OFF // Table Read Protection Block 3 (Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks)// CONFIG7H#pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks)// #pragma config statements should precede project file includes.// Use project enums instead of #define for ON and OFF.#include <xc.h>typedef enum {fE_State0,fE_State1,fE_State2,fE_State3} firstEnum_t;typedef enum {sE_State0,sE_State1,sE_State2,sE_State3,sE_State4,sE_State5,sE_State6,sE_State7,} secondEnum_t;typedef struct {firstEnum_t a;secondEnum_t b;} structA;typedef struct {firstEnum_t a :2;unsigned padding :3;secondEnum_t b :3;} structB;void main() {structA structure;structure.a = sE_State7;structure.b = 11;while(1);}if anyone has a solution however, i'd like to hear it as well** OT: i don't know how i failed to get notifications in one month, but XC8 V2.0 is apparently C99 now.

author: dmills
author_rank: Super Contributor
timestamp: July 11, 2018, 07:22:57 pm
content: Got me wondering about some sort of funky combination of a macro, some #defines and the ## operator now, but bleh.Regards, Dan.

author: free_electron
author_rank: Super Contributor
timestamp: July 11, 2018, 08:17:21 pm
content: A couple of clarifications.:These are not real CPU registers. They are bytes stored in RAM that happen to contain bitfields. These bytes are overlaid in a union with an array so the array can easily be accessed using an I2C command parser. They are, in essence, the command and control registers of a virtual I2C device.I am making an I2 cslave. For example i want to emulate a PCA9555 16 bit io registerthat thing has 8 registers0x00 : 8 bits : input port 00x01 : 8 bits : input port 10x02 : 8 bits : output port 00x03 : 8 bits : output port 10x04 : 8 bits : polarity port 00x05 : 8 bits : polarity port 10x06 : 8 bits : direction 00x07 : 8 bits : direction 1i want to add an 8th register0x08 : interrupt [global_enable,nibble3,nibble2,nibble1,nibble0,mode1,mode0}the nibble bits enable sensing on nibbles in the io registers. nibble3 enables top nibble of input 1. nibble 2 enables lower nibble of input1so i want to do things like:interrupt_register.nibbles = nibble_3 | nibble_0;interrupt_register.mode = rising_only;since interrupt_register is part of a union it is also available as registersMy transport routines become very simply. On receive i dump data in registers[] and increment a pointer as long as no STOP is received.In the actual guts of the emulation code i can now use symbol names to access the stream= and i don't need to muck with bit-twiddling the registers[] array. likeCode:if (registers & 0x80) then // global interrupt is set// orregisters |= 0x80i can simply sayCode:if (interrupts.global_enable) then ....I want to make this bulletproof. not for someone else, but for myself. I can never remember all the bits and fields and their correct possible settings. i hate to dig through datasheets and look at header files. I want the code editor to show me the possible settings automatically. just like in visual studio.I spend sometimes hours finding a mistake in code that could simply be avoided if the damn language , ide or compiler would just be a bit smarter and tell me : this here , that's not the correct thing that fits in there ...And this testing should happen at compile time. not at runtime. none of the stuff is computation based. it is all bit masking and testing. something most CPU's can do in 1 or 2 instructions.I also don't want to write the typical crappy library code that needs to execute 20 instructions to set a bit.In short i want to find a way to define a memory map of bits and bytes where i can assign symbolic names that automatically resolve to possible settings.here is what i did now, but this does not foolproof the possible states.Code:#define GP_PINMODE_OUT_PWM 0x00#define GP_PINMODE_OUT_PUSHPULL 0x01#define GP_PINMODE_IN_ANALOG 0x02#define GP_PINMODE_IN_DIGITAL 0x03#define GP_INTMODE_DISABLED 0x00#define GP_INTMODE_POSEDGE 0x01#define GP_INTMODE_NEGEDGE 0x02#define GP_INTMODE_BOTHEDGE 0x03typedef struct {unsigned int GP_PINMODE :2; // Pinmode : see GPPINMODEunsigned int GP_PUL :1; // Pull-up enable = 1=enables, 0= disabledunsigned int GP_INTMODE :2; // Interrupt : see GPINTMODEunsigned int DUMMY :3; // Dummy bits, not used} GPxCONF;typedef union _memory_map_ {struct {volatile char POINTER;volatile GPxCONF GP1CONF;volatile GPxCONF GP2CONF;volatile GPxCONF GP3CONF;} REGISTER;volatile char ARRAY;} m_map;volatile m_map REGISTERS;void main(void){REGISTERS.ARRAY = 0x55; // this touches GP2CONFREGISTERS.REGISTER.GP2CONF.GP_INTMODE = GP_INTMODE_POSEDGE;}the autocomplete correctly suggests me the field and subfield names. so happy until there.i just want it to suggest me GP_INTMODE_POSEDGE, and the other 3.who knows how ?

author: JPortici
author_rank: Super Contributor
timestamp: July 11, 2018, 08:59:01 pm
content: I wonder if one could write a netbeans plugin to do this..like parse the line up to the cursor, if it's right after a "case" or a "=" check variable name,type and suggest appropriate defines or enums?

author: cv007
author_rank: Frequent Contributor
timestamp: July 11, 2018, 10:06:35 pm
content: QuoteI think it will also show comments for the field, so when you type the period you see the field options and when you highlight the option you will see the comment for the option. Not perfect, but if you comment each struct field, you will see the available options. (I'm assuming MPLABX)

author: free_electron
author_rank: Super Contributor
timestamp: July 12, 2018, 12:52:41 am
content: yup, MPLABxi am one step further now.It now correctly suggests me the options.

author: free_electron
author_rank: Super Contributor
timestamp: July 12, 2018, 01:02:32 am
content: except it doesnt compile. AAARGH. I HATE C(rap) language !

author: obiwanjacobi
author_rank: Super Contributor
timestamp: July 12, 2018, 07:13:46 am
content: I haven't done C in ages.I think this might be C++ but perhaps it will inspire...https://github.com/obiwanjacobi/atl/blob/master/Source/Code/ArduinoTemplateLibrary/EnumScope.h

author: ajb
author_rank: Super Contributor
timestamp: July 12, 2018, 06:15:39 pm
content: You could get the same syntax and autocompletion by creating a const struct that holds the allowable values (but you still won't get compile-time checking, because it's still C):Code:typedef struct {struct {uint8_t OUT_PWM;uint8_t OUT_PUSHPULL;uint8_t IN_ANALOG;uint8_t IN_DIGITAL;} GPxPINMODES;struct {uint8_t DISABLED;uint8_t POSEDGE;} GP_INTMODES;} GPxCONF_t;typedef struct {uint8_t PINMODE : 2;uint8_t PUL: 1;uint8_t INTMODE : 2;uint8_t DUMMY: 3;} GPxCONF_reg;static const GPxCONF_t GPxCONF = {// these could be bitfields as well if you want to save a few bytes.GPxPINMODES = {.OUT_PWM = 0,.OUT_PUSHPULL = 1,.IN_ANALOG = 2,.IN_DIGITAL = 3,},.GP_INTMODES = {.DISABLED = 0,.POSEDGE = 1,},};void init(void){GPxCONF_reg mem_map_reg;mem_map_reg.INTMODE = GPxCONF.GP_INTMODES.DISABLED;}You won't be able to use a tagged initializer in vanilla C89, and the value struct should be decorated as necessary to keep it out of RAM if that's important to you. I'm not sure how well it will optimize in MPLABX, and in the end it's not any different from using defines or regular enums, just a different presentation style.Quote from: obiwanjacobi on July 12, 2018, 07:13:46 amYes, that's C++.

author: free_electron
author_rank: Super Contributor
timestamp: July 12, 2018, 07:11:29 pm
content: i have tried the same using an enumerated list and that failed. i will try your approach.i need to check what that compiles to though ... i don't want to waste RAM or ROM ( my CPU has 1.5K of ROM and 64 bytes of ram .... )if i perform a bit test i expect the compiler to use BSN <location> <bit> (bit test and skip next if set)so the check becomes :: subroutine : handle pullup setBSN <GP1CONF> <GP_PUL> , check gp_pul bit in gp1conf.RET // return if not setblabla . handling codeRET // doneCode optimisation is very important .

author: free_electron
author_rank: Super Contributor
timestamp: July 14, 2018, 04:39:07 am
content: Right. I figured some things out and got it to work.The guiding principles behind this experiment- no RAM usage- smallest code- predicatable execution. no wiggle room for the optimizers.- no libraries that call call call.- symbolic names- everything hardcoded. no mathematical acrobatics at runtime. compile time is fine- no bleeping pointers or pointer arithmetic.- no room for user to things like writing to nonvolatile or loading data of wrong type in system.- things like switch case , == and != need to work correctlyThink about a register that consists of 8 flipflops.in an FPGA i can write REGISTER[7:6] = 2'b01 and this will write those two flipflops in one clocktick.The equivalent code in a CPUbecomesMOV B Register // get contents of registerAND B 0b0011_1111 // msk off 2 top bitsMOV A 0b0000_0001 // load 01 in accumulatorSHL A,6 // shift accumaltor left 6 positionsOR A,B or b and accumulatorMOV Register A // send it back into 'REGISTER'this is sheer insanity. just to twiddle some bits ... oodles of code, moves , or register usages.The functions i need are- test a bit for true- test a bit for false- set a bit- clear a bit- toggle a bit- check a subfield for a value from a predefined list , kind of like an enumerated list , allowing usage of standard flow control like 'switch' , if == / != operators- set a subfield to a value of a predefined list.I want to approach this as close as possible to what is happening in an FPGA- compiler should be able to resolve most arithmetic at compile time as everything is constant based.- shortest possible code.- no ram usage- inline compilation so no calls, push pop and other overhead- interrupt safe. no risk of context switch loss. no usage of stack to preserve intermediates.Why all of this ? well , to create 'virtual devices' in a microcontroller.Think of a peripheral like an I2C or SPI chip.Those things are essentially number of registers with data in them. some registers pack multiple fields of varying size.To code these kind of things it is nice to have these registers as a continuous block of memory so you can access it using a read and write pointer from the transport handler routines.The incoming commands tell you what byte to write and what the byte is. nothing more , nothing less. short and good.To code the logic that forms the devices it would be nice to have symbolic access to the individual chunks without every time having to write all the bit-twiddling code.consider a virtual device with a memory map like this :0x00 : CONFIG ,-,-,-,-,,0x01 : TEMPERATURE [d7..d0]0x02 : GPCONF1 -,-,-,INTMODE1,INTMODE0,PULLUP_ENABLE,PINMODE0,PINMODE10x03 : GPCONF2 -,-,-,INTMODE1,INTMODE0,PULLUP_ENABLE,PINMODE0,PINMODE10x04 : GPDATA1 8 bits0x05 : GPDATA2 8 bitsthe config bits are flags. If a flag is SET then that device will generate an interrupt_on_changeFor example :if GP2INT is set , and the pinmode is input , and intpmode it is set to rising edge then an interuupt will be generated if that pin goes from low to high.possible modes for INMODE- none- rising- falling- bothpinmode :digital input -> state is stored in GPDATAx as 0x00 or 0xFFanalog input -> adc result is stored in GPDATAx as a number between 0x00 and 0xffdigital output -> pin is follows bit0 of GPDATAxPWM output -> pin has duty cycle set by GPDATAxall in all something that is easily cobbled up in an FPGA , but in a microcontroller becomes spaghetticode unless you have symbolic access to the 'flipflops'SO, here we go:First, lets make our registerbank so the transport routines can read and write based on a pointer. The simplest construction : an array of unsigned chars.Code:unsigned char REGISTERSETSo far so good. We have allocated all the ram we ever need.Let's create some symbolic names so we can easily access these things from the logic side.Code:unsigned char REGISTERSET;#define CONFIG REGISTERSET#define TEMPERATURE REGISTERSET#define GPCONF1 REGISTERSET#define GPCONF2 REGISTERSET#define GPDATA1 REGISTERSET#define GPDATA2 REGISTERSETlet's set up the interrupt flag mapping.Code:#define INTENABLE_MASK 0b10000000 // bit 7 of CONFIG register#define GP1INT_MASK 0b00000001 // bit 0 of CONFIG register#define GP2INT_MASK 0b00000010 // bit 1 of CONFIG register#define ADCINT_MASK 0b00000100 // bit 2 of CONFIG registerand the mapping for the INTmode and PINMODE of the GPCONFx registers.Code:#define PINMODE_MASK 0b000000011#define PULLUP_MASK 0b000000100#define INTMODE_MASK 0b000011000Next let's define the possible states INTMODE and PINMODE and pullupCode:#define PINMODE_IN_ANALOG 0b00000000#define PINMODE_IN_DIGITAL 0b00000001#define PINMODE_OUT_PUSHPULL 0b00000010#define PINMODE_OUT_PWM 0b00000011#define PINMODE_PULLUP 0b00000100#define INTMODE_NONE 0b00000000#define INTMODE_RISING 0b00001000#define INTMODE_FALLING 0b00010000#define INTMODE_BOTH 0b00011000Note that the above are NOT masks, they are the settings of possible fields, aligned to their mask. This is done intentionally as i don't want any risk of the compiler injecting unnecessary >> and << operations. the fields are pre-aligned.now we need some code to 'extract' the individual fields.Code:// reading#define GPCONF1_PINMODE (GPCONF1 & PINMODE_MASK)#define GPCONF1_INTMODE (GPCONF1 & INTMODE_MASK)#define GPCONF2_PINMODE (GPCONF2 & PINMODE_MASK)#define GPCONF2_INTMODE (GPCONF2 & INTMODE_MASK)// writing#define GPCONF1_INTMODE_SET (GPCONF1 & ~INTMODE_MASK)|=#define GPCONF2_INTMODE_SET (GPCONF2 & ~INTMODE_MASK)|=#define GPCONF1_PINMODE_SET (GPCONF1 & ~INTMODE_MASK)|=#define GPCONF2_PINMODE_SET (GPCONF2 & ~INTMODE_MASK)|=and some for the individual bitsCode:#define GP1INT_SET FLAGS |= GP1INT_MASK#define GP1INT_ISSET (FLAGS & GP1INT_MASK)!=0#define GP1INT_CLEAR FLAGS &= ~GP1INT_MASK#define GP1INT_ISCLEAR (FLAGS & GP1INT_MASK)==0#define GP1INT_TOGGLE FLAGS ^= GP1INT_MASK#define GP2INT_SET FLAGS |= GP1INT_MASK#define GP2INT_ISSET (FLAGS & GP1INT_MASK)!=0#define GP2INT_CLEAR FLAGS &= ~GP1INT_MASK#define GP2INT_ISCLEAR (FLAGS & GP1INT_MASK)==0#define GP2INT_TOGGLE FLAGS ^= GP2INT_MASK#define ADCINT_SET FLAGS |= GP1INT_MASK#define ADCINT_ISSET (FLAGS & GP1INT_MASK)!=0#define ADCINT_CLEAR FLAGS &= ~GP1INT_MASK#define ADCINT_ISCLEAR (FLAGS & GP1INT_MASK)==0#define ADCINT_TOGGLE FLAGS ^= GP2INT_MASK#define INTENABLE_SET FLAGS |= GP1INT_MASK#define INTENABLE_ISSET (FLAGS & GP1INT_MASK)!=0#define INTENABLE_CLEAR FLAGS &= ~GP1INT_MASK#define INTENABLE_ISCLEAR (FLAGS & GP1INT_MASK)==0#define INTENABLE_TOGGLE FLAGS ^= GP2INT_MASKThis seems like a lot of work to build, but it is easily creatable using a script that reads a definition table.using a simply notation likeCode:// MEMMAPPER// REG 0, CONFIG  INTENABLE, [6:3] - ,  ADC_INT ,  GP2INT ,  GP1INT// REG 2, GPCONF1[7:5] - , [4:3] INTMODE , , PULLUP , [1:0] PINMODE// REG 3, GPCONF2[7:5] - , [4:3] INTMODE , , PULLUP , [1:0] PINMODE// MODE INTMODE {NONE:00, RISING:01,FALLING:10,BOTH:11}// MODE PINMODE {IN_ANALOG:00, IN_DIGITAL:01,OUT_PUSHPULL:10, OUT_PWM:11}// TARGET#include "registermap.inc"i can have a simple script that scans my source files for the magic keyword 'MEMMAPPER' then parses the comment lines until it hits the keyword TARGET , and spits the data into the filename given in the include.all the #define stuff is automatically generated from a simple notation syntax.But why you ask. Well . becasue now i can write the logic for this virtual devices using symbolic names and not have to bother if i got the mapping of the fileds right, the correct shift offsets , i dont need ot dig in header files to see what is what as autocomplete will suggest me possible settings.An example :Code:GP1INT_SET; // set the GP1INT flag trueGP1INT_CLEAR; // clear the GP1INT flagif (GP1INT_ISCLEAR) { // if GP1int is cleargo_do_this;}if (GP1INT_ISSET) { // if setgo_do_that;}switch (GPCONF1_PINMODE){case PINMODE_IN_ANALOG : GPDATA1= ADCread; break;case PINMODE_IN_DIGITAL : GPDATA1 = porta.1; break;case PINMODE_OUT_PUSHPULL : PORTa.mode = pushpull; PORTa.1 = GPDATA1; break;case PINMODE_OUT_PWM : PORTa.mode = MODE_pmwy; PORTaPWMvalue = GPDATA1; break;};....and so onThis the process of coding the control logic very simple. If in the future the memory map changes ( i allocate the individual fields and registers differently ) my logic still keeps working correctly !