GOSUB stack overflow

Tony P

Member
Project:
4 digit access code (random numbers)
Correct guess produces 7 segment display of required digits for mechanical digilock

After solving a random number problem (previous post) my program is working fine apart from a problem with a GOSUB stack error if I make too many incorrect button presses.

QUESTION: Is there a way of reducing or resetting the GOSUB stack?


Code:
;REVISION 3
;18M2+

table 0,(1,2,3,4)	;DIGITS TO RANDOMIZE
			
	pause 500 	
	random b0
	b2=b0//4			
	readtable b2,b10;SET 1ST DIGIT	
		
	do random b0
	b3=b0//4
	loop while b3=b2	
	readtable b3,b11;SET 2ND DIGIT	

	do random b0
	b4=b0//4
	loop while b4=b3 or b4=b2 
	readtable b4,b12;SET 3RD DIGIT

	do random b0
	b5=b0//4
	loop while b5=b4 or b5=b3 or b5=b2
	readtable b5,b13;SET 4TH DIGIT
	
	let b16=0 ;WHEN B16=4 GOTO WINNER ROUTINE

main:

	pulsout b.6,10;SET 7 SEGMENT DISPLAY TO 00
	
 	if b10=1 then goSUB sw1:	if b10=2 then goSUB sw2:	if b10=3 then gOSUB sw3: if b10=4 then goSUB sw4
	
 	HIGH B.0
	if b11=1 then goSUB sw1:	if b11=2 then goSUB sw2:	if b11=3 then goSUB sw3: if b11=4 then goSUB sw4
	
 	HIGH B.1
	if b12=1 then goSUB sw1:	if b12=2 then goSUB sw2:	if b12=3 then goSUB sw3: if b12=4 then goSUB sw4
	
 	HIGH B.2
	if b13=1 then goSUB sw1:	if b13=2 then goSUB sw2:	if b13=3 then goSUB sw3: if b13=4 then goSUB sw4
	
sw1:
	if pinC.1 =1 then goto sound1
	if pinC.0 =1 then goto fail
	if pinC.7 =1 then goto fail
	if pinC.6 =1 then goto fail
	if pinC.2 =1 then goto fail
	goto sw1
sw2:
	if pinC.1 =1 then goto fail
	if pinC.0 =1 then goto sound2
	if pinC.7 =1 then goto fail
	if pinC.6 =1 then goto fail
	if pinC.2 =1 then goto fail
	goto sw2
sw3:
	if pinC.1 =1 then goto fail
	if pinC.0 =1 then goto fail
	if pinC.7 =1 then goto sound3
	if pinC.6 =1 then goto fail
	if pinC.2 =1 then goto fail
	goto sw3
sw4:
	if pinC.1 =1 then goto fail
	if pinC.0 =1 then goto fail
	if pinC.7 =1 then goto fail
	if pinC.6 =1 then goto sound4
	if pinC.2 =1 then goto fail
	goto sw4
	
sound1:
	sound B.4,(120,40)
	let b16=b16+1
	if b16=4 then goto winner
	RETURN
sound2:
	sound B.4,(120,40)
	let b16=b16+1
	if b16=4 then goto winner
	RETURN
sound3:
	sound B.4,(120,40)
	let b16=b16+1
	if b16=4 then goto winner
	RETURN
sound4:
	sound B.4,(120,40)
	let b16=b16+1
	if b16=4 then goto winner
	RETURN


fail:
	let b16=0	; RESET WINNER COUNT

		low B.0	;
		low B.1	;LED's OFF
		low B.2	;	
		low B.3	;
	
	high B.7	;RED FAIL LED ON
	sound B.4,(75,20,50,40)
	pause 300
	low B.7	;RED FAIL LED OFF
	goto MAIN	

winner:

	low B.0	;
	low B.1	;
	low B.2	;LED's OFF
	low B.3	;
	low B.7	;
	
		for b2 = 1 to 5	;
		high B.0		;
		pause 50		;
		low B.0		;
		high B.1		;
		pause 50		;
		low B.1		;
		high B.2		;
		pause 50		;
		low B.2		;
		high B.3		;LED's FLASH SEQUENCE
		pause 50		;
		low B.3		;
		pause 50		;
		high B.2		;
		pause 50		;
		low B.2		;
		pause 50		;	
		high B.1		;
		pause 50		;
		low B.1		;
		pause 50		;
		next b2		;
	
	pulsout B.6,10	;SET 7 SEGMENT DISPLAY TO 00
	for b3 = 1 to 76	;SET NUMBER TO REQUIRED OUTPUT FOR 7 SEGMENT DISPLAYS
	pulsout B.5,10	;SEND NUMBER TO DISPLAY
		
		RANDOM W0		;
		B9=W0//85+50	;SET RANDOM SOUND AS 7 SEGMENT DISPLAY ADVANCES
		sound B.4,(B9,7)	;
		next b3
	pause 500
	



GOTO MAIN		;TEST PURPOSES ONLY - REMOVE BEFORE FINALISING IC
 

nick12ab

Senior Member
QUESTION: Is there a way of reducing or resetting the GOSUB stack?
No.

The reason for this problem is that you are using a 'goto main' at the end of the fail routine which means that the earlier 'gosub' in the program does not get 'cancelled out'. You need to change your code either so that there is a return after the fail routine, or so that there are gotos instead of gosubs.

Why are you not using the #picaxe directive?
 

westaust55

Moderator
Same situation as nick12ab has mentioned exists for the winner routine.
Using a GOSUB to go to the tests, then if correct using GOTO to get to the Winner: routine but then returning with GOTO Main.
So while the Fail routine will likely be the first to cause grief even the Winner routine needs restructuring.
 

bpowell

Senior Member
So, I understand what's going on with the stack overflow (as described above) but is there a work-around? I'm just curious...some way to reset the Gosub stack with a well-placed POKE?
 

bpowell

Senior Member
Rossko...I recognize the RETURN is the correct way to come out of a GOSUB...I'm asking if there is another programmatic way to reset the GOSUB stack. Where is it located? What memory address? What does it look like? There is ALWAYS more than one way to skin a cat...the RETURN is obviously the RIGHT way, but my question is looking for the alternative methods.
 

inglewoodpete

Senior Member
Rossko...I recognize the RETURN is the correct way to come out of a GOSUB...I'm asking if there is another programmatic way to reset the GOSUB stack. Where is it located? What memory address? What does it look like? There is ALWAYS more than one way to skin a cat...the RETURN is obviously the RIGHT way, but my question is looking for the alternative methods.
There is always the possibility of doing what you suggest. However, it is extremely bad practice to 'fiddle' with most operating system variables, like the stack. The exception is the hardware control variables (Special Function Registers), if you have a good understanding of the PIC used for the PICAXE.

It is far, far better to stucture your program correctly: use a 'Return' statement when you need to exit a subroutine. If you think you need to use a 'GoTo' to exit a subroutine, then you shouldn't be using a subroutine. It's all about good programming practice.
 

g6ejd

Senior Member
PICAXE Basic does inherently support (not good practice though) a GOTO to exit a subroutine, because the stack space is circular and in effect resets the stack. It only goes badly wrong if you use GOTO in some instances and RETURN in others within the same structure and then expect it all to work correctly :)
 

nick12ab

Senior Member
Because if I do I get this: Error: #Directive error - invalid #picaxe type
You shouldn't do, not unless you decided to specify '18m2+' which you don't need to do.

The compiler is automatically in 18M2+ mode and attempting to program a PICAXE-18M2 with code that uses PICAXE-18M2+ only features will simply display an error message.
 

BeanieBots

Moderator
So, I understand what's going on with the stack overflow (as described above) but is there a work-around? I'm just curious...some way to reset the Gosub stack with a well-placed POKE?
The RESET command will reset the gosub stack and also take you back to the start of your program.
However, there is no better solution than good programming practice.
 

bpowell

Senior Member
Thanks Beanie...I was thinking about the reset command as well.

It seems to me, last time I had a program with a gosub error, it was caught in the PE editor, never downloaded to the chip (I think...it's been a while)...so I suppose, although you could possibly "work around" the issue; the PE compiler may make it a moot point by refusing to download until you resolve the issue in code.

Nobody is saying to throw-out best practice...and the fervent defending of the "best practices" seems to be the theme de jour lately on this forum...I just enjoy trying to understand how things work. I appreciate the answer.
 

BeanieBots

Moderator
It would be a very impressive compiler that could catch all naughties relating to gosubs!
If you jump out of a gosub by using a goto, it will still download OK but could easily result in a stack error unless a return was met at some point.
Some poeple advocate that "goto" should NEVER be used at all and I was even reading about a version of BASIC aimed at education that has the command dissabled. Personally, I think there are perfectly valid times when goto is OK to use but then I started off with assembler where you'd be pretty lost without it. (old habits die hard).
 

rossko57

Senior Member
Following established "Good practice" is a way to avoid suprises. Here, one suprise has already come from the stack error. The next may come from any circumvention:
You and I don't know if the subroutine stack is also used behind the scenes for other purposes - serial comms or somesuch. Should your project grow and develop a nasty bug because the stack was being cleared, I would guess the experience would be more frustrating than educational?

It is true to say that just because Picaxe BASIC doesn't provide a command for XX that you shouldn't ever do it! But in this case it is very hard to see any advantage or gain in performance, legibility, or maintainability from using GOSUB with no RETURNs
 

KeithRB

Senior Member
Thanks Beanie...I was thinking about the reset command as well.

It seems to me, last time I had a program with a gosub error, it was caught in the PE editor, .
About the only thing it could catch is if you used GOSUB and had *no* RETURN at all in the program. Almost anything else is the equivalent of solving the halting problem.
 

hbl2013

Senior Member
PICAXE Basic does inherently support (not good practice though) a GOTO to exit a subroutine, because the stack space is circular and in effect resets the stack. It only goes badly wrong if you use GOTO in some instances and RETURN in others within the same structure and then expect it all to work correctly :)
Good to know this. I have a program where I "gosub" in a "main" program from one routine to another and I had a need to start all over again on command. I do it by checking the status of a button in the subroutine, and when the button is pushed, the routine is restarted at "main:". In this case I don't care where the stack is, I want to start all over again anyway. I was thinking about using a RESET in the subroutine, but might try a "GOTO main" to accomplish the task.
 

lbenson

Senior Member
Good to know this. I have a program where I "gosub" in a "main" program from one routine to another and I had a need to start all over again on command. I do it by checking the status of a button in the subroutine, and when the button is pushed, the routine is restarted at "main:". In this case I don't care where the stack is, I want to start all over again anyway. I was thinking about using a RESET in the subroutine, but might try a "GOTO main" to accomplish the task.
If you repeatedly exit code which has been entered with "gosub" using "goto" (without a "return"), you will eventually get a stack overflow. This is true even with "GOTO main"--there is nothing magic in picaxe basic about "main"--it is just another label, tho a commonly used one. "RESET" will start you over from the beginning (for better or worse) with the gosub stack reset.
 
Top