Использование самостоятельно описываемых типов
Параметры вывода позволяют использовать не только предопределенные типы.
Они также позволяют использовать типы которые самостоятельно описаны программистом.
В качестве примера, рассмотрим ситуацию в которой необходимо прочитать содержимое регистра флагов процессора и проверить состояние флага переноса (carry flag).
Известно, что флаг переноса находится в бите 0 регистра флагов процессора.
Следовательно, можно использовать соответствующую битовую маску для проверки его состояния:
if (Eax and 16#00000001#) = 1 then
Put_Line ("Carry flag set"); end if; |
Однако, может возникнуть необходимость в установке или чтении других битов.
В таком случае, для облегчения доступа к индивидуальным битам, может оказаться более предпочтительным применение соответствующего типа данных.
Примером, который демонстрирует такой подход, может служить следующая программа:
with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code; with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;
procedure Bits is subtype Num_Bits is Natural range 0 .. 31; type Flags_Register is array (Num_Bits) of Boolean; pragma Pack (Flags_Register); for Flags_Register'Size use 32; Carry_Flag : constant Num_Bits := 0; Register : Flags_Register; begin Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "pop %0", -- загрузить флаги из стека в переменную Register Outputs => Flags_Register'Asm_Output ("=g", Register)); if Register(Carry_Flag) = True then Put_Line ("Carry flag set"); end if; end Bits; |
Следует заметить, что в подобных случаях выбор варианта решения больше зависит от требований, которые предъявляются к уровню абстракции в программе.
При использовании полной оптимизации (-O2), генерируемый компилятором код ассемблера будет идентичен для обоих вариантов:
#APP pushfl pop %eax #NO_APP testb $1,%al |
Очевидно, что компилятор использует инструкции ассемблера для непосредственной проверки битов которые нас интересуют.