Записи

    Рассмотренные в предыдущем разделе структуры предназначены для

    многобайтовых данных. Но в некоторых случаях требуется побитовое

    определение объектов данных. Для таких случаев в Макроассемблере

    имеется механизм описания данных, который называют записью

    (RECORD). Действие оператора RECORD аналогичны действиям операторов

    STRUC и MACRO. Оператор RECORD задает определенную конфигурацию

    данных. Присвоенное записи имя становится для ассемблера еще одним

    оператором. Вы можете использовать это имя записи для задания

    специальных конфигураций данных. От оператора STRUC оператор RECORD

    отличается тем, что он определяет объекты на уровне отдельных

    битов. Каждому из полей оператор RECORD присваивает имя и указывает

    его ширину в битах. Оператор RECORD можно использовать для

    формирования битовых полей длиной до 16 бит.

 

      Здесь мы опять воспользуемся примером. На Фиг. 6.15 приведена

    еще одна ничего не делающая программа, связанная с установоением

    даты изменения файла. В определении Блока управления файлом

    содержится 16-битовое поле, в котором содержится дата формирования

    или последнего изменения данного файла операционной системой. При

    открыти файла DOS заполняет это поле в блоке FCB на основе

    информации из каталога дискеты. В 16-ти батих поля даты

    закодированы год, месяц и число. Из приведенного на Фиг. 6.15

    оператора RECORD видно строение этого слова данных.

            Microsoft (R) Macro Assembler Version 5.00              1/1/80 04:03:43

            Фиг. 6.15 Записи                                  Page         1-1

 

                                          PAGE    ,132

                                          TITLE   Фиг. 6.15 Записи

 

                                    DATE_WORD       RECORD  YEAR:7,MONTH:4,DAY:5

 

             0000                   STACK   SEGMENT STACK

             0000  0040[                        DW      64      DUP (?)

                   ????

                               ]

             0080                   STACK   ENDS

 

             0000                   CODE    SEGMENT

                                          ASSUME  CS:CODE

 

             0000                   FCB     LABEL   BYTE

             0000  01               DRIVE       DB      1         ; Номер устройства

             0001  46 49 47 36 2D 31 35     FILE_NAME       DB      'FIG6-15 '      ; Имя файла

                 20

             0009  41 53 4D               FILE_EXT          DB      'ASM'           ; Тип файла

             000C  0000             CURRENT_BLOCK   DW      0           ; Номер текущего блока

             000E  0080            RECORD_SIZE     DW      80H         ; Логический размер записи

             0010  00000000               FILE_SIZE       DD      0           ; Размер файла в байтах

             0014  0000             DATE        DATE_WORD       <>      ; Дата последнего изменения

             0016  000A[                  RESERVED          DB      10 DUP (?)      ; Зарезервировано ДОС

                    ??

                               ]

             0020  00               SEQ_NUMBER      DB      0           ; Номер текущей записи

             0021  00000000               RANDOM_NUMBER   DD      0           ; Номер записи при прямом

 

             0025                   RECORDS           PROC    FAR

             0025  1E                     PUSH    DS              ; Адрес возврата

             0026  B8 0000                      MOV     AX,0

             0029  50                     PUSH    AX

             002A  0E                     PUSH    CS              ; Установка DS на сегмент CODE

             002B  1F                     POP     DS

                                          ASSUME  DS:CODE

             002C  8D 16 0000 R                 LEA     DX,FCB               ; Открытие файла

             0030  B4 0F                        MOV     AH,0FH

             0032  CD 21                        INT     21H

 

             0034  A1 0014 R                    MOV     AX,DATE

             0037  25 FE00                      AND     AX,MASK YEAR         ; Выделение года

             003A  B1 09                        MOV     CL,YEAR              ; Сдвиг вправо

             003C  D3 E8                        SHR     AX,CL

             003E  8A F8                        MOV     BH,AL                ; Сохранение года в регистре BH

 

             0040  A1 0014 R                    MOV     AX,DATE

             0043  25 01E0                      AND     AX,MASK MONTH

             0046  B1 05                        MOV     CL,MONTH

             0048  D3 E8                        SHR     AX,CL

             004A  8A D8                        MOV     BL,AL                ; Сохранение месяца в регистре BL

 

                        Фиг. 6.15 Записи (начало)

             004C  A1 0014 R                    MOV     AX,DATE

             004F  25 001F                      AND     AX,MASK DAY          ; Сохранение дня в регистре AL

 

             0052  CB                     RET

             0053                   RECORDS           ENDP

             0053                   CODE    ENDS

                                          END     RECORDS

 

                        Фиг. 6.15 Записи (продолжение)

 

      В примере на Фиг. 6.15 записи присваивается имя DATE_WORD. В

    поле операндов оператора RECORD видно, что в записи DATE_WORD

    имеется три поля. Первые 7 бит содержат год (YEAR), следующие 4

    бита - месяц (MONTH) и последние 5 бит - число (DAY). Так же, как и

    в случае оператора MACRO, данная исходная программа просто

    определяет тип конкретной записи, названной DATE_WORD. Оператор

    RECORD не формирует соответствующих данных в памяти до тех пор,

    пока вы не используете имя записи в качестве ассемблерного

    оператора.

 

      В рассматриваемом примере запись DATE формируется в блоке FCB с

    использованием определения записи DATE_WORD. Метка DATE обозначает,

    а запись DATE_WORD формирует эту 16-битовую область данных. Как мы

    увидим ниже, существуют способы определения для полей записей

    значений по умолчанию и их изменения. В данном примере мы ничего не

    делали для изменения стандартных нулевых значений каждого из

    полей.

 

      Приведенная на Фиг. 6.15 программа иллюстрирует не только

    структуру оператора RECORD, но и некоторые операции, которые

    упрощаются благодаря этому оператору. В первой части программы

    открывается файл, имя которго указано в блоке FCB. Остальная часть

    программы извлекает из FCB информацию о дате и перебрасывает

    отдельные поля в регистры микропроцессора 8088.

 

      Во-первых, программа выделяет в дате из блока FCB значение

    года: переслав слово данных DATE в регистр AX, программа обнуляет в

    нем командой AND значения, относящиеся к месяцу и числу. Обратите

    здесь внимание на непосредственный операнд MASK YEAR. Так как YEAR

    - это поле записи, то оператор MASK возвращает значение, которое

    выделяет в данном слове поле YEAR. В данном случае это значение

    равно 0FE00H. Первые 7 бит в нем - единицы, а остальные биты

    обнулены. Это значение маски соответстувет битам, которые формируют

    значения YEAR в слове данных. В результате логического умножения

    данного значения и всей остальной записи остается только поле

    YEAR.

 

      Следующими командами программа перемещает поле YEAR в правый

    конец слова. Полю YEAR соответствует значение, равное сдвигу,

    который необходим для перемещения данного поля до правой границы

    слова. В данном случае это значение равно девяти. Сдвиг вправо на 9

    бит обращает значение года в число в регистре AL. (Следует помнить,

    что DOS кодирует год в виде числа в перделах от 0 до 119. Эти

    значения соответствуют годам от 1980 до 2099).

 

      В последующей группе команд программа выделяет из записи поле

    MONTH. Здесь также используются оператор MASK и значения сдвигов,

    из записи DATE_WORD. Подобным образом программа выбирает из записи

    значение поля DAY.

 

      В данном примере не выполняется никакой полезной работы, так

    как записанные в регистры значения оказываются потерянными при

    возвращении управления DOS. Однако вы можете запустить программу

    через отладчик и установить точку прерывания на команде возврата.

    Отладчик выводит на дисплей содержимое регистров BH, BL и AL, так

    что вы можете увидеть дату. Более практичная программа после

    считывания значений, относящихся к дате, преобразовывала бы их в

    код ASCII для вывода на экран. Либо вы могли бы оформить нашу

    программу как процедуру, которая вырабатывает информацию о дате для

    другой программу.

 

      Имеется еще несколько особенностей операции RECORD, которые

    следует рассмотреть. На Фиг. 6.15 приведен фрагмент таблицы

    символических имен из ассемблерного листинга. В этой таблице

    содержится информация, которая имеется у ассемблера о каждом из

    полей записи. В этой таблице нас будет интересовать второй ряд

    заголовков: "Shift Width Mask Initial" (Сдвиг Длина Маска Начальное

    значение"). Как видно из таблицы символических имен, запись

    DATE_WORD имеет длину 16 бит и состоит из трех полей. Каждое поле

    имеет четые атрибута. Значение сдвига равно числу битов в записи,

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

    насколько нужно сдвинуть данное поле, чтобы выровнять его на правый

    край. Значение маски служит для выделения поля в записи. Цифра 1 в

    слове маски показывает, что соответствующая позиция относится к

    данному полю.

 

      Ассемблер может оперировать значением длины любого поля записи.

    Вы можете задать длину поля при ассемблировании с помощью оператора

    WIDTH. Например, команда

 

      MOV AL,WIDTH YEAR

 

      в нашем примере помещает в регистр AL значение, равное семи.

 

      Столбец начальных значений в таблице символических имен

    показывает, какие значения ассемблер вставляет при формировании

    записи. Можно задавать записи с начальными значениями, отличными от

    нуля. Имеется также возможность изменить эти значения при генерации

    области данных. Для задания начальных значений вы после указания в

    операторе RECORD каждого из полей ставите знак равенства и

    соответствующее значение. Запись DATE_WORD с начальными значениями,

    соответствующими 1 января 1983 года, будет иметь вид:

 

      DATE_WORD RECORD YEAR:7=3, MONTH:4=1, DAY:5=1

 

      Эти значения можно переназаначить точно таким же способом, как

    и в случае структур. Когда вы формируете запись, в угловых скобках

    содержатся конкретные значения для данной генерации. Если нужно,

    чтобы дата была 5 января 1984 года, то вы можете сгенерировать

    запись следующим образом:

 

      DATE DATE_WORD <4,,5>

 

      Как и в случае макрокоманд или структур, параметры здесь

    являются позиционно-зависимыми. Так как параметр, соответствующий

    месяцу, не указан, то ассемблер воспользуется для него начальным

    значением, указанным в операторе RECORD.

 

      Обратите внимание, что программа на Фиг. 6.15 определяет блок

    FCB, не полбзуясь оператором STRUC, рассмотренным в предыдущем

    разделе. Мы не могли воспользоваться оператором STRUC, потому что

    данные каждого поля структуры должны определяться одним из

    операторов типа DEFINE. Мы не можем использовать имя записи в

    качестве одного из полей структуры. Тот способ, который мы

    применили на Фиг. 6.15, - один из позволяющих обойти эту

    трудность.

      Можно решить эту же задачу другим способом. Хотя ассемблер и не

    генерирует область данных, пока имя записи не использовано в

    программе в качестве оператора, но он хранит описания полей,

    указанных в операторе RECORD. Это позволяет определить в программе

    запись DATE_WORD, не используя ее для задания поля DATE структуры

    данных. Это все равно, что определить макрокоманду, но не вызывать

    ее. Остальная часть программы остается без изменений. Имена

    различных полей записи DATE_WORD имеют в программе смысл и могут

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

 

      То же самое верно и для оператора STRUC. Определение структуры

    задает для ассемблера значения смещений, даже если вы не вызываете

    затем эту структуру для формирования области данных. Вы можете

    воспользоваться этим для размещения блока FCB по умолчанию по

    адресу 05CH в префиксе программного сегмента. Так как блок FCB

    существует всегда, то нет необходимости использовать эту структуру

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

    на Фиг. 6.16 и 6.15, почти идентичны, за исключением некотрых

    деталей. В программе на Фиг. 6.16 блок FCB определяется не

    последовательностью команд типа DEFINE, а оператором STRUC.

    Обратите внимание, что при ассемблировании данной программы не

    называется ни запись DATE_WORD, ни структура FCB: они служат лишь

    для задания смещений в области данных.

            Microsoft (R) Macro Assembler Version 5.00              1/1/80 04:03:49

            Фиг. 6.16 Структуры и записи                      Page         1-1

 

                                          PAGE    ,132

                                          TITLE   Фиг. 6.16 Структуры и записи

 

                                    DATE_WORD       RECORD  YEAR:7,MONTH:4,DAY:5

 

                                    FCB     STRUC

             0000  00               DRIVE       DB      0         ; Номер устройства

             0001  20 20 20 20 20 20 20     FILE_NAME       DB      '        '      ; Имя файла

 

                        Фиг. 6.16 Структуры и записи (начало)

                 20

             0009  20 20 20               FILE_EXT          DB      '   '           ; Тип файла

             000C  0000             CURRENT_BLOCK   DW      0           ; Номер текущего блока

             000E  0080             RECORD_SIZE     DW      80H         ; Логический размер записи

             0010  00000000               FILE_SIZE       DD      0           ; Размер файла в байтах

             0014  0000             DATE        DW      0         ; Дата последнего изменения файла

             0016  000A[                  RESERVED          DB      10 DUP (?)      ; Зарезервировано ДОС

                    ??

                               ]

 

             0020  00               SEQ_NUMBER      DB      0           ; Номер записи в блоке

             0021  00000000               RANDOM_NUMBER   DD      0           ; Номер записи в файле

             0025                   FCB     ENDS

 

             0000                   STACK   SEGMENT STACK

             0000  0040[                        DW      64 DUP (?)

                   ????

                               ]

 

             0080                   STACK   ENDS

 

             0000                   CODE    SEGMENT

                                          ASSUME  CS:CODE

 

             0000                   RECORDS           PROC    FAR

             0000  1E                     PUSH    DS              ; Занесение в стек адреса возврата

             0001  B8 0000                      MOV     AX,0

             0004  50                     PUSH    AX

                                          ASSUME  DS:CODE               ; DS файтически указывает на PSP

             0005  BA 005C                      MOV     DX,05CH              ; Адрес FCB в PSP

             0008  B4 0F                        MOV     AH,0FH               ; Открыть файл

             000A  CD 21                        INT     21H

 

             000C  BB 005C                      MOV     BX,05CH              ; Адрес FCB

             000F  8B 47 14                     MOV     AX,[BX].DATE

             0012  25 FE00                      AND     AX,MASK YEAR         ; Выделение года из полной даты

             0015  B9 0009                      MOV     CX,YEAR              ; Выравнивание вправо

             0018  D3 E8                        SHR     AX,CL

             001A  8A F0                        MOV     DH,AL                ; Сохранение года в регистре DH

 

             001C  8B 47 14                     MOV     AX,[BX].DATE

             001F  25 01E0                      AND     AX,MASK MONTH

             0022  B9 0005                      MOV     CX,MONTH

             0025  D3 E8                        SHR     AX,CL

             0027  8A D0                        MOV     DL,AL                ; Сохранение месяца в регистре DL

 

             0029  8B 47 14                     MOV     AX,[BX].DATE

             002C  25 001F                      AND     AX,MASK DAY          ; Сохранение дня в регистре AL

 

             002F  CB                     RET

             0030                   RECORDS           ENDP

             0030                   CODE    ENDS

                                          END     RECORDS

 

                 Фиг. 6.16 Структуры и записи (продолжение)

      И последнее замечание об использовании записей и структур. Мы

    применяем эти средства, потому что они позволяют писать программы

    не вникая в конкретные детали структуры данных. Используя для

    описания данных оператор STRUC, мы можем рассматривать ссылки на

    каждое из полей как смещение внутри структуры данных. При этом

    программисту не нужно знать фактическое смещение этого поля. То же

    самое верно и для записи из битов. Если программа использует

    операторы MASK и сдвига, то распределение бит этого поля достаточно

    задать только в соответствующем операторе RECORD.

 

      Выигрыш от применения этих программных средств становится

    очевидным при разработке больших систем, с которыми связано большое

    число программистов или программ. В этом случае вы наверняка будуте

    модифицировать структуры данных по мере разработки программ. Если

    при написании программ пользоваться описанными структурными

    моделями, то изменить их и связанные с ними программы будет

    несложно: вы модифицируете определенную структуру данных, а затем

    вновь транслируете все программы с использовнием этой структуры.

    Сами же программы в изменениях не нуждаются. Можно поступить еще

    проще, если хранить структуру данных, как отдельный файл, который

    будет включаться в каждую ассемблируемую программу оператором

    INCLUDE: таким образом вы будете иметь только одну версию структуры

    данных. Описанные средства упрощают процесс создания большой

    программы со всеми изменениями, возникающими при ее разработке.

Hosted by uCoz