Help Z180 Interrupt Vector INT0 Mode 2

Hello,

I hope you have not totally forgotten the old z180 cpu and you can help me understand a tiny thing about the "I" register into which I am trying to write the address of IV[128]. But the "I" register is 8 bits wide and that is the only one needed for using INT0 in mode 2 "IM 2".

here's a pseudo code of what I have

short IV[128]; // Globals

void IntHandler() { resetWatchdog(); }

int main() { IV[0] = IntHandler;

// just load the char param into i register register_i( ((short)IV & 0xFF00L)>>8);

_asm( "EI\n"); _asm( "IM 2\n");

rest_of_the_program(); }

Thanks, TB

Reply to
Tosca Berisha
Loading thread data ...

On Wed, 23 Nov 2005 15:38:35 -0500, Tosca Berisha wrote in comp.arch.embedded:

First of all, you haven't explained the problem that you have, or what you need help with. So I can only make some general comments below.

I hope you realize that the interrupt vector table should begin on a page address, one divisible by 256, hex address 0000, 0100, 0200, and so on through FF00.

If you actually just allocate an array of 128 16-bit values like this in C, there is a good chance it won't be aligned properly.

Presumably some external device is generating a signal on the INT0 pin to trigger an interrupt. You don't show how you are resetting the device.

Also you don't show how the necessary steps of re enabling interrupts and performing an interrupt return are being performed.

I don't know what compiler you are using. Some of them have extensions for doing this sort of thing for you, usually by using something like "#pragma interrupt", or just adding the extended keyword 'interrupt' to the function definition.

I'd write this a little differently. If the address of the table is in the high half of memory, 0x8000 or greater, then you can have a real problem here. You cast the address to short, which is a signed type, and if 0x8000 or greater it will have a negative value. Then you mask off the low 8 bits and right shift. But right shifting a negative value is implementation-defined as to whether the sign bit is extended or not. That shouldn't make any difference when you write it to an 8-bit register, but still...

Then finally, by introducing a constant with an 'L' suffix, you are immediately promoting the signed short to a signed long.

I'd write it:

register_i((unsigned short)IV >> 8) & 0xFF);

You're showing how you set INT0 into mode 2, although if I were you I'd set the interrupt mode _before_ I enabled interrupts.

But you are not showing us anything at all about programming the external device that is generating the interrupts. Generally I've done this with Zilog specific peripherals like SIO, DART, PIO, CIO.

There is a lot of detail you have left out of your pseudo code, and since you haven't told us what your problem is, there is no telling what part you have left out might be significant.

You need to be more specific about what your problem is.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
Reply to
Jack Klein

The Z180 uses a 256-Byte interrupt vector table. It must start at a 256-byte boundary. The I register holds the upper 8 bits of the vector table, your interrupt source provides the lower bits. This then forms an index into the interrupt vector table, which points to the interrupt handler.

Example:

- You want your interrupt vector table at 0xF000

- Your watchdog timer hardware generates a vector of 0x08.

- You program the I register with 0xF0

- When the watchdog triggers, the Z180 will form a pointer by combining 0xF0 and 0x08 into 0xF008

- At address 0xF008 the CPU reads the address of your interrupt handler from your vector table

- The CPU jumps to the handler

Rob

Reply to
Rob Turk

Jack, thank you.

I see that a lot of things are not the way I understood from zilog docs.

Jack Kle> On Wed, 23 Nov 2005 15:38:35 -0500, Tosca Berisha

Well actually I had no idea of what might had been wrong. I had too many uncertain points.

I did not know this. I thought the array could be upto 256 bytes long anywhere in the memory.

It is a key that triggers INT0. But I don't understand how to specify which element of the vector will be executed. The z180 pdf says that 8bits are read from the data bus. That value will determine the position the element in the Interrupt Vector. But how do we pecify that value? Is that a random value? O is it programmed into some register first? Which one?

Yes, I am using ZDS 3.68 with compiler v1.03. Sorry I should have included the #pragma interrupt in the pseudo code. I have in my actual code.

Great

OK

Well, it is a key connected to a 3-state buffer and to the INT0 pin. How do you program it?

The current behaviour is not as expected. When the interrupt is treiggered it wont jump to my IntHandler()function. It is resetting the CPU instead.

//TB

Jack thank you

Reply to
Tosca Berisha

Rob Turk wrote: [...]

How do you make it generate 0x08? In my case I have key connected to INT0? Is there a place to program this value? Where? What is it called?

Thanks Rob, This is a fantastic step-by-step guide.

TB

Reply to
Tosca Berisha

No. The I register provides the higher 8 bits of the vector, the interrupting device must place the lower bits of the vector on the bus.

If your key is connected to a Z80PIO, this chip will put the vector on the bus. If your key is directly connected to INT0, you cannot use this interrupt mode at all, unless you provide a chip that complies to this interrupt mode scheme of placing a vector on the bus.

If you drive the INT0 line directly from the key, you must use a different interrupt mode where INT0 jumps directly to a fixed address.

Meindert

Reply to
Meindert Sprang

I implemented all advised things here. I still could not get the CPU jump to the interrupt handler.

Here is the actual code after corrections. I should still be missing something.

typedef volatile unsigned char __EXTIO *PBINTIO; #define READ_KEY ((PBINTIO)0x0300) #define INTERRUPT_VECTOR_LOCATION 0xEE00u #define IV_LEN 255u

unsigned short *IV;

void isrv_setup() { IV = INTERRUPT_VECTOR_LOCATION;

_asm( " ld a,high _IV\n"); _asm( " ld i,a\n");

_asm( " ld a,00h\n"); _asm( " out0 (%33),a\n"); // register il is at 0x33

isrv_set_handlers();

_asm( " IM 2"); // Set the interrupt mode _asm( " EI\n"); // Enable the interrupt }

void isrv_reset() { unsigned char i;

for( i=0; i< IV_LEN; i++) { IV[i] = 0; } }

void isrv_set_vector( int nIRQ, intfun pfn) { IV[ nIRQ] = (short) pfn; }

void isrv_set_handlers() { unsigned char i; for( i=0; i< IV_LEN; i++) { IV[i] = isrv_routine0; } }

#pragma interrupt void isrv_routine0() { unsigned char keynum; WATCHDOG; keynum = READ_KEY[0]; V2SendSignal( keynum); // Send over via serial port }

TB

Me>

Reply to
Tosca Berisha

No, this is a matter of hardware. Your hardware must be able to recognise an Interrupt Acknowledge and then place the vector value (0x08 in this example) on the bus. If your hardware isn't designed to do that, then interrupt mode

2 is not for you.

Rob

Reply to
Rob Turk

On Thu, 24 Nov 2005 02:21:13 -0500, Tosca Berisha wrote in comp.arch.embedded:

OK, let's start from the beginning. I'm going to reply here to this response, and to some of your other responses in the thread.

Let me repeat the summary of Mode 2, which the Z180 inherits completely unchanged from the original Z80, and I used on the Z80 for years.

Interrupt mode 2 allows you to have up to 128 different interrupt service routines. The addresses of these routines must be stored in an interrupt address table. The table more or less needs to start on a 256 byte boundary. For this example, we'll use address 0xF000. The high 8 bits of this address, 0xF0, is loaded into the processors I register.

When the INT0 pin is triggered in mode 2, the processor generates an interrupt acknowledge bus cycle. This cycle is recognized by peripheral devices outside the chip because both MI and IORQ signals go active (low) at the same time, something that never happens at any other time. The processor automatically adds two wait states to this cycle, and more wait states can be added by external devices.

During this interrupt acknowledge cycle, the highest priority peripheral with an active enabled interrupt drives an 8-bit byte onto the data bus. This should normally be an even value, 0x00, 0x02,

0x04, ... 0xFA, 0xFC, 0xFE. The Z180 documentation does not specify that it must be even, but I think the original Z80 did.

The processor reads the value on the data bus at the end of the interrupt acknowledge cycle and uses it and the contents of the I register to make a full 16-bit address. It does this by putting the 8 bits in the I register into the top 8 bits of the address, and the byte it just read on the data bus into the low 8 bits of the address.

Then it generates two memory accesses, to the address it just made and to that address plus one, to fetch the 16-bit address of the interrupt service routine. It pushes the program counter and clears the interrupt enable flags and jumps to the address it just fetched.

As to why the table must start on a page boundary, just suppose it started at the end of a page, for example at 0xE0FE. You will put the address of your interrupt 0 handler at 0xE0FE, the address of your interrupt 1 handler at 0xE100, interrupt 2 at 0xE102, and so on. You can only put the 0xE0 into I register. So when the device that is supposed to generate interrupt 2 responds to an interrupt acknowledge cycle by putting 0x04 on the bus, the processor will concatenate this with the I register and try to fetch an interrupt service routine address from 0xE004, not 0xE102 where you have put it.

But all of this depends on having external devices that understand how to operate with the Z80/Z180 bus and interrupt mode 2.

In the old days of the Z80, there were dedicated peripherals designed for this, the PIO, the SIO, the DART, and the CTC. They were connected to the M1 and IORQ signals of the processor, and recognized the interrupt acknowledge cycle when both of these signals were active at the same time. They even recognized when the processor fetched the RETI instruction at the end of the interrupt service routine and reset their internal interrupt logic.

Zilog also made some general purpose peripherals in the Z85xx family, such as the Z8536 CIO and the Z8530 SCC, which could be used with the processors with a little bit of external logic.

And with some logic chips or a PLD of some kind, you could use other, non-Zilog devices with mode 2.

But to use mode 2, you must have one or more devices that recognize the interrupt acknowledge cycle and put an even number, between 0x00 and 0xFE, on the bus.

Do you have such devices on your board? If you don't, you can't use mode 2.

But the only reason to use mode 2 is if you have multiple devices capable to generating their own unique vectors during the interrupt acknowledge cycle.

If you only have one device that can generate the interrupt, or if you have multiple devices but they don't support the interrupt acknowledge vector sequence, the only sensible thing to do is use interrupt mode

  1. In mode 1, the processor does not perform an acknowledge cycle at all. If just saves the program counter and sets the interrupt flags and jumps to address 0x0038 to run the routing starting there.

If you have only a single device, your ISR does what is necessary to service the device, clear the device's interrupt logic, and reset it so it can generate another interrupt when it needs to.

If you have multiple devices that don't support the interrupt acknowledge cycle, your mode 1 routine starting at 0x0038 will have to check all the devices that can generate the interrupt and take care of the device or devices that need service.

You asked:

I don't know what this key is connected to. If it is connected to some Zilog compatible peripheral (PIO, SIO, DART, CTC) or semi-compatible (CIO, SCC) device with additional logic, there is a register in the device where you put the low byte of the address, and it will output it during the interrupt acknowledge cycle. So yes, if you have a device compatible with the Z80/Z180 mode 2 vectored interrupts, you do program the value into a register, but in the peripheral device, not the processor.

As I said before, since this is not part of a device that supports mode 2 vectored interrupts, I would use mode 1. But the thing that you need to remember is that INT0 is level sensitive. At the end of every instruction cycle, the processor samples the INT0 pin. If it is active (low), and interrupts are enabled, then it will respond to the interrupt.

That means that something in your interrupt service routine must cause the hardware to deactivate the INT0 pin, let it go high again, until the key is released and pressed a second time. If not, as soon as your interrupt service routine ends with the instructions EI and RET or RETI, if the INT0 pin is still low, the processor will immediately execute the interrupt service routine again.

I'd suggest you read the section on interrupts in the Z180 manual again. The document I am looking at, the one currently available on Zilog's web site, is "UM0050 Z8018x MPU Family User Manual".

The bit about starting the vector table on a 256 byte boundary is mentioned in "Interrupt Vector Register (I)" on page 66.

Hope this helps.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
Reply to
Jack Klein

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.