The information I have states that the userport' serial speed is about 1200bps, but there are some carts going up to 9600bps. I would imagine that the 1200 is the limit, so I'm wondering how is the 9600 achieved ?

(I know there are expansion port adapters capable of even higher speeds, even 115200, my question is specifically about user-port ones).

  • I don't know how it's actually done, but I see two options: (1) Use the parallel port and an external shift register (2) The CIA data sheet says "max. usable baud rate is determined by line loading", so add some analog stuff to make it work faster. Do you know any more details about the "fast" cartridges, like their name?
    – dirkt
    Jun 23, 2016 at 7:12
  • @dirk fast expansion carts are: Link232, Swiftlink (those do at least 38400bps) Jun 23, 2016 at 8:21
  • Sorry, I phrased the question badly: Do you know any details, like names, about the fast (up to 9.6 kbps) cartridges for the userport? Then one can google and try to find out how they work. I found the EZ-232, and I'm looking into that now, but maybe there are others?
    – dirkt
    Jun 23, 2016 at 9:11

3 Answers 3


The RS232 routines in the ROM of the C64 use port B of the CIA (PB0-PB7 on the userport) to input/output all RS232 signals including Tx and Rx. So these routines don't actually use the shift register capabilities of the CIA, and have to do the shifting, parity calculation and bit-banging in software. That's the reason the speed is limited to about 1200 bps.

If you connect the Tx and Rx signal instead of port B to the serial port (SP) of each of the two CIAs (SP1 and SP2 on the userport), you can do the shifting etc. in hardware. The driver for this "special" wiring was called "UP9600". The source for an adapted variant is e.g. here (the original link at jamtronix seems to be dead). Then indeed, as the CIA datasheet says, "the maximum usable baud rate is determined only by the line loading", and 9600 bps were possible.

So the 9600 bps were achieved by using the full capabilities of the hardware that was already present (and probably should have been used by the routines in the ROM in the first place).

An example of a schematic that used jumpers to allow both kinds of wiring can be found e.g. for the GLINK-LT User Port RS232 cartridge.

Today, many RS232-USB adapters use TTL levels anyway, so to connect to one of these, one even wouldn't need the level converters.

  • I have been trying to make my own terminal software using Vice emulator and TCPSER. But for some reason I am not able to get 2400 baud working with it using Kernal calls. I see that if I load up StrikeLink 2014, it works fine at 2400 baud. By adding some breakpoints I do however notice that StrikeLink is not calling the Kernal serial routines so obviously they have overcome the 1200 baud limit by some custom code without actually needing a hardware hack. Naturally for 9600 baud it requires the hardware hack and the special UP9600 code.
    – Johncl
    Nov 9, 2017 at 15:25
  • It's not quite as simple as you make out. The CIA's shift register doesn't handle the parity calculations or the start and stop bits that are necessary to make asynchronous RS2322 serial signals work. That's why the kernal routines don't use it. I don't know what clever hack the UP9000 software used, but it's not at all obvious how they did it, and your link to the source code is dead.
    – user722
    Dec 10, 2018 at 18:36
  • @RossRidge: If performance is critical, using 256 bytes for a parity table is apt to be more practical than trying to compute parity any other way. I know there are some nice tricks that can reduce parity computation to a surprisingly small number of cycles, but they're not likely to be as fast as sequence: lda byte / sta $+1 / bit parityTable.
    – supercat
    Dec 11, 2018 at 0:02
  • @supercat Calculating the parity is the easy part. Outputting and inputting the start and stop bits is the not at all obvious part. The CIA's shift registers just shift in and out 8 bits of data. They don't generate or accept the start and stop bits that make RS232 asynchronous serial ports work. The shift registers implement a synchronous serial interface instead, one that requires a external clock to determine the shift-in rate. An external clock that RS232 ports don't have.
    – user722
    Dec 11, 2018 at 1:45
  • @RossRidge: I found a new link to an adapted version of the source code (I don't know how much they adapted, maybe the original is still out there somewhere). They use a lookup table, and timer B for the receiving timing. I guess sending is just done by cycle counting, but I didn't verify that.
    – dirkt
    Dec 11, 2018 at 7:09

At least 2400 bps requires no special hardware but extra software, see https://github.com/nanoflite/c64-up2400-cc65.


The design of a software UART on the 6502 involves some compromises between code size, RAM usage, and CPU loading. The Kernel's UART routines are designed to minimize code size and RAM usage at the expense of CPU loading. At higher baud rates, it's necessary to minimize CPU loading which in turn requires using code which is bigger and uses more RAM than would be needed at slower speeds.

BTW, I'm not sure why the UART pins are laid out as they are. If the receive data pin were located at bit 7 and transmit on bit 0, that would eliminate the need for any manual register saves or restores most of the times the interrupt fires. Even while transmitting and ready for reception, a typical invocation of the NMI could look like:

lsr txBuffer
rol $DD01
bit $DD0D ; Reset interrupt flag
bcc startBit1
inc $FFFB ; Service next interrupt with a routine 256 bytes higher

One would need to have many different interrupt service routines placed to allow code to switch among them efficiently, but the performance could be much faster than if code had to save and restore the values of the A, X, and/or Y registers every time the interrupt fires.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .