Cold Water

Retro

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.

Reviving a Sony CDP-991, Part 5

The player is playing again. Time for some final cleanings.

Fifth Problem: Crackling Volume Pot

The volume assembly with the pots on the left. Just spray into the hole. After the calibration part, this is going to be a walk in the park. Or so I thought.

On the CDP-991, the playback volume can be changed via remote control. There's a motor that actually drives the volume knob and the potentiometers. The custom file feature allows to store individual volume settings for different CDs.

Due to the construction with the pots and the motor, the volume control assembly is rather big and is mounted to the front panel with screws. I removed the screws and the volume knob, but still could not remove the assembly for cleaning. It was still fixed somewhere else. The service manual gave no hint about how to remove it.

So I just tried my luck and sprayed some contact cleaner into the holes of the three pots close to the front. It worked, and saved me from finding a way to remove the assembly.

And that's it... I closed the case and polished it with a microfiber cloth and a soft cleaner. Then I put batteries into the remote control.

Welcome back to the living room, my good old CD player! ๐Ÿ˜€

Retrospective

Looking back, I am surprised how much is still working and in a good shape after 27 years. I am also surprised that I could still get a good service manual, and all the necessary replacement parts.

For most parts of the restauration, I just needed a screwdriver, cotton buds, isopropanol, and grease. So basically it can be done by anyone who isn't all thumbs. Just the pickup replacement and the calibration needs more elaborate equipment.

Reviving a Sony CDP-991, Part 4

The CD player finally plays music again, but still has trouble with the higher tracks.

Fourth Problem: Worn out Laser

Let's find out how worn out the laser actually is. I soldered a wire to the RF test point and connected it to a scope.

The service manual recommends to use a special test CD, but you can use any CD for the RF test. I found out that older CDs gave better results, probably because modern production methods are cheaper and less reflective. Do not use CD-R, as these discs use a dye and are not as reflective as standard CDs.

According to the service manual, the signal should have an amplitude of about 1.2Vpp (+0.2V, -0.4V).

The actual amplitude is just above 600mVpp, far below the required minimum of 800mVpp.

The first thing one should try now is to clean the lens. I used a cotton bud and 100% isopropanol for that. After that, the amplitude was a little better with about 880mVpp, but still at the minimum.

I could now recalibrate the player and try to get the best out of it.

But honestly, a new optical pickup module for this player can be ordered at the Bay for less than โ‚ฌ20. It's not genuine, since Sony is not producing the KSS-240A any more. But I am curious how good that remake actually is. If it should be worse, I could still continue to use the original one.

The optical pickup is very sensitive to static discharge. If you try the replacement at home, use an antistatic wrist strap and other ESD protective measures while handling it!

The replacement of the pickup was much easier than expected. Only a plastic clip is holding the metal shaft of the sled in place, so it could easily be pulled out. After that, I could remove the old pickup.

And while I was at it, I thoroughly cleaned the shaft and the plastic parts from the old grease again, and reapplied a bit of fresh silicone grease. I also applied a little bit of grease to the gears that are moving the sled.

Then I inserted the new pickup and fixed it with the shaft. The first test with the new pickup brought good news, and bad news. The good news is that the replica pickup is working, and the RF amplitude is even above the 1.4Vpp upper limit now.

The bad news is that the drive was making a loud and awful whistling noise now. So I recalibrated the player as described in this restauration project of a similar player.

The calibration was successful. The whistling noise is gone (except on track changes), and the player plays all the tracks again. I am sure an experienced technician can calibrate it a lot better, but I'm happy with the result.

And thanks to my proper cleaning, the sled noises are also much better. By all means good enough considering the age of the player.

I'm almost done! There's just a tiny problem left: The crackling volume pot.

Reviving a Sony CDP-991, Part 3

In the last part, I made the tray operational. It's a precondition for this part: Inserting a CD.

Third Problem: Stuck Sled

In the opened tray, I put a random CD from my shelf. Then I pressed the close button. The tray closed, and a few seconds later, the player showed the number of tracks and total playback time. Yaaay! ๐ŸŽ‰ The optical pickup seems to be working.

Daringly I pressed the play button, the disc started spinning, and then...

Nothing. No playback. Just silence.

I also noticed that the characteristical noise of the moving sled was missing when I changed through the tracks.

My first thought was that the laser was worn out after all. But then, on the other hand, the pickup would not have been able to read the CD's table of contents. It rather seems that the sled was stuck.

The optical deck with the pickup on the sled I disassembled the drive again. The pickup moves on a metal shaft and a plastic strip. Both were greased. I carefully removed the old lubrication with cotton buds and isopropanol, then applied a tiny bit of fresh silicone grease.

Next attempt... And this time, it was successful! After about 15 years, I heard music from my good old CD player again!

But I'm not done yet, as I already spotted some more problems. The player refuses to play higher track numbers, and the sled is still making strange noises on a track change. Also, the volume potentiometer crackles awfully and needs some cleaning. More of that in the next parts.

Reviving a Sony CDP-991, Part 2

In the first part, I powered up my CDP-991 again after 15 years. Next step: Open the tray.

Second Problem: Weak Tray Belt

The replaced tray belt I pressed the "open/close" button, and the tray opened. I pressed the button again, and the tray closed. Almost. After a few seconds it opened again. Obviously, the mechanism got too weak to completely close the tray.

The exploded view in the service manual revealed that the tray is moved by three gears and a belt. I removed the drive and then the tray, to expose the tray belt. Indeed, it got flabby over the years.

Warning: There are live parts inside the case. Before opening it, always unplug the power. If you are going to plug in the power while the case is open, always take appropriate measures to protect yourself from accidentally touching live parts. If in doubt, better ask a trained person for help.

It was easy to find a new tray belt on the Bay for just a few euros. I ordered one, and it was already delivered the next day.

After replacing the belt and assembling the drive, the tray finally opens and closes now. It is a clever construction. The top gear moves the tray. When it is almost closed, the gears are pushed sideways and turn the cam below (the silver part in the photo). The cam pushes up the optical deck and clamps the CD to the spindle. This part of the movement takes quite a lot of force. Too much for the old and worn-out belt.

It is a good idea to replace the belt on spec, after all those years. If the tray still won't close, the culprit might be the switches on the tray motor assembly. On the photo, you can see their "tongues" to the left and right of the motor. These switches detect when the tray is fully opened or closed. The contacts may have become dirty over the years, and just need some cleaning. Since my player's tray is operating normally now, I postponed this "fun" to a long and boring winter's day. ๐Ÿ˜‰

The next step is critical. Is the laser pick-up still operational after all those years? Let's find out! I'm going to insert a CD.