Yes, it might seem somewhat odd that Intel added extra logic to set
the segment registers but not not the stack pointer; this means that
even though you don't need to set up both the stack segmeent register
SS and the stack pointer register SP to have a working stack, you
still need to do some setup.
The decision to set the segment registers at start had a cost, too:
extra gates are needed in the reset logic for each register that you
want to have a predetermined value after reset rather than a random
one. That leads to either removing other features you'd like to have
or a bigger die and its consequent lower yield.
But it starts to make sense when you remember that:
Marketed as source compatible, the 8086 was designed to allow
assembly language for the 8008, 8080, or 8085 to be automatically
converted into equivalent (suboptimal) 8086 source code, with little
or no hand-editing.[wp]
The 8080 didn't define the SP's value because there is no sensible
default for it: where the stack can be depends entirely on the system
address map design. This means that any 8080 code you "auto-port" to
the 8086 will not be using the stack unless it has set up the SP
first. But such code certainly won't be setting up the SS, because
that didn't exist on the 8080. Thus, to make such auto-porting easier,
and offer an alternative to changing the software, the system designer
updating hardware to use an 8086 instead of an 8080 can simply design
it to have a similar enough memory map to the 8080 system that the
code that sets up the stack can be re-assembled for the 8086 without
changes.
So why is 0000h
a reasonable default for the SS? The default is only
necessary for systems designed for auto-ported code (systems with new
code would set SS at the same time as they set SP). On such systems it
does force the designer to put RAM at the bottom of the memory map,
but this is a reasonable enough place to put it anyway, since it
doesn't actually matter what segement the RAM or ROM is in: the ported
code assumed it was running in a single 64K address space anyway. This
also allows you to re-use almost exactly the 8080 board's existing
memory decoding logic on an 8088 system as well; you simply ignore or
force to zero the top four bits of the 20-bit external addresses.
But why make the CS different, FFFFh
instead of 0000h
? Doing this
gives a little more flexability with a new 8088 design intended to
upgrade an existing 8080 design. The new design you must always have a
little bit of new startup code at FFFF0
since the 8080 did not run
code there at startup (it loaded the program counter with 0000h
at
reset), but there are two simple options for this depending on what
you're trying to achieve with the upgrade.
If your new 8088 system still can live within a 64 KB memory space for
RAM and ROM, you can re-use almost exactly the existing address
deocding logic and minimize the amount of new logic you need to add
for the 8088 by ignoring the top four bits of the 20-bit address bus,
causing the second and subsequent 64K chunks of the address space to
be mirrors of the first 64K chunk. The only thing you need to add is a
bit of ROM at 0FFF0
with a new startup routine that loads CS with
0000
and does a JMP #0000
, which would call the original
(auto-ported) startup code.
If you need more than 64K address space in the new system (perhaps
because that was the whole point of switching from an 8080 to an 8088,
or you can't conveniently put any ROM at 0FFF0
), you can put the new
ROM outside that 64K address space or also map the original ROM to a
new address at 10000
or above, giving you 64K of address space for
RAM at 00000
and another 64K of address space for ROM above that.
(This would be a fairly easy way of solving the problem of having an
8080 system that had run out of address space for RAM and ROM but was
otherwise satisfactory.) In either case, your new startup code is as
described above: load the CS with an appropriate value for the ROM
location and jump to your original (auto-ported) startup code.
In the case of moving the ROM outside the lowest 64K of address space,
if your code never ran data from the "data area," this should be a
pretty simple conversion with almost all of the machine code
re-assembled automatically from the original 8080 source code. (If
not, you'll still probably have an easier time than doing a full
rewrite.)
There are actually some further subtle issues with the above that I am
ignoring here in the interests of saving space, but you (or anyone
else) could ask another question specifically about this kind of
conversion if you're interested.