Next: Pragma Pack for Arrays, Previous: Bit_Order Clauses, Up: Representation Clauses and Pragmas [Contents][Index]
In this section we will review the effect of the Bit_Order attribute definition clause on byte ordering. Briefly, it has no effect at all, but a detailed example will be helpful. Before giving this example, let us review the precise definition of the effect of defining Bit_Order. The effect of a non-standard bit order is described in section 13.5.3 of the Ada Reference Manual:
"2 A bit ordering is a method of interpreting the meaning of the storage place attributes."
To understand the precise definition of storage place attributes in this context, we visit section 13.5.1 of the manual:
"13 A record_representation_clause (without the mod_clause) specifies the layout. The storage place attributes (see 13.5.2) are taken from the values of the position, first_bit, and last_bit expressions after normalizing those values so that first_bit is less than Storage_Unit."
The critical point here is that storage places are taken from the values after normalization, not before. So the Bit_Order interpretation applies to normalized values. The interpretation is described in the later part of the 13.5.3 paragraph:
"2 A bit ordering is a method of interpreting the meaning of the storage place attributes. High_Order_First (known in the vernacular as ’big endian’) means that the first bit of a storage element (bit 0) is the most significant bit (interpreting the sequence of bits that represent a component as an unsigned integer value). Low_Order_First (known in the vernacular as ’little endian’) means the opposite: the first bit is the least significant."
Note that the numbering is with respect to the bits of a storage unit. In other words, the specification affects only the numbering of bits within a single storage unit.
We can make the effect clearer by giving an example.
Suppose that we have an external device which presents two bytes, the first byte presented, which is the first (low addressed byte) of the two byte record is called Master, and the second byte is called Slave.
The left most (most significant bit is called Control for each byte, and the remaining 7 bits are called V1, V2, ... V7, where V7 is the rightmost (least significant) bit.
On a big-endian machine, we can write the following representation clause
type Data is record Master_Control : Bit; Master_V1 : Bit; Master_V2 : Bit; Master_V3 : Bit; Master_V4 : Bit; Master_V5 : Bit; Master_V6 : Bit; Master_V7 : Bit; Slave_Control : Bit; Slave_V1 : Bit; Slave_V2 : Bit; Slave_V3 : Bit; Slave_V4 : Bit; Slave_V5 : Bit; Slave_V6 : Bit; Slave_V7 : Bit; end record; for Data use record Master_Control at 0 range 0 .. 0; Master_V1 at 0 range 1 .. 1; Master_V2 at 0 range 2 .. 2; Master_V3 at 0 range 3 .. 3; Master_V4 at 0 range 4 .. 4; Master_V5 at 0 range 5 .. 5; Master_V6 at 0 range 6 .. 6; Master_V7 at 0 range 7 .. 7; Slave_Control at 1 range 0 .. 0; Slave_V1 at 1 range 1 .. 1; Slave_V2 at 1 range 2 .. 2; Slave_V3 at 1 range 3 .. 3; Slave_V4 at 1 range 4 .. 4; Slave_V5 at 1 range 5 .. 5; Slave_V6 at 1 range 6 .. 6; Slave_V7 at 1 range 7 .. 7; end record;
Now if we move this to a little endian machine, then the bit ordering within the byte is backwards, so we have to rewrite the record rep clause as:
for Data use record Master_Control at 0 range 7 .. 7; Master_V1 at 0 range 6 .. 6; Master_V2 at 0 range 5 .. 5; Master_V3 at 0 range 4 .. 4; Master_V4 at 0 range 3 .. 3; Master_V5 at 0 range 2 .. 2; Master_V6 at 0 range 1 .. 1; Master_V7 at 0 range 0 .. 0; Slave_Control at 1 range 7 .. 7; Slave_V1 at 1 range 6 .. 6; Slave_V2 at 1 range 5 .. 5; Slave_V3 at 1 range 4 .. 4; Slave_V4 at 1 range 3 .. 3; Slave_V5 at 1 range 2 .. 2; Slave_V6 at 1 range 1 .. 1; Slave_V7 at 1 range 0 .. 0; end record;
It is a nuisance to have to rewrite the clause, especially if the code has to be maintained on both machines. However, this is a case that we can handle with the Bit_Order attribute if it is implemented. Note that the implementation is not required on byte addressed machines, but it is indeed implemented in GNAT. This means that we can simply use the first record clause, together with the declaration
for Data'Bit_Order use High_Order_First;
and the effect is what is desired, namely the layout is exactly the same, independent of whether the code is compiled on a big-endian or little-endian machine.
The important point to understand is that byte ordering is not affected. A Bit_Order attribute definition never affects which byte a field ends up in, only where it ends up in that byte. To make this clear, let us rewrite the record rep clause of the previous example as:
for Data'Bit_Order use High_Order_First; for Data use record Master_Control at 0 range 0 .. 0; Master_V1 at 0 range 1 .. 1; Master_V2 at 0 range 2 .. 2; Master_V3 at 0 range 3 .. 3; Master_V4 at 0 range 4 .. 4; Master_V5 at 0 range 5 .. 5; Master_V6 at 0 range 6 .. 6; Master_V7 at 0 range 7 .. 7; Slave_Control at 0 range 8 .. 8; Slave_V1 at 0 range 9 .. 9; Slave_V2 at 0 range 10 .. 10; Slave_V3 at 0 range 11 .. 11; Slave_V4 at 0 range 12 .. 12; Slave_V5 at 0 range 13 .. 13; Slave_V6 at 0 range 14 .. 14; Slave_V7 at 0 range 15 .. 15; end record;
This is exactly equivalent to saying (a repeat of the first example):
for Data'Bit_Order use High_Order_First; for Data use record Master_Control at 0 range 0 .. 0; Master_V1 at 0 range 1 .. 1; Master_V2 at 0 range 2 .. 2; Master_V3 at 0 range 3 .. 3; Master_V4 at 0 range 4 .. 4; Master_V5 at 0 range 5 .. 5; Master_V6 at 0 range 6 .. 6; Master_V7 at 0 range 7 .. 7; Slave_Control at 1 range 0 .. 0; Slave_V1 at 1 range 1 .. 1; Slave_V2 at 1 range 2 .. 2; Slave_V3 at 1 range 3 .. 3; Slave_V4 at 1 range 4 .. 4; Slave_V5 at 1 range 5 .. 5; Slave_V6 at 1 range 6 .. 6; Slave_V7 at 1 range 7 .. 7; end record;
Why are they equivalent? Well take a specific field, the Slave_V2 field. The storage place attributes are obtained by normalizing the values given so that the First_Bit value is less than 8. After normalizing the values (0,10,10) we get (1,2,2) which is exactly what we specified in the other case.
Now one might expect that the Bit_Order attribute might affect bit numbering within the entire record component (two bytes in this case, thus affecting which byte fields end up in), but that is not the way this feature is defined, it only affects numbering of bits, not which byte they end up in.
Consequently it never makes sense to specify a starting bit number greater than 7 (for a byte addressable field) if an attribute definition for Bit_Order has been given, and indeed it may be actively confusing to specify such a value, so the compiler generates a warning for such usage.
If you do need to control byte ordering then appropriate conditional values must be used. If in our example, the slave byte came first on some machines we might write:
Master_Byte_First constant Boolean := ...; Master_Byte : constant Natural := 1 - Boolean'Pos (Master_Byte_First); Slave_Byte : constant Natural := Boolean'Pos (Master_Byte_First); for Data'Bit_Order use High_Order_First; for Data use record Master_Control at Master_Byte range 0 .. 0; Master_V1 at Master_Byte range 1 .. 1; Master_V2 at Master_Byte range 2 .. 2; Master_V3 at Master_Byte range 3 .. 3; Master_V4 at Master_Byte range 4 .. 4; Master_V5 at Master_Byte range 5 .. 5; Master_V6 at Master_Byte range 6 .. 6; Master_V7 at Master_Byte range 7 .. 7; Slave_Control at Slave_Byte range 0 .. 0; Slave_V1 at Slave_Byte range 1 .. 1; Slave_V2 at Slave_Byte range 2 .. 2; Slave_V3 at Slave_Byte range 3 .. 3; Slave_V4 at Slave_Byte range 4 .. 4; Slave_V5 at Slave_Byte range 5 .. 5; Slave_V6 at Slave_Byte range 6 .. 6; Slave_V7 at Slave_Byte range 7 .. 7; end record;
Now to switch between machines, all that is necessary is to set the boolean constant Master_Byte_First in an appropriate manner.
Next: Pragma Pack for Arrays, Previous: Bit_Order Clauses, Up: Representation Clauses and Pragmas [Contents][Index]