Что такое offset в ассемблере
MS-DOS и TASM 2.0. Часть 9. Указатель.
Понятие указателя.
Понятие указателя (pointer) — одно из самых важных в программировании. Для начинающего программиста просто необходимо его понять и усвоить. Попробуем разобраться, что это такое и зачем он нужен.
Процессор работает с памятью. Минимально возможное значение памяти — 1 бит (bit). Память воспринимается машиной блочно. Один минимальный блок равен одному байту (byte), 1 байт = 8 бит (bit — минимальная адресуемая единица памяти).
Чтобы произвести манипуляции даже с битом, мы должны поместить его значение в байтный блок. Так как всего в байте восемь бит, в байтном блоке может содержаться до 256 (2 в степени 8) разных значений(DEC: от 0 до 255, HEX: от 0 до FFh). Побайтные блоки идут друг за другом. Каждый байт условно пронумерован (от нуля и далее…). Порядковый номер блока называется смещением (offset) или указателем (pointer).
Hiew в помощь.
Запускаем Hiew. Открываем текстовый файл D:\WORK\PTR\ptr-1.txt.
Открываем текстовый файл с помощью HEIW.
Текстовый файл содержит только символы ASCII.
Переходим в 16-тиричный (HEX) режим просмотра (Enter или F4->HEX). Перед нами удобное отображение документа в 16-тиричном виде. Каждая строка содержит по 16 (10h) байт. В левой стороне для удобство ориентации строки пронумерованы (нумерация, естественно также в 16 битном режиме). В средней колонке мы видим код в виде цифр — так, как его видит компьютер (для удобства просмотра через каждые четыре байта проставлен дефис — это только для нас).
Текстовый документ в HEX виде.
В крайней правой колонке отображается те же значения, но в виде ASCII символов. Таблица символов ASCII — это перечень соответствия цифр определённым буквам и символам (смотри таблицу символов ASCII) с помощью которых информация транслируется машиной в более понятный для человека вид. Таблица символов ASCII (American Standard Code for Information Interchange — американский стандартный код для обмена информацией) принята согласно достигнутым на международном уровне соглашениям. Наша любимая среда разработки DOS Tasm Editor (TASMED ) имеет ряд удобных подсказок, облегчающих написание и изучение программ, в частности встроенную таблицу ASCII.
American Standard Code for Information Interchange (ASCII).
Например, по смещению 0000005Dh располагается число 66h, которому согласно таблице символов ASCII, соответствует латинская буква «f».
Значению 66h соответствует буква f в соответствии с кодировкой ASCII.
При передвижении указателя посредством клавиш влево-вправо, вверх-вниз в верхней части окна мы заметим изменения показателя смещения (offset), на которое указывает курсор. Так вот, значение этого смещения и есть указатель (pointer) на байтные блоки.
Подсказки, вшитые в среду разработки Tasm Editor (TASMED ).
Указатель и смещение — pointer, offset, address.
Разница между смещением (offset) и указателем (pointer) практически нет. Подразумевается, что смещение (offset) всегда относительно чего-то. В нашем случае — от начала файла. Слово «указатель» (pointer) не несёт в себе обременения относительности чего-то, но фактически является синонимом понятия offset.
Указатель, офсет, смещение — pointer, offset, address.
При запуске программа отображается в память. Отображается определённым способом, согласно данным заголовка исполняемого файла или типа файла (*.com). Указатель (pointer) в данном случае будет отсчитываться от начала выделенной для программы памяти. Иногда его называют «эффективный адрес» (Effective Address). Таким образом, указатель на один и тот же блок кода в памяти и на жёстком диске исполняемого файла не будут совпадать.
А теперь внимательно проанализируйте следующие высказывания (все они истинны):
Синонимы слову «указатель», с которыми вы столкнётесь:
Необходимо помнить.
Необходимо отметить, что в языке программирования Си и С++ понятие указателя несколько конкретизировано и более узко определено, чем в ассемблере: указатель – переменная, значением которой является адрес ячейки памяти. То есть, указатель — это тип переменной, которая хранит значение, а значение — адрес ячейки памяти. В дальнейшем мы будем говорить об указателе в широком смысле — более понятном и типичном для ассемблера, как в данной статье.
Общее понятие указателя вами освоено. В последствии мы разберёмся более детально, но всему своё время…
Добавить комментарий Отменить ответ
Для отправки комментария вам необходимо авторизоваться.
MS-DOS и TASM 2.0. Часть 18. Ещё раз об указателе.
Указатель в программировании.
В статье MS-DOS и TASM 2.0. Часть 9. Указатель просто и понятно было рассмотрено, что такое указатель в программировании (pointer). Сейчас мы перейдём к вопросу практического использования указателя. Ещё раз напомним, что указатель в ассемблере — более широкое понятие, чем в Си и С++, где указатель определён как переменная, значением которой является адрес ячейки памяти. Указатель — не только переменная. Указатель в программировании на ассемблере — адрес определённой ячейки памяти. Жёсткой привязки к понятию «переменной» нет.
Преимущество указателя — простая возможность обращаться к определённой части исполняемого кода либо данных, избегая их дублирования. Например, один раз написав код функции, мы можем обращаться к нему неоднократно, осуществляя вызов указанной функции. Кстати, вызов функции — это переход исполнения кода по указателю, который для удобства «обозвали» понятным для человека названием (ну, например, «MyBestFunc»).
Указатель в программировании используется также для получения и передачи входных-выходных значений функций. С этим применением мы встретимся при Windows программировании.
Указатель — адрес ячейки памяти.
Указатель в программировании на ассемблере — адрес ячейки памяти, содержащей определённые последовательности цифр — блоки кода и данных. Блоки кода и данных называются значениями указателя, например: строка, массив, структура, функция, переменная, константа.
Указатель — адрес ячейки памяти, содержащей блоки кода и данных.
Указателю можно присвоить условное обозначение (const_a, const_b, my_mass_1, MY_STRUCT_1, BitMask, my_prnt_func), определив тип данных или кода, на которые он указывает (db, dd, STRUC, RECORD, proc). Практически мы уже проделывали эти операции, создавая исходники наших простейших программ:
Работа с указателями.
Основная проблема начинающего программиста — это различие между указателем и значением, расположенным по адресу памяти, на которую указывает указатель.
Получение значения, на которое указывает указатель в Си и C++ называется «разыменование указателя». Это понятие можно употреблять и в ассемблере.
Получать адрес ячейки памяти (значение указателя) при программировании на ассемблере можно двумя способами, назовём их «статический» и «динамический». Реализовано это с помощью инструкций OFFSET и LEA.
При «статическом» способе указатель будет вычислен во время компиляции. Он не меняется и не вычисляется в процессе выполнения программы. В очень упрощённой форме: его значение равно смещению в байтах от начала программы, спроецированной в память, плюс адрес в оперативной памяти — «точка входа», адрес загрузки программы в память, который задаётся во время компиляции и записывается в заголовке (в начале) файла. Таким образом полученное значение фактически является константой.
OFFSET (Offset — смещение).
Получить «статическим способом» указатель в программировании на ассемблере можно с помощью оператора OFFSET (оператор — команда компилятору — подпрограмме, которая собирает исполняемый файл из исходного кода, написанного языком программирование). Offset возвращает значение метки в памяти. Меткой является любое именованное значение кода и данных. Например, имя переменной, константы или массива (именованное обозначение блока данных). Имя функции — фактически также является меткой (именованным обозначением блока кода).
LEA (Load Effective Address — загрузить эффективный адрес).
При «динамическом» способе адрес вычисляется в процессе исполнения программы. Для этого используется команда LEA (Load Effective Address).
lea операнд1, операнд2
Операнд 1 — это регистр-приёмник (ax, bx, dx и т.д.), куда будет перемещён эффективный адрес (указатель) ячейки памяти, в которой расположен операнд2.
Для начала, необходимо усвоить, что результат оба способа дают одинаковый, но иногда компилятор не имеет возможности определить указатель во время сборки программы. Например, при выделении динамической памяти (столкнёмся в 32 битном Windows программировании), не известно, по какому адресу она будет выделена. Если по этому адресу у нас будет находиться структура, то определить указатель на поля структуры возможно с использованием команды LEA.
Операнды в языке ассемблера
Операнд – объект, над которым выполняется машинная команда.
Операнды ассемблера описываются выражениями с числовыми и текстовыми константами, метками и идентификаторами переменных с использованием знаков операций и некоторых зарезервированных слов.
Операнды могут комбинироваться с арифметическими, логическими, побитовыми и атрибутивными операторами для расчета некоторого значения или определения ячейки памяти, на которую будет воздействовать данная команда или директива.
Способы адресации операндов
Под способами адресации понимаются существующие способы задания адреса хранения операндов:
Операнд задается на микропрограммном уровне (операнд по умолчанию): в этом случае команда явно не содержит операнда, алгоритм выполнения команды использует некоторые объекты по умолчанию (регистры, признаки и т.д.).
Операнд задается в самой команде (непосредственный операнд): операнд является частью кода команды. Для хранения такого операнда в команде выделяется поле длиной до 32 бит. Непосредственный операнд может быть только вторым операндом (источником). Операнд-получатель может находиться либо в памяти, либо в регистре.
Операнд находится в одном из регистров (регистровый операнд): в коде команды указываются именами регистров. В качестве регистров могут использоваться:
Прямая адресация : эффективный адрес определяется непосредственно полем смещения машинной команды, которое может иметь размер 8, 16 или 32 бита.
Косвенная адресация в свою очередь имеет следующие виды:
Косвенная базовая (регистровая) адресация. При такой адресации эффективный адрес операнда может находиться в любом из регистров общего назначения, кроме sp/esp и bp/ebp (это специфические регистры для работы с сегментом стека). Синтаксически в команде этот режим адресации выражается заключением имени регистра в квадратные скобки [].
Данный способ адресации позволяет динамически назначить адрес операнда для некоторой машинной команды и применяется при организации циклических вычислений и при работе со структурами данных, массивами.
Косвенная базовая (регистровая) адресация со смещением предназначена для доступа к данным с известным смещением относительно некоторого базового адреса, используется для доступа к элементам структур, когда смещение элементов известно заранее, на стадии разработки программы, а базовый (начальный) адрес структуры должен вычисляться динамически, на стадии выполнения программы. Модификация содержимого базового регистра позволяет обратиться к одноименным элементам различных экземпляров однотипных структур данных.
Косвенная индексная адресация. Для формирования эффективного адреса используется один из регистров общего назначения, но обладает возможностью масштабирования содержимого индексного регистра.
Значение эффективного адреса второго операнда вычисляется выражением mas+( esi *4) и представляет собой смещение относительно начала сегмента данных.
Наличие возможности масштабирования существенно помогает в решении проблемы индексации при условии, что размер элементов массива постоянен и составляет 1, 2, 4 или 8 байт.
Данный вид адресации также может использоваться со смещением.
Косвенная базовая индексная адресация. Эффективный адрес формируется как сумма содержимого двух регистров общего назначения: базового и индексного. В качестве этих регистров могут применяться любые регистры общего назначения, при этом часто используется масштабирование содержимого индексного регистра.
В случае использования косвенной базовой индексной адресация со смещением эффективный адрес формируется как сумма трех составляющих: cодержимого базового регистра, cодержимого индексного регистра и значения поля смещения в команде.
При использовании подобного выражения для перехода нельзя забывать о длине самой команды, в которой это выражение используется, так как значение счетчика адреса соответствует смещению в сегменте кода данной, а не следующей за ней команды. В приведенном выше примере команда jmp занимает 2 байта. Длина этой и некоторых других команд может зависит от того, какие в ней используются операнды. Команда с регистровыми операндами будет короче команды, один из операндов которой расположен в памяти. В большинстве случаев эту информацию можно получить, зная формат машинной команды.
Операторы в языке ассемблера
Операнды являются элементарными компонентами, из которых формируется часть машинной команды, обозначающая объекты, над которыми выполняется операция. В более общем случае операнды могут входить как составные части в более сложные образования, называемые выражениями . Выражения представляют собой комбинации операндов и операторов , рассматриваемые как единое целое. Результатом вычисления выражения может быть адрес некоторой ячейки памяти или некоторое константное (абсолютное) значение.
Выполнение операторов ассемблера при вычислении выражений осуществляется в соответствии с их приоритетами. Операции с одинаковыми приоритетами выполняются последовательно слева направо. Изменение порядка выполнения возможно путем расстановки круглых скобок, которые имеют наивысший приоритет.
Характеристика основных операторов.
Операторы сдвига выполняют сдвиг выражения на указанное количество разрядов. Например,
Операторы сравнения (возвращают значение истина или ложь) предназначены для формирования логических выражений. Логическое значение истина соответствует логической единице, а ложь – логическому нулю. Логическая единица – значение бита равное 1, логический ноль – значение бита, равное 0.
Назначение операторов сравнения приведено в таблице
Оператор | Условие |
eq | == |
ne | != |
lt | |
ge | >= |
Логические операторы выполняют над выражениями побитовые операции. Выражения должны быть константными. Например,
Индексный оператор [ ]. Транслятор воспринимает наличие квадратных скобок как указание сложить значение выражения за [] со значением выражения, заключенным в скобки. Например,
Наличие индексного оператора указывает транслятору, что необходимо получить значение по вычисленному адресу.
Оператор переопределения типа ptr применяется для переопределения или уточнения типа метки или переменной, определяемых выражением. Тип может принимать одно из следующих значений.
Тип | Пояснение | Назначение |
byte | 1 байт | переменная |
word | 2 байта | переменная |
dword | 4 байта | переменная |
qword | 8 байт | переменная |
tword | 10 байт | переменная |
near | ближний указатель | функция |
far | дальний указатель | функция |
В примере для сравнения значения по адресу esi с константой необходимо явно указать, данные какого типа будут сравниваться.
Оператор переопределения сегмента : (двоеточие) вычисляет физический адрес относительно конкретно задаваемой сегментной составляющей, в качестве которой могут выступать:
Оператор именования типа структуры . (точка) также заставляет транслятор производить определенные вычисления, если встречается в выражении.
Оператор получения сегментной составляющей адреса выражения seg возвращает физический адрес сегмента для выражения, в качестве которого могут выступать метка, переменная, имя сегмента, имя группы или некоторое символическое имя.
Оператор получения смещения выражения offset позволяет получить значение смещения выражения в байтах относительно начала того сегмента, в котором выражение определено. Например,
Оператор type возвращает число байтов, соответствующее определению указанной переменной:
Оператор width возвращает размер в битах объекта типа RECORD или его поля.
Что такое offset в ассемблере
Ассемблер и программирование для IBM PC.
24.Справочник по директивам языка Ассемблер
Ц е л ь: подробно описать операторы и директивы языка Ассемблер.
Некоторые особенности Ассемблера кажутся на первый взгляд несколько
странными. Но после того, как вы ознакомились с простейшими и наиболее
общими свойствами Ассемблера, описанными в предыдущих главах, то
обнаружите, что описания в этой главе более понятны и являются удобным
руководством к программированию. В данной главе представлены операторы
атрибутов, операторы, возвращающие значение, директивы данных, а также
индексная адресация памяти.
ИНДЕКСНАЯ АДРЕСАЦИЯ ПАМЯТИ
________________________________________________________________
При прямой адресации памяти в одном из операндов команды указывается
имя определенной переменной, например для переменной COUNTER:
Во время выполнения программы процессор локализует указанную
переменную в памяти путем объединения величины смещения к этой переменной
с адресом сегмента данных.
При индексной адресации памяти ссылка на операнд определяется через
базовый или индексный регистр, константы, переменные смещения и простые
переменные. Квадратные скобки, определяющие операнды индексной адресации,
действуют как знак плюс (+). Для индексном адресации памяти можно
использовать:
— базовый регистр BX в виде [BX] вместе с сегментным регистром
DS или базовый регистр BP в виде [BP] вместе с сегментным регистром
SS. Например, с помощью команды
MOV DX,[BX] ;Базовый регистр
в регистр DX пересылается элемент, взятый по относительному адресу в
регистре BX и абсолютному адресу сегмента в регистре DS;
— индексный регистр DI в виде [DI] или индексный регистр SI в
виде [SI], оба вместе с сегментным регистром DS. Например, с помощью
команды
MOV AX,[SI] ;Индексный регистр
в регистр AХ пересылается элемент, взятый по относительному адресу в
регистре SI и абсолютному адресу сегмента в регистре DS;
— [константу], содержащую непосредственный номер или имя в
квадратных скобках. Например, с помощью команды
MOV [BX+SI+4],AX ;База+индекс+константа
MOV DX,8[DI][4] ;Смещение+индекс+константа
в регистр DX пересылается элемент, взятый по абсолютному адресу в
регистре DS, смещению 8, относительному адресу в регистре DI и
константе 4.
Эти операнды можно комбинировать в любой последовательности. Но
нельзя использовать одновременно два базовых регистра [BX + BP] или два
индексных регистра [DI + SI]. Обычно индексированные адреса используются
для локализации элементов данных в таблицах.
ОПЕРАТОРЫ ЯЗЫКА АССЕМБЛЕР
________________________________________________________________
Существует три типа ассемблерных операторов: операторы атрибута,
операторы, возвращающие значение, и операторы. специфицирующие битовую
строку. В данном разделе рассмотрены первые два типа операторов.
Операторы, специфицирующие битовую строку, оператор MASK, счетчик сдвига и
оператор WIDTH относятся к директиве RECORD и будет рассмотрены в
следующем разделе.
Оператор LENGTH
——————
Оператор LENGTH возвращает число элементов, определенных операндом
DUP. Например, следующая команда MOV заносит в регистр DX значение 10:
TABLEA DW 10 DUP(?)
.
MOV DX,LENGTH TABLEA
Если операнд DUP отсутствует, то оператор LENGTH возвращает значение
01. См. операторы SIZE и TYPE в этом разделе.
Оператор OFFSET
——————
Оператор OFFSET возвращает относительный адрес переменной или метки
внутри сегмента данных или кода. Оператор имеет следующий формат:
OFFSET переменная или метка
MOV DX,OFFSET TABLEA
устанавливает в регистре DX относительный адрес (смещение) поля TABLEA в
сегменте данных. (Заметим, что команда LEA выполняет аналогичное действие,
но без использования оператора OFFSET.)
Оператор PTR
—————
Оператор PTR используется совместно с атрибутами типа BYTE, WORD или
DWORD для локальной отмены определенных типов (DB, DW или DD) или с
атрибутами NEAR или FAR для отмены значения дистанции по умолчанию. Формат
оператора следующий:
В поле «тип» указывается новый атрибут, например BYTE. Выражение имеет
ссылку на переменную или константу. Приведем несколько примеров оператора
PTR:
FLDB DB 22H
DB 35H
FLDW DW 2672H ;0бьектный код 7226
MOV AН,BYTE PTR FLDW ;Пересылает 1-й байт (72)
ADD BL,BYTE PTR FLDW+1 ;Прибавляет 2-й байт (26)
MOV BYTE PTR FLDW,05 ;Пересылает 05 в 1-й байт
MOV AX,WORD PTR FLDB ;3аносит в АХ байты (2235)
CALL FAR PTR[BX] ;Длинный вызов процедуры
Директива LABEL, описанная в следующем разделе, выполняет функцию,
аналогичную оператору PTR.
Оператор SEG
—————
Оператор SEG возвращает адрес сегмента, в котором расположена
указанная переменная или метка. Наиболее подходящим является использование
этого оператора в программах, состоящих из нескольких отдельно
ассемблируемых сегментов. Формат оператора:
SEG переменная или метка
Примеры применения оператора SEG в командах MOV:
MOV DX,SEG FLOW ;Адрес сегмента данных
MOV DX,SEG A20 ;Адрес сегмента кода
В результате ассемблер сокращает машинный код операнда от двух до
одного байта. Эта возможность оказывается полезной для коротких переходов
вперед, так как в этом случае ассемблер не может сам определить расстояние
до адреса перехода и резервирует два байта при отсутствии оператора SHORT.
Оператор SIZE
—————
Оператор SIZE возвращает произведение длины LENGTH и типа TYPE и
полезен только при ссылках на переменную с операндом DUP. Формат
оператора:
См. пример для оператора TYPE.
Оператор TYPE
—————
Оператор TYPE возвращает число байтов, соответствующее определению
указанной переменной:
Определение Число байтов
DB 1
DW 2
DD 4
DQ 8
DT 10
STRUC Число байтов, определённых в STRUC
NEAR метка FFFF
FAR метка FFFE
Формат оператора TYPE:
TYPE переменная или метка
Ниже приведены примеры, иллюстрирующие применение операторов TYPE, LENGTH
и SIZE:
ДИРЕКТИВЫ АССЕМБЛЕРА
________________________________________________________________
В данном разделе описано большинство ассемблерных директив. В гл.5
были подробно рассмотрены директивы для определения данных (DB, DW и
проч.).
В директиве указываются имена сегментных регистров, групп (GROUP) и
выражений SEG. Одна директива ASSUME может назначить до четырех сегментных
регистров в любой последовательности, например:
Для отмены любого ранее назначенного в директиве ASSUME сегментного
регистра необходимо использовать ключевое слово NOTHING:
Если, например, регистр DS оказался не назначен или отменен ключевым
словом NOTHING, то для ссылки к элементу из сегмента данных в командах
используется операнд со ссылкой к регистру DS:
MOV AX,DS:[BX] ;Использование индексного адреса
MOV AX,DS:FLDW ;Пересылка содержимого поля FLDW
Конечно, регистр DS должен содержать правильное значение сегментного
адреса.
Директива EXTRN подробно рассмотрена в гл.21.
Следующая директива GROUP объединяет SEG1 и SEG2 в одном ассемблерном
модуле:
GROUPX GROUP SEG1,SEG2
SEG1 SEGMENT PARA ‘CODE’
ASSUME CS:GROUPX
SEG1 ENDS
SEG2 SEGMENT PARA ‘CODE’
ASSUME CS:GROUPX
SEG2 ENDS
Директива INCLUDE
——————-
Отдельные фрагменты ассемблерного кода или макрокоманды могут
использоваться в различных программах. Для этого такие фрагменты и
макрокоманды записываются в отдельные дисковые файлы, доступные для
использования из любых программ. Пусть некоторая подпрограмма,
преобразующая ASCII-код в двоичное представление, записана на диске С в
файле по имени CONVERT.LIB. Для доступа к этому файлу необходимо указать
директиву
причем в том месте исходной программы, где должна быть закодирована
подпрограмма преобразования ASCII-кода. В результате ассемблер найдет
необходимый файл на диске и вставит его содержимое в исходную программу.
(Если файл не будет найден, то ассемблер выдаст соответствующее сообщение
об ошибке и директива INCLUDE будет игнорирована.)
Для каждой вставленной строки ассемблер выводит в LST-файл в 30-й
колонке символ С (исходный текст в LST-файле начинается с 33-й колонки).
В гл.20 (Макрокоманды) дан практический пример директивы INCLUDE и
дано объяснение, каким образом можно использовать эту директиву только в
первом проходе ассемблера.
Директива LABEL
——————
Директива LABEL позволяет переопределять атрибут определенного имени.
Формат директивы:
В качестве типа можно использовать BYTE, WORD или DWORD для
переопределения областей данных или имен структур или битовых строк.
Директивой LABEL можно переопределить выполнимый код, как NEAR или FAR.
Эта директива позволяет, например, определить некоторое поле и как DB, и
как DW. Ниже проиллюстрировано использование типов BYTE и WORD:
REDEFB LABEL BYTE
FIELDW DW 2532H
REDEFW LABEL WORD
FIELDB DB 25H
DB 32H
MOV AL,REDEFB ;Пересылка первого байта
MOV BX,REDEFW ;Пересылка двух байтов
Первая команда MOV пересылает только первый байт поля FIELDW. Вторая
команда MOV пересылает два байта, начинающихся по адресу FIELDB. Оператор
PTR выполняет аналогичные действия.
Директива NAME
—————-
Директива NAME обеспечивает другой способ назначения имени модулю:
Ассемблер выбирает имя модуля в следующем порядке:
1) если директива NAME присутствует, то ее операнд становится
именем модуля;
2) если директива NAME отсутствует, то ассемблер использует
первые шесть символов из директивы TITLE;
3) если обе директивы NAME и TITLE отсутствуют, то именем модуля
становится имя исходного файла.
Выбранное имя передается ассемблером в компоновщик.
Директива ORG
—————
Для определения относительной позиции в сегменте данных или кода
ассемблер использует адресный счетчик. Рассмотрим сегмент данных со
следующими определениями:
Смещение Имя Операция Операнд Адресный счетчик
00 FLDA DW 2542H 02
02 FLDB DB 36H 03
03 FLDC DW 212EH 05
05 FLDD DD 00000705H 09
Выражение может быть абсолютным числом, но не символическим именем, и
должно формировать двухбайтовое абсолютное число. Рассмотрим следующие
элементы данных, определенные непосредственно после поля FLDD:
Смещение Имя Операция Операнд Адресный счетчик
Первая директива ORG возвращает адресный счетчик в нулевое значение.
Поля FLDX, FLDY и FLDZ определяют те же области памяти, что и поля FLDA,
FLDB и FLDC:
Смещение: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| | | |
FLDA FLDB FLDC FLDD
| | |
FLDX FLDY FLDZ
MOV AX,FLOC ;Одно слово
MOV AL,FLDZ ;Oдин байт
Директиву ORG можно использовать для переопределения областей памяти.
При этом следует правильно устанавливать адресный счетчик и учитывать все
переопределяемые адреса памяти. Кроме того, новые переменные не должны
определять константы, так как при этом будут перекрыты константы,
определенные ранее. Директиву ORG нельзя использовать внутри определения
STRUCT.
Директива PROC
—————-
Любая процедура представляет собой совокупность кодов, начинающуюся
директивой PROC и завершающуюся директивой ENDP. Обычно эти директивы
используются для подпрограмм в кодовом сегменте. Ассемблер допускает
переход на процедуру с помощью команды JMP, но обычной практикой является
использование команды CALL для вызова процедуры и RET для выхода из
процедуры.
Процедура, находящаяся в одном сегменте с вызывающей процедурой,
имеет тип NEAR:
имя-процедуры PROC [NEAR]
Если операнд опущен, то ассемблер принимает значение NEAR no умолчанию.
Если процедура является внешней по отношению к вызывающему сегменту, то ее
вызов может осуществляться только командой CALL, а сама процедура должна
быть объявлена как PUBLIC. Более того, если в вызываемой процедуре
используется другое значение ASSUME CS, то необходимо кодировать атрибут
FAR:
PUBLIC имя-процедуры,
имя-процедуры PROC FAR
При вызове любой процедуры с помощью команды CALL необходимо
обеспечить возврат по команде RET.
Директива PUBLIC подробно описана в гл.21.
Имя директивы и имена полей могут быть любыми уникальными
идентификаторами. После каждого имени поля следует двоеточие (:) и размер
поля в битах, которое может быть от 1 до 16 бит:
Число определенных битов Принимаемый размер
BITREC RECORD BIT1:3,BIT2:7,BIT3:6
BITREC2 RECORD BIT1:3=101B,BIT2:7=0110110B,BIT3:011010B
Предположим, что директива RECORD находится перед сегментом данных.
Тогда внутри сегмента данных должен быть другой оператор, который отводит
память под данные. Для этого необходимо определить уникальное имя, имя
директивы RECORD и операнд, состоящий из угловых скобок (символы меньше и
больше):
Данное определение генерирует объектный код AD9A. который записывается как
9AAD в сегмент данных. В угловых скобках может находиться значение,
переопределяющее BITREC.
Программа на рис.24.1 иллюстрирует определение BITREC директивой
RECORD, но без начальных значений. В этом случае соответствующий оператор
в сегменте данных инициализирует каждое поле операндом в угловых скобках.
Дополнительно к директиве RECORD имеются операторы WIDTH, MASK и
фактор сдвига. Использование этих операторов позволяет изменять
определение директивы RECORD без изменения команд, которые имеют ссылки на
директиву RECORD.
О п е р а т о р WIDTH. Оператор WIDTH возвращает число битов в
директиве RECORD или в одном из ее полей. На рис.24.1 после метки А10
имеется два примера оператора WIDTH. Первая команда MOV загружает в
регистр BH число битов во всем поле RECORD BITREC (16 бит); вторая команда
MOV загружает в регистр AL число битов в поле BIT2 (7 бит). В обоих
случаях ассемблер генерирует для числа битов непосредственный операнд.
Рис.24.1. Использование диpективы RECORD
Ф а к т о р с д в и г а. Прямая ссылка на элемент в RECORD,например:
в действительности не имеет отношения к содержимому BIT2. Вместо этого
ассемблер генерирует непосредственный операнд, который содержит «фактор
сдвига», помогающий изолировать необходимое поле. Непосредственное
значение представляет собой число, на которое необходимо сдвинуть BIT2 для
выравнивания справа. На рис.24.1 после метки В10 имеются три команды,
которые загружают в регистр CL фактор сдвига для полей BIT1, BIT2 и BITЗ.
О п е р а т о р MASK. Оператор MASK возвращает «маску» из единичных
битовых значений, которые представляют специфицированное поле, иными
словами, определяют битовые позиции, которые занимает поле. Например,
оператор MASK для каждого из полей, определенных в области BITREC,
возвращает следующие значения:
Поле Двоичное значение Шестнадцатиричное значение
В1Т1 1110000000000000 Е000
В1Т2 0001111111000000 1FC0
В1ТЗ 0000000000111111 003F
На рис.24.1 три команды после метки С10 загружают в регистры значения
оператора MASK для полей BIT1, BIT2 и BITЗ. Команды после меток D10 и Е10
иллюстрируют выделение значений полей BIТ2 и BIТ1 соответственно из
области BITREC. После метки D10 в регистр АХ загружается все значение
области, определенной директивой RECORD, а затем из этого значения с
помощью оператора MASK выделяются только биты поля BIТ2:
Область RECORD: 101 0110110 011010
AND MASK BIТ2: 000 1111111 000000
Результат: 000 0110110 000000
В результате сбрасываются все биты, кроме принадлежащих к полю BIТ2.
Следующие две команды приводят к сдвигу содержимого регистра АХ на шесть
битов для выравнивания справа:
После метки Е10 в регистр AХ загружается все значение области,
определенной директивой RECORD, и так как BIТ1 является самым левым полем,
то в примере используется только фактор для сдвига значения вправо на 13
бит:
Директива SEGMENT
——————-
Ассемблерный модуль может состоять из одного или более сегментов,
части сегмента или даже частей нескольких сегментов. Формат директивы:
имя_сегмента SEGMENT [выравнивание] [объединение] [класс]
.
.
.
имя_сегмента ENDS
Все операнды являются необязательными. Ниже описаны операнды для
выравнивания, объединения и указания класса.
В ы р а в н и в а н и е. Операнд выравнивания определяет начальную
границу сегмента, например
PAGE = xxx00
PARA = хххх0 (граница по умолчанию)
WORD = ххххe (четная граница)
BYTE = ххххх
О б ъ е д и н е н и е. Операнд объединения указывает способ обработки
сегмента, при компоновке:
NONE: Значение по умолчанию. Сегмент должен быть логически отделен от
других сегментов, хотя физически он может быть смежным.
Предполагается, что сегмент имеет собственный базовый адрес;
STACK: Для компоновщика операнд STACK аналогичен операнду PUBLIC. В
любой компонуемой программе должен быть определен по крайней мере
один сегмент STACK. Если объявлено более одного стека, то стековый
указатель (SP) устанавливается на начало первого стека;
COMMON: Для сегментов COMMON с одинаковыми именами и классами
компоновщик устанавливает один общий базовый адрес. При выполнении
происходит наложение второго сегмента на первый. Размер общей области
определяется самым длинным сегментом;
AT-параграф: Параграф должен быть определен предварительно. Данный
операнд обеспечивает определение меток и переменных по фиксированным
адресам в фиксированных областях памяти, таких, как ROM или таблица
векторов прерываний в младших адресах памяти. Например, для
определения адреса дисплейного видеобуфера используется
VIDEO_RAM SEGMENT AT 0B800H
Класс: Операнд класс может содержать любое правильное имя,
заключенное в одиночные кавычки. Данный операнд используется
компоновщиком для обработки сегментов, имеющих одинаковые имена и
классы. Типичными примерами являются классы ‘STACK’ и ‘CODE’.
Следующие два сегмента объединяются компоновщиком в один физический
сегмент при одном значении сегментного регистра:
———————————
Ассемблерный SEG1 SEGMENT PARA PUBLIC ‘CODE’
модуль 1 ASSUME CS:SEG1
.
SEG1 ENDS
———————————
Ассемблерный SEG2 SEGMENT PARA PUBLIC ‘CODE’
модуль 2 ASSUME CS:SEG1
.
SEG2 ENDS
———————————
Сегменты могут быть вложенными один в другой:
SEG1 SEGMENT
. Начало SEG1
SEG2 SEGMENT
. Область SEG2
SEG2 ENDS
. Конец SEG1
SEG1 ENDS
Для объединения сегментов в группы используйте директиву GROUP.
Директива STRUC
——————
Директива STRUC обеспечивает определение различных полей в виде
структуры. Данная директива не поддерживается в малом ассемблере ASM.
Формат директивы:
Имя-структуры STRUC
.
[определение полей данных]
.
Имя-структуры ENDS
Структура начинается собственным именем в директиве STRUC и
завершается таким же именем в директиве ENDS. Ассемблер записывает поля;
определенные в структуре, одно за другим от начала структуры. Правильными
операторами определения полей являются DB, DW, DD и DT с указанием имен
или без них.
На рис.24.2 директива STRUC определяет список параметров PARLIST для
ввода имени с клавиатуры. Следующий далее оператор выделяет память под
данную структуру:
Данный оператор обеспечивает адресацию структуры внутри программы. Угловые
скобки (символы меньше и больше) в данном случае пусты, но в них можно
указать данные для переопределения областей внутри структуры.
В командах ассемблера может использоваться прямая адресация по имени
структуры. Для ссылки на определенное поле внутри структуры в командах
используется имя структуры (PARAMS в данном примере) и через точку имя
конкретного поля:
Используя оператор выделения памяти, можно переопределить содержимое
полей внутри структуры. Правила для практического использования этой
возможности можно найти в руководстве по Ассемблеру.