Useful macros | |
Bitarray definition and declaration | Group of macros to declare and/or define bitarrays. |
CPCT_1BITARRAY | Define or declare arrays with 1-bit elements |
CPCT_2BITARRAY | Define or declare arrays with 2-bits elements |
CPCT_4BITARRAY | Define or declare arrays with 4-bits elements |
CPCT_6BITARRAY | Define or declare arrays with 6-bits elements |
Bitarray element encoding | Macros that help initializing, populating and inserting elements manually into different types of bitarrays |
CPCT_ENCODE6BITS | Encodes 4 6-bits elements into 3 bytes for a CPCT_6BITARRAY |
CPCT_ENCODE4BITS | Encodes 2 4-bits elements into 1 byte for a CPCT_4BITARRAY |
CPCT_ENCODE2BITS | Encodes 4 2-bits elements into 1 byte for a CPCT_2BITARRAY |
Define or declare arrays with 6-bits elements
Name | C identifier for the bitarray. |
Elems | Number of elements the array will have (total items) |
#define CPCT_6BITARRAY (Name, Elems)
#define CPCT_4BITARRAY (Name, Elems)
#define CPCT_2BITARRAY (Name, Elems)
#define CPCT_1BITARRAY (Name, Elems)
These macros make more comfortable the process of declaring and defining bitarrays. Any bitarray is a normal array of bytes in the end. However, it is usually accessed through CPCtelera’s bitarray function to manage elements smaller than 1 byte.
When a bitarray is defined, a calculation has to be made to know how many bytes are required to store the total amount of X-bits elements it will contain. For instance, to store 8 2-bits elements, 16 bits are needed, so 2 bytes needs to be allocated. Therefore, in the next code segment, both sentences are equivalent:
CPCT_2BITSARRAY(my_array, 8); // This allocates 2 bytes (8 2-bits elements) u8 my_array[2]; // This does exactly the same.
These 4 macros can be used either for defining and for declaring bitarrays. This example shows how to define and declare different bitarrays:
// Declaring an array (without defining it) // This one is a 40 bits array (5 bytes, 10 4-bits elements) extern CPCT_4BITSARRAY(an_array, 10); // Define and populate a constant array // This other is a 16-bits array (2 bytes, 8 2-bits elements) const CPCT_2BITSARRAY(other_array, 8) = { 0b00011011, 0b11100100 // Populate with 8 elements (0,1,2,3,3,2,1,0) };
Encodes 4 6-bits elements into 3 bytes for a CPCT_6BITARRAY
Encodes 2 4-bits elements into 1 byte for a CPCT_4BITARRAY
Encodes 4 2-bits elements into 1 byte for a CPCT_2BITARRAY
#define CPCT_ENCODE6BITS(A, B, C, D)
#define CPCT_ENCODE4BITS(A, B)
#define CPCT_ENCODE2BITS(A, B, C, D)
A-D | Individual elements to be inserted in the bitarray |
CPCT_ENCODE6BITS | Each value must be in the range [0, 63] |
CPCT_ENCODE4BITS | Each value must be in the range [0, 15] |
CPCT_ENCODE2BITS | Each value must be in the range [0, 3] |
These macros help in the initialization of bitarray elements mainly, and can also help on their population and modification. They get individual elements (one number for each element) and pack them together, putting each element into its corresponding bits.
Let us understand this better with an example. Suppose we wanted to initialize a CPCT_4BITARRAY with these elements: 5, 10, 15, 1. We could do it manually this way:
// Define and populate a new bitarray with 4-bits elements CPCT_4BITARRAY(mybitarray, 4) = { 0x5A, // First 2 elements of the bitarray, 5 and 10, 4-bits each, hexadecimally codified 0xF1 // Next 2 elements of the bitarray, 15 and 1, 4-bits each, hexadecimally codified };
In this case, we know that each hexadecimal digit corresponds to 4-bits and use this to define 4 elements in 2 bytes, being each one a hexadecimal digit. We can do exactly the same, easily, using CPCT_ENCODE4BITS macro:
// Define and populate a new bitarray with 4-bits elements CPCT_4BITARRAY(mybitarray, 4) = { CPCT_ENCODE4BITS( 5, 10), // Add elements 5 and 10 CPCT_ENCODE4BITS(15, 1) // Add elements 15 and 1 };
This code does the same as previous one, but its easier to do. We use CPCT_ENCODE4BITS macro and directly write desired values in order, as in a conventional array definition. This let us forget about internal codification and focus on our data.
This example is from a platform game, where user controls a running man that has to jump over metal spikes. The game needs an array defining the layout of the floor, having 4 types of floor block: (0) normal floor, (1) decelerator floor, (2) jump-booster floor, (3) spikes. This is the definition of the first level:
// Define level 1 layout. It starts with normal floor, going then through // some easy jumps and ending with a decelerator floor previous to a // jump-booster which is required for the final jump. CPCT_2BITARRAY(level_1, 24) = { CPCT_ENCODE2BITS(0, 0, 0, 0) // Start with normal floor , CPCT_ENCODE2BITS(3, 0, 0, 0) // Some easy jumps (spike and then 3 floors) , CPCT_ENCODE2BITS(3, 0, 0, 0) , CPCT_ENCODE2BITS(1, 1, 2, 2) // Decelerator floor, followed by 2 jump-boosters , CPCT_ENCODE2BITS(3, 3, 3, 3) // Big spike hole , CPCT_ENCODE2BITS(0, 0, 0, 0) // Normal floor at the end of the level }
The same operation this code does (defining and populating an array), could have been manually done this way:
// Defining and populating level 1, using 2 bits for each element // (4 elements per byte, so 6 bytes required, as 6 x 4 = 24). We // use binary numbers for clarity (2bits per element), and include hexadecimal // and decimal for comparison purposes only. level_1[6] = { 0b00000000, 0b11000000, 0b11000000, 0b01011010, 0b11111111, 0b00000000 }; // = { 0x00, 0xC0, 0xC0, 0x5A, 0xFF, 0x00 }; Same in hexadecimal // = { 0, 192, 192, 80, 255, 0 }; Same in decimal
Another example could be the definition of a height map for a 2D lateral game filled with mountains. Each element of the next array will refer to the height of the floor at a given location. Thinking of a floor that will never be taller than 63 pixels, we can use a 6-bits array to define the layout of the map. This would be a map for the second level of this game:
// Define the height map for the level 2 that # < 28 // will be a great mountain like this one >> ### < 24 CPCT_6BITARRAY(heightmap_l2, 12) = { // ### < 20 CPCT_ENCODE6BITS( 0, 4, 8, 16) // ###### < 16 , CPCT_ENCODE6BITS(24, 28, 24, 16) // ###### < 12 , CPCT_ENCODE6BITS(16, 4, 0, 0) // ####### < 8 }; // _#########__ < 4