PIC16 beginner questions

easyhermit

New Member
Hey, I consider myself a beginner with PICs even though I've been messing with them for a few years; I just never made anything impressive with them.

I'm reading about interrupts. Mainly the part where you save and then restore the registers in an ISR. I was studying Jon Wilder 's code here: https://www.electro-tech-online.com/threads/pic-timer-interrupt.128914/post-1073925

Code:
ISR:

        movwf        W_TEMP            ;save W
        swapf        STATUS,W        ;save STATUS
        movwf        STATUS_TEMP
        banksel        0                ;bank 0
        movfw        PCLATH            ;save PCLATH
        movwf        PCLATH_TEMP

        ; ISR code

        movfw        PCLATH_TEMP        ;restore PCLATH
        movwf        PCLATH
        swapf        STATUS_TEMP,W    ;restore STATUS
        movwf        STATUS
        swapf        W_TEMP,F        ;restore W
        swapf        W_TEMP,W
        retfie                        ;return to main code

And this code by Diver300 here: https://www.electro-tech-online.com/threads/pic-interrupt-saving-w-and-status.34143/post-261191

Code:
movwf        w_save
swapf         STATUS,w
movwf        status_save
swapf         w_save, f

after the interupt, you should use:-

swapf       status_save, w
movwf      status
swapf       w_save, f
retfie

The first three lines are equivalent, but Diver300 has
Code:
swapf w_save, f
for line 4. What's the purpose of that? Is it important? Also, Jon Wilder saves PCLATH, but Diver300 doesn't. Can someone please explain the differences and reasoning between these two methods?

The datasheet says it swaps nibbles. So does that mean if f = 8Eh, the destination register would be E8h?
 
Last edited:
My code from 17 years ago had a bug. I'm sorry about that. I haven't used PIC16 microcontrollers much since then.

Firstly, about PCLATH. That is the write-only latch for the high byte of the program counter. I've just had to look up the details.

For CALL and GOTO instructions, the program space is effectively split into areas that are 1024 instructions in length. When any CALL or GOTO instruction happens, the value of PCLATH controls in which area of 1024 instructions is executed next.

A lot of PIC16 microcontrollers have space for less than 1024 instructions, so PCLATH does not need to change for GOTO and CALL instructions.

Another use of PCLATH is for computed jumps. Those are often used in lookup tables, where a number is added to the PCL (which is the bottom 8 bits of the program counter). If PCL is directly manipulated by the program, so something like:-
Code:
    addwf    PCL
then PCLATH is used for the top bits of the program counter.

If all lookup tables are in the first 256 instructions, then PCLATH can be left at it's default value of zero.

So PCLATH only needs to be changed if the program is bigger than 1024 instructions or if it has lookup tables that are outside of the first 256 instructions. My ISR code ignores PCLATH on the assumption that it's not needed.

Similarly, the bank select command should be used if the ISR uses any registers that are only in one bank. My code assumes that the temporary registers (status_save and w_save) are in the areas of memory that can be accessed in any bank.

Secondly, the swapf.

My code from 17 years ago is wrong. It should read:-
Code:
movwf        w_save              ;w_save is now the pre-interrupt W
swapf         STATUS,w                     ;w is now the pre-interrupt STATUS  (swapped)
movwf        status_save                  ;status_save is the pre-interrupt STATUS  (swapped)
swapf         w_save, f           ;w_save is now the pre-interrupt W (swapped)

after the ISR, you should use:-

swapf       status_save, w                 ;w is now the pre-interrupt STATUS (swapped twice, so back to how it started)
movwf      status                             ;STATUS is now the pre-interrupt STATUS (swapped twice, so back to how it started)
swapf       w_save, w                       ;W is now the pre-interrupt W (swapped twice, so back to how it started)
retfie

The idea is to save and restore the W register and the STATUS registers. The MOVWF and the SWAPF commands do not affect the STATUS register so the code is written to use those only.

SWAPF, as you worked out, just swaps the nibble order, so 0x12 becomes 0x21. It is used in the ISR instead of MOVF because MOVF affects the STATUS register. To avoid swapping the W or STATUS registers, SWAPF has to be used an even number of times to restore the registers correctly.

Specifically the line:-

Code:
swapf         w_save, f

could be after the ISR. The other example used w_temp instead of w_save, but that's just a name. It puts the SWAPF w_save, f after the ISR, which is could be better as the ISR gets executed one instruction sooner.

It is needed because SWAPF has to be used to restore the W register because MOVF, W would affect the STATUS register. The W register has to be restored as the last item because restoring anything else uses the W register.
 
Last edited:
Cookies are required to use this site. You must accept them to continue using the site. Learn more…