Multiplayer is fun! Often, games get even more fun when more players get involved.
From that point of view, it’s a bit depressing that the C64 only offers two control ports – thus only two joysticks can be connected.
Sometimes, you could try to use the keyboard to support more players but that gets crowded pretty fast. Also, one might argue that joystick players have quite an unfair advantage over keyboard players.
You could try to map buttons to the analog inputs (like the psx64 does) but this can lead to complicated circuits, too (and you’d need games that support it).
With that in mind, I was pretty surprised walking into a room one day and seeing four players at a game of Bombmania (Dynablaster/Bomberman-port for C64) – each having a regular joystick. How did they do it? Was some kind of hidden Voodoo involved?
As it turns out, they were using a four-player-adapter. This device is put into the C64 user port and contains two joystick connectors. Supported software then knows how to access it at the user port.
The user port is worth mentioning – as it demonstrates a mentality that seems to have died out: It allows the user to easily connect the computer to other (even home-built) hardware and access it easily.
One complete port (Port B; 8 pins) of one internal I/O-Chip (CIA2) is directly passed to the outside world. You can e.g. attach an LED, and within two pokes you can flip it between light and non-light. This can be done directly after switching on the C64 – no need to wait a minute for Windows (or Linux or MacOs) and starting additional hardware-access-software or searching for drivers.
In the modern PC world, this seems very uncommon. Interfacing home-brewn-hardware has gotten increasingly difficult. For quite some time, you could use the parallel port but that is dying, too. A long time ago, I bought an I/O-card. It was ISA (easier to access). I’m not sure whether those are available for PCI or PCIe today, but they should be more complicated to program for (the whole driver stuff) and I guess, they’re not as cheap easier (my I/O-48 was about 50 DM). I can’t even find anything like it at Conrad or Reichelt. The only thing coming close is an USB experimenting interface (buildkit) for 30 EUR.
The fun thing is: You might buy a cheap AVR (ATMega8 seldom costs more than 3 EUR) and then interface the hardware and report back to the PC via RS232… Oh wait, RS232 is dying, too.
But if you ever wondered what the GPIO-pins in your wireless router or on the Raspberry Pi are for: It’s this. They just are not present on standard PCs anymore – and I guess this is caused by less people wanting to mess with hardware and interfacing it with their computers anymore.
But in the “good old times”, it was fairly common to connect all kinds of stuff to the user port and having two more joysticks is one of the more “innocent” stuff.
I even built one of those 4-player-adapters myself, then (it was one of those “looks nice, gotta have one”-moments):
Note that I included a reset button (C64 had no standard one)… Once I solder to the port, it was easy to include it.
Unfortunately, this is a long time ago and I mostly forgot everything technical about the adapter. So when I stumbled over this mess:
I still remembered that it very likely was one of those adapters (it has a 74LS257 IC) – but no details. Details, however, would have been very nice because this device definitely looks broken (bent pins, disconnected wires) and I wanted to bring it back into action.
So I decided to learn again what this is all about – and document it on the way.
First, let’s take a look at the user port:
I admit: This is not really helping. So let’s look at the user port technically:
This is the pin order when looking onto the user port from outside (like in the photo). Of course, it is mirrored when looking onto the connector plug but as you will look at the soldering side when soldering, this evens out again. If you’re lucky, your plug will have the letters and numbers imprinted.
Note that the official numbering does not use the letters I and G. I guess that those are letters one might easily mistake for numbers so they just left them out.
Also note that there is space between 1/A and 2/B and again between 10/L and 11/M. User port connectors can use bridges to prevent that someone inserts the board or cartridge or whatever upside-down – or uses enough raw force to create new spaces between 2/B and 3/C as well as between 11/M and 12/N. Most people should register that the connector doesn’t fit before they destroy their hardware. It also doesn’t hurt to put a “This side is UP!” or “NO! this side does not belong up! You shouldn’t be able to read it when inserting it into your Commodore” sticker on finished hardware.
As the connectors you can buy often don’t have any bridges, it is strongly recommended to insert some yourself. I’ve found the discarded parts from plastic binders to work fairly well; also parts from “Rasterband” have been recommended to me.
The upper side has some interesting stuff (like Counters, serial Ports, AC-output) but for the adapter, 1/12 (GND) 2 (P5V) and possibly 3 (Reset) will be most useful.
On the lower side, you will find the CIA-Pins:
- C: PB0; Bit0
- D: PB1; Bit1
- E: PB2; Bit2
- F: PB3; Bit3
- H: PB4; Bit4
- J: PB5; Bit5
- K: PB6; Bit6
- L: PB7; Bit7
A and N are GND, again. In order to use these buts, you first have to decide in which direction data is flowing – You can use each bit for input or output. This is done via poking to 0xDD03 (56579). So with a simple
POKE 56579,15 (0x0F = 0b00001111)
PB0 to PB3 (C-F) will be output, the rest will be input. Once this is done, you can set the values by poking to 0xDD01 (56577).
POKE 56577,5 (0x05 = 0b00000101)
PB0 and PB2 will be HIGH, PB1 and PB3 will be LOW and PB4-PB7 are ignored as they are inputs (you could poke ones there and it wouldn’t matter). If you want to read data from the port, you can simply peek 0xDD01 (56577). Be sure to AND the value with a mask of the input pins as you most likely don’t want the output information to mess up your data:
IF (PEEK(56577) AND 128) THEN PRINT "PB7 IS HI"
IF (PEEK(56577) AND 64) THEN PRINT "PB6 IS HI"
IF (PEEK(56577) AND 32) THEN PRINT "PB5 IS HI"
IF (PEEK(56577) AND 16) THEN PRINT "PB4 IS HI"
So you just need to connect two joystick plugs to the User Port and you have your adapter, right? Well, there’s a slight problem with this. C64 joysticks have 5 Signals (up, down, left, right, button) so with two joysticks you have ten signals. PortB only has eight bits. You may find out that PA2 is on the user port, too, and can be adressed in a similar way, but the “standard” four-player-adapter only uses PortB.
It switches between the joysticks, using a multiplexer.
This is why the 74LS257 is in there. One of the pins is used to switch between Joystick 3 and Joystick 4. Basically, the IC has four outputs and two inputs for every output.
- Signal A (later used for Up): 2/3 -> 4
- Signal B (later used for Down): 5/6 -> 7
- Signal C (later used for Left): 14/13 -> 12
- Signal D (later used for Right): 11/10 -> 9
Note that the IC also has an Enable Pin (15). In more complicated circuits you can use this to switch between other chips by enabling only one of them. As the 74LS257 is the only IC (and about the only technical thing in general) in the four-player-adapter, Enable can be connected to GND (thus enabling the chip) all the time.
You can read more about the technical details of this chip in it’s datasheet (just google “74LS257”). In general, it’s a good idea to know some basic types of ICs – you never know when one of them might fit into your project but when you know their scopes, you might recognize your weapon of choice more quickly.
So, basically the directional signals of the additional joystick ports and their common grounds need to be connected to the multiplexer. You might notice that with only four signals from each joystick, one cannot be multiplexed. The “button” is considered important enough to be connected to the user port directly. This means that four bits of PortB are used as input for the directions (from the multiplexer), two are used as input for the buttons – and one is needed as output to switch the multiplexer between the two joysticks.
This is where things became complicated. The multiplexer information above was created from the 74LS257 datasheet so I consider it to be correct. But which Userport-Pin is to be used for which button joystick? And is Joystick3 to be selected with Select=High (and therefore at In1) or with Select=Low (and therefore at In0)?
Finding more information turned up some contradictions. The most verbose source seems to be the building instructions from Protovision (makers of Bombmania and quite some more games using this adapter) but at the time of writing one of their diagrams contradicts commodore standard, their table contradicts their diagram and their example code contradicts their port ordering. One might expect that they used this code fragment in their games and one should use it for orientation – but can I be sure about that?
Time to hit the games and check with my existing adapter. First observation: My adapter still works – phew. Second observation: Some 4-player-games don’t use the joystick button and don’t help in any way. Also, some games avoid numbering the players (“this snake”) so don’t help either.
One game labels my controller as player3 (button works, too); This controller has the button connected to Userport.H (PB4) and the up-pin connected to 74LS257.3 (In1A). This is sometimes called “Up2”, “B”, the plug is sometimes labels “2” or oriented suspiciously on the right side of a diagram – but it seems to be player3, the first additional plug.
Well, as long as the button is mapped to the right joystick it is not too important which one of those is called “Joystick3” or “Connector2”. Switching the plugs shouldn’t pose too much a problem.
With that in mind, the Joysticks can be connected to the multiplexer:
Next, you want the Multiplexer output to flow to the user port. As on normal joystick ports, the directions use the four least significant bits, they are connected to the lower four bits on the user port, too. PB7 (Pin L) is used to switch the multiplexer. Also don’t forget to pull Enable (15) to GND so that the the multiplexer activates. To power the multiplexer, we take P5V from Userport.2 (upper row)
Finally, let’s not forget to connect the buttons directly to the user port. Also some joysticks may need power, so let’s connect them to P5V from the Userport, too:
And we have the final circuit:
If you want to, you can add a little reset button (closing button between 1 and 3). I didn’t include this in the circuit as it’s a rather simple modification.
And – of course – everything looks cleaner once you hide the mess in good-looking-boxes:
After soldering this, you might want to test it. Of course, you can use the downloadable games for that but it also might prove worthwhile to know the peeks and pokes.
10 POKE 56579,128
This is necessary as PB7 will be used as output (to the multiplexer). PB0 to PB5 will be used as input. We don’t care about PB6.
20 POKE 56577,128
This sets select to high, therefore selecting joystick3
30 PRINT PEEK(56577),
Prints information from the multiplexer. Note that the button is equivalent to 2^4 (16) and that a button press from Joystick 4 will be seen here, too (as 32). The comma is added so that no new line is printed. Instead, both values should be printed in the same line – like columns
40 POKE 56577,0
This sets select to low, therefore selecting joystick4
50 PRINT PEEK(56577)
Again, prints information from the multiplexer. Again, Button3 is 16 and Button4 is 32.
60 GOTO 20
Repeats the loop. Note that line 10 is initialisation and does not need to be run again.
Of course, in a real application/game you will mask the values and do something with them but for a simple test, this program should suffice.
Btw: This adapter is known as “CGA adapter” (for Classical Games who first built them). Never versions of VICE allow emulating it (in case you want to test the program, first).