Copyright (C) А.Гавва V-0.4w май 2004

17. Низкоуровневые средства для системного программирования

Кроме богатого набора традиционных средств абстракции данных, Ада, в отличие от многих современных языков программирования, предоставляет ряд низкоуровневых средств, которые могут быть удобны при организации взаимодействия с используемым оборудованием или внешним программным обеспечением. Например, с помощью таких средств можно управлять требуемым двоичным представлением данных или размещением объектов в фиксированных адресах физической памяти.

Описанию средств системного программирования Ады посвящено приложение C (Annex C) стандарта Ada95. Кроме того, различные реализации компиляторов могут предусматривать дополнительные атрибуты типов и/или директивы компилятора которые управляют внутренним представлением объектов и поведением окружения времени выполнения.

Следует заметить, что многие зависящие от реализации системы константы располагаются в стандартном пакете System. Например, число битов в элементе памяти, число доступных элементов памяти, наибольшее и наименьшее доступное целое число, имя операционной системы и т.д.

17.1 Спецификация внутреннего представления данных

С помощью спецификации представления, для идентификаторов любого перечислимого типа можно указать специфические значения для внутреннего представления значений перечислимого типа. Предположим, что у нас есть перечислимый тип, который описывает страны:


type  Country is (USA, Russia, France, UK, Australia);

Мы можем, используя спецификацию представления, указать для каждого идентификатора этого перечислимого типа значение телефонного кода соответствующей страны следующим образом:


type  Country is (USA, Russia, France, UK, Australia);
for Country use (USA => 1, Russia => 7, France => 33, UK => 44, Australia => 61);

Таким образом, внутреннее значение используемое, например, для представления идентификатора France будет 33.

При спецификации внутреннего представления перечислимого типа, значения для внутреннего представления должны указываться в порядке увеличения значений и быть уникальны. Следует также обратить внимание на то, что атрибуты перечислимого типа будут возвращать значения которые не учитывают спецификацию внутреннего представления, например:

Country'Succ(USA)   возвратит: Russia
Country'Pred(Australia)   возвратит: UK
Country'Pos(Russia)   возвратит: 1
Country'Val(2)   возвратит: France

Для того, чтобы получить доступ к значениям внутреннего представления перечислимого типа необходимо воспользоваться стандартным настраиваемым модулем Ada.Unchecked_Convertion, который позволяет получить внутреннее представление объекта как значение другого типа, не выполняя при этом никаких промежуточных преобразований.

Следует заметить, что использование модуля настраиваемой функции Ada.Unchecked_Convertion обладает одним ограничением: объект-источник и объект-приемник должны иметь одинаковый битовый размер. Поэтому, чтобы гарантировать соответствие размеров объектов источника и приемника, необходимо указать компилятору размер внутреннего представления перечислимого типа Country. Например, можно указать, что размер внутреннего представления типа Country должен быть равен размеру типа Integer. Это можно выполнить следующим образом:


type  Country is (USA, Russia, France, UK, Australia);
for Country'Size use Integer'Size;
for Country use (USA => 1, Russia => 7, France => 33, UK => 44, Australia => 61);

Напомним, что атрибут T'Size возвращает размер экземпляра объекта типа T в битах.

Следующий пример простой программы, которая выводит телефонный код Франции, демонстрирует использование рассмотренных средств спецификации внутреннего представления:


with  Ada.Unchecked_Conversion;
with  Ada.Text_IO;              use   Ada.Text_IO;

procedure Main  is

    type  Country is (USA, Russia, France, UK, Australia);
    for Country'Size use Integer'Size;
    for Country use (USA       => 1,
                                   Russia    => 7,
                                   France    => 33,
                                   UK        => 44,
                                   Australia => 61);

    function International_Dialing_Code is
            new Ada.Unchecked_Conversion (Country, Integer);

begin
    Put ("International Dialing Code for France is ");
    Put ( Integer'Image(International_Dialing_Code (France)) );
    New_Line;
end Main;

17.2 Привязка объекта к фиксированному адресу памяти

В некоторых случаях может потребоваться выполнение чтения или записи по фиксированному абсолютному адресу памяти. Простым примером подобной ситуации может быть то, что операционная система MS-DOS хранит значение времени в фиксированных адресах памяти 46E и 46C (шестнадцатеричные значения). Более точная спецификация этих значений следующая:

046E - 046F  -  время дня в часах
046C - 046D  -  число отсчетов таймера с начала текущего часа
(один отсчет таймера равен 5/91 секунды)

Таким образом, для получения текущего времени необходимо осуществить привязку объекта к фиксированному адресу памяти. Для осуществления этого, можно привязать переменную Time_Hight типа Integer к фиксированному адресу 16#046E# следующим образом:


Time_Hight_Address  : constant  Address := To_Address (16#046E#);

type  Time  is range 0 .. 65365;
for Time'Size use 16;

Time_Hight  : Time;
for Time_Hight'Address use Time_Hight_Address;

Следует заметить, что здесь, тип Time является беззнаковым 16-битным целым. Величина адреса 16#046E# должна иметь тип Address, который описывается в пакете System. Стандартная функция To_Address, которая выполняет преобразование целочисленного значения в значение адреса, описывается в пакете System.Storage_Elements.

17.3 Организация доступа к индивидуальным битам

Организацию доступа к индивидуальным битам можно рассмотреть на примере операционной системы MS-DOS, в которой фиксированный адрес памяти 16#0417# содержит состояние установок клавиатуры. Вид физического представления этого байта следующий:

7 6 5 4 3 2 1 0
Insert Caps
Lock
Num
Lock
Scroll
Lock
       

Пример следующей простой программы демонстрирует организацию доступа к индивидуальным битам, характеризующим состояние клавиатуры:


with  Ada.Text_IO;              use   Ada.Text_IO;
with  System.Storage_Elements;  use   System.Storage_Elements;

procedure Keyboard_Status_Demo is

    Keyboard_Address  : constant Address := To_Address (16#0417#);

    type  Status  is  (Not_Active, Active);
    for   Status  use (Not_Active => 0, Active => 1);
    for   Status'Size  use 1;

    type  Keyboard_Status is
        record
            Scroll_Lock : Status;   -- состояние Scroll Lock
            Num_Lock    : Status;   -- состояние Num Lock
            Caps_Lock   : Status;   -- состояние Caps Lock
            Insert      : Status;   -- состояние Insert
        end record;
    for Keyboard_Status use
        record
            Scroll_Lock at 0 range 4..4;  -- бит 4
            Num_Lock    at 0 range 5..5;  -- бит 5
            Caps_Lock   at 0 range 6..6;  -- бит 6
            Insert      at 0 range 7..7;  -- бит 7
        end record;

    Keyboard_Status_Byte  : Keyboard_Status;
    for Keyboard_Status_Byte'Address use Keyboard_Address;

begin

    if  Keyboard_Status_Byte.Insert = Active  then
        Put_Line("Insert mode ON");
    else
        Put_Line("Insert mode OFF");
    end if;

    if  Keyboard_Status_Byte.Caps_Lock = Active  then
        Put_Line("Caps Lock mode ON");
    else
        Put_Line("Caps Lock mode OFF");
    end if;

    if  Keyboard_Status_Byte.Num_Lock = Active  then
        Put_Line("Num Lock mode ON");
    else
        Put_Line("Num Lock mode OFF");
    end if;

    if  Keyboard_Status_Byte.Scroll_Lock = Active  then
        Put_Line("Scroll Lock mode ON");
    else
        Put_Line("Scroll Lock mode OFF");
    end if;

end Keyboard_Status_Demo;

В данном примере, тип Status описан так, чтобы значения этого типа занимали ровно один бит. Далее, с используя тип Status, описывается тип записи Keyboard_Status, внутреннее представление которой соответствует физической структуре байта состояния клавиатуры.

Следует заметить, что спецификатор "Scroll_Lock at 0 range 4 .. 4" указывает, что объект Scroll_Lock должен быть размещен по нулевому смещению в четвертой битовой позиции записи Keyboard_Status (отсчет ведется в битах от начала записи).


Copyright (C) А.Гавва V-0.4w май 2004