Since this project was such a last-minute thing, I didn’t put any real preparation into it. Because of that, I’m still waiting for some important parts to arrive from Sparkfun. I’ll be sure to post some pics as soon as they show up.
In the meantime, I’ve been learning how to program the Commodore 64’s RS-232 port (this will be relevant later!). Unlike the Amiga, or most PCs in the 80s and 90s, the C64 doesn’t really have an RS-232 port, or even a dedicated UART. While the earlier Commodore PET included a 6551 ACIA chip with a dedicated UART, the ACIA was left out of the Vic-20 and the 64 – probably as a cost-cutting measure. Instead, the C64 emulates the ACIA’s interface using a combination of software and one of the C64’s two 6526 CIA chips.
There are a couple drawbacks to this approach. The big one is that it just doesn’t work very well. The software routines in the C64 Kernal aren’t good for much more than 1200 bps – any faster and it can’t keep up, characters get dropped, and chaos ensues. Some terminal programs for the C64 included their own optimized serial port emulation routines, and could get into the 2400 or 4800 bps range. There were also hardware solutions, including cartridges that added a 6551 ACIA to the system.
I want to keep things simple, so I’m just going to try to get by at 1200 bps. The robot I’m trying to control for this project isn’t very fast, so I think this will be sufficient.
The other drawback is that there isn’t a physical serial port. Instead, the RS-232 signals are exposed on the User Port. I’ve talked about the User Port before – it’s basically a port on the back of the C64 with an exposed circuit board edge, and a bunch of control lines hooked up to one of the CIA chips. As the name suggests, it’s pretty hacker-friendly. In fact, it’s not that different from the GPIO pins on a modern hacker toy like a Raspberry Pi.
If you want to connect something to the User Port, you need to come up with the physical interface on your own. My interface is pretty simple – I bought a connector online, soldered a bunch of wires to it, and plugged that into a breadboard. From there I could route the signals however I wanted.
This still didn’t give me a “proper” RS-232 though, because the signal levels were wrong. RS-232 typically uses +12V and -12V signals, while the User Port provides 0V and +5V. This actually works to my advantage, though. I have a 5V FTDI cable that I use to connect my PC to the serial interface of my Project:65 computer. I can connect the User Port’s receive and transmit signals to the FTDI cable, and it Just Works. I fired up PuTTY on the PC and CCGMS (a terminal program) on the C64 and I was able to transmit text back and forth between them. (I haven’t messed around with flow control signals yet, but I may later on if I have to.)
All of that was actually pretty easy. Ironically (for me as a software guy) I ran into trouble when I tried to write my own code on the C64 side. The software interface for the emulated RS-232 port is pretty simple – it just appears as device number 2 to the C64’s standard I/O routines. The Programmer’s Reference Guide even has an example terminal program written in BASIC.
I was writing my software in C and compiling it with cc65, which had worked so well for my Questron Character Editor program from the last Retrochallenge. My first try was close. The C64 would print characters when it received characters from the PC – just not the right characters.
If you know the C64 at all well, you might think that I wasn’t translating between ASCII and PETSCII correctly. And while that’s not exactly it, you’re tantalizingly close to the truth.
If you don’t know the C64 well, you’re probably asking “What the heck is PETSCII?” For historical reasons, the Commodore 8-bit computers all use a variant of ASCII character codes that’s colloquially known as PETSCII. It’s based on an early version of ASCII, but with differences such as the inclusion of a set of “keyboard graphics” characters. One side effect of this is that if you send a string like “Hello” from the PC, the C64 will render it as “hELLO”. So that’s slightly inconvenient.
That doesn’t exactly describe the way my serial port test program was messing up. No matter what I sent from the PC, the C64 would receive a character with the value 255, or occasionally something one bit off from that. I wondered if the baud rate was being set wrong.
It turns out the problem wasn’t because I didn’t translate from ASCII to PETSCII – it was because my compiler did when I didn’t expect it to!
Here’s what was going on: When you open a device on the C64, you always specify a filename. For the RS-232 device, the filename is actually a string of magic characters that set the serial port parameters. I wanted to communicate at 1200 baud 8n1 (8 bits! No parity! One stop bit! Doesn’t that take you back?). The magic code for that setting was two bytes – an 8 and a 0. Since the cc65 wrapper function takes a C-style string for the filename, I declared one the usual way: “\x08\x00”. It should have worked. CCGMS worked. The BASIC example from the Programmer’s Reference Guide worked. Why didn’t my code work?
I actually had to look at the assembly output from the C compiler before I could figure out what was happening. I found the declaration for my filename variable in the data segment, and there I saw it: “0x14 0x00”. 0x14? 0x14?!!!??!!!!
cc65 is a cross compiler. It expects its input files to be ASCII, not PETSCII (UTF-8 is right out). Because it would be tremendously painful if it didn’t, it automatically translates string literals in the source code from ASCII to PETSCII. I fully did not expect it to translate characters defined with explicit hexadecimal values, but it does.
Part of why I didn’t expect this is that ASCII 0x08 isn’t even a printable character – it’s backspace. Sure enough, that gets translated to PETSCII 0x14, which is associated with the C64 delete key.
The solution? Just declare my filename as an array of bytes: “unsigned char name[] = {0x08, 0x00, 0x00};”, bypassing cc65’s translation. Problem solved. Communication happens. Hurray!
And just think – this was the easy part!
Hi, good to see you’re blogging again, love reading these! I got interested in toying with C64 RS-232 a couple of years ago with the intention of writing VT100 capable terminal and use it with Linux box for IRC and telnetting into C64 BBS’s. I didn’t quite finish it as I got distracted with other projects (this is what usually happens with my hobby projects..), but I got working good enough for my needs.
I really suggest you read George Hug’s article in Transactor vol 9 issue 3, “Toward 2400”. It has great explanations of deficiencies and bugs of KERNAL RS-232 routines, and gives an example routine capable of 2400bps. I had problems with even 1200bps working reliably, but after adapting the new RS-232 routine listed there, I was able to get it up to 4800bps without issues. I live in PAL-land, so I had to calculate new values based on PAL C64 clockrate (that one used NTSC), and going up to 4800 required just some minor tweaking.
At 4800bps ASCIIPETSCII translations, handling of VT100 escape codes and scrolling the screen were starting to become issues, due to time CPU needed to handle incoming data. I made a new character set for ASCII, and that helped quite a bit as I was able to drop translating incoming ASCII to PETSCII, saving some precious cycles. The most time consuming bit was scrolling the screen. I did some calculations back then, and if I remember correctly, even 9600bps might have been doable without it.
Thanks for that reference to the Transactor article – I was finally able to try that code out and it worked like a charm!
Interesting tale and solution, takes me back. 1200 baud 8N1 indeed!
Hi!
I’m currently playing around with CC65 and hooking up my C64 to my Arduino! Sadly I couldn’t really find any code examples of using the C64 serial port with C, and it seems there is no library for that either (probably due to the simplicity of configuring it)
If you could point me to the right place to find a code example, that would be awesome! 🙂
Well, I used the cbm_k_* functions, which are basically just a thin layer over the C64 KERNAL routines. So to set up the port for 1200 bps I used:
And then to open the port:
And to read and write from it (here I’m echoing stuff from the keyboard out the RS232, and writing the input from the RS232 onto the screen):
Hope that’s helpful, even if it’s way late.