Showing posts with label Serial. Show all posts
Showing posts with label Serial. Show all posts

Sunday, 8 March 2026

PET Serial Interfacing

It turns out it is actually quite easy to interface a Commodore PET or Mini PET to serial, as long as you don't want to go too fast.

There are various ways to do this, and this post by Paul Rickards describes several of them.

Those were all using vintage software, which has a couple of disadvantages, firstly it is difficult to change, but mainly it was designed to interface using crude RS232 level shifters.

The RS232 serial communication protocol was designed in 1960, and as such, was designed to be robust. Communicating over large distances in electrically noisy environments.

The RS232 standard* varies by revision and applications, but by the 1980s was mainly working with signals at +/- 12V. With +12V representing a binary 0 and -12V representing a binary 1. In practice you might find +12V down to +5V representing the zeroes, and anything from 0V down to -12V for the ones.

* it is with pure optimism that I use the words "standard" and "RS232"in the same sentence

This shows the "real" serial port on my PC which is giving 5V and -7V. This is the byte $42, a capital B. It is sent big-endian, with one stop bit (a 0 on the end). You can read that from right to left in the order it was sent as 0100 0010 (42) and then 0 the stop bit. (I realise now that 42 is a binary palindrome, so it not a particularly good example here)

Inside the communications equipment, there will be drivers to convert these RS232 levels to TTL levels. In the 1980s, these were 1488 and 1499 drivers, which required +/- 12V supplies. But the 1990s (when I started fiddling with serial comms) there was a family of chips starting with the MAX232 which did the level conversion and inversion and also generated it's own supplies from a single 5V rail.

In TTL land, the 0s are 0V (or there abouts) and the 1s are 5V (or somewhere over 2V at least).

Here is the same B, and it is now the more logical way around to show 0 0100 0010.

And $40, an @ sign to show the highest bits are indeed at the right of the picture. The transmitter sent 0100 0000 and the final 0. 0100 0000 = $40 = @.

When people were trying to interface to the PET in the 1980s, they went for the simplest solution, and used diode / resistor type level shifters to drop the RS232 input voltages down to safe* levels for the PET userport. The PETs output of 0V and 5V is just about enough for RS232.

A typical interface of the day would be something like this.

The diode protects against the negative side of the incoming signal, and two resistors form a potential divider to drop the signal level. The other resistor protects the output of the PET against the 50:50 chance you have wired up pins 2 and 3 backwards. (It is like inserting USB plugs, you get it the right way around third time)

* This seems to be expecting a maximum of about 9V as anything more than that would give over 5V at the out. I think I would have preferred if the 620Ω was more like 1KΩ to be safe

You will notice there is no inversion going on here, so the software of the day was expecting the input signals to be the RS232 way around, and carried out the inversion in software.

Roll on modern days, and "real" RS232 is rarely used. But it is common to use USB to serial converters, often called FTDI cables (since they made the driver chips). These operate at 5V (or sometimes 3.3V), and work with non-inverted signals.

That means you can connect up one of these FTDI cables to a PET without having to do any inversion in hardware or software. But that does mean the old software cannot be used.

There is however modern software which expects the non-inverted direct connect signals and so it all works together quite nicely.

The adapter I built here is the simple non-inverting, non-level shifting variety, with two resistors for a bit of extra protection.

This pin arrangement is slightly different, but is the same as that commonly used on the VIC20 and Commodore 64. (as it common with many of these things, CA1 is used to generate an interrupt, but it's value cannot be read directly, so the same receive data signal is connected to a pin that can be read. CB2 is the output of the shift register, although this may not be being used as such due to a bug in early revisions of the 6522 VIA it is connected to.

Although you can potentially use adapters designed for the VIC20 and the C64, it is important to remember that no matter what people on the internet tell you, this person on the internet is telling you there is no power on the PET userport connector.

I used the prototyping board in case I needed to add an inverter, something like this, but it wasn't required in the end.

That plugs into the userport and the FTDI cable connects, with ground (usually the black wire) to the left. RTS and CTS are not connected, there is no hardware handshaking.

The software I used was PETTERM, available here:

There are lots of versions. The release includes four, but if you build from source, there are thirteen more.

The main ones have 40 or 80 depending on how many columns your PET screen has, and G for normal/graphics keyboards (i.e. the Mini PET), and B for business keyboards.

I will come to the versions with a memory size later.

For the moment, I am just using petterm40G.prg which suits the MiniPET.

When it starts up, it defaults to 1200 baud, which is fine. 2400 is available, and seemed stable in this release build, but was a bit flaky in the later version built from source.

On the PC end, I setup Tera Term to match. The only setting I had to change was the end of line character. Carriage return just sent the cursor on the PET to the left, CR+LF was required to move it down a line as well.

It was time to try typing stuff.

Success!

Both ways!

I tried various things and it seemed to be working quite well, converting ASCII to PETSCII and handling various control codes for arrow keys, backspace etc.

Even big chunks of text.

It also supports 80 column mode on the Mini PET 40/80, so you can read even more.

This is working fine as a terminal, but is it possible to transfer files?

Yes, that's what the other versions were. Select the memory size of your machine, and load the appropriate version.

You will see it loads but does nothing. You need to type SYS 20480 to enable it. (there are different SYS commands for the different versions).

This brings up a slightly different menu, with two new options. These allow you to load a program into memory over serial. You can also send a file to the PC end. You would load it before you type the SYS command.

These transfers use the XMODEM protocol, so you need a suitable client at the PC end.

This gets the PET ready to receive, you then start up the PC end.

And away it goes.

The transmit pins using CB2 on the PET, which is the same used to drive the speaker, so you get a click when you press a key, or when each block is acknowledged, which is a handy indication things are progressing if you don't have a scope wired up.

Hey this is a posh modern scope, let's decode that serial and see what it says.

Ah no. The hardware and firmware on the scope are perfectly cable of doing it, and probably of higher bandwidth and greater sample depth, but it's all knobbled by licenses. Must remember to google how to hack it one day....

It's not immediately obvious what happens next. The program has been loaded into RAM, but the terminal is still running. You need to press CLR/HOME to go back to the menu, then X to exit. Then enter to get a READY prompt. And then your loaded program is ready for you to type RUN (or save to disk, or whatever you want to do).

And that all seemed to work nicely.

I had planned at one point to add an FTDI connector to the Mini PET, and to add "load over XMODEM" to the file browser, in the same way as on the Minstrel 4D.

Maybe one day I will revisit that.

I did make a PCB version of that.

Which makes things a bit neater.

If anyone is interested in one of those, let me know. Things seem to be running a bit slow on Tindie for some reason. The last few things I added, I had the PCBs made by JLCPCB, shipped here, built and tested, all before the listing was approved.


Adverts

My Tindie store contains all sort of kits, test gear and upgrades for the ZX80, ZX81, Jupiter ACE, and Commodore PET.


Patreon

You can support me via Patreon, and get access to advance previews of blog posts, and progress updates on new projects like the big news on the Mini PET that was announced this week, along with the and Mini VIC and other behind the scenes updates. This also includes access to my Patreon only Discord server for even more regular updates.

Sunday, 30 November 2025

A New Serial Card for Minstrel 4th and RC2014

Here it is, a new serial card specifically designed for the Minstrel 4th and the RC2014 bus.

It may look like there is something missing. Let's just check.

  • No UART
  • No ACIA
  • No DART
  • No SIO
  • No microcontroller
  • No Raspberry Pi

So how is it doing it?

Read on.

There is a lot of backstory to get to this point, I covered in a separate post:

The essential part is I wanted a card that used all new parts, but there was no serial chip in production that I thought was suitable (the W65C51N is in production, but some unfortunate flaws make it unsuitable).

I had tried to create a "pseudo 6850" card many times, in many different ways, but none of them were 100%.

It all seemed to come down to communication between the Z80 and the serial chip. These simple chips can only deal with one byte at a time, so the timing of the RTS signal and getting that one byte read is quite important, and I kept getting into a situation where it worked "most" of the time.

(N.B. as I have been working on this, I have concluded that the 68B50 also works only "most" of the time, and suffers from missing bytes when they are sent too fast, but I still wanted to do better, and I think I have)

The 68B50 only has a single byte buffer, so has to receive a character, wait for it to be read by the Z80, receive the next character, wait for that to be read etc.

The way the software driving the 68B50 works is it set RTS briefly to indicate it is ready to receive a byte. It then checks to see if a byte has been received and reads that byte of data if there is one. If there isn't, it pulses RTS again and repeats.

The calling code then checks if a byte was received, and if not, go back and calls it again.

One day I was looking at the traces and I noticed that the RTS pulses were only slightly longer than the actual data bits......

A dangerous thought occurred to me.

Can I bit-bash RS232?

The answer is yes, of course I can, that was actually one of the first projects I did with the early microcontrollers back in the 1990s (see a previous post), although that was a PIC with something like 4MHz clock and the serial was probably 9600 baud or something like that.

This is a Z80 with no single cycle IO instructions (very far from it), and also the serial is 115200 baud.

They both can run at up to 10MHz, but the Z80 instruction takes 12 times as many cycles, and the baud rate is 12 x what it was. I am 144 times the programmer I was 30 years ago?

Looking at the traces, it became apparent, that all the bits of the serial data were 28 clock cycles long, confirmed by checking the whole bytes from start bit to stop bit with 8 data bits in between were all 280 cycles long.

Just out of academic interest, I had a look to see how close 28 cycles per bit at 3.25MHz would be.

116071 baud

OK, that's not bad, fairly close to 115200 isn't it?

Well, it turns out to be 0.76% fast, and the tolerance is something like 5% on some older systems, so maybe it is viable to bit-bash RS232 at 115200 baud (or near offer).

Ah, but do I have enough cycles?

Software Alarmingly, the OUT instruction is 11 or 12 cycles depending on which version I use. Ouch, that's almost half of the time I have.

After a bit of juggling around, I managed to make it work in 28 cycles.

I added that to my rebuilt Minstrel 4th Enhanced ROM (now you see why I needed to be able to rebuild it)

That looks like RS232 to me.

The logic analyser was happy enough with it, as was a serial terminal. Using the TEE command, I was able to VLIST over serial!

Transmitting was possible, but the next bit with going to be more complicated - was I going to be able to get the receive code into just 28 cycles?

Yes I was, in fact, I even had cycles to spare, so used the 8 cycle version of RL A instead of the 4 cycle RLA. Such luxury.

But, as usual, it wasn't 100%.

The problem is with detecting the start bit, to know when the start sampling the incoming data.

Most of the time, it would hit it fine, and sample all the bits in the right place and decode the data.

However, sometimes it would have been busy setting RTS again and by the time it sampled the start bit, it was a little too late and was off by a whole bit in this case.

I tried a few variations, including turning RTS on and off around each sample point, and turning it on at the start and one turning it off then the stop bit was detected, but none of those options were 100%.

Yay, yet another "not quite good enough serial card replacement".

Plan B

(are we really only onto B? maybe it has looped around?)

I had one ace up my sleeve that I was holding back, time to play it.

The plan had been to use the 28 cycle version at 3.25MHz to generate 115200 baud, to match the original used in the Grant Searle system, the RC2014 and Minstrel 4th enhanced ROM.

This is all based on clock cycles, so when that same code runs at 6.5MHz, it would generate 230400 baud, which is also a standard rate, although not as commonly used as 115200.

I knew I had a fallback that I if I doubled it to 56 cycles per bit, that would give me 57600 baud at 3.25MHz, and would now give 115200 baud at 6.5MHz. 57600 is also a standard rate, more so that 115200, but it felt like a little bit of a cheat, I would have liked to do the 28 cycle version if I could.

Yes, I know I said in a previous post that the modern get-out-clause for lazy programming was to specify a faster processor, but in this case I tried really hard to avoid that.

It just means the baud rate of the serial terminal or serial server Python code would need to be selected based on the clock frequency of the Minstrel 4th, not too much of a problem,

The main receive and transmit code just needed padding out to 56 cycles.

I later changed that from 7x NOP (4 cycles) to 4x LDA (7 cycles).

N.B. I later changed it again to load A with $FF as that value is written to the upper address bus during the IN and OUT instructions and that will disable the keyboard matrix

This was all padding, other than around the code which detected the start bit.

With that now in place, I was able to tweak things again.

Now it is checking more regularly, the data samples fit neatly into the middle of the transmitted bits.

Here I have tried to show the range by overlaying the worse case (in pink) where the start bit appears just after it has been sampled, and the best case (in purple), where the start bit appears just before it is sampled.

That seems to be working well, no bad characters.

I still got the occasional double character when RTS was active just a little too long and a second byte was sent immediately after the first.

To get around that, I tried just a single short RTS pulse at the start, and then a run of samples.

That worked nicely.

I couldn't find any definition of how long the RTS line needs to be asserted to be valid. RS232 is one of the "standards" that is anything but standardised, having evolved over the incredible 65 years it has been around.

Most of the time, this will be used with an FTDI type serial module, so I checked the datasheet for that, but again, nothing specific about RTS/CTS timing.

It appears to work fine with the brief pulses, maybe it's edge triggered?

Because this is very much one byte at a time, it needs RTS/CTS to be used, so I decided to remove the connections to the RX and TX pins on RC2014 bus, as receive would not work without hardware flow control.

I am not sure how much those are used, they were originally there to connect the serial from the 68B50 card to the Raspberry Pi video terminal board - is that still a thing?

I later relented and put solder blob jumpers so you can reconnect them if you really want.

Coding Transmit

Transmit works out fairly straightforward. Toggle the TX line up and down to generate the bits and the return.

As with the 68B50 bards, CTS is not checked, so transmission is never blocked. I have made that pin readable with a CTS word, so you could write code to check that before transmit if you had an application the required it, or roll it into a blocking TX function. Generally an FTDI USB cable to a PC would never need to assert CTS as it can read as many bytes as you can throw at it.

Coding Receive

There are three ways you can deal with reading characters over serial.

Polling

This is usually done as part of a loop, or on a timer interrupt, for example on the vertical refresh on the Minstrel 4th.

The implementation here toggles RTS and waits about a character length before giving up if it does not see a start bit.

This is the equivalent of periodically opening the front door shouting "anyone got any parcels for me?", but is slightly better than it sounds because with the RTS control, they won't attempt to deliver parcels when your door is closed.

Blocking

This is where you want input from the user, and don't have anything else to do. You call the function and it will wait until a character has been received before returning.

Here it repeats the sequence of toggle RTS, wait for a start bit, toggle RTS, wait for a start bit etc. until a character has been received (currently this returns after 1000 tries so as not to lock the machine up)

This is like standing at the front door, waiting for the courier to arrive and only going back inside when you have the parcel.

Interrupt

Here you keep running your code until you get an interrupt telling you a character has been received.

This is the equivalent of working away on other things, and when you hear the doorbell, going to the door and receiving the parcel, assuming you get there in time before the courier gives up and goes back in their van.

That mode is not supported on this new card, the main reason is the interrupt pin is used by the video circuitry on the Minstrel boards, so is not easily available for other purposes.

I did consider having a non-maskable interrupt triggered by the start bit, but the time between the interrupt being raised and the interrupt handler getting started is non consistent, and depends on the instruction being executed when the interrupt occurs, and if you need to preserve registers and machine state, you quickly run out of cycles (the driver is back in the van and on their way back to the depot by the time you get to the door).

The Minstrel 4th Enhanced ROM

The main intended use of this card is with the Minstrel 4th Enhanced ROM, this uses serial in a few ways, all of which this card can be used for.

TEE and TTY

The TEE command can be used to send the screen output to the serial port. This involves transmitting characters as they are printed to the screen, that is fine.

Transmit is called whenever a character is to be printed to the screen.

TTY can be used to allow typing in commands over serial, and using TEE to send the responses back via serial.

Receive is called as part of the keyboard scan routine, using the "polling" method above. If there is a character received, it is handled in the same way as a key press.

I spent quite a while tweaking things to avoid missing characters. It turns out when I went back to get some more sample traces for comparison that the 68B50 version wasn't very good at dealing with that either, and would sometimes miss characters itself.

The new one seems pretty solid and certainly with the FTDI (or clone) USB serial adapters I have tested with, it is using RTS to effectively stop more than one character at a time.

Serial Server

There is a Python script which runs on the PC end with a folder of TAP files, the user can use the LS, TAPIN and TAPOUT commands, along with versions of LOAD and SAVE adapted to use serial.

This uses serial TX and blocking serial RX, so works fine with this card taking only a few seconds to load 3D Monster Maze at 57600 BAUD.

I am sure you know my criteria for something working is "can it run (or in this case load) 3D Monster Maze".

Well, yes, it can.

It seems to spend most of the time toggling RTS, waiting for the next bytes, so the dropping to half the baud rate has not made much of a difference.

Typing in code over serial

One of the features I had on the Minstrel 4D was being able to type in some forth source code, I have added that here with a new TYPEIN command, so it will type in the contents of a .fs or .txt file.

Pasting a file into a terminal is also an option, but I need to work on adding some punctuation characters that are missing.

I also noticed it missed the ; at the end of a statement, when pasted as a bunch of lines. It seemed to successfully read the character, but then ignores it.

One neat thing about software serial is you aren't limited to just one RX and TX. For testing purposes, I added an echo of the character received using the RX_LED pin, and that confirmed the ; is received correctly. Needs more investigation.

Implementation

I have tried to keep things neat and simple. Four main ICs, but which one is the UART?

I did start off needing to add only one 74HC74. My initial plan was to integrate this into the Minstrel 4th, and use the $FE I/O ports already present. Only 6 bits are used on the input port, and one on the output port.

This worked, but unfortunately it triggers the speaker which toggles on and off with reads and writes, so it buzzes when sending or receiving data. I could call that a "feature", or add a jumper to disable the speaker, but I'd rather not.

I didn't have space for four chips, so I went back to an external card.

Address decoding

I went through a few variations on the way. I was initially looking at a 688 and a 138, or a pair of 138s.

I decided I would prefer to go for the simplicity of plain logic gates. I went for address $FF, since I could do that with just two ICs, a 20 and a 32. I decided not to give the option of changing the address, since that would need different versions of the ROM. I don't think $FF is used by other devices, so should be available.

I reordered the pins to make the layout easier. It makes me happy to know the layout is completed with no vias, I like the challenge of laying a board out like that. I'm sure no one else cares one jot.

LEDs

One thing I disliked on the 68B50 card I made was the TX and RX LEDs, these were buffered versions of the RX and TX lines (since there were spare gates on the 04 chip used for the clock). The bits were not very long, so unless you were sending lots of data, you wouldn't see them flash very easily.

One of the things that I spent too much time on with the failed AVR microcontroller version was getting the LEDs to flash for a minimum period, so you got a visible blink, even if it was only a single character.

If I could, I wanted to do something in hardware to make the LEDs persist long enough to see.

Input

The obvious choice for something like this would be a 245 or a 365 / 367, since all of those are already on the Minstrel 4th BOM. Those are 8 or 6 way input buffers, and I only really need one input for RX data. I may as well add a second for CTS input, although that will probably not be used most of the time.

In the end I went for a 74HC125, this is four way, but they are individually controlled, and I have another use for the two spare gates.

Output

Again there are various choices, I need two outputs, TX and RTS so an eight way 574 would be overkill. I could use a 74, that is a logical choice, but I have a use for two more outputs.

My thinking was I could control the TX and RX LEDs separately, but as I found with the AVR version, it is easy to find a place to turn the LED on, but it is difficult to arrange for it to be turned off tens of milliseconds after the byte has finished transmission, long enough for the flash to be visible.

Analogue electronics to the rescue!

This is what I came up with. The two spare 125 gates act as buffers for the RC time delay time delay circuit. The RX LED version is identical.

It is setup so when the TX_LED_Pulse signal goes low during transmission, it is only low for 150µs, but that is long enough to discharge the 100nF capacitor C5 via diode D1 so the input to the buffer goes low and the LED turns on.

Approximately 50ms later, C5 has charged up enough via the 1MΩ resistor R1 to count as a logic 1, so the LED goes off again. This is retriggerable as every transmitted byte will discharge C5 again, so the LED will remain on until 50ms after the last byte was transmitted.

The latch has a reset input, so would set all the outputs to 0V at power on. That would keep C5 permanently discharged via D1 and the LED would be on until the port was written to. I would rather not have to clear the LEDs at power on, as that would mean any code running would need to know about the card, and I wanted to keep the option of using unmodified original ROMs.

I did try inverting the logic and wiring it the other way up, with the capacitor to the 5V rail, reset by a positive pulse. But I had to double think everything, and the negative trigger just made more sense to me.

The extended pulse LEDs adds 8 passive components, (ok 6 passives and 2 semi-conductor diodes if you want to be pedantic), but I think it's useful to have the LEDs there to know things are working.

Getting back to which chip to use, I could use two 74 chips, to give me the four outputs I need, and the /Q output which would reset high and so the LEDs would flash once at power on themselves whist the capacitor was charging for the first time, which is a nice effect.

Then I remembered the 175, this is a four way latch with reset and Q and /Q outputs. It's not a widely used chip, I had to hunt around and the only one I found was from 1980!

I probably desoldered that from some scrap board about 30 years ago. I knew it would come in handy one day. My hoarding habit totally vindicated.

Given the way technology moves these days, it blows my mind that this is still in production 45 years later, in an almost identical form. Long may that continue. (yes, I know that is a 74HC on the right, you can still get 74LS versions as well)

I also wired up the transmit and RTS outputs to the inverted versions, so they will reset to high (inactive), which is ideal, the card needs no software initialisation after a reset.

I have added copious arrows to the schematic to keep reminding me of the signal direction and avoid getting TX and RX swapped.

I added 2K2 resistors in all the signals to the FTDI connector for protection in case of such a swap. The Grant Searle Z80 SBC design did that, and it seems to make sense, so I have been using it every where I add an FTDI port. I also have a jumper to power the RC2014 bus from the FTDI cable 5V if you wanted to.

I was changing things a lot, so did all the initial tested on breadboard until the PCBs arrived.

Once everything was finalised, it was time to move to a PCB. Here is the first off the line.

I have been moving towards labelling things as "Tynemouth" recently, rather than "Tynemouth Software", so this is the "Tynemouth" "Software Serial Card", rather than the "Tynemouth Software" "Serial Card". And never the "Tynemouth Software" "Software Serial Card".

What it can do

  • Bit bash serial transmit at the following rates
    • 57600 BAUD (3.25MHz clock)
    • 115200 BAUD (6.5MHz clock)
    • 115200 BAUD (7.3738MHz clock with extra padding for 64 cycles per bit)
    • Other BAUD rates with more padding code
  • Bit bash protocols other than RS232 8N1 if you want to write the code
  • Blocking and polled serial receive
  • Load TAP files over serial
  • Save TAP files over serial
  • Type FS files over serial
  • Echo screen to serial
  • Remote typing via serial
  • Report CTS status

What it can't do

  • Change BAUD rates on the fly
  • Interrupt driven RX or TX
    • This means currently the MS BASIC and CP/M ROMs for RC2014 will not work
  • Operate with only TX and RX, it needs RTS and CTS for hardware flow control
  • Make tea (working on it, for a future revision)

What next?

I have listed these on Tindie, as usual in kit or assembled form, with optional IC sockets if you want.

I am still working on tidying up the changes to the enhanced ROM for the Minstrel 4th, so will offer that as a download or an optional programmed ROM.

I would like to get this working with Grant Searle's modified Microsoft BASIC ROM, so it could be used on standard RC2014 systems. That uses the interrupts, so need more than a trivial change to make it work. It should be OK as it polls the status register to check for a byte, I should be able to change that to do the quick "toggle RTS and wait briefly to see if there is a start bit received".

I am hoping this will be seen as a neat idea and a good companion board to the Minstrel 4th, and not "some idiot thinks he can bit-bash high baud rate RS232 on a Z80......"


Adverts

The new Software Serial card is available from Tindie,as a kit or assembled, and with an optional replacement ROM if you don't want to burn your own.

The Minstrel 4th is also available form my Tindie store, optionally with a Software Serial card included:

Shipping

I can still ship worldwide.

Currently shipping Royal Mail to the US is working. You pay the 10% tariff upfront (as part of the postage), so that should be plain sailing and no problems with customs. We're all pawns in a petulant child's political games, but we've got to just keep going and try to make this stuff work.

I have also built some more Mini PET 40/80 Internal boards, since the Mini PET II is still in development but people keep asking, so here they are.



Patreon

You can support me via Patreon, and get access to advance previews of posts like this and development logs on new projects like the Mini PET II and Mini VIC and other behind the scenes updates. This also includes access to my Patreon only Discord server for even more regular updates.