I introduced myself a few days ago here:
http://forum.allaboutcircuits.com/showpost.php?p=376421&postcount=225
I'd like to share my first PIC trick with you, and it works on 18F series devices.
The "Computed GOTO" is a valuable method of implementing structures like state machines and lookup tables (FWIW, I use TBLRD for lookup tables for the 18F parts as they are more efficient). As you all know, a computed goto has the form of:
where the w register acts like an index to the next instruction to be executed. This capability has existed since the very first PIC silicon (PIC16C54).
The Computed GOTO was relatively straight forward, until Microchip added the two byte instruction format, as in the 18F series parts. Therefore, it has become necessary to multiply w by 2 to get the proper index prior to the jump.
In addition, as WREG is only 8 bits wide, a jump table could be at most 256 instructions long (16C) or 128 instructions long (18F) *and* all jump destinations must reside within a 256 byte page as defined by the PCLATH register, which must be set properly prior to executing the jump, resulting in such code as:
Then, after assembly, the list file must be inspected to ensure the jump table does not cross page boundaries. This is easy for a small number of tables, especially if you align the tables to page boundaries with ORG directives. But more numerous and longer tables become more difficult. And rearranging code using multiple ORG directives tends to waste program memory.
One of the interesting, and little used, features of the 18F series silicon is that it provides access directly to the stack, through the top-of-stack registers TOSL, TOSH, and TOSU. These registers can be exploited to create a computed GOTO that is no longer restricted to page boundaries, page size, or WREG size.
Here is a subroutine that will preform a computed GOTO anywhere in memory, with 256 possible jumps, and works anywhere in program memory, even across page boundaries:
;**************************************
;** CJUMP -- Preform a computed jump **
;**************************************
;** w = index (0 to 255) **
;**************************************
cjump
Upon entry, the top-of-stack registers have the return address following the calling instruction. The routine simply adds (twice) the value of WREG to that address, and the returns to the indexed instruction. Since the TOS registers fully define the return address, without reference to PCLATH or PCLATU, the table can cross or span multiple code pages. A minor modification can be made to add an additional 8 bit register that, when used in conjuction with WREG, could support a jump table up to 65536 instructions long.
To use this routine, simply set WREG with an index from 0 to 255, and call CJUMP:
The call itself takes 10 instruction cycles, as opposed to 6 in the traditional way (including loading PCLATH and doubling WREG). Code space is actually saved as each additional table only needs a single CALL (or RCALL) instruction with no additional support.
BEWARE: The data sheets warn against modifying the TOS registers when using interrupts. I've discussed this concept with Microchip engineers, and they have given no good reason why the TOS may get corrupted if interrupts are used, *and* I have used this structure repeatedly in implementations with *lots* of high and low priority interrupts operating continuously and simultaneously. I have never found a single instance where the TOS gets corrupted under any circumstances.
Also, I have no idea whatsoever how this routine would work within C compiled code (or a real-time os) where the compiler or OS manages the stack.
Enjoy!
http://forum.allaboutcircuits.com/showpost.php?p=376421&postcount=225
I'd like to share my first PIC trick with you, and it works on 18F series devices.
The "Computed GOTO" is a valuable method of implementing structures like state machines and lookup tables (FWIW, I use TBLRD for lookup tables for the 18F parts as they are more efficient). As you all know, a computed goto has the form of:
addwf pcl,f
instruction 0
instruction 1
...
instruction n-1
instruction 0
instruction 1
...
instruction n-1
where the w register acts like an index to the next instruction to be executed. This capability has existed since the very first PIC silicon (PIC16C54).
The Computed GOTO was relatively straight forward, until Microchip added the two byte instruction format, as in the 18F series parts. Therefore, it has become necessary to multiply w by 2 to get the proper index prior to the jump.
In addition, as WREG is only 8 bits wide, a jump table could be at most 256 instructions long (16C) or 128 instructions long (18F) *and* all jump destinations must reside within a 256 byte page as defined by the PCLATH register, which must be set properly prior to executing the jump, resulting in such code as:
movlw high jmptab
movwf pclath
bcf status,c
rlncf index,w
addwf pcl,f
jmptabmovwf pclath
bcf status,c
rlncf index,w
addwf pcl,f
instruction 0
instruction 1
...
instrucion n-1
instruction 1
...
instrucion n-1
Then, after assembly, the list file must be inspected to ensure the jump table does not cross page boundaries. This is easy for a small number of tables, especially if you align the tables to page boundaries with ORG directives. But more numerous and longer tables become more difficult. And rearranging code using multiple ORG directives tends to waste program memory.
One of the interesting, and little used, features of the 18F series silicon is that it provides access directly to the stack, through the top-of-stack registers TOSL, TOSH, and TOSU. These registers can be exploited to create a computed GOTO that is no longer restricted to page boundaries, page size, or WREG size.
Here is a subroutine that will preform a computed GOTO anywhere in memory, with 256 possible jumps, and works anywhere in program memory, even across page boundaries:
;**************************************
;** CJUMP -- Preform a computed jump **
;**************************************
;** w = index (0 to 255) **
;**************************************
cjump
addwf tosl,f
skpnc
incf tosh,f
addwf tosl,f
skpnc
incf tosh,f
return
skpnc
incf tosh,f
addwf tosl,f
skpnc
incf tosh,f
return
Upon entry, the top-of-stack registers have the return address following the calling instruction. The routine simply adds (twice) the value of WREG to that address, and the returns to the indexed instruction. Since the TOS registers fully define the return address, without reference to PCLATH or PCLATU, the table can cross or span multiple code pages. A minor modification can be made to add an additional 8 bit register that, when used in conjuction with WREG, could support a jump table up to 65536 instructions long.
To use this routine, simply set WREG with an index from 0 to 255, and call CJUMP:
movfw index
call cjump
instruction 0
instruction 1
...
instruction n-1
call cjump
instruction 0
instruction 1
...
instruction n-1
The call itself takes 10 instruction cycles, as opposed to 6 in the traditional way (including loading PCLATH and doubling WREG). Code space is actually saved as each additional table only needs a single CALL (or RCALL) instruction with no additional support.
BEWARE: The data sheets warn against modifying the TOS registers when using interrupts. I've discussed this concept with Microchip engineers, and they have given no good reason why the TOS may get corrupted if interrupts are used, *and* I have used this structure repeatedly in implementations with *lots* of high and low priority interrupts operating continuously and simultaneously. I have never found a single instance where the TOS gets corrupted under any circumstances.
Also, I have no idea whatsoever how this routine would work within C compiled code (or a real-time os) where the compiler or OS manages the stack.
Enjoy!