
5.2. Команды и последовательности команд
Задачи, выполняемые компьютерной программой, состоят из последовательности небольших шагов, таких как сложение двух чисел, проверка определенного условия, чтение символа с клавиатуры или вывод такового на экран. Следовательно, компьютер должен поддерживать команды для выполнения следующих четырех типов операций:
пересылка данных между памятью и регистрами процессора;
арифметические и логические операции с данными;
управление последовательностью выполнения программ и их команд;
операции ввода-вывода.
Мы начнем со знакомства с командами первых двух типов. Но прежде чем приступать к их анализу, вам следует ознакомиться со специальной нотацией, используемой для описания операций с регистрами.
Нотация для описания операций с регистрами
Нам необходим способ для описания операций пересылки информации из одного места в компьютере в другое. Приемниками и источниками информации могут быть память, регистры процессора и регистры подсистемы ввода-вывода. Как правило, мы будем идентифицировать место хранения информации символическим именем, представляющим его аппаратный двоичный адрес. Например, именами для адресов в памяти могут быть LOC, PLACE, A, VAR2; регистры процессора могут иметь имена R0, R5, AX, DX, а регистры ввода-вывода — имена DATAIN, OUTSTATUS и т. д. Данные, хранящиеся по указанному адресу, обозначаются именем этого адреса, заключенным в квадратные скобки. Таким образом, выражение
R1 ← [LOC]
обозначает, что содержимое памяти по адресу LOC пересылается в регистр процессора R1.
В качестве еще одного примера рассмотрим операцию, которая складывает содержимое регистров R1 и R2 и помещает полученный результат в регистр R3. Это действие записывается так:
R3 ← [R1] + [R2]
Данный тип записи называется RTN (Register Transfer Notation — нотация регистровых передач). Обратите внимание, что в правой части выражения RTN всегда стоит значение, а в левой — имя того места в памяти, куда его следует поместить, заменив старое содержимое.
Нотация языка ассемблера
Для представления машинных команд и программ нам потребуется другой тип нотации. Для этой цели мы будем использовать формат языка ассемблера. Например, команда, выполняющая первую из указанных в предыдущем разделе операций, то есть пересылку данных из памяти по адресу LOC в регистр процессора AX, записывается так:
Move AX,LOC
После выполнения этой команды содержимое памяти по адресу LOC остается неизменным, а старое содержимое регистра AX перезаписывается.
Во втором примере мы складывали два числа, содержащихся в регистрах процессора R1 и R2, и помещали результат в регистр R3. На языке ассемблера эта операция записывается так:
Add R3,R1,R2
Базовые типы команд
Сложение двух чисел относится к числу фундаментальных операций любого компьютера. Инструкция
С=А+В
в программе на языке высокого уровня — это команда компьютеру сложить текущие значения двух переменных, А и В, и присвоить их сумму третьей переменной, С. При компиляции программы, содержащей эту инструкцию, переменным А, В и С назначаются конкретные адреса памяти. Содержимое памяти по этим адресам представляет значения трех переменных. Поэтому приведенная выше инструкция на языке высокого уровня требует выполнения компьютером следующего действия:
С ← [А] + [В]
Для выполнения этого действия содержимое памяти по адресам А и В должно быть переслано в процессор, где будет вычислена сумма. Полученная сумма должна быть отправлена обратно в память и записана по адресу С.
Для начала давайте предположим, что это действие выполняется посредством одной машинной команды. Эта команда содержит адреса трех операндов: А, В и С. Символически такую трехадресную команду можно представить как
Add C,A,B
Команда Add образована от слова Addition (сложение). Операнды А и В называются исходными операндами, а С — операндом назначения или результирующим операндом. В общем случае команда этого типа имеет такой формат:
Операция МестоНазначения,Источник1,Источник2
Если для указания адреса одного операнда в памяти необходимо k бит, в закодированной форме данной инструкции для адресов должно быть отведено 3k бит и еще сколько-то бит для кода самой операции Add. В случае современного процессора с 32-разрядным адресным пространством трехадресная команда слишком громоздка для одного слова разумной длины. Поэтому для представления команд такого типа обычно используется формат длиной в несколько слов.
Для выполнения этой же задачи в качестве альтернативы можно использовать несколько более простых команд, с одним-двумя операндами. Предположим, что процессором поддерживаются двухадресные команды в виде:
Операция МестоНазначения,Источник
Команда Add такого типа
Add A,B
будет выполнять операцию A ← [А] + [В]. После вычисления суммы результат будет переслан обратно в память и сохранен по адресу A с заменой исходных данных, хранившихся по этому адресу. Это означает, что A является и исходным, и результирующим операндом команды.
В случае двухадресных команд для решения нашей задачи одной двухадресной команды недостаточно. Потребуется еще одна двухадресная команда, которая копирует значение из одного места памяти в другое. Вот она:
Mov С,A
Эта команда выполняет операцию C ← [A], оставляя содержимое памяти по адресу A неизменным. Команда образована от слова Move, означающего «перемещение», использовано здесь не совсем точно: более уместно было бы назвать эту команду Copy (копирование). Однако именно это, первое, название команды давно закрепилось в компьютерном мире. Операция С ← [А] + [В] может быть выполнена помощью двух команд и в такой последовательности:
Mov C,B
Add C,A
Во всех приведенных выше командах первым задается операнд назначения, а исходные операнды после него. Этот порядок характерен для выражений на языке ассемблера, используемых в машинных командах многих компьютеров, например, персональные компьютеры на базе процессора Intel.. Но существует достаточно много компьютеров, в которых порядок операндов обратный. К сожалению, единого соглашения, принятого всеми производителями, не существует. Даже в языке ассемблера одного компьютера могут использоваться команды с разным порядком операндов.
В процессоре Intel только двухадресные и одноадресные команды и результирующий операнд всегда задается первым.
Следует отметить одно очень важное обстоятельство. Если делать арифметические операции с данными, которые находятся в памяти, то время выполнения таких операций будет очень большим, а процессор будет работать с остановками на то время, пока не отработает память по пересылке исходных данных из памяти и результата в память. Поэтому в некоторых современных процессорах арифметические операции выполняются с данными, которые находятся в регистрах процессора, а для пересылки данных в регистры и назад в память используются команды Load и Store. Команда Mov тоже может использоваться для пересылки данных из памяти в регистр процессора и из регистра процессора в память, поскольку направление пересылки в ней задается просто порядком операндов. Поэтому команда
Mov A,Ri означает то же, что и команда
Store A,Ri,
а команда
Mov Ri,A — то же, что и команда
Load Ri,A
В процессоре Intel нет команд Load и Store, вместо них используется команда Mov.
В тех процессорах, которые производят арифметические операции только над содержимым регистров, задача С = А + В может быть выполнена путем применения такой последовательности команд:
Mov Ri,A
Mov Rj,B
Add Ri,Rj
Mov C,Ri,
Если процессор позволяет использовать операнды, из которых один находится в памяти, а все остальные в регистрах (например Intel), то эту же задачу можно реализовать и по-другому:
Mov Ri,A
Add Ri,B
Mov C,Ri
Время выполнения конкретной задачи зависит от того, насколько быстро команды пересылаются из памяти в процессор и насколько быстро осуществляется доступ к операндам этих команд. Операции, в которых участвует память, выполняются гораздо медленнее, чем операции с участием регистров. Поэтому значительного ускорения работы можно добиться в тех случаях, когда несколько операций подряд выполняются над хранящимися в регистрах данными без обращения к памяти. При компиляции программ, написанных на языке высокого уровня, в машинный язык важно минимизировать частоту перемещений данных между памятью и регистрами процессора.
Итак, теперь вы имеете представление об одно-, двух- и трехадресных командах и знаете о возможности использовать команды, в которых все операнды задаются неявно. Такие команды применяются в машинах, хранящих операнды в структуре, которая называется стеком. А сами команды называются ноль-адресными. Концепция стека вводится в разделе 5.10.
Выполнение команд и линейный код
В предыдущем разделе мы рассматривали форматы команд на примере операции
С ← [А] + [В]. На рис. 5.3. показан фрагмент программы, выполняющей эту задачу, в том виде, в каком он представлен в памяти компьютера. Мы предполагаем, что компьютер поддерживает только одноадресные команды и что его процессор содержит много регистров. Длина слова составляет 32 разряда, а память адресуется побайтово. Три команды программы расположены в следующих друг за другом словах, начиная с адреса i. Поскольку каждая команда занимает 4 байта, вторая и третья команды начинаются по адресам i + 4 и i + 8. Для того чтобы упростить задачу, мы также предполагаем, что полный адрес целиком задается в команде, занимающей одно слово, хотя при таких размерах адресного пространства и той длине слова, которая поддерживается современными процессорами, подобное, как правило, невозможно.
Рассмотрим процесс выполнения этой программы подробно. Процессор содержит регистр, называемый счетчиком команд (Program Counter, PC), в котором хранится адрес следующей команды. Чтобы программа начала выполняться, адрес ее первой команды (в нашем примере i) должен быть помещен в регистр PC. Затем управляющие схемы процессора будут использовать информацию из этого регистра для выполнения последовательно расположенных в памяти команд, по одной за раз, в порядке увеличения их адресов. Этот процесс называется последовательным выполнением программы, а сама программа, представленная в памяти как список команд, называется линейной. В ходе выполнения каждой команды адрес, хранящийся в регистре PC, увеличивается на 4, чтобы этот регистр указывал на следующую команду программы. Таким образом, после выполнения команды Move, расположенной по адресу i + 8, регистр PC будет содержать значение i + 12, указывающее на первую команду следующего фрагмента программы.
Адрес памяти Содержимое памяти