Z80 TRAP and CP/M

Don Y dgy at DakotaCom.Net
Tue Apr 4 21:23:48 CDT 2006


Roy J. Tellason wrote:
> On Tuesday 04 April 2006 09:08 am, Don Y wrote:
>> Roy J. Tellason wrote:
>>> On Monday 03 April 2006 12:57 am, Don Y wrote:
>>>> I think the real reason (?) goes to a difference in cultures between
>>>> register rich and register poor machines IN THAT TIMEFRAME.
>>> I think you're probably right...
>>>
>>>> E.g., if you are writing code for a 68xx, you have little choice
>>>> but to do everything in memory addressing.  Whereas if you are
>>>> using 8080/8085/Z80 et al., you just get used to *keeping* things
>>>> in registers (I can recall spending lots of time evaluating
>>>> which arguments I would put in which registers so I could
>>>> *keep* them there -- or somewhere else in the register set -- for
>>>> the duration of the algorithm... XCHG being a favorite tool
>>>> in those cases!)
>>> Yeah.  Though XTHL was another one...
>> As was PCHL.  But, XCHG was lots of bang for the buck (4 clocks?)
> 
> Yes.  And I often wished that they'd had one that would work with BC,  as 
> well...  :-)

The typical usage was HL & DE for source/destination pointers
and BC as length/byte count.  So, you could
	LXI H,...
	LXI D,...

	...

	DAD B	;HL points to something
	XCHG	;save HL in DE, get at contents of DE
	DAD B	;DE points to similar "thing"

	...

>>>> Moving to something like a 99000 can be terribly distressing
>>>> for the register rich crowd to become accustomed to!  ;)
>>> I found,  though,  when I was doing some z80 stuff a while back that most
>>> of the time I'd use one pair for an address pointer and maybe one other
>>> besides the accumulator and that was it,  over 90% of the time.
>> Z80 is a different beast from the 808[05].  You write code differently for
>> it because it has a much richer instruction set.
> 
> But I never used most of that stuff.  Never got around to doing anything with 
> the index registers,  the alternate set,  or a lot of those "specials" that 

Alternate register set was ideal for interrupt service (assuming
IRQs aren't nested *or* you restrict the alt registers for use by
a single IRQ that can never interrupt itself).  Consider the
cost of saving all of that state and restoring it before RETI
and you can see how quickly you can save a lot of time!

> were added.  I kinda liked relative jumps,  though,  since it made it way 
> easier to do relocatable code and plus they were a byte smaller.  :-)

A favorite trick was to finish a product.  Then grep for all
CALLs.  Sort this list to come up with the N most common CALLs,
and then assign those to the "unused" RST's.  You could often
save 500 bytes of code with that 10 minute exercise...

>> But, if you are doing things like adding an 8 digit BCD number to another 8
>> digit BCD number, you quickly discover that you need a lot of registers (two
>> pointers for the two arguments -- assuming the destination is one
>> of these as well, a "digit counter", the accumulator and, of course, the
>> carry).
> 
> I suppose.  I guess it all depends on what you want to do...
> 
>>> Oh,  and the little monitor program that I was playing with didn't push a
>>> parameter on to the stack,  it put it inline,  right after the call to
>>> some subroutine -- the called code would pick it up and use it and adjust
>>> the stack pointer to just past it for the return.    :-)
>> Yes, a favorite trick for "printing" strings, etc.
>>
>> 	CALL OUTPUT
>> 	DB  'Hello, World!'
>> 	<insert next instruction here>
> 
> Just so.

We would often develop our own interpreters to make some of this
stuff more efficient to code.  This would get coded in-line
with appropriate macros.  For example:

	STATE IDLE
ON '0' THRU '9' GOTO IDLE EXECUTING Accumulate_Digit
ON BARCODE      GOTO TEST EXECUTING Clear_and_Read_then_Test
ON CLEAR        GOTO IDLE EXECUTING Clear_Accumlator
ON ENTER        GOTO TEST EXECUTING Test_Value
ENDSTATE

	STATE TEST
ON GOODVALUE    GOTO ACCEPT EXECUTING Process_Value
ON BADVALUE     GOTO REJECT EXECUTING Signal_Error
ENDSTATE

So, with ~25 bytes, I can code a numeric data entry routine
(trivial example) in a way that actually is reasonably easy
to read.

>>> I have to dig out that code,  anyhow,  there were a few little tricks in
>>> there that I think I want to use again.  Like funneling everything
>>> through a dispatch table,  so that un-programmed EPROM showing up as
>>> "FFFF" in some entries would be handled automatically ("CRASH!" :-),  
>>> ferinstance.
>> Or, using INR M to 'test' flags.
> 
> Yep!

THogh this caused all sorts of problems when I got my first
ICE!  :-(  (think about it  :> )

> Sometimes I like the absolutely minimalist approach to things,  too.  One of 
> these days I'm gonna dig that code out,  burn it,  and see how small a z80 
> box I can build that'll still prove useful for all sorts of things.  I sure 
> have enough of those parts on hand here...

I have a tiny multitasking executive that runs in a handful of
bytes.  Slick as snot -- if you adopt its coding style.




More information about the cctalk mailing list