Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 3000239.doc
Скачиваний:
23
Добавлен:
30.04.2022
Размер:
1.12 Mб
Скачать

3. Пересылки. Арифметические команды

3.1. Обозначения операторов команд

При описании команд следует указывать, какие операнды в них допустимы, а какие – нет. Для сокращения подобного рода указаний в табл. 3 введены обозначения, которыми затем будут использоваться при описании команд.

Таблица 3

Сокращения, используемые при описании команд

Местонахождение операнда

Обозначение

Запись в языке ассемблера

в команде

i8, i16, i32

константное выражение

в регистре общего назначения

r8, r16

имя

имя регистра

в сегментном регистре

sr

CS, DS, SS, ES

в ячейке памяти

m8, m16, m32

адресное выражение

Непосредственные операнды (т. е. задаваемые в самой команде) будут обозначаться буквой i (от immediate, непосредственный) с указанием за ней, сколько разрядов - 8 (байт), 16 (слово) или 32 (двойное слово) – отводится на него в команде. При этом отметим: запись i16 не означает, что операнд не может быть небольшим числом, в качестве i16 можно указать и операнд 0, и операнд 32000, лишь бы он занимал не более 16 разрядов. В языке ассемблера непосредственные операнды записываются в виде константных выражений.

Регистры будут обозначаться буквой r (от register) с указанием за ней требуемого размера регистра: r8 – это байтовые регистры (АН, AL, ВН и т. п.), а r16 – регистры размером в слово (АХ, ВХ, SI и т. п.). При этом буквой r обозначаются только регистры общего назначения, сегментные же регистры будут обозначаться как sr.

Если операнд находится в памяти, то в команде указывается адрес соответствующей ячейки. Такие операнды обозначаются буквой m (от memory, память) с указанием размера ячейки. В языке ассемблера эти операнды задаются адресными выражениями.

3.2. Команды пересылки

В ПК достаточно много команд пересылки, здесь будут рассмотрим только две из них - MOV и XCHG, а также оператор PTR.

Команда MOV

В ПК есть машинные команды пересылки байта или слова (переслать одной командой двойное слово нельзя). Пересылаемая величина берется из команды, регистра или ячейки памяти, а записывается в регистр или ячейку памяти (записывать в команду, естественно, нельзя). Таких команд много, но в языке ассемблера все они записываются одинаково:

Пересылка (move): MOV op1, op2

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

По команде MOV на место первого операнда пересылается значение второго операнда: op1:=op2. Флаги эта команда не имеет.

Примеры:

MOV АХ,500 ; А:=500

MOV BL,DH ; BL:=DH

В команде MOV допустимы следующие комбинации операндов, приведенные в табл. 4.

Таблица 4

Допустимые комбинации операндов команды MOV

op1

ор2

r8

i8, r8, m8

пересылка байтов

m8

i8, r8

r16

i16, r16, sr, m16

пересылка слов

sr (кроме CS)

r16, m16

m16

i16, r16, sr

Из этой таблицы видно, что запрещены пересылки из одной ячейки памяти в другую, из одного сегментного регистра в другой, запись непосредственного операнда в сегментный регистр. Это обусловлено тем, что в ПК нет таких машинных команд. Если по алгоритму все же нужно такое действие, то оно реализуется в две команды, пересылкой через какой-нибудь несегментный регистр. Например, записать число 100 в сегментный регистр DS можно так:

MOV АХ,100

MOV DS,AX ;DS:=100

Отметим также, что командой MOV нельзя менять содержимое сегментного регистра CS. Это связано с тем, что регистры CS и IP определяют адрес той команды программы, которая должна быть выполнена следующей, поэтому изменение любого из этих регистров есть ничто иное, как операция перехода, а не пересылка. Команда же MOV не реализует переход.

Как известно, в ПК числа размером в слово хранятся в памяти в «перевернутом» виде, а в регистрах – в нормальном, неперевернутом. Команда MOV учитывает это и при пересылке слов между памятью и регистрами сама «переворачивает» их:

Q DW 1234h ; Q: 34h, Q+l: 12h

MOV AX, Q ; AH=12h, AL=34h

По команде MOV можно переслать как байт, так и слово. А как узнать, что именно – байт или слово – пересылает команда? Не может ли получиться так, что мы хотели написать команду пересылки слова, а оказалось, что команда пересылает байт? Ответ такой: размер пересылаемой величины определяется по типу операндов, указанных в символьной команде MOV. Более точно ситуация здесь следующая.

Пусть, к примеру, имеются такие описания переменных:

X DB ? ; ТУРЕ X = BYTE

Y DW ? ; TYPE Y = WORD

Как правило, в команде MOV легко узнается тип (размер) одного из операндов, он и определяет размер пересылаемой величины. Например:

MOV ВН,0 ; пересылка байта

; (ВН - байтовый регистр)

MOV Х,0 ; то же самое (X описан как

; имя байтовой переменной)

MOV SI,0 ;пересылка слова (SI – регистр

; размером в слово)

MOV Y,0 ; то же самое (Y описан как

; имя переменной слова)

Следует отметить, что здесь по второму операнду (0) нельзя определить, какого он размера: ноль может быть и байтом (00h), и словом (0000h).

Если можно определить размеры обоих операндов, тогда эти размеры должны совпадать (либо байты, либо слова), иначе ассемблер зафиксирует ошибку. Например:

MOV DI, ES ;пересылка слова

MOV СВ, Х ;пересылка байта

MOV DX, AL ;ошибка (DX – слово, AL – байт)

MOV ВН, 300 ;ошибка (ВН – байт,

; а 300 не может быть байтом)

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

Оператор указания типа (PTR)

Рассмотрим ситуацию, когда по операндам команды MOV нельзя определить размер пересылаемой величины.

Забегая вперед, следует отметить, что если некоторый адрес А надо модифицировать, скажем, по регистру SI, то в языке ассемблера это записывается так: A[SI]. Исполнительный адрес в этом случае вычисляется по формуле Аисп = А+[SI]. В частности, при А=0 эта запись имеет вид [SI] (0 не указывается) и задает исполнительный адрес, равный содержимому регистра SI: Aиcп = [SI].

С учетом этого рассмотрим такую задачу. Пусть в регистре 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)+1. Од­нако в данном конкретном случае старшинство операторов не иг­рает никакой роли, т. к. обе записи – BYTE PTR (Z+1) и BYTE PTR Z+1 – эквивалентны по смыслу: в первом случае мы сначала увеличиваем адрес Z на 1 и только затем сообщаем, что Z+1 надо рассматривать как адрес байта, а во втором случае мы сначала сообщаем, что Z – это адрес байта, и лишь затем увеличиваем его на 1 (при этом «байтовость» адреса сохраняется).

Итак, оператор PTR используется в следующих ситуациях: когда типы операндов команд неизвестны и потому надо указать явно тип одного из операндов, и когда нас не устраивает тип, приписанный имени при его описании, и потому мы должны указать нужный нам тип.

Команда XCHG

В машинных программах приходится довольно часто переставлять местами какие-то две величины, и хотя такую перестановку можно реализовать только с помощью команды MOV, в ПК введена специальная команда для этого:

Перестановка (exchange): XCHG op1,op2

Эта команда меняет местами значения своих операндов (они должны быть ли­бо байтами, либо словами): op1 <=> ор2. Флаги при этом не меняются.

Пример:

MOV АХ, 62 ;АХ=62

MOV SI, 135 ;SI=135

XCHG AX, SI ;AX=135, SI=62

Допустимые типы операндов команды XCHG приведены в табл. 5.

Таблица 5

Допустимые комбинации операндов команды XCHG

op1

ор2

r8

r8, m8

перестановка байтов

m8

r8

r16

r16, m16

перестановка слов

m16

r16

Как видно, не допускается перестановка содержимого двух ячеек памяти. Если надо сделать такую перестановку, то это реализуется через какой-нибудь регистр. Например, поменять местами значения байтовых переменных X и Y можно так:

MOV AL,X ;AL=X

XCHG AL,Y ;AL=Y, Y=X

MOV X,AL ;X=Y (исходное значение)