Allora, funziona così.
Codice:
uint8_t reg = 0;
uint8_t bitValue = 1;
uint8_t nBit = 4;
reg ^= (-bitValue ^ reg) & (1 << nBit);
viene fatto il complemento a 2 di bitValue. Essendo bitValue un byte che dovrebbe assumere, in quella formula, solo 0 o 1, con 1 il suo complemento diventa 0b11111111 e con 0 diventa 0b00000000. Facendo lo XOR con il valore di reg in pratica si usa bitValue come una maschera per ottenere l'opposto del valore dei bit di reg. Ricordo che lo XOR restituisce 1 solo se uno dei dur bit in causa è 1, altrimenti restituisce 0. Fatto ciò, fa un AND logico con nBit, una maschera che rappresenta il bit da modificare.
Facciamo un esempio.
reg=45 --> che in binario è 00101101
nBit = 6 --> bit che vogliamo modificare (il 7°)
bitValue = 1 --> valore che vogliamo assegnare
il risultato è 109.
Vediamo come ci arrivo.
-bitValue restituisce 0b11111111
0b11111111 XOR 00101101 restituisce 11010010
11010010 AND 010000000 restituisce 01000000 perché il bit è già a 1.
A questo punto lo XOR alla fine riassegna a reg il valore finale perché un altro XOR riporta i bit alterati al loro valore iniziale TRANNE per il bit che è stato modificato dalla funzione nel caso abbia cambiato valore.
Difatti facciamo l'esempio di cambio di stato.
reg=45 --> che in binario è 00101101
nBit = 5 --> bit che vogliamo modificare (il 6°)
bitValue = 0 --> valore che vogliamo assegnare
Come sopra, ma questa volta pongo il bit a 0.
reg ^= (-bitValue ^ reg) & (1 << nBit
-bitValue restituisce 00000000
00000000 XOR 00101101 restituisce 00101101
00101101 AND 00100000 restituisce 00100000
00101101 XOR 00100000 restituisce 00001101
00001101 è 13 in decimale