To supplement @PeterCordes's excellent answer, I thought it would be worth going into the details of exactly how close to source code compatible the two processors are -- for example, how easy would it be to use textual substitutions (e.g. macros) to automatically translate 8080 code to 8086 code, and what the limitations would be.
The first point would be to examine how the registers in the architecture can be mapped. Fortunately, the 8086 registers are effectively a superset of the 8080 registers, so we can map A to AL, BC to CX, DE to DX and HL to BX (this ends up with the registers in a non-intuitive order, as HL can be used for indirect memory addressing, which is better supported using BX than the other general purpose registers on the 8086 -- but note that this unusual ordering of the registers is actually reflected in their conversion to machine code, suggesting that while the mnemonics for the registers weren't named with 8080 compatibility in mind, the design of the instruction set was). Clearly SP and IP must map to the registers for the same purpose, as must the flag register, which conveniently has the bits with the same meanings in the same locations when it is stored elsewhere. But here we note the first incompatibility: the 8080 groups the A register with the flags register (referring to the combination as the 'processor status word') and handles them together as a unit (for example when pushing and popping to the stack), but in the 8086 both are expanded to a full 16 bits and handled individually.
This means that the following 8080 instructions have no single instruction that can perform the same operation on the 8086:
PUSH PSW ; "push af" for those who prefer Z80 syntax
POP PSW ; "pop af"
To emulate these operations on the 8086 you'd need multiple instructions:
LAHF ; Load AH from low-order 8-bits of flags
PUSH AX
POP AX
SAHF ; Store AH in low-order 8-bits of flags
Again, I have a suspicion that the LAHF and SAHF instructions were specifically designed to allow this translation -- they're a pretty unusual operation to support in most respects -- and the choice of AH (when AL would be the more usual target for such an operation) seems strongly to indicate that these instructions were added to make 8080 translation easier.
Looking through the table of instructions supported by the 8080, few others stand out as not being easy one-to-one translations, although as Peter Cordes points out many 1-byte instructions become 2-byte instructions on the 8086 (e.g. MOV C,M
or Z80 equivalent ld c,(hl)
which is 4Eh on the 8080 would convert to MOV CL,[BX]
or 8Ah 0Fh on the 8086, or PCHL
load program counter with HL - equivalent to 8086 CALL BX
FFh D3h). Also slightly tricky are the RST n
instructions, which could plausibly convert to INT nn
, although there are subtle differences on the receiving end of the call ... but that would usually be system software, and I believe the intent was to allow easy application compatibility but that a complete rewrite of system software would have been expected. Another group of instructions that aren't supported are conditional calls and returns (e.g. CNZ addr
/ call nz, addr
) which would need to be emulated in the 8086 by a conditional jump with the opposite condition skipping over the instruction.
There are two issues that are caused by the expansion of single byte instructions into two bytes:
- Self-modifying code would end up hopelessly broken (although it would need substantial modification to work anyway)
- Code that only just fits into the 64K memory space of the 8080 may struggle to fit into a 64K segment on the 8086. In the majority of cases this can be mitigated by moving from the "tiny" memory model (i.e. code and data in the same segment) to the "small" memory model (i.e. one segment for code and one for data).
It therefore seems reasonably simple to perform an automated one-to-one translation of code from 8080 to 8086; a macro assembler may well have been able to handle the translation, even if dedicated packages (as mentioned above) weren't available. It wouldn't work for all programs, but with a small increase in memory required, it should be reasonably simple to make most programs work successfully.
Another interesting question is to what extend the extended variants of the 8080 are also compatible? That is, either the 8085 or the Z80?
The relevant 8085 extensions are:
RIM
(read interrupt masks) and SIM
(store interrupt masks) - the operations here are entirely unsupported by the 8086; machines using the 8086 typically use an external programmable interrupt control that provides the same feature.
DSUB
, ARHL
, and RDEL
are undocumented 16-bit arithmetic instructions that are obviously well supported on the 8086
LDHI
, interestingly, is (an undocumented) equivalent to the 8086s LEA DX, [BX+n]
instruction, which I'd previously thought to be entirely unique to the 8086.
LDSI
is a parallel to LDHI
but using SP as its source register: the 8086 equivalent would be LEA DX, [SP+n]
, except that the 8086 doesn't support using SP like that ... you'd have to wait for the 80386 to get support for an equivalent instruction, and then only with 32-bit registers. You'd probably encode this as MOV DI, SP; LEA DX, [DI+n]
instead. Which is the first time I've seen a 4 byte instruction come out from a single byte instruction input...
SHLX
and LHLX
are 16-bit (undocumented) indirect memory operations, of a kind quite natural on the 8086.
- This leaves the remaining set of undocumented instructions: jumps based on the equally undocumented X5 and overflow flags. The 8086 has no equivalent to any of these instructions.
The Z80 is harder still: it extends the 8080's register set with not just a new pair of index registers (IX and IY, which could be mapped to DI and SI ... although the undocumented I[X/Y]H and I[X/Y]L instructions would have no direct equivalent there) but an entire duplicate set of registers (which cannot be mapped to anything, because we've run out of registers now). Any application using the Z80's exx
or ex af, af'
instructions would be difficult to translate automatically. Other Z80 instructions are easier, e.g. djnz
has no exact equivalent (the 8086's LOOP CX
is a 16-bit equivalent, but there is no 8 bit version), and instructions like ldi
, ldd
, etc are broadly (although not precisely) equivalent to the 8086's string processing instructions (e.g. MOVSB
) and repeat prefix (REP MOVSB
being roughly equivalent to ldir
) -- although the precise details are different, meaning some register remapping may be necessary to make them work. On the whole, the possibility of doing automatic translation of Z80 programs is a whole lot less convincing.
RPE
) and conditional call (e.g.CNS
) instructions were commonly used in 8080 and Z80 programs. And as far as I know, no x86 CPU ever supported comparable instructions. Some other 8080 instructions (likeXTHL
) are only supported in 32-bit code starting with the 80386. I doubt that the 8086 was really source-code compatible to the 8080. Maybe there were assemblers that could replace one 8080 instruction likeCNS
by multiple 8086 instructions. ...