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
LockNum
LockScroll
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