Issue
147 October 2002
Watch
Me Pull A Rabbit Out of My Hat
Start
Rabbit 3000 No-Risk
CISC Peck-O-Periphs
Time Traveler One-Stop
Shopping Keep Motoring
Sources and PDF
NO-RISK CISC
The Rabbit 3000 is by no
means object-code compatible with the Z80/180, because
a number of opcodes were deleted or moved around. I
don’t really consider this a problem and indeed argue
that the ostensible virtue of object-code compatibility
is overblown in the embedded world.
For embedded applications,
you almost always have to change your software when
you use a new chip. As a practical matter, with software
defining so many of an embedded product’s features,
the scenario is usually, new chip = new product = new
features = new software.
And even if you really
want the new chip to run old software, you’re going
to have to tweak the files at least a little bit. For
instance, the Z180 is considered object-code compatible
with the Z80 because all of the opcodes are the same.
But still, to upgrade from a Z80 you would have to add
some ’180-specific initialization code and be ready
to hack around subtle changes in timing.
What matters isn’t compatibility,
but rather familiarity. Z80/180 users will feel right
at home with a Rabbit 3000, and it’s easy to discern
the relatively few significant changes.
One of the novel features
added to the Z80 was an extra register bank, which is
a concept that remains popular to this day. The premise
behind the scheme was that it would allow faster interrupt
response by virtue of simply switching between register
banks rather than pushing and popping all of the registers
on and off the stack.
This makes a lot of sense
in principle, but how about in practice? As someone
who’s dabbled a fair bit with Z80s and Z180s over the
years, I personally can’t recall ever using the alternate
register set in this manner, or in fact at all.
Similar experience presumably
inspired the Rabbit designers to make better use of
these valuable, underutilized registers. Instead of
being restricted to just exchanges, the alternate registers
can be accessed directly by using an alternate destination
(ALTD) prefix (e.g., via loads and stores and pushes
and pops).
Another use of the prefix
trick is with I/O, which is handled in a different manner
than before. On the earlier Z80/180 models, I/O devices
were accessed with unique opcodes (IN, OUT, and variations).
I’m not sure that this was ever a good idea to begin
with, but it’s certainly archaic at this point. Instead,
for the Rabbit 3000, an I/O prefix (IOI for internal
and IOE for external I/O) can be used with any conventional
memory access instruction to cause the memory address
to be interpreted as a 16-bit I/O address.
Going way back, the 8080
did have an advantage over other 8-bit chips at the
time; it had the ability to perform some 16-bit operations
(e.g., load, store, add, etc.), albeit a limited set.
Rabbit has boosted the 16-bit capability significantly
to include, for example, shifts and other logical operations
such as AND and OR.
There’s also the interesting
new BOOL instruction that works with the 16-bit HL register
pair, the de facto 16-bit accumulator. It’s really simple:
BOOL HL sets HL to one if the contents of HL are nonzero
and leaves it zeroed otherwise. This proves useful to
the C compiler for handling conditional evaluation;
it also provides the basis for short instruction sequences
that perform 16-bit signed and unsigned compares.
The traditional vector
table interrupt system is enhanced with a four-level
priority scheme. The worst-case interrupt latency for
the highest priority is just the length of the longest
instruction (or longest sequence of uninterruptible
instructions), which is approximately 20 clocks.
The MMU is tweaked to manage
the 1-MB address space differently than before. The
basic notion that a 64-KB portion of the physical memory
is always present remains the same. However, the details
have been changed to better suit the memory management
strategy employed by the C compiler.