Subscribe via RSS

Commodore 64: Serial Interface to Arduino

So, in my previous post, I was heading towards building an archaic circuit to control trains with the User Port. This would've worked, had I spent a lot more time and built a very complex circuit. Instead I've now chosen a new path... let's hook the C64 up to an Arduino and do most of the work there. The C64 can be the display and input/output for controlling the trains.

Interfacing both components

The C64 User Port has both a 'parallel port' with 8 i/o pins and a serial port. I initially wanted to use the parallel pins, but came to the conclusion that I'd have to write my own language on both sides and deal with the data transfer timings and clock synchronisation. Instead, it'll be easier to use industry-standard RS-232!

I suppose this is a bit of a cop-out. I wanted to build something that was dated to the level of technology that existed back when these machines were in their prime... unfortunately my electronic knowledge isn't up to scratch... so getting to a variable 12v output wasn't overly easy. It also would not have been PWM. Due to all this, including the Arduino into the mix isn't such a bad idea. Plus, everyone I'd asked for help told me to do this... even sending me links to my own blog posts :)


Serial plugs have a single channel, with each end having one transmit (TX) and one receive (RX) pin. Each end will send data down the cable via the TX pin and expect to receive data on the RX pin. Standard serial cables are 'straight through', meaning that the TX pin is connected to the TX pin and likewise with RX. Doesn't quite make sense, does it? How are two separate devices meant to eachother if they are both transmitting down the same singular TX wire and hearing silence on the RX?

This all becomes clear once you realise that devices fit into two categories: DTE (data terminal equipment) and DCE (data circuit-terminating equipment, also known as data communication equipment.) In the end, these two devices boil down to one being the master (the DTE) and one being the slave (the DCE.)

Of course, you can purchase 'cross-over' cables for serial connections. These are known as null-modem cables and allow you to hook two DTEs together. Say, for example, you want to transfer a file from your PC to your Amiga, or somesuch!

In my previous serial project, when I connected the IBM receipt printer to the Arduino, I needed the Arduino to be the master, and so I hacked around until I had correctly configured it as a DTE. This time around we want the Arduino to be the DCE. Due to this, be careful to follow the pinouts and wiring from the serial port to the MAX232 in the circuits below!

Note: For further reading/wiring on RS-232, there's a good article at avr Programmers and another at Advantech.

C64 Serial Port

The User Port on the C64 also has serial connections. These are TTL and so need to be brought up to the RS-232 5v standard. The MAX232 IC will do this for us quite easily. We'll also use one at the other end for the Arduino.

UPDATED (2024): The CTS and RTS wires were incorrectly ordered on the original diagram. The following diagram is now correct, but I've decided to leave the comments at the bottom of this article which state otherwise.

The circuit is derived from This circuit was also correct in that the pins are wired up as DTE. This means that you could use it, as-is, to also hook to a modem or any other DCE device.

The MAX232 needs few extra components. Fortunately, these extra components are identical on both ends, so buy everything in duplicate! The capacitors are all 1.0uf electrolytic. I used 1k resisters on the LEDs so as not to draw too much current from the User Port.

Arduino Serial Port

This is nearly the same circuit as the C64 end. The funniest thing happened here... if you google enough 'arduino max232' you'll just loop back around to my post, from ages ago on interfacing an IBM printer to the Arduino. Just make sure you don't use that circuit! It's DTE, we need DCE as per below! I've left out the RTS/CTS as I don't intend on using any form of handshaking in this project. It's still in the circuit above for the C64 so that you can use the port for other purposes.


As per usual, make sure you DO NOT apply 5v in the wrong direction... I did and it ruined a few caps and possibly the IC. Garbage then came through the serial port. If this ever happens, then throw out the components and start again; you'll never be able to trust them.

Also make sure that you use the 5v pin on the Arduino. AREF is NOT a valid voltage source.

Hooking it all together

Build both circuits above and give one a male and the other a female db-9 connector. The DCE device usually gets the female, so put this on the Arduino-side!

DSC03947 DSC03956 DSC03954

DSC03955 DSC03950

If you want to roll your own cable, then grab some grey IDC and two crimp-style plugs. Just make sure that you have pin 1 matched to pin 1. If you're splitting the cable, then paint a wire (or use a marker) to ensure that you get the orientation correct. It's really easy to confuse pin 1.

DSC03998 DSC04000 DSC04002

From above, you can see the pin numbering. I slid the second port all the way to the end, prior to crimping, to ensure that the numbers matched up. Using the red '#1 wire' on the cable worked wonders too.

Testing with Strike Terminal 2013 Final

Download Strike Term 2013 Final from here and then get it to your C64. I copied the D64 to my SD2IEC and loaded it up. Hit M and select User port. Hit b and switch it to 1200 Baud (or other baud, depending on what you've configured in the Arduino.)

DSC03976 DSC03978 DSC03957

DSC03958 DSC03964 DSC03965

Once ready, hit f5 and then hit enter on the default server. This'll start sending modem AT commands down the serial and they should start showing up at the other end. Either open the Arduino Serial Monitor... or edit the code to display it. I bought some 8x8 LED Matrices to render the data coming in.

DSC03963 DSC03971

There were no real caveats here... everything just worked! Press f3 to get to the terminal. Hit commodore+e for local echo and then commodore+i to 'send id'. You should now be able to type freely... everything will be sent down the wire.

DSC03983 DSC03984 DSC03985

DSC03986 DSC03987 DSC03988

At that point I only had one matrix... so the last char typed was displayed.

Writing C code to use the Serial Port

Nanoflite has written a 2400 baud User Port Serial Driver for cc65. I originally tried to use this at 1200 baud, as that's what I'd been using everywhere and heard it was the max the User Port was capable of. It turns out that this driver only supports 2400 baud! Download it and put the source somewhere.

Switch to the driver directory and compile it:

cl65 -t c64 --module -o c64-up2400.ser c64-up2400.s

Copy this to the folder that has your source file it. I slightly modified the example.

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <serial.h>
#define DRIVERNAME  "c64-up2400.ser"

static const struct ser_params serialParams = {
    SER_BAUD_2400,      /* Baudrate */
    SER_BITS_8,         /* Number of data bits */
    SER_STOP_1,         /* Number of stop bits */
    SER_PAR_NONE,       /* Parity setting */
    SER_HS_NONE         /* Type of handshake to use */

int main (void)
  int xx;
  puts("C64 serial ...");

  // Load driver

  // Open port

  // Enable serial
  ser_ioctl(1, NULL);

  for (xx = 0; xx < 10; xx++) {
  return EXIT_SUCCESS;

Compile this:

cl65 -t c64 -O -o trainctl2 trainctl2.c

I then put it on the SD2IEC and loaded it via LOAD "0:TRAINCTL2",8 followed by a RUN.

DSC03993 DSC03989 DSC03991

DSC03992 DSC03994 DSC04003

Shit... worked... this is great! Next it's time to put a PWM throttle onto the Arduino and control it from the Commodore... I'll tinker with graphical programming in C also.

Comments (20) Trackbacks (2)
  1. Hello,

    Why it is necessary to invert CTS ad RTS ?

    Thanks in advance for your help !


    • Hello Yoruk,
      The circuits were taken from the link provided directly under the images.
      Unfortunately, it’s been such a long time that I don’t remember the exact reasons for the inversion.
      I’d go with just trusting them ;)

    • Yoruk — see my comment below. This is indeed true, they are swapped, and it can cause problems with very old devices.

  2. Hi,

    The DB9 in the C64 serial diagram (the 1st diagram), is that a FEMALE DB9 or MALE DB9? I am building one.

    • Hi AL,

      That’s entirely up to you. It depends what you want to connect to. If you have long enough IDC cable, then you can make it female to plug straight into a PC.
      If you want to use a standard modem serial cable, then, actually, it should also be a female.


      • Thanks, Steven! Female it is! I just downloaded the CC65 compiler. Will try things out.

        Sorry to have forgotten to ask this previously, but I made the board exactly based on the schematic (the first diagram). You mentioned that this schematic is derived off the one from the one at I see that your diagram has pins 9 & 12 of the MAX232 swapped, respectively, to the 74LS00 and to the user port. Just wanted to confirm that is your intention.

        Thanks again!

        • AL,

          Good pick up. I don’t have a C64 anymore, but it would seem that I’ve mixed up 9 and 12. There’s no reason for the CTS to be mixed with RX.

          Please use the original. I’ll try and find time to fix the diagram above.

          (As per the comment further below, the diagram above is perfectly OK! Turns out that I just aligned the R1/T1 and R2/T2 on the MAX232.)


  3. Steven, actually maybe your diagram is OK?

    Please read It says the MAX232 pins 9 (R2OUT) and pin 12 (R1OUT) act the same.

    Your diagram currently says:

    1) pin 9 of the MAX232 is R2OUT, and is connected to pins 4 and 5 of the 74LC00, which inverts the signal and is connected to the K pin on the C64 port.

    2) The MAX232’s pin 12 is connected to the RX (pins 7+B+C of the C64’s user port), and the TX connection is for pins 11 of the MAX232 and pins 5+6+L+M of the user port.

    So, I am not sure I understand that you mentioned you mixed the CTS with RX.

    If there is no problem, then I won’t have to cut the traces and patching wires.


    • AL,

      Apologies, it was late last night when I was trying to review this issue for you. I’d written this article over 4 years ago!

      So, if you look at the source document, you can see they’ve used T1 and R2, which I must’ve seen as messy and re-aligned. Therefore my circuit uses T1/R1 and T2/R2.

      If you’ve modified your boards already, then you’ll also just need to swap the output wires. In the end the MAX232 is just a voltage converter, so just make sure the outputs are correct for the pins on the serial port.


  4. Hey Steven! I know its been a while since you made this article, but I just set my C64 back up and want to build something just like this. I just have a few questions if you don’t mind. One is, could you elaborate a bit on why userport pins 6, M, L and 5 are tied together? That’s what it seems to intend, that those four pins are tied together and go to pin 11 of the Max232. That would connect the CIA 1 timer to the CIA 2 serial port, which I guess would make sense if it was somehow used for timing. The other question I have is, why is the positive side of the capacitor coming off pin 6 of the max232 going to ground? Is that just how the IC operates?

    Thanks so much in advance for your time, and for helping to keep this wonderful retro computing hobby alive. I know you said you don’t have a C64 anymore, but your contributions are still appreciated!

  5. Steven,

    I hope this doesn’t post twice, because it didn’t appear after the first time, but I got it working last night! I built your circuit exactly as described, sans the 6/L bridge at first — I wanted to see if it would work without it (it didn’t!) So I had to put that in, and then everything worked flawlessly! I was able to send AT commands to an old modem, and even transfer a few files across from a PC using xmodem at 2400 baud! Thank you thank you thank you again, from central Maine, USA!! Enjoy your model trains, and I’ll enjoy my games thanks to you!!


  6. Stephen,

    I just wanted to include some additional info here, so nobody has to struggle for 2 or 3 days to find this tidbit of knowledge like I did.

    I did get the interface working and initially everything seemed to work fine with the PC/null modem cable, but then when I hooked up dad’s old Kantronics packet modem for ham radio I hit a wall where nothing would seem to work. The KAM was receiving data from the 64, but the 64 couldn’t receive data back it seemed like. I fought with this for two days, and it made no sense because a Zoom faxmodem AND the null modem cable both worked fine.

    I finally remembered that somebody in the comments mentioned RTS and CTS being swapped. I unswapped it, and the KAM worked immediately, as did everything else that did before.

    Sorry to bother you again, but as I said before, I’m posting this as much for the benefit of anyone else who should come after me — RTS and CTS indeed are swapped from how they should be on a DTE device. It just doesn’t matter unless your device is very old, because modern RS232 devices have some form of internal compensation to make up for this.

    So, again, enjoy your trains! I know you don’t have a 64 anymore, but if you ever get another one, I’d love to build myself a nicer version of this and send you this interface as a thank you.

    • Thank you for following up on this. I’ve gone ahead and re-worked the original circuit diagram to make sure the pins match up. As you mentioned, it was just a fluke that things worked on my end as I didn’t need the extra circuitry!

      • Absolutely! I wonder if that’s part of why you had trouble with the 64 freezing when you’d send certain characters — Novaterm was acting like that. Your comment box has a limit so I had to cut this fact from the first one, but several other, more modern devices worked prior to the swap and still did after — I could surmise that modern RS232 devices have some way to detect and compensate for this, but dad’s packet TNC from 1986 just couldn’t.

        As we say on the ham bands… 73s! (A general pleasant sign off wishing the other operator a good day!)

Leave a comment