A bit-shift operator comes in two main varieties (and I'm not talking about the direction): shift, and rotate.
Additionally, both come in two directions, so typically you have four:
- shift-left
- shift-right
- rotate left
- rotate right
The first two shift the bits a number of bits to one direction. Any bits that "fall off" the end disappear. Any bits that "appear" on the other end, are zero.
Typically you also specify how many bits to shift the value.
So:
1000 shl 1 = 0000 (the 1 fell off the end, and a 0 appeared on the other end)
1000 shr 1 = 0100 (a zero fell off the right end)
Rotation doesn't lose bits that fall off, instead they are rotated back in on the other side.
1000 rol 1 = 0001 (the 1 was rotated back in on the other side)
You can think of the two operations as:
- For shifting, the number contains an infinite number of zeroes on both ends, that follows the value as you shift it
- For rotation, the number is repeated an infinite number in both directions, that follows the value as you shift it
There's also a variant of the rotation one, rotate through carry, that uses the carry flag in the process as an extra bit.
If the carry flag starts as 0, here's what happens:
1000 rcl 1 = 0000 (rcl = rotate through carry to left)
0000 rcl 1 = 0001 (now the 1 came back, it was temporarily stored in carry flag)
The last one can, in machine code, be used to move single bits from one register to another:
rcl ax, 1 ; rotate AX-register, 16-bit, left 1 bit, through carry
rcr bx, 1 ; rotate BX-register, 16-bit, right 1 bit, through carry
Here we take the leftmost bit from AX, temporarily rotate it out into the carry flag, and then rotate it back into the leftmost bit of BX.
Now, you can usually combine shifting with the other bitwise operators. For instance, to set bit N of value (where N is 0-based, and bit 0 is the rightmost one), you can do this:
value = value OR (1 shl N)
Here we first shift the value 1 N times to the left. If N is 0, this does not shift the bit at all.
Then we OR the result of that shifting with the existing value, and store it. OR has the effect of combining the 1's so that if either value has a 1-bit in a specific position, the result is also a 1-bit in that position.
So for the shifting:
1 shl 0 = 00000001 shl 0 = 00000001
1 shl 1 = 00000001 shl 0 = 00000010
1 shl 2 = 00000001 shl 0 = 00000100
1 shl 3 = 00000001 shl 0 = 00001000
1 shl 4 = 00000001 shl 0 = 00010000
1 shl 5 = 00000001 shl 0 = 00100000
1 shl 6 = 00000001 shl 0 = 01000000
1 shl 7 = 00000001 shl 0 = 10000000
Then the OR:
???????? OR 00100000 = ??1?????, where ? means whatever it was before
Let me take a couple of the lines of code you've posted:
UBRRH = ((XTAL / (8 * 250000)) - 1)>>8; // 250kbps at 16Mhz
UBRRL = (XTAL / (8 * 250000)) - 1;
The first does a calculation, (XTAL / (8 * 250000)) - 1
, which I don't know the purpose behind. This is normal math, however, so it calculates something. Let's call it a frequency (judging by the comment.)
This value is calculated twice, so let's rewrite the above statements:
UBRRH = value >>8; // 250kbps at 16Mhz
UBRRL = value;
Here I have to guess, but I'm guessing that UBRRH and UBRRL are both declared to be of type "BYTE", which means they can store at most 8 bits of value each. This means that the code actually reads like this:
- UBRRH takes the upper 8 bits of "value", shifts them into the lower 8 bits, and stores those. Since it only stores a byte, it chops off the rest, which means it grabs bits 8-15
- UBRRL takes the lower 8 bits, and chops off the rest, which means it grabs bits 0-7
Since the names of the two end in L
and H
, they fit with the assumption.