I know you all are using C to program PIC18s these days, but I am still crunching along writing awesome code in .asm on a daily basis -- and loving every minute of it.
I came up with a neat new trick today -- out of necessity -- and I wanted to share.
Suppose you had a number of subroutines, and you wanted to call one (and only one!) of them based upon an index value. The 'C' equivalent would be:
In .asm, you'd normally do something like this:
There are a couple of things I don't like about this. First, the need to ensure pclath points to the proper page -- and the associated problem of ensuring that all destinations within the jump table are within that page -- gets annoying after a while. I previously solved this issue with another trick, a routine called CJUMP, detailed here. Using CJUMP, the code reduces to the following:
This is a bit better, as I no longer need to deal with PCLATH or worry about my jump table spanning page boundaries. But, I still have *two* tables. Can I reduce this to one simple table? Yes!
First, I want it to be possible that my subroutines may be anywhere in the address space, so I must use either CALLs or GOTOs, not RCALLs or BRAs. So I changed my CJUMP routine as follows:
I've now got two options: a computed jump into either a table of single word instructions or a table of double word instructions (CJUMP2). Using this, I can try something like:
But this is not correct. If the index is 0, routine0, routine1, and all the subsequent routines will be executed. I only want routine0 called, followed by a branch to JDONE. What to do?
Using the PIC18 PUSH instruction, I can preset the return stack to point to anywhere I like. If I set it to JDONE prior to indexing in the table, and use GOTOs instead of CALLs in the table, then I get what I desire:
Further, I can simplify with a macro:
For final code that looks like this:
Short, sweet, easy to read. I thought this was pretty neat. Comments welcome.
PS -> Why does this new editor turn my tabs into spaces?!!!!!
I came up with a neat new trick today -- out of necessity -- and I wanted to share.
Suppose you had a number of subroutines, and you wanted to call one (and only one!) of them based upon an index value. The 'C' equivalent would be:
Code:
switch (index)
{
case 0: routine0(); break;
case 1: routine1(); break;
...
case n: routinen(); break;
}
Code:
movlw high jtab ;ensure jump in page
movwf pclath
rlncf index,w ;double index
addwf pcl,f ;and make computed jump
jtab bra vect0 ;vector to call for specified routine
bra vect1
...
bra vectn
vect0 call routine0 ;and make the call
bra jdone ; "break" equivalent
vect1 call routine1
bra jdone
...
vectn call routinen
bra jdone
jdone ;finished
Code:
movf index,w ;get index in wreg
call cjump ;and jump to indexed vector
bra vect0 ;vector to call for specified routine
bra vect1
...
bra vectn
vect0 call routine0 ;and make the call
bra jdone ; "break" equivalent
vect1 call routine1
bra jdone
...
vectn call routinen
bra jdone
jdone ;finished
First, I want it to be possible that my subroutines may be anywhere in the address space, so I must use either CALLs or GOTOs, not RCALLs or BRAs. So I changed my CJUMP routine as follows:
Code:
;****************************************
;** CJUMP2 -- Preform a computed jump **
;** over 2 word instructions **
;****************************************
;** w = index (0 to 63) **
;****************************************
cjump2 rlncf wreg,f
;********************************************
;** CJUMP -- Preform a computed jump **
;** over single word instructions **
;********************************************
;** w = index (0 to 127) **
;********************************************
cjump rlncf wreg,f
addwf tosl,f
skpnc
incf tosh,f
return
Code:
movf index,w ;get index in wreg
call cjump2 ;and jump to indexed vector
call routine0 ;and make the call
call routine1
...
call routinen
jdone ;finished
Using the PIC18 PUSH instruction, I can preset the return stack to point to anywhere I like. If I set it to JDONE prior to indexing in the table, and use GOTOs instead of CALLs in the table, then I get what I desire:
Code:
push ;create a new stack entry
movlw high jdone ;copy return address to stack
movwf tosh
movlw low jdone
movwf tosl
movf index,w ;get index in wreg
call cjump2 ;and jump to indexed vector
goto routine0 ;and make the call
goto routine1
...
goto routinen
jdone ;upon RETURN, each routine ends up here
Code:
switch macro idxreg,retaddr
push ;create a new stack entry
movlw high retaddr ;copy return address to stack
movwf tosh
movlw low retaddr
movwf tosl
movf idxreg,w ;get index in wreg
call cjump2 ;and jump to indexed vector
endm
Code:
switch index,jdone ;index to routine, exit at jdone
goto routine0 ;and make the call
goto routine1
...
goto routinen
jdone ;upon RETURN, each routine ends up here
PS -> Why does this new editor turn my tabs into spaces?!!!!!
Last edited: