Reversing the ZX Vega: The Emulator

If you’ve been reading this, I’ve been (over a couple of years) messing around with my ZX Vega to try and work out how it works. You can read part one, where I look at the firmware (now no longer available) and part two, where I look at the hardware.

For a change I’m going to look at the emulator to work out which flavour of Spectrum emulator it is. From part one, my original supposition is that the device is running the fuse emulator.

[Quick note: as this is referring to stuff on the Spectrum, I’m using $ as a signifier for a hexadecimal number as opposed to the C standard 0x or the Acorn & which I grew up with]


The thing about emulators is that they are written from the knowledge available about the device at the time. Some stuff, such as the contents of the system ROMs can be extracted from live devices. Other stuff has to be gleaned from the datasheets and/or behaviours seen on the real systems.

Why is this important? Well, the Spectrum is based on a NEC D780C-1, which is a version of the Zilog Z80, which was based on the Intel 8080. This means that we can analyse how the emulator handles the CPU in unusual circumstances.

Specifically, we’re looking at undocumented op-codes. Unlike sensible modern CPUs, the older 8-bit CPUs build commands up on certain patterns of bytes that were physically mapped onto the silicon.

This meant that by providing unused opcode it is possible to cause strange effects. By mapping these out, we can fingerprint the emulator in use.

It should be noted at this point that when fuse was first written the Z80 had not been fully mapped out. This has happened a number of times and the information is publicly available, meaning there are less unknowns with how the Z80 behaves. Support for some facilities has improved and later versions of fuse show a different signature.

There are a number of test suites for emulators out there, but I’m going to concentrate on one specific aspect: the MEMPTR register. Because I’ve made the assumption the Vega is running fuse and fuse didn’t support MEMPTR until 2017, which is a long time after the last Vega firmware release.


The Z80 used a group of 16 bit registers, some of which could be split into two 8 bit registers . For example the register BC coud be used as two 8 bit registers, B and C.

In total there are four of these registers that can be easily accessed: AF, BC, DE and HL. These registers are special because they are duplicated to allow quick swapping of data, the duplicates are known as AF’, BC’, DE’ and HL’.

There are some other, well known 16 bit registers that cannot be as easily read, these are the two index registers, IX and IY; the stack pointer, SP; the program counter, PC and the interrupt register, IR.

There is one final 16 bit register, WZ, which is often mistakenly thought to be duplicatible. This register is also know as MEMPTR. It is used as a temporary holder for instructions that directly access memory without having to use the stack. For example, the instruction LD A,($8000) will load the accumulator with the byte at the contents of the memory address $8000. When this instruction is found, the value of the memory address needs to be stored somewhere – it is placed in MEMPTR. This goes back to the Intel 8080.

It is meant to be totally unaccessible from machine code. So, this is all good; expect, it was noted that the BIT n, (HL) instruction produced weird effects in bits 3 and 5 of the flags register (the F part of the AF register, or F’ of the AF’ register). Specific it will copy bits 11 and 13 of MEMPTR to bits 3 and 5 of F.

This now gives us a technique we can use to read something from MEMPTR: we can write a specific value to it and read some of it back with BIT n, (HL). As reading the whole value of MEMPTR through two bits would take a long time; we could manage this by writing the four known values for bits 11 and 13 (i.e. set them to 00, 01, 10 and 11) and retrieving them.

Although we can’t directly read the flags register, we can push it to the stack and retrieve it into another register. Here’s this is Z80 code (the first Z80 I’ve written in nearly 30 years, I’ve always preferred 6502 or ARM):

01 FF 27    LD BC, $27FF   ; After this WZ will be $2800, bit 13 = 1; bit 11 = 1
0A          LD A, (BC)     ; WZ = $27FF + 1
CB 66       BIT 4, (HL)    ; F[3]=WZ[11]; F[5]=WZ[13]
F5          PUSH AF        ; push AF onto the stack
C1          POP BC         ; pop it into BC
79          LD A, C        ; Take the lower byte (i.e. F)
E6 20       AND $20        ; AND with $20 - i.e. mask out bit 5
47          LD B, A        ; Push into B (for return to BASIC)
79          LD A, C        ; Take F again
E6 08       AND $08        ; AND with $08 - i.e. mask out bit 3
4F          LD C, A        ; Push into C (for return to BASIC)
C9          RET            ; Return to Basic

So this will result in just bit 5 in B and just bit 3 in C. We’re using BC as the Spectrum USR command will return the value of BC to BASIC.

So we can now run this in Spectrum BASIC and recover some of WZ; using this noddy BASIC program:

10 FOR a=32768 to 32784
20 READ j
30 POKE a,j
40 NEXT a
50 LET r = USR 32768
60 PRINT r
70 DATA 1, 255, 39, 10, 203, 102, 245, 193, 121, 230, 32, 71, 121, 230, 8, 79, 201

If we run this we get back the response 8200, which is $2008 in hex. This means that bit 5 is set in B and bit 3 is set in C; which is exactly what we wanted. This is the same response in both fuse (non-MEMPTR supporting version) and ZXSpectrum4.NET (which supports MEMPTR).

Okay, lets check all four values:

10 FOR a=32768 TO 32784
20 READ j
30 POKE a,j
40 NEXT a
50 LET p=39: LET v=8200
60 GO SUB 200
70 LET p=31: LET v=8192
80 GO SUB 200
90 LET p=7: LET v=8
100 GO SUB 200
110 LET p=0: LET v=0
120 GO SUB 200
130 STOP
200 POKE 32770, p
210 LET r=USR 32768
220 IF r=v THEN PRINT (p+1), "Pass"
240 DATA 1, 255, 39, 10, 203, 102, 245, 193, 121, 230, 32, 71, 121, 230, 8, 79, 201

So, if we run the above on ZXSpectrum4.NET we see that they all pass:

But if we run it on our old version of fuse, only the first test passes, further debugging shows that fuse always sets the flags to 1 and 1:

Running on the Vega

To actually prove what the Vega runs, we need something a bit better than my proof of concept above.

Fortunately there are a number of emulator test suites out there that fully test undocumented and unusual opcodes.

So in theory if the results from one of these test suites matches up between fuse and the Vega we can be very certain what the Vega is running.

I chose the RAXOFT z80memptr test suite to try this out.

No surprise, it works correctly on ZXSpectrum4.NET:

When we try this on fuse, as this is the fuse version before MEMPTR support was added, it fails on every test, running on my Windows box:

So what happens on the Vega? I think you can already guess – it gets the exact same results as the old version of fuse. In this case I need to apologise for my bad photography:


Now this is by no means final that it doe or does not prove that the Vega is running fuse. But if it isn’t running fuse, it is quite indicative that the emulator it uses it very similar to that of fuse.

The only way to really confirm this would be to get further into the device as it is running…

Reversing the ZX Vega: Hardware

It’s been a while since I wrote part one. I promised I’d have a look at the hardware on the Vega.

Here is the front of the board:

And the rear:

As can be seen from the front photo, the components are quite simple, with the major ICs being:

  • CPU (in red), an NXP MCIMX233DAG4C ARM based processor
  • Storage (in yellow), a Spansion FL512SA1F01 512 Mb SPI Flash chip
  • Memory (in purple), an Alliance AS4C8M16D1-5TCN 128 Mb DRAM

Note memory sizes are in Mbits – i.e. 1024 * 1024 bits; to get these in bytes we need to divide by 8, so that’s 64 MB of storage and 16 MB of RAM.

The storage chip is NOR SPI flash. SPI flash is relatively simple to read and (mostly) follows a standard core of commands defined by JEDEC.

I’ve rigged this up through a custom cable, from a 16 pin Pomona clip to one of my Raspberry Pis (as it gives me a lot of control over what I can do).

I have a few python scripts written to read NOR flash over SPI. One of these is a raw command line interface to the JEDEC op codes.

The first step here is to identify I can actually talk to the chip, this can be performed by using the RDID (opcode 0x9f), RES (opcode 0xAB) and REMS (opcode 0x90) commands. These should return the same information about the chip identifying the manufacturer and the density of the device which should tell us the size.

This produces the below, which isn’t quite right, the manufacturer ID is AMD and the density would work out as 4294967296 bytes or 4096 MB, which is a bit silly. We know it should be about 64MB:

A quick test to see whether we can read data, using the READ (opcode 0x03) command. Ah; we’re reading data. That’s a good thing, maybe the identification results aren’t quite perfect.

Using flashrom fails to identify the chip, which means flashrom won’t even try to read it:

pi@raspberrypi:~/spireader $ flashrom -p linux_spi:dev=/dev/spidev0.0
flashrom v0.9.9-r1954 on Linux 4.9.24-v7+ (armv7l)
flashrom is free software, get the source code at

Calibrating delay loop... OK.
Found Generic flash chip "unknown SPI chip (RDID)" (0 kB, SPI) on linux_spi.
This flash part has status NOT WORKING for operations: PROBE READ ERASE WRITE
The test status of this chip may have been updated in the latest development
version of flashrom. If you are running the latest development version,
please email a report to if any of the above operations
work correctly for you with this flash chip. Please include the flashrom log
file for all operations you tested (see the man page for details), and mention
which mainboard or programmer you tested in the subject line.
Thanks for your help!
No operations were specified.

So, I’m going to use my own code, which allows you to read it from a web browser with a one click solution, as shown below:

Because the chip didn’t respond correctly to the RDID call I had to make a couple of amendments to not try and read too much data from the chip.

So, I know have the file read from the onboard storage and… It’s exactly the same as the firmware, meaning that the CPU decrypts it on the fly, which, I’d expected. Bugger.

Right, onwards, if you look closely at the photos of the rear of the board, there are a couple of test points that are visible: one in-circuit programming type header at the top (red) and two sets of three test points a bit further down (yellow, marked J4 and blue marked J3) .

Using a multimeter in continuity mode to attempt to buzz out where these go shows me that the connector marked J4 connects to ground and the PWM0 and PWM1 pins of the CPU, according to the datasheet these are commonly used for Debug UART. J3 has 3.3V and a connection to the DEBUG pin of the CPU, this is used for SJTAG.

The header at the top only appears to buzz to ground and VCC, this is probably due to components between the connectors and the pins they connect to. This is something to look into later.

I can’t test the SJTAG connection with the tools I have with me, but UART is a simple serial protocol that uses two wires for transit and receive and is commonly used for consoles on devices.

A quick trip to the garage, where I keep the soldering iron, allows me to tack some wires to the board; now I can rig up a logic analyser to allow me to monitor those pins whilst I boot my Vega.

And what do I get… not quite what I expected:

Looks like they updated the bootloader text screen. Unfortunately that’s it, there’s nothing else as I continue to use the Vega. This really isn’t going anywhere fast unless I can either get the SJTAG interface or the other header working.