Tables without retlw on pic16.

Status
Not open for further replies.

Pommie

Well-Known Member
Most Helpful Member
In another thread someone was asking about doing calls from within an interrupt as they wanted to use a lookup table for a seven segment display. I suggested an alternative way to do a lookup table without a call instruction.

I realised this hadn't come up previously and so I thought I'd post it here.
Code:
		movwf	temp
		addwf	PCL,f
		xorlw	"H"^"e"
		xorlw	"e"^"l"
		xorlw	"l"^"l"
		xorlw	"l"^"o"
		xorlw	"o"^" "
		xorlw	" "^"W"
		xorlw	"W"^"o"
		xorlw	"o"^"r"
		xorlw	"r"^"l"
		xorlw	"l"^"d"
		xorlw	"d"^"!"
		xorlw	"!"
		xorwf	temp,w
The above code can be run inline and whatever value is in W prior to it being executed will be used as an offset into the phrase "Hello World!". This saves a valuable call in the interrupt. The only downside is it is less efficient.

Mike.
 
I posted an example in 2007 in the PIC DayOfWeek() Routine thread that shows the extra code and the operand format for a "boundary tolerant" in-line table.

The operand format of a "non-boundary tolerant" version of the in-line table looks similar to your example but uses two less words of program memory. The first parameter is the desired value, the second parameter cancels out WREG, and the third parameter (in parenthesis) cancels out the following line;

Code:
;******************************************************************
;*                                                                *
;*  Day_of_Week, 2000-2099           Mike McLaren, K8LH, Aug '07  *
;*                                                                *
;*  params: Month (1..12), Day (1..31), Year (0..99)              *
;*  output: W = 0..6 (Sun..Sat)                                   *
;*                                                                *
;*  34 words (14 bit core)                                        *
;******************************************************************
        radix dec
;
;  char Month;                  // 1..12
;  char Day;                    // 1..31
;  char Year;                   // 0..99 (2000-2099)
;
;  char DayOfWeek()
;  {                            // returns 0..6 (Sun..Sat)
;    char Leap;
;    rom Y2K[] = { 5, 1, 1, 4, 6, 2, 4, 0, 3, 5, 1, 3 };
;
;    Leap = (Year & 3) || (Month > 2);
;    return (Y2K[Month-1] + Day + Leap + Year + Year/4) % 7;
;  }
;
DayOfWeek_1
        clrf    Leap            ; Leap = 0                        |B0
        movf    Month,W         ; W = Month (1..12)               |B0
        addlw   -3              ; C = 0 if Jan or Feb             |B0
        movf    Year,W          ; W = Year (0..99)                |B0
        andlw   b'00000011'     ; is this a leap year?            |B0
        skpz                    ; yes, skip, else                 |B0
        setc                    ; set C (will force Leap = 1)     |B0
        rlf     Leap,F          ; Leap = 0 or 1                   |B0
        decf    Month,W         ; W = Month-1 (0..11) index       |B0
        [COLOR=Blue]addwf   PCL,F           ; get Y2K[Month-1] array element  |B0
Y2K     xorlw   5^00^(1^01)     ; 6 (Jan 2000)                    |B0
        xorlw   1^01^(1^02)     ; 2 (Feb)                         |B0
        xorlw   1^02^(4^03)     ; 2 (Mar)                         |B0
        xorlw   4^03^(6^04)     ; 5 (Apr)                         |B0
        xorlw   6^04^(2^05)     ; 0 (May)                         |B0
        xorlw   2^05^(4^06)     ; 3 (Jun)                         |B0
        xorlw   4^06^(0^07)     ; 5 (Jul)                         |B0
        xorlw   0^07^(3^08)     ; 1 (Aug)                         |B0
        xorlw   3^08^(5^09)     ; 4 (Sep)                         |B0
        xorlw   5^09^(1^10)     ; 6 (Oct)                         |B0
        xorlw   1^10^(3^11)     ; 2 (Nov)                         |B0
        xorlw   3^11            ; 4 (Dec)                         |B0[/COLOR]
;  WREG = (((Y2K[Month-1]+Day+Leap)*2 + Year/2)/2 + Year) % 7
        addwf   Day,W           ; add Day (1..31)                 |B0
        addwf   Leap,F          ; add Leapday (0 or 1)            |B0
        rlf     Leap,F          ; multiply by 2                   |B0
        rrf     Year,W          ; divide year by 2                |B0
        addwf   Leap,F          ; add 'em together                |B0
        rrf     Leap,W          ; divide result by 2              |B0
        addwf   Year,W          ; add Year (0..99)                |B0
Mod7a   addlw   -7              ; result % 7                      |B0
        bc      Mod7a           ;                                 |B0
        addlw   7               ;                                 |B0
        return                  ;                                 |B0
;
 
Last edited:
I knew I had seen it somewhere but couldn't remember where. Nice routine BTW.

Mike.
 
Code:
[COLOR=Blue]
        addwf   PCL,F           ; get Y2K[Month-1] array element  
Y2K     xorlw   5^00^(1^01)     ; 6 (Jan 2000)                   
        xorlw   1^01^(1^02)     ; 2 (Feb)                        
        xorlw   1^02^(4^03)     ; 2 (Mar)                        
        xorlw   4^03^(6^04)     ; 5 (Apr)                        
        xorlw   6^04^(2^05)     ; 0 (May)                         
        xorlw   2^05^(4^06)     ; 3 (Jun)                         
        xorlw   4^06^(0^07)     ; 5 (Jul)                         
[/COLOR]
When the micro jumps down to the line that starts with:

xorlw 1^01^(1^02) ; 2 (Feb)

what is the next instruction carried out?
 
Last edited:
Code:
[COLOR=Blue] [COLOR=Black]addwf PCL,F ; get Y2K[Month-1] array element |B0
Y2K xorlw 5^00^(1^01) ; 6 (Jan 2000) |B0
xorlw 1^01^(1^02) ; 2 (Feb) |B0
xorlw 1^02^(4^03) ; 2 (Mar) |B0
xorlw 4^03^(6^04) ; 5 (Apr) |B0
xorlw 6^04^(2^05) ; 0 (May) |B0
xorlw 2^05^(4^06) ; 3 (Jun) |B0
xorlw 4^06^(0^07) ; 5 (Jul) |B0[/COLOR][/COLOR]

When the micro jumps down to the line that starts with:

xorlw 1^01^(1^02) ; 2 (Feb)

what is the next instruction carried out?

Original post restored, DO NOT delete posts to make nonsense of a thread! - moderator.
 
Last edited by a moderator:

The one on the next line!!

If W was 3 at the start of the code then all the instructions in red will be executed and W will contain 4 (in green).

Mike.
 
Last edited:
What are you having trouble with Colin? Understanding XOR logic, or single-stepping through the code using MPLAB simulator and the "watch" window?
 
Firstly, what is the actual value of:
4^03^(6^04)

then what is each step actually doing to the value in w


 
Firstly, what is the actual value of:
4^03^(6^04)

Well, 4^03^(6^04) = 5 but the instruction associated with that operand is an xorlw instruction and so you're really looking at WREG^4^03^(6^04) and the result is dependent on the value in WREG.

then what is each step actually doing to the value in w
Here's where learning how to use the MPLAB Simulator might be helpful. I explained that the first parameter in the operand contains the desired value, the second parameter cancels out WREG, and the third parameter (in parenthesis) cancels out the action of the folowing line. The instructions and operands are constructed in such a way that you end up with WREG equal to the "desired value" from the line where you entered the table.

Are you familiar with XOR logic? For example, have you come across and do you understand the following code example?

Code:
Input
        call    Get232          ; get incoming Hyperterminal char
        xorlw   "A"             ; the "A" key?
        skpnz                   ; no, skip, else
        goto    do_A            ; perform the "A" function
  
        xorlw   "B"^"A"         ; the "B" key?
        skpnz                   ; no, skip, else
        goto    do_B            ; perform the "B" function
  
        xorlw   "C"^"B"         ; the "C" key?
        skpnz                   ; no, skip, else
        goto    do_C            ; perform the "C" function
        goto    Input           ; branch (not "A", "B", or "C")
We check the input for "A" using an exclusive-or instruction. If it's equal we branch and process the "A" code but if it's not equal WREG ends up containing "character"^"A". Now if we exclusive-or WREG with "A" again then WREG will contain the original character and that's what we're doing in the next test. We exclusive-or WREG with "A" to get the original character value and then we exclusive-or WREG with "B" to test for WREG == "B". We're just combining the exclusive-or "A" and exclusive-or "B" in one operand since they're both constants. Does that make sense?

Why would someone want to do it this way? Good question. It eliminates one variable and uses one less instruction per test but it's at the expense of readability for those who may be unfamiliar with the technique. But that's why we include meaningful comments to let you know exactly what's going on. It's not really coding "at the next level" or "at a higher level". It's just taking advantage of a fuller understanding of the instruction set and logic to write more efficient code.

Regards, Mike
 
Last edited:
How do you get 4^03^(6^04) = 5
Code:
  110 (6)
^ 100 (4)
------
  010
^ 011 (3)
------
  001
^ 100 (4)
------
  101 (5)
If you select the "scientific" mode of Windows Calculator you'll get an XOR key and you can test it yourself in decimal, hex, or binary. Are you just messin' with us or do you really not understand XOR logic?

Regards, Mike
 
Last edited:
Of course I understand XOR logic but not how to process the instruction I requested.
I now realise the instruction is processed from right to left, but why are the last two values in brackets? 4^03^(6^04)
What would be the result of: 4^03^6^04
 
You can process that operand in any order and you'd get the same result. I placed the last two XOR values in parenthesis simply to make it easier to see that it represented the value that needs to be canceled out in the next instruction line.
 
Last edited:
The compiler will simply put:

xorlw 5

when assembling, so why have you made the instrcution so complex to comprehend?

and were did you generate the figures from?






 
Colin,

If you look at the first post it is much easier to understand, can you see that it is clearer to write xorlw "H"^"e" rather than xorlw 0x2d. See how easy it is to see what the message is?
Code:
		xorlw	"H"^"e"
		xorlw	"e"^"l"
		xorlw	"l"^"l"
		xorlw	"l"^"o"
		xorlw	"o"^" "
		xorlw	" "^"W"
In Mike's (K8LH) case he divided it into the value required, the offset and the value that needed canceling. A logical way to write it.

Mike.
 
Last edited:
The compiler will simply put:

xorlw 5

when assembling, so why have you made the instrcution so complex to comprehend?

and were did you generate the figures from?

I suspect at this point that this concept may be a bit "over your head", so to speak (lol). If I had used xorlw 5 instead I think even I would have trouble remembering how I came up with that value (lol).
 
Last edited:

He wouldn't even acknowledge that he understood that simple "ABC" example and explanation I posted. I'm beginning to think it's "a lost cause" (lol).

After copying one of my programs onto his website last year (without my permission) he complained about my isochronous bin2bcd routine, saying "This routine is so complex that I am not going to explain it.".
Code:
;
;  setup 'tens' and 'ones' for next loop
;
        clrf    tens            ; isochronous bin2bcd routine     |B0
        addlw   -80             ; W = W - 80                      |B0
        rlf     tens,F          ; shift in 2^3*10 bit             |B0
        btfss   tens,0          ; borrow? no, skip, else          |B0
        addlw   80              ; W = W + 80                      |B0
        addlw   -40             ; W = W - 40                      |B0
        rlf     tens,F          ; shift in 2^2*10 bit             |B0
        btfss   tens,0          ; borrow? no, skip, else          |B0
        addlw   40              ; W = W + 40                      |B0
        addlw   -20             ; W = W - 20                      |B0
        rlf     tens,F          ; shift in 2^1*10 bit             |B0
        btfss   tens,0          ; borrow? no, skip, else          |B0
        addlw   20              ; W = W + 20                      |B0
        addlw   -10             ; W = W - 10, now W = "ones"      |B0
        rlf     tens,F          ; shift in 2^0*10 bit             |B0
        btfss   tens,0          ; borrow? no, skip, else          |B0
        addlw   10              ; W = W + 10, now W = "ones"      |B0
        movwf   ones            ; save "ones"                     |B0
;
It seems reasonably simple and intuitive to me but I can understand how it might throw someone off if they don't understand it and aren't willing to study or simulate it. It's much easier to say "it's too complex" (lol). In all fairness, I can see where my 2^3*10 (2 to the 3rd power times 10) type notations in the comments may be confusing...

Cheerful regards, Mike
 
Last edited:
Code:
        xorlw    "H"^"e"
        xorlw    "e"^"l"
        xorlw    "l"^"l"
        xorlw    "l"^"o"
        xorlw    "o"^" "
        xorlw    " "^"W"

First of all, what is the above doing?
How does the letter "H" appear on a screen?
It needs a lot more explaining.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…