I have the following subroutine. (The entire code is attached below.)
SUBSendByte(InbbASBYTE,InrbASBYTE)'rb > 0 read MISO FOR bIdx = 7 TO 0 STEP -1 'MSBfirstTARGET_SCK=0TARGET_MOSI=bb.bIdxNOPNOPNOPTARGET_SCK=1IFrb>=1THEN'SHIFT MISObyte, left, 1 SET C OFF ROTATE MISObyte left IF TARGET_MISO=1 THEN MISObyte.0 = 1 ELSE 'adummyelsebranchtomakeexecutiontimethesameforbothcases'SHIFT dummy, left, 1 SET C OFF ROTATE dummy left IF TARGET_MISO=1 THEN dummy.0 = 1 END IF NEXTEND SUB
Look at the line where TARGET_MOSI is set to the bit value of the variable BB. TARGET_MOSI is defined as such
Instead of setting the port pin to whatever the value of the bit has, the generated assembler code first clears the pin, meaning sets it to 0, (cbi PORTB,2) and then checks the bit and if necessary sets it to 1. This is not what the BASIC instruction say should be done.
The instructions say to set it either 0 or 1 depending on the value of the bit. It is not instructing to set it first to 0 and then check the value of the bit to change the pin if necessary.
Imagine the pin is 1 before the routine is called. Rather than leaving it at 1 it first becomes 0 and then later 1. This routine is used for bitbanging, which produces an incorrect signal at that pin. There may be other problems with this routine. The BASCOM equivalent works perfectly. The GCBASIC created completely fails. This is one problem I noticed.
Thank you! I was looking at the wrong .asm file. Your second suggestion
If bb.bIdx = 1 Then
TARGET_MOSI = 1
Else
TARGET_MOSI = 0
End If
should be the preferred one because it produces much smaller code than the #option volatile. 16 bytes less to be exact. This may in fact be a worthwhile change in the compiler. Whenever a bit value is asigned to a port, it could apply this IF-ELSE solution. That avoids shooting onself in the foot and produces much smaller code than the volatile workaround.
But the larger question is why does this SendByte subroutine not properly work when compiled by GCBASIC but works when compiled by BASCOM. I am at the end of my wits. No idea why the GCBASIC one fails. Here is the BASCOM basic and assembler with some annotations to make it clearer what happens.
SUBSendByte(byvalbASBYTE,byvalrbASBYTE)'rb>0 read MISO FOR bIdx = 7 TO 0 STEP -1 'MSBfirstTARGET_SCK=0TARGET_MOSI=b.bIdxNOPNOPNOPTARGET_SCK=1IFrb>=1THENSHIFTMISObyte,left,1IFTARGET_MISO=1THENSETMISObyte.0ELSE'a dummy else branch to make execution time the same for both cases SHIFT dummy, left, 1 IF TARGET_MISO=1 THEN SET dummy.0 END IF NEXTEND SUB
and here the assembler code including the helper subroutines
The SendByte() function is used in an ATtiny841 to program another AVR via its MOSI/MISO/SCK/RES interface. To enter the programming mode, after RESET has been set low, one sends the command 0xAC, 0x53, 0x00, 0x00, and the target microcontroller responds during the third byte with 0x53. This works perfectly with the BASCOM generated code. However, with the GCBASIC generated code I am reading back 0x7E and sometimes 0x7C. The fact that it reads back different values suggests some kind of race condition or timing issue that is absent from the BASCOM generated code. But more fundamentally, why is it producing the wrong value in MISObyte?
If anybody has ideas or suggestions of what I could try, let me know. I am testing this on the same hardware at the same speed. The problem must therefore be somewhere in the code.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
So, using AVRASM tells me that the ASM is valid. As AVRASM would have rejected.
I believe from my experience that GCBASIC is operating faster than BASCOM.
I would rewrite the sub to use the GCBASIC operations to create delays.
Does this clock the data out? I just lifted this from the forum, changing the vars.
SUB SendByte(In bb AS BYTE, In rb AS BYTE) 'rb > 0 read MISO
repeat 8
if bb.7 = ON then
set TARGET_MOSI ON
else
set TARGET_MOSI OFF
end if
set TARGET_SCK On
rotate bb left
set TARGET_SCK Off
end repeat
set TARGET_SCK ON
End Sub
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Yes, this routine clocks the data out but also, if rb is 1, reads at the same time the data on the TARGET_MISO pin into the MISObyte variable. It looks like the reading is the issue because if the target microcontroller would not get the correct "enter program mode" command, it wouldn't respond at all on the MISO line. The fact that it does, and we get something different to all zero or all 1 back at TARGET_MISO, suggests that the sending part works ok.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I think you are talking about your routine? I was asking about the one I posted.
I just wanted to confirm the send works. With routine I posted.
But, you code cannot read back at the 'same time' because of the else statement. You routine is either clocking a byte out OR clock ing a byte in. Is this correct?
If correct.. then routine name is not correct. Should be mySPITransfer()
In the routine, which is very typical across many libraries, the sub sends and gets a byte. There is no need for a secondary parameter as the data is clock in regardless and is sent back out via SPITempOut ( the 2nd parameter ).
So, this uses the inhernent cycles of the rotates to give sufficent clock cycles to the target device. BASCOM looks like it is using the SHIFT calls - but, this is all about timing.
sub mySPITransfer( IN SPISendByte as byte, OUT SPITempOut as byte )
SPITempOut = 0
set TARGET_SCK Off
repeat 8
if SPISendByte.7 = ON then
set TARGET_MOSI ON
else
set TARGET_MOSI OFF
end if
'Device is cpol = 0. Invert SCK if cpol = 1
SET TARGET_SCK ON
rotate SPISendByte left
rotate SPITempOut left
if TARGET_MISO = On then
set SPITempOut.0 On
Else
set SPITempOut.0 Off
end if
SET TARGET_SCK OFF
end repeat
end Sub
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
But, you code cannot read back at the 'same time' because of the else statement. You routine is either clocking a byte out OR clock ing a byte in. Is this correct?
My code does both send (to TARGET_MOSI) and read back from TARGET_MISO and stores it in the global variable MISObyte.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I think I have identified the bug in the compiler that is causing the problem. I don't want to believe it, and maybe I am going crazy working for 2 days on this problem, but here it is for the compiler experts to take a look.
The difference is only how I name the second parameter in the function. Here is the version that works as intended:
SUBSendCmd(InrbASBYTE)'a four byte command sent out via SCK and MOSI FOR I1=1 TO 4 withRead = 0 IF rb = I1 THEN withRead = 1 SendByte(CMD(I1),withRead) NEXT TARGET_SCK = 0END SUBSUB SendByte(In bb AS BYTE, In rbx AS BYTE) 'rb>0readMISOREPEAT8TARGET_SCK=0Ifbb.7=1ThenTARGET_MOSI=1ElseTARGET_MOSI=0EndIfTARGET_SCK=1IFrbx>=1THENSETCOFFROTATEMISObyteleftIFTARGET_MISO=1THENMISObyte.0=1ELSE'a dummy else branch to make execution time the same for both cases SET C OFF ROTATE dummy left IF TARGET_MISO=1 THEN dummy.0 = 1 END IF ROTATE bb left END REPEATEND SUB
and here is the version that does not work
SUBSendCmd(InrbASBYTE)'a four byte command sent out via SCK and MOSI FOR I1=1 TO 4 withRead = 0 IF rb = I1 THEN withRead = 1 SendByte(CMD(I1),withRead) NEXT TARGET_SCK = 0END SUBSUB SendByte(In bb AS BYTE, In rb AS BYTE) 'rb>0readMISOREPEAT8TARGET_SCK=0Ifbb.7=1ThenTARGET_MOSI=1ElseTARGET_MOSI=0EndIfTARGET_SCK=1IFrb>=1THENSETCOFFROTATEMISObyteleftIFTARGET_MISO=1THENMISObyte.0=1ELSE'a dummy else branch to make execution time the same for both cases SET C OFF ROTATE dummy left IF TARGET_MISO=1 THEN dummy.0 = 1 END IF ROTATE bb left END REPEATEND SUB
As you can see, the only difference is that in the version that works, the second parameter of SendByte is called rbx, whereas in the version that does not work, it is called rb. I thought that parameter names are local and should not interfere with other subroutines that use the same parameter name.
I am attaching the full code file of the non-working version below.
But that is not how a BASIC compiler is supposed to work. Function and Subroutine parameters should be local and shadow global variables with the same name. If all these subroutine parameters become global variables, how are you supposed to build and use libraries? How can one know which variable names have already been used as parameters in some library function? This can create all kinds of unexpected conflicts and failures.
Subroutine parameters need to be handled via a stack or buffer in SRAM where they are stored temporarily while the subroutine is executed, and then released when it has finished.
To be frank, with this everything-is-a-global-variable, GCBASIC is entirely useless for anything but the most trivial applications.
Last edit: miniTesla 7 days ago
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Then please explain to me, how would I know that I am using the same parameter name as some other library function so that I can avoid a conflict?
Example, I opened the first source file that gets included when this example is compiled. It is picas.h. It has subroutines that use a parameter with the name LineX1. If I would use the same name in one of my subroutines, a conflict could take place that breaks the code. How would I know?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
We do not need to complicate this. The issue is function and subroutine parameter name conflicts. If GCBASIC is treating every parameter as a global variable, then it must at least issue notices or warnings when reuse takes place. Simply have the compiler output a table that shows any parameter name that is used in more than one function/subroutine. That way the code developer has a straightforward way to know if a conflict may be present.
HTML report for RAM variables. Usage etc
The html report only provides information about subroutines, not variables/parameters.
There is currently no simple way I can see, to identify such variable name conflicts.
Last edit: miniTesla 7 days ago
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Yes, all doable. This is an Open Source project. That is the sort of functionality that could be added, in the context that this is the first request for this I have seen since 2013.
For now. I would use ASM as a great reference.
And, I should mention that option explicit will provide a level of protection for undeclared variables,
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Name conflicts are a core issue for compilers. Why this has not been address at the very beginning is not understandable to me. You are relying on luck that a variable or parameter name does not conflict with some library. Avoiding conflicts in one's own code is one isse, but extending that to all the libraries and internal functions one may use is insane. It is not some nice-to-have feature, it is essential to writing software.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Not a question that I can answer. Hugh, the original architect never included that. If he were to do it all again then I guess he would. He would have implemented naming structure also.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
GCBASIC already does some name conflict checking. For example, if I #define the same name twice, the compiler issues a warning. The same should be done for DIM and function/subroutine parameters since they all are global variables. A warning should be issued so that the application developer can decide if this is intentional or if it is a conflict that has to be fixed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
It is a necessity. Anything else is like playing russian roulette. You would never know if a conflict breaks your code. Such conflicts usually break the code in subtle ways that are hard to debug, as this one example above has shown. Two days spent of debugging when it came down to a simple name conflict that the compiler could have flagged, particularly since the compiler works in a non-standard way to any other BASIC compiler I am aware of. Treating parameters as global variables is highly unusual.
Where in the documentation is that highlighted? Such out of norm behavior needs to be highlighted and mentioned more than once.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have the following subroutine. (The entire code is attached below.)
Look at the line where TARGET_MOSI is set to the bit value of the variable BB. TARGET_MOSI is defined as such
The assembler created for this subroutine is
The problem is this portion
Instead of setting the port pin to whatever the value of the bit has, the generated assembler code first clears the pin, meaning sets it to 0, (cbi PORTB,2) and then checks the bit and if necessary sets it to 1. This is not what the BASIC instruction say should be done.
The instructions say to set it either 0 or 1 depending on the value of the bit. It is not instructing to set it first to 0 and then check the value of the bit to change the pin if necessary.
Imagine the pin is 1 before the routine is called. Rather than leaving it at 1 it first becomes 0 and then later 1. This routine is used for bitbanging, which produces an incorrect signal at that pin. There may be other problems with this routine. The BASCOM equivalent works perfectly. The GCBASIC created completely fails. This is one problem I noticed.
Please use #option votatile {bit}. I think this may resolve.
Doesn't do anything. Even with
The generated assembler is the same. It first clears the port with "cbi PortB,2" and then later sets it with "sbi PortB,2".
Last edit: miniTesla 2026-05-07
That is odd. because I get a Two-Pass Bit-Copy Pattern
The compiler generates both a clear and set path for every bit assignment, ensuring the pin is always actively driven.
Pass 1 — Clear if bit is 0:
Pass 2 — Set if bit is 1:
The pin is a direct, stable copy of
bb.bIdxat all times.Or, you can use
Thank you! I was looking at the wrong .asm file. Your second suggestion
should be the preferred one because it produces much smaller code than the #option volatile. 16 bytes less to be exact. This may in fact be a worthwhile change in the compiler. Whenever a bit value is asigned to a port, it could apply this IF-ELSE solution. That avoids shooting onself in the foot and produces much smaller code than the volatile workaround.
But the larger question is why does this SendByte subroutine not properly work when compiled by GCBASIC but works when compiled by BASCOM. I am at the end of my wits. No idea why the GCBASIC one fails. Here is the BASCOM basic and assembler with some annotations to make it clearer what happens.
and here the assembler code including the helper subroutines
The SendByte() function is used in an ATtiny841 to program another AVR via its MOSI/MISO/SCK/RES interface. To enter the programming mode, after RESET has been set low, one sends the command 0xAC, 0x53, 0x00, 0x00, and the target microcontroller responds during the third byte with 0x53. This works perfectly with the BASCOM generated code. However, with the GCBASIC generated code I am reading back 0x7E and sometimes 0x7C. The fact that it reads back different values suggests some kind of race condition or timing issue that is absent from the BASCOM generated code. But more fundamentally, why is it producing the wrong value in MISObyte?
If anybody has ideas or suggestions of what I could try, let me know. I am testing this on the same hardware at the same speed. The problem must therefore be somewhere in the code.
Check your GCBASIC program by compiling with AVRASM. Select this in Prefs Editor. Do you get the same error?
Do you have a protocol analyser? what does that show?
You have shared the BASCOM code but not the current GCBASIC code.
I have used AVRASM but it doesn't fix the problem.
I do not have a logic analyzer to measure the signals on the pins directly.
So, using AVRASM tells me that the ASM is valid. As AVRASM would have rejected.
I believe from my experience that GCBASIC is operating faster than BASCOM.
I would rewrite the sub to use the GCBASIC operations to create delays.
Does this clock the data out? I just lifted this from the forum, changing the vars.
Yes, this routine clocks the data out but also, if rb is 1, reads at the same time the data on the TARGET_MISO pin into the MISObyte variable. It looks like the reading is the issue because if the target microcontroller would not get the correct "enter program mode" command, it wouldn't respond at all on the MISO line. The fact that it does, and we get something different to all zero or all 1 back at TARGET_MISO, suggests that the sending part works ok.
I think you are talking about your routine? I was asking about the one I posted.
I just wanted to confirm the send works. With routine I posted.
But, you code cannot read back at the 'same time' because of the else statement. You routine is either clocking a byte out OR clock ing a byte in. Is this correct?
If correct.. then routine name is not correct. Should be mySPITransfer()
In the routine, which is very typical across many libraries, the sub sends and gets a byte. There is no need for a secondary parameter as the data is clock in regardless and is sent back out via SPITempOut ( the 2nd parameter ).
So, this uses the inhernent cycles of the rotates to give sufficent clock cycles to the target device. BASCOM looks like it is using the SHIFT calls - but, this is all about timing.
To use
Send: mySPITransfer ( bytetosend, somevariableyoudontcareaboutbutitcannotbeaconstant)
Get: mySPITransfer ( somevariableyoudontcareaboutbutitcannotbeaconstant, bytereceived )
Both... mySPITransfer( bytetosend, bytereceived)
My code does both send (to TARGET_MOSI) and read back from TARGET_MISO and stores it in the global variable MISObyte.
OK.
I think I have identified the bug in the compiler that is causing the problem. I don't want to believe it, and maybe I am going crazy working for 2 days on this problem, but here it is for the compiler experts to take a look.
The difference is only how I name the second parameter in the function. Here is the version that works as intended:
and here is the version that does not work
As you can see, the only difference is that in the version that works, the second parameter of SendByte is called rbx, whereas in the version that does not work, it is called rb. I thought that parameter names are local and should not interfere with other subroutines that use the same parameter name.
I am attaching the full code file of the non-working version below.
Why
RBXWorked andRBdid not. You know all this but I writing up for those that read this in years to come.Works
Breaks
**Key fact about GCBASIC **
All variables — including parameters — are stored in global SRAM.
There are no true locals.
The compiler assigns each parameter a global SRAM address.
What went wrong with
rbYou already have a global variable:
When you declare:
GCBASIC sees the parameter name rb, and because it matches the global name RB, the compiler assigns the same SRAM address to the parameter.
Result
Inside
SendByte, the parameterrband the globalRBare the same byte.This breaks the SENDCMD/SENDBYTE logic, because SENDCMD expects RB to be its own working variable.
4. Why
rbxworksrbxdoes not match any global variable nameRBis no longer overwrittenEverything works again.
5. How to make
RBwork safelyOption 1 — Rename the parameter
Recommended and simplest:
Option 2 — Rename the global variable
If you must use
rbas the parameter name:Then:
No name collision → separate SRAM addresses → works.
6. Summary
rbcollided with globalRB, so the parameter overwrote the global.rbxavoided the collision, so it worked.But that is not how a BASIC compiler is supposed to work. Function and Subroutine parameters should be local and shadow global variables with the same name. If all these subroutine parameters become global variables, how are you supposed to build and use libraries? How can one know which variable names have already been used as parameters in some library function? This can create all kinds of unexpected conflicts and failures.
Subroutine parameters need to be handled via a stack or buffer in SRAM where they are stored temporarily while the subroutine is executed, and then released when it has finished.
To be frank, with this everything-is-a-global-variable, GCBASIC is entirely useless for anything but the most trivial applications.
Last edit: miniTesla 7 days ago
Not sure how to reply. Just to say awareness and use of parameters has enabled 10000 of solutions to be build over the last 20 years.
Local variables is on the development roadmap, along with structures.
Then please explain to me, how would I know that I am using the same parameter name as some other library function so that I can avoid a conflict?
Example, I opened the first source file that gets included when this example is compiled. It is picas.h. It has subroutines that use a parameter with the name LineX1. If I would use the same name in one of my subroutines, a conflict could take place that breaks the code. How would I know?
The issue is much larger.
The AVR architecture is a flat namespace architecture. Registers, register bits, compiler tool, include file must ALL have unique names.
Addressing needs to ensure uniqueness.
In GCBASIC we provide the following which could be used to inspect.
I do not know of any compiler that provides such extensive access to the inner working for RAM or Constants.
Last edit: Anobium 7 days ago
We do not need to complicate this. The issue is function and subroutine parameter name conflicts. If GCBASIC is treating every parameter as a global variable, then it must at least issue notices or warnings when reuse takes place. Simply have the compiler output a table that shows any parameter name that is used in more than one function/subroutine. That way the code developer has a straightforward way to know if a conflict may be present.
The html report only provides information about subroutines, not variables/parameters.
There is currently no simple way I can see, to identify such variable name conflicts.
Last edit: miniTesla 7 days ago
Yes, all doable. This is an Open Source project. That is the sort of functionality that could be added, in the context that this is the first request for this I have seen since 2013.
For now. I would use ASM as a great reference.
And, I should mention that option explicit will provide a level of protection for undeclared variables,
Name conflicts are a core issue for compilers. Why this has not been address at the very beginning is not understandable to me. You are relying on luck that a variable or parameter name does not conflict with some library. Avoiding conflicts in one's own code is one isse, but extending that to all the libraries and internal functions one may use is insane. It is not some nice-to-have feature, it is essential to writing software.
Not a question that I can answer. Hugh, the original architect never included that. If he were to do it all again then I guess he would. He would have implemented naming structure also.
GCBASIC already does some name conflict checking. For example, if I #define the same name twice, the compiler issues a warning. The same should be done for DIM and function/subroutine parameters since they all are global variables. A warning should be issued so that the application developer can decide if this is intentional or if it is a conflict that has to be fixed.
A good approach. A neat solution.
It is a necessity. Anything else is like playing russian roulette. You would never know if a conflict breaks your code. Such conflicts usually break the code in subtle ways that are hard to debug, as this one example above has shown. Two days spent of debugging when it came down to a simple name conflict that the compiler could have flagged, particularly since the compiler works in a non-standard way to any other BASIC compiler I am aware of. Treating parameters as global variables is highly unusual.
Where in the documentation is that highlighted? Such out of norm behavior needs to be highlighted and mentioned more than once.