The Dinosaur computer

This is how my daughter calls the XZ81 I dug up from the closet the other day. The reason for the fight with the dust-bunnies was partially to show her what computers looked like in my childhood abut also that someone threw out an old 12″ CRT monitor so I finally had something to hook it up to.

As I was putting it together, I realized that with the 2kB RAM inside (I have the US model, the Timex 1000) isn’t very much to do anything useful with. It also occurred to me that it would be a fun weekend project to build a memory expansion for the little old lady.

So, I got to work, researched the subject a little, looked at how other people have done it and started planning. I could have gone the traditional route of using an external extension, one that plugs into the back of the machine:

ZX81 memory expansion

I wasn’t too thrilled by this idea mainly because of how unreliable these edge-connectors are. I was looking for ways to do it inside the case of the machine. There were several designs out there, but all of them required soldering stuff to the motherboard, which – while I wasn’t afraid of doing – I didn’t like: I wanted to preserve the machine in it’s original form.

Pretty much the only remaining option left is to replace one of the chips on the board with something else. If you look at the schematic of the machine you quickly realize that the only chip that has all the required signals on its pins is the Z80 CPU. Luckily my machine had that chip socketed already, so removing it seemed simple enough.

The original design

With these, I had a plan and started working. The schematic, I came up with was the following:

The wrong way of extending the memory

The warning on it of course gives the story away, but I didn’t want to publish incorrect stuff on the web, because people will get it without context and start using it.

At any rate, I got the PCB designed in the form factor of a 40-pin DIP package, sent it out to manufacturing, ordered the parts and waited.

The folks I’ve used for manufacturing are called AllPCB. No, I’m not paid by them for any advertisement, but I find it rather amazing that they made me 25 pieces of the PCB for $5.00. Granted, they’ve changed another $17.00 for shipping, but still. It was really, really cheap for a 2-layer board. For this money, they got me the finished boards quicker from China, than what Digikey managed with the components from Grand Forks, ND.

PCBs, lots of PCBs

Now, for the design: since I intend to plug this into the processor socket, I have to remove the original CPU, which of course meant that my board would have to not only contain the RAM chip, but also a new processor. So those are the two main components on the design. The rest is some glue logic to tie things together into a functional system.

I have not found 16kB SRAM chips, I chose a 32kB one. I would have hated seeing half of that memory to go to waste, so I came up with this ‘clever’ address decoder logic, that could map the full 32kB anywhere of the three interesting 16k boundaries of the Z80 address space: 0x0000, 0x4000 or 0x8000, depending on the strapping of the input pins.

To select the RAM chip, I needed to mix in the address decoder output with the /MREQ signal from the processor, that’s what two of the NAND gates do.

The next problem was: how to not select the on-board RAM chip, the original 2kB that comes with the machine. Normally, the memory extender boards tie /RAMCS to 5V, which is available on the extension connector. This is not a luxury I have though: this signal is not generated by the CPU, but the ULA, so it’s not available on the pins I’m working with. No big deal though, the way the ULA generates this signal is simply ‘NAND-ing’ together MREQ and A14. So, I thought, if I mask out /MREQ during RAM accesses, the ULA will never select the external RAM and I’m good. That’s what the other two NAND gates achieve.

A final touch for completeness was the tri-stating of the modified /MREQ_EXT signal whenever the Z80 releases the bus to an external requestor. This is not something that ever happens in a ZX81, but still, I thought it would be a nice touch.

I also included the option to have a higher-speed clock-source on the board so that the replacement CPU could be clocked at a higher speed. Again, not something that’s useful with the ZX81, where video generation is done by the CPU and thus is intimately tied to the clock-speed, but I thought, maybe this board would be a usable upgrade in other machines as well. Maybe.

Build and debug

When all the components showed up, I got to work.

Luckily, the first thing I’ve done was to solder in just the processor (and the bypass capacitors) and replace the original CPU of the machine with my version. For this I had to of course short /MREQ and /MREQ_EXT with a piece of wire, but the test was a success: the machine booted!

First boot with new CPU

After the champagne, I got the rest of the components soldered in. I was in high mood and ready to move on to my next project. So, I plugged the machine in and…

Well, damn!

After reviewing my schematic and board build again and again (pin 1 locations OK, supplies OK, nothing is getting too hot), I’ve realized that maybe I had a short somewhere. After all, these are hand-soldered SMT components, and though I did look for shorts before, I wasn’t all that careful with the inspection.

That was a good call: I did find two of the RAM chip data lines shorted. Fixing that was not hard and it did make a difference: instead of seeing noise on the screen, I’ve been greeted by a nice, white background with no cursor.

Now, it’s worth mentioning at this point, that my diagnostic toolbox was rather limited at the moment: I had a logic probe and a DVM, but no scope or logic analyzer. That meant that I mostly had to do with head-scratching and experimentation. This is where my initial boot-test with just the new CPU proved to be extremely beneficial. I knew that the processor – alone – was working and I also knew that most of the pins must be connected up right. The only wire that was not connected directly between the processor and the main board was the /MREQ signal. Of course, just connecting these back together is really not an option as it would re-enable the on-board RAM, but at least gave me a hint as to where to look.

To get to the bottom of the issue, I had to understand how the ZX81 generated video. And for that I needed to know what’s inside the ULA. Well, that of course is not well documented, but it is known that the ZX81 is essentially a bug-fix and cost-reduced version of the ZX80. This earlier machine didn’t have a ULA in it, all the logic was in discrete gates. So, looking at the video generation circuit of that design was my best hope.

The whole schematic is available here, but to cut to the chase, I eventually noticed this little detail:

You see, IC5, is an 8-bit latch, and pin-11 is the enable input. It’s purpose in life is to latch in the character code from the processor so that it can be used to read the character ROM for screen updates. The way it works is that if any of the three inputs (the clock, /RFSH and NOT /MREQ) is low, the latch holds its previous value. Only if all three are high, the latch will track its input, eventually capturing a new value. In essence, it’ll capture a new value if /MREQ goes from low to high, while we’re not in a refresh cycle. So my clever idea of masking out /MREQ for RAM accesses also meant that the character code was not latched in into this circuit and consequently the video logic kept reading code 0 (or whatever the reset value happened to be) from the character ROM.

Needless to say, this is problematic, because it’s the same /MREQ that is used to generate the on-board RAM enable signal, RAM C.S. as you can see just below the highlighted logic.

What can I do? I have to output /MREQ to the board to engage this latch, but if I do, I will enable either the ROM or the RAM on board (depending on the state of A14. Well, the solution is to remove the on-board RAM chip from its socket, right? Easier said then done of course, because that chip was soldered down in my machine. The solution however much I hated it was to solder a piece of wire from RAM C.S. to 5V to permanently disable the on-board RAM. I also had to make sure I connected the processor /MREQ signal all the way out to the main board, so about three of the 6 gates I have in my design are now doing nothing.

Did it work? Of course not! Still no video. But why? Well, it turns out that A15 doesn’t take part of address decode for ROM and RAM for a reason: it is also part of how the video is generated in the ZX81. A15 must be high for the ULA to override the op-code of the fetched instruction with a NOP, something that’s essential for video generation to work. So, RAM must be decoded in the address range 0xc000 to 0xffff, which I didn’t do. Another thing I did realized was that – since the ROM address decode doesn’t take A15 into consideration either – the address range from 0x8000 to 0xbffff would map to the ROM anyway, so decoding it for the RAM is a mistake: both chips would be on the bus. All in all, my plan to decode the RAM from 0x4000 to 0xbfff is totally incorrect.

After all these changes, the final board looks like this:

You can see the extra black wire that disables the on-board RAM chip.

Are we there yet?

Sort of. With all these changes, the machine booted, I could download games that required 16k of RAM and run them. So all is good, right?

Well, not quite. You see, there is yet another issue: the way video generation works is that the processor is ‘executing’ from video memory. This very regular, incremental fetching from the RAM is needed to re-load the shift-register that’s responsible for generating the pixel data on a precise timing. It’s a rather ingenious idea, I have to admit. However there’s a problem with it: normally, the video data isn’t anything that can be ‘executed’. So if the processor vectors into the video memory and starts executing it, it would certainly just hang, right?

This is where the designers of the ZX81 (and the ZX80 before that) pulled another trick: they detect the fact that the processor is ‘executing’ from the video memory (this is what A15 is used for) and overwrite the data loaded from memory by the Z80. This of course only after it captured the proper data from the address bus. The way it does that is simply driving all the data-lines to 0 at the right time, so the Z80 actually thinks it read a NOP instruction.

Clever. Except that in my circuit the RAM chip data lines are directly connected to the Z80 data bus, so when the ULA pulls this trick, it fights with the RAM for access. In the original design, there’s a set of 470Ω resistors between the RAM and the ULA to limit the current flowing during this time.

How does this work then in my case? I’m guessing that the RAM chip I happened to choose has a lower drive-strength then the ULA and the second one wins. At least wins ‘enough’ that the CPU detects it as ‘low’ logic level, so everything works out OK. This however is not a good way and long-term can damage either of the two chips, so I should not run the machine this way for long.

Play it again Sam

While I was working on this board, I’ve noticed some tricks that people mention to get high-resolution graphics out of these machines. This technique involves including the /RFSH signal in the RAM chip-select logic. I didn’t have that in the original design, but it can be included – and with all the prior changes, I need a new design anyway. So, with all of this, the new – functional – schematic is the following:

Functional ZX81 memory expansion

I had some of these new boards made as well and put together one. I put it in the machine …

Turned it on, and … nothing. Nothing! I panicked and for a while contemplated tearing out what little hair I have left, but then it hit me: maybe I just have some soldering problems again? So back to under the magnifying glass, poring over all the pins and solder joints. I did indeed find three problems. After fixing them, finally the machine roared into life. As much of course as a ZX81 can roar…

Roar…

You will find the KiCAD project for the latest version here.

The curious subject of video

On the pictures above you might have noticed that I have a computer monitor connected up to my ZX81. In fact, as I mentioned before, this monitor was the trigger for the whole odyssey.

Of course the machine didn’t originally have a video output and the monitor in question is just that, a monitor, there’s no TV tuner in it.

That means that I had get the video output out. My original way of dealing with it was to simply take the video signal that originally went into the modulator and bring it out on a connector. Later, I’ve disconnected the modulator and brought the signal out on the original RF connector. This is the setup you see on the pictures.

Now – obviously – this works, but as it turns out it’s not ideal. As it also turns out there’s a ton of half- and misinformation on this floating around on the WWW, so I decided to do my best to clear things up.

My model, being an NTSC version, features a video output of the following kind:

ZX81 NTSC video output to modulator

Now, connecting this output to a video input of a monitor has some problems, but not what some mention on the ‘tubes’. The added current draw of the connected monitor example is rather minuscule. True, we would shunt the 390 ohm resistor with the 75 ohm input resistance of the monitor, however that only increases the ULA current by a mA or so. Should not be that big of a deal. However, if you put a scope probe on the supposed video output, you get the following waveform:

You see how ridiculously high the front porch and the black level of the video is? (Yes at this point I’ve borrowed a scope, this kind of surgery is impossible without one.) For reference, this is what it should look like:

NTSC signal levels per http://bealecorner.com/trv900/tech/RS170A.jpg

The peak-to-peak level of ~1V is OK, but the front porch should sit at 286mV wheres it is at almost 600mV for my machine.

Many people suggest we simply ‘buffer’ the video output using a BJT:

Video buffer per https://www.bytedelight.com/?page_id=3560

I’ve built this, but it doesn’t work at all. I don’t have a scope shot of it, but essentially the problem with this circuit is the following: the Vbe voltage on the transistor should be ~0.7V before it starts conducting at all. This means that anything below 0.7V gets translated to 0V on the output. When the input approaches the 1V peak level, the transistor opens up and drives about 1V to the output. In other words, this circuit should completely erase the front porch of the signal. Which indeed it does.

Now, the reason why that’s a problem is that monitors use the front-porch and sync levels to figure out signal amplitude and correct for DC bias (many video signals are AC-coupled) as well as for amplitude mismatch. If you erase the front porch, you screw with this circuit. After learning all this, I was surprised I saw an image on the screen at all!

There are many variations of this circuit floating around. They are all wrong for the same reason.

Others have suggested that we simply connect three diodes in series to generate the output signal. Again, it doesn’t take much brain power to figure out it won’t work either: while it is capable of restoring the back-porch level to where it should be, it also reduces the while level from 1V to about 350mV. This should – and indeed does – result in a very dark picture.

That discussion however gave me an idea: would it be possible to combine the two circuits to make something functional?

I came up with the following circuit:

Correct video buffer for ZX81

In the above, the voltage source ‘IN’ is the stand-in for the video output of the ULA. You see the three diodes as well as the emitter-follower buffer. Notice that I’m using the ULA output directly, not through the diode+resistor divider that’s part of the original circuit. I’ve also added the 75 ohm load that the monitor would present. To understand how this would work, you have to know the signal levels at the ULA output. Here’s what the scope-capture of that point (IN node above) looks like:

You can see that the black level (which is roughly the same as back-porch level) sits at about 2.2V. The white level is at around 3.2V. The bottom of the sync of course is 0V. We need a circuit that translates these levels to 0.28V, 1V and 0V respectively. Simulating the DC transfer function of the circuit above in LTspice results in this:

You see it does the job pretty well. 2.2V gets translated to 277mV, while 3.2V turns into 1V. Funnily enough, the portion between 2.2 and 3.2V input is fairly linear, so if the ZX81 would have more gray levels, this circuit would work with those quite well too. Alas, it doesn’t so matters it not.

The only possible downside to this circuit is that the output impedance is very low, much lower than the prescribed 75 ohms. However, as long as the input of the monitor is properly terminated, there shouldn’t be a big problem with reflections.

Even if that’s not the case, this shouldn’t be a big deal with a relatively short cable. The propagation delay of a 75 ohm coax cable is around about 4.5ns/m. NTSC video as roughly 6MHz bandwidth. That corresponds 166ns cycle time. If the delay of the cable is less than – say – 16ns or so, you have time for 10 reflections before you start seeing visual effects. That translates to more than 3 meters (10ft) of cable. You’re most likely OK.

I’ve built this circuit, gutted my modulator and put it in the old cage:

RF modulator: 0; Video buffer: 1

I was actually surprised to see that it worked the first go. The measured output signal was also what I expected from the simulations:

The monitor is also quite happy with the new video diet, I have a living fossil of a machine and I can savor all the typing pleasure of the ZX81 keyboard…

Print Friendly, PDF & Email