Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 08:57 01 Aug 2025 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 : Most elegant MMBasic way to ......

     Page 1 of 2    
Author Message
hitsware2

Guru

Joined: 03/08/2019
Location: United States
Posts: 719
Posted: 08:07am 16 Aug 2020
Copy link to clipboard 
Print this post

..... Do this ?
my site
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3292
Posted: 09:00am 16 Aug 2020
Copy link to clipboard 
Print this post

Check "Obsolete Commands and Functions" in the user manual.
Geoff Graham - http://geoffg.net
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 2170
Posted: 09:58am 16 Aug 2020
Copy link to clipboard 
Print this post


ON x GOTO/SUB 100,200,300,400...


=


Select Case x
   Case 1
       'equivalent code for ON x = 1
   Case 2
       'equivalent code for ON x = 2
   Case 3
       'equivalent code for ON x = 3
   Case 4
       'equivalent code for ON x = 4
   ...
End Select


Select makes it really easy to trap values of x that you are not expecting or want to take a generic action for (Case Else). Consider comparing a value for the ascii value for printable charcters... you would have to have ON x {tons of cryptic line numbers} where as a single Case 32 To 126 does that, and a Case Else catches everything outside this range. Stupid example but you get the point.   Here is a real-world example from a string edit routine I use daily:


Select Case Asc(x$)
   Case 13
       StrEdit$=a$:Exit Function
   Case 127,8
       If Len(a$)>0 Then
           a$=Left$(a$,Len(a$)-1):SStr a$,h,v
       EndIf
   Case 32 To 126
       If Len(a$)<q Then a$=a$+x$:SStr a$,h,v
   Case Else
       'ignore
End Select


Select Case, like ON x, works just like a big list of IF statements but looks nicer.

As a rule, GOSUB is deprecated (archaic) in just about every modern basic. You can only pass arguments by using global variables and it relies on line numbers/labels instead of extending the language with nicely named procedures. It is maintained as a legacy statement but to be avoided if possible... don't think I have used one (where options exist) for 40 years. You should use Sub or Function (if you want something back).

GOTO is the same and should be avoided as it can result in an uncontrolled exit from a structure (FOR/SELECT/WHILE/SUB/FUNCTION etc. - even GOSUB) which can cause memory leaks and weird stuff with the stack. I don't know if this applies to MMBasic, but can't see how it doesn't... if you constantly call a GOSUB and then GOTO the line that calls it, the stack will fill with unused RETURN addresses.

That said, There is a tendency to stab yourself trying to maintain structure when a simple GOTO would do. I try to avoid it but if it is within an "encapsulated" block of code and for a short distance, then I don't really have a problem with it. But as I said, I do *try* to avoid it.

At the end of the day, write your code how you want but I always strive to improve.  

my 2p
Edited 2020-08-16 20:32 by CaptainBoing
 
capsikin
Guru

Joined: 30/06/2020
Location: Australia
Posts: 341
Posted: 10:26am 16 Aug 2020
Copy link to clipboard 
Print this post

  CaptainBoing said  

ON x GOTO/SUB 100,200,300,400...


=


Select Case x
   Case 1
       'equivalent code for ON x = 1
   Case 2
       'equivalent code for ON x = 2
   Case 3
       'equivalent code for ON x = 3
   Case 4
       'equivalent code for ON x = 4
   ...
End Select



I imagine the ON x GOTO being faster since it's only one line, but I don't know if it's true.
  Quote  

Select makes it really easy to trap values of x that you are not expecting or want to take a generic action for (Case Else). Consider comparing a value for the ascii value for printable charcters... you would have to have ON x {tons of cryptic line numbers} where as a single Case 32 To 126 does that, and a Case Else catches everything outside this range. Stupid example but you get the point.  

As a rule, GOSUB is deprecated (archaic) in just about every modern basic. You can only pass arguments by using global variables and it relies on line numbers/labels instead of extending the language with nicely named procedures. It is maintained as a legacy statement but to be avoided if possible... don't think I have used one (where options exist) for 40 years. You should use Sub or Function (if you want something back).

GOTO is the same and should be avoided as it can result in an uncontrolled exit from a structure (FOR/SELECT/WHILE/SUB/FUNCTION etc. - even GOSUB) which can cause memory leaks and weird stuff with the stack. I don't know if this applies to MMBasic, but can't see how it can't... if you constantly call a GOSUB and then GOTO the line that calls it, the stack will fill with unused RETURN addresses.


I've seen this bug using GOTO out of a SUB. But I think C lets you GOTO out of a loop or switch/case (SELECT CASE equivalent). Not so easy in an interpreted language though.

Testing ... MMBASIC doesn't seem to mind jumping out of a FOR loop many times.
  Quote  

That said, There is a tendency to stab yourself trying to maintain structure when a simple GOTO would do. I try to avoid it but if it is within an "encapsulated" block of code and for a short distance, then I don't really have a problem with it. But as I said, I do *try* to avoid it.

At the end of the day, write your code how you want but I always strive to improve.  

my 2p
 
capsikin
Guru

Joined: 30/06/2020
Location: Australia
Posts: 341
Posted: 10:40am 16 Aug 2020
Copy link to clipboard 
Print this post

I expected one of these tests to fail - test 1 getting a stack overflow or test 2 being disallowed like in a compiled language.


goto test2
'test one - works fine
test1:
for n=1 to 1000000
 for i=1 to 200
   if (n mod 500 = 0) THEN print n,i
   if i > 3 THEN GOTO loopend
 next i
loopend:
next n

'test 2 - also works fine
test2:
for n=1 to 1000000
 for i=1 to 200
   if (n mod 500 = 0) THEN print n,i
 if i > 3 THEN next n
 next i
next n


I bet conditional NEXT could be even more fun and confusion than GOTO. And I wonder about conditional FOR.
 
mkopack73
Senior Member

Joined: 03/07/2020
Location: United States
Posts: 261
Posted: 11:10am 16 Aug 2020
Copy link to clipboard 
Print this post

There is NEVER EVER a good reason to use a goto. Have we learned NOTHING over the last 40+ years? Anything you think you need a goto to achieve I guarantee you I can find a way to do without it.
 
darthvader
Regular Member

Joined: 31/01/2020
Location: France
Posts: 87
Posted: 12:39pm 16 Aug 2020
Copy link to clipboard 
Print this post

  mkopack73 said  There is NEVER EVER a good reason to use a goto. Have we learned NOTHING over the last 40+ years? Anything you think you need a goto to achieve I guarantee you I can find a way to do without it.


So true  

But it's not 40+ years , 30 years ago in university , it was not allowed to use GOTO.
If you do , you got a note of 0 , it means , you loose a full year and can retake your chance next semester ... he he he ...

  Quote  I guarantee you I can find a way to do without it.


It's sometime tricky but again , it's true  

Cheers.
Theory is when we know everything but nothing work ...
Practice is when everything work but no one know why ;)
 
hitsware2

Guru

Joined: 03/08/2019
Location: United States
Posts: 719
Posted: 03:35pm 16 Aug 2020
Copy link to clipboard 
Print this post

Thank You !  

  Geoffg said   "Obsolete"


That's why I didn't find it  
my site
 
Poppy

Guru

Joined: 25/07/2019
Location: Germany
Posts: 486
Posted: 04:17pm 16 Aug 2020
Copy link to clipboard 
Print this post

  mkopack73 said  There is NEVER EVER a good reason to use a goto.


But there is!

Good for debugging if you just want to break out intentionally, of course actually not for final coding.

But I think "old" stuff must not get lost, it can and should simply be still available paralelly for all those who intentionally want to use it ... for what reason ever.

GOTO is great!


... at least for
20 GOTO 10

... and don´t forget to warn little Mira!


Andre ... such a GURU?
 
vegipete

Guru

Joined: 29/01/2013
Location: Canada
Posts: 1132
Posted: 06:19pm 16 Aug 2020
Copy link to clipboard 
Print this post

  mkopack73 said  There is NEVER EVER a good reason to use a goto.

All this hating on GOTO. Show me a processor that doesn't have a machine code equivalent to GOTO. The STM32 chips have a bunch of branch instructions that goto the targeted memory location. PIC32 chips have branches and jumps, as does the 6502 and 68000. The Z80 has jumps... they all do.

It is our little minds that can't handle the resulting spaghetti formed from pages of ill-planned goto-filled code. If the code is correct, the processor will faithfully wend its way through all that spaghetti. That's a really big if though.

When I am writing assembly code, I gleefully use branch and jump instructions, saving memory and sometimes execution speed.

But with high level languages, there is no need for goto. MMBasic is such a high level language. If you think you are really stuck and need a goto, it is time to re-examine your program logic. And don't forget the EXIT and CONTINUE commands. These two commands can replace many tempting uses of goto.
Edited 2020-08-17 04:23 by vegipete
Visit Vegipete's *Mite Library for cool programs.
 
Tinine
Guru

Joined: 30/03/2016
Location: United Kingdom
Posts: 1646
Posted: 04:33pm 17 Aug 2020
Copy link to clipboard 
Print this post

Assembly programmers are well respected but they won't get very far without jmp, je, jne, etc.

Change the spelling to GOTO and all of a sudden you're not a real programmer.

I have yet to test this with MMBASIC but the ON x Goto/Gosub in some dialects, creates a jump table. This is WAY faster than evaluating every "case" or "if".

I use On x GOSUB in MMBASIC all the time.

I guess it's what we call state machine programming. I am not going to be using MMBASIC for a few days so it would be interesting to learn from Geoff or Pete whether ON x GOSUB does in fact create a jump table.

Regards,

Craig
Edited 2020-08-18 02:36 by Tinine
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 2170
Posted: 05:52pm 17 Aug 2020
Copy link to clipboard 
Print this post

[VegiPete] agree and [Tinine] agree.

see this snippet:

'Web API parsing

 LANRxLN$=GetIO$(LAN,1)
 IF LANRxLN$="" OR LANRxLN$="T/O" THEN GOTO Main

 CL LANRxLN$,0
 IF MID$(LANRxLN$,2,8)=",CONNECT" THEN
   Chan=VAL(LANRxLN$)
   IF Chan>=0 AND Chan<=4 THEN
     FlagSet Conn+Chan
     CL "Inbound connect channel "+STR$(Chan),0
     GOTO IPEXIT
   END IF
 END IF

 IF MID$(LANRxLN$,2,7)=",CLOSED" THEN
   Chan=VAL(LANRxLN$)
   IF Chan>=0 AND Chan<=4 THEN
     IF FlagTest(Conn+Chan)=0 THEN A$="Abnormally " ELSE A$=""
     CL "Closed channel "+STR$(Chan),0
     FlagRes Conn+Chan
     GOTO IPEXIT
   END IF
 END IF

 IF LEFT$(LANRxLN$,5)="+IPD," THEN
   
   DO:a$=GetIO$(LAN,2):LOOP UNTIL a$="Connection: Keep-Alive"
   
   Chan=VAL(MID$(LANRxLN$,6,1))
   IF Chan>4 THEN GOTO IPDerrEXIT
   IF FlagTest(Conn+Chan)=0 THEN GOTO IPDerrEXIT 'channel not open
   
   z=INSTR(LANRxLN$,":GET /") '+IPD,0,nnn:GET /smssend?tel=077?msg=XYZ HTTP/1.1
   IF z=0 THEN GOTO IPDerrEXIT
   LANRxLN$=MID$(LANRxLN$,z+6)
   
   z=INSTR(LANRxLN$," HTTP")
   IF z=0 THEN GOTO IPDerrEXIT
   LANRxLN$=LEFT$(LANRxLN$,z-1)

   args=Split(Replace$(LANRxLN$,"+"," "),"?")

   SELECT CASE SP$(1)
     CASE "smssend"
       IF args <3 THEN GOTO IPDerrEXIT
       Tel$="":Msg$=""
       
       FOR n=2 TO args
         a$=Trim$(URLDecode$(MID$(sp$(n),5)))
         SELECT CASE LEFT$(ucase$(SP$(n)),4)
           CASE "TEL="
             Tel$=a$
           CASE "MSG="
             Msg$=a$
         END SELECT
       NEXT
       IF Tel$="" OR Msg$="" THEN GOTO IPDerrEXIT

       CL "O/B SMS "+Tel$+":"+Msg$,0
       z=GSMSend(Tel$,Msg$)
       IF z=0 THEN
         B$="OK: Message Queued for sending":GOTO IPOKEXIT
       ELSE
         B$="ERROR: Gateway failed to send message {"+STR$(z)+"}"
         GOTO IPOKEXIT
       END IF

     CASE "stat"
       'IF z=>8 AND Z<=11 THEN
       'other API calls go here...
       'z=INSTR(LANRxLN$,":GET /whatever?")
       'IF z<>0 THEN
       'etc...

       '  B$="OK: STAT="+LL1$+LL2$
       '  GOTO IPOKEXIT

   END SELECT

   GOTO IPDerrEXIT 'default action IF we don't find an API call

 END IF

 GOTO IPEXIT

IPDerrEXIT:
 B$="ERROR: Malformed request {"+LANRxLN$+"}"

IPOKEXIT:
 PRINT #LAN,"AT+CIPSENDEX="+STR$(Chan)+",255"
 PAUSE 100

 PRINT#LAN,B$+"\0"
 PAUSE 100

 PRINT#LAN,"AT+CIPCLOSE="+STR$(Chan)
 FlagRes Conn+Chan

IPEXIT:

Trying to eliminate all those GOTOs with "structured methods" when you are knee-deep in multiple condition checks would be a royal PitA and not add anything to the code other than a "badge of honour". Worth mentioning that this code exists in the main thread of the code, not in a SUB so EXIT is not an option.

As I said, when it makes lfe easy (and fast in this case) I'll do it... it's just not the first blade out of the swiss army knife.
Edited 2020-08-18 03:54 by CaptainBoing
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 4044
Posted: 05:53pm 17 Aug 2020
Copy link to clipboard 
Print this post

  vegipete said  All this hating on GOTO.

See Goto Considered Harmful.

  vegipete said  Show me a processor that doesn't have a machine code equivalent to GOTO. The STM32 chips have a bunch of branch instructions that goto the targeted memory location. PIC32 chips have branches and jumps, as does the 6502 and 68000. The Z80 has jumps... they all do.

Yes, they're adequate for getting things done and very cheap.  Most code is written in high-level languages to avoid the GOTO etc issues.

  vegipete said  It is our little minds that can't handle the resulting spaghetti formed from pages of ill-planned goto-filled code.

Not just our little minds.  It's realistically impossible to prove program correctness when they're spaghetti-like.

John
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 2170
Posted: 06:34pm 17 Aug 2020
Copy link to clipboard 
Print this post

a little code. Times in mS (natch)

I tried to avoid timing anything but the switch and made the workload as identical as I could. Select is around twice as fast as a On x GoSub but it's all relative and tweakable


Dim Integer n,t,tt

for n=1 to 10
timer=0
Select case n
case 1
t=timer
tt=tt+t
?n,t
case 2
t=timer
tt=tt+t
?n,t
case 3
t=timer
tt=tt+t
?n,t
case 4
t=timer
tt=tt+t
?n,t
case 5
t=timer
tt=tt+t
?n,t
case 6
t=timer
tt=tt+t
?n,t
case 7
t=timer
tt=tt+t
?n,t
case 8
t=timer
tt=tt+t
?n,t
case 9
t=timer
tt=tt+t
?n,t
case 10
t=timer
tt=tt+t
?n,t
End Select
Next
?tt,
?"with Select":?

tt=0
for n=1 to 10
timer=0
On n GoSub l1,l2,l3,l4,l5,l6,l7,l8,l9,l10
Next
?tt,
?"with Gosub":?

End

l1: t=timer
tt=tt+t
?n,t
return
l2: t=timer
tt=tt+t
?n,t
return
l3: t=timer
tt=tt+t
?n,t
return
l4: t=timer
tt=tt+t
?n,t
return
l5:t=timer
tt=tt+t
?n,t
return
l6: t=timer
tt=tt+t
?n,t
return
l7: t=timer
tt=tt+t
?n,t
return
l8: t=timer
tt=tt+t
?n,t
return
l9: t=timer
tt=tt+t
?n,t
return
l10:t=timer
tt=tt+t
?n,t
return


and the output... (prints are not part of the timing). Slowest and fastest CPU speeds on a '170.

At 48Mhz, most of the switches are beyond the resolution of the timer.


cpu 5
> RUN
1       1
2       1
3       1
4       1
5       2
6       1
7       2
8       2
9       3
10      2
16     with Select

1       2
2       2
3       3
4       3
5       3
6       3
7       3
8       3
9       4
10      3
29     with Gosub

> cpu 48
> RUN
1       0
2       0
3       0
4       0
5       0
6       0
7       0
8       0
9       0
10      1
1      with Select

1       0
2       0
3       0
4       1
5       0
6       0
7       1
8       0
9       0
10      1
3      with Gosub


Edited 2020-08-18 04:49 by CaptainBoing
 
hitsware2

Guru

Joined: 03/08/2019
Location: United States
Posts: 719
Posted: 07:33pm 17 Aug 2020
Copy link to clipboard 
Print this post

> Select is around twice as fast as a On x GoSub

That sorta make sense looking @


ON int(rnd*3)+1 GOSUB a,b,c
a: do choice #1 : RETURN
b: do choice #2 : RETURN
c: do choice #3 : RETURN


versus


SELECT CASE int(rnd*3)

  CASE 0 : do choice #1
  CASE 1 : do choice #2
  CASE 2 : do choice #3

END SELECT

Because everything goes in one direction ...
Instead of having the RETURN
my site
 
Tinine
Guru

Joined: 30/03/2016
Location: United Kingdom
Posts: 1646
Posted: 08:33pm 17 Aug 2020
Copy link to clipboard 
Print this post

Many thanks for doing the leg work Cap'n.

I guess I'm going back to Select Case.

This is good news, actually because I'm switching from the MX470 to the MX170 and so nothing is lost.
Makes no real difference, actually but it just makes me feel better to know that I have greater efficiency.  

Regards,

Craig
 
vegipete

Guru

Joined: 29/01/2013
Location: Canada
Posts: 1132
Posted: 08:57pm 17 Aug 2020
Copy link to clipboard 
Print this post

  CaptainBoing said  see this snippet:

'Web API parsing
...


Trying to eliminate all those GOTOs with "structured methods" when you are knee-deep in multiple condition checks would be a royal PitA and not add anything to the code other than a "badge of honour". Worth mentioning that this code exists in the main thread of the code, not in a SUB so EXIT is not an option.

As I said, when it makes lfe easy (and fast in this case) I'll do it... it's just not the first blade out of the swiss army knife.


I count 14 GOTOs in that snippet. One is "GOTO Main" which I can't comment on. All the remaining ones involve exiting the snippet through one of 3 routes. I can see relatively easily replacing them with structure and likely one state variable.

However, I don't disagree with their use. The usage is in my view logical and sensible. The purpose of the code is to test a bunch of conditions in a particular order and exit once a condition has been met. That to me is NOT spaghetti-like.

It's stuff like this:
10 W=500:Dim F(W):P=1:A=3
20 F(P)=A:P=P+1:If P>W Then GoTo DONE
30 A=A+2:X=1
40 S=A/F(X):If S=Int(S) Then 30
50 X=X+1:If X<P And F(X)*F(X)<=A Then 40
60 GoTo 20

DONE:
that is spaghetti and should be expunged.
Visit Vegipete's *Mite Library for cool programs.
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 2170
Posted: 09:50pm 17 Aug 2020
Copy link to clipboard 
Print this post

  hitsware2 said  >
That sorta make sense ... Because everything goes in one direction ... Instead of having the RETURN


The timing is so that only the switch logic is compared - the Return is not counted. I specifically didn't want to count the Return in the timings as that would be an obvious tilt in favour of Select Case (read; cheating-ish). I only wanted to see how long it took to make the decision - not do the resultant work.  

You are right Return would add more time, but then so would skipping the remaining Case statements - hence my interest in the switch only.

  vegipete said    
My point precisely - it *could* be done (of course), but as I have a number of points where I might want to bail and each is nested a level further in than the preceding one, writing that as structured would increase code size and not really gain anything except a "I avoided GOTO today" badge  

I rarely use them, but when I do I don't see it as an excuse to just write 1970s unfathomable code. Yeah they still have a place.  
Edited 2020-08-18 07:58 by CaptainBoing
 
Tinine
Guru

Joined: 30/03/2016
Location: United Kingdom
Posts: 1646
Posted: 10:45pm 17 Aug 2020
Copy link to clipboard 
Print this post

Yes! The moral of the story is don't write spaghetti code!

Don't blame GOTO.

I work over in the Parallax Propeller world and I am one of the few users of PropBasic (very cool). It is best described as a translator rather than a compiler and GOTO translates directly to a jmp. Sure I have alternatives but they all generate extra instructions.

Even their C compilers can't touch PropBasic  
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 2170
Posted: 07:17am 18 Aug 2020
Copy link to clipboard 
Print this post

interesting. So in the "translated" code, are GOTO lines actual memory addresses?

I use Great Cow Basic (which is a compiler) for some PIC based projects and I often have a browse through the assembler output just for kicks (I am starting to think there may be some truth behind Mrs. Boing's claim that I am "sad"). Anyway, in the asm output, GOTO & GOSUB are pretty much precisely what you would write if you were doing the assembler yourself - jumps and calls (PIC assembler actually uses GOTO instead of the more usual m/c instructions of JMP, BRA etc...). This tickles my efficiency nerve as I know that executes as fast as possible. I am tempted to try the above bit of code on a stock 18Fxxx and have a look and see what the code is like.
 
     Page 1 of 2    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025