Cold Water

ZX Spectrum "Recoiled"

From my first days of home computing, I still have two ZX Spectrum 48K. The first one is my own one, which I restored in the previous part. This second Speccy was a donation from a friend. It was broken and written off as irreparable, so he wanted to throw it away, but I asked him to give it to me instead.

Let's find out what we have here…

Memorable Surprise

This Speccy also has an Issue Two board, but it seems to be a bit older because it has an older ULA 5C112E-3, while my own one has an ULA 6C001E-6.

Another Issue 2 board.

What surprised me was the tiny daughterboard that is used for IC26.

This one is having a tiny daughterboard though.

I first thought it was some kind of post-production fix for a PCB error, but it turned out to have a much simpler explanation. For the upper 32K RAM, Sinclair used eight 32KBit DRAM chips of various manufacturers. Those chips were actually 64KBit chips, but one half of the memory turned out to be defective after production, so they were sold with half of the capacity for cheaper.

To run a Spectrum, all eight of the chips need to have the defect in the same half. A wire bridge on the board then configured whether the "upper" or "lower" half of the RAM was to be used. For the OKI M3732 chips that were used on this board, the internal memory cell addressing is a bit different though. Let's put it that way, on these chips either the "left" or "right" half was defective. The tiny daughterboard just takes care of the necessary modification on the address lines to run the OKI chips. Maybe they have just been the cheapest around when Sinclair produced this batch.

Damage Assessment

In order to see anything, I first did the "composite mod" that I also did on my other ZX Spectrum. It just needs a wire and a few minutes of work, so it is well invested time even if this ZX Spectrum actually turns out to be irreparable.

After that, I connected the Speccy to the TV, took a deep breath, and then turned on the power.

This isn't looking good.

Yes, this computer is definitely broken.

New Coil

The first thing that should be tested on a broken ZX Spectrum is if the voltages are correct. The 4116 RAM chips need three of them: +5V, +12V, and -5V. The +5V were there, but instead of +12V I only got +7V, while the -5V were completely missing.

With further checks I found the culprit: the coil was shortened. And there must have been a lot of heat involved, as the isolation plastics was completely melted and got an almost black color. The left photo shows this coil, the right one shows a good coil for comparison.

The isolator is melted. The primary and secondary side are shorted.This is how a good coil is supposed to look.

That kind of damage usually happens when an expansion cartridge is removed while the ZX Spectrum is still powered, causing a short circuit on the power lines. This poor computer must have given one last smoke signal before its decease.

The coil was custom made for the Spectrum. One can still get remakes today, but they are quite expensive. So why not just wind a new one myself?

First I thoroughly removed all the old copper wire and the charred isolator plastics. I was hoping that I could just unwind the old coils and count the number of windings, but the isolation was melted to a single lump of plastic. The wire eventually tore, and I had to use a cutter to get the remains off the ferrite core. When I was done, it looked like the coil just exploded on my desk.

The battlefield.

Luckily, the circuit diagram gives us all the information we need to know.

The original coil wire had a diameter of 28AWG (or 0.32mm), so we need isolated transformer copper wire of the same strength. First we start with the inner coil. Wind a bit of the wire firmly around the pin marked in red on the next photo, then do 13 turns on the ferrite, then wind the wire firmly to the other pin. The windings on the ferrite don't need to be perfect, but should still be as tight as possible.

After that, we do the same with the outer coil, having 39 turns. It is important that both coils are wound in the same direction. It doesn't matter if both coils are wound clockwise or anti-clockwise, as long as you use the same direction for both coils.

First the inner coil with 13 turns, then the outer coil with 39 turns. Start with the pin marked red. Use the same direction for both coils.

Finally, use a lighter to remove the isolation on all four pins, then use flux and a bit of solder to fix the wire ends to the pins. Now check with a multimeter. Both the primary and secondary coil should have less than 1Ω, but there should be no resistance between both coils.

After that, the coil can be soldered to the board again. The fifth pin serves as a key for the correct orientation.

I could only get 0.35mm wire, so my coil got a bit too "fat".

A shortened coil always causes secondary damage, so I preemptively replaced the components that usually fail as well:

  • TR4: It can (and should) be replaced with a ZTX651, which is stronger and more reliable. They can still be found at good electronic retailers. I was researching for a standard transistor as replacement, but even though there were some types, the ZTX651 was always the strongest recommendation.
  • TR5: The original type is not available any more, but can be replaced with a ZTX751 or a standard BC557 (which must be mounted facing in the opposite direction).
  • D16: This can be any standard 5V1 Zener diode.

After that, I connected it to power, and (to my surprise, to be honest) all the three voltages were back and correct.

What's Next?

The picture on the TV was still unchanged, but I had already expected that more components would be damaged.

I checked the temperatures of the ICs with my finger. If you try this at home, be very careful because a broken chip can get so hot it can easily burn your skin within a second.

The ULA got warm, but that's normal. The CPU also got a bit warm, which wouldn't be a surprise on modern computers, but the Z80A is supposed to stay cold. I unsoldered it, and replaced it with a 40 pin socket and a used SGS Z80A CPU that I once recovered from a broken ZX-81.

I powered it up again, and it just worked! 🎉

So there was just a burnt coil and a broken CPU. This repair was much easier than I had expected.

Finishing Works

Like on my other ZX Spectrum, I first replaced all the old electrolytic capacitors. I also used a fresh 7805 voltage regulator, and thermal paste for better cooling.

My first Spectrum got a transparent case and a chrome faceplate. For this ZX Spectrum I decided to keep the original look, so I just replaced the broken keyboard membrane. The old faceplace had some visible dents and scratches, so it was replaced as well. I then washed the original case in warm water with a bit of dish detergent, and then put it all back together.

The restored ZX Spectrum 48K.

And that is the story of the two sisters who got a nice makeover, and are now fit for the next 30 years. 🙂

ZX Spectrum "Chrome"

I still own two ZX Spectrum 48K from my very early days of home computing. The first is my own one, I got it from my parents as a Christmas present back in 1985. The other one was owned by a friend. It was broken and couldn't be repaired, so he first intended to throw it away, but then gave it to me instead.

So here are the two sisters…

Two Sinclair ZX Spectrum 48K

This article is about the restauration of my own ZX Spectrum. There will be a follow-up for the other one.

Let's have a look under the hood. This Speccy has a standard Issue Two board, with a floating transistor on the CPU as an usual factory modification of that board.

My own Speccy. The ROM, ULA, and a 74LS532 are socketed.

It looks alright so far. Let's find out if it is still working.

Composite Mod

All home computers of that era were designed to be connected to the "antenna in" of an ordinary color TV. The TV was tuned to UHF channel 36 to receive the signal. The picture quality was quite okay back in those days, but poor for today's standards.

Today, almost all TVs have a composite input, so there is no need for modulating the signal any more. Luckily the ZX Spectrum can be easily modified to give a composite signal. First, the two existing wires on the side of the TV modulator are unsoldered and just bent to the side (so the mod can be reversed if desired). Inside the modulator, the resistor is disconnected from the RCA jack. Then a new wire is connected from the outside's former signal pad straight to the RCA jack.

The old two wires are removed. A new wire is connected to the left "signal" pad.Inside the modulator, the resistor is disconnected, and the wire is soldered to the RCA jack.

After that modification, the Spectrum can be directly connected to the TV's "composite in". The modification can be easily reverted, and there is no need to drill an additional hole into the case.

Testing

I have lost the original ZX Spectrum PSUs, but any stabilized 9 V PSU with at least 1.5 A will do as a replacement. It is very important to check the polarity of the barrel plug! Most modern PSUs have the positive pole at the inside of the plug, while the ZX Spectrum expects the positive pole at the outside:

Many Speccys certainly have been killed by using a replacement PSU with the wrong polarity.

I powered it up, and to my surprise it was still working!

Almost 40 years old, and still working.

All I would need to do now is giving it the usual technical overhaul.

Recapping

The first thing is to replace the electrolytic capacitors. The old ones dry out over the years, and lose their capacity. Some may even leak and damage the PCB.

To keep the old look, many people prefer axial caps with the classic shape and a blue (or at least black) color.

High quality axial caps are difficult to find and quite expensive. I chose to use Vishay capacitors with an expected lifetime of 2,000 h. However the reference photos of the retailer deceived me. The nice black "classic" caps turned out to have an odd shape, and an aluminum or plastic grey color. They are not of an inferior quality, quite the contrary, but they just don't look vintage. I still decided to use them.

Shiny new caps that should last for much longer.Sadly these ones don't have a classic look, but they will do their job.

There is a trap on the Issue 2 boards: the polarity of C46 is indicated backwards on the silkscreen. The capacitor must be installed with the positive end to the left.

C46 (the upper one) is correctly installed with the positive end to the left, while the silkscreen claims it's on the right.

After the recapping, I thoroughly washed the board with IPA and a toothbrush. Then it was time for another test.

Operation Successful…

…but the patient died. This is what I saw when I powered it up again:

This doesn't look good...

Obviously I broke something. 😯 But what?

I first checked the voltages, but they were all right. No chip was getting hot, except of the ULA, but that's normal.

The ROM chip is socketed, so I removed it. Without the ROM, the CPU always executes the same instruction (RST 38h), which fills the memory and results in a distinct screen pattern.

Screen pattern without ROM, indicating that the CPU is working.

The pattern was there, so the CPU was fine, but it had some noise in it. I suspected a broken RAM chip, and the signal on the data bus actually looked a bit strange on the scope. I started to replace a few suspicious RAM chips, but to make a long story short, it didn't change anything. I was clearly on the wrong track.

I tested the ROM, but it was fine. I swapped the ULA with the one from the other Speccy, but it didn't help either. I checked all the capacitors I had replaced, but they had the correct values and orientation, even that infamous C46.

What has just happened that damaged a previously working Spectrum so badly?

I noticed that, as the only standard chip, IC23 was socketed on that machine. It must have been from a previous repair, because unlike all the other soldering joints, the lead was yellowish there. When I touched the joints with the tip of my soldering iron, they were also sizzling. This was just scrap. I completely removed the old lead, and soldered in a new socket.

Could this have been the problem? I gave power to the Speccy. And yes, it was working again! 😄

I guess that when I cleaned the board with IPA, I partially dissolved the flux in these old soldering joints, making them cold. IC23 is used for the proper access timing of the upper RAM. With a bad timing, the upper RAM might just have disturbed the entire data bus.

Finishing Works

With the Speccy brought back to live, I did some final cleanups.

A defective TR4 is a common cause for a broken power converter. It was still working here, but I precautionary replaced it with a ZTX651, which is the more reliable successor type.

I also preemptively replaced the 7805 voltage regulator by a fresh one, and used thermal paste for better cooling. It's common in the retro scene to replace the 7805 with a modern step-down converter that does not need any cooling, but I decided against it. I like to feel the heat of a working ZX Spectrum.

The ribbons of the keyboard membrane got brittle over the years, and already started to break. Luckily there are new membranes available on the market. And since I was on it, I also ordered a transparent replica case, a black rubber keyboard mat, and a chrome faceplate. I especially like the idea of a transparent case making the inside of this old computer visible.

The restaured ZX Spectrum 48K "Chrome Edition"

The first of both sisters is restored now. The other one might be more difficult to restore though, as it was said to be "broken beyond repair". Let's find out.

R Tape loading error, Part 2

In the first part I showed how the Sinclair ZX Spectrum stored data on tape. This second part explains what is stored, and what causes a tape loading error.

The ZX Spectrum BASIC offers a SAVE command for saving all kind of data. It can be used to save a BASIC program, variable arrays, but also arbitrary parts of memory. These files are always saved in two separate blocks. The first block is called header. It contains the file name, data type, and other meta information. The second block follows about a second later and contains the data itself.

The internal structure of each block is identical. The first byte distinguishes between header ($00) and data blocks ($FF). The final byte is a parity checksum. Everything between these two bytes is the payload.

A header block always contains a payload of 17 bytes. The first byte identifies the file type, followed by the file name (10 characters), followed by the length of the data block, and closed by two optional parameters that have different meanings depending on the file type. The length and the two parameters consume two bytes each, with the lower byte coming first because the Z80 CPU is little endian.

This is an example header block of a screenshot:

00$00 = Header
0003$03 = Binary file (Code or SCREEN$)
0153S
0268h
0372r
0465e
0564d
062E.
077Az
086Fo
096En
1065e
11001BLength: 6912 bytes ($1B00)
130040Parameter 1, here: starting address ($4000)
150000Parameter 2, here: unused
20Parity

A screenshot is actually just a memory dump that starts at address $4000 (which is the starting address of the screen buffer) and is exactly 6912 bytes long (the ZX Spectrum has a resolution of 256×192 monochrome pixels plus 32×24 bytes color attributes, giving a screen buffer size of 6912 bytes).

For other file types, the two optional parameters have different meanings. For example, a BASIC program file stores the line number to start at after loading.

The final byte is the parity. It is used for error detection, and computed just by XOR-ing all the bytes that have been read. The result must be $00, otherwise a "R Tape loading error" is reported.

This kind of error detection is rather weak. Due to the nature of the XOR operation, two wrongs give a right. This means that when the block contains an even number of bad bits at the same position, they will be undetected. It is also not possible to correct reading errors, as the XOR operation only allows to identify the position of the bad bit, but not the actual byte that contained the error. More sophisticated error correction algorithms would have slowed down the loading process, though.

The parity is computed as a final step, after all the bytes have been read from the block on tape. For that reason, the loader can only decide at the end of the recording whether the loading was successful or not.

But then, why does the tape loading error sometimes appear while the block is still loading? Well, in the first part I have explained that the loading routine just reads an unknown number of bytes. It ends when waiting for a pulse change took to long. Now, if there is an audio gap on tape, the signal seems to end just in the middle of the block. It is then very likely that the parity checksum is wrong because there are still bytes missing.

Some simple copy protections made use of the way the Spectrum loads data from tape. A very common way were “headerless” files, where the header block was left out and only the data block was recorded on tape. The BASIC LOAD command was unable to read those files because of the missing header.

R Tape loading error

In the early time of home computers, at the beginning of the 1980's, hard disks and even floppy disks were too expensive for home use. The cheapest way for storing large amounts of data was the cassette tape. Cassettes and tape recorders were affordable and available in almost any household.

In this blog article, I'm going to explain how the Sinclair ZX Spectrum stored programs on cassette tapes. Other home computers of that time, like the Commodore 64 or Amstrad CPC, worked in a similar fashion.

Cassette tapes were designed to store audio signals like voice or music, so the inventors of the home computers had to find a way to convert data to audio signals. The easiest way is to serialize the data to a bit stream of 1's and 0's, and generate a long rectangular wave cycle for "1" and a short rectangular wave cycle for "0". This is what the ZX Spectrum actually does!

A short wave cycle is generated by giving power to the audio output for 855 so called T-states, and then turning off the power for another 855 T-states. A "T-state" is the time of a single clock pulse of the Z80-A CPU. As the CPU of a classic ZX Spectrum is clocked with 3.5 MHz, a T-state has a duration of 286 ns. The duration of a short wave cycle is thus 489 µs, giving an audio frequency of about 2,045 Hz. The long wave cycle is just twice as long.

Due to all kind of filters in the analog audio path, the rectangular signal is smoothed to a sinusoidal signal when played back. A Schmitt trigger inside the ZX Spectrum's hardware converts the audio signal back to a rectangular shape. Since the audio signal can have different amplitudes or could even be inverted, the hardware only cares for signal edges, not for levels. All that the loader routine now has to do is to measure the duration of the pulses, regenerate the bit stream, and put the bytes back together.

If you think that things cannot be that easy, you are right. 😄 The most difficult part for the loader is to find the start of the bit stream. If it is off by only one cycle (or even just a pulse), all the bytes are shifted by one bit, and the result is useless. All kind of noise on the tape makes it impossible to just wait for the signal to start, though.

For this reason, the recording starts with a leader signal, followed by a sync wave cycle, followed by the bit stream itself. The leader signal is just a continuous wave with a pulse length of 2,168 T-states, giving an 806 Hz tone that is displayed by red and cyan border colors on the TV. The sync wave cycle is a pulse of 667 T-States "on", followed by 735 T-states "off". After that, the actual data stream begins, which is displayed in blue and yellow border colors. When the last bit was transmitted, the data stream just ends.

So when the ZX Spectrum loads a file from tape, it first waits for the 806 Hz leader signal. If it was detected for at least 317 ms, it waits for the sync pulses, then it starts reading the bit sequence until there is a timeout while waiting for the next pulse.

It is a very simple way to store data on tape. And still, it is surprisingly reliable. After 30 years, I could recover almost all files from my old cassette tapes. Some of them were of the cheapest brands I could get my hands on back in 1987.

The only disadvantage is that this method is very slow. With 489 µs for a "0" and 978 µs for a "1", saving just 48 KBytes of data can take up to 6 minutes, giving an average bit rate of 1,363 bps (yes, bits per second). If we were to save a single 3 MBytes mp3 file that way, it would take almost 5 hours (and 5 cassettes with 60 minutes recording time each).

Some commercial games used speed loaders and copy protections. Speed loaders just reduced the number of T-states for the pulses, which increased the bit rate. Some copy protections used a "clicking" leader tone, where the leader signal was interrupted before the minimal detection time of 317 ms was reached. The original loader routine could not synchronize to these kind of signals, so it was impossible to read those files into copy programs. Those protection measures could still be circumvented by copying directly from tape to tape, but this only worked a few times due to increasing audio noise.

In the next article, I will take a deeper look at the bit stream contents, and I will also explain where the dreaded "R Tape loading error" comes from.

Z80 Disassembler

Some days ago, I was adding a Z80 disassembler to my tzxtools. I could not find one for Python, so I decided to write my own. The result fits into a single Python source file. This article is the Making-of…

The Zilog Z80 is an 8 bit processor. This means that (almost) all instructions only consume 1 byte. For example, the instruction ret (return from subroutine) has C9 as byte representation. Some commands are followed by another byte (as a constant to be used, or a relative jump displacement) or another two bytes (as a 16 bit constant or absolute address). Some examples:

C9------retReturn from subroutine
3E23----ld a,$23Load constant $23 into A register
C33412--jp $1234Jump to address $1234

Note that for 16 bit constants, the bytes seem to be reversed in memory. This is because the Z80 is a so-called little endian CPU, where the lower byte comes first. Some other processor families (like the 68000 ) are big endian and store the higher word first.

So there are 256 instructions only, which makes it pretty easy to disassemble them. I used an array of 256 entries, where each entry contains the instruction of the respective byte as a string. For constants, I have used placeholders like "##" or "$". If such a placeholder is found in the instruction string after decoding, the appropriate number of bytes are fetched, and the placeholder is replaced by the value that was found.

If we were to write a disassembler for the 8080 CPU, we were done now. However, the Z80 has some extensions that need to be covered, namely two extended instruction sets and two index registers.

One set of extended instructions is selected by an $ED prefix, and contains rarely used instructions. The other instruction set is selected by a $CB prefix and has bit manipulation and some rotation instructions.

EDB0----ldirCopy BC bytes from HL to DE
ED4B7856ld bc,($5678)Loads value from address $5678 into BC register pair
CBC7----set 0,aSet bit 0 in A register

For the $ED prefix, I used a separate array for decoding the instructions. The $CB instructions follow a simple bit scheme, so the instructions could be decoded by a few lines of Python code.

The Z80 provides two index registers, called IX and IY. They are used when the instruction is prefixed with a $DD or $FD byte, respectively. These prefixes basically use the selected index register instead of the HL register pair for the current instruction. However, if the (HL) addressing mode is used, an additional byte sized offset is provided. The index registers can be combined with the $CB prefix, which can make things complicated.

E5------push hlPush HL to stack
DDE5----push ixPush IX to stack (same opcode E5, but now with DD prefix)
FDE5----push iyPush IY to stack (now with FD prefix)
FD2180FFld iy,$FF80Load $FF80 constant into IY register
DD7E09--ld a,(ix+9)Load value at address IX+9 to A register (offset is after opcode)
CBC6----set 0,(hl)Set bit 0 at address in HL
FDCB03C6set 0,(iy+3)Set bit 0 at address IY+3 (offset is before opcode)

When the disassembler detects a $DD or $FD prefix, it sets a respective ix or iy flag. Later, when the instruction is decoded, every occurance of HL is replaced by either IX or IY. If (HL) was found, another byte is fetched from the byte stream and used as index offset for (IX+dd) or (IY+dd).

There is one exception. The examples above show that the index offset is always found at the third byte. This means that when the index register is combined with a $CB prefix, the actual instruction is located after the index. This is a case that needed special treatment in my disassembler. If this combination is detected, then the index offset is fetched and stored before the instruction is decoded.

Phew, this was complicated. Now we’re able to disassemble the official instruction set of the Z80 CPU. But we’re not done yet. There are a number of undocumented instructions. The manufacturer Zilog never documented them, they are not quite useful, but they still work on almost any Z80 CPU and are actually in use.

Most of them are covered just by extending the instruction arrays. Additionally, the $DD or $FD prefixes do not only affect the HL register pair, but also just the H and L registers, giving IXH/IYH and IXL/IYL registers. This is covered by the instruction post processing. A very special case is the $CB prefix in combination with index registers, giving a whole bunch of new instructions that store the result of a bit operation in another register. This actually needed special treatment by a separate $CB prefix instruction decoder.

Finally, the ZX Spectrum Next is going to bring some new instructions like multiplication or ZX Spectrum hardware related stuff. They were again covered by extending the instruction arrays. The only exceptions are the push [const] instruction where the constant is stored as big endian, and the nextreg [reg],[val] instruction that is (as the only instruction) followed by two constants.

And that’s it. 😄 This is how to write a Z80 disassembler in a single afternoon.