Spectrum compatibility issues

You may have, at some time, tried to run certain software either under emulation or on certain real Spectrum models and have found yourself uttering expletives at the screen when the game crashes, hangs, or exhibits other behaviour that makes the experience decidedly sub-par.

Why does this happen, I hear you say, surely a Spectrum is a Spectrum, right? Wrong, there are subtle differences in the hardware and firmware of the various 128 models compared with the original 48K machine that affect software in either subtle or obvious ways. I’ll cover these aspects below.

Dude, where’s my ROM interrupt vector table?

When the original Spectrum 16/48K range was launched, it did so with an incomplete ROM. It was intended to have the ROM include all of the commands and code to support the Interface 1 and Microdrives from the start, but due to Steven Vickers leaving Sinclair before release, this was never finished. (This was later added by adding an onboard ROM to the Interface 1 that got paged in under certain circumstances, but that’s another story). The important bit to note is that there’s just over 1K of free space in the 48K Spectrum ROM, and this is all filled with FF’s.

Before long, programmers that wanted to set up custom interrupt routines noticed this space and figured out that they could use this as an interrupt vector table. This was a cheeky way of saving 257 bytes of RAM that could be put to good use elsewhere, and all was well with the world until the Spectrum 128K was introduced.

With this model, the 48K ROM (ROM 1) was patched to include keypad scanning functionality and the test card code within the previously unused space. This was disastrous for software that made use of this ROM space for their interrupt vector table, as instead of arriving at their custom interrupt routine as planned, the processor typically executed off into the wild blue yonder, so to speak.

There wasn’t really any workaround for this apart from software houses issuing ‘128K fixed’ versions of their games that didn’t rely on the ROM for the interrupt vector table.

Footnote – this worked by setting the I register to a value between 0x39 and 0x3B. When an interrupt occurs and IM2 mode is set, the processor takes the two bytes pointed to by I * 256 + 0xFF and executes a call to this address. This would end up being 0xFFFF, due to the contents of ROM at the location being pointed to. The programmer would have stored 0x18 (JR) at this location, and since the first byte of the ROM is 0xF3, the processor would execute a JR 0xF3, which sets the program counter to 0xFFF4. The programmer would then have placed a JP to their actual interrupt service routine here.

Fun with Paging

Running Jetpac in 128K mode is notorious for a particular problem in that once the game starts, the screen goes blank, however one can still hear the game continuing in the background.

What’s happening here is that in some Ultimate games, there is debugging code that attempts to write data to a hardware device on port 0x7FFD. This is fine on a standard 48K machine where there’s nothing to respond to this port, but on a 128K machine this is the paging and screen switching register! The shadow screen gets switched to (which is blank), and the game happily continues oblivious to the fact that the user can’t see what’s going on.

On a standard Spectrum, loading from tape, this is easily worked around by selecting 48 BASIC mode or issuing the SPECTRUM command from 128 BASIC (which both lock the paging register.

DivMMC users have a bit more work to do, though, as these machines start in what’s called USR 0 mode. This is effectively 48 BASIC but with the paging register unlocked. So before loading Jetpac, you need to issue the command OUT 32765, 48 to lock paging before loading the game.

My Spectrum is Faster Than Yours

The 48K Spectrum was infamous for a phenomenon known as ‘dot crawl’. This manifests itself most clearly when red and green colours (or other combinations) are next to one another on screen, causing a moving pattern effect.

This occurs because there are two clock sources on the 48K machine – a 14MHz crystal that clocks the ULA and the CPU (when divided by 4 to produce 3.5MHz). There’s also a 4.43MHz crystal that is used by the LM1889 YUV to composite encoder chip. Unfortunately, these frequencies don’t share a fundamental base frequency which leads to the dot crawl effect, as the video dot clock is never in sync with the PAL colour clock.

For the 128K machine, this was corrected by replacing both clock sources with a single 17.7345MHz crystal – this can be divided by 4 to produce the 4.43MHz required for the PAL encoder (now a TEA2000), and by 5 for the now 3.54MHz CPU clock.

The net result of this is that the 128K machine runs slightly faster than the 48K machine (70908 clock cycles per 20ms/50Hz frame as opposed to 69888). Screen timings are altered slightly due to this (228 x 311 T states per frame on the 128, 224 x 312 on the 48).

Why does this matter? Well, any game that does fancy border effects and relies on their being exactly 224 T-states per line for timing purposes is going to look wrong on 128 machines. The most obvious case of this is Dark Star, which uses careful timing to draw a space invader graphic in the border area – this is badly skewed on 128 machines.

Dark Star high score table. 48K on the left, 128K with timing issues on right.

Floating Bus, or Lack Thereof

This is covered in more detail in the article covering memory contention and the floating bus. The net effect is that games that rely on the floating bus for video synchronisation are going to hang or perform jerkily on +2A and +3 machines where this quirk of the hardware is absent.

Memory Contention differences (or, It Works on My Machine)

When the orignal 128K Derby technical specification was published, it described the 8 pages of RAM that could be paged in as either contended or uncontended (see the above linked memory contention article for more detail). As code runs slower in contended RAM, programmers would not place timing critical code (like loading routines) in these contended banks, which according to the spec were banks 4,5,6 and 7.

This would have been fine, except for an error in the PAL chip which resulted in the contended banks being 1,3,5 and 7 instead. Programmers either wrote their code against the spec and encountered unexpected results, or observed actual behaviour – which caused problems down the line when the +2A and +3 range were architected to implement contention as per the original spec!

This error was carried over from the 128 to the grey +2 due to the limited development time the +2 had after the Amstrad takeover of Sinclair.

An example of the net result is Fantasy World Dizzy, which contains sampled speech which sounds wrong on a 128 or grey +2, and correct on a +2A or +3.

Back to Technical