Команды умножения и деления




Команды умножения

Если сложение и вычитание беззнаковых и знаковых чисел производятся по одним и тем же алгоритмам, то умножение чисел этих двух классов выполняется по разным алгоритмам, в связи с чем в ПК имеются две команды умножения:

Умножение целых без знака (multiply): MUL op Умножение целых со знаком (integer multiply): IMUL op

В остальном эти команды действуют одинаково:

Умножение байтов: X:=AL * op (op: r8, m8)

Умножение слов: (DX,AX):= АХ * ор (ор: r16, m16)

Операнд ор, указываемый в команде, - это лишь один из сомножителей; он может находиться в регистре или в памяти, но не может быть непосредственным операндом. Местонахождение другого сомножителя фиксировано и потому в команде не указывается явно: при умножении байтов он берется из регистра AL, а при умножении слов - из регистра АХ.

Местонахождение результата умножения также заранее известно и потому в команде явно не указывается. При этом под результат отводится в два раза больше места, чем под сомножители. Это связано с тем, что умножение n-значных чисел в общем случае дает произведение из 2n цифр, и с желанием сохранить все цифры произведения. При умножении байтов результат имеет размер слова и записывается в весь регистр АХ (в АН - старшие цифры произведения, в AL - младшие), а при умножении слов результат имеет размер двойного слова и записывается в два регистра - в регистр DX заносятся старшие цифры произведения, а в регистр АХ - младшие цифры.

Примеры:

Итак, команды умножения выдают результат в удвоенном формате. Это не всегда удобно: то мы работали с числами-байтами, а тут приходится переходить на обработку чисел-слов. В то же время далеко не всегда величина произведения столь велика, что ему нужен удвоенный формат; например, в первой и третьей из наших команд умножения для результата вполне достаточно было бы обычного формата. Поэтому важно знать, действительно ли произведению нужен двойной формат или ему достаточно и одинарного формата. Иногда об этом известно заранее (мы заранее знаем, что перемножаются небольшие числа), но иногда это можно установить только после умножения. В последнем случае вопрос о том, умещается ли результат умножения в формат сомножителей или нет, решается с помощью анализа флагов переноса CF и переполнения OF, которые в обеих командах умножения меняются синхронно и по следующему правилу:

CF=OF=1 - если произведение занимает двойной формат

CF=OF=0 - если произведению достаточен формат сомножителей

При CF=0 (одновременно и OF=0) можно считать, что произведение байтов занимает только регистр AL, а произведение слов - только регистр АХ, и дальше можно работать только с этими регистрами. Но если CF=1, то дальше приходится работать с произведением как с числом удвоенного формата.

Литература:

1. Пильщиков В.Н. Программирование на языке ассемблера IBM РС. – М.: «Диалог - МИФИ», 1999 – 288с.

2. Использование Turbo Assembler при разработке программ. – Киев: «Диалектика», 1994. – 288с.

 

Лекция 8.

Тема: ПЕРЕХОДЫ.

Содержание: Безусловный переход. Оператор SHORT. Прямой переход. Косвенный переход.

Краткое содержание лекции:

Безусловный переход. Оператор SHORT

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

Переходы бывают условными и безусловными. Если переход делается только тогда, когда выполнено некоторое условие, то такой переход называется условным, а если он делается независимо от каких-либо условий, то это безусловный переход.

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

Изучение команд перехода начнем с безусловного перехода,

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

Безусловный переход (jump): JMP op

Здесь операнд тем или иным способом указывает адрес перехода, т. е. адрес команды, которая должна быть выполнена следующей. Рассмотрим, что это за способы.

Прямой переход

В данном случае в качестве ор указывается метка той команды, на которую надо передать управление:

JMP <метка>

Пример:

JMP L;следующей будет выполняться команда с меткой L

L: MOV АХ, О

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

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

Напомним, что в ПК имеется регистр IP (указатель команд), в котором всегда хранится адрес той команды, что должна выполняться следующей. Поэтому сделать переход по адресу - значит записать данный адрес в регистр IP. Казалось бы, в команде перехода должен задаваться именно адрес перехода. Однако в ПК машинная команда прямого перехода устроена так, что в ней указывается не этот адрес, а разность между ним и адресом команды перехода. Другими словами, отсчет адреса перехода ведется от команды перехода, в связи с чем такой переход называют относительным. Действие же самой команды перехода заключается в прибавлении этой величины к текущему значению регистра IP.

(Замечание. Если говорить точнее, то относительный адрес перехода отсчитывается не от самой команды перехода, а от следующей за ней команды. Дело в том, что в ПК выполнение любой команды начинается с засылки в регистр IP адрес следующей по порядку команды и только затем выполняется собственно команда. Поэтому в команде перехода относительный адрес будет прибавляться к значению IP, которое уже указывает на следующую команду, а потому от этой следующей команды и приходится отсчитывать относительный адрес перехода. Однако в дальнейшем мы не будем обращать внимание на эту деталь, поскольку для ЯА она в общем-то не существенна.)

В ПК имеются две машинные команды прямого перехода, в одной из которых относительный адрес перехода задается в виде байта (такая команда называется коротким переходом), а в другой - в виде слова (это команда длинного перехода). В каждой из этих команд операнд рассматривается как целое со знаком (от -128 до +127 или от -215 до 215-1), поэтому при сложении его с IP значение этого регистра может как увеличиться, так и уменьшиться, т. е. возможен и переход вперед, и переход назад.

Естественно, возникает вопрос: в чем выгода от того, что указывается не сам адрес перехода, а его расстояние от команды перехода? Это объясняется стремлением создателей ПК сэкономить память, занимаемую командами перехода. Если в команде указывать сам адрес перехода, то на него придется всегда отводить слово (2 байта). Но практика показывает, что в большинстве случаев переходы делаются на команды, расположенные недалеко от команд перехода, поэтому разность между адресами этих двух команд, как правило, небольшая, для нее достаточно и байта. Учитывая это, создатели ПК сделали так, чтобы именно эта разность и указывалась в командах перехода, чтобы большинство команд перехода можно было сделать на один байт короче.

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

Более того, в ЯА в команде JMP вообще нельзя указывать непосредственный операнд, а можно указывать только метку. Так что, программируя на ЯА, можно считать, что в команде перехода указывается сам адрес перехода.

Оператор SHORT

Зачем же мы тогда обо всем этом рассказали? Дело в следующем. Встречая символьную команду перехода с меткой, ассемблер вычисляет разность между адресом этой метки и адресом самой команды перехода и оценивает величину этой разности. Если она небольшая, укладывается в байт, тогда ассемблер формирует машинную команду короткого перехода (она занимает 2 байта), а если разность большая - формирует команду длинного перехода (3 байта). Однако сделать такой выбор ассемблер может, только если метка была описана до команды перехода, т. е. если эта метка является ссылкой назад. Поскольку ассемблер просматривает текст программы сверху вниз, то, дойдя до команды перехода, он будет знать как адрес метки, так и адрес команды перехода и потому сможет вычислить разность между этими адресами, сможет оценить ее величину. Но если в команде перехода указана метка "вперед", то, встретив команду перехода, ассемблер еще не будет знать адреса метки и потому не сможет оценить величину разности, не сможет определить, какой здесь переход - короткий или близкий. Об этом он узнает позже, когда дойдет до описания метки, а пока он "на всякий случай" формирует команду длинного перехода - так он не ошибется.

Однако такой способ трансляции переходов вперед не всегда выгоден: если метка окажется близко расположенной, то мы потеряем байт на этом. Так вот, если мы заранее знаем, что переход вперед будет коротким, и если нам жалко терять байт на команде перехода, то мы должны предупредить ассемблер о том, что переход будет коротким. Для этого в ЯА введен оператор SHORT (короткий), который ставится в команде перехода перед меткой. В этом случае ассемблер сформирует машинную команду короткого перехода:

JMP L; длинный переход (3 байта)

JMP SHORT L; короткий переход (2 байта)

L: …

Отметим, что если мы указали оператор SHORT, но при этом ошиблись (переход на самом деле оказался длинным), тогда ассемблер зафиксирует ошибку. Так что пользоваться оператором SHORT надо осторожно, только если мы твердо уверены, что переход будет коротким. Обычно этот оператор используют лишь тогда, когда сокращение памяти, занимаемой программой, - это вопрос жизни или смерти.

Оператор SHORT можно ставить и перед меткой, описанной раньше, т. е. при переходе назад, однако в этом случае ассемблер проигнорирует этот оператор, поскольку он и так будет знать "расстояние" перехода.

Косвенный переход

Теперь рассмотрим другую разновидность безусловного перехода - косвенный переход. В этом случае в команде перехода указывается не сам адрес перехода, а то место, где находится этот адрес. Таким местом может быть регистр общего назначения или слово памяти:

JMP r16 или JMP m16

В этих командах берется содержимое указанного регистра или слова памяти, оно рассматривается как адрес некоторой команды программы и именно по этому адресу делается переход. Причем этот адрес рассматривается как "настоящий", а не отсчитанный от команды перехода.

Примеры ([х] - содержимое ячейки или регистра х):

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

А сейчас рассмотрим одну проблему, с которой сталкивается ассемблер при трансляции команд безусловного перехода. Возьмем команду JMP Z, где Z - некоторое имя (но не имя регистра). Что это такое - прямой переход по метке Z или косвенный переход по адресу из ячейки Z? Если имя Z описано до этой команды, то здесь проблемы нет: если именем Z помечена команда (рис. а), то это переход по метке, а если имя Z описано в директиве DW (рис. б), то это косвенный переход.

Но если Z - ссылка вперед, т.е. это имя описывается позже (рис. в), тогда ассемблер не будет знать, какой здесь переход. Чтобы снять эту неоднозначность, в ЯА принято следующее соглашение: в подобной ситуации ассемблер всегда считает, что Z - метка, и потому всегда формирует команду прямого перехода по этой метке (причем команду длинного перехода). Если же затем обнаружится, что Z - не метка, то будет зафиксирована ошибка.

Так вот, если нас это правило не устраивает, если нам нужен косвенный переход, то мы обязаны сообщить об этом ассемблеру. Для этого используется уже известный нам оператор PTR: вместо просто имени Z надо записать конструкцию WORD PTR Z, которой мы сообщаем ассемблеру, чтобы он рассматривал Z как имя переменной размером в слово, чтобы он формировал машинную команду косвенного перехода.

Итак, при переходах вперед имеем следующие случаи:

 

Литература:

1. Абель П. Язык Ассемблера для IBM РС и программирования: Пер. с англ. – М.: Высшая школа, 1992. – 477с.

2. Нортон П., Сохуэ Д. Язык ассемблера для IBM РС: Пер. с англ. – М.: Издательство «Компьютер»; Фиансы и статистика, 1992. – 352 с.

3. Пильщиков В.Н. Программирование на языке ассемблера IBM РС. – М.: «Диалог - МИФИ», 1999 – 288с.

 

Лекция 9, 10.

Тема: Команды сравнения и условного перехода. Примеры программирования разветвляющихся алгоритмов.

Содержание: Команды сравнения. Команды условного перехода.

Краткое содержание лекции:

Если переход осуществляется только при выполнении некоторого условия и не осуществляется в противном случае, то такой переход называется условным. Условный переход обычно реализуется в два шага: сначало сравниваются некоторые величины, в результате чего соответствующим образом формируются флаги (ZF, SF и т. д.), а затем выполняется собственно условный переход в зависимости от значений флагов. Поэтому мы сейчас рассмотрим и команду сравнения, и команды условного перехода.

Сравнение (compare): CMP opl, op2

Эта команда эквивалентна команде SUB opl, op2 за одним исключением: вычисленная разность opl - op2 никуда не записывается. Поэтому единственный и главный эффект от команды сравнения - это установка флагов, характеризующих полученную разность, или, что то же самое, характеризующих сравниваемые величины ор! и ор2. Как формируются флаги при вычитании, мы уже рассматривали(см. разд. 3.3), поэтому повторяться не будем.

Что же касается команд условного перехода, то их в ПК достаточно много, но в ЯА они все записываются единообразно:

Jxx <метка>

где операнд указывает метку той команды программы, на которую надо сделать переход в случае выполнения некоторого условия, а мнемокод начинается буквой J (от jump), за которой следует одна или несколько букв, в сокращенном виде описывающих это условие.

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

В первую группу входят команды, которые ставятся после команды сравнения. В их мнемокодах с помощью определенных букв описывается тот исход сравнения, при котором надо делать переход. Это такие буквы:

  • Е - equal (равно)
  • N - not (не, отрицание)
  • G - greater (больше) - для чисел со знаком
  • L - less (меньше) - для чисел со знаком
  • А - above (выше, больше) - для чисел без знака
  • В - below (ниже, меньше) - для чисел без знака

Как видно, для условий "меньше" и "больше" введены две системы обозначений. Это связано с тем, что после сравнения чисел со знаком и сравнения чисел 1 без знака надо реагировать на разные значения флагов. Отметим, что одна и та же команда условного перехода может иметь в ЯА несколько названий-синонимов. Это объясняется тем, что одно и то же условие перехода может быть сформулировано по-разному. Например, условие "меньше" -это в то же время и условие "не верно, что больше или равно", поэтому переход по меньше для знаковых чисел обозначается и как JL, и как JNGE. Какое из этих названий-синонимов использовать - это личное дело автора программы. Теперь приведем названия всех команд условного перехода, используемых после команды сравнения (через косую черту указаны названия-синонимы):

(Объясним, к примеру, почему в команде условного перехода "по меньше" для знаковых чисел (JL) проверяется соотношение OF<>SF. Если в команде CMP opl,op2 сравниваемые числа трактуются как знаковые, тогда возможны две комбинации флагов, соответствующие условию opl < op2. Во-первых, если при вычитании opl - op2 не было переполнения мантиссы (OF=0), тогда флаг SF фиксирует настоящий знак разности opl - op2 и потому SF = 1 означает, что opl - op2 < 0, т.е. opl < op2. Во-вторых, если при вычитании произошло переполнение мантиссы (OF=1), тогда результатом команды будет число с противоположным знаком, чем у настоящей разности, и поскольку флаг SF фиксирует не знак настоящей разности, а знак результата команды, то условие SF = 0 означает, что у искаженного результата знак положителен, а значит, 1 у настоящей разности знак отрицателен, т. е. opl < op2. Итак, условию opl < op2 соответствует либо OF=0 и SF=1, либо OF=1 и SF=0, что можно записать более коротко: OF<>SF. Именно это условие и указано в таблице для команды JL.)

Пример. Пусть X, Y и Z - переменные размером в слово. Требуется записать в Z максимальное из чисел X и Y. Решение этой задачи различно для чисел со знаком (см. слева) и для чисел без знака (см. справа), т. к. приходится использовать разные команды условного перехода:

Во вторую группу команд условного перехода входят те, которые ставятся после команд, отличных от команды сравнения, и которые реагируют на то или иное значение какого-нибудь определенного флага. В мнемокодах этих команд указывается первая буква проверяемого флага, если переход должен быть выполнен при значении 1 у флага, либо эта буква указывается с буквой N (not), если переход надо сделать при нулевом значении флага:

(Замечание. Легко заметить, что следующие пары мнемокодов эквивалентны: JE и JZ, JNE и JNZ, JB и JC, JNB и JNC.) Пример. Пусть А, В и С - беззнаковые байтовые переменные. Требуется вычислить С=А*А+В, но если ответ превосходит размер байта, тогда надо передать управление на метку ERROR. Возможное решение этой задачи:

И, наконец, в третью группу входит только одна команда условного перехода, проверяющая не флаги, а значение регистра СХ:

JCX2 <метка>

Действие команды JCXZ (jump if CX is zero) можно описать так:

if CX*0 then goto <метка>

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

Отметим общую особенность команд условного перехода: все они осуществляют только короткий переход, т. е. с их помощью можно передать управление не далее чем на 127-128 байтов вперед или назад. Это примерно 30-40 команд (в среднем одна команда ПК занимает 3-4 байта). Дело в том, что в ПК все машинные команды условного перехода имеют вид КОП i8 и реализуют короткий относительный переход: IP:=IP+i8, а команд с операндом в слово (i16) нет. Это объясняется тем, что в большинстве случаев как раз и нужны такие короткие переходы, а с другой стороны, для команд длинных условных переходов в ПК попросту не хватило кодов операций.

Естественно, возникает вопрос: а как в этих условиях осуществлять длинные условные переходы, на расстояние более 127 байтов от команды перехода? Здесь надо привлекать команду длинного безусловного перехода. Например, при "далекой" метке М оператор

if AX=BX then goto M

следует реализовывать так:

if AX>BX then goto L; goto M; L:...

На ЯА это записывается следующим образом:

CMP AX,BX

JNE L JMP М

Получается очень коряво, то иного варианта нет.

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



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: