[comp.os.cpm] Interrupts on the N* Advantage

rbloom@APG-1.ARPA (Robert Bloom AMSTE-TEI 3775) (11/24/86)

Help.

     I find I have to write an interrupt-driven application for 
the NorthStar Advantage.  Unfortunately, I don't know how.  
Specifically, I do know how to enable the interrupts, and write 
the interrupt handler, but actually what happens at interrupt 
time eludes me.

     At the interrupt, the z80 jumps to the interrupt routine.  
Where?  My z80 book says, "the z80 expects an [RST or CALL] 
instruction to be placed on its data buss [by the interrupting 
device]"  how it do dat?  Two people who I asked said "to where 
you program it to" but I have no idea how to do that.

     I have a Advantage technical manual.  It may have the info I 
need but I find it incomprehensible.  (I gather that interrupt 
routines are strongly hardware-dependant.)  And I do understand 
that the Advantage is not very fast, but can't believe a simple 
get-and-put routine with interrupts wouldn't be fast enough for 
4800 baud.

     So, can someone help me out?  I'm trying to store the input 
characters from the SIO card away till they can be processed.  I 
have DTR control on the port but I haven't been able to get that 
to work.  (I'm trying to write a very simple high-speed commo 
program.)  I really need a low-level example.

bob bloom

jkg@gitpyr.gatech.EDU (Jim Greenlee) (11/28/86)

In article <1078@brl-adm.ARPA> rbloom@APG-1.ARPA (Robert Bloom AMSTE-TEI 3775) writes:
>     At the interrupt, the z80 jumps to the interrupt routine.  
>Where?  My z80 book says, "the z80 expects an [RST or CALL] 
>instruction to be placed on its data buss [by the interrupting 
>device]"  how it do dat?  Two people who I asked said "to where 
>you program it to" but I have no idea how to do that.

OK, here is my fuzzy recollection of how interrupts work on the Z80:

There are three interrupt modes on the Z80 - 0, 1, and 2. The mode is selected
by executing an IM instruction whose operand is the mode desired.

Mode 0 is the 8080-compatible mode. The Z80 expects the interrupting device
to put a value out on the data bus which indicates what location to jump to.
Their are 8 choices - 0H, 8H, 10H, 18H, 20H, 28H, 30H, and 38H. The processor
executes a CALL instruction to one of these locations, which contains either
an interrupt handler (if it can be handled with only 8 bytes of code) or a
JMP instruction to another location which contains the interrupt handler.

Mode 1 is the simplest mode. All maskable interrupt requests are vectored to
location 38H via a CALL instruction. This mode is equivalent to executing an
RST 7 in Mode 0.

Mode 2 is the most flexible mode, with full vectoring to any location you 
want. In this mode a priority is established among peripherals which can
generate an interrupt. This is done by daisy-chaining the peripherals through
the IEI/IEO pins. Whenever a device generates an interrupt, it pulls its
IEO output low. Also every device in the chain monitors its IEI pin. If this
pin goes low, then the device is disabled from producing interrupts, and the
device will again pull its IEO pin low. In this way, devices farther down the
chain are prevented from interrupting the processor while it is servicing a
higher priority interrupt.

Once this happens, the device puts an 8 bit vector on the data bus. The Z80
reads this vector, which must be programmed as part of the device's initial-
ization, and concatenates it with the contents of the I register. This register
is set to be the high order 8 bits of a 256 byte vector table. This table
can be located anywhere in memory - this is what your friends mean by "it's
wherever you put it". The 8 bit vector from the peripheral points to one of
the 256 locations in this table, which contains the start address of the inter-
rupt handler. You can have several of these tables in memory and switch back 
and forth simply be changing the contents of the I register.

If this is still confusing, e-mail me any specific questions you have and I'll
try to answer them. As I said, it's been a while since I did any Z80 stuff and
my memory is a little fuzzy, but I'll do my best.

                                              Jim Greenlee

-- 
The Shadow...!{akgua,allegra,amd,hplabs,ihnp4,seismo,ut-ngp}!gatech!gitpyr!jkg

Jryy, abj lbh'ir tbar naq qbar vg! Whfg unq gb xrrc svqqyvat jvgu vg hagvy lbh
oebxr vg, qvqa'g lbh?!

jon@amc.UUCP (Jon Mandrell) (12/02/86)

Sorry, I tried to mail this, but it got bounced.


If the UART which you are using is a Z80 SIO or DART, then the interrupts
are easy.  If you have something like and 8251, then things get a bit worse.

All of the following assumes that you have a Z80 device out there:

  The Z80 actually has 3 interrupt modes:
  Mode 0:
  A device toggles the INTR line, and the Z80 reads one byte off of the data
bus and executes it.  This instruction is usually a RST, since these are one-
byte CALLs (so they can be forced easily).  The RST instructions run you to
one of 8 locations in low memory (0000, 0008, 0010, 0018, 0020, 0028, 0030,
or 0038).  Actually, you can force any instruction onto the bus, but it doesn't
really make sense to do a INC or whatever when an interrupt happens.
 This is not the mode you would be using.

 Mode 1:
 A device toggles the INTR line, and the Z80 branches to location 0038.  It
pushes the current PC onto the stack, so that you can perform an RET and get
back to your program.  This mode is rarely used.

 Mode 2:
 Welcome to the real power of interrupts.  In this mode, the I register
contains the top 8 bits of an address.  When a device toggles the INTR line,
the Z80 acknowledges it, and then reads in one byte.  This is the bottom
8 bits of an address.

	((I reg) * 256) + (byte read in)

The Z80 goes to the calculated address, and grabs the word there.  This is
the address vector of the interrupt routine.  Perhaps an example would help.

Suppose you had the following code at location XX00 (any page boundary):



; channel B of the SIO

	dw	chbtx		; transmitter empty
	dw	chbstat		; external status change
	dw	chbrx		; receive char available
	dw	chbspec		; special receive condition

; channel A of the SIO

	dw	chatx		; transmitter empty
	dw	chastat		; external status change
	dw	charx		; receive char available
	dw	chaspec		; special receive condition



Then, if you perform the following code:



	ld	a,XX		; the page number of the interrupt table
	ld	i,a		; setup the interrupt register



Any interrupt vectors that are placed on the bus will access the above
table.  Still with me?

Now, you need to set the SIO up.  You might want to find a DART or SIO
manual for this code to make sense, but here goes.  I use the OTIR to
output a block of data to the same port.  You need to know the ports that
the SIO resides at.



	ld	hl,table	; the address of the table
	ld	b,tlen		; the length of the table
	ld	c,SIO_PORT	; the port address
	otir



with a table that looks like this:



table:
	db	0		; junk.  Make sure we are at register 0
	db	18h		; reset the SIO
	db	4		; switch to register 4
	db	44h		; X16 clock, 1 stop, no parity
	db	3		; switch to register 3
	db	0c1h		; rx 8 bits, rx enable
	db	5		; switch to register 5
	db	0eah		; dtr, tx 8 bits, tx enable, RTS
	db	1		; register 1 (interrupt enable)
; set Y to 1 if you want a transmitter interrupt, or to 0 if you want to
; poll.  0 is probably the way you want to go.  Note that the value given
; below disables interrupts for special receive conditions, and for status
; changes (modem signals), so that the entries into the interrupt table above
; need not have them.
	db	000110Y0b	; rx int on all chars
; if this is channel B of the SIO which you are initializing, add the following
	db	2		; switch to register 2
	db	0		; vector value
tlen	equ	$-table		; calculate the length of the table



If you are not initializing channel B with the above code, you need to send
a 2 and a 0 to channel B's control port also.

Well, that is the very basics.  Make sure your interrupt routines end with
the RETI instruction, instead of a RET.  If you have any questions, send
me some mail, or give me a call at (206) 882-5252.

-- 
Jon Mandrell    (ihnp4!uw-beaver!tikal!amc!jon)
Applied Microsystems Corp.

"flames >& /dev/null" - me