Interrupts and subroutines: keeping things tidy

InvaderZim

Senior Member
Hi all, I'm familiar with basic interrupt operation. Here's my situation:

I've got subroutines that are called both in the main code and also in the interrupt code. Things are complicated enough that I decided to have a policy of preventing the subroutines from being interrupted. That prevents a subroutine from being part-way through, getting interrupted, and then being called again, which can lead to variables being corrupted upon return from the interrupt.

To keep things tidy, I turn off interrupts at the start of a subroutine, and then turn them on again at the end of a subroutine. I was worried about enabling the interrupt within the interrupt subroutine; I usually only do this at the very end of the interrupt sub. However, the manual says that interrupts will stay disabled until the interrupt sub returns, even if you turn them on at the beginning of the sub. Great! Everything sounds perfect...on paper.

In reality it seems that re-enabling the interrupts within the subroutines will not cause the interrupt sub to be called recursively, however any pauses in the interrupt sub will be prematurely terminated by interrupt conditions (as if interrupted).

Any advice? Clear as mud? I could post code but I figured that would just confuse things. Thanks in advance!
 

InvaderZim

Senior Member
Addendum: replacing a "pause 3000" with a "count 0,3000,temp" will achieve the desired behavior. It really does seem that the pause is being cut short, while the un-interruptable count command will delay properly.
 

papaof2

Senior Member
I don't think that PAUSE is the only "do nothing" command which can be interrupted (check the manual for others). That's the best reason to wait until the end of the sub to re-enable interrupts - it ensures that the interrupt sub can't be interrupted, even if you use "do nothing" code within the interrupt sub.

John
 

hippy

Ex-Staff (retired)
It could well be that pauses are being terminated early in the interrupt routine when a further interrupt occurs. There should really be no reason to enable interrupts early, just before the interrupt routine return would be the best place to put them.

Using shared or re-enterant subroutines can be a challenge. The best recommendation is never to enable or disable interrupts within a shared subroutine ...

Code:
Enable Interrupts
Do
  :
  Disable Interrupts
  Gosub Shared
  Re-Enable Interrupts
  :
Loop

Interrupt:
  :
  Gosub Shared
  :
  Re-Enable Interrupts
  Return

Shared:
  :
  Return
 

inglewoodpete

Senior Member
It could well be that pauses are being terminated early in the interrupt routine when a further interrupt occurs. There should really be no reason to enable interrupts early, just before the interrupt routine return would be the best place to put them.
I don't think that's how interrupts are reenabled.

You can insert a SETINT instruction to reenable interrupts within an interrupt routine, however the interrupts will not be reenabled until the interrupt routine's RETURN statement is executed.

Finally, it is generally not a good policy the have an interrupt routine call subroutines: the purpose of the interrupt routine is to capture a brief event that normal polling may miss. Interrupt code execution time should be kept to a minimum: where possible, use it to set a flag that the main routine can pick up when it's ready. Each to his own, of course:rolleyes:
 

hippy

Ex-Staff (retired)
I don't think that's how interrupts are reenabled.

That was my reading of how InvaderZim had things setup ( next to last para in first post ).

There are a range of flags which relate to interrupts ( SETINT issued or not, potential interrupt signal seen, within interrupt or not ) rather than a single 'interrupt enabled' flag so behaviour may depend on just one of those flags not the entirety. Put the re-enabling at the end of the interrupt ( SETINT then RETURN ) and behaviour should be as if there's only a single, enabled or not.
 

InvaderZim

Senior Member
Hippy is right in describing my situation (nice pseudo-code!). The reason I allowed subroutines to "self disable" interrupts was to keep things tidy. The manual does say in Note 2 of SETINT that "The interrupt will not be enabled until the ‘return’ [of the interrupt routine] command is executed." This led me to think that it was ok to re-enable interrupts early after all (previously I'd always waited until just before the interrupt return). Everything besides the truncated pauses appears to work as the manual describes; I think my program would quickly crash if it was truely calling the interrupt routine recursively.

I've used an extensive amount of code in the interrupt routine for convenience; in the past it's worked the best, since constant polling in multiple parts of the main code gets tiresome. My options now are:

1) Follow Hippy's suggestion of disabling interrupts in the main code, outside of subs
2) Deal without pauses; use count instead
3) Abandon this use of interrupt

For now I'm going to do #1; I might have to go to #3 though, if it becomes too cumbersome to keep disabling interrupts.

Thanks all for reading, and for your advice!
 

hippy

Ex-Staff (retired)
Actually there is an option (4) - you can put the disable interrupt within the shared routine, even re-enable interrupts before RETURN. When calling the shared routine from within the interrupt make sure you disable the interrupts after the GOSUB using SETINT OFF. That should minimise the number of disables you need to add. Technical gets the thanks for that helpful suggestion.

Code:
Enable Interrupts
Do
  :
  Gosub Shared
  :
Loop

Interrupt:
  :
  Gosub Shared
  Disable Interrupts
  :
  Pause 1000 ' will last 1000
  :
  Re-Enable Interrupts
  Return

Shared:
  :
  Disable Interrupts
  :
  Re-Enable Interrupts
  :
  Return
A quick test with an 08M demonstrates the interrupt behaviour with respect to PAUSE. The following code runs as expected, the PAUSE takes 10 seconds, bashing the button connected to Pin 3 has no affect.

Code:
#Picaxe 08M
#Terminal 4800

SerTxd( "Ready", CR, LF )

Gosub EnableInterrupts ' Interrupt on pin3 going high

Do
Loop

Interrupt:

  SerTxd( "Interrupted ... " )
  Pause 10000
  SerTxd( "Pause complete and Exiting", CR, LF )

EnableInterrupts:
  SetInt %01000, %01000
  Return
Add a 'Gosub ReEnableInterrupts' immediately on entry to the routine and the PAUSE gets terminated early while the interrupt state is present. Having terminated pause and exited, the interrupt often will occur again, but its not a recursive interrupt call, only after the RETURN has executed.

After the 'Gosub EnableInterrupts' add a "SetInt OFF" and, "normal service is resumed"; the PAUSE is back to 10 seconds long, no longer 'interrupted'.
 
Last edited:

InvaderZim

Senior Member
Very clever! You've given me more to think about.

Thanks again for all your work, Hippy; it's "above and beyond" as usual. This community never fails to amaze me with its enthusiasm and cleverness. I'm in your debt!
 
Top