Команды формата RR

{toc_noshowall}Регистры играют огромную роль при программировании. При изучении двоичной целочисленной арифметики мы рассматривали использование регистров в. качестве накопителей. Вследствие своего относительного быстродействия регистры обычно используются для временного хранения некоторой часто используемой и, возможно, часто изменяемой информации, а также в качестве счетчиков, необходимых для организации циклических процессов. Как мы увидим в гл. 8, регистры служат и для выполнения операции с адресами. Любая нетривиальная программа использует в своей работе регистры.

Из предыдущего раздела нам известно, что в командах формата RRоба операнда находятся в регистрах общего назначения. В каждом из 16 таких регистров может храниться полное 32-разрядное слово. В большинстве случаев полное слово рассматривается как двоичное целое. Отрицательные числа задаются в виде дополнительных кодов. Так, например, в команде сложения формата RRслагаемыми являются 32-разрядные двоичные целые, хранящиеся в регистрах, указанных в поле операндов команды; результатом также является 32-разрядное целое.

Мы видели что шестнадцатеричная система удобна для краткой записи двоичной информации. Имейте в виду, что в последующем тексте, если нет специальных оговорок, любая информация об объектах уровня машинного языка, такая, как машинные команды, содержимое регистров и ячеек памяти, записывается в шестнадцатеричной форме.

С другой стороны, программа-ассемблер предоставляет возможность перевода любых чисел в шестнадцатеричную форму. Поэтому все числа в программе на языке ассемблера при отсутствии специальных оговорок рассматриваются как десятичные. Если нам, к примеру, понадобилось использовать регистр 14 в качестве операнда в программе на языке ассемблера, мы должны просто всюду писать 14 как ссылку на соответствующий регистр. Если мы хотим сделать то же самое, но на языке машины, те теперь вместо 14 нам придется писать Е. Аналогично если в некотором регистре хранится десятичное число 50, то в шестнадцатеричной форме записи рассматриваемое полное слово будет представлять собой 00000032.

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

Основной командой этой группы является команда LR(LOADREGISTER— ЗАГРУЗКА), которая может быть описана следующим образом:

Операция

Операнды

Имя

Результат выполнения

LR

R1.R2

Load Register

R1(R2)

Так мы будем описывать любую новую команду. В первой колонке указано мнемоническое обозначение операции, во второй — символическая форма задания операндов. Третья содержит название команды, четвертая — результат ее выполнения.

Результатом выполнения описанной команды является пересылка содержимого второго из указанных регистров в первый. Если говорить более подробно, то по команде LRпроизводится выборка содержимого регистра второго операнда (оно не изменяется в процессе выполнения команды) и запоминание полученного числа в регистре первого операнда, сопровождающееся уничтожением его исходного содержимого. Пусть регистры содержат

(5)=00000170

(10) = FFFFFFE0

Пусть также мы хотим переслать содержимое регистра 10 в регистр 5, используя команду LR. Из данного описания команды ясно, что регистр 5 нужно употребить в качестве регистра первого операнда, R1, а регистр 10— в качестве регистра второго операнда, R2. Команда, следовательно, будет выглядеть так:

LR5,10

В результате получится

(5) = FFFFFFE0

(10)=FFFFFFE0

Если же написать

LR10,5

то результат будет иной:

(5)=00000170

(10)=00000170

По команде LRинформация, хранящаяся в регистре второго операнда, копируется в регистр первого операнда.

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

LCRR1,R2 LoadComplementRegisterR1 — (R2)

По команде LCR(ЗАГРУЗКА ДОПОЛНЕНИЯ) вычисляется дополнительный код числа, хранящегося в регистре второго операнда, и запоминается в регистре первого операнда. Фактически в регистр результата попадает исходное число с обратным знаком. Таким образом, если

(5)=0000000А

(7)=36В42975

перед выполнением команды

LCR7,5

то после ее выполнения будем иметь

(5) = 0000000А

(7) = FFFFFFF6

Говоря более подробно, LCRпроизводит выборку содержимого по адресу второго операнда, вычисляет его дополнительный код и заносит результат в регистр первого операнда. Если мы хотим переслать число, хранящееся в регистре 13, с обратным знаком в регистр 1, следует только написать

LCR 1,13

LPR R1,R2 Load'Positive Register R1|(R2)|

Команда LPR(ЗАГРУЗКА ПОЛОЖИТЕЛЬНАЯ) производит засылку модуля числа, хранящегося в регистре второго операнда, в регистр первого операнда. Если число, хранящееся в регистре второго операнда, положительное, то команда LPRпересылает его без изменения, если, оно отрицательное, то пересылается дополнительный код. Пусть

(6) = 00011А6С

(8) = FFFFFFE3

Тогда после выполнения команды

LPR8,6

получится

(6)=00011А6С

(8)=00011А6С

Тот же результат был бы получен и при выполнении команды

LR8,6

Если бы, однако, команда была написана иначе, а именно

LPR6,8

то результат имел бы вид

(6) = 00 00001D

(8) = FFFFFFE3

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

LCR6,8

Иными словами, если число в регистре второго операнда положительное, то действие команды LPRэквивалентно действию команды LR, а если отрицательное, то результат ее выполнения ничем не отличается от результата выполнения команды LCR.

LNR R1,R2 Load Negative Register R1—|(R2)|

Команда LNR(ЗАГРУЗКА ОТРИЦАТЕЛЬНАЯ) производит перемещение модуля числа, хранящегося в регистре второго операнда в регистр первого со знаком минус. Если перед выполнением команды

LNR5,2

в регистрах хранились следующие числа:

(2)  = 00000100

(5) = FFFFFC63

то после ее выполнения они изменяются на следующие:

(2) = 00 00 0100

(5) = FFFFFF00

Модуль числа (2) со знаком минус находится теперь, в регистре 5.

Все команды пересылки, рассмотренные выше, обеспечивали пересылку информации из одних регистров в другие. Но как попадает в регистры исходная информация? Существует несколько способов первоначальной загрузки регистров. Одним из способов является использование операций ввода, рассматриваемых в разд. 4.3. Можно также использовать команды, обеспечивающие пересылку информации из памяти в регистры. Для загрузки в регистр R1 двоичного эквивален

та десятичного числа dнеобходимо написать

Это — особый случай использования команды загрузки, который мы рассмотрим более подробно при изучении команд типа RX. Результатом обработки данной команды ассемблером является занесение в специально отведенную область внутри программы двоичного эквивалента указанного числа. При выполнении программы слово, содержащее этот двоичный эквивалент, будет помещено в регистр, обозначенный R1. Операнды вида=  будем называть литералами. Применение литералов такого типа обеспечивает занесение соответствующих полных слов в предназначенные для этого ячейки памяти. Информация, хранящаяся в этих ячейках, может быть использована затем при выполнении программы. Если в программе будет написано

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

(3)=00000001

4.2.2. ДВОИЧНАЯ ЦЕЛОЧИСЛЕННАЯ АРИФМЕТИКА

Рассмотрев различные варианты пересылки полных слов (возможно, с модификацией) между регистрами, перейдем теперь к изучению арифметических команд формата RR. Существуют четыре команды — AR, SR, MR, DR, выполняющие арифметические операции над двоичными целыми: сложение, вычитание, умножение и деление соответственно.

При выполнении каждой из этих операций регистр первого операнда используется в качестве накопителя: первоначально он содержит один из операндов арифметической информации, а после выполнения команды содержит весь результат или только его часть в соответствии с рассматриваемым случаем. Содержимое регистра R2 при выполнении команды не изменяется.

ARR1,R2 AddRegisterR1(R1) + (R2)

С помощью команды AR(СЛОЖЕНИЕ) производится суммирование двух чисел, находящихся в указанных регистрах, причем результат попадает в первый из этих регистров, т. е. в R1. Содержимое R2 не меняется в процессе выполнения команды. Пусть

(5)=00000023

(8)=00000016

и выполняется команда

AR 5,8

Исходное

содержимое

регистров

Команда

результирующее

содержимое

регистров

(3)= 00000010

(4)= 00000005

AR

4,3

(3)= 00000010

(4)= 00000015

(10) = 00016032

(12) = FFFFFFE2

AR

10,12

(10) = 00016014

(12) = FFFFFFE2

(0) = FFFFFFF5

(2) = FFFFFFF6

AR

2,0

(0) = FFFFFFF5

(2)= FFFFFFFB

Рис. 4.1. Команда AR.

В результате содержимое регистров будет следующим:

(8)= 00000016 (не изменилось)

(5)= 00000023 + 00000016 = 00000039

На рис. 4.1 приведены результаты выполнения команды ARдля некоторых других случаев. Итак, для сложения двух чисел, находящихся в регистрах, необходимо выполнить команду AR, указав соответствующие регистры в поле операндов. Регистр, в котором мы хотим получить сумму, должен быть указан первым.

Удвоение содержимого некоторого регистра достигается выполнением команды ARс одинаковыми первым и вторым операндами. По команде

AR7,7

содержимое регистра 7 фактически умножается на 2.

Мы уже видели, что в результате сложения двух слишком больших чисел может возникнуть переполнение. Это приводит к ситуации, называемой особым случаем целочисленной арифметики.

SRR1,R2 SubtractRegisterR1(Rl) —(R2)

Выполнение команды SR(ВЫЧИТАНИЕ) приводит к засылке в регистр R1 разности содержимых регистров R1 и R2, содержимое R2 при этом не меняется. Отметим, что порядок указания регистров в команде определяет, в каком из них находится уменьшаемое, а в каком — вычитаемое.

Пусть сначала

(5)=00000006

(7)=00000020

После выполнения команды

SR7,5

Исходное

содержимое

регистров

Команда

Результирующее

содержимое

регистров

(5) = FFFFFFF6

(7) = 00000020

SR 5.7

(5) = FFFFFFD6

(7) = 00000020

(4) = 00000030

(6) = 00000007

SR 4,6

(4) = 00000029

(6) = 00000007

(10) = FFFFFF72

(3) = FFFFFF31

SR 10,3

(10) = 00000041

(3) = FFFFFF31

Рис. 4.2. Команда SR.

получаем результат

(5)=00000006

(7)=0000001А

На рис. 4.2 приведены результаты применения команды SRдля некоторых других случаев.

Известно, что результатом вычитания числа из самого себя является 0, поэтому в результате выполнения команды

SR8,8

получится

(8)=00000000

независимо от того, каково было первоначальное содержимое регистра 8.

При выполнении операции вычитания находится дополнительный код содержимого R2, производится сложение полученного результата с содержимым регистра R1 и результат снова засылается в регистр R1.

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

MR R1.R2 Multiply Register RI.R1 + 1(R1+1)X(R2)

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

После выполнения команды MRрезультат помещается в два регистра,, содержимое которых можно рассматривать как единое 64-разрядное двоичное целое. Для хранения результата отводятся регистры R1 и следующий за ним регистр, который мы обозначили R1 + 1. Отметим, что R1 + 1 —это регистр, следующий за R1, а не (R1)+1.

Если R1 — регистр 4, то R1 + 1 — регистр 5. Тот факт, что регистры связаны вместе, т. е. что их содержимое должно рассматриваться как единое двойное слово, обозначается разделением обозначений этих регистров в символической записи команды запятой, например R1.R1 + 1.

Регистр R1 должен иметь четный номер, в противном случае возникающая ситуация рассматривается как особый случай спецификации. Сомножители находятся в регистрах R2 и R1 + 1. Иногда для записи произведения достаточно и одного полного слова, в этих случаях результат помещается в регистр R1 +1, регистр R1 при этом содержит код 00000000, если произведение положительно, и FFFFFFFF— в противном случае. Это естественное следствие применения принципов арифметики в дополнительных кодах. В процессе выполнения команды MRсодержимое R1 изменяется даже в случае, когда R1 непосредственно не используется для хранения результата операции.

Пусть нам нужно вычислить произведение чисел, записанных в регистрах 7 и 10. Поскольку в качестве первого операнда в команде MRразрешается использование регистров лишь с четными номерами, то, значит, один из сомножителей должен находиться в регистре с нечетным номером. Поскольку один сомножитель находится в регистре 7, естественно использование регистра 6 в качестве R1. Итак, наша команда имеет следующий вид:

MR6,10

Предположим теперь, что перед выполнением команды интересующие нас регистры содержали следующее:

(6)=0126С349

(7)=00000021

(10)=00000003

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

(6)=00000000

(7)=00000063

(10)=00000003

Рассматривая содержимое регистров 6 и 7 как единое двойное слово, полученное произведение можно представить в следующем виде:

00000000 00000063

Если же первоначально мы имели

(6) = 01 26С3 4 9

(7) = FFFFFFDF(10) = 00000003

то после выполнения команды

MR6,10

Исходное

содержимое

регистров

Команда

Результирующее

содержимое

регистров

(11)= 0000003А

(12)= FFFFF68C

(13)= 00000007

MR 12.1.1

(11)= 0000003А

(12)= 00000000

(13) = 00000196

(2)= 0000068С

(3)= 000000С0

(7) = FFFFFFFC

MR 2,7

(2)= FFFFFFFF

(3)= FFFFFFD0

(7) = FFFFFFFC

(5) = 00002000

(8)= 00300000

(9)= 9FFFFFFF

MR 8,5

(5) = 00002000

(8)= 00000С00

(9)= 00002000

Рис. 4.3. Команда MR.

Получим

(6)= FFFFFFFF

(7) = FFFFFF9D

(10) = 00000003

Произведение равно

FFFFFFFF FFFFFF9D

что является 64-разрядным дополнительным кодом числа —

Рис. 4.3 демонстрирует результаты применения команды MRв некоторых других случаях.

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

DR

R1,R2

Divide Register

(R1, R1 + 1)(R2):

R1остаток

R1 +1частное

В команде MRпредусмотрена возможность получения 64-разрядного результата. Команда DR(ДЕЛЕНИЕ), напротив, позволяет делить двойное слово, причем в результате может получиться число, помещающееся снова в один регистр. Делимым является 64-разрядное число, помещающееся в регистрах R1 и R1 + 1. Делитель располагается в R2. По окончании выполнения деления частное помещается в R1+1, а остаток — в R1. R1, как и для команды умножения, должен иметь четный номер. Иначе ситуация, которая возникает при попытке выполнения команды, будет рассматриваться как особый случай спецификации.

По определению

делимое=делитель х частное+остаток

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

Рассмотрим пример:

(2) = FFFFFFFF

(3) = FFFFF100

(9) = 00000007

Пусть выполняется команда

DR2,9

Делимым является двойное слово

FFFFFFFFFFFFF100

составленное из слов, находящихся в регистрах 2 и 3. Это 64-разрядное двоичное целое равно=. Это число делится на 00000007,

содержащееся в регистре 9. В результате (проверьте) получится с остатком 4. Остаток помещается в R1 (регистр 2), а частное — в R1+1 (регистр 3). Следовательно, после выполнения команды мы получим:

(2)= 0000000 4

(3)= FFFFFDDC

(9) = 00000007

На рис. 4.4 приведены примеры применения команды DRв некоторых других случаях.

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

(2)=00000006

(3)=00000000

(6)=00000003

Исходное

содержимое

регистров

Команда

Результирующее

содержимое

регистров

(0) = 00000000

(1) = 00000040

(12) = 00000005

DK 0,12

(0)= 00000004

(1) = 0000000С

(12) = 00000005

(8)= FFFFFFFF

(9)= FFFFFF65

(3) = 0000000С

DR  8,3

(8)= FFFFFFF5

(9)= FFFFFFF4

(3) = 0000000С

(6)= 00369С36

(7) = 000СС935

(11) = 03000000

DR 6,11

(6)= 000СС935

(7) = 12341200

(11)= 03000000

 

Рис. 4.4. Команда DR,

 

и приводится попытка выполнения команды

DR 2,6

Частное, 200000000, является 34-разрядным числом, поэтому в нашем случае невозможно поместить его в регистр 3. Число, которое окажется в регистре 3, если мы все же попытаемся записать туда получившийся результат, нельзя будет использовать как частное нашей операции деления. Возникновение подобной ситуации ведет, как уже отмечалось, к прерыванию «выполнения программы и выдаче пользователю сообщения об ошибке.

В большинстве реальных ситуаций делимое содержит менее 32 битов, т. е. для его хранения требуется всего один регистр. Тем не менее в команде DRделимое всегда рассматривается как 64-битовое число. Итак, поскольку обычно для представления делимого хватает одного регистра, для получения правильного результата при делении перед выполнением соответствующей команды необходимо убедиться, что первая часть двойного слова делимого не испортит результата (во второй части слова при этом находится делимое). Пусть

 (5) =00000050

и мы хотим разделить это число на (7). Для этого можно использовать команды

DR4,7

при условии, что содержимое регистра 4 не нужно нам для каких-либо других целей. Пусть также

(4)=00200000

Фактически делимым является

(4,5) =00200000 00000050

Результат, очевидно, будет ошибочен, так как делимое в данном случае является очень большим положительным числом, а не, как хотелось бы. Следовательно, перед выполнением команды DRнеобходимо заслать 0 в регистр 4.

Пусть теперь

(5) = FFFFFFE0

т. е. является отрицательным числом. Казалось бы, перед выполнением команды деления необходимо снова поместить 0 в регистр 4. Если это будет сделано, то фактическим делимым окажется число

(4,5)=00000000 FFFFFFE0

которое, как 64-разрядное целое, является положительным числом, поэтому мы снова получим неверный результат. Для получения верного результата перед выполнением команды DRнеобходимо заслать в регистр 4 число

FFFFFFFF

Тогда истинное делимое будет

(4,5) = FFFFFFFFFFFFFFE0

Именно это отрицательное число нам бы и хотелось использовать в качестве делимого.

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

1. Делимое находится в регистре с нечетным номером.

2.Каждый бит предыдущего регистра равен знаковому биту 32- разрядного делимого.

Условие 1 обеспечивает возможность выполнения команды деления. Условие 2 следует из требования использования двойного слова в качестве делимого.

На рис. 4.5 демонстрируется пример одного из путей преодоления перечисленных препятствий: нам нужно разделить число, находящееся в регистре 4, на содержимое регистра 9. Сначала нужно переслать содержимое регистра 4 в произвольный регистр с нечетным номером, например регистр 7 (во многих случаях выбор регистров ограничен вследствие использования регистров для других целей). Теперь засылаем в регистр 8 число 00000001 для последующего умножения (7) на

LR

7,4

MOVE (4) ТОAN OOD-NUMBERED REGISTER

L

8,=F'1'

PUT THE NUMBER 1 IN REG 8

MR

6,8

MULTIPLY (7) BY 1 TO BE SURE (6) WILL

DR

6,9

BE CK FOR THE DIVIDE INSTRUCTICN

Рис. 4.5. Деление (4) на (9).

(8). Условие 2 оказывается выполненным после завершения умножения, и, таким образом, все подготовлено для получения правильного результата при делении.

Вообще говоря, приведенный пример демонстрирует далеко не лучший способ программирования, так как полученная программа малоэффективна и использует дополнительный регистр. При подготовке к выполнению деления целых чисел обычно определяется знак делимого, и предшествующий регистр с четным номером загружается кодом 00000000, если это знак — плюс и FFFFFFFF, если минус. Подобные действия рассматриваются в гл. 5.

 
Статьи раздела