GOTO abuse

Anything Sinclair ZX Basic related; history, development, tips - differences between BASIC on the ZX80 and ZX81
Post Reply
Shaun_B
Posts: 474
Joined: Wed Apr 22, 2009 10:22 am

GOTO abuse

Post by Shaun_B »

I was wondering about doing the following sort of code in one line:

Code: Select all

10 PRINT"PRESS 1 TO GOTO 100 OR PRESS 2  TO GOTO 200"
20 IF INKEY$="1" THEN GOTO 100
30 IF INKEY$="2" THEN GOTO 200
40 GOTO 20
100 REM ....
200 REM ....
Or at least the lines at 20 through to 40 inclusive into a single line. I came up with this method:

Code: Select all

10 PRINT"PRESS 1 TO GOTO 100 OR PRESS 2  TO GOTO 200"
20 GOTO 20+(INKEY$="1")*80+(INKEY$="2")*180
100 REM ....
200 REM ....
Okay so you have to do a bit of maths first (and I'm sure that you're all better at maths than I am) but it seems to work unless there's a better way of doing the same thing that I haven't realised?

Regards,

Shaun.
poglad
Posts: 133
Joined: Mon Mar 24, 2014 3:11 pm
Location: Aberdeen, Scotland

Re: GOTO abuse

Post by poglad »

Which is "better" depends on your aims. Personally I'd do it the first way - in structured programming this is a "case", and the ZX81 doesn't offer that directly so you have to use GOTO to do it - a perfectly good use of GOTO. The first way retains the appearance of the list of operations based on a value. If you were translating a program from C to run on a ZX81, the first way would be easiest. You'd probably store INKEY$ in a variable first though, and then test that.
Shaun_B
Posts: 474
Joined: Wed Apr 22, 2009 10:22 am

Re: GOTO abuse

Post by Shaun_B »

poglad wrote:Which is "better" depends on your aims. Personally I'd do it the first way - in structured programming this is a "case", and the ZX81 doesn't offer that directly so you have to use GOTO to do it - a perfectly good use of GOTO. The first way retains the appearance of the list of operations based on a value. If you were translating a program from C to run on a ZX81, the first way would be easiest. You'd probably store INKEY$ in a variable first though, and then test that.
As a developer, I agree with you. I work with Javascript, which gets minimised and obfuscated down to one line (albeit of multiple functions and variables, of course).

I like these BASIC tricks no matter how 'dirty' they are as it's something different from the day job. After all, there are no coding standards that I'm aware of on the ZX81 and other 8-bits, which to me means freedom to do what I like ;-)

Regards,

Shaun.
poglad
Posts: 133
Joined: Mon Mar 24, 2014 3:11 pm
Location: Aberdeen, Scotland

Re: GOTO abuse

Post by poglad »

Absolutely! :D
Shaun_B
Posts: 474
Joined: Wed Apr 22, 2009 10:22 am

Re: GOTO abuse

Post by Shaun_B »

For completeness, here's another example (NOT TESTED):

Code: Select all

10 PRINT"ENTER 1 TO GOTO 100 OR ENTER 2 TO GOTO 200"
20 INPUT A
30 GOTO A*100
40 STOP
100 REM ....
110 STOP
200 REM ...
Then there is a problem: Human error... so you need to add a check:

Code: Select all

25 IF A<1 OR A>2 THEN GOTO 20
So, you still have a problem, if a key is pressed that isn't numeric, it'll throw an error. So change it again to accommodate for this:

Code: Select all

20 INPUT A$
25 IF A$<>"1" AND A$<>"2" THEN GOTO 20
30 GOTO A*VAL A$
Progress? Well INPUT isn't a very nice command on the ZX81 in my opinion, so we're back to the start of the thread :-P

(I may have the logic incorrect on the last example, so it needs testing).

Regards,

Shaun.
poglad
Posts: 133
Joined: Mon Mar 24, 2014 3:11 pm
Location: Aberdeen, Scotland

Re: GOTO abuse

Post by poglad »

Well, in a structured pseudo-code it would be:

Code: Select all

do
    k$=inkey$
loop while k$<>"1" and k$<>"2"

case k$
    "1": blah blah blah for case 1
    "2": blah blah blah for case 2
end case
So my first attempt might be:

Code: Select all

100 LET K$=INKEY$
110 IF K$<>"1" AND K$<>"2" THEN GOTO 100
120 IF K$="1" THEN GOTO 1000
130 IF K$="2" THEN GOTO 2000
140 STOP

1000 blah blah blah for case 1
1010 blah blah blah for case 1
1020 GOTO 140

2000 blah blah blah for case 2
2010 blah blah blah for case 2
2020 GOTO 140
You could do a calculated GOSUB to avoid having extra GOTOs back to the end of the case list. Then it would be:

Code: Select all

100 LET K$=INKEY$
110 IF K$<>"1" AND K$<>"2" THEN GOTO 100
120 GOSUB 1000*VAL K$
130 STOP

1000 blah blah blah for case 1
1010 blah blah blah for case 1
1020 RETURN

2000 blah blah blah for case 2
2010 blah blah blah for case 2
2020 RETURN
That's a bit nicer. If K$ doesn't happen to be always 1 or 2, but could be other keys such as A and B, you could do:

Code: Select all

100 LET K$=INKEY$
110 IF K$<>"A" AND K$<>"B" THEN GOTO 100
120 IF K$="A" THEN GOTO 150
130 IF K$="B" THEN GOTO 170
140 GOTO 180
150 GOSUB 1000
160 GOTO 180
170 GOSUB 2000
180 STOP
This has the advantage that if you want to do a fall-through from one case to another (like in C), so that the case for A also falls through into the case for B, you would just remove line 160. Even though there are now a few GOTOs, it's still very nicely structured and maintainable, with no nasty surprises or "spaghetti coding". You can remove line 120 and line 140 in fact, as you know that it can only be A and B in this case, but I've just coded the "case" generically from the pseudo-code.

Another way of handling more generic keys would be to set up a string of options, e.g. O$="ABXH12", and then have a routine that found the index position of the key in that string. So if you pressed "A", it would return 1, and if you pressed "1" then it would return 5. You could then either use the index to calculate the line number to use, or use it to look up the line number in an array. That approach might be quite good if there were lots of different menus in your application, and you wanted them to use the same code to read the keypress and jump to the correct subroutine. You could use the same code, but just redefine O$ and the line number array depending on which menu you were in. That would be overkill for the example you gave, though.
Post Reply