Sunday 3 December 2023

ZX80 BASIC on a Minstrel 4D - Part 2 - New tape load and save routines

Continuing the series of posts from Patreon in early November, working on getting ZX80 BASIC running on the Minstrel 4D.

Yesterday I gave up on trying to make the old ZX80 tape load routine work. I do not know what exactly is wrong, I have tried all the combinations I can think, and is just isn't working.

So, I wrote a new one.

A new tape loading routine

The screen border on the Minstrel 4D is driven by the load signal, so you can see the data on the tape in it's groups of 4 or 9 pulses, which represent the 0's and 1s.

The ZX80 cannot generate the screen during loading, so normally the screen is just black. As the hardware on the Minstrel 4D is different, the screen persist with the last update, so the LOAD command remains visible where it was typed until the program has loaded.

I thought it might cause the user to think they hadn't pressed return, or it had not registered, so I tried blanking the screen, which sort of worked. I also tried filling it with 50% grey characters, which is not normal to see on a ZX80, so acts as a "something is going on and nothing is being drawn on the screen".

Loading is a difficult thing to debug, but I realised I could make use of the display. I will go into more detail in a future post, but I can write to the display when the Z80 is doing other things.

Here I am writing the bits remaining counter when it gets to the end of a byte. Most of the time with the old load routine, I was lucky to get more than a single 1. This was an unusual occasion I captured when the volume level on the tape was wrong and the data was rubbish, but it was rubbish in just the right way to make the old tape routine accept that rubbish and try to load it.

I decided to write a new load routine, and see if that worked.

The idea seems to be the a series of either 4 or 9 pulses, followed by a gap. A wrote code to detect the transitions in the signal (so polarity would not be an issue), and waited until there was a big enough gap since the last transition to count as the end.

This shows a 1 and a 0 being detected. Yellow is the sampling pulses, green is the input signal, and blue shows writes to RAM once a bit has been detected.

I used the debug to record the 0s and 1s here showing the 66 bytes that make up the "Hello" program I was testing with.

There is no formatting to the data, no header or end marker. End detection on the ZX80 is based on the data in the file itself. It is written to RAM from 0x4000 upwards, overwriting all the system variables, including E_LINE, the end marker. As memory fills up with data being loaded, the current address is compared with E_LINE until they match, and then loading is complete and the program is listed. (E_LINE is initially set to a high address so that it will not stop before E_LINE has been overwritten with the value from the tape)

Yay, finally. Can you imagine how relieved I was to finally get that working (at about 5AM this morning, but who's counting).

And it even worked.

I needed to remove the debug, but it then went back to being a sort of boring grey. 

I think I can do better than that.

Now when you type LOAD, the screen is cleared and you get a little box.

As loading progresses, the current address is displayed. I thought about subtracting 4000, so it counted up from 0000 instead of 4000, but I think it's fine like that. It also counts in hex as that is far easier to do. Very easy as it happens due to the character set of the ZX80. Character 28 through 37 are 0 to 9, and 38 goes straight to A, so characters 28 through to 43 are 0123456789ABCDEF. To display a number in hex, just add 28 to it. (It is a little more complicated in practice as you need to split the byte into two nibble, and display each one separately.)

That seems to be working quite nicely. I was a little worried about the extra overhead, but it only adds a small delay, see the extra write pulses in blue at the end of a byte, more than enough space before the next one starts.

I successfully loaded many programs, including a large text adventure, Citadel.

And a version of Othello with a surprisingly good AI player.


I knew save was going to have similar problems to load. The hardware of the Ace is again fairly similar to that of the ZX80 when it comes to saving, but the actual triggers are different.

The ZX80 re-purposes the VSync generator to drive the mic input of a cassette recorder. So again, when it is doing that, it cannot drive the TV signal, but that's par for the course with the ZX80.

Here the signal is turned on by any IO writes, and turned off by keyboard IO reads.

The IO read to the keyboard doubles to turn the sync signal off and also read the column which includes the space key, so that can be used to abort the save.

The sync signal generated is a series of pulses representing 0s and 1s. A 0 is 4 pulses, a 1 is 9 pulses. There are no byte or block markers.

The version which is recorded on the tape is AC coupled by the capacitor C14, so ends up as a stream of pulses of 300us / 3.33kHz, which is within the range of a basic 1980s domestic cassette recorder.

The Minstrel 4D has the same output section as the Jupiter Ace, which is something resembling a proper IO port. In this case, a 1 bit output port latching the value of D3 written to I/O port FE.

In order to make save work on the ZX80 4K BASIC on the Minstrel 4D, I just need to change the IO write (to unused address 0xFF) to be a write to 0xFE with D3 set to 1, and the reads of 0xFE to be writes to 0xFE with D3 set to 0.

I need to add extra reads to 0xFE to check for the break key. This is only tested between bytes, so an extra read there shouldn't be a problem.

The only problem is the speaker on the Ace is also controlled by IO reads and writes to port FE so it will click on an off with each byte.

I don't think there is a way around that. I can't change the hardware, so it's either clicking or no way to abort a save once it has started.

Testing out save, that worked fine.

Rather than having to mess around patching in place, I decided it would be easier to just create a new save function, that way I could also use the on screen counter the same as the new load routine.

That all seems to work as expected, I have tried saving and loading and all seems to work well and interchangeably with a Minstrel 2 running the original ZX80 4K BASIC ROM.

In the next post I will be trying out some games and seeing if the special "flicker-free" programs work.


Minstrel 4D

Minstrel 4D kits and ready built units are available from


You can support me via Patreon, and get access to advance previews of blog posts and behind the scenes updates. These are often in more detail than I can fit in here. This also includes access to my Patreon only Discord server for even more regular updates.