News:

Let's find out together what makes a PIC Tick!

Main Menu

Sanity check?

Started by dr-zin, Oct 01, 2025, 05:17 PM

Previous topic - Next topic

dr-zin

Hello again.  I have a question about subroutines.  There may not be a conventional answer, as it may be compiler specific.  Anyway, I am calling a subroutine (no arguments, no result) using Gosub to perform repetitive calculations on Global variables.  Within the routine are some error checking comparisons that drop out of the routine if certain results occur using Goto Main.  Without errors, the subroutine finishes the calculations and exits normally using Return.

My question:  If I exit the Gosub using a Goto (instead of a Return), will the program barf if I try to call the Subroutine again from the Main program area because the Return was bypassed [does it ruin or scramble the stack or whatever by not having the routine released normally]?  Any answers or opinions?

Below is some pseudocode that demonstrates the situation.  At the end is an alternate version of Subroutine1 that avoids using Goto.  Is this required, or am I just being paranoid/anal.  I've had some unusual program operation, and I'm trying to see if the repeated Sub call is an obvious problem, or if I have a more devious timing issue.  Thanks, Paul

Pseudo Code
****
Setup/Config./Var. declarations
****
Main:
Start main routine
****
Gosub Subroutine1
continue main routine
goto other Subroutines
call Subroutines several times by returning to Main using Goto Main
****
finish up and print results
End

Subroutine1:
****
Compute calculation using global variables
****
Error Check section
If x<14 then
    High LoLED
    delayms 1000
    Low LoLED
    goto Main
Endif
If x>21 then
    High HiLED
    delayms 1000
    Low HiLED
   goto Main
Endif
****
Continue calculations
Return

Subroutine2:
****
Return

Subroutine3:
****
Return


Alternate Sub1----
Subroutine1:
****
Compute calculation using global variables
****
Error Check section
If x<14 then
    High LoLED
    delayms 1000
    Low LoLED
    goto Error Esc Label
Endif
If x>21 then
    High HiLED
    delayms 1000
    Low HiLED
   goto Error Esc Label
Endif
****
Continue calculations
Error Esc Label:
Return

RGV250

Hi,
QuoteMy question:  If I exit the Gosub using a Goto (instead of a Return), will the program barf if I try to call the Subroutine again from the Main program area because the Return was bypassed [does it ruin or scramble the stack or whatever by not having the routine released normally]?  Any answers or opinions?
Do not do this, you will end in trouble, if you want to exit a subroutine early do as you have at the end and jump to a marker just before the Return so it exits as it should. Otherwise as you said, it will mess the stack up sooner or later.

Bob

dr-zin

Thanks Bob,

I came to the same conclusion, hence the reach out to see if this was necessary or not.  In this case, it's a simple change that, if nothing else, clears up some spaghetti code.  I just didn't know if it was essential or not.  The manual says all Gosubs must end in a Return, but doesn't specify that you must exit the routine only by this door.  It seems reasonable, but I don't have a knowledge of the underlying assembly structures for memory allocation.  Thanks for the quick response.  --Paul

John Drew

The call to a subroutine  puts the return address onto the stack. If you use a GOTO then the return address stays there rather than being removed by a Return.
Older PICs have a very small stack. It will rapidly fill with all the leftover addresses. The device will quickly crash.
John

david

That's a great question and I'm guilty as charged. I had always wondered if it was a bit naughty but never found issues with it.
I have a Goto inside a subroutine that branches to a piece of code to shut down the micro so maybe that's why it's tolerating it.
I also notice I have a subroutine inside a subroutine but that should be ok. 

Fanie

Subroutines are awesome to do repeated routines throughout the program instead of having to make the code long with repeated parts in it.

It saves the current address where you call from, and returns to address + 1

You can terminate a gosub prematurely too

GoSub:
     do code
     do code
     if A = 25 then ----- : Return  ' if the rest of code needs not execution
     do code
     do code
     do code
Return

As John said, as long as the stack is maintained, it works.

I have used gosub.... return to greatly reduce code that would otherwise not fit in the pic's small memory.
You just look for repeat pieces of code and put them in a gosub.

Stephen Moss

#6
Quote from: RGV250 on Oct 01, 2025, 05:41 PMHi,
QuoteMy question:  If I exit the Gosub using a Goto (instead of a Return), will the program barf if I try to call the Subroutine again from the Main program area because the Return was bypassed [does it ruin or scramble the stack or whatever by not having the routine released normally]?  Any answers or opinions?
Do not do this, you will end in trouble, if you want to exit a subroutine early do as you have at the end and jump to a marker just before the Return so it exits as it should. Otherwise as you said, it will mess the stack up sooner or later.

Bob
A slightly cleaner way (in my opinion) of doing this in code would be to use the compilers newer Sub & EndSub method of defining subroutines. That way instead of...

Subroutine1:
****
Compute calculation using global variables
****
Error Check section
If x<14 then
    High LoLED
    delayms 1000
    Low LoLED
     goto Error Esc Label
Endif
If x>21 then
    High HiLED
    delayms 1000
    Low HiLED
   goto Error Esc Label
Endif
****
Continue calculations
Error Esc Label:
Return

You do...

Sub Subroutine1()
****
Compute calculation using global variables
****
Error Check section
If x<14 then
    High LoLED
    delayms 1000
    Low LoLED
   ExitSub
Endif
If x>21 then
    High HiLED
    delayms 1000
    Low HiLED
   ExitSub
Endif

EndSub

And call it with Subtroutine1(), not GoSub Subroutine1.

The ExitSub command is effectively a Return command, but you could not place a return there when using the classic method of defining a subroutine because the compiler would see the first Return as defining the end of the subroutine.

Additionally I think that...
  • The Sub & EndSub more clearly define in code the start and end of you subroutines
  • The Sub & EndSub method of defining a subroutine it similar to that of defining a Procedure, so it provides a certain familiarity if/when someone who has not used Procedures before subsequently needs to.

dr-zin

Thanks for the additional feedback.  Stephen, as I recently stated in a different post, I open to try to incorporate newer language structures in my code, and Sub/EndSub is a good example.  It's just mostly muscle memory that keeps me using the familiar commands.  I began programming in BASIC (yes, all caps) in 1981, and I just naturally think in the old commands (wish we still supported Data/Read. Sigh...).  It's like a native language that I have to convert from to get to Python and other languages I had to use at work.  So I'm slow to branch out and leave the familiar for the "new-fangled".  I will take a fresh look at the manual to see what hidden gems have been added that I can use.

As for Procedures, I haven't used those since studying Pascal in college.  That was a wonderful language but relegated now.  I don't know if I'll ever make the jump to using those structures?  Anyway, thanks and regards for your comments and feedback.  --Paul

CPR

Quote from: dr-zin on Oct 02, 2025, 01:28 PMAs for Procedures, I haven't used those since studying Pascal in college.  That was a wonderful language but relegated now.

Take a look at https://www.lazarus-ide.org/

John Drew

#9
Hi Stephen,
Fanie's approach will work fine. I do it frequently. The flow is OK.
My programs often have nested sub routines (or procedures) that exit early with a return depending on a condition and they do cut down on repetitive code. It's just a matter of keeping an eye on how deep to avoid a stack crash.
18F have bigger stacks generally.

GOTO is to be avoided where possible as it creates string code that is hard to follow and harder to Debug. Procedures and functions enable small bundles of code that make the program flow more logical. I realise you know the above but some don't.

I begin writing a new program by creating a framework by typing probable procedure names, put a short comment describing what I want to happen in there and then filling the code as the program is built. I find this easier than drawing flow diagrams.
Just some thoughts.
John

charliecoutas

Good advice above. I stopped myself using GOTO years ago, and I now write better and more reliable code.

Charlie

CPR

Quote from: charliecoutas on Oct 02, 2025, 04:28 PMGood advice above. I stopped myself using GOTO years ago, and I now write better and more reliable code.
Charlie

Yes, in any high level language they're probably not the most elegant approach? But when you look at Assembler they're everywhere. So. horses and courses, maybe?

dr-zin

Hello CPR,

Thanks for the link.  Interesting.  I have nothing but fond memories for Pascal, even if it was a bit terse.  The reason I don't want to rekindle that flame is because the prime mover for Pascal use with ucontrollers is Mikroelektronika, and my brief association with them decades ago was not fruitful.  I think you can understand?  I don't want to jump in that river again. Positron (oops, I briefly typed Proton, and had to correct) basic gives me everything I need and still has room for me to continue growing as needed.  It fills the niche perfectly.  --Paul