A procedure called "Read-Modify-Write".
What it involves is entirely in the name. You read. Then you modify. Then you write.
//Read in the value of the output register
tempVariable = [output register]
//set all bits you want to modify to be 0.
tempVariable &= [some mask];
//or in the values of the bits with those bits you want unchanged set to 0
tempVariable |= [new value of bits];
//Write the new value back to the output register
[output register] = tempVariable;
The key is basically to end up with the values of bits you want to be unchanged to be written back to the output register along with the new values of bits you do want to change.
To determine what the output register is for your device, you should refer to its datasheet.
We can't just write to the register directly because it will affect the bits we don't want to change as well. So we need a sequence of operations that will change only the bits we want to. This is where bitwise operators come in.
There are several bitwise operators, but the important two are
& (and) and
| (or). Bitwise and anything with a 0 and it sets that bit to be 0, bitwise and anything with 1 and it stays the same. Bitwise or anything with a 1 and it sets that bit to be a 1, bitwise or anything with 0 and it stays the same. These two operators allow us to make the required changes because we now have a way to set just some bits to 0 and a way to set just some bits to 1.
The new value you want to write will require some bits setting to 0 and some bits setting to 1. We can achieve this by doing a bitwise and followed by a bitwise or. The and is used to set all bits we want to change to be 0 to allow us to then do the or which sets only the bits we want to be 1 to be 1.
An example will help. Say you want to modify the lower 5 bits to a value of
0b01011 but leave the upper 3 bits unchanged. Lets also say the current value is
0b10111101. So we follow the procedure:
Step 1, mask:
Current: 0b101 11101
Bitmask: 0b111 00000 <- remember a 1 means don't change, a 0 means clear.
Result : 0b101 00000
Step 2, modify:
Masked : 0b101 00000
New Val: 0b000 01011 <- remember a 1 means set to 1, a 0 means unchanged
Result : 0b101 01011
And there you have it - notice the upper 3 bits were unchanged in both operations, whilst the lower bits were updated to match the new value.
To bring up a point mentioned in the comments and the other answer, that this should indeed be done on the output register which was the original intention of my answer. It seems there is some confusion in assuming that by port I was referring to the PORTx registers in PICs - in fact the output register on some devices is the LATx register. Some PICs don't have a LATx register. On AVRs for example PORTx is the output register. The datasheet for your device will tell you what the output register is.
Furthermore, the technique can be used to modify variables as well as registers, and can be used when modifying the registers for things other than just I/O ports - you can modify things like control registers for serial peripherals and such as well.
Because of the differences in naming of registers and the fact that the process is a very universal approach, in the above I had tried to be generic as the same thing applies to not just PICs but any microcontroller - in fact pretty much anything that requires some bits of a register to be modified but not others.