ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 24.12.2021
Просмотров: 12161
Скачиваний: 10

Типы команд 383
команд вводится команда NEG. Важно понимать разницу между арифметической
операцией NEG и логической операцией NOT. Операция NEG производит
аддитив-
ную инверсию
числа (такое число, сумма которого с изначальным числом дает 0).
Операция NOT просто инвертирует все биты в слове. Эти операции очень похожи,
а для системы, в которой используется представление в обратном двоичном коде,
они идентичны. (В арифметике дополнительных кодов для выполнения команды
NEG сначала инвертируются все биты, а затем к полученному результату прибав-
ляется 1.)
Унарные и бинарные операции часто объединяются в группы по функциям,
которые они выполняют, а вовсе не по числу операндов. В первую группу входят
арифметические операции, в том числе операция отрицания. Во вторую группу
входят логические операции и операции сдвига, поскольку эти две категории очень
часто используются вместе для извлечения данных.
Сравнения и условные переходы
Практически все программы должны проверять свои данные и на основе результа-
тов изменять последовательность команд, которые нужно выполнить. Рассмотрим
функцию квадратного корня Ох. Если число х отрицательное, процедура сообщает
об ошибке; если число положительное, процедура вычисляет квадратный корень.
Функция
sqrt
должна проверять х, а затем совершать переход в зависимости от
того, положительно число х или отрицательно.
Это можно сделать с помощью специальных команд условного перехода, кото-
рые проверяют какое-либо условие и совершают переход в определенный адрес
памяти, если условие выполнено. Иногда определенный бит в команде указывает,
нужно ли осуществлять переход в случае выполнения условия или в случае не-
выполнения условия соответственно. Часто целевой адрес является не абсолют-
ным, а относительным (он связан с текущей командой).
Самое распространенное условие, которое нужно проверить, — равен ли опре-
деленный бит нулю или нет. Если команда проверяет знаковый бит числа и совер-
шает переход к метке (LABEL) при условии, что бит равен 1, то если число было
отрицательным, будут выполняться те утверждения, которые начинаются с метки
LABEL, а если число было положительным или было равно 0, то будут выполнять-
ся те утверждения, которые следуют за условным переходом.
Во многих машинах содержатся биты кода условия, которые указывают на осо-
бые условия. Например, там может быть бит переполнения, который принимает
значение 1 всякий раз, когда арифметическая операция выдает неправильный ре-
зультат. Проверяя этот бит, мы проверяем выполнение предыдущей арифмети-
ческой операции, и если произошла ошибка, то запускается программа обработки
ошибок.
В некоторых процессорах есть специальный разряд (бит) переноса, который
принимает значение 1, если происходит перенос из самого левого бита (например,
при сложении двух отрицательных чисел). Бит переноса нельзя путать с битом
переполнения. Проверка бита переноса необходима для вычислений с повышен-
ной точностью (то есть когда целое число представлено двумя или более словами).

384 Глава 5. Уровень архитектуры команд
Проверка на ноль очень важна при выполнении циклов и в некоторых других
случаях. Если бы все команды условного перехода проверяли только 1 бит, то то-
гда для проверки определенного слова на 0 нужно было бы отдельно проверять
каждый бит, чтобы убедиться, что ни один бит не равен 1. Чтобы избежать подоб-
ной ситуации, во многие машины включается команда, которая должна проверять
слово и осуществлять переход, если оно равно 0. Конечно же, это решение просто
перекладывает ответственность на микроархитектуру. На практике аппаратное
обеспечение обычно содержит регистр, все биты которого соединяются операцией
ИЛИ, чтобы выдать на выходе один бит, по которому можно определить, содер-
жит ли регистр биты, равные 1. Бит Z на рис. 4.1 обычно вычисляется следующим
образом: сначала все выходные биты АЛУ соединяются операцией ИЛИ, а затем
полученный результат инвертируется.
Операция сравнения слов или символов очень важна, например, при сортиров-
ке. Чтобы произвести сравнение, требуется три адреса: два нужны для элементов
данных, а в третий адрес будет совершаться переход в случае выполнения условия.
В тех компьютерах, где форматы команд позволяют содержать три адреса в команде,
проблем не возникает. Но если такие форматы не предусмотрены, нужно что-то
сделать, чтобы обойти эту проблему.
Одно из возможных решений — ввести команду, которая выполняет сравнение
и записывает результат в один или несколько битов условия. Следующая команда
может проверить биты условия и совершить переход, если два сравниваемых зна-
чения были равны, или неравны, или первое из них было больше второго и т. д.
Такой подход применяется в Pentium II и UltraSPARC II.
В сравнении двух чисел есть некоторые тонкости. Сравнение — это не такая
простая операция, как вычитание. Если очень большое положительно число срав-
нивается с очень большим отрицательным числом, операция вычитания приведет
к переполнению, поскольку результат вычитания не может быть представлен. Тем
не менее команда сравнения должна определить, удовлетворено ли условие, и воз-
вратить правильный ответ. При сравнении не должно быть переполнений.
Кроме того, при сравнении чисел нужно решить, считаются ли числа числами
со знаком или числами без знака. Трехбитные бинарные числа можно упорядо-
чить двумя способами. От самого маленького к самому большому:
Без знака Со знаком
000 100 (самое маленькое)
001 101
010 ПО
011 111
100 000
101 001
НО 010
111 011 (самое большое)
В колонке слева приведены положительные числа от 0 до 7 по возрастанию.
В колонке справа показаны целые числа со знаком от -4 до +3 в дополнительном
двоичном коде. Ответ на вопрос: «Какое число больше: 011 или 100?» зависит от
того, считаются ли числа числами со знаком. В большинстве архитектур есть ко-
манды для обращения с обоими типами упорядочения.

Типы команд 385
Команды вызова процедур
Процедура — это группа команд, которая выполняет определенную задачу и кото-
рую можно вызвать из нескольких мест программы. Вместо термина процедура
часто используется термин
подпрограмма,
особенно когда речь идет о программах
на языке ассемблера. Когда процедура закончила задачу, она должна вернуться к
соответствующему оператору. Следовательно, адрес возврата должен как-то пере-
даваться процедуре или сохраняться где-либо таким образом, чтобы можно было
определить местонахождение после завершения задачи.
Адрес возврата может помещаться в одном из трех мест: в памяти, в регистре
или в стеке. Самое худшее решение — поместить этот адрес в одну фиксирован-
ную ячейку памяти. Тогда если процедура будет вызывать другую процедуру, вто-
рой вызов приведет к потере первого адреса возврата.
Более удачное решение — сохранить адрес возврата в первом слове процедуры.
Тогда первой выполняемой командой будет второе слово процедуры. После завер-
шения процедуры происходит переход к первому слову, а если аппаратное обеспе-
чение в первом слове наряду с адресом возврата дает код операции, то происходит
непосредственный переход к этой операции. Процедура может вызывать другие
процедуры, поскольку в каждой процедуре имеется пространство для одного адре-
са возврата. Но если процедура вызывает сама себя, эта схема не работает, поскольку
первый адрес возврата будет уничтожен вторым вызовом. Способность процеду-
ры вызывать саму себя, называемая
рекурсией,
очень важна и для теоретиков, и
для практиков. Более того, если процедура А вызывает процедуру В, процедура В
вызывает процедуру С, а процедура С вызывает процедуру А (непосредственная
или цепочечная рекурсия), эта схема сохранения адреса возврата также не работает.
Еще более удачное решение — помещать адрес возврата в регистр. Если про-
цедура рекурсивна, ей придется помещать адрес возврата в другое место каждый
раз, когда она вызывается.
Самое лучшее решение — поместить адрес возврата в стек. Когда процедура
завершена, она выталкивает адрес возврата из стека. При такой форме вызова про-
цедур рекурсия не порождает никаких проблем; адрес возврата будет автоматичес-
ки сохраняться таким образом, чтобы избежать уничтожения предыдущего адреса
возврата. Мы рассматривали такой способ сохранения адреса возврата в машине
ПУМ(см.рис.4.10).
Управление циклом
Часто возникает необходимость выполнять некоторую группу команд фиксиро-
ванное количество раз, поэтому некоторые машины содержат команды для облег-
чения этого процесса. Все эти схемы содержат счетчик, который увеличивается
или уменьшается на какую-либо константу каждый раз при выполнении цикла.
Кроме того, этот счетчик каждый раз проверяется. При выполнении определенно-
го условия цикл завершается.
Определенная процедура запускает счетчик вне цикла и затем сразу начинает
выполнение цикла. Последняя команда цикла обновляет счетчик, и если условие
завершения цикла еще не выполнено, то происходит возврат к первой команде
цикла. Если условие выполнено, цикл завершается и начинается выполнение ко-

386
Глава 5. Уровень архитектуры команд
манды, идущей сразу после цикла. Цикл такого типа с проверкой в начале представ-
лен в листинге 5.3. (Мы не могли здесь использовать язык Java, поскольку в нем
нет оператора goto.)
Цикл такого типа всегда будет выполняться хотя бы один раз, даже если п<0.
Рассмотрим программу, которая поддерживает данные о персонале компании.
В определенном месте программа начинает считывать информацию о конкретном
работнике. Она считывает число п — количество детей у работника, и выполняет
цикл п раз, по одному разу на каждого ребенка. Она считывает его имя, пол и дату
рождения, так что компания может послать ему или ей подарок. Если у работника
нет детей, п будет равно 0, но цикл все равно будет выполнен один раз, что даст
ошибочные результаты.
В листинге 5.4 представлен другой способ выполнения проверки, который дает
правильные результаты даже при п<0. Отметим, что если одна команда выполняет
и увеличение счетчика, и проверку условия, разработчики вынуждены выбирать
один из двух методов.
Листинг 5.3.
Цикл с проверкой в конце
Листинг 5.4.
Цикл с проверкой в начале
i=l: i=l:
L1: первый оператор: LI: if(i>n) goto L2:
первый оператор:
последний оператор:
1-1+1: последний оператор:
if (i<n) goto LI: i=i+l:
goto LI:
L2:
Рассмотрим программу, которую нужно произвести для следующего выражения:
for (i=0; i<n: 1++) {операторы}
Если у компилятора нет никакой информации о числе п, он должен применять
подход, приведенный в листинге 5.4, чтобы корректно обработать случай п<0. Од-
нако если компилятор может определить, что п>0 (например, узнав, как определе-
но п), он может использовать более удобный код, изложенный в листинге 5.3. Ко-
гда-то в стандарте языка FORTRAN требовалось, чтобы все циклы выполнялись
один раз. Это позволяло всегда порождать более эффективный код (листинг 5.3).
В 1977 году этот дефект был исправлен, поскольку даже приверженцы языка
FORTRAN начали осознавать, что плохо иметь оператор цикла с такой странной
семантикой, пусть он и позволяет экономить одну команду перехода на каждый цикл.
Команды ввода-вывода
Ни одна другая группа команд не различается настолько сильно в разных маши-
нах, как команды ввода-вывода. В современных персональных компьютерах ис-
пользуются три различные схемы ввода-вывода:
1. Программируемый ввод-вывод с активным ожиданием.
2. Ввод-вывод с управлением по прерываниям.
3. Ввод-вывод с прямым доступом к памяти.

Типы команд 387
Мы рассмотрим каждую из этих схем по очереди.
Самым простым методом ввода-вывода является программируемый ввод-
вывод, который часто используется в дешевых микропроцессорах, например во
встроенных системах или в таких системах, которые должны быстро реагировать
на внешние изменения (это системы, работающие в режиме реального времени).
Эти процессоры обычно имеют одну входную и одну выходную команды. Каждая
из этих команд выбирает одно из устройств ввода-вывода. Между фиксирован-
ным регистром в процессоре и выбранным устройством ввода-вывода передается
один символ. Процессор должен выполнять определенную последовательность
команд при каждом считывании и записи символа.
В качестве примера данного метода рассмотрим терминал с четырьмя 1-байт-
ными регистрами, как показано на рис. 5.19. Два регистра используются для ввода:
регистр состояния устройства и регистр данных. Два регистра используются для
вывода: тоже регистр состояния устройства и регистр данных. Каждый из них имеет
уникальный адрес. Если используется ввод-вывод с распределением памяти, все
4 регистра являются частью адресного пространства, и будут считываться и за-
писываться с помощью обычных команд. В противном случае для чтения и записи
регистров используются специальные команды ввода-вывода, например IN и OUT.
В обоих случаях ввод-вывод осуществляется путем передачи данных и информации
о состоянии устройства между центральным процессором и этими регистрами.
Готовность к получению
Наличный символ следующего символа
/ Состояние клавиатуры / Состояние дисплея
\
Разрешенное прерывание Разрешенное прерывание
Буфер клавиатуры Буфер дисплея
Полученный символ
С и м в о л
Д ™
в ы в о д а
на дисплеи
Рис. 5.19.
Регистры устройств для простого терминала
Регистр состояния клавиатуры содержит 2 бита, которые используются, и 6 би-
тов, которые не используются. Аппаратное обеспечение устанавливает самый ле-
вый бит на 1 всякий раз, когда появляется символ. Если программное обеспечение
ранее установило на 1 бит 6, то производится прерывание. В противном случае
прерывания не происходит. При программируемом вводе-выводе для получения
входных данных центральный процессор обычно находится в цикле, периодичес-
ки считывая регистр состояния клавиатуры, пока бит 7 не примет значение 1. Ко-
гда это случается, программное обеспечение считывает буферный регистр клавиа-
туры, чтобы получить символ. Считывание регистра данных вызывает установку
бита CHARACTER AVAILABLE (наличия символа) на 0.
Вывод осуществляется сходным образом. Чтобы написать символ на экране,
программное обеспечение сначала считывает регистр состояния дисплея, чтобы
узнать, установлен ли бит READY (бит готовности) на 1. Если он не установлен,