Further info on my radical approach ... I'm going to try sticking in a PSoC4 running a kind of Z8 emulator!
Initially I was considering a pure Z8 instruction emulator, however I don't think the performance will be there, the PSoC is 48MHz vs. the Z8 at 8MHz and the arm is pretty efficient, but for full emulation I don't think it's enough headroom to get close to native speed given the emulation complexity of some of the instructions that the Z8 handles in a very few clock cycles. [I'm not sure that performance is a particular issue in this case, so may be something to experiment with later.]
So option 2 is to re-spin the firmware and effectively translate it into C with probably some assembly if needed, although the compiler will probably be better at optimising than I would be.
I have written a simple disassembler in Perl ... stage 1 was to completely match the output of the MAME disassembler which is done (with the exception of the indexed LD instructions for which the datasheet is really vague and the MAME one seems to disagree with ... but thankfully it's not a problem.)
Stage 2 was to update the disassember to actually follow the code paths so that I could work out which bits of the firmware were code and which were data (which is the reason why the indexed LD isn't a problem!) -- this is also done.
The next step here is to update it again to isolate truly independent subroutines (i.e. ones that aren't jumped into by something else) so that they can be pulled out as separate functions, and then to start looking at detecting reads and writes to the control registers. The actual control registers look to be simple, RP is only set to 0xF0 in one place and is then followed by a load of RP relative code, the rest of the firmware uses a fully qualified read or write (for want of a better phrase.) So I'm working on having the disassembler identify RP relative access that can be fully qualified because no other code path gets in between the RP set and the access. The port access reads/writes are going to be a little more challenging and unfortunately some might need to be checked at run-time.
There is a minor issue in the keyboard interrupt code since this uses a jump table in the firmware, so I think this is probably going to have to be manually re-written in C, although I want to try to avoid this if I can so the code translation is as automated as possible to cope easily with different versions.
Other than that, from a software perspective, I think this is entirely doable. I would like to get my hands on other versions of the actual Z8 bit of the firmware.
Hardware wise ... I've looked at the PSoC capability of driving the "external bus" using pure software, and whilst I think it might work, it's a bit fraught, it's at the limits of the performance of the device (using direct register writes, none of this API rubbish), interrupts would probably need to be disabled for most of the time, and it just feels wrong.
I've put together a hardware bus component using verilog which is an approximation of the bus access process for both read and writes, mostly to see if it would fit within the available logic and it seems fine (with a fair amount of headroom), so it feels like that's the way to go. The good thing about the PSoC is that the same board could easily use the hardware or software option, and in fact reconfiguring the ports to not be address/data bus can be achieved through software so it would be possible to emulate that as well (although not needed for the 8840, other than a test mode I've found in the firmware.)
The clock is an interesting point ... the IMO on the PSoC isn't as accurate at the crystal in the 8840, which may or may not be a problem. My plan was to use the IMO to generate the 8MHz clock internally and output it on XTAL2 so it can be used by the ADC (leaving XTAL1 unconnected.) I'm not convinced timing is critical (it's not an integrating ADC as far as I can tell) so this may be fine. There are three other alternatives ... (1) an external crystal on the board, (2) an external WCO to trim against, or (3) use the 8840's 8MHz crystal directly ... I think I can put together a board where all of these are an option, I just need to make sure it doesn't screw up the routing as some of the clock inputs are right in the middle of a contiguous port on the device I'm considering.
Reset is another interesting consideration, if you use a normal pin for reset input you can choose to ignore the watchdog reset in the device which will simplify debugging.
So next step on the hardware is to put together a board, I think I can squeeze a TQFP64 in between the DIP40. Once I have a board then I can start debugging in a live unit.
Yes ... I know I'm mad.