Gosub nesting limit. What happens when you reach it?

HertzHog

Member
I have a complex program, but one that worked. I then wanted to add another 'twiddly bit', I put it into another GoSub routine, thinking it would be easy to identify and test the new bit. It would not work. Bizarre things happened when the new routine was triggered and specifically when it should have jumped back. My conclusion is the stack limit was reached on the 18M2. There was no warning from the compiler, not too surprising perhaps. How do others recognise or deal with or predict this problem? Once I realised what the problem might be, it was easy to re-write the code to avoid the extra GoSub. HertzHog.
 

g6ejd

Senior Member
AFAIK the stack pointer cycles around, so eventually it points to the start of the stack again. The manual says about 8 deep, so as the compiler does not tell you it's add them up yourself.
There is also the PE directive as mentioned in the manuals:

#gosubs 16/255 Programming Editor Only
Set the gosubs mode (16/255) on X parts.
Example: #gosubs 16
 

nick12ab

Senior Member
You could probably rewrite your code so that the gosubs are never more than 1 deep,

#gosubs 16/255 Programming Editor Only
Set the gosubs mode (16/255) on X parts.
Example: #gosubs 16
Although the manual isn't very clear, this does just set the number of gosub commands allowed, not the stack depth. Example:
Code:
main:
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
	gosub main
Code:
 gosub main
           ^

Error: Only 16 gosub commands allowed!
 

HertzHog

Member
Many of the GoSubs were needed, in a complex program it is no so easy to keep a track of their depth. The random behaviour when the code that was logically correct failed, was unexpected. I had not heard the issue mentioned here on the forum, but I am sure it has been. HertzHog
 

Buzby

Senior Member
I had not heard the issue mentioned here on the forum, but I am sure it has been. HertzHog
The 8 deep stack is in manual 2, under GOSUB.

Regarding the fact there there was no warning from the compiler, it can only count the number of 'gosubs' in the static text file, max 255.
It can not determine how your dynamic program flow will be, so it can't pre-calculate the stack depth you will need.

It is *very* rare that program needs anywhere near an 8 deep stack.
What is it that your program is doing ?

As nick says, you could probably rewrite is to use only 1 or 2 stack levels.

Cheers,

Buzby
 

HertzHog

Member
Ok, yes the limit is in the manual and I did not expect the compiler to really monitor the depth of the GoSubs as the program runs. The program fills the 18m2+ chip memory space. But, it does not have or depend on potentially infinite recursion for its logic. It was easy to reduce the depth and it is fine now. I just spent ages trying to work out why the newly added logic failed to run. The only clue was the program seemed to crash when it should have just neatly jumped back from the newly added GoSub. I did solve the problem myself and then realised it was not something I had seen shared on the forum.
If I tell you what the program does you will laugh and tut tut at needless complexity. I could not justify the 'features' I have built in without loosing you and boring you. So, perhaps it is best just regarded as an exercise in learning about the limits of what you can get these clever little chips to do. HertzHog.
 

Paix

Senior Member
Sounds like an infinite series of what if's to extend your knowledge base as you tack on more code to do more things for you. Enjoy. Every bit of code doesn't have to be presentable or well dressed to be very satisfying.
 

Buzby

Senior Member
Sounds like an infinite series of what if's ...
That still doesn't explain how it manages to get more than 8 deep in the stack.
HertzHog might learn a better way of doing it if he shared the code.
code doesn't have to be presentable or well dressed to be very satisfying.
Agreed, but it's *much* more satisfying to have tidy understandable code !.
 

hippy

Ex-Staff (retired)
I think it's a bit like going for that sixth gear in a car forgetting that it's only got five !

There's very little to stop you trying for it other than remembering not to, keeping track of how deep you are going when it comes to subroutine nesting. People were perhaps more conscious of the limits when the stack was smaller but as it has grown and there's less chance of running into the limit it becomes easier to forget there is a limit.
 

HertzHog

Member
Well I thought the GoSubs made it more readable and reusable. Their depth was not infinite. In another language I use, one can test the nesting depth in code or an error message is generated at run time.
It was easy to re-write it to avoid going over the limit, particularly with the small modification I was adding. It was just unexpected at the time to make a small change and to find it was a needle that broke this camel's back and have a program crash for what seemed a random reason particularly when the extra logic itself was simple. So, I thought I should share the phenomenon with the forum. HertzHog.
Hippy has made the point well too.
 

srnet

Senior Member
So does the firmware crash if you exceed the limit, or does it do something like reset the processor ?
 

Buzby

Senior Member
.... In another language I use, one can test the nesting depth in code, or an error message is generated at run time.
PICAXE cannot generate an error message at runtime because it has not got a Console.
( It does send the odd message out on serout, try using i2cboot to see some of them. There maybe one for deep nesting, I've never checked. )

I'm interested in your 'other language' that can check nesting depth without running the code.

What language is it ?
 

hippy

Ex-Staff (retired)
The common behaviour when you go over the stack depth is that RETURN's will then eventually take you to some other return point in the program and the program then gets stuck in some kind of loop. It really depends on the specific code. The firmware doesn't crash but the program is unlikely to do what it was meant to.
 

srnet

Senior Member
The firmware doesn't crash but the program is unlikely to do what it was meant to.
IC.

So the program crashes instead ?

Is there a reason why the firmware cannot detect the limits of the call stack, and do a reset if its exceeded ?
 

hippy

Ex-Staff (retired)
Is there a reason why the firmware cannot detect the limits of the call stack, and do a reset if its exceeded ?
The answer's probably two-fold; being a circular stack has its uses if used appropriately, and a reset ( or any other action ) requires extra firmware and still results in a program not doing what it should and that could make it potentially harder to debug, especially if it turns a program flaw into an apparent hardware issue.
 

srnet

Senior Member
and a reset ( or any other action ) requires extra firmware and still results in a program not doing what it should and that could make it potentially harder to debug, especially if it turns a program flaw into an apparent hardware issue.
So even though the firmware must be able to detect when its reached the limits of the push\pull on the stack, it cant take action, really ?

What is the safer option in the event of an overflow, crash and wreck havoc, or reset ?
 

boriz

Senior Member
I've been a programmer for a long time, and apart from recursive (self calling) subs/functions, I don't think I ever needed 8 nested GOSUBs. Maybe you could use a better structure? (Like Mr Monk says: You'll thank me later)
 
Last edited:

Buzby

Senior Member
I've been a programmer for a long time, and apart from re-iterative (self calling) subs/functions, I don't think I ever needed 8 nested GOSUBs.
Same here !.

I'd really like to see what this code is.

It could be some sort of maze solver, with sophisticated backtracking abilities.

Or maybe a Chess machine, with multiple lookaheads.

Or even some kind of code-breaking engine.

Go on HertzHog, give us a clue !.
 

boriz

Senior Member
"I'd really like to see what this code is." - EEK! I wouldn't. I hated this sort of debugging even when I was getting paid for it.
 
M

mkstevo

Guest
Hmmm...

I've written some Windows programs in Delphi (whose language is a sort of Object based extension of Pascal) and while I've never counted I think I might have got up to eight levels of nesting if you count a Procedure as a *similar* command call to a GoSub. With recursive Procedures, eight calls is hardly getting started.

Having done some programming in this style, it becomes a natural way of thinking and you spend a lot of time looking at any duplicated code, wondering if it could be tidied up with a few calls to a GoSub/Procedure.

Of course the stack in Windows for any particular program is almost limitless, and so long as you always return out of each procedure (back whence you came!) you should never find the limit.

PicAxe has it's limitations and demands a certain degree of discipline and innovation to get done what you want, within those limits, which makes it just as, if not more of a challenge.
 

Buzby

Senior Member
... you spend a lot of time looking at any duplicated code, wondering if it could be tidied up with a few calls to a GoSub....
Quite right, that's exactly what gosubs/procedures are good for, reducing duplicated code.
( But your ever-so-clever compiler might replace your gosubs with multiple copies of identical inline code to speed it up, at the expense of overall code size. )

However, I get the feeling HertzHog is not using gosubs to reduce the duplicated code, but for some kind of 'tidying' exercise.
 

hippy

Ex-Staff (retired)
This code runs to five deep. Call the routines from inside some 'real program' and the stack depth limit could easily be blown. It is a little contrived but I don't think it could be called entirely contrived, badly designed or unreasonable. I'd call it rather elegant.

Code:
Do
  w0 = $1234 : Gosub ShowWord
Loop

ShowWord:
  b2 = b1 : Gosub ShowByte
  b2 = b0 : Gosub ShowByte
  Return

ShowByte:
  b3 = b2 / $10 : Gosub ShowNibble
  b3 = b2 & $0F : Gosub ShowNibble
  Return

ShowNibble:
  b3 = b3 & $0F + "0"
  If b3 > "9" Then : b3 = b3+7 : End If
  Gosub OutLcd
  Gosub OutTerminal
  Return

OutLcd:
  b4 = C.0 : b5 = N2400 : Gosub Out
  Return

OutTerminal:
  b4 = C.1 : b5 = N4800 : Gosub Out
  Return
  
Out:
  SerOut b4, b5, ( b3 )
  Return
 

HertzHog

Member
Thanks for your comments. I did not want you guys to try and understand my program which you would feel is needlessly complicated. It does exactly what I want it to, how I want it to, which no one else could be expected to understand. It was easy to remove the extra layer of depth that crashed it. I wanted to keep the discussion generic not specific to my code, as I had fixed the problem before posting.
The GoSubs are both for reuse and tidiness and are not recursive without limit. My issue is, it is not trivial to know what depth you are are runtime, 8 can be reached easily, and the consequence of breaching the limit can be very unexpected and puzzling. Personally I think a reset would have been easier to debug than a random behaviour which is what I got.
FYI Foxpro has a way of testing and so not breaching the read level which is analogous to GoSub depth. HertzHog
 

HertzHog

Member
My code is not so far from Hippy's example. I am displaying a character series and numbers on a single 7 segment display. The idea is to have minimal hardware with maximum software functionality. The characters are flashed in turn to give the message string. I'm using an AXE049 and monitoring temperature. It mostly displays current temperature, but also shows trends and stores high, low, and 'mode' temperatures. To protect it from the environment it needs to be sealed in a transparent container e.g. a high tech jam jar. However, then you can't press the buttons, which I had used to give access to displaying the stored max and min values, without opening it up. I recently had the idea of using the LDR already present on the board, to remotely control it with a torch beam flash. So, it could be prompted to display stored limits without opening it up. The flash triggered jumping out of its current display mode into a mode displaying stored data before returning to current display mode. So, it was not surprising or unreasonable to have reached a GoSub limiting figure like 8. I had already ensured it could not be endlessly re-triggered in a recursive manner as I knew that would cause problems!
I had not heard dire warnings of what would happen if you hit the limit and thought it worth sharing with the forum.
The loops were to reuse code, it is filling the program space as it is. The final straw was in a GoSub to make it easy to comment out for testing, when the logic was removed from that non-important GoSub it works fine.
Hippy talked of a sixth gear and that is a good analogy, but it is easy to know which gear you are in before you slip one up or down. Not so easy in code or on a bike with Dérailleur gears and a click up and down shift. HertzHog
 
Top