h.n
h.n

Reputation: 153

bitwise operation techinques for unpacking and packing values into one int

Finally understood what was wrong. It was my brain

Probably the worst embarrassment of the year...a comment do you understand how binary operators work? was right.

A >> 2 #shifts the value of A to right by 2 bits

And all this time, for years, I thought 2 is shifted by the value of A in the case above!!

Sorry guys for this silly question and thank you for correcting my idiocy and your tolerance and kindness are much appreciated.

Original post

I'm trying to understand how a portion of a DIY keyboard firmware works.The firmware is called qmk: https://github.com/qmk/qmk_firmware

I'm not exactly certain but the code is in a c variant called AVR c.

What the code does?

The C macro changes keyboard layer when held, like a function key does, and send a hid usage id to pc when quickly tapped.

The code

The initial value for layer is a decimal between 1 to 32.
kc is any one of the hid usage ids.

#define QK_LAYER_TAP          = 0x4000
#define LT(layer, kc) (kc | QK_LAYER_TAP | ((layer & 0xF) << 8))
#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))
#define ACTION_LAYER_TAP(layer, key)  (ACT_LAYER_TAP<<12 | (layer)<<8 | (key))

In another file within a loop, the below is fired when user presses a key to which maybe LT(2,KC_A) is bound.

case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
    action.code = ACTION_LAYER_TAP_KEY((keycode >> 0x8) & 0xF, keycode & 0xFF);
    break;

What happens

So, in the case of LT(2,KC_A), this macro #define LT(layer, kc) (kc | QK_LAYER_TAP | ((layer & 0xF) << 8)) yields 04 | 0x4000 | ((2 & 0xf) << 8).

KC_A -> 101
0x8 -> 00001000
0xf -> 00001111
0x4000->100000000000000
2&0xf -> 00000010
((2&0xf)<<8) -> 1000000000

The firmware detects 0x4000 bit mask and calls the above case QK_LAYER_TAP, then some magic number keycode appears which originates from LT macro.

For LT(2,04) keycode yields 2 for (keycode >> 0x8) & 0xF, and keycode yields 4 for keycode & 0xff.

The question(Edited since no one understood me)

keycode contains a bit sequence in int16 from the first and the second parameters of the LT macro call.

Since evaluating the expression keycode & 0xff yields the hid usage id passed to LT, it is clear keycode contains the second parameter of LT in the first 8 bits.

To this point everything is clear. But....

(keycode >> 0x8) & 0xF yields the first parameter of TL.

Upvotes: 0

Views: 1063

Answers (2)

Drew
Drew

Reputation: 229

As others have said, you should rewrite this as a single succinct question. ..But, it sounds like what you meant to ask is: "what is this code doing". I'll try to explain.

your case statement is executing this line: action.code = ACTION_LAYER_TAP_KEY((keycode >> 0x8) & 0xF, keycode & 0xFF);

ACTION_LAYER_TAP_KEY takes 2 arguments. Lets look at them individually.

The first is (keycode >> 0x8) & 0xF, what this does is take the keycode and bit shift it 8 places to the right (bits that go off the end are lost). The result is then and-ed (bitwise) with 0xF (00001111). This has the effect of discarding everything but but the first 4 bits.

The second is keycode & 0xFF. What this does is discard everything except the first 8 bits of keycode. So now the arguments are keycode bits 8-11 and keycode bits 0-7.

Ok, now what does ACTION_LAYER_TAP_KEY do with this data?

#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))

Ok, it calls ACTION_LAYER_TAP with the same arguments. Now lets look at that.

ACTION_LAYER_TAP(layer, key) (ACT_LAYER_TAP<<12 | (layer)<<8 | (key))

First it takes ACT_LAYER_TAP, which is probably a constant and bit shifts it by 12 to the left. Then it does the same to layer except by 8, then it takes these two and key and or's them (bitwise). The result will be <ACT_LAYER_TAP><layer><key> packed into the integer. Layer can have values 0-15, and key can have values 0-255.

Refering back to the above, we can see that <layer> is really just bits 8-11 of keycode. and key is bits 0-7 of keycode. So what we really end up with is <ACT_LAYER_TAP><first 12 bits of keycode> packed into an integer.

Upvotes: 1

Basya
Basya

Reputation: 1485

You are dealing with firmware which is driving hardware. Shifting some bits and ORing them with others is creating the contents of a hardware register, in this case apparently a hardware configuration register.

It needs to put in bit flags, shift values, etc., to match the format of the register, which contains a lot of information, each piece one or more bits long, at specific locations.

To understand exactly what this is doing you would need to know specifics about the hw/sw interface, which may not be forthcoming.

Now, to your specific question. You wrote:

shift the above to right by 4 4 >> 0x8 results in zero...

This is shifting keycode 8 bits to the right, not shifting 8 by keycode bits. Since the first 8 bits are one value, which you have explained, this is extracting what is coded into the higher bits.

For example:

If your two parameters are 5 (to be encoded into the high part) and 4 (to be encoded into the low word):

lower word, in binary 00000100

higher word, in binary 00000101

Together: 0000010100000100

So bitwise & with 0xFF will give you 4 Shifting the value 8 bits to the right will give you 5.

Also, these are most likely unsigned integers. Signed integers are not generally used for this kind of work.

Upvotes: 1

Related Questions