In short, to better support interrupts, because interrupts were
arguably broken (or at least very limited in usability) on the 8008.
The direct answer to the question of why you'd move the stack off-die is
"space": they needed a bigger stack and dedicating a lot of die space for a
larger stack on the 8080 was basically a non-starter. But the question
underneath that is, "why did they see a need for a bigger stack?"
The Re-entrancy Problem
Subroutines in general on the 8008 had a re-entrancy problem: in order to
use any registers in the routine you needed to save the current values so
that they could be restored on return. But worse yet, interrupt routines in
particular could make the system unreliable if they used any instructions
that changed any flags, since those might change flags set and about to be
(but not yet) tested in the interrupted code.
Practically, these values could be saved only in fixed locations, meaning
that you could have only one level of interrupts, and non-trivial
subroutines could not be called from interrupts. (Nor from themselves,
directly or indirectly, unless care was taken to be careful of registers
and flags destroyed by the call, but this at least was a more soluble
problem for the programmer.)
This problem was solved in the 8080 by extending the stack operations to be
able to store not only program counter values (for returning from
interrupts and subroutines) but also register pair values (including the
flags). This provides a convenient and efficient way to save register
values and restore them later, and makes writing re-entrant routines much
easier.
The Intel 8080 Assembly Language Programming Manual gives some
clear evidence that this is what was on the minds of the designers:
- "Fully programmable stacks, allowing unlimited subroutine nesting and
full interrupt handling capability." (p. v)
- "The stack pointer, a register which enables various portions of memory
to be used as stacks. These in turn facilitate execution of subroutines
and handling of interrupts as described later." (p. 1)
- "...any interrupt subroutine should save at least the condition bits and
restore them before performing a RETURN operation. (The obvious and most
convenient way to do this is to save the data in the stack, using PUSH
and POP operations.)" (p. 60)
The Need for More Stack Space
There's no particular indication that eight levels of nested
subroutines/interrupts was a major problem. Modern developers, used to
using a lot of stack space, often perceive small stacks as a likely
problem, but experienced developers of code for 8-bit systems of this
nature well know that you use a lot less stack than modern developers
think.
That said, whether 8 levels of subroutine nesting was an issue or not, it's
clear that as soon as you start using stack space for temporary storage of
registers you're going to need a lot more than eight words of it. Just
storing the A register and flags pair alone significantly reduces your
nesting capacity, and it's not unreasonable that an interrupt routine might
also want to use at least one other register pair.
Aiming for something like 32 to 64 words (64 to 128 bytes) of stack is
probably reasonable if you're looking at the 8080 as a relatively similar,
but somewhat larger and cleaner, version of the 8008 architecture. But once
you've made the decision to move it off-chip, increasing the size beyond
that is cheap, and in some ways it's easier to have a full 8-bit or 16-bit
stack pointer than to use an odd size.
I've found no particular evidence that the stack pointer was made a full 16
because they felt any need for a stack to be that large. It's clear that at
least some experienced microprocessor developers (the MOS 6502 team) felt
that an 8 bit stack pointer (256 byte stack) was plenty. It's possible that
the 8080 designers disagreed, or it's possible that they felt they couldn't
force a particular area to be RAM, as the 6502 designers could. (Even more
than the MC6800, the 6502 design strongly encouraged page $00 to be RAM, so
forcing page $01 to be RAM was no hardship.) Or perhaps it just didn't
occur to them that registers pointing into memory could be any less than 16
bits.
Stack Frames Were Not Anticipated
Some minicomputer systems of the day, notably the PDP-11 using BCPL and C,
had the concept of a "stack frame," where space for parameters to and local
storage for a function were allocated on the stack.
This was pretty clearly not the intent of the 8080 designers. While they
make it easy to load the stack pointer (via SPHL
), there is no simple way
to retrieve it, much less SP-relative indexing instructions as provided by
the PDP-11. (This was true of other early 8-bit processors as well; the
first major processor to provide stack-relative addressing modes was
perhaps the MC6809.) Further, the manual makes it clear that they intended
parameters to be passed in registers, using HL as a pointer to further data
when there was more than could fit into the registers:
Sometimes it is more convenient and economical to let the subroutine load
its own registers. One way to do this is to place a list of the required
data (called a parameter list) in some data area of memory, and pass the
address of this list to the subroutine in the H and L registers.
("Transferring Data To Subroutines," p. 51)
RST
, used for interrupts. (This seriously limited what you could safely do in interrupts becuase the flags were not preserved; I go into detail about this in my answer below.)r
registers on chip (by convention). Any register could be used but the return address register was part of the branch instruction IIRC.