PDA

View Full Version : jumping out of a gosub reminder



longpole001
- 1st February 2020, 04:39
hi guys ,

i am reviewing some old code and updating stucture

I have a need to do a gosub that has a number of program error checks in it

the gosub is called from many places in code to check if the fuction is allowed depending on the hardware status which is read in the error check sub

if no errors are found with the hardware test groups selected in the error check routine then returns to the called section

How ever if it finds an error then it has a direct goto to the service subs and commands branch off to deal with the required error condition , never returning to the orginal routine

this then breaks out of the orginal "return" to calling sub

it not great to do but i can see any way to avoid this , in the structure , and ii know after gosub , a return not done can cause issues

i can avoid the " return " by putting in a series ofg flag so it knows where it was orginally called from but that not a really practical and like to avoid

what i am i forgetting or better way to aproach this

HenrikOlsson
- 1st February 2020, 08:17
There's no real problem to use GOTO (or nested GOSUBs for that matter) from a subroutine to which you've jumped with GOSUB as long as you eventually end up at a "matching" RETURN but you CAN NOT allow the call stack to simply grow because eventually the thing will crash. If the condition you describe happens rarely you might not see the effects of it until after a (very) long time but trust me, sooner or later it will crash.

Don't missunderstand, there can be one GOSUB and twenty RETURN statements in a program, or the other way around, no problems but you can not keep pusing return addresses on the call stack without eventually taking them off.

Instead of that direct goto you mention, why can't you do that as a subroutine?

longpole001
- 1st February 2020, 10:19
"Don't missunderstand, there can be one GOSUB and twenty RETURN statements in a program, or the other way around, no problems but you can not keep pusing return addresses on the call stack without eventually taking them off."

i not seen it crash , but the code did have several goto's to a few other subroutines

most old subs in this code had a error checking of "status test" , to do would be called from many places in the code , some times within several subroutines deep before the error checking routine was called
then if the error check was positive in the routine it would then do a goto another subroutine to a menu and the option it would then show for the error never to return to orginating sub

so from this it would mean the deeper in of other subroutines the error check routine was called would be left on the stack as far as i can see ???

not a good , but never saw a a crash , but maybe that error did not occur perhaps , but seems like its not a thing to be done -


i am reviweing to so that error subs are a goto from the outset , but this then requires a return location from each differnat location its called from , and the more error checkdone in the subrouint the more options of goto creates , but this is really messy stuff

what i would like is the ability to have my return and eat it too , lol , but cant see how that would work as clearing the last "return" address , still leaves all the other return address that may have been stacked prior to the sub that had the goto in it , too bad i cant request a fluch of the stack of the now un need returns - but that just bad programing

just have to redo all the error checking so that is one level or returns to the calling routine and returns i think

mpgmike
- 2nd February 2020, 01:17
Here is a simple exercise to review the Stack:


10: Main:
20: DO
30: IF PORTB.1 = 1 THEN
40: GOSUB CheckIn
' Stack Contains 40
50: ENDIF
60: LOOP

70: CheckIn:
80: IF PORTB.2 = 1 THEN
90: GOTO Verify
' Stack Still Contains 40
100: ENDIF
110: RETURN

120: Verify:
130: IF PORTB.1 = 1 THEN
140: RETURN
' This RETURN jumps to the address in the Stack + 1 (address), so the point of RETURN = 50
150: ELSEIF PORTB.2 = 1 THEN
160: GOTO NameCalling
' Stack Still Contains 40
170: ENDIF
180: RETURN

longpole001
- 2nd February 2020, 01:25
thanks mike

i am going though this code slowly to find any goto's that have found their way into sub's , fixed about 10 or so that if the error condition happend wold have acted on the goto , and left the stack with the address sitting on there from the gosub

mpgmike
- 2nd February 2020, 16:37
I commonly use GOTO to jump from one Sub to another when there is nothing left to do in the first Sub. This keeps the Stack from building up, and reduces execution time. Working with the Nextion TFT screens, sending UART commands must terminate with $FF $FF $FF, which I often add in a subroutine. Since that is the last part of the operation, I GOTO FfFfFf: from the last part of the Send_Data: routine, then RETURN from FfFfFf:. It saves a couple jumps, which may involve ASM BANKSELs.

Know that you are doing it intentionally, know where your Stack is pointing so you know where you ultimately RETURN to, and it can work to your advantage.

pedja089
- 2nd February 2020, 22:11
I had similar ideas, but give up on them. I use something like this:

Gosub xx : If err=1 then goto yy

XX:
if something to triger going to err sub then
err=1 ' goto in different sub after retun
else
err=0 ' return and continue normally
endif
But if you must have gosub, without return, you can look up in datasheet of PIC, POP command. It should pop up stack, and free one space.


The TOS value is pulled off the return
stack and is discarded. The TOS value
then becomes the previous value that
was pushed onto the return stack.
This instruction is provided to enable
the user to properly manage the return
stack to incorporate a software stack

If I understand POP and PUSH commands, you could do something like this

If err=1 THEN
@ POP
GOTO ErrHandler
ENDIF

mpgmike
- 3rd February 2020, 14:54
Don't use both. What was printed in the above post will GOTO a new address, then erase the RETURN address. This will generate a Stack Underflow condition and the PIC will "exhibit unpredictable behavior". Just use GOTO and leave the Stack alone.

pedja089
- 3rd February 2020, 18:28
But if I understand correctly he want to goto and never return.
So you need to pop stack to prevent overflow.

mpgmike
- 4th February 2020, 00:18
Then yes, POP the Stack. Not sure when such an approach might be considered good programming practice, though.

richard
- 4th February 2020, 00:55
Not sure when such an approach might be considered good programming practice

using goto to control program flow = spaghetti code [hard to unravel ,hard to modify ]

spaghetti code with stack popping to undo dead subs ?

franken code ? [as in Frankenstein ]
you would need good comments with a flow diagram and a state diagram at hand to unravel it

pedja089
- 4th February 2020, 13:27
franken code ?

Not necessarlly, Lot of hi level programing language have this already implemented. eg in .net: On Error GoTo, Try, catch, etc...
Same could be done for HW. If something external happened then you need unconditionally goto something, and start program from known place.

It is not easy to implement, by any means. But it could very useful.
What longpole001 have exactly on his mind, I can't tell...

Art
- 4th February 2020, 13:40
You could count your gosub nesting level in another variable.
Increment for every gosub, and decrement for every return.
You don’t know about PBP's own gosubs, but they are supposed to be the compiler’s problem, and aren’t your responsibility.

So you would know if the value wasn’t zero when you thought it should be.

pedja089
- 4th February 2020, 14:57
When compiler gosub, it will always return. Unless you use ISR to modify stack, which I wouldn't recommend.

richard
- 4th February 2020, 21:38
Not necessarlly, Lot of hi level programing language have this already implemented. eg in .net: On Error GoTo, Try, catch, etc...

hmm, in python at least a sub with a "try" still returns , even if the processing aborts and executes an "try" error routine.
i see no goto or stack popping going on.

a couple of python subs with try


def process_d(tname,q): while exitflg==0:
dx=ser.readline()
try :
rt='s'

ff= string.rstrip(dx)
int(ff,16) ##non hex chr will cause ValueError
if dx[0]=='A':
rt='w'

#print dx
ts=str(datetime.datetime.now())[:19]
r_x=[ts,dx,rt]
queueLock.acquire()
q.put(r_x)
queueLock.release()
except ValueError:
#print dx
dx=[]


def bag_reading ():
global readings
bag_data_time =datetime.datetime.now()
if len(readings)>0:

try:
conn=sqlite3.connect('/media/CORSAIR/wh2.db')
curs=conn.cursor()
curs.executemany("INSERT INTO log values((?),(?),(?),(?))", (readings))
# commit the changes
conn.commit()
conn.close()
print 'write ',len(readings),' ',readings[-1][0]
readings=[]
bag_data_time += datetime.timedelta(minutes=5)
except sqlite3.OperationalError as err :
print 'db busy',err
bag_data_time += datetime.timedelta(minutes=1)
return bag_data_time

longpole001
- 5th February 2020, 00:46
I had similar ideas, but give up on them. I use something like this:
Code:

Gosub xx : If err=1 then goto yy

XX:
if something to triger going to err sub then
err=1 ' goto in different sub after retun
else
err=0 ' return and continue normally
endif



Pedja idea of this is what i was wanting to for error condiions, but the number of options of error check is about 20 or so


what i had was several sub routines that specific error checking set flag and byte values given for the error
each error had a goto another sub routine to show the menu and selection of options to deal with the error found .

The large error sub was called from many locations, at times 3 or 4 subs deep in code to check the status of hw to see if that option was ok ,
if it was ok it would return , but if the error was need to be serviced then a direct would goto the " service routines and the options presented " NEVER TO return to the location , it would then run the error service options , a restart was often done after the error was checked / fixed

what i have done now is not have any goto , just close all the returns back to start , then runs the error service routines - its probably nicer , but a lot more work and code at some points that , just doing a goto and service the error

pedja089
- 5th February 2020, 07:39
You could just goto error handler if you found error. Then make just one exit of it, and exit with @ Reset.
This will reset PIC and clear stack. Same way as if you power off then on.

longpole001
- 6th February 2020, 05:47
yes some of the options force a reset