Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 23:06 24 Apr 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : MMBasic core: bug in cmd_gosub()

Author Message
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3839
Posted: 04:35pm 21 Jan 2023
Copy link to clipboard 
Print this post

Peter, Geoff,

You may not care, but:

Currently:
void cmd_gosub(void) {
   if(gosubindex >= MAXGOSUB) error("Too many nested GOSUB");
   errorstack[gosubindex] = CurrentLinePtr;
   gosubstack[gosubindex++] = nextstmt;
   LocalIndex++;
   if(isnamestart(*cmdline))
       nextstmt = findlabel(cmdline);
   else
       nextstmt = findline(getinteger(cmdline), true);
   IgnorePIN = false;

   CurrentLinePtr = nextstmt;
}


IMO it should be:
void cmd_gosub(void) {
   if(gosubindex >= MAXGOSUB) error("Too many nested GOSUB");
   const char *return_to = nextstmt;
   if(isnamestart(*cmdline))
       nextstmt = findlabel(cmdline);
   else
       nextstmt = findline(getinteger(cmdline), true);
   IgnorePIN = false;

   errorstack[gosubindex = CurrentLinePtr;
   gosubstack[gosubindex++ = return_to;
   LocalIndex++;

   CurrentLinePtr = nextstmt;
}


This way if findlabel/findline fail (which they can) the stacks and the LocalIndex don't get clobbered.

Observation: I didn't expect LocalIndex and LOCAL variables to work with GOSUB ... I don't mind, I just thought LOCAL was part of the SUB/FUNCTION structured programming rather than the older GOSUB style. Note that in general there is scope for clobbering here because no setjmp is installed to recover if you are skipping errors and call GOSUB. I'll leave it as an exercise for the reader (or not), as GOSUB isn't really something I would ever use with MMBasic.

Best wishes,

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8574
Posted: 04:55pm 21 Jan 2023
Copy link to clipboard 
Print this post

Can you provide an example of how/where this matters and your change improves things?

From the manual FYI

  Quote  Obsolete Commands and Functions
Detailed Listing
These commands and functions are mostly included to assist in converting programs written for Microsoft
BASIC. For new programs the corresponding modern commands in MMBasic should be used.
Note that these commands may be removed in the future to recover memory for other features.
GOSUB target Initiates a subroutine call to the target, which can be a line number or a label.
The subroutine must end with RETURN.
New programs should use defined subroutines (i.e. SUB…END SUB).

Edited 2023-01-22 02:55 by matherp
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3839
Posted: 06:24pm 21 Jan 2023
Copy link to clipboard 
Print this post

  matherp said  Can you provide an example of how/where this matters and your change improves things?


Try this (demonstrated with MMB4W):
> list
Option Explicit On

foo()
End

Sub foo()
 Local x% = 1
 On Error Skip 1
 Gosub no_such_label
 x% = 2
 Print "Caught: " Mm.ErrMsg$
End Sub
> run
Caught: Error in line 9: Cannot find label
Error in line 10: X is not declared


Note that
 a) X is declared.
 b) if X hadn't been declared then its error should be the only one displayed before the program aborts.

Also try commenting out line 10 "x% = 2" to see:
> run
Caught: Error in line 9: Cannot find label
Caught: Error in line 9: Cannot find label


MMB4L which contains my suggested fix produces this output for both cases:
> run
Caught: Error in line 9: Cannot find label


The problem is because as-written cmd_gosub() updates the state as if the GOSUB had occurred before checking whether the GOSUB is even possible.

It's not really very significant for the majority of users (possibly ALL-1 users) and I wouldn't have encountered it myself but for the fact that I am trying to write comprehensive tests for labels before switching MMB4L to use hash lookup for them.

However I tell you about this stuff because I see it, and in this case I even have a simple fix for it, it's up to you and Geoff whether you act on it or not.

  thwill said  Note that in general there is scope for clobbering here because no setjmp is installed to recover if you are skipping errors and call GOSUB.


I'm no longer so sure about that, I think I was letting myself get carried away with speculation, I should restrict myself to errors I have actually experienced .

  matherp said  From the manual FYI ...


Yes, and makes sense, though if you removed them then you would lose compatibility with a host of legacy non-MMBasic programs.

Best wishes,

Tom
Edited 2023-01-22 04:37 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 06:41pm 21 Jan 2023
Copy link to clipboard 
Print this post

  thwill said  
  matherp said  From the manual FYI ...


Yes, and makes sense, though if you removed them then you would lose compatibility with a host of legacy non-MMBasic programs.

@Tom, in my opinion it's easy to replace with Sub/End Sub. For me "Gosub" is a dinosaur.

Best regards
Michael
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3839
Posted: 07:01pm 21 Jan 2023
Copy link to clipboard 
Print this post

  twofingers said  @Tom, in my opinion it's easy to replace with Sub/End Sub. For me "Gosub" is a dinosaur.


Absolutely, it's a dinosaur, and it would make life simpler if it was removed from MMBasic ... however in those mythical sunlit uplands where a young person is pried from their phone/tablet/VR headset/vape and persuaded there might be some merit in typing in some creaky old BASIC program it might happen to contain a GOSUB statement as it is pretty much a BASIC staple [pun intended].

Best wishes,

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 07:15pm 21 Jan 2023
Copy link to clipboard 
Print this post

I understand both sides, but I wouldn't be sad if it were removed.

 
Justplayin

Guru

Joined: 31/01/2014
Location: United States
Posts: 308
Posted: 07:58pm 21 Jan 2023
Copy link to clipboard 
Print this post

I wish people would stop trying to turn BASIC into C. I believe GOSUB is a critical part of BASIC.  BASIC is supposed to simple to use, which GOSUBs are and SUBs are not so much.  Yes I do use SUBs but I prefer GOSUB when I'm doing a quick bit of code.
I am not a Mad Scientist...  It makes me happy inventing new ways to take over the world!!
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 5712
Posted: 08:41pm 21 Jan 2023
Copy link to clipboard 
Print this post

Oh, I dunno - they are much of a muchness if you treat them similarly. It's just that you have the option of making SUBs more powerful - you don't need to.


'main routine
myloop:
 do something here
 GOSUB asubroutine
 'do something else
GOTO myloop

asubroutine:
 'do something here
RETURN

--- or ---

'main routine
DO
 'do something here
 dothisroutine
 'do something else
LOOP

SUB dothisroutine
 'do something here
END SUB


Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
Nimue

Guru

Joined: 06/08/2020
Location: United Kingdom
Posts: 367
Posted: 08:48pm 21 Jan 2023
Copy link to clipboard 
Print this post

  Justplayin said  I wish people would stop trying to turn BASIC into C. I believe GOSUB is a critical part of BASIC.  BASIC is supposed to simple to use, which GOSUBs are and SUBs are not so much.  Yes I do use SUBs but I prefer GOSUB when I'm doing a quick bit of code.


The debate between (precise) historical preservation of (programming) languages vs reinterpreting old languages in light of more modern paradigms is an interesting one.

Line numbers (either present or "hidden") are a good example.  When I learned to program in BASIC, I was never really aware of what the point of line numbers was, just that I needed to use them.   This persisted into my Spectrum / BBC Basic day - then QBasic rocked my world.  

BUT.... whilst Gosubbing to a "label" seemed so powerful, students ability to Return to a different part of the code than where it was called from pretty much reinvented spaghetti coding of the "GOTO" days. From what I remember back in the day students could not understand their own code 48hrs after it was written.  So when we embraced SUB and CALL that tidied that up.

Depending on the students (and teachers) I often use "LOGO" as a starting point for teaching "coding".  Interesting LOGO has many calls/functions that are often missing / removed depending on the interpretation. For example, OPENREAD filename -- yes, LOGO has file i/o but this is often left out - presumably in an attempt to make LOGO even more beginner friendly.  

My point (clumsily made) is that teaching "structured programming" concepts (if/then/else, iteration, subroutines/functions) is what matters, not how to actually implement in a specific language.

For the record - I've got no residual love for GOSUB.

Nimue
Entropy is not what it used to be
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 11:33pm 21 Jan 2023
Copy link to clipboard 
Print this post

FYI:
For me surprising: Gosub/Return is tree times faster than a Sub/End Sub.
I don't mean to say that it has practical relevance in every case or very often!

'Test for execution time Gosub vs Sub()
'--------------------------------------
Option explicit
Dim float  t, t_L, t_P
Dim integer i, loops = 100000

Timer =0
For i = 1 To loops

Next
t= Timer

Timer =0
For i = 1 To loops
GoSub L_test
Next
t_L= Timer-t

Timer =0
For i = 1 To loops
P_test
Next
t_P= Timer-t

Print
Print "Time for "loops " loops"
Print "empty loop   ||   Gosub   ||   Sub()"
Print t, t_L, t_P
End

'-----
Sub P_test' test procedure
End Sub 'end test procedure
'-----

L_test: ' label test gosub
Return ' return test gosub
'-----
End '******************************

Result:
Time for  100000 loops
empty loop   ||   Gosub   ||   Sub()
203.196         509.118         1437.068

Edited 2023-01-22 09:45 by twofingers
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3839
Posted: 11:41pm 21 Jan 2023
Copy link to clipboard 
Print this post

  twofingers said  FYI:
For me surprising: Gosub/Return is t[h]ree times faster than a Sub/End Sub ...


Nice observation.

SUB/END SUB does a great deal more book-keeping than GOSUB/RETURN because, for example, it needs to deal with the possibility of the SUB having parameters. Possibly it could be optimised ... possibly not.

Of course you are testing an extreme case, if you were actually doing anything in the "subroutine" the relative difference due to the overhead would be much less.

Best wishes,

Tom
Edited 2023-01-22 09:42 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 11:50pm 21 Jan 2023
Copy link to clipboard 
Print this post

  thwill said  Of course you are testing an extreme case, if you were actually doing anything in the "subroutine" the relative difference due to the overhead would be much less.

I see that exactly the same way!
Kind regards
Michael
 
Goksteroo
Senior Member

Joined: 15/03/2021
Location: Australia
Posts: 110
Posted: 06:53am 22 Jan 2023
Copy link to clipboard 
Print this post

Why the t_L= Timer-t and t_P= Timer-t since you have zeroed the timer before each test? Oops!

Add some work to each routine and you get more 'real world' results.

'Test for execution time Gosub vs Sub()
'--------------------------------------
Option explicit
Dim float  t, t_L, t_P
Dim integer i,pp, loops = 100000

Timer =0
For i = 1 To loops
pp=100
do
inc pp,-1
loop until pp<1


Next
t= Timer

Timer =0
For i = 1 To loops
GoSub L_test
Next
t_L= Timer

Timer =0
For i = 1 To loops
P_test
Next
t_P= Timer

Print
Print "Time for "loops " loops"
Print "empty loop   ||   Gosub   ||   Sub()"
Print t, t_L, t_P
End

'-----
Sub P_test' test procedure
pp=100
do
inc pp,-1
loop until pp<1
End Sub 'end test procedure
'-----

L_test: ' label test gosub
pp=100
do
inc pp,-1
loop until pp<1
Return ' return test gosub
'-----
End '******************************

gives these results....
Time for  100000 loops
empty loop   ||   Gosub   ||   Sub()
1528.6083      1524.8994    1549.1483
 
DaveJacko
Regular Member

Joined: 25/07/2019
Location: United Kingdom
Posts: 52
Posted: 11:00pm 23 Jan 2023
Copy link to clipboard 
Print this post

Loosely related to this thread..

has anyone else noticed that..
you accidentally code 2 subs with the same name,
MMBasic helps you, and throws an error - fab!

if you are thick like me, and type two line labels with the same name,
MMBasic just goes to the first one, no error!

this set my OBD2 ELM 327 reader project back quite a bit.
(is this called a hashing table thing issue?)

Thanks you lot !
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024