1
\$\begingroup\$

I am following a tutorial on programming baseline 12F series PICs in assembler.

I have a question regarding bitwise operators specifically the << left shift operator when combined with the | inclusive OR operator.

For example say I'm using a PIC 12F508 and I want to set the bits to use Timer 0, assign the prescaler to Timer 0, and use a 1:256 prescale in the option register.

I also want the other bits I'm not touching stay set to 1 since the option register by default is 11111111.

Rather than specifying the bits directly such as

movlw    b'11010111'
option

I'd rather address the bits by name as specified in the PIC12F508 include file that way I'm less likely to clear the wrong bit.

Like this:

movlw    1<<NOT_GPWU | 1<<NOT_GPPU | 0<<T0CS | 1<<TOSE | 0<<PSA | b'111'
option    

I'd rather not address the individual prescaler set bits by name since I feel it makes it harder to read.

This was introduced in the tutorial and I believe I understand how this works but I just want to verify I understand what's going on.

First off the order of precedence for the bitwise operators are as follows:

~  compliment

left shift  <<

right shift >>

bitwise AND &

bitwise exclusive OR ^

bitwise inclusive OR |

In other words if there are no ( ) parenthesis which changes the order of precedence the operators toward the top of the list will be carried out first.

So since the << left shift it at the top of the list and the inclusive or | is at the bottom. The left shifts are performed first.

For example NOT_GPPU = 1 << 6 since 6 is specified as the bit position in the include file. So 01000000 would be the result.

T0CS = 0 << 5 since 5 is specified as the bit position in the include file. So 00000000 would be the result. This left shift is unnecessary since it does nothing as 0 left shifted 5 times is still 0 but it specifies what the T0CS bit is set to for clarity.

So going down the line from MSB to LSB after the left shifts we have:

GPWU 10000000

GPPU 01000000

T0CS 00000000

TOSE 00010000

PSA> 00000000

--------------111

So now these values or inclusive ORed together by the assembler to get the final value to be moved to the option register.

-------11010111 = Result of inclusive OR

Bitwise Inclusive or truth table:

A   B   Out
1   1   1
0   0   0
1   0   1
0   1   1

So then:

movlw    1<<NOT_GPWU | 1<<NOT_GPPU | 0<<T0CS | 1<<TOSE | 0<<PSA | b'111'
option

Becomes:

movlw    b'11010111'
option

So based on my understanding I don't even have to put the left shifts in the proper bit order correct?

For example:

movlw    0<<T0CS | 1<<NOT_GPPU | 1<<NOT_GPWU | 0<<PSA | 1<<TOSE | b'111'
option

Of course it makes more sense to put them in order but and obviously the last 3 bits are position sensitive because I'm not specifying them by name (PS2, PS1, PS0).

But the same binary output 11010111 should result?

Thanks In Advance.

\$\endgroup\$
4
\$\begingroup\$

Yes, you seem to understand correctly how MPASM processes numeric expressions.

While using the bit names is way better than the hard coded HEX values most people use, I think there is yet a better way. The problem with the bit names is that they can be cryptic, and you are relying on the name only to explain what each bit does. This method also doesn't work well with multi-bit fields, as you demonstrate with the b'111' at the end. I had to look in the datasheet to find that these are the prescaler divider selection bits.

I like to show each field on a separate line so that each gets its own end of line comment. Here is a example of setting the OPTION register in some 12F508 code from 2009:

         movlw   b'01000010' ;set OPTION register
                 ; 0-------  wake-up on change enabled
                 ; -1------  weak pullups disabled on GP0,GP1,GP3
                 ; --0-----  timer 0 clocked from instruction clock
                 ; ---0----  timer 0 inc on rising edge of T0CKI (not used)
                 ; ----0---  prescaler assigned to timer 0
                 ; -----010  prescaler = 1:8
         option

I use this method for most SFR settings.

\$\endgroup\$
  • 1
    \$\begingroup\$ Nice commenting trick. I love code that comes with documentation like that. \$\endgroup\$ – slebetman Oct 18 '15 at 4:28
3
\$\begingroup\$

Yes, it doesn't matter what order you put them in, and indeed the whole idea of using the names is that you don't need to know what numbers they represent at the point you used them. If you could only use them in a particular order, then the names would represent an enormous hazard.

Commonly, one wouldn't include all the bits which are NOT set, so instead of

1<<NOT_GPWU | 1<<NOT_GPPU | 0<<T0CS | 1<<TOSE | 0<<PSA | b'111'

one might write

1<<NOT_GPWU | 1<<NOT_GPPU | 1<<TOSE | b'111'

You always have to make sure you're clear on whether the person who wrote the headers is using bit numbers or providing the complete mask (i.e. the shift has already been done) - either is common, and you need to know which is which when you come to use them.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.