Book: "20 GOTO 10" By Steven Goodwin

A new book on retrocomputing.

About the book

Do you know what secret messages were hidden in Commodore BASIC? Why the highest score possible in Pac-Man is 3333360? That Steve Wozniak set the price of the Apple ][ computer at $666.66? Or why the Amstrad CPC 472 had an 8K chip that was never connected?

From 0 to 2147483647, and from Acorn Atoms to VIC-20s, 20 GOTO 10 takes us on an adventure through the history of retro computers and games consoles – one number at a time.

By following the ‘GOTO’ instructions at the end of each entry, you’ll create a unique journey through this treasure trove of forgotten geek lore and fascinating trivia. With any luck, you’ll discover the number used to grant infinite lives in Jet Set Willy on the Sinclair ZX Spectrum, the reason a single digit might require seven bytes of memory, and how – through numbers – we can understand more than just the internal workings of our favourite retro machines.


I hope this isn’t about Tristan Miller’s rather puckish submission to the TPUG Fall 2015 newsletter:

10 a=125708:gosub 20:a=33435700:gosub20:a=17059266:gosub 20:end
20 a=rnd(-a)
30 a=int(rnd(a)*22):if a then print chr$(a+64);:goto30
40 print:return

which prints:


It’s left as an exercise to the reader why this appears to find hidden messages.

I believe that was the Apple 1. Woz seems like the kind of guy who may have wished to set the Apple ][ price similarly, but it cost $1298 in its base configuration, due to factors beyond his control. Maybe he could have pulled some strings and made it $1111.11, but it didn’t happen.

I wonder which particular Basic version… It doesn’t work on CBM Basic 2:

But the other Easter egg is there…


BTW, a lesser known fact about WAIT 6502,1: the second argument is actually a multiplier.
Meaning WAIT 6502,4 will print the message four times, WAIT 6502,20 twenty times and so on. Even zero is a valid argument.



It is alleged to work on VIC-20, Commodore 64, Commodore 16, and Plus/4. I tried it on a PET and got three lines of nonsense. I tried it in Applesoft and got three lines of different nonsense, after replacing IF A THEN with IF A>0 THEN, because Applesoft insisted on storing the former as IF AT HEN.

1 Like

Actually, I didn’t know that, but tried it and it works.

Interestingly, when I first found out about it, it din’t appear to work, but corrupted my assembled image - because it was poking the characters directly into screen memory - at $8000 on a PET - which is the start of the code in my system. So I hacked the hack to use the generic character print code - which is why I see the lower case ‘a’ rather than the ‘!’ character due to the ‘PETSCI’ character set.


For anyone who wants to find another “secret message” in the C64 ROM:

If the argument of RND() is negative, we end up at the code at $E0D3, which just scrambles the argument. At this point, the argument is already in the floating point accumulator (FAC):

0061 ... exponent
0062 ... mantissa 1
0063 ... mantissa 2
0064 ... mantissa 3
0065 ... mantissa 4
0066 ... extracted sign bit
0070 ... rounding bits

The code at $E0D3 swaps bytes 4 & 1, and bytes 2 & 3 of the mantissa and realigns the floating point value as a positive number between 0 and 1:

                 swap FAC mantissa 4 and 1
E0D3  LDX $65    X <- mantissa 4
E0D5  LDA $62    A <- mantissa 1
E0D7  STA $65    A -> mantissa 4
E0D9  STX $62    X -> mantissa 1

                 swap FAC mantissa 2 and 3
E0DB  LDX $63    X <- mantissa 2
E0DD  LDA $64    A <- mantissa 3
E0DF  STA $63    A -> mantissa 2
E0E1  STX $64    X -> mantissa 3

                 clear sign of FAC (-> positive value)
E0E3  LDA #$00   load zero
E0E5  STA $66    store it as sign

                 handle exponent
E0E7  LDA $61    load FAC exponent
E0E9  STA $70    store it as rounding bits
                 (will be shifted in by NORMALIZE)
E0EB  LDA #$80   load $80 (bias for exponent: value < 1)
E0ED  STA $61    store it as FAC exponent

                 adjust and store return value
E0EF  JSR $B8D7  call NORMALIZE (realign and clean up FAC)
E0F2  LDX #$8B   set pointers for RND result
E0F4  LDY #$00   (RNDX: $008B)
E0F6  JMP $BBD4  round and store value of FAC there

Now reverse engineer your seed values…
(We’re still missing the code regarding how the rounding bits – containing the original exponent – are processed, but this is rather complex and lengthy. Maybe a blog post…)

Interestingly, the code for the PET ROM 1.0 looks identical, but doesn’t produce the same results (here, addresses align conveniently with the byte positions in FAC, starting at $B0 with the exponent, followed by the mantissa and the sign):

DF78  LDX $B4     swap mantissa 4 and 1
DF80  LDX $B2     swap mantissa 2 and 3
DF82  LDA $B3
DF84  STA $B2
DF86  STX $B3
DF88  LDA #$00    clear sign
DF8C  LDA $B0     load exponent
DF8E  STA $BF     store it as rounding bits
DF90  LDA #$80    set up new exponent
DF92  STA $B0
DF97  LDX #$DA
DF99  LDY #$00    (RNDX: $00DA)
DF9B  JMP $DAA6   copy result

The result is rather (as with PET BASIC 2/3 and 4):


Which suggests some differences in the floating point normalization? Maybe regarding rounding, where the original exponent is shifted in from the “rounding bits”?
(Again, an in-depth analysis is probably too lengthy for this thread.)

WAIT 6502,1 doesn’t work on Commodore PET BASIC 4.0. It locks up the system. Same on the C64 (BASIC V2)

I remember reading that when the PET came out and Commodore was showing it off, Bill Gates did that on the demo unit and Tramiel was not happy about it. So he made sure that it got removed in later versions.

It doesn’t work on PET BASIC 4.0 either.

It seems that the related code was removed (I recall reading about Commodore eventually removing this, too). Address 6502 is in the BASIC user area ($1966), which is probably just holding something like $AA, if it is not used by a program., causing the WAIT command to wait indefinitely, as nothing is going to change that value.

But WAIT 6502,170 ($AA = dec. 170) should at least return without locking the system.
(Be sure to test the value first by `PRINT PEEK(6502)'.)

Note that $666.66 retail is $500.00 wholesale (what Apple charged the store) plus a standard 1/3 markup.

But yes, Woz was fascinated with repeating numbers and had quite an adventure to get a telephone number with all 8s. He found out why this wasn’t the best idea when he kept getting calls from babies.

Works on C64 BASIC (aka CBM BASIC version 2), and if you add spaces to lines 10 and 30, Plus/4 (BASIC 3.5) and C128 (BASIC 7.0). Doesn’t seem to work on any of the PET Basics I’ve tried.

Best of all, it runs correctly on mist64/cbmbasic so you can brute-force find new messages at 5000× the speed of a C64.

I think, this was meant to designate the original PET version (where it does not work, see above). PETs became CBMs in Europe, since Philips had already claimed that name (for “Programm-Entwicklungs-Terminal”), and, I think, also elsewhere for the business version. So, in this sense, “CBM” doesn’t stand for Commodore in general, but for the CBM series computers, AKA PETs.

That’s interesting. I’m not particularly familiar with those – where are additional spaces required? Is this about “gosub20” and “goto30”?

You might have to fight an army of C64 owners over what constitutes “version 2” of Commodore’s BASIC, for it is written:

    **** COMMODORE 64 BASIC V2 ****



Commodore 6502 BASIC interpreters reporting a version above 2 (so PET 8032, C16, Plus/4, C128 and likely many others) didn’t accept source code without spaces between keywords. GOSUB20, accepted on a C64, was a syntax error on a Plus/4.

You may also might face a fight over what the BASIC with the following prompt on the PET was:




Depending, whether you start counting with the buggy pre-release BASIC for the PET 2001 or with the one that was actually first released, this is either BASIC 2 or BASIC 3 on the PET. Even in various Commodore documents we do find varying attributions, since these (at least in some instances) refer to this as “BASIC LEVEL II”, but, apparently, sometimes this is also used for the patched ROMs for the first release. (Moreover, for the upgraded 2/3 version, there is a source for just the editor, which has a 3.x version number. But I’m personally unsure, if this applies only to the editor or to the entire BASIC.) – In social media status terms: it’s complicated. :slight_smile:
(Personally, I go with and various others, using the 2.0 designation for this.)

But, I think, the difference is here really in “CBM BASIC 2” (or 3, → PET) vs “Commodore BASIC 2.0” (-> VIC-20, C64), which is based on the former.

PS: I don’t know the book, but this may be a worthy entry for numbers 1 to 3. :wink:

1 Like

I think guy Commodore hired to number its BASIC versions is the same one that decided Kaypro model numbers.

1 Like