Good question and extra information, thanks. I wasn't aware of 16 bit code/data segments on 64 bit x86 CPUs.
I don't think I seriously considered using di for A (I've sort of started using a convention of lower case = 8086 registers, upper case = 8080 register so I may as well stick to it). Simply because of the many cases like the two I've used as examples, both of which are really simplified by having an 8086 8 bit register for A. As a 8080/Z80 programmer learning 8086 at the time, it took me a while to realise that al being off-limits for register A due to lodsb wasn't really a big problem, because any of the 8086 8 bit registers had equivalent expressability to the 8080 accumulator. At that point I stopped worrying about using the standard 8080 -> 8086 register mappings. It's more than possible that I was seduced by apparent simplicity and actual runtime performance could have been tweaked a little higher on average by something with a little more superficial complexity. I was in a hurry and looking to get my 8 bit tools running on my new box over a weekend or similar I think (I can't really remember exactly).
It was such a long time ago that I've forgotten a lot. Only after I wrote the "shower thought" post above did I remember that as well as dh, I had bp in reserve (basically I'd forgotten about bp). I suppose the extra resource (bp) meant even a normal dispatch loop emulator could also be written without needing to emulate any 8080 register with memory, contrary to what I wrote about above.
One optimisation I think I did do was actually to emulate MVI A,imm and other single byte immediate instructions with a lodsw rather than a lodsb, effectively prefetching the next opcode. I had the 256 opcode routines packed together initially, then I'd distribute them to their ultimate 0x0000, 0x0101, 0x0202 ... locations at runtime and append the lodsb; mov ah,al; jmp ax suffix then. So no problem if the code for MVI A,imm was lodsw; mov dl,al; mov al,ah; jmp ax; The appended suffix would still be there, it would just never execute.
My first implementation of this emulator took the form of a binary prefix that could be prepended to any CP/M 80 .com file with the Operating System's concatenate command (PIP ?), to make a CP/M 86 .cmd file. Later on I adapted it to MS-DOS with a similar deployment strategy, not a huge job because the basic system calls I needed were close cousins to MS-DOS equivalents. As you say, quite different for Linux.
In general the window of widespread usefulness for this tool was limited; basically the time when CPM/80 software was still competitively capable and PCs were still not at least an order of magnitude faster than their CP/M 80 predecessors (after that point conventional emulators were fine). I never took the chance to try and share my code with the world, that was harder then. So it never helped out anyone but me sadly.
Love your rep_lodsb handle by the way.