PIC Microcontroller Memory/Interrupt Method

reentrant code

A subroutine is "re-entrant" if it is designed so that, if it is ever interrupted in the middle, and then the interrupt routine calls the same subroutine, then everything works the way it ought to work.

Alas, it is very difficult to write re-entrant code on the Microchip PIC processors.

For example, if you have a "square_root" subroutine, and you call it with "9", but while it is still calculating it gets interrupted, and the interrupt routine decides it needs to find the square root of "4" -- what happens? Most square root routines for the PIC will return the proper value "2" to the interrupt routine ... but when the calculation of the square root of "9" resumes, they wig out and return "2" or some other completely wrong answer.

Our options are:

nested interrupts require data stacks

(see also software stacks on the Scenix )

Nikolai Golovchenko says:

Yes, the nested interrupts are possible. The method I used on 16F84 processed two interrupts (RBIF and TMR0IF), but more interrupts could be okay. To do this, I use software stack macros (see attachment) that use FSR as a stack pointer. A typical interrupt handler looks like this:
            ORG     0x004                   ; interrupt vector location
            PUSHW                             ;store W
            PUSHSTATUS                 ;store STATUS

                                            ;code goes here

            POPSTATUS                      ;restore W
            POPW                            ;restore STATUS
            retfie                          ; return from interrupt

After storing W and STATUS goes usual interrupt flags polling .
As soon as a set flag is detected, it should be cleared and GIE=1 to allow other interrupts to actually interrupt the process.
Be careful not to allow too many interrupts to be processed, only the ones that have critical response time, because hardware stack may overflow. The best way is to keep GIE reset during the most critical interrupt so that it could finish without unwanted interrupts.
Priority of different interrupts is programmed by flags polling sequence(in case they trigger at the same time).
That's it.

By the way, I find software stack macros very useful in practically every program. The best example is when you need one scratch pad register. Using stack it's simple:

PUSH    ;reserve one byte on stack
....use INDF as a scratch pad reg
POP    ;restore stack

Good luck.

;*******************************
;Software stack organization
;*******************************
;FSR is a stack pointer
;Top of stack is in INDF
;Stack grows in upper addresses direction
;*******************************************************************
;MACRO:      INSTR.NUMBER:   STATUS INFLUENCE:          W INFLUENCE:
;
;PUSHW          2               no                      no
;POPW           3               no                      yes
;POPW2          2               yes!!!                  yes
;PUSHSTATUS     3               no                      yes!!!
;POPSTATUS      3               yes                     yes!!!
;PUSH           1               no                      no
;POP            1               no                      no
;*******************************************************************
;Notes: 1) FSR should point at the stack top
;       2) PUSHSTATUS and POPSTATUS must be used in pair,
;       because these macros mangle quadruples
;*******************************************************************

PUSHW   MACRO
        INCFSZ FSR, F   ;never goes zero, but STATUS is safe
        MOVWF INDF
        ENDM

POPW    MACRO
        SWAPF INDF, F
        SWAPF INDF, W	;same as movfw indf, but STATUS unchanged
        DECFSZ FSR, F   ;never goes zero, but STATUS is safe
        ENDM

POPW2   MACRO
        MOVF INDF, W
        DECFSZ FSR, F   ;the popped W STATUS is safe
        ENDM

PUSHSTATUS      MACRO
        SWAPF STATUS, W
        INCFSZ FSR, F   ;never goes zero, but STATUS is safe
        MOVWF INDF
        ENDM

POPSTATUS       MACRO
        SWAPF INDF, W
        MOVWF STATUS
        DECFSZ FSR, F
        ENDM

PUSH    MACRO
        INCFSZ FSR, F
        ENDM

POP     MACRO
        DECFSZ FSR, F
        ENDM

Robin Abbott of Forest Electronic Developments says:

This might be of use to someone. Recently I had a project where a subroutine took a value in W and saved to a software stack:
movwf Temp
movfw sp        ; Stack pointer
movwf FSR    ; Point to it
movfw Temp
movwf 0

Trouble is it uses a temporary variable which I didn't have (it is in an interrupt). This alternative which makes use of XOR uses no temporary variable at the expense of 1 extra word:

  movwf FSR
  movfw sp
  xorwf FSR
  xorwf FSR,w
  xorwf FSR
  movwf 0

You can also use this to swap two variables (say x and y) without a temporary variable leaving X (or Y if order is reversed) in W.

    movfw x       ; Get X
    xorwf y         ; Y is now X^Y
    xorwf y,w     ; W is now (X^y)^X==Y  (say OldY)
    movwf x       ; Now X is OldY
    xorwf y         ; finally Y is (OldX^Y)^Y==OldX

I think this may be an old technique - I have vague memories of something similar from the pre-history of programming, but only found a use for it now

(See math/bit/swap.htm for details. )

Timer tutorial (incl prescalers) by Andrew Warren

Are the stack-based math routines /techref/microchip/pfu.htm compatible with the above code?

James Cameron [quozl at us.netrek.org] says:

I've also done a software stack, mainly for mathematical functions, in order to significantly reduce memory (file register) utilisation on a 12C509. Not a call and return stack, just a parameter stack. http://quozl.netrek.org/stack-math.asm

{ed: This has now been expanded at http://quozl.netrek.org/uptime/ to include

FORTH on the PIC uses 2 software stacks: the Data stack and the "Rack" http://wiki.enst.fr/bin/view/Picforth/FakeReturnStack

deeper return stack

"PIC12C509 - Getting Around the Stack Limitation" by Peter H. Anderson 1997 http://www.phanderson.com/PIC/12C509/stack.html says "The 12C5 series PIC has only a two level stack which limits the number of nested subroutine calls to two. This may be a very serious limitation. (The 16C84 has an eight level stack which permits nested subroutines to eight deep. I can't imagine a program where this will not be sufficient).". That article describes one way of implementing a deep return stack, when the hardware return stack is inadequate.

See also

Interested: