Trying to follow C18 interrupt context code...

I’m trying to follow the code generated by the compiler for interrupt context save/restore. I am trying to understand how the interrupt routine is using the stack. By compiler convention, FSR1 is the stack pointer and FSR2 is the frame pointer. I think I follow the context save ok, but have problems following the context restore.

<code>


224:               #pragma interruptlow InterruptHandlerLow save=PROD /* You may want to save additional symbols. */
225:               
226:               unsigned int intb_int = 0;
227:               unsigned int intb_lft = 0;
228:               unsigned int intb_rgt = 0;
229:               void InterruptHandlerLow ()     
  54DA    CFD8     MOVFF 0xfd8, 0xfe4	    STATUS  -> ++(SP)	
  54DC    FFE4     NOP
  54DE    CFE0     MOVFF 0xfe0, 0xfe4		BSR     -> ++(SP)
  54E0    FFE4     NOP
  54E2    6EE4     MOVWF 0xfe4, ACCESS	   W       -> ++(SP)
  54E4    CFDA     MOVFF 0xfda, 0xfe4		FP(H)   -> ++(SP)	
                        ; Save old FP(H)
  54E6    FFE4     NOP	
  54E8    CFE2     MOVFF 0xfe2, 0xfda		SP(H)   -> FP(H)
                        ; Setup new FP(H)
  54EA    FFDA     NOP
  54EC    CFE9     MOVFF 0xfe9, 0xfe4        FSR0L	  -> ++(SP)
  54EE    FFE4     NOP
  54F0    CFEA     MOVFF 0xfea, 0xfe4		FSR0H	  -> ++(SP)
  54F2    FFE4     NOP
  54F4    CFF3     MOVFF 0xff3, 0xfe4		PRODL   -> ++(SP)
  54F6    FFE4     NOP
  54F8    CFF4     MOVFF 0xff4, 0xfe4        PRODH   -> ++(SP)
  54FA    FFE4     NOP
  54FC    52E6     MOVF 0xfe6, F, ACCESS	(SP)++  -> (SP)++ 
                        ; Read PRODH just stored on stack, increment sp
                        ; Write PRODH again, increment sp again
  54FE    CFD9     MOVFF 0xfd9, 0xfe6		FP(L)   -> (SP)++   
                        ; Save frame pointer address
  5500    FFE6     NOP
  5502    CFE1     MOVFF 0xfe1, 0xfd9		SP(L)	   -> FP(L)   
                        ; Set frame pointer address
  5504    FFD9     NOP
  5506    0E05     MOVLW 0x5				 0x5      -> W
  5508    26E1     ADDWF 0xfe1, F, ACCESS   W + SP(L)  ->
SP(L) 
                        ; Allocate five bytes local var. space 

So we end up with a stack looking like the following:

; SP ->                        ]
;        FP[4]: mask           ] local auto variables...
;        FP[3]: ibit           ]
;        FP[2]: int_count      ]
;        FP[1]: intb_curr      ]
;        FP[0]: int_byte       ] <- FP
;        FSR2L (old FP(L))     ]
;        PRODH - ???           ]
;        PRODH                 ]
;        PRODL                 ]
;        FSR0H                 ]
;        FSR0L                 ]
;        FSR2H (old FP(H))     ]
;        Wreg                  ]
;        BSR                   ]
;        STATUS                ]
;                                ]

At the end of the routine, we restore context.  
This is where I have problems following the code.

  5926    CFD9     MOVFF 0xfd9, 0xfe1		FP(L)     -> SP(L)
                        ; Rollback SP to frame pointer
                        ; SP now pointing to int_byte
  5928    FFE1     NOP
  592A    52E5     MOVF 0xfe5, F, ACCESS        (SP)--    -> (SP)--
                        ; ??????????????
                        ; Read int_byte, decrement sp to point to FP(L)
                        ; write int_byte over FP(L), decrement sp 
                        ; sp ends up pointing to PRODH????
                        ; ??????????????
  592C    CFE7     MOVFF 0xfe7, 0xfd9		(SP)      -> FP(L)	
                        ; ??? Load FP with PRODH???
  592E    FFD9     NOP


  5930    52E5     MOVF 0xfe5, F, ACCESS        (SP)-- -> (SP)--


  5932    CFE5     MOVFF 0xfe5, 0xff4		(SP)-- -> PRODH
  5934    FFF4     NOP
  5936    CFE5     MOVFF 0xfe5, 0xff3		(SP)-- -> PRODL
  5938    FFF3     NOP
  593A    CFE5     MOVFF 0xfe5, 0xfea		(SP)-- -> FSR0H
  593C    FFEA     NOP
  593E    CFE5     MOVFF 0xfe5, 0xfe9		(SP)-- -> FSR0L
  5940    FFE9     NOP
  5942    CFE5     MOVFF 0xfe5, 0xfda		(SP)-- -> FSR2H
  5944    FFDA     NOP
  5946    50E5     MOVF 0xfe5, W, ACCESS	(SP)-- -> W
  5948    CFE5     MOVFF 0xfe5, 0xfe0		(SP)-- -> BSR
  594A    FFE0     NOP
  594C    CFE5     MOVFF 0xfe5, 0xfd8		(SP)-- -> STATUS
  594E    FFD8     NOP
  5950    0010     RETFIE 0
611:

Bud,

I think the confusion is in the “MOVF 0xfe6, F, ACCESS” statement. The only purpose of this line is to increment FSR1 register (stack pointer) or in your notation (SP)++. Technically, it modifies the status register as well, but it’s not used in this case. This line enables the code to go from pre-incrementing the stack pointer in the previous lines, to post incrementing the stack pointer in later lines. If they didn’t do this, then the same register would get overwritten during the switch. It has nothing to do with actually saving any registers or the PRODH register.

When restoring the context, the equivilent MOVF statment just decrements the stack pointer. Hopefully, this will make things a little clearer.

Mike

52E6 MOVF 0xfe6, F, ACCESS

This instruction, according to the PIC18F manual, should cause the FSR1L to be incremented twice - once upon the reading of the data pointed to by FSR1 through the POSTINC1 pseudo-register and once again upon writing to ram pointed to by FSR1 through POSTINC1.

If the instruction was the following, then FSR1 would only be accessed on the read with the results left in the W register. In this case the FSR1 would be only incremented once.

50E6 MOVF 0xfe6, W, ACCESS

This was the instruction I was expecting to see.

Bud

Lets assume that I don’t have direct access to the interrupt service routine itself, but do have the ability to hook into certain interrupts to get my subroutine to run at interrupt level.

The purpose of all this is to try and have the ability to conditionally inject an event handler stack frame onto the stack. The interrupt routine would then end up returning to the event handler with interrupts off… asynchronously handle one or more events and then return to the real previous user level code.

Bud

Mike,

Thanks - I believe you are correct. I’m re-reading the PIC18 specification. Although I read the spec as indicating that an access to the POSTINC, PREINC, or POSTDEC locations cause the associated FSR to be appropriately incremented or decremented after an indirect access I think what I should have read in addition was “per instruction cycle”.

So a MOVF POSTINC1, F, ACCESS will do a post increment at the end of the instruction cycle and not at the end of the individual accesses. Subtle but obviously very important distinction.

The MOVFF since it is two instruction cycles can increment/decrement on each access but the MOVF is only a single instruction cycle.

Thanks again,
Bud

One of the other things that confused me is that the C18 stack calling convention is initially broken in the interrupt routine by using the PREINC to do stack pushes instead of the expected POSTINC I was expecting. The stack pointer should always be pointing to the next unused location on the stack.

It turns out this is true with one exception. Since there is no PREDEC instruction, the compiler to pop things off the stack must use two instructions, a MOVF POSTDEC followed by a MOVFF INDF, <reg>. Because these two instructions are non-atomic and an interrupt can catch the execution between these two instructions. Interrupt routines must assume that the stack location being currently pointed to may currently be in use and shouldn’t be overwritten. Hence the interrupt stack protocol of apparently allocating an extra stack entry at the beginning of the stack manipulation.

Now on to the fun stuff, figuring out how to inject an event driven handler with user level code onto the stack - <insert evil laugh here>

Bud