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=WZ; F=WZ 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" 230 RETURN 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…