Log in

View Full Version : Trying to follow C18 interrupt context code...


dcbrown
20-12-2006, 17:43
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:

Mike Bortfeldt
20-12-2006, 18:41
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

dcbrown
20-12-2006, 22:34
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

dcbrown
20-12-2006, 22:45
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

dcbrown
21-12-2006, 00:34
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

dcbrown
21-12-2006, 09:01
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