This is about the way that Solidity
byte types are represented for use in the EVM:
byte types are all left-justified within a 32-byte word, i.e. a single
byte type is left-shited by 31 * 8 = 248 bits before being put on the stack.
In your first example, variable
b is a single-width
byte type so it is put on the stack as
0xf has no particular type, so it is put on the stack as
0x0f, which is the same as
0x000000000000000000000000000000000000000000000000000000000000000f. The logical
and of these is clearly zero.
In your second example, both of the operands to
and are explicitly
byte types, therefore Solidity computes:
0x2c00000000000000000000000000000000000000000000000000000000000000 & 0x0f00000000000000000000000000000000000000000000000000000000000000, and then it reports the single byte result
c in the Check event (the left-most byte of the result, since that's how
bytes are stored in Solidity).
The Remix IDE debugger is very helpful for stepping through code like this to see what's going on under the hood. If you are going to use some assembly code I highly recommend it - as you can see there are some surprises.