А теперь рассмотрим ситуацию, когда по операндам команды MOV нельзя определить размер пересылаемой величины.
Забегая вперед, отметим, что если некоторый адрес А надо модифицировать, скажем, по регистру SI, то в ЯА это записывается так: A[SI]. Исполнительный адрес в этом случае вычисляется по формуле Aисп = A + [SI]. В частности, при А=0 запись имеет вид [SI] (0 не указывается) и задает исполнительный адрес, равный содержимому регистра SI: Аисп = [SI].
C учетом этого рассмотрим такую задачу. Пусть в регистре SI находится адрес некоторой ячейки памяти и требуется записать 0 в эту ячейку. Тогда, казалось такое обнуление можно сделать с помощью команды
MOV [SI], 0
Однако это не так. Дело в том, что по этой команде нельзя понять, какого размера ноль пересылается, поскольку второй операнд может обозначать ноль как размером в байт (00h), так и размером в слово (0000h), а что касается первого операнда, то адрес из регистра SI может быть адресом ячейки как размером в байт, так и размером в слово (напомним, что с одного и того же адреса могут начинаться ячейки разных размеров).
Итак, в этой команде ни по первому, ни по второму операнду нельзя определить размер пересылаемой величины, а потому ассемблер не сможет определить, на какую конкретную машинную команду заменять эту символьную команду. В подобных ситуациях ассемблер фиксирует ошибку, сообщая, что типы операндов неизвестны. Чтобы не было этой ошибки, автор программы должен явно указать тип хотя бы одного из операндов команды.
Для этого в ЯА введен оператор указания типа PTR (от pointer, указатель), который записывается следующим образом:
<тип> PTR <выражение>
где <тип> - это BYTE, WORD или DWORD (есть и другие варианты, но мы их пока не рассматриваем), а выражение может быть константным или адресным.
Если указано константное выражение, то оператор "говорит", что значение этого выражения (число) должно рассматриваться ассемблером как величина указанного типа (размера); например, BYTE PTR 0 - это ноль как байт, a WORD PTR 0 - это ноль как слово (запись BYTE PTR 300 ошибочна, т. к. число 300 не может быть байтом). Отметим, что в этом случае оператор PTR относится к константным выражениям.
Если же в PTR указано адресное выражение, то оператор "говорит", что адрес, являющийся значением выражения, должен восприниматься ассемблером как адрес ячейки указанного типа (размера); например: WORD PTR A - адрес А обозначает слово (байты с адресами А и А+1). В данном случае оператор PTR относится к адресным выражениям.
С использованием оператора PTR наша задача решается так: если мы имеем в виду обнуление байта по адресу из регистра SI, то для этого надо использовать команду
MOV BYTE PTR [SI],0 или MOV [SI], BYTE PTR 0
а если надо переслать нулевое слово, то команду
MOV WORD PTR [SI],0 ИЛИ MOV [SI], WORD PTR 0
Отметим, что обычно принято уточнять тип операнда-адреса, а не тип непосредственного операнда.
Оператор PTR полезен еще в одной ситуации - когда надо не уточнить тип операнда, а изменить его. Пусть, к примеру, Z - переменная размером в слово:
Z DW 1234h;Z: 34h, Z+l: 12h
и надо записать ноль не во все это слово, а только в его первый байт - в тот, где находится величина 34h. Так вот, сделать это командой
MOV Z,0
нельзя, т. к. по ней 0 запишется в оба байта. Почему? Имя Z описано в директиве DW и потому, как мы уже знаем, получает тип WORD: TYPE Z = WORD. Когда ассемблер определяет размер операнда команды, в качестве которого указано имя переменной, то он учитывает тип, полученный именем при описании. Поэтому в нашем случае ассемблер и считает, что пересылается нулевое слово. Обычно так и должно быть, но сейчас нас это не устраивает, нам сейчас нужно, чтобы ассемблер рассматривал Z как имя байта. Вот такое изменение типа имени и позволяет сделать оператор PTR:
MOV BYTE PTR Z,0; Z: 00h, Z+1: 12h
Здесь мы сказали ассемблеру, чтобы он игнорировал тот тип имени Z, который был приписан ему при описании, и считал, что имя Z обозначает байт. (Отметим, что такое изменение типа локально, оно действует только в данной команде.)
Аналогичная ситуация возникает, если мы хотим получить доступ ко второму байту переменной Z. Например, для записи 15 в этот байт нельзя использовать команду
MOV Z+1,15
т. к. считается, что адрес Z+1 обозначает слово. Это общее правило в ЯА: адрес вида <имя>+/-<целое> имеет тот же тип, что и <имя>. Поэтому число 15 запишется в два байта - с адресами Z+1 и Z+2. Если же мы хотим изменить только байт по адресу Z+1, тогда надо воспользоваться оператором PTR:
MOV BYTE PTR (Z+1),15
Здесь конструкция BYTE PTR (Z+1) "говорит", что Z+1 надо рассматривать как адрес байта, а не слова.
Отметим, что в ЯА оператор PTR по старшинству выполняется до оператора сложения, поэтому запись BYTE PTR Z+1 трактуется как (BYTE PTR Z)+l. Однако в данном конкретном случае старшинство операторов не играет никакой роли, т. к. обе записи - BYTE PTR (Z+1) и BYTE PTR Z+1 - эквивалентны по смыслу: в первом случае мы сначала увеличиваем адрес Z на 1 и только затем сообщаем, что Z+1 надо рассматривать как адрес байта, а во втором случае мы сначала сообщаем, что Z - это адрес байта, и лишь затем увеличиваем его на 1 при этом "байтовость" адреса сохраняется).
Итак, оператор PTR используется в следующих ситуациях: когда типы операндов команд неизвестны и потому надо указать явно тип одного из операндов, i когда нас не устраивает тип, приписанный имени при его описании, и потому мы должны указать нужный нам тип.
Команда XCHG
В машинных программах приходится довольно часто переставлять местами какие-то две величины, и хотя такую перестановку можно реализовать только с помощью команды MOV, в ПК введена специальная команда для этого:
перестановка (exchange): XCHG opl, op2
Эта команда меняет местами значения своих операндов (они должны быть либо байтами, либо словами): ор1 <=> ор2. Флаги при этом не меняются.
Пример:
Допустимые типы операндов команды XCHG:
Как видно, не допускается перестановка содержимого двух ячеек памяти. Если все-таки надо сделать такую перестановку, то это реализуется через какой-нибудь регистр. Например, поменять местами значения байтовых переменных X и Y можно так: