My 1983 VIC-20 Eggs game has a typo in the joystick routine

Down the VIC-20 joystick rabbit hole I go…

In my recently-recovered “first program I ever had published,” I noticed that the joystick was not working. I was testing on VICE (the Versatile Commodore Emulator) on a Mac. The code that I had to read the joystick was clearly something I found in a magazine or somewhere because I have no idea what it does:

30 POKE37154,127
35 J=(NOT((PEEK(37151)AND128)/8+(PEEK(37151)AND60)/4))+32
40 POKE37154,255

I suspected maybe a typo was introduced when VIC-NIC NEWS typed in my program to print it out for publication. I did some searching to see how you were supposed to read the VIC-20 joystick. I was surprised that the Commodore VIC-20 manual didn’t offer any code to do this.

I found an Atari Magazine page that simply said you used two PEEKs:

The VIC is designed to handle only one joystick, and it takes two bytes to control that joystick. In the VIC, location 37137 is PEEKed to read the joystick for the up, down, left, and fire button movements. Location 37152 is PEEKed to detect movements to the right.

To see just how easy it is to detect movement on the joystick, plug in your joystick, type in one of the following short programs, and then RUN. The programs simply PEEK the joystick control bytes, and then PRINT that reading to the screen.

For the VIC-20:

10 PRINT PEEK(37137), PEEK(37152) : GOTO 10
– Atari Magazine, issue 42

Curious. My code was not using either of those addresses. Either my code or this magazine had a typo.

I tried those two PEEKs in VICE and noticed only the fire button was making the values change. This matched the VICE joystick display also showing only the fire button working.

I then thought the joystick I was using (a USB joystick that came with my TheVIC20) might have an issue. I tested it using a joystick test program, and it appeared to read all directions correctly.

A bit of more searching and I found that the Mac version of VICE has has an issue with joysticks not working since sometime in 2022:

https://sourceforge.net/p/vice-emu/bugs/1785

Even if my code was correct (to be determined), I would not be able to test it on the Mac version of VICE.

I then tried running Eggs from the Windows version of VICE and was able to see the joystick work — but, it was using UP and DOWN to move the cup left and right. Odd. I know I must have intended for it to use left and right, because my typed instructions clearly stated:

“Use joystick to move the cup left and right to catch falling eggs.”

– Eggs instructions.

While technically it didn’t say “move the joystick left or right,” I think it would have been a very odd choice for me to use “up” to mean left, and “down” to mean right, in a game like this.

This led me back to suspecting a typo in my joystick code. I went back to those two PEEKs from Atari magazine and tested them on the Windows version of VICE. This time, it seemed to change for all directions other than right. It gave values for Up, Up-Left, Left, Left-Down and Down, as well as the button. Clearly, this code was wrong (or maybe the Windows version of VICE also had an issue).

I decided to figure out where my joystick code came from. I searched for this code:

J=(NOT((PEEK(37151)AND128)/8+(PEEK(37151)AND60)/4))+32

…and archive.org actually had a single match! It was found in Issue #8 of The TORPET (Toronto Pet) on page 31. This Commodore PET publication had a section about the then-new VIC-20. For fun, here is the entirety of their VIC-20 section:

VIC NOTES
by John O’Hare

As of Mid October, 15,000 VIC’s have been sold in Japan, 15,000 more in the U.S., and, though not yet available in Europe, orders are accumulating fast. -JS

If you really want a color PET, Commodore suggests you consider buying a VIC as a peripheral for your PET! Seriously, it uses Upgrade BASIC V2, is cassette compatible, and has excellent color compared to Apple, and Atari. -JS

Some VIC POKE locations are:

51 decrements when key hit. 204 cursor on flag.
56 pointer to top of memory. 211 cursor column.
67 holds 1 when key hit. 214 cursor row.
197 value of key depressed. 36865 border position (vertical).
145 flag for left shift key. 36864 border position (horizontal).
245 detects special keys. 36869 pointer to character generator.
240= norm. 255 = 7168.

To read a joystick on a VIC (standard ATARI joystick works), use the following subroutine:

9000 POKE 37154,127
9010 JO= (NOT((PEEK(37152)AND128) /8+( PEEK(37151)AND60)/4)) +32
9020 POKE 37154,255
9030 RETURN

JO Values returned are: 1 = up, 17 = up and rt, 16= right, 18 = down and r t,
2 = down, 6 = down and Ift, 4= left, 5= up and Ift.

VIC GAME REVIEWS

VIC GAMES PAC, $25 from Creative Software. 3 VIC arcade games: VIC Trap, Seawolf, and Bounce Out. Seawolf and Bounce Out are machine language, with very fast action. Very good games, but could be better if they used Hi Res graphics. -JOH

– The TORPET, Issue #8, Page 31

Wow, early days – reporting only 15,000 VIC-20s had been sold at that point!

In my code, I check for the value of 18 to move the bucket left (“ifj=18thenb=b-1”) and a value of 17 to move the bucket right (“ifj=17thenb=b+1”). That matches the values for up and down in this TORPET article! I guess I really did mean for up and down to move left and right.

But I have a theory about this, which I will get to in a moment…

The thing I immediately noticed was this code used two different memory locations, while my program (as listed in VIC-NIC NEWS) used the same memory location twice. I consulted a VIC-20 memory map to see what all the various addresses are for:

  • 37137 – Port A output register: (PA0) Bit 0=Serial CLK IN, (PA1) Bit 1=Serial DATA IN, (PA2) Bit 2=Joy 0, (PA3) Bit 3=Joy 1, (PA4) Bit 4=Joy 2, (PA5) Bit 5 = Lightpen/Fire button, (PA6) Bit 6=Cassette switch sense, (PA7) Bit 7=Serial ATN out
  • 37151 – Port A (Sense cassette switch)
  • 37152 – Port B output register: keyboard column scan, (PB3) Bit 3 =cassette write line, (PB7) Bit 7 =Joy 3
  • 37154 – Data direction register B

The TORPET code looked wrong since one of the locations it was using had nothing to do with the joystick.

The Atari Magazine looked better since it used the two locations that mentioned joystick – 37137 and 37152. 37137 contains three joystick directions (Joy 0-2) plus the button. 37152 contains the final joystick direction (Joy 3). However, running this code doesn’t show anything changing for “right”.

I think the extra POKEs the code I used (and the code in The TORPET used) may explain that. It seems you have to POKE to change the purpose of some of this bits. 37154 is a data direction register for Port B (37152), and before my joystick code I POKE it to 127 which is a binary pattern of 01111111. After doing the joystick PEEK, the code POKEs it to 255, which is 11111111. I am suspecting that that 0 (bit 7) indicates read of whatever is hooked to that bit — which is Joy 3 (PB7). It looks like you turn it to 0, then you can PEEK it, then it gets set back to a 1 after.

I added this to the Atari Magazine code:

5 POKE 37154,127
10 PRINT PEEK(37137), PEEK(37152) : GOTO 10

…and now the second value changes when the joystick is pressed right. Huzzah!

It looks like the first byte reads up, down, left and fire, and the second is right. The POKE is only used to toggle the right bit on and off to do a read. It should get restored after (set back to 1) but in my quick demo, I didn’t bother to do that and I found I could not type certain keys. Aha, that explains “keyboard column scan.” That bit must be used to read a keyboard column, normally, and can be switched to read the joystick instead.

I will have to explore that more, later, but for now I can clearly see an issue with my code. By using only one of those locations, it is never reading the I/O bit that represents the “right” direction.

The code listed in The TORPET is incorrect:

9000 POKE 37154,127
9010 JO= (NOT((PEEK(37152)AND128) /8+( PEEK(37151)AND60)/4)) +32
9020 POKE 37154,255
9030 RETURN

37151 does not have anything to do with the joystick. This code takes the PEEK of that location and ANDs it with 60 (bit pattern 00111100) telling me it is expecting to use four bits. That matches the four joystick bits that are at 37137:

  • Bit 2 – (PA2) Bit 2=Joy 0
  • Bit 3 – (PA3) Bit 3=Joy 1
  • Bit 4 – (PA4) Bit 4=Joy 2
  • Bit 5 – (PA5) Bit 5 = Lightpen/Fire button

I think The TORPET got it wrong, and that second PEEK should be 37137.

The PEEK of 37152 seems okay. It ANDs it with 128 (bit pattern 10000000) which matches looking for that bit for the joystick:

  • Bit 7 – (PB7) Bit 7 =Joy 3

I suspect Atari Magazine just omitted the POKE needed to have the bits come from the joystick port, and The TORPET just had the wrong address listed. Since I had never heard of The TORPET, I expect I got my code from one of the few magazines I had at the time (Family Computing, Compute or Compute’s Gazette).

I changed my code to use 37137, but it still did not work! Now I could only move to the right by pressing Up+Right.

I think it is time to figure out what this code is trying to do.

Figuring out what this code is trying to do

Let’s start with the “correct” TORPET code and dissect what it is doing:

J=(NOT((PEEK(37152)AND128) /8+( PEEK(37137)AND60)/4)) +32
  • 37137 – Port A output register: (PA0) Bit 0=Serial CLK IN, (PA1) Bit 1=Serial DATA IN, (PA2) Bit 2=Joy 0, (PA3) Bit 3=Joy 1, (PA4) Bit 4=Joy 2, (PA5) Bit 5 = Lightpen/Fire button, (PA6) Bit 6=Cassette switch sense, (PA7) Bit 7=Serial ATN out
  • 37152 – Port B output register: keyboard column scan, (PB3) Bit 3 =cassette write line, (PB7) Bit 7 =Joy 3

PEEK(37152) uses “AND 128” to get just the high bit (bit 7) of Port B, which is the “right” joystick direction. It will either be 0 (no right) or 128 (right). It then divides by 8 which effectively shifts that bit over three places to the right:

10000000 - 128 (original reading)
00010000 - 16 (after the divide by 8)

PEEK(37137) uses “AND 60” to mask out the four bits in the middle. If they were all “on” (you can’t do that with a normal joystick), it would read 00111100. It then divides by 4 which effectively shifts those bits over two places:

00111100 - 60 (original reading if all joystick inputs were "on")
00001111 - 15 (after the divide by 4)

It adds those two values together, so this code is creating a new value of 5 bits, where those five bits represent the joystick switches for up, down, left, right and fire (00011111). From looking at the bit pattern, this new byte represents the following:

  • Bit 0 – Up
  • Bit 1 – Down
  • Bit 2 – Left
  • Bit 3 – Fire
  • Bit 4 – Right

Next I wondered why the program uses “NOT.” From checking the PEEKs, I see that when a joystick switch is not on, it shows a 1. When it is activated, it shows a 0. The NOT is used to invert that logic:

If no switches are pressed, the value is 31 (00011111). NOT flips the bits, so that value becomes 11100000. (Actually, I think it is represented as 16-bits, so 11111111111000000.) I believe when you use AND/OR/etc. the value is treated as a 16-bit signed value (-32768 to 32767) with the high bit representing negative. There result is the 31 becomes a -32. This explains the “+32” at the end.

The result is a value that is 0 if no switches are pressed. All of this match (dividing, adding, NOTing) does make the routine slow so maybe we’ll play with speeding it up sometime…

Using this newly created bit pattern, the BASIC program could use specific values to represent a direction (up), or two directions at the same time (up+left). My game did not use the fire button, but I think the way you would check that would be to use AND on the JO value to look for that specific bit, that way the rest of the value could be anything (up+fire, up+left+fire, nothing+fire, etc.).

Here is a test program for the “fixed” TORPET code:

10 rem fixed torpet code
20 gosub 9000
30 if jo and 1 then print "up ";
40 if jo and 2 then print "down ";
50 if jo and 4 then print "left ";
60 if jo and 8 then print "fire ";
70 if jo and 16 then print "right";
80 if jo<>0 then print
90 goto 20
9000 poke 37154,127
9010 jo=(not((peek(37152)and128)/8+(peek(37137)and60)/4))+32
9020 poke 37154,255
9030 return

Though, a mystery I Have yet to understand, is why do so many VIC-20 joystick references say you can also use 37152 and 37151? This code also works:

9010 jo=(not((peek(37152)and128)/8+(peek(37151)and60)/4))+32

Anyone know?

Now … why did I use Up and Down in my original Eggs? I think this is because the joystick code I had was broken and never returned a value for right. This was because it was PEEKing the same byte twice instead of the second byte being the one that had the “right” switch! I probably couldn’t get it to work, so I just used UP and DOWN.

Using this “fixed” code, I could now alter my program to work like I originally intended:

0 rem 2024/2/11 fixed joystick
1 print"{clear}{down*3} eggs"
2 print"{down*3} by allen huffman"
3 print"{down*2}vic-nic news july 1983"
4 forj=1to5000:next
5 poke36879,8:printchr$(147)
10 b=8130:m=3:sc=0
15 e=int(rnd(1)*20)+7703
20 printchr$(19);chr$(5);"score:";sc
25 pokeb,21:pokee,81
30 poke37154,127
35 j=(not((peek(37152)and128)/8+(peek(37137)and60)/4))+32
40 poke37154,255
45 ifj=0then80
50 pokeb,32
55 ifj=4thenb=b-1
60 ifj=16thenb=b+1
65 ifb=8120thenb=b+1
70 ifb=8141thenb=b-1
75 pokeb,21
80 pokee,32
85 e=e+22
90 pokee,81
95 ife=>8120ande=bthen110
100 ife=>8120ande<>bthen115
105 goto25
110 sc=sc+10:pokee,32:goto15
115 m=m-1:pokee,32
120 ifm=0then135
125 goto15
135 fort=1to5:printchr$(17):next
140 fort=1to6:printchr$(32);:next
145 printchr$(158);"game over"
150 print"{down*2}play again? y/n"
155 geta$:ifa$="y"thenrun
160 ifa$="n"thenend
165 goto155

And now, while I still wonder why so many VIC articles disagree on how to read the joystick, at least I can move left and right in my 1983 classic VIC-20 game Eggs…

Until next time…

3 thoughts on “My 1983 VIC-20 Eggs game has a typo in the joystick routine

  1. Sean Patrick Conner

    I converted the addresses you gave into hexadecimal, and it clued me in to what might be going on. The following should be a table of two columns (I hope)—hex, then decimal:

    9111 37137
    911F 37151
    9120 37152

    On the Coco, you have two PIAs, one at $FF00 and the other at $FF20, but the way the hardware is addressed, you can also address both PIAs at multiple of four addresses, so $FF00, $FF04, $FF08, … $FF1A and then the second one at $FF20, $FF24, $FF28 … $FF3A. Looking at the addresses above, I think it’s a similar effect.

    Reply
      1. Sean Patrick Conner

        Something like that. Due to the hardware decoding of the address lines, there might be multiple addresses that still trigger the hardware.

        Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.