вторник, 9 июня 2009 г.

Данные сложного типа. Записи.

Запись — структурный тип данных, состоящий из фиксированного числа элементов длиной от одного до нескольких бит.
При описании записи для каждого элемента указывается его длина в битах и, что необязательно, некоторое значение.
Суммарный размер записи определяется суммой размеров ее полей и не может быть более 8, 16 или 32 бит.
Если суммарный размер записи меньше указанных значений, то все поля записи “прижимаются” к младшим разрядам.
В отличие от структур, дающих имена байтам, словам, двойным словам или целым массивам, в записях определяются строки битов внутри байтов, слов или двойных слов.

Использование записей в программе, так же, как и структур, организуется в три этапа:
  1. Задание шаблона записи, то есть определение набора битовых полей, их длин и, при необходимости, инициализация полей.
  2. Определение экземпляра записи. Так же, как и для структур, этот этап подразумевает инициализацию конкретной переменной типом заранее определенной с помощью шаблона записи.
  3. Организация обращения к элементам записи.
Компилятор TASM, кроме стандартных средств обработки записей, поддерживает также и некоторые дополнительные возможности их обработки.

Описание записи

Описание шаблона записи имеет следующий синтаксис:

имя_записи RECORD <описание элементов>

Здесь:
<описание элементов> представляет собой последовательность описаний отдельных элементов записи согласно синтаксической диаграмме (см. рис.):

При описании шаблона память не выделяется, так как это всего лишь информация для транслятора ассемблера о структуре записи.
Пример:

iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00

Так же, как и для структур, местоположение шаблона в программе может быть любым, но при этом необходимо учитывать логику работы однопроходного транслятора.

Определение экземпляра записи

Для использования шаблона записи в программе необходимо определить переменную с типом данной записи, для чего применяется следующая синтаксическая конструкция (см. рис.):

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

Если инициализировать поля не требуется, то достаточно указать ? при определении экземпляра записи:

flag iotest ?

Если вы составите и исследуете в отладчике тестовый пример с данным определением записи, то увидите, что все поля переменной типа запись flag обнуляются. Это происходит несмотря на то, что в определении записи заданы начальные значения полей.

Если требуется частичная инициализация элементов, то они заключаются в угловые (< и >) или фигурные ({ и }) скобки.
Различие здесь в том, что в угловых скобках элементы должны быть заданы в том же порядке, что и в определении записи. Если значение некоторого элемента совпадает с начальным, то его можно не указывать, но обязательно обозначить его запятой. Для последних элементов идущие подряд запятые можно опустить.
К примеру, согласиться со значениями по умолчанию можно так:

flag iotest <> ;согласились со значением по умолчанию

Изменить значение поля i2 можно так:

flag iotest <,10,> ; переопределили i2

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

flag iotest {i2=10} ;переопределили i2,
;не обращая внимания на порядок
;следования других компонентов записи

Работа с записями
Как организовать работу с отдельными элементами записи? Обычные механизмы адресации здесь бессильны, так как они работают на уровне ячеек памяти, то есть байтов, а не отдельных битов. Здесь программисту нужно приложить некоторые усилия.
Прежде всего для понимания проблемы нужно усвоить несколько моментов:
  • Каждому имени элемента записи ассемблер присваивает числовое значение, равное количеству сдвигов вправо, которые нужно произвести для того, чтобы этот элемент оказался “прижатым” к началу ячейки. Это дает нам возможность локализовать его и работать с ним. Но для этого нужно знать длину элемента в битах.

    mov ah,i2 ;получаем количество сдвигов в право

  • Сдвиг вправо производится с помощью команды сдвига shr.
  • Ассемблер содержит оператор width, который позволяет узнать размер элемента записи в битах или полностью размер записи. Варианты применения оператора width:

    width имя_элемента_записи ;значением оператора
    ;будет размер элемента в битах

    width имя_экземпляра_записи
    или
    width имя_типа_записи ;значением оператора
    ;будет размер всей записи в битах.

    mov al,width i2
    ...
    mov ax,width iotest

  • Ассемблер содержит оператор mask, который позволяет локализовать биты нужного элемента записи. Эта локализация производится путем создания маски, размер которой совпадает с размером записи. В этой маске обнулены биты на всех позициях, за исключением тех, которые занимает элемент в записи.
  • Сами действия по преобразованию элементов записи производятся с помощью логических команд.
И так, поскольку в системе команд МП 86 практически нет средств работы с битовыми полями, поэтому непосредственно обратиться к элементу записи невозможно. Чтобы произвести обработку интересующего нас элемента, его нужно сначала выделить, сдвинуть, при необходимости, к младшим разрядам, выполнить необходимые действия и поместить его обратно на свое место в записи. Это приходится осуществлять с помощью команд сдвигов и логических операций - and, or, xor и not.

Выделение элемента записи:
  • Поместить запись во временную память — регистр (8, 16 или 32-битный в зависимости от размера записи).
  • Получить битовую маску, соответствующую элементу записи, с помощью оператора mask.
  • Локализовать биты в регистре с помощью маски и команды and.
  • Сдвинуть биты элемента к младшим разрядам регистра командой shr. Число разрядов для сдвига получить с использованием имени элемента записи.
В результате этих действий элемент записи будет локализован в начале рабочего регистра и далее с ним можно производить любые действия.

Работа с элементом записи:

Как мы уже выяснили, с элементами записи производятся любые действия, как над обычной двоичной информацией.
Единственное, что нужно отслеживать, — это размер битового поля. Если, к примеру, размер поля увеличится, то впоследствии может произойти случайное изменение соседних полей битов. Поэтому желательно исключить изменение размера поля.

Помещение измененного элемента на его место в запись:
  • Используя имя элемента записи в качестве счетчика сдвигов, сдвинуть влево биты элемента записи (команда shl).
  • Если вы не уверены в том, что разрядность результата преобразований не превысила исходную, можно выполнить “обрезание” лишних битов, используя команду and и маску элемента.
  • Подготовить исходную запись к вставке измененного элемента путем обнуления битов в записи на месте этого элемента. Это можно сделать путем наложения командой and инвертированной маски элемента записи на исходную запись.
  • С помощью команды or наложить значение в регистре на исходную запись.
Ну и примерчик для медитации...
Програмулина ни чего не выводит на экран, так как предназначена для медитации на нее под отладчиком. В программе создается экземпляр записи iotest с именем flag, имеющий двоичное значение (по умолчанию) - 10001100 (h). Программа устанавливает третий слева бит (младший бит i2) в единичку. Соответственно, после этих изменений, в переменной flag должно быть значение ACh (10101100).

Теперь листинг программы...

И немного отладчика...

Этот скрин сделан после выполнения первых двух команд программы. Следующий уже после выполнения команды or.

Комментариев нет: