So far, the Zolatron 64 6502-based homebrew computer has output (a 16×2 LCD screen) but nothing in the way of input.
I have toyed with the idea of getting it to output to, say, HDMI using a Teensy as an intermediary. And also of giving it a PS/2 keyboard input (the latter still being a possibility).
But in the end I decided the simplest way of having I/O is to equip it with a serial port. And, in theory, this isn’t too difficult, involving little more than a single UART chip.
Choosing a chip
Lots of people swear by different UART chips, and popular options include the 6850 (designed for the 6800 family) and the NXP 2692A. But I made an early decision to go with the 6551. This is the chip designed for the 65xx architecture. It has a built-in baud rate generator and requires very little in the way of external components.
Of course, no-one makes it anymore. So I hit up eBay and availed myself of three pre-owned R6551 chips.
Using eBay is always a gamble when buying chips: there are so many duds and fakes. But the ones that arrived had sensible date codes and just looked the right vintage.
Uh-oh, we have a problem…
Of course, then I found out there’s a problem with the original NMOS version of the 6551. It has to do with an outgoing byte not being completely sent if the state of the /CTS signal changes part way through.
This was fixed in the CMOS version of the chip. So back to eBay. I bought three 65C51 chips from a vendor in Poland. And while I was at it, I bought three WDC branded chips, the W65C51, from a vendor in the UK, because what could possibly go wrong with genuine WDC parts, right?
Well, then I learned that the WDC chips also have a bug – an even more serious one. The 6551 has a status register and one of the bits in this register is the Transmit Data Register Empty (TDRE) flag. Basically, this is set to 1 if the previous byte that was in the register has been sent down the wire and it’s okay for you to write another byte’s worth of data to the Transmit Data Register. A typical software routine involves checking this bit, waiting until it goes high, then writing the byte. Simple.
Except that the bug in WDC’s chip meant that this bit was always stuck high. And the firm never got around to fixing it. Instead, it published a workaround suggesting that you just put a delay loop in your code to give the chip time to send out the contents of the Transmit Register – which is a kludge if ever I saw one.
Allegedly, the Rockwell-branded chips don’t have this problem. I ordered some from eBay, but they came from China so their provenance is obviously in some doubt.
So here’s the interesting bit: when the ‘WDC’ chips turned up, they were branded ‘GT Eµ’. And somehow I ended up with nine Rockwell-branded 65C51 and the three, original R6551s (also Rockwell branded). Some of the chips have etching that looks suspiciously fresh.
Once I’ve got the software running [spoiler alert: it’s working] I intend to test all these chips.
Wiring it up
The D0-D7 pins are the data bus, so no problem there. The PHI2, RWB, IRQB and RESB pins just connect to the identically named clock, read/write, interrupt request and reset pins on the 6502. And the TxD and RxD pins are our connection to the outside world. So what about the rest?
RS0 and RS1 deserve some attention. These are the register select pins. The registers are what you use to send and receive data and control or read the status of the ACIA. and they live at specific address – offset from whatever base address you’ve chosen for the ACIA. In my memory map, the decoding has the ACIA at address $B000. So that becomes the address of the first register.
Actually, precisely which register also depends on the state of the RWB (read/write) line. If you set the address to $B000 and RWB is low, then you’re addressing the Write Transmit Data Register (whatever you have on the data bus will be written to this register). If RWB is high, then the Read Receiver Data Register operation is in play and whatever is in the read register gets put on the data bus. In this way, these three pins – RS0, RS1 and RWB give us control over eight possible operations. The addresses given in the following table are for my particular address map:
RWB = Low
RWB = High
|$B000||Low||Low||Write Transmit Data Register||Read Receiver Data Register|
|$B001||Low||High||Programmed Reset||Read Status Register|
|$B002||High||Low||Write Command Register||Read Command Register|
|$B003||High||High||Write Control Register||Read Control Register|
So, to carry out operations on this chip, I need to be able to set addresses $B000-$B003. To do this, RS0 and RS1 are connected to the address bus pins A0 and A1 respectively.
There are two chip select pins – CS0 and /CS1 (the latter being active low). I’m using decoding logic, so I tied CS0 high and /CS1 is connected to the relevant pin of the 74HC138 decoder.
One external component you do need is a 1.8432MHz clock crystal. This simply connects across the XTL0 and XTL1 pins. So no drama there.
The RTS and CTS pins are there if you want to do flow control. I’m considering that optional for now. The same goes for DTR, DCD and DSR. With all five of these ‘think about some other day’ signals, I’ve tied the pins low.
[UPDATE 20/09/2021: On further reflection I realised that the /RTS and /DTR pins are outputs. Therefore they don’t need tying low. I’m leaving them unconnected.]
And that’s it for connections.
Cranking out code
Then I wrote some code. We’ll get into that in the next post, but let’s recap our situation here:
- I have a variety of 6551 chips, any one of which could be dead or fake.
- I’ve wired up the chip, but this is on a breadboard, so any one of the wires could have a poor connection or be attached to the wrong signal.
- I have a first draft of the code, in assembler, based on my understanding of how to work with the 6551 chip.
What could go wrong?