These PICs do exactly what the datasheet says they do. If there is a problem, its with your code or your (lack of) reading the datasheet.
The important thing to keep in mind is that bit operations on a port register read the port, do the bit operation, then write the whole port back out. Note that reading the port register reads the instantaneous state of the pins, NOT necessarily what was last written to those pins. That can differ from what was last written in two cases:
- The TRIS bit is set so that the pin is in high impedance state. The actual pin value is therefore up to the external circuit. If you permanently intend the pin to be a input in this application, then this case doesn't matter. The value written to the port register bit is irrelevant to a pure input pin.
- The external circuit is holding the pin in the opposite state it was last set to. This could be due to a hardware bug, but also just due to slew rate limitations. The pin may not have had enough time to get to its new state before your code reads it again.
It's usually case 2 that people mess up. Here is a classic example of such a screwup:
; All Port B pins were previously configured as outputs.
clrf portb ;drive all Port B pins low
... ;many cycles pass
bsf portb, 0 ;RB0 starts going high
bsf portb, 1 ;Oops! RB0 not high yet, so gets set low
The simple fix is to leave enough time between the two last BSF instructions so that RB0 is solidly at its new state by the time you try to set RB1. Usually only a single instruction is enough.
Due to the timing of individual actions within each instruction, the example above gives RB0 only 1 Q-cycle, if I remember right, between it getting driven to its new state and PortB being read again. That's only ¼ of a instruction cycle. Even a single other instruction between the two BSF provides 5x more time for RB0 to get to its new state. That's usually enough.
However, don't go by rules of thumb. Do the math. The datasheet tells you the minimum current the PIC will source on attempt to drive RB0 high. You know what your external circuit draws, and the maximum capacitance on the line.
You will see a lot of bad advice out there to always use shadow registers. That makes the code more clunky, adds more chance of error, and is unnecessary in most cases. The real solution is to actually understand what is happening, then make sure the mechanisms that would cause trouble are not triggered. I have on very rare occasions used a shadow register for a port on a old PIC 12 or 16, but probably only once or twice.
Note that all newer PIC families from the original PIC 16 have LAT registers. These completely get around this problem. The LAT register retains the last value written to a port. The port register still exists, and as before, it contains the instantaneous value of the port pins on a read. So instead of doing things like BSF on the port register (BSF PORTB,1) you do it on the LAT register (BSF LATB,1).
All PIC 18, 24, 30, 33 and the new enhanced PIC 16 have LAT registers. You really only run into the read-modify-write problem on the 12 bit and old 14 bit cores.