22
\$\begingroup\$

I felt nostalgic recently, and decided to install a Commodore-64 emulator. C-64 BASIC 2.0 is the first language I ever programmed in... some 20 years ago (yes, C-64 was already well beyond deprecated by then).

Implementing a simple in such a limited language, and un-learning pretty much everything I learned in at least the last decade to write code in a procedural paradigm with GOTO and line numbers, was much more fun (and tricky!) than I had imagined.

Here's the code - I limited the output to 15 values, so as to be able to see the full sequence on-screen.

10 GOSUB 100
20 GOSUB 1000
99 END
100 REM CLEAR SCREEN
110 PRINT CHR$(147)
120 RETURN
200 REM MODULO
210 LET MOD% = V%-INT(V%/FB%)*FB%
220 RETURN
1000 REM INIT VARIABLES
1010 LET FIZZ$ = "FIZZ"
1011 LET BUZZ$ = "BUZZ"
1020 LET FIZZ% = 3
1021 LET BUZZ% = 5
1030 LET MIN% = 1
1031 LET MAX% = 15
1100 PRINT FIZZ$ + ":" + STR$(FIZZ%)
1101 PRINT BUZZ$ + ":" + STR(BUZZ%)
1102 PRINT FIZZ$ + BUZZ$ + ":" + STR$(FIZZ%*BUZZ%)
1105 PRINT
2000 REM ACTUAL FIZZBUZZ LOOP
2010 FOR X = MIN% TO MAX%
2015 LET RESULT% = STR$(X)
2020 LET FB% = FIZZ%*BUZZ%
2021 LET V% = X
2024 GOSUB 200
2025 IF MOD%=0 THEN LET RESULT$=FIZZ$+BUZZ$ : GOTO 2050
2030 LET FB% = FIZZ%
2031 GOSUB 200
2035 IF MOD%=0 THEN LET RESULT$=FIZZ$ : GOTO 2050
2040 LET FB% = BUZZ%
2041 GOSUB 200
2045 IF MOD%=0 THEN LET RESULT$=BUZZ$ : GOTO 2050
2050 PRINT RESULT$
2090 NEXT X
2099 RETURN

Output:

Commodore-64 emulator showing fizzbuzz program variables and output

This will sound very weird, but it's actually a pretty big concern with C-64 code, so don't laugh (ok, laugh, but it's not a joke): how's my line numbering?

I tried to write the code in such a way that it would be easy to extend into prompting the user for MIN% and MAX% values, but also for FIZZ% and BUZZ%; I was also concerned with implementing a "modulo" subroutine, since I knew that logic would have to be used in 3 places.

I lost quite a lot of the language's capabilities over the years (the biggest challenge was really to stop thinking in terms of objects and methods for a while). Is there something in the language that could help improve this code?

Other than that, any inconsistencies or weirdness in sight?

\$\endgroup\$
  • \$\begingroup\$ No you didn't... Wait...Yep, you sure did lol. \$\endgroup\$ – Phrancis Sep 3 '15 at 3:02
  • 5
    \$\begingroup\$ @glampert I believe he doesn't use sequential numbers so he doesn't have to change every line number if he wants to add more lines later. \$\endgroup\$ – 23fc9a62-56de-47fb-97b4-737890 Sep 3 '15 at 3:29
  • 2
    \$\begingroup\$ @glampert the problem with sequential numbers is that it casts the code into cement and makes it impossible to change or extend it. \$\endgroup\$ – Mathieu Guindon Sep 3 '15 at 3:29
  • 1
    \$\begingroup\$ Uhum, yep, it makes sense, thanks for clarifying! \$\endgroup\$ – glampert Sep 3 '15 at 3:31
  • 1
    \$\begingroup\$ Only two letters in a variable name are significant. Long names help readability, but I wonder if that outweighs the risk of not noticing that two different names are for the same variable? \$\endgroup\$ – Hurkyl Sep 3 '15 at 4:43
14
\$\begingroup\$

First off, thank you for reminding me of the good old days !

BUG alert

Well, it is not really a bug, but as you know the % suffix indicates that the variable will be an integer

2010 FOR X = MIN% TO MAX%
2015 LET RESULT% = STR$(X)
2020 LET FB% = FIZZ%*BUZZ%

but you are assigning the result of STR$() which is a string to it. Be happy that commodore basic just forgive such things.

So changing RESULT% to RESULT$ will help, but nevertheless this STR$() method is called to often because if the modulo returns 0 the value is overwritten. So placing this on the line above the PRINT RESULT$ will make it more shiny.

Neither FIZZ% nor BUZZ% will change so calling LET FB% = FIZZ%*BUZZ% inside the loop is a bad idea. This FB% variable should be declared and initialized where the other variables are declared and initialized.


LET

The LET comand to assign values to variables can be omitted because you can assign values to variables without it. In addition if you omit them you will save some memory and reduce the execution time.


DEF FN and FN

Defining and calling a "function", will reduce some of your GOSUB calls and will make your code more pretty.

So I would suggest to define 3 functions like so

5 DEF FN FIZZMOD(V) = V-INT(V/FIZZ%)*FIZZ%
6 DEF FN BUZZMOD(V) = V-INT(V/BUZZ%)*BUZZ%
7 DEF FN FBMOD(V) = V-INT(V/FB%)*FB%

Style is important in a procedurale language. With the extensive use of GOTO and GOSUB it is very important to distinguish between the parts of the program.

By adding an "empty" REM in front and after the "defining" REM of a "sub" it is easier to grasp what it is about. It then looks like you would group logical parts by a new line which unfortunately can't be done by only a line number.

Like so

200 REM 
201 REM MODULO
202 REM 
210 LET MOD% = V%-INT(V%/FB%)*FB%
220 RETURN

EDIT

Take into account the very good hint by @psmears given in the comments

Another way to do this (which can look cleaner than a REM with no text) is to leave a line with just a ":" on it.


Using much more REM statements at the top indicating what for instance GOSUB 100 will do


Applying this would result in

 1 REM GOSUB 100 > CLEAR SCREEN
 2 REM GOSUB 1000 > INIT VARIABLES
 3 REM GOSUB 1100 > FIZZ BUZZ
 4 REM
 5 DEF FN FIZZMOD(V) = V-INT(V/FIZZ%)*FIZZ%
 6 DEF FN BUZZMOD(V) = V-INT(V/BUZZ%)*BUZZ%
 7 DEF FN FBMOD(V) = V-INT(V/FB%)*FB%
 9 REM
10 GOSUB 100
20 GOSUB 1000
30 GOSUB 1100
99 END
100 REM
101 REM CLEAR SCREEN
102 REM
110 PRINT CHR$(147)
120 RETURN
200 REM
1000 REM 
1001 REM INIT VARIABLES
1002 REM
1010 FIZZ$ = "FIZZ"
1011 BUZZ$ = "BUZZ"
1020 FIZZ% = 3
1021 BUZZ% = 5
1022 FB% = FIZZ% * BUZZ%
1030 MIN% = 1
1031 MAX% = 15
1032 RETURN
1090 REM
1091 REM FIZZ BUZZ 
1092 REM
1100 PRINT FIZZ$ + ":" + STR$(FIZZ%)
1101 PRINT BUZZ$ + ":" + STR(BUZZ%)
1102 PRINT FIZZ$ + BUZZ$ + ":" + STR$(FB%)
1105 PRINT
2000 REM ACTUAL FIZZBUZZ LOOP
2010 FOR X = MIN% TO MAX%
2025 IF FN FBMOD(X)=0 THEN RESULT$=FIZZ$+BUZZ$ : GOTO 2050
2035 IF FN FIZZMOD(X)=0 THEN RESULT$=FIZZ$ : GOTO 2050
2045 IF FN BUZZMOD(X)=0 THEN RESULT$=BUZZ$ : GOTO 2050
2049 RESULT$ = STR$(X)
2050 PRINT RESULT$
2090 NEXT X
2099 RETURN

the code lines which aren't used anymore or have been moved

2020 FB% = FIZZ%*BUZZ%
2021 LET V% = X
2024 GOSUB 200

2030 REM LET FB% = FIZZ%
2031 REM GOSUB 200  

2040 REM LET FB% = BUZZ%
2041 REM GOSUB 200

2015 RESULT$ = STR$(X)  

btw, I didn't test the resulting program because the emulator won't let me load a file. Pasting code isn't working either. I tested the MOD stuff and it worked.

\$\endgroup\$
  • 2
    \$\begingroup\$ +1 for leaving empty lines to emphasise the start of subroutines. Another way to do this (which can look cleaner than a REM with no text) is to leave a line with just a ":" on it. \$\endgroup\$ – psmears Sep 3 '15 at 9:53
3
\$\begingroup\$

Just for grins and giggles for anyone who's old like me, below is a TI BASIC version of the OP's code. Note that TI BASIC (and even TI Extended BASIC) didn't have a concept of integer arithmetic. It was all floating point. Also, the language itself wasn't just interpreted, like most BASICs of the day, but it was interpreted by an interpreter - that is - the BASIC interpreter was written in a language called GPL, which itself was run under the GPL interpreter in the console. Further, users' programs were stored in a port-mapped memory called VDP which was read or written through a single memory address one byte at a time. This lead to the language's notorious slowness compared to its contemporaries. History lesson-slash-rant over :)

10 GOSUB 100
20 GOSUB 1000
99 END
100 REM CLEAR SCREEN
110 CALL CLEAR
120 RETURN
200 REM MODULO
210 LET MOD = V-INT(V/FB)*FB
220 RETURN
1000 REM INIT VARIABLES
1010 LET FIZZ$ = "FIZZ"
1011 LET BUZZ$ = "BUZZ"
1020 LET FIZZ = 3
1021 LET BUZZ = 5
1030 LET MIN = 1
1031 LET MAX = 15
1100 PRINT FIZZ$ & ":" & STR$(FIZZ)
1101 PRINT BUZZ$ & ":" & STR$(BUZZ)
1102 PRINT FIZZ$ & BUZZ$ & ":" & STR$(FIZZ*BUZZ)
1105 PRINT
2000 REM ACTUAL FIZZBUZZ LOOP
2010 FOR X = MIN TO MAX
2015 LET RESULT$ = STR$(X)
2020 LET FB = FIZZ*BUZZ
2021 LET V = X
2024 GOSUB 200
2025 IF MOD<>0 THEN 2030
2026 LET RESULT$=FIZZ$&BUZZ$
2027 GOTO 2050
2030 LET FB = FIZZ
2031 GOSUB 200
2035 IF MOD<>0 THEN 2040
2036 LET RESULT$=FIZZ$
2037 GOTO 2050
2040 LET FB = BUZZ
2041 GOSUB 200
2045 IF MOD<>0 THEN 2050
2046 LET RESULT$=BUZZ$
2047 GOTO 2050
2050 PRINT RESULT$
2090 NEXT X
2099 RETURN

and Heslacher's answer translated:

 1 REM GOSUB 100 > CLEAR SCREEN
 2 REM GOSUB 1000 > INIT VARIABLES
 3 REM GOSUB 1100 > FIZZ BUZZ
 4 REM
 5 DEF FIZZMOD(V) = V-INT(V/FIZZ)*FIZZ
 6 DEF BUZZMOD(V) = V-INT(V/BUZZ)*BUZZ
 7 DEF FBMOD(V) = V-INT(V/FB)*FB
 9 REM
10 GOSUB 100
20 GOSUB 1000
30 GOSUB 1100
99 END
100 REM
101 REM CLEAR SCREEN
102 REM
110 CALL CLEAR
120 RETURN
200 REM
1000 REM 
1001 REM INIT VARIABLES
1002 REM
1010 FIZZ$ = "FIZZ"
1011 BUZZ$ = "BUZZ"
1020 FIZZ = 3
1021 BUZZ = 5
1022 FB = FIZZ * BUZZ
1030 MIN = 1
1031 MAX = 15
1032 RETURN
1090 REM
1091 REM FIZZ BUZZ 
1092 REM
1100 PRINT FIZZ$ & ":" & STR$(FIZZ)
1101 PRINT BUZZ$ & ":" & STR$(BUZZ)
1102 PRINT FIZZ$ & BUZZ$ & ":" & STR$(FB)
1105 PRINT
2000 REM ACTUAL FIZZBUZZ LOOP
2010 FOR X = MIN TO MAX
2025 IF FBMOD(X)<>0 THEN 2035
2026 RESULT$=FIZZ$&BUZZ$
2027 GOTO 2050
2035 IF FIZZMOD(X)<>0 THEN 2045
2036 RESULT$=FIZZ$
2037 GOTO 2050
2045 IF BUZZMOD(X)<>0 THEN 2049
2046 RESULT$=BUZZ$
2047 GOTO 2050
2049 RESULT$ = STR$(X)
2050 PRINT RESULT$
2090 NEXT X
2099 RETURN
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.