I discovered that there is a new method on GCB (#define Porta.0 volatile) to eliminate the problem, but it did not work correctly. The problem was especially presented to me on the piloting of Gate Mos 2N7000 or the like.
Everything works well as if command is: porta.0= ! porta.0
But it has no effect if checked too ASM:
do
porta.0=0
porta.0=1
loop
To avoid the RMW problem I do as recommended by microchip with the use of a variable but a dedicated command would be convenient, or maybe I'm wrong something with GCB.
Thank you
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I can look into this. We should fix. Please give me the exact code that works, and the code that fails. I just need the shortest code. But, at the moment I do not know the chip or enough to look at this.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This example uses 12f675 but I saw that the same is obtained on other pic 16f819 etc. Practically on all the PICs who do not have the LAT register.
The modification of the door normally takes place without preventing RMW.
In Mikrobasic the volatile word is also used during the variable declaration.
A variable whose value can change asynchronously, out of the direct control of the flow of the main program. The compiler will not make optimizations on it, precisely because it must consider that the value could be altered by:
Interruption routine (ISR)
Direct accesses from hardware peripherals
Asynchronous operations from other threads or processes (if present)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I didn't know about the volatile declaration I discovered it looking at the example code. In Help says it is used to avoid glitches on the doors, a problem I had long ago on an automation of a gate that used several Mos for piloting. I used a variable to change the door and worked well:
dim LATA as byte
LATA=0
do
LATA.0=1:PORTA=LATA
LATA.0=0:PORTA=LATA
loop
On recent Pic + there is no problem as an "lat" register that avoids these problems.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
When interrupts are disabled, the compiler outputs straightforward bit instructions with direct RMW on the GPIO register:
;doSysDoLoop_S1;gpio.1=1;Handler for Read-Modify-Write problembankselGPIOmovfGPIO,WmovwfSYSPICRWMCACHEbsfSYSPICRWMCACHE,1movfSYSPICRWMCACHE,WmovwfGPIO;gpio.2=0;Handler for Read-Modify-Write problemmovwfSYSPICRWMCACHEbcfSYSPICRWMCACHE,2movfSYSPICRWMCACHE,WmovwfGPIO;loopgotoSysDoLoop_S1
🔍 Problem: These RMW operations are vulnerable. If an interrupt modifies GPIO during or between bsf/bcf instructions, bit state loss may occur.
🧵 Resolving GPIO RMW Hazards with Shadow Register Workaround
#option shadowregister GPIO
📌 Context
In traditional PIC microcontroller setups, especially those lacking hardware LAT registers, modifying a GPIO pin within a Read-Modify-Write (RMW) sequence can introduce unexpected behavior when pin states are physically influenced by external loads. If a pin like GPIO.1 is overloaded and pulled low, reading the port during software staging can yield false states, leading to unintended outcomes when writing back.
💡 Hugh's Insight
Hugh shared, via a private email, an excellent observation about how even methodical caching into SYSPICRWMCACHE (proposed in previous post) still doesn’t prevent read corruption if the pin is pulled below logic threshold. This vulnerability persists during operations like:
movfGPIO,W; reads corrupted pin statemovwfGPIO; rewrites incorrect data back
To address this, Hugh proposed a manual shadow register emulating LAT behavior:
bcfLATA,n; clear software latch bitbsfLATA,n; set latch bit (if needed)movfLATA,W; load the full latch statemovwfGPIO; apply safely to hardware port
⚠️ This still requires interrupt protection between the movf and movwf steps to ensure atomicity.
🔧 Related: TRIS and OPTION_REG on 12-bit PICs
Hugh also cited GCBASIC’s approach for 12-bit instruction PICs lacking direct bit-level access to certain registers. GCBASIC introduces shadow variables for registers like TRIS and OPTION_REG, allowing controlled manipulation:
Set or clear bits in the variable copy.
Write back using W register:
movfshadowTRIS,Wtris
This technique mirrors the LAT workaround, substituting hardware latching with software staging.
🛠️ Alternate Recommendations
Hugh pointed to two mitigation paths:
Option
Description
12F1840 or similar
Has proper LAT registers eliminating RMW issues
Load reduction
Ensure GPIO switching completes in <1 cycle to avoid logic corruption
✅ Next Steps
Remove the currently implemented RWM code.
Implement the Shadow Register method as proposed by Hugh to ensure state integrity and eliminate RMW pitfalls.
This solution will be exposed as: #option shadowregister GPIO
We will resolve.
❤️
1
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
You have given a nice description of the problem, I think the solution is as written in the last posts as any reading of the door can cause errors.
It is nice that the compiler will automatically solve this problem, great GCB.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Well, when I finished I take tests on my gate home that gave me many problems and I solved with lat variable.
To test well you have to do it in the real world with the load on the doors (especially capacitive).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have completed the new capability for GCBASIC that generates read-modify-write (RMW)-safe assembly for PIC microcontrollers—especially useful on chips like the PIC12F675 and may more, which lack LATx registers and are prone to subtle latch corruption when using high-level pin commands.
This capability ensures that output operations never corrupt input latches, preserving predictable behaviour and making the generated code interrupt-safe when the user controls the interrupts.
To use is very simple. Add a compiler directive and the RWM ASM is generated.
This is available from build 1490 and onwards.
🔧 What Problem Does It Solve?
The example program is a good example of the RWM problem.
On chips without LATx, instructions like GPIO.2 = 1 compile to:
bsfGPIO,2
This triggers a read-modify-write on the GPIO register. If any pins are configured as inputs, their bits reflect external voltage levels during the read—so writing back the modified byte can unintentionally clear output latches for those input pins.
This can cause:
Unexpected output levels when switching pin direction
Corrupted latch states during interrupts
Hard-to-trace bugs in mixed input/output setups
✅ What the new capability does
Instead of relying on the register GPIO, the new capability uses a RAM-based shadow latch (e.g. LATA) to track intended output states. All pin changes update this shadow, and writes to GPIO are done using full-byte transfers from LATA.
Example Output (RMW-Safe)
; Set GP2 high without affecting GP0;GP2 = 1bsfLATA,GP2; Update shadow latchmovfLATA,WmovwfGPIO; Safe write to port
This avoids reading GPIO entirely and ensures that input pins (like GP0) do not influence the output latch state.
🧠 Visualizing the RWM Hazard
Pin
Direction
External Level
GPIO Read
Latch Impact
GP0
Input
0 V
0
Cleared
GP2
Output
—
0
Set to 1
If you read GPIO and write back with GP2 set, GP0’s 0 gets written too—even though it’s just an input. That’s the trap.
📦 Adapter Features
✅ RMW-safe output for all pin operations
✅ Shadow latch tracking via LATA RAM variable
✅ Compatible with GCBASIC macros and inline ASM
📚 How to Use
#optionshadowregistergpio
The current implementation supports gpio, porta, portb.
The new AdaptRMWIssues function is a sub routine within the compiler designed to process assembly code lines generated by GCBASIC for Microchip PIC microcontrollers (e.g., PIC16F819). It adapts instructions that operate on I/O ports (GPIO, PORTA, PORTB) to use software cache registers (LATA, LATB) in RAM, mitigating Read-Modify-Write (RMW) issues and ensuring reliable reads of output port states.
This is critical for this type of PIC which lacks hardware latch registers, as direct port operations can lead to bit corruption or unreliable state reads due to external loads or pin transitions.
rmwAsmLine (String): The assembly code line to process (e.g., movf PORTA, W or bcf PORTB, 1).
rmwLineNumber (String): The line number of the assembly code, used for debugging or logging (not utilized in the function logic).
Return Value
String: The adapted assembly code line(s) with operations redirected to use LATA or LATB, or an empty string ("") if no adaptation is needed or the input is invalid.
Purpose
The function ensures RMW-safe and reliable port operations by:
Replacing RMW-prone instructions (bcf, bsf, incf, etc.) targeting GPIO, PORTA, or PORTB with equivalent operations on LATA or LATB, followed by copying to the port.
Replacing read instructions (movf PORTx, W) with movf LATx, W to use the intended output state.
Replacing write instructions (movwf PORTx, clrf PORTx) with operations on LATA or LATB, followed by copying to the port.
Replacing bit-test instructions (btfsc, btfss) on PORTx with LATA or LATB for reliable state checks.
This is essential for these PICs, where LATA and LATB are user-defined RAM variables acting as software caches to track intended port output states.
Supported Instructions
The function processes the following instructions when they target GPIO, PORTA, or PORTB:
I discovered that there is a new method on GCB (#define Porta.0 volatile) to eliminate the problem, but it did not work correctly. The problem was especially presented to me on the piloting of Gate Mos 2N7000 or the like.
Everything works well as if command is: porta.0= ! porta.0
But it has no effect if checked too ASM:
do
porta.0=0
porta.0=1
loop
To avoid the RMW problem I do as recommended by microchip with the use of a variable but a dedicated command would be convenient, or maybe I'm wrong something with GCB.
Thank you
I can look into this. We should fix. Please give me the exact code that works, and the code that fails. I just need the shortest code. But, at the moment I do not know the chip or enough to look at this.
This example uses 12f675 but I saw that the same is obtained on other pic 16f819 etc. Practically on all the PICs who do not have the LAT register.
The modification of the door normally takes place without preventing RMW.
In Mikrobasic the volatile word is also used during the variable declaration.
A variable whose value can change asynchronously, out of the direct control of the flow of the main program. The compiler will not make optimizations on it, precisely because it must consider that the value could be altered by:
Interruption routine (ISR)
Direct accesses from hardware peripherals
Asynchronous operations from other threads or processes (if present)
I didn't know about the volatile declaration I discovered it looking at the example code. In Help says it is used to avoid glitches on the doors, a problem I had long ago on an automation of a gate that used several Mos for piloting. I used a variable to change the door and worked well:
dim LATA as byte
LATA=0
do
LATA.0=1:PORTA=LATA
LATA.0=0:PORTA=LATA
loop
On recent Pic + there is no problem as an "lat" register that avoids these problems.
I have looked into this, and not yet resolved but I am starting to understand the issue in the compiler.
Does this resolve? When you use a BIT is set the state?
If this works then this tells me the part of the compiler that I need to look into deeper.
I have update the compiler. It is a specific change for this issue. It will need testing.
When #option volatile is used with PIC ( with no LAT ) the ASM will read the register, cache the register, change the cache, write the cache.
I will post a link to the new compiler if you can test and provide feedback.
how would that work if ports are also written to inside an interrupt?
@Jerry. What a great question. I had overlooked but this is now handled. A better explanation.
Does this look OK? Could I just toggle GIE and not use SYSINTSTATESAVE0 ?
Evan
Read later post, as this has been removed from the compiler.
🧵 Resolving RMW Issues in GPIO Control with GCBASIC on PIC12F675
GCBASIC handles GPIO bit manipulation on the PIC12F675 with care—especially when RWM issues can occur, then the added complexity of interrupts.
The classic Read-Modify-Write (RMW) problem can corrupt GPIO state if an interrupt fires mid-operation and touches the same register.
📘 1. Source GCBASIC Code
Here is the minimal control loop setting
gpio.1 = 1
andgpio.2 = 0
:⚙️ 2. GCBASIC-Generated ASM (Without Interrupt Handling)
When interrupts are disabled, the compiler outputs straightforward bit instructions with direct RMW on the
GPIO
register:🔍 Problem: These RMW operations are vulnerable. If an interrupt modifies
GPIO
during or betweenbsf
/bcf
instructions, bit state loss may occur.🛡️ 3. GCBASIC-Generated ASM (With Interrupt Protection)
With interrupt support enabled, GCBASIC wraps RMW operations to preserve atomicity using:
SYSINTSTATESAVE0
: stores interrupt enable stateSYSPICRWMCACHE
: staging register for safe bit operations✅ Benefit: Ensures no interrupt corrupts the GPIO state mid-operation—crucial for reliable output in multi-context systems.
🧮 Summary
Last edit: Anobium 2025-08-05
🧵 Resolving GPIO RMW Hazards with Shadow Register Workaround
#option shadowregister GPIO
📌 Context
In traditional PIC microcontroller setups, especially those lacking hardware LAT registers, modifying a GPIO pin within a
Read-Modify-Write
(RMW) sequence can introduce unexpected behavior when pin states are physically influenced by external loads. If a pin like GPIO.1 is overloaded and pulled low, reading the port during software staging can yield false states, leading to unintended outcomes when writing back.💡 Hugh's Insight
Hugh shared, via a private email, an excellent observation about how even methodical caching into
SYSPICRWMCACHE
(proposed in previous post) still doesn’t prevent read corruption if the pin is pulled below logic threshold. This vulnerability persists during operations like:To address this, Hugh proposed a manual shadow register emulating LAT behavior:
⚠️ This still requires interrupt protection between the
movf
andmovwf
steps to ensure atomicity.🔧 Related: TRIS and OPTION_REG on 12-bit PICs
Hugh also cited GCBASIC’s approach for 12-bit instruction PICs lacking direct bit-level access to certain registers. GCBASIC introduces shadow variables for registers like
TRIS
andOPTION_REG
, allowing controlled manipulation:This technique mirrors the LAT workaround, substituting hardware latching with software staging.
🛠️ Alternate Recommendations
Hugh pointed to two mitigation paths:
✅ Next Steps
#option shadowregister GPIO
We will resolve.
You have given a nice description of the problem, I think the solution is as written in the last posts as any reading of the door can cause errors.
It is nice that the compiler will automatically solve this problem, great GCB.
I will update this thread when I have made the changes. :-)
Well, when I finished I take tests on my gate home that gave me many problems and I solved with lat variable.
To test well you have to do it in the real world with the load on the doors (especially capacitive).
🛠️ New GCBASIC Capability: RMW-Compliant ASM Output creates Safer PIC Code
🚀 Overview
I have completed the new capability for GCBASIC that generates read-modify-write (RMW)-safe assembly for PIC microcontrollers—especially useful on chips like the PIC12F675 and may more, which lack
LATx
registers and are prone to subtle latch corruption when using high-level pin commands.This capability ensures that output operations never corrupt input latches, preserving predictable behaviour and making the generated code interrupt-safe when the user controls the interrupts.
To use is very simple. Add a compiler directive and the RWM ASM is generated.
This is available from build 1490 and onwards.
🔧 What Problem Does It Solve?
The example program is a good example of the RWM problem.
On chips without
LATx
, instructions likeGPIO.2 = 1
compile to:This triggers a read-modify-write on the
GPIO
register. If any pins are configured as inputs, their bits reflect external voltage levels during the read—so writing back the modified byte can unintentionally clear output latches for those input pins.This can cause:
✅ What the new capability does
Instead of relying on the register
GPIO
, the new capability uses a RAM-based shadow latch (e.g.LATA
) to track intended output states. All pin changes update this shadow, and writes toGPIO
are done using full-byte transfers fromLATA
.Example Output (RMW-Safe)
This avoids reading
GPIO
entirely and ensures that input pins (like GP0) do not influence the output latch state.🧠 Visualizing the RWM Hazard
0
0
1
If you read
GPIO
and write back with GP2 set, GP0’s0
gets written too—even though it’s just an input. That’s the trap.📦 Adapter Features
LATA
RAM variable📚 How to Use
The current implementation supports
gpio
,porta
,portb
.🧪 YouTube
I posted a video with a lot more detail, see https://youtu.be/t6PvKRTl8ls
AdaptRMWIssues Function Documentation
Overview
The new
AdaptRMWIssues
function is a sub routine within the compiler designed to process assembly code lines generated by GCBASIC for Microchip PIC microcontrollers (e.g., PIC16F819). It adapts instructions that operate on I/O ports (GPIO
,PORTA
,PORTB
) to use software cache registers (LATA
,LATB
) in RAM, mitigating Read-Modify-Write (RMW) issues and ensuring reliable reads of output port states.This is critical for this type of PIC which lacks hardware latch registers, as direct port operations can lead to bit corruption or unreliable state reads due to external loads or pin transitions.
Function Signature
Parameters
rmwAsmLine
(String): The assembly code line to process (e.g.,movf PORTA, W
orbcf PORTB, 1
).rmwLineNumber
(String): The line number of the assembly code, used for debugging or logging (not utilized in the function logic).Return Value
LATA
orLATB
, or an empty string (""
) if no adaptation is needed or the input is invalid.Purpose
The function ensures RMW-safe and reliable port operations by:
bcf
,bsf
,incf
, etc.) targetingGPIO
,PORTA
, orPORTB
with equivalent operations onLATA
orLATB
, followed by copying to the port.movf PORTx, W
) withmovf LATx, W
to use the intended output state.movwf PORTx
,clrf PORTx
) with operations onLATA
orLATB
, followed by copying to the port.btfsc
,btfss
) onPORTx
withLATA
orLATB
for reliable state checks.This is essential for these PICs, where
LATA
andLATB
are user-defined RAM variables acting as software caches to track intended port output states.Supported Instructions
The function processes the following instructions when they target
GPIO
,PORTA
, orPORTB
:bcf
,bsf
,incf
,decf
,comf
,rlf
,iorwf
,andwf
,xorwf
,rrf
movf
(when destination isW
)movwf
,clrf
btfsc
,btfss
Logic Flow
1. Input Validation
rmwAsmLine
is empty or starts with a comment (;
).movf PORTA, W ; comment
→movf PORTA, W
).2. Tokenization
movf PORTA, W
→["movf", "PORTA", "W"]
).MOVF
→movf
).3. Instruction Check
targetInstructions
(bcf
,bsf
, ...,movf
,movwf
,clrf
) ortargetBitTestInstructions
(btfsc
,btfss
).4. Operand Analysis
PORTA,1
→["PORTA", "1"]
).GPIO
,PORTA
,PORTB
) and maps it to the corresponding shadow register (LATA
forGPIO
/PORTA
,LATB
forPORTB
).HashMapGet(ShadowOutputs, "GPIO"|"PORTA"|"PORTB")
. Returns an empty string if not enabled.5. Instruction Adaptation
For
movf
:W
(e.g.,movf PORTA, W
), replaces withmovf LATA, W
.F
or other values.For
movwf
orclrf
:movwf PORTA
with:asm movwf LATA movf LATA, W movwf PORTA
clrf PORTA
with:asm clrf LATA movf LATA, W movwf PORTA
For
btfsc
orbtfss
:btfsc PORTA, 5
withbtfsc LATA, 5
(similarly forbtfss
).For bit operations (
bcf
,bsf
):bcf PORTA, 1
with:asm bcf LATA, 1 movf LATA, W movwf PORTA
For byte operations (
incf
,decf
, etc.):PORTx
withLATx
(e.g.,incf PORTA, F
→incf LATA, F
), followed by copying toPORTx
if destination isF
.6. Interrupt Handling
INTOFF
,INTON
) ifUserInt
orSysInt
is true, though these are currently commented out.7. Output
Chr(10)
).PORTA
instead ofporta
) in the output.Example Usage - internal and not exposed to user
Input
Output
Explanation
movf PORTA, W
→movf LATA, W
: Reads the intended output state fromLATA
.bcf PORTB, 2
→bcf LATB, 2
;movf LATB, W
;movwf PORTB
: UsesLATB
to avoid RMW.movwf PORTA
→movwf LATA
;movf LATA, W
;movwf PORTA
: Writes toLATA
first.btfsc PORTA, 5
→btfsc LATA, 5
: TestsLATA
for reliable state.clrf PORTB
→clrf LATB
;movf LATB, W
;movwf PORTB
: ClearsLATB
and updatesPORTB
.Assumptions
LATA
andLATB
defined as RAM variablesShadowOutputs
hash map tracks whetherLATA
/LATB
are enabled forGPIO
,PORTA
, orPORTB
using#option shadowregister gpio|porta|portb
clrf TRISA
), soLATA
/LATB
represent the intended output state.INTOFF
/INTON
are optional.Limitations
GPIO
,PORTA
, orPORTB
. Other registers are ignored.movf
with destinationF
(e.g.,movf PORTA, F
) is not adapted, as it's rare and may require different handling.PORTA
pins as inputs). In such cases,PORTA
must be read for input pins.INTOFF
/INTON
) is commented out and may need to be enabled if interrupts are used.Potential future enhancements
TRISA
/TRISB
and readPORTA
/PORTB
for input pins.INTOFF
/INTON
if interrupts are enabled to ensure atomic operations.RMW Safety Test Cases (Legacy PICs)
dir gpio out
dir gpio in
gpio.n = 0 or 1
PortState = gpio
PortState = gpio.5
gpio = CONSTANT_VALUE
gpio = PortState
gpio.1 = !gpio.1
gpio = !gpio
gpio = NOT PortState
gpio.3 = gpio.4
Note:
Initialize
LATA = 0x00
before tests to ensure predictable output.LATA
andPortState
are RAM variables.