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