What are the rules for bitmasks? Like 0xFF vs. 0xFC

10,415

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.

Share:
10,415
Admin
Author by

Admin

Updated on July 10, 2022

Comments

  • Admin
    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?