What are the rules for bitmasks? Like 0xFF vs. 0xFC
Solution 1
What you are doing is bitwise arithmetic. Ignore the high-order bits for now (since they are all 0's) and simply consider the two hexadecimal values 0xFFC0
and 0x12F0
. Then a bitwise and
works exactly like multiplication in base 10. It will look like this on the bit level:
0xFFC0 = 1111111111100000
&
0x12F0 = 0001001011110000
This then equals 0001001011100000 = 0x12F0
A trick to converting to and from hex-binary is this. Every two hex digits is a byte (i.e. 8 bits). For instance, 0xFF
is a single byte. Thus you can convert this to its binary representation by simply writing the bit-value for each hex digit (i.e. 0xF (base-16) = 1111 (base-2) = 15 (base-10)
). Since we know each byte is always exactly 8-bits, each hex digit converts to its own 4-bit binary representation. Then you only need to memorize binary representations for hexadecimal values 0000
(0) to 1111
(F) and replace them appropriately. The trick works in both directions.
As far as bitmasks go, this is simply useful for extracting values from a bitvector. A bitvector is (usually) a simple data type (i.e. int
, char
, etc.). Then, each particular bit signifies a type of value to enable or disable. So if I have a bitvector (char = single byte, so consider this data type for bitvector, for example) of 0x01
and my lowest-order bit signifies a door is enabled, then this bitvector has a door. If my bitvector's value is 0x02
, then there is no door enabled (but in 0x03
there is a door). Why is this? You need to always look at the underlying binary representation to fully understand a bitvector/bitmask.
0x01 = 00000001
, 0x02 = 00000010
, 0x03 = 00000011
As you can see, in the first and third values, the lowest-order bit is set. However, in the second value, the second lowest-order bit is set, but not the lowest-order. You can use this second value to signify another property, however (although for the purpose of example there is no door in the second value).
Then note, the corresponding bitmask (coincidentally) to retrieve a door from the bitvector formatted as above would be 0x01
since 0x01 & 0x01 = 1
, 0x02 & 0x01 = 0
, and 0x03 & 0x01 = 1
(again, return to the binary representation and multiply)
Solution 2
Pay attention to the binary, not just the hexadecimal. C
in hex is 12 in decimal is 1100
in binary; F is 1111
in binary. So
F & 1 == 1, F & 2 == 2, C & F == C, 0 & 0 == 0.
Solution 3
0xF
is all 1
bits: 1111
. The bitwise AND of any value and 0xF
is the same value. In other words, an AND with 0xF
in a given position preserves the bits from the other operand.
So in 0x0000FFC0 & 0x000012F0
, the F
digits are preserving the corresponding digits:
0xF & 0x1 = 0x1
0xF & 0x2 = 0x2
0xC & 0xF = 0xC
Similarly:
0xFFC0 & 0x12F0 = 0x12C0
As you probably know, the bitwise AND of any value and 0
is 0
. So constructs like 0x0000FF00
are used to preserve only the 2nd least significant byte.
The usage of 0xFFC0
really depends on how the rest of the code is using that least significant byte. In the explanation of the flags, the top half of the least significant byte is documented as: an entrance to a room. Somehow that's important in System.out.println(map[x][y] & $ROOM_ID);
.
0xC
is 1100
in binary, so by including 0xC0
in the 0xFFC0
mask, the code is also trying to preserve the two most significant bits of that least significant - rightmost - byte. Note that on those two bits you can encode exactly 4 values. This matches a game design with entrances possible on 4 compass points, if the game is 2D and each room is a square, Zork-style.
As for the rules for bitmasks, you could do worse that starting with the wiki article: it covers the basics pretty well. Ultimately this is all boolean algebra.
Admin
Updated on July 10, 2022Comments
-
Admin almost 2 years
Im working on a game that creates Procedurally generated dungeons, I found an example that uses bit masking to retrieve things like room number and type of door.
In the example he uses a bitmask to pull details from the integer for each tile. and the integer is broken down like this
0xLLSDRRET L - is the Level Number S - Denotes a special tile(Like Stairs) D - is if its a door, and what type(Door, Arch, Trapped) R - Room number E - Flags an entrance to a room T - Names the type of tile(Floor, Cooridor, Blocked)
In this He uses a bit mask to get, for instance, the room number like:
int[][] map = new int[40][40] int $ROOM_ID = 0x0000FF00; System.out.println(map[x][y] & $ROOM_ID);
Now with this if map[x][y] was for instance 0x00001200 the output would be 1200. This part of Masks I understand.
But in the source $ROOM_ID is ACTUALLY 0x0000FFC0 and I dont understand what the C does,because I tried diferent values and I cant seem to grab what the C does, for example
0x00001200 output-> 1200 0x00001210 output-> 1200 0x00001220 output-> 1200 0x00001230 output-> 1200 0x00001240 output-> 1240 0x00001250 output-> 1240 0x00001260 output-> 1240 0x00001270 output-> 1240 0x00001280 output-> 1280 0x00001290 output-> 1280 0x000012A0 output-> 1280 0x000012B0 output-> 1280 0x000012C0 output-> 12C0 0x000012D0 output-> 12C0 0x000012E0 output-> 12C0 0x000012F0 output-> 12C0
Can Someone with more knowledge of bitmasks explain why 0x0000FFC0 & 0x000012F0 = 12C0?