Wednesday, September 26, 2012

AdaTutor - Advanced Topics (6)

Representation Clauses and System

Ada normally represents an enumeration type internally with successive integers starting at zero.  For example, if we write

type Command is (Left, Right, Forward, Back);
the compiler will normally represent Left with 0, Right with 1, etc.  Usually this doesn't concern the programmer.  However, after the above declaration, we can specify the internal representation with a representation clause like this:
for Command use (Left => 1, Right => 2, Forward => 4, Back => 8);
We might want to do that if, for example, we're sending a value of type Command to some hardware which will interpret the bit patterns.  The values must be assigned in increasing order with no duplications, but gaps are permitted.  The attributes 'Succ, 'Pred, 'Pos, and 'Val are not affected. Thus Command'Pos(Back) is still 3.

Ada lets us specify the Size, in bits, of the objects of a given type:

   type Num is range 0 .. 100;
   for Num'Size use 8;
Similarly, we can specify the attribute 'Small for a fixed point type:
   type Voltage is delta 0.01 range -20.0 .. 20.0;
   for Voltage'Small use 1.0/128.0;
These attributes can also be read:
   I : Integer := Num'Size;
   F : Float := Voltage'Small;

Before discussing the remaining types of representation clauses, we must briefly mention the package System that comes with Ada.  System contains implementation dependent specifications.

A brief outline of package System is in section 13.7 of the Ada 95 RM.  However, the full package specification should appear in the documentation that came with your compiler.  Of interest here is the type Address.  In our examples, we'll assume that System.Address is some integer type.  (On some PC implementations of Ada, type System.Address is a bit more complicated.)

We can specify the absolute address of a variable, a constant, a task entry, a procedure, or a package, with a constant of type System.Address.  In Ada 83 the program must with System.  This is useful for memory-mapped I/O, interrupt handlers, etc.  For example:

   Modem_Control : Integer;
   for Modem_Control'Address use 16#7C00#;
   task Interrupt_Handler is
      entry Clock_Interrupt;
      for Clock_Interrupt'Address use 16#100#;
   end Interrupt_Handler;
   procedure Keystroke;
   for Keystroke'Address use 16#200#;

In Ada 83, the syntax is for Keystroke use 16#200#, and Ada 95 accepts this older syntax for compatibility.

Finally, we can specify how records are stored.  This example forces A and B to be stored in bits 0 .. 3 and 4 .. 7 of byte 0 of the record, and C and D to be packed into byte 1.  The optional clause for Packed'Alignment use 2; specifies that all records of type Packed will begin at even addresses:

   type Nibble is range 0 .. 15;
   type Packed is record
       A, B, C, D : Nibble;
   end record;
   for Packed'Alignment use 2;
   for Packed use record
      A at 0 range 0 .. 3;
      B at 0 range 4 .. 7;
      C at 1 range 0 .. 3;
      D at 1 range 4 .. 7;
   end record;

In Ada 83, we use the clause record at mod 2; after for Packed use, rather than saying for Packed'Alignment use 2; before for Packed use:

   type Nibble is range 0 .. 15;
   type Packed is record
      A, B, C, D : Nibble;
   end record;
   for Packed use record at mod 2;
      A at 0 range 0 .. 3;
      B at 0 range 4 .. 7;
      C at 1 range 0 .. 3;
      D at 1 range 4 .. 7;
   end record;

Again, Ada 95 accepts this older syntax for compatibility.

An implementation of Ada need not accept most representation clauses to meet the standard.  If any clause is rejected, an error message will be displayed.


   type Answer is (Yes, No, Maybe);
   for Answer use (Yes => 1, No => 2, Maybe => 4);
What is Answer'Val(20)?
  1. Answer'Val(2) is No.
  2. Answer'Val(2) is Maybe.

< prev   next >

No comments: