Z80 emulation: Using Z80 Instruction Exerciser (Zexall/Zexdoc)
Z80 emulation: Using Z80 Instruction Exerciser (Zexall/Zexdoc)
Jeff Avery on the Web
Using Z80 Instruction Exerciser (Zexall/ /Zexdoc)
After I wrote my Exceltronix Multiflex Super System Z80 emulator, I discovered that some CP/M programs did not run correctly. I found a program on the Web called Z80 Instruction Exerciser that runs tests on a Z80 emulator and checks that its instructions give the same results as would a hardware Z80 CPU. There are two versions of the program: Zexall and Zexdoc. The Z80 microprocessor has eight flags but only six of them are documented. Zexall tests that all eight flags are set correctly while Zexdoc tests only the six documented flags; both versions test that registers and memory are set correctly. Since the undocumented flags do not affect programme execution, I decided to go with Zexdoc.
I downloaded Zexdoc from http://mdfs.net/Software/Z80/Exerciser/ and loaded it onto one of my emulated floppy disks. I found from http://mdfs.net/Software/Z80/Exerciser/Spectrum/ that: “There are 68 tests, each of which take about 3 to 4 minutes, so the whole program can take over four and a half hours!”.
Running at 200 instructions per millisecond, the first three tests took 48 minutes. Upping the emulation speed to 1,000 instructions per millisecond brought the time down to 10 minutes. The results were that the first two tests failed while the third was successful. When continuing with the other tests, the emulator stopped with an error message “Invalid instruction”.
When I investigated the invalid instruction, I found that Zexdoc was testing many undocumented instructions. These are instructions not listed in the Zilog documentation but which produce some sort of a result. In my emulator I had coded only the documented instructions and had no interest in the undocumented ones. However, if I was going to be able to use Zexdoc without it halting in mid-test, I had to go back and add the undocumented instructions to my emulator. I found the .pdf file: The Undocumented Z80 Documented was invaluable in helping me do this. Using Google search, this file can be found at several sites.
For many of the other errors, I suspected that my implementation of the Z80 carry flags, particularly the half-carry flag was incorrect. I found the webpage http://www.z80.info/z80sflag.htm very useful in checking my code. After doing this and making some other corrections, 59 of the 67 tests now reported an O.K. status. The other eight were proving problematic.
One test that was still failing was the daa,cpl,scf,ccf test and I suspected that it was the DAA instruction that was to blame. I checked that I had implemented it according to the documentation found in Zilog’s Z80 User Manual (.pdf) and also found several other methods on the Web forums that purported to provide a working DAA emulation. None worked. Finally I found an SMS Power! Forum page which told me: all documentation about how the DAA instruction works handle only valid input combinations. Zexall/Zexdoc, however, tests DAA after any operation. To properly handle all cases, it recommended The Undocumented Z80 Documented, a .pdf file which can be found, using Google search, at several sites. I downloaded it and changed my DAA logic to conform to its instructions and it worked!
As I made my way through the Zexdoc tests, it became a problem waiting for the early tests to complete before it got to the test I was interested in. Not having had any luck in finding a macro assembler that would successfully re-assemble Zexdoc, I wrote an assembler routine to modify the Zexdoc machine code. It changed the Zexdoc code so that it shows a modified start-up screen, showing a list of the 67 tests and asking the user to select one. To implement it, I loaded Zexdoc into the Temporary Program Area (TPA) and then loaded my assembled routine on top of it, thus patching the code. Finally, I saved the result as a new .com file.
How my fix works is that Zexdoc begins with a routine to display its title and then runs a short control loop to run through all the tests. At the start of the loop it gets the address of the first test. If this is modified by adding two bytes per test, it can be changed to start at any specific test. When it has finished a test it jumps back to the beginning of the loop. If this jump instruction is changed to no-ops, the programme flow will fall through and end. With this improvement I was able to zero in on the problematic tests.
Unfortunately, Zexdoc doesn’t tell you why a test fails, only that it does. While running the test it generates a number called a cyclic redundency check (crc) and compares it to the crc that had previously been generated on hardware. To fix my remaining problem opcodes, I needed to be able to run a test on a single instruction. I found the same SMS Power! Forum page mentioned above to be useful in explaining how to read the Zexall/Zexdoc macros to determine which instructions are being executed. For instance, looking at the Zexdoc source code, the first macro is:
adc16:db0c7h; flag mask
tstr0edh,042h,0,0,0832ch,04f88h,0f22bh,0b339h,07e1fh,01563h,0d3h,089h,0465eh
tstr0,038h,0,0,0,0,0,0f821h,0,0,0,0,0; (1024 cycles)
tstr0,0,0,0,0,0,0,-1,-1,-1,0d7h,0,-1; (38 cycles)
db0f8h,0b4h,0eah,0a9h; expected crc
tmsg'<adc,sbc> hl,<bc,de,hl,sp>....'
The last line, tmsg '<adc,sbc> hl,<bc,de,hl,sp>....', causes a message to be displayed on the screen to announce that Zexdoc is testing the ADC and SBC Z80 instructions and that it is testing the opcodes that load the HL register by adding the contents of the BC, DE, HL or SP register. Therefore, the instructions being tested in this first test are:
ADC
ADC
ADC
ADC
SBC
SBC
SBC
SBC
ED4A
ED5A
ED6A
ED7A
ED42
ED52
ED62
ED72
HL,BC
HL,DE
HL,HL
HL,SP
HL,BC
HL,DE
HL,HL
HL,SP
The first tstr line of the above macro has, as its first parameters, 0edh,042h which identifies that the numerically lowest opcode to be tested is 0E42h (the SBC HL,BC instruction). The second tstr line has the hex parameter 038h (i.e. binary 0011 1000) which denotes that those three bits of the hex 0E42h opcode will be incremented to generate the remaining opcodes to be tested (i.e. 0xED52, 0xED62 etc.). Thus, by changing the parameters, a single instruction can be tested. E.g. changing the first tstr parameters to 0edh,072h and the tstr parameter to 000h, will cause Zexdoc to test only the SBC HL,SP instruction.
After changing these hex parameters to tell Zexdoc to test a single instruction, Zexdoc will then generate a new crc for just that one instruction. It would compare this crc to the crc previously generated on hardware when running all the opcodes for that test so the comparison would, of course, fail. What I needed to do next was to get the Exceltronix Multiflex Super System hardware running again so that I could run the same single-opcode test on the hardware to obtain a new hardware crc for manual comparison. This is what I did.
With this help I was able to fix the remaining problems and get a clean bill of health from Zexdoc on all 67 tests.
HL <- HL + BC + Carry
HL <- HL + DE + Carry
HL <- HL + HL + Carry
HL <- HL + SP + Carry
HL <- HL - BC - Carry
HL <- HL - DE - Carry
HL <- HL - HL - Carry
HL <- HL - SP - Carry