Interrupts

... or the lack thereof.

First, there is one thing you ought to know:
If you try programming a loop which does nothing but to wait for an
interrupt to happen, don't use

WAIT_FOREVER
             WAIT_FOREVER;
because a 32 Bit jump disables the interrupt for the next instruction word,
which also will be one and the same jump.
So the interrupts will be permanently disabled.

Try

  PC--, nop, nop;
instead.

To simplify the CPU design, we decided that the CPU should not have any
interrupt logic... besides generating a control signal which enables or
disables all the interrupts in general.

The idea was, to have an interrupt controller outside the CPU, which
triggers on the first cycle of an instruction fetch, takes over the bus,
and then forges a 32 Bit word on the data bus with Bit 31 zero, what will
cause the CPU to load said word into PC (before saving the old contents
into LR, that is).

Unfortunately, it turned out that there is a little disadvantage to this strategy.
Remember, that the CPU fetched a word from an address, which was
supposed to contain an instruction from the normal program flow.
The word was skipped/discarded by the interrupt controller, while PC
(saved into LR) was incremented to the next word.

So we skipped a word in the program flow, and thus LR saved
on stack contained return address +1.
We need to correct this when leaving the interrupt service routine later.

Remember the 6502, which uses a different format when popping the
return address from stack for RTS and RTI ?
We are going to have a similar effect...


Interrupt vecor table, each entry takes two 32 Bit words:

 org 0x00000000;

 nop,        nop,     nop;        //Reset clears the program counter to 0.
 COLDSTART_VECTOR;                //let's boot again.
 I, SP-- nf, [SP]=LR, PC=[PC++];  //[--SP]=LR, PC=#ISR_1
 ISR_1;                           //interrupt service routine 1
 I, SP-- nf, [SP]=LR, PC=[PC++];  //[--SP]=LR, PC=#ISR_2
 ISR_2;                           //interrupt service routine 1
...and so on. For this example, the interrupt controller would have to place 0x00000002
or 0x00000004 etc. on the data bus, to make the CPU jump into the right
line of code in our interrupt table.

Now to describe, what the ISR (interrupt service routine)
should look like:

First to save the registers.
Remember, that LR already was saved when we jumped the interrupt vector !

ISR entry point:

 I, SP-- nf, [SP]=R5,  SP-- nf; //[--SP]=R5  
 I, [SP]=R4, SP-- nf,  [SP]=R3; //[--SP]=R4, [--SP]=R3
 I, SP-- nf, [SP]=R2,  SP-- nf; //[--SP]=R2
 I, SP-- nf, R6=SR nf, [SP]=R6; //[--SP]=SR //workaround for the SR bug

"nf" is the assembler notation for "do not not modify Flags".
"I" means "block interrupts for the next instruction word".

Saving the registers takes 16 cycles.
Since TREX needs four clock pulses for one cycle (or bus access),
this would take 4 us on a 16 MHz TREX.

Note, that the vector table jump we did before this also took 7 cycles,
what would be 1.75 us for a 16 MHz TREX.

(16 MHz TREX interrupt response time isn't quite overwhelming, but still
better than for a 16 MHz 68000 using movem.l to save/restore the registers.)

Then to take care of the interrupt source and to do some stuff...

ISR exit:

 I, SR=[SP], SP++ nf,  R2=[SP]; //SR=[SP++], R2=[SP++]
 I, SP++ nf, R3=[SP],  SP++ nf; //R3=[SP++] 
 I, R4=[SP], SP++ nf,  R5=[SP]; //R4=[SP++], R5=[SP++]
 I, SP++ nf, R6=[SP],  SP++ nf; //PC=[SP++]-1
    R6-- nf, PC=R6 nf, nop    ;

The return from an ISR takes 19 cycles (the CPU detects/skips NOPs),
what would be 4.75 us on a 16MHz TREX.


[HOME] [UP]/ [BACK] [1] [2] [3] [4] [5] [6] [7] [8] [NEXT]

(c) Dieter Mueller 2007, 2008