Перемещаемые операнды отличаются от адресных тем, что они не привязаны к конкретному адресу физической памяти. Сегментная составляющая адреса перемещаемого операнда неизвестна и будет определена после загрузки программы в память для выполнения.
Счетчик адреса – специфический вид операнда. Он обозначается знаком S. Специфика этого операнда в том, что когда транслятор ассемблера встречает в исходной программе этот символ, то он подставляет вместо него текущее значение счетчика адреса. Значение счетчика адреса или, как его иногда называют, счетчика размещения представляет собой смещение текущей машинной команды относительно начала сегмента кода. В формате листинга счетчику адреса соответствует вторая или третья колонка (в зависимости от того, присутствует или нет в листинге колонка с уровнем вложенности). Если взять в качестве примера любой листинг, то видно, что при обработке транслятором очередной команды ассемблера счетчик адреса увеличивается на длину сформированной машинной команды. Важно правильно понимать этот момент. К примеру, обработка директив ассемблера не влечет за собой изменения счетчика. Директивы, в отличие от команд ассемблера, – это лишь указания транслятору на выполнение определенных действий по формированию машинного представления программы, и для них транслятором не генерируется никаких конструкций в памяти.
При использовании подобного выражения для перехода не забывайте о длине самой команды, в которой это выражение используется, так как значение счетчика адреса соответствует смещению в сегменте команд данной, а не следующей за ней команды. В нашем примере команда jmp занимает 2 байта. Но будьте осторожны, длина команды зависит от того, какие в ней используются операнды. Команда с регистровыми операндами будет короче команды, один из операндов которой расположен в память. В большинстве случаев эту информацию можно получить, зная формат машинной команды и анализируя колонку листинга с объектным кодом команды;
4) регистровый операнд – это просто имя регистра. В программе на ассемблере можно использовать имена всех регистров общего назначения и большинства системных регистров;
5) базовый и индексный операнды. Этот тип операндов используется для реализации косвенной базовой, косвенной индексной адресации или их комбинаций и расширений;
6) структурные операнды используются для доступа к конкретному элементу сложного типа данных, называемого структурой.
Записи (аналогично структурному типу) используются для доступа к битовому полю некоторой записи.
Операнды являются элементарными компонентами, из которых формируется часть машинной команды, обозначающая объекты, над которыми выполняется операция. В более общем случае операнды могут входить как составные части в более сложные образования, называемые выражениями. Выражения представляют собой комбинации операндов и операторов, рассматриваемые как единое целое. Результатом вычисления выражения может быть адрес некоторой ячейки памяти или некоторое константное (абсолютное) значение.
Возможные типы операндов мы уже рассмотрели. Перечислим теперь возможные типы операторов ассемблера и синтаксические правила формирования выражений ассемблера, и дадим краткую характеристику операторов.
1. Арифметические операторы. К ним относятся:
1) унарные «+» и «—»;
2) бинарные «+» и «—»;
3) умножения «*»;
4) целочисленного деления «/»;
5) получения остатка от деления «mod».
Эти операторы расположены на уровнях приоритета 6,7,8 в таблице 4.
Рис. 8. Синтаксис арифметических операций
2. Операторы сдвига выполняют сдвиг выражения на указанное количество разрядов (рис. 9).
Рис. 9. Синтаксис операторов сдвига
3. Операторы сравнения (возвращают значение «истина» или «ложь») предназначены для формирования логических выражений (рис. 10 и табл. 3). Логическое значение «истина» соответствует цифровой единице, а «ложь» – нулю.
Рис. 10. Синтаксис операторов сравнения
Таблица 3. Операторы сравнения
4. Логические операторы выполняют над выражениями побитовые операции (рис. 11). Выражения должны быть абсолютными, т. е. такими, численное значение которых может быть вычислено транслятором.
Рис. 11. Синтаксис логических операторов
5. Индексный оператор []. Скобки тоже являются оператором, и транслятор их наличие воспринимает как указание сложить значение выражение_1 за этими скобками с выражение_2, заключенным в скобки (рис. 12).
Рис. 12. Синтаксис индексного оператора
Заметим, что в литературе по ассемблеру принято следующее обозначение: когда в тексте речь идет о содержимом регистра, то его название берут в круглые скобки. Мы также будем придерживаться этого обозначения.
6. Оператор переопределения типа ptr применяется для переопределения или уточнения типа метки или переменной, определяемых выражением (рис. 13).
Тип может принимать одно из следующих значений: byte, word, dword, qword, tbyte, near, far.
Рис. 13. Синтаксис оператора переопределения типа
7. Оператор переопределения сегмента «:» (двоеточие) заставляет вычислять физический адрес относительно конкретно задаваемой сегментной составляющей: «имя сегментного регистра», «имя сегмента» из соответствующей директивы SEGMENT или «имя группы» (рис. 14). При обсуждении сегментации мы говорили о том, что микропроцессор на аппаратном уровне поддерживает три типа сегментов – кода, стека и данных. В чем заключается такая аппаратная поддержка? К примеру, для выборки на выполнение очередной команды микропроцессор должен обязательно посмотреть содержимое сегментного регистра cs и только его. А в этом регистре, как мы знаем, содержится (пока еще не сдвинутый) физический адрес начала сегмента команд. Для получения адреса конкретной команды микропроцессору остается умножить содержимое cs на 16 (что означает сдвиг на четыре разряда) и сложить полученное 20-битное значение с 16-битным содержимым регистра ip. Примерно то же самое происходит и тогда, когда микропроцессор обрабатывает операнды в машинной команде. Если он видит, что операнд – это адрес (эффективный адрес, который является только частью физического адреса), то он знает, в каком сегменте его искать, – по умолчанию это сегмент, адрес начала которого записан в сегментном регистре ds.
А что же с сегментом стека? В контексте нашего рассмотрения нас интересуют регистры sp и Ър. Если микропроцессор видит в качестве операнда (или его части, если операнд – выражение) один из этих регистров, то по умолчанию он формирует физический адрес операнда, используя в качестве его сегментной составляющей содержимое регистра ss. Это набор микропрограмм в блоке микропрограммного управления, каждая из которых выполняет одну из команд в системе машинных команд микропроцессора. Каждая микропрограмма работает по своему алгоритму. Изменить его, конечно, нельзя, но можно чуть-чуть подкорректировать. Делается это с помощью необязательного поля префикса машинной команды. Если мы согласны с тем, как работает команда, то это поле отсутствует. Если же мы хотим внести поправку (если, конечно, она допустима для конкретной команды) в алгоритм работы команды, то необходимо сформировать соответствующий префикс.
Префикс представляет собой однобайтовую величину, численное значение которой определяет ее назначение. Микропроцессор распознает по указанному значению, что этот байт является префиксом, и дальнейшая работа микропрограммы выполняется с учетом поступившего указания на корректировку ее работы. Сейчас нас интересует один из них – префикс замены (переопределения) сегмента. Его назначение состоит в том, чтобы указать микропроцессору (а по сути, микропрограмме) на то, что мы не хотим использовать сегмент по умолчанию. Возможности для подобного переопределения, конечно, ограничены. Сегмент команд переопределить нельзя, адрес очередной исполняемой команды однозначно определяется парой cs: ip. А вот сегменты стека и данных – можно. Для этого и предназначен оператор «:». Транслятор ассемблера, обрабатывая этот оператор, формирует соответствующий однобайтовый префикс замены сегмента.
Рис. 14. Синтаксис оператора переопределения сегмента
8. Оператор именования типа структуры «.»(точка) также заставляет транслятор производить определенные вычисления, если он встречается в выражении.
9. Оператор получения сегментной составляющей адреса выражения seg возвращает физический адрес сегмента для выражения (рис. 15), в качестве которого могут выступать метка, переменная, имя сегмента, имя группы или некоторое символическое имя.