ГЛАВА 6. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ НА ЭВМ VAX

6.1. ВВЕДЕНИЕ

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

6.2. ЧИСЛА СО ЗНАКОМ И БЕЗ ЗНАКА

РАБОТА С ЧИСЛАМИ И ИХ ИНТЕРПРЕТАЦИЯ

Одно из преимуществ представления чисел в дополнительном коде заключается в том, что для чисел со знаком и без знака используются одинаковые алгоритмы сложения и вычитания. (Другие представления чисел со знаком, применяемые в различных ЭВМ, не обладают таким свойством.) Хотя этот факт кажется удивительным - объяснить его довольно просто. Для этой цели удобнее рассматривать 4-битовые числа, а не 8-, 16- или 32-битовые, поскольку существенно уменьшается число возможных комбинаций.

С помощью четырёх битов можно составить 24, или 16, комбинаций. Как показано на рис. 6.1, можно составить схему, на которой 16 комбинаций располагаются по окружности. Это напоминает циферблат часов, на котором вместо 12 позиций - 16, а в позиции, соответствующей 12 час., помещено двоичное число 0000. Для указания одного из 16 двоичных чисел используется указатель. Прибавление к числу 1 рассматривается как перемещение указателя на одну позицию вперёд. Вычитание 1 рассматривается как перемещение указателя на одну позицию назад.

Если прибавлять единицу 16 раз подряд, указатель совершит полный оборот и вернётся в начальное положение. Математики это называют системой счисления по модулю 16[1]. Но можно было бы назвать это ошибочным, так как очевидно, что X плюс 16 не равно X. Для того чтобы сделать эту систему счисления непротиворечивой, необходимо условиться, что где-то на циферблате должна быть точка возникновения ошибки.

Рис. 6.1. Арифметика чисел без знака

Например, эту точку можно расположить между числами 1111 и 0000. Каждый раз, когда к числу 1111 прибавляется или из числа 0000 вычитается 1, возникает ошибка, которая называется переполнением числа без знака. После того, как точка возникновения ошибки определена, каждой двоичной комбинации может быть поставлена в соответствие её десятичная интерпретация. Если двоичная комбинация 0000 является представлением десятичного нуля, то тогда комбинация 1111 должна быть представлением десятичного числа 15. В итоге будет получена система счисления по модулю 16 для чисел без знака.

Однако точку возникновения ошибки можно расположить на циферблате в каком-нибудь другом месте. В частности, её можно поместить между числами 0111 и 1000, как показано на рис. 6.2. Когда к числу 0111 прибавляется или из числа 1000 вычитается 1, возникает ошибка, называемая переполнением числа со знаком. Если двоичным комбинациям дать теперь десятичную интерпретацию, то окажется, что они представляют десятичные числа от -8 до +7. (Отметим, что если двоичная комбинация 0000 представляет десятичный нуль, то в соответствии с определением вычитания мы вынуждены интерпретировать комбинацию 1111 как десятичное число -1). В результате получим уже знакомое нам представление чисел в дополнительном коде.

Рис. 6.2. Арифметика чисел со знаком

Именно так интерпретируются двоичные числа в ЭВМ семейства VAX, только они включают не 4 бита, а 8, 16 или 32. Фактическое число битов определяется типом используемой инструкции, т.е. чем работает она с байтами, словами или длинными словами. Точка возникновения ошибки переполнения для чисел без знака, представленных в формате длинного слова, расположена между значениями ^XFFFFFFFF и ^X00000000, а между значениями ^X7FFFFFFF и ^X80000000 расположена точка возникновения ошибки переполнения для чисел со знаком, представленных в формате длинного слова.

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

ОБРАБОТКА ПЕРЕПОЛНЕНИЯ

Для того чтобы программист мог обнаружить два указанных типа переполнения, в процессоре имеется два флага условий. Они называются бит С и бит V. Бит С устанавливается равным 1, когда в процессе выполнения арифметической операции происходит перенос из старшего разряда результата. Это аналогично переполнению числа без знака, показанному на рис. 6.1. В случае, если переноса не происходит, бит С сбрасывается в нуль. Бит V устанавливается в 1 в случае переполнения числа со знаком, как показано на рис. 6.2, и сбрасывается в нуль, если такого переполнения не происходит.

Для того чтобы биты С и V можно было использовать, необходимы средства для проверки их состояния. Ниже приведены инструкции, необходимые для этого:

Мнемоника

Значение

BCS

Перейти, если бит С установлен (С = 1)

BCC

Перейти, если бит С сброшен (С = 0)

BVC

Перейти, если бит V установлен (V = 1)

BVC

Перейти, если бит V сброшен (V = 0)

В качестве примера использования этих битов приведём фрагмент программы, в котором производится сложение двух чисел со знаком, представленных в формате длинного слова, после чего происходит переход к метке ERROR, если возникает переполнение результата, а именно если результат либо меньше -2147483648, либо больше +2147483647:

        ADDL2   A,B            ; СЛОЖИТЬ ЧИСЛА A И В
        BVS     ERROR          ; ПРИ ПЕРЕПОЛНЕНИИ ПЕРЕЙТИ НА ERROR
        .
        .

ERROR:  .                      ; НАПЕЧАТАТЬ СООБЩЕНИЕ ОБ ОШИБКЕ

Отметим, что эти четыре инструкции подобны другим инструкциям перехода и имеют такие же ограничения на диапазон адресов возможных переходов: 128 байтов, расположенных перед, и 127 байтов, расположенных после адреса в программном счётчике. В случае, если адрес перехода ERROR удалён от инструкций BVS более чем на 128 байтов, могут потребоваться инструкции JMP или BRW. Например,

        ADDL2   A,B             ; СЛОЖИТЬ ЧИСЛА А И В
        BVC     10$             ; ЕСЛИ НЕТ ПЕРЕПОЛНЕНИЯ - ПРОДОЛЖИТЬ,
        JMP     ERROR           ; ИНАЧЕ ПЕРЕЙТИ НА ERROR
10$:    .
        .
        .
ERROR:  .                       ; НАПЕЧАТАТЬ СООБЩЕНИЕ ОБ ОШИБКЕ
  

ДРУГИЕ ФЛАГИ УСЛОВИЙ

Уже было рассмотрено два флага условий: бит С и бит V. В ЭВМ семейства VAX имеется ещё два флага условий: бит N и бит Z. Все эти 4 флага называются кодами условий. Биты N и Z предназначены для упрощения проверки условий, возникающих в ходе выполнения любых арифметических операций. В гл. 5 уже рассматривались инструкции проверки и сравнения; покажем теперь, что в действительности происходит при выполнении условных переходов.

Бит N устанавливается в 1 всегда, когда результат операции отрицателен. Это происходит даже при выполнении такой простой инструкции, как MOVL. После её выполнения бит N будет установлен, если пересылаемое число отрицательно. Бит N сбрасывается, если число является положительным. На самом деле значение бита N обычно равно значению знакового или старшего бита результата. Однако переполнение может привести к тому, что знаковый бит результата будет содержать неверное значение. Фактически бит N отражает значение знакового бита правильного результата, даже если при переполнении результат не может целиком поместиться в ячейке результата операции.

Бит Z в определённом смысле аналогичен биту N. Бит Z устанавливается в 1 в случае, если результатом операции является число 0. Бит Z будет сброшен, если результат не равен 0. В том же смысле, что и бит N, бит Z отражает состояние правильного результата даже в случае переполнения. Значения битов N и Z, так же как и других битов кодов условий (битов С и V), могут проверяться с помощью инструкций перехода. Но в отличие от инструкций перехода по значению битов С и V, в мнемоническом обозначении которых фиксируется имя конкретного бита кодов условий (С или V), инструкции перехода по значению битов N и Z имеют мнемонику, обозначающую только тип условного перехода. Ниже приведены четыре инструкции, которые составляют список инструкций условных переходов по значению одного бита кодов условий:

Мнемоника

Значение

BNEQ

Перейти, если предыдущий результат не равен 0, т.е. если бит Z сброшен

BEQL

Перейти, если предыдущий результат равен нулю, т.е. если бит Z установлен в 1.

BGEQ

Перейти, если предыдущий результат больше или равен 0, т.е. если бит N сброшен

BLSS

Перейти, если предыдущий результат меньше нуля, т.е. если бит N установлен в 1.

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

Мнемоника

Значение

BGTR

Перейти, если предыдущий результат больше 0, т.е. оба бита N и Z сброшены

BLEQ

Перейти, если предыдущий результат меньше или равен 0, т.е. если либо бит N, либо бит Z установлен в 1.

ИНСТРУКЦИИ ОБРАБОТКИ ДАННЫХ

Инструкции обработки данных устанавливают биты кодов условий в нулевое или единичное состояние в соответствии с результатом операции обработки данных. Такие инструкции, как MOVL, CLRL, а также логические операции не могут вызывать переполнения. При выполнении этих инструкций бит V сбрасывается, но значение бита С не изменяется. Почти то же самое происходит при выполнении инструкций проверки, рассмотренных в гл. 5. Инструкциями обработки данных являются инструкции, работающие непосредственно с данными, например MOVL, ADDL, SUBL и т.д. В отличие от них инструкции управления, такие как JMP, BRB, BNEQ и т.д., могут осуществлять проверку значений битов кодов условий, но не могут работать непосредственно с данными и, следовательно, устанавливать в необходимое состояние биты кодов условий.

Среди инструкций обработки данных имеется две дополнительные группы инструкций: TSTL, TSTW, TSTB и CMPL, CMPW, CMPB. Частично эти инструкции были описаны в гл. 5, но понять, как они работают, можно, только рассмотрев их совместно с состоянием кодов условий. По инструкции TSTL выполняется выборка элемента данных, проводится его проверка и устанавливаются в соответствующие состояния коды условий. На этом действие инструкции завершается. При выполнении инструкции данные не изменяются и не используются для других целей. Инструкция TSTL всегда сбрасывает биты V и С, поскольку проверка числа не может вызвать ни одного из видов переполнения. Биты N и Z будут устанавливаться в соответствующее состояние в зависимости от того, меньше или равно 0 просматриваемое число.

Инструкция CMPL по действию аналогична инструкции TSTL, но в отличие от неё оперирует двумя данными. По инструкции CMPL A,B происходит вычитание содержимого длинного слова с адресом B из содержимого длинного слова с адресом A, при этом результат вычитания проверяется и в соответствии с его значением устанавливаются биты кодов условий, после чего результат уничтожается. Таким образом, результат не сохраняется и содержимое длинных слов A и B не изменяется, что и позволяет использовать инструкцию CMPL так, как было показано в предыдущих главах, например:

CMPL    A,B
BEQL    10$

Необходимо отметить, что инструкции условного перехода BGEQ, BGTR, BLSS и BLEQ не следует использовать при обработке чисел без знака. Например, если рассматривать содержимое длинных слов как число без знака, то число ^XFFFFFFFF больше числа ^X00000000. Однако операция сравнения, предшествующая инструкции BGTR, даст противоположный результат, поскольку число ^XFFFFFFFF будет рассматриваться как отрицательное, а именно как -1. Для решения этой проблемы предусмотрены четыре инструкции условного перехода, применяемые при обработке чисел без знака:

Мнемоника

Значение

BGTRU

Перейти, если больше (для чисел без знака)

BGEQU

Перейти, если больше или равно (для чисел без знака)

BLEQU

Перейти, если меньше или равно (для чисел без знака)

BLSSU

Перейти, если меньше (для чисел без знака)

Эти инструкции имеют смысл только тогда, когда они используются совместно с инструкциями сравнения. Так, с помощью следующих инструкций можно перейти к метке 10$, если содержимое длинного слова с адресом A больше, чем содержимое длинного слова с адресом B (в беззнаковом представлении):

CMPL    A,B
BGTRU   10$

Любопытно отметить, что инструкция BLSSU идентична инструкции BCS. На машинном языке эти инструкции имеют одинаковый код операции ^X1F (см. п. 6 в упр. 6.1).

Сравнение или проверка на равенство осуществляется одинаково для чисел со знаком и без знака. Однако для того, чтобы избавить программиста от необходимости помнить, когда надо использовать различные переходы для чисел со знаком и без знака, в языке ассемблера предусмотрены мнемонические коды BEQLU и BNEQU, которые в действительности при ассемблировании переводятся в те же машинные коды операции, что и мнемоника инструкций BEQL и BNEQ. Это позволяет программисту быть последовательным и использовать символ U каждый раз, когда он имеет дело с числами без знака.

ИНСТРУКЦИИ ПРЕОБРАЗОВАНИЯ ДАННЫХ И ЗНАКА

Часто в одной задаче возникает необходимость в совместной обработке различной информации, представленной в различных форматах: байта, слова и длинного слова. Но при вычитании и сложении данных различного формата возникают трудности. Так, нельзя непосредственно сложить содержимое байта с содержимым слова. Для этого необходимо преобразовать формат байта в формат 16-битового слова. Затем содержимое полученного слова можно сложить с содержимым другого слова. С помощью следующих инструкций можно к содержимому байта с адресом B прибавить содержимое слова с адресом W:

CLRW    R0           ; ОЧИСТИТЬ МЛАДШИЕ БИТЫ РЕГИСТРА R0
MOVB    B,R0         ; ПЕРЕСЛАТЬ В МЛАДШИЕ 8 БИТ ЧИСЛО В
ADDW2   R0,W         ; И СЛОЖИТЬ ВОСЬМИБИТОВОЕ ЧИСЛО С W

Для выполнения этой операции требуется последовательность из трёх инструкций, а также временная память для хранения содержимого нового слова. В данном случае для этого используется регистр R0, но можно было бы использовать и ячейку памяти. Инструкция CLRW обнуляет 16 битов регистра R0. Затем содержимое байта с адресом B пересылается в восемь младших битов слова в регистре R0. После этого новое содержимое слова в регистре R0 складывается с содержимым слова, расположенного по адресу W.

При применении этого способа необходимо помнить, что содержимое байта должно быть числом без знака. Поскольку инструкция CLRW обнуляет старшие 8 битов слова в регистре R0, этот способ нельзя использовать, если содержимое байта должно интерпретироваться как число со знаком. Это означает, что содержимое регистра R0 рассматривается всегда как положительное число от 0 до 255. Для сложения чисел со знаком, представленных в различном формате, необходимо проверить значение знакового бита числа, содержащегося в байте. В случае отрицательного числа старшие биты слова в регистре R0 необходимо установить равными 1, а не 0, что может быть реализовано с помощью следующих инструкций:

        CLRW    R0              ; ОЧИСТИТЬ МЛАДШИЕ БИТЫ РЕГИСТРА R0
        TSTB    B               ; ЕСЛИ ЧИСЛО В ОТРИЦАТЕЛЬНОЕ,
        BGEQ    10$
        MOVW    #-1,R0          ; TO ВО ВСЕХ БИТАХ ДОЛЖНЫ БЫТЬ ЕДИНИЦЫ
10$:    MOVB    B,R0            ; ПЕРЕСЛАТЬ В МЛАДШИЕ 8 БИТ ЧИСЛО В 
        ADDW2   R0,W            ; И СЛОЖИТЬ ЧИСЛО СО ЗНАКОМ С W

Как видите, это довольно сложно. Поэтому ЭВМ семейства VAX включают группу инструкций преобразования формата, которые копируют числа со знаком, представленные в формате байта, слова или длинного слова, в области памяти требуемого формата. Приведённый ниже список включает инструкции преобразования формата, работающие с целочисленными операндами. Кроме того, существуют инструкции преобразования формата, работающие с операндами с плавающей точкой; они рассматриваются в гл. 12.

Мнемоника

Значение

CVTBW

Преобразовать байт в слово

CVTBL

Преобразовать байт в длинное слово

CVTWB

Преобразовать слово в байт

CVTWL

Преобразовать слово в длинное слово

CVTLB

Преобразовать длинное слово в байт

CVTLW

Преобразовать длинное слово в слово

На самом деле эти инструкции действуют так же, как инструкции пересылки семейства MOV, за исключением того, что форматы операнда-источника и операнда-получателя различны. При выполнении этих инструкций значения бита N и бита Z устанавливаются в зависимости от значения результата так, как это было описано выше. Бит С всегда сбрасывается. Бит V устанавливается равным 1 в случае, если результат (число со знаком) слишком велик и выходит за пределы требуемого формата. Очевидно, что это может произойти только при выполнении инструкций, которые преобразуют большие форматы в меньшие, например CVTWB. Операнд-источник, представляющий собой слово, вполне может иметь значение больше 127 или меньше -128, но оно может не поместиться в формате байта (формат результата).

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

CVTBW   B,R0
ADDW2   R0,W

Эти инструкции выполняют то же самое, что и предыдущая программа. Инструкции преобразования меньших форматов в большие, такие как CVTBW, заполняют старшие биты результата значением знакового бита исходного операнда. Например, содержимое байта ^X03 будет расширено для представления в формате слова как ^X0003, но при преобразовании ^X83 получается ^XFF83.

Для инверсии знака числа используется другая группа инструкций. Приведённые ниже инструкции по действию аналогичны следующим операторам Фортрана или Паскаля:

Фортран Паскаль

X = -Y

X := -Y;

 

Мнемоника

Значение

MNEGB

Переслать байт с изменением знака

MNEGW

Переслать слово с изменением знака

MNEGL

Переслать длинное слово с изменением знака

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

MNEGL   X,R0 

осуществит пересылку 32-битового дополнительного кода операнда-источника с адресом X в регистр R0, изменив таким образом знак исходного числа на противоположный.

Данные инструкции обрабатывают биты N и Z обычным образом, устанавливая знак результата и признак равенства результата равным 0. Бит V устанавливается равным 1, если результат переполняет разрядную сетку. Напомним, что при представлении чисел в дополнительном коде положительных чисел будет на одно меньше, чем отрицательных. Так, самое большое по абсолютному значению отрицательное число, которое можно представить в формате слова, равно -32768, или ^X8000, а самое большое положительное число равно +32767, или ^X7FFF. Поэтому не существует противоположного по знаку значения для отрицательного числа ^X8000, представленного в формате слова. При этом произойдёт установка бита V.

Использование бита С несколько необычно. Он устанавливается равным 1, если результат не равен 0. Это может показаться бессмысленным, но является полезным при изменении знака чисел с многократно увеличенной точностью (см. раздел этой главы, посвящённый многократно увеличенной точности). Фактически логика состоит в том, что получение противоположного по знаку значения числа эквивалентно вычитанию его из 0. Например, инструкция

MNEGL   X,R0

установит бит С в то же состояние, что и пара инструкций

CLRL    R0
SUBL2   X,R0

(На самом деле каждый из четырёх битов кодов условий N, Z, V и С будет установлен в обоих случаях в одно и то же состояние.) Если содержимое длинного слова с адресом X не равно 0, то в результате выполнения инструкции SUBL2 бит C установится равным 1, потому что вычитание ненулевого значения из 0 всегда вызывает переполнение числа без знака.

Инструкции преобразования формата и инструкции пересылки с изменением знака предназначены для работы с числами со знаком. Также бывает необходимо переслать число без знака в область памяти большего формата, например, чтобы сложить два числа без знака, т.е. содержимое байта и длинного слова. Инструкции преобразования формата, такие как CVTBL, не могут использоваться как надёжное средство для этой цели, поскольку содержимое байта будет в данном случае рассматриваться как число со знаком.

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

CLRL    R0
MOVB    B,R0

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

Мнемоника

Значение

MOVZBW

Переслать содержимое байта, дополненное незначащими нулями, в слово

MOVZBL

Переслать содержимое байта, дополненное незначащими нулями, в длинное слово

MOVZWL

Переслать содержимое слова, дополненное незначащими нулями, в длинное слово

Эти инструкции обнуляют старшие биты операнда-получателя и пересылают значение операнда-источника в младшие биты операнда-получателя. Бит V сбрасывается, так как переполнения, очевидно, быть не может. Бит N сбрасывается, поскольку в знаковый бит операнда-получателя засылается нуль. Значение бита С не изменяется.

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

MOVB    L,B

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

УПРАЖНЕНИЯ 6.1

  1. Приведите десятичные эквиваленты для следующих слов, представленных в шестнадцатеричном виде, интерпретируя их как числа без знака, а затем как 16-битовые числа со знаком в дополнительном коде:
    а)

    ^X00FD;

    б)

    ^XFFFD;

    в)

    ^X7FFF;

    г)

    ^XF716;

    д)

    ^X8000;

    е)

    ^X8001;

    ж)

    ^X80FD;

    з)

    ^X7655;

    и)

    ^X6EEF.

  2. Для каждого числа из п. 1 упражнения 6.1 покажите его представление в 16-битовом дополнительном коде при изменении знака числа на противоположный, как это было бы сделано инструкцией MNEGW. Кроме того, для каждого случая укажите значения битов N, Z, V и С, которые могут изменяться в результате выполнения этой инструкции.
  3. Сложите пары слов так, как это было бы сделано инструкцией ADDW3. Кроме того, для каждой пары чисел укажите значения битов N, Z, V и С, которые могут изменяться в результате выполнения этой инструкции:
    а)

    ^X011A и ^X00FD;

    б)

    ^X6A5A и ^X7FEC;

    в)

    ^X65ED и ^XFFEB;

    г)

    ^XFFFF и ^XFFFF;

    д)

    ^X7FFF и ^X8001;

    е)

    ^XD7B7 и ^X6DF8.

  4. Выполните п. 3 упр. 6.1, заменив сложение вычитанием первого числа из второго, так, как это было бы сделано инструкцией SUBW3.
  5. Покажите, что в случае представления чисел в дополнительном коде при возникновении переполнения числа со знаком знаковый бит результата имеет значение, противоположное тому, которое следовало бы ожидать.
  6. Объясните, почему инструкция BLSSU аналогична инструкции BCS. Какая инструкция условного перехода для чисел без знака аналогична инструкции BCS? Объясните, почему.
  7. Ниже приведён список значений, которые являются исходным содержимым регистра R0. Для каждого из этих значений укажите, каким станет новое содержимое регистра R0 и какие значения примут биты N, С, V и Z в результате выполнения инструкций INCL R0, DECL R0, INCW R0, DECW R0:
    а)

    ^X7FFFFFFF;

    б)

    ^X80000000;

    в)

    ^XFFFFFFFF;

    г)

    ^X00000000;

    д)

    ^X00000001;

    е)

    ^X80000001.

  8. * Напишите подпрограмму, которая печатает четыре числа, каждое из которых может иметь значение ^X00000000 или ^X00000001, отражающее текущие значения битов N, Z, V и С. (Примечание: выполнение инструкции JSB не влияет на коды условий, так же как не влияет на коды условий выполнение инструкций переходов различного типа. Однако коды условий изменяются при выполнении таких инструкций, как MOVL Набор инструкций ЭВМ VAX включает специальные инструкции (нами ещё не рассмотренные), которые могли бы упростить выполнение этого задания, но мы рекомендуем обойтись пока без них. Напишите программу в виде дерева переходов). Кроме того, напишите основную программу, которая должна тестировать подпрограмму следующим образом: она должна выполнять различные вычисления, после которых вызывать подпрограмму, которая будет выводить на печать результаты (т.е. значения битов N, Z, V и С), полученные после каждого вычисления.

6.3. УМНОЖЕНИЕ И ДЕЛЕНИЕ

ОСНОВНЫЕ ИНСТРУКЦИИ

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

В группу основных инструкций умножения и деления входят следующие:

MULB2

DIVB2

MULB3

DIVB3

MULW2

DIVW2

MULW3

DIVW3

MULL2

DIVL2

MULL3

DIVL3

Во многом они действуют аналогично инструкциям сложения и вычитания с двумя и тремя операндами. Например, инструкция

MULL3   A,B,C

перемножит содержимое длинных слов с адресами A и B и поместит результат в длинное слово с адресом C. Аналогично инструкция

MULB2   X,Y

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

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

DIVL3   A,B,C

разделит содержимое длинного слова с адресом B на содержимое длинного слова с адресом A и поместит частное в длинное слово с адресом C. Аналогично инструкция

DIVB2   X,Y

разделит содержимое байта с адресом Y на содержимое байта с адресом X и поместит частное в байт с адресом Y.

Между инструкциями умножения и деления и инструкциями сложения и вычитания имеется существенное различие. Как было показано ранее, числа со знаком и без знака обрабатываются основными инструкциями сложения и вычитания одинаково; единственная разница в их обработке состоит в том, что по-разному обнаруживается и интерпретируется переполнение. Тогда как умножение и деление чисел со знаком существенно отличается от умножения и деления чисел без знака.

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

При делении целых чисел основная трудность заключается в том, что результат деления может не быть целым числом. Описанные в этом разделе инструкции целочисленного деления ЭВМ VAX используют те же правила обработки результата, что и большинство языков высокого уровня, таких как Фортран или Паскаль. Дробная часть частного всегда усекается, и результат, таким образом, "округляется" в сторону 0. Это справедливо как для положительных, так и для отрицательных результатов. Так, результат деления 7 на 2 с усечением равен 3, Аналогично результат деления -7 на 2 с усечением равен -3.

Наконец, рассмотрим, как эти инструкции воздействуют на коды условий. Как и следовало ожидать, биты N и Z устанавливаются или сбрасываются в зависимости от того, каким является результат (произведение или частное): отрицательным или равным нулю. При этом имеется в виду реальный результат, записанный в памяти, без учёта потерянных из-за переполнения или округления битов.

Бит V устанавливается при переполнении. Так как в результате умножения могут получаться большие числа, то легко понять, где именно возникает переполнение. Бит V устанавливается, если произведение слишком велико и выражается числом, которое не помещается в заданном формате результата. Другими словами, при умножении чисел в формате байта их произведение должно находиться в диапазоне значений ^X00 - ^X7F. При умножении чисел в формате слова значение произведения должно находиться в диапазоне значений ^X0000 - ^X7FFF, а в случае длинных слов значение произведения должно быть в диапазоне значений ^X00000000 - ^X7FFFFFFF.

Может быть непонятно, каким образом возникает переполнение при делении, поскольку частное обычно меньше делимого, которое уже находится в заданном формате. Очевидной исключительной ситуацией является деление на 0. Понятно, что при попытке деления на 0 будет установлен бит N. Но имеется ещё одна исключительная ситуация, которая является результатом асимметрии, свойственной системе представления чисел в дополнительном коде. Вспомним, что наибольшее по абсолютному значению отрицательное число в таком представлении не имеет противоположного себе по знаку. Так, в формате байта можно представить числа от -128 до +127, в формате слова - от -32768 до 32767, в формате длинного слова - от -2147483648 до +2147483647. Поэтому при выполнении арифметических операций над числами в формате слова для числа -32768 не существует числа, противоположного ему по знаку. Это означает, что инструкции DIVW2 и DIVW3 при попытке деления числа -32768 на -1 вызовут переполнение. В аналогичной ситуации возникает переполнение при выполнении инструкций деления, работающих с байтами и длинными словами. И во всех случаях произойдёт установка бита V в 1.

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

ИНСТРУКЦИИ РАСШИРЕННОГО УМНОЖЕНИЯ И ДЕЛЕНИЯ

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

Ниже показано действие инструкции расширенного умножения:

EMUL    A,B,C,D

где A, B, C и D являются обычными операндами. Операнды A и B - 32-битовые множитель и множимое. Операнд D - 64-битовое произведение, имеющее формат квадраслова. Операнд C - 32-битовое число со знаком, прибавляемое к 64-битовому произведению. Эта инструкция часто используется для выполнения таких операции, как перевод из одной системы счисления в другую, где реализуется функция умножения с последующим суммированием. Именно для этой цели в инструкцию EMUL включен операнд C. Если к произведению ничего прибавлять не надо, эту инструкцию всегда можно записать в виде

EMUL    A,B,#0,D

Отметим, что в инструкции мы впервые сталкиваемся с практическим использованием квадраслов. В гл. 3 упоминалось, что информация может пересылаться блоками размером 8, 16, 32 и 64 бита с помощью инструкций MOVB, MOVW, MOVL и MOVQ соответственно. Инструкция MOVQ осуществляет пересылку квадраслова, т.е. 64 бита или 8 байтов. Обращение к ячейкам памяти при выполнении инструкции MOVQ не представляет сложностей, поскольку эта инструкция является просто расширением других инструкций многобайтовой пересылки MOVW и MOVL. Однако если инструкция MOVQ адресуется не к ячейкам памяти, а к регистрам, то могут возникнуть трудности, связанные с тем, что разрядность регистра 32 бита. Поэтому, если операнд имеет формат квадраслова и применяется режим регистровой адресации, это означает, что операнд расположен в двух регистрах - регистре, указанном в инструкции, и регистре со следующим старшим номером. Таким образом, инструкция

MOVQ    X,R0

поместит 32 младших бита операнда X, имеющего формат квадраслова, в регистр R0, а старшие 32 бита этого операнда - в регистр R1. Аналогично инструкция

EMUL    A,B,#0,R3

поместит 64-битовое произведение операндов A и B в регистры R3 и R4.

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

EDIV    D,E,F,G

где операнд D - 32-битовый делитель, операнд E - 64-битовое делимое, операнды F и G - 32-битовые частное и остаток. Если остаток не равен 0, его знак соответствует знаку делимого. Поскольку в этой инструкции все операнды являются числами со знаком, определение знака частного следует обычным правилам алгебры. Если делимое и делитель имеют одинаковый знак, предполагается, что частное будет положительным. Если делимое и делитель имеют разные знаки, то предположительно частное будет отрицательным. В действительности знак частного может отличаться от предполагаемого в случае, если частное равно нулю или произошло переполнение.

Переполнение может возникать по двум причинам. Бит V установится при делении на 0. Но при делении 64-битового числа на 32-битовое число не гарантируется получение 32-битового результата. Например, деление числа ^X000123456789ABCD на число ^X0000100 даёт частное ^X00000123456789AB и остаток ^X000000CD. Отметим, что остаток всегда меньше делителя и, следовательно, должен поместиться в 32 битах. Частное в этом случае не умещается в формате длинного слова, и, следовательно, произойдёт переполнение.

Последнее замечание касается того, что инструкции расширенного умножения и деления в некотором роде симметричны. В частности, операнд-слагаемое в инструкции EMUL является отчасти дополнением остатка в инструкции EDIV. Например, рассмотрим расширенное деление

EDIV    D,E,F,G

Если при выполнении этой инструкции не возникло переполнения, то расширенное умножение

EMUL    D,F,G,X

даст результат в формате квадраслова (операнд X), идентичный по значению делимому (операнд E) в инструкции EDIV, имеющему такой же формат.

При выполнении этих инструкций биты N и Z устанавливаются в зависимости от значения результата: в случае инструкции EMUL - произведения, в случае инструкции EDIV - частного. Бит V установится, если при выполнении инструкции EDIV возникнет переполнение. При выполнении инструкции EMUL переполнение невозможно, так как 32-битовые операнды при умножении всегда дают результат, занимающий не более 64 бита. Бит С, так же как и в случае основных инструкций умножения и деления, всегда сбрасывается.

6.4. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ С ПОВЫШЕННОЙ ТОЧНОСТЬЮ

ПРЕДСТАВЛЕНИЕ С ДВОЙНОЙ ТОЧНОСТЬЮ

Как было показано выше, существуют инструкции сложения и вычитания для чисел в формате байтов (8 битов), слов (16 битов) и длинных слов (32 бита). В форматах байтов и слов можно представить числа весьма ограниченного диапазона, но даже в формате длинного слова не всегда удаётся представить требуемое число. Числа без знака, представляемые в формате длинного слова, имеют значения от 0 до 4294967295. Представленные в этом же формате числа со знаком могут иметь значения от -2147483648 до +2147483647. Казалось бы, такой диапазон достаточно велик для большинства задач, однако существуют некоторые задачи, оперирующие большими числами. Для того чтобы вычислительная система была универсальной, в ней должны быть предусмотрены возможности работы с любыми числами.

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

Число с двойной точностью можно воспринимать просто как 64-битовое число или квадраслово. Оно может размещаться в двух длинных словах: 32 старших бита в одном длинном слове и 32 младших бита - в другом. Часто бывает удобно рассматривать эту пару длинных слов как одно 64-битовое квадраслово. При этом становится очевидной возможность представления 64-битовых чисел как со знаком, так и без знака. Для этого требуется только обобщить идею арифметики в дополнительных кодах и расширить её область применения с 32-битовых на область 64-битовых чисел. Как и раньше, крайний левый бит всего числа целиком является знаковым. Им мог бы быть знаковый бит старшего из двух длинных слов, используемых для представления числа. (Отметим, что знаковый бит младшего длинного слова является просто одним из битов в середине числа и не имеет отношения к знаку всего числа.)

СЛОЖЕНИЕ И ВЫЧИТАНИЕ ЧИСЕЛ С ДВОЙНОЙ ТОЧНОСТЬЮ

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

Рассмотрим сложение

 

1  2  3

 

4  5  6

+

1  1  2

 

2  3  3


 

2  3  5

 

6  8  9

Как видно, правая половина суммы равна сумме правых половин складываемых чисел. Аналогично левая половина суммы равна сумме левых половин складываемых чисел.

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

 

      1

 

Перенос

 

1  2  3

 

7  8  9

+

1  1  2

 

5  6  6


 

2  3  6

 

3  5  5

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

Аналогичная процедура используется при вычитании:

 

      1

 

Заём

 

2  3  6

 

3  5  5

-

1  1  2

 

5  6  6


 

1  2  3

 

7  8  9

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

ИНСТРУКЦИИ СЛОЖЕНИЯ И ВЫЧИТАНИЯ С ПЕРЕНОСОМ

Описанные выше методы применимы также для операций двоичной арифметики, выполняемых с 32-битовыми регистрами. В ЭВМ семейства VAX бит С показывает наличие переноса при сложении (или вычитании) младших частей больших чисел. Следовательно, для учёта переноса необходимо прибавить (вычесть) значение бита С к (из) старшей части результата. Конечно, можно было бы проверить значение бита С, а затем увеличить (или уменьшить) результат, находящийся в регистре, на 1. Специально для этой цели разработчики ЭВМ VAX ввели следующие инструкции. Для сложения с переносом используется инструкция

Мнемоника

Значение

ADWC

Сложить с переносом

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

Мнемоника

Значение

SBWC

Вычесть с заёмом (переносом)

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

Эти две инструкции могут использоваться совместно с другими арифметическими инструкциями для сложения 6 1-битовых чисел А и В. Пусть числа А и В содержатся в длинных словах с адресами AL, AR, BL, BR, где буквы L и R обозначают левую и правую половину числа соответственно. Сложение с двойной точностью осуществляется с помощью инструкций

ADDL2   AR,BR
ADWC    AL,BL

Аналогично вычитание А из В производится с помощью инструкций

SUBL2   AR,BR
SBWC    AL,BL

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

6.5. АЛГОРИТМЫ УМНОЖЕНИЯ И ДЕЛЕНИЯ

ИНСТРУКЦИИ СДВИГА

В десятичной системе счисления легко умножать и делить на 10 или на любую степень числа 10, например 100, 1000, 10000 и т.д. Для этого нужно только приписать к числу нули справа при умножении или отбросить цифры справа при целочисленном делении. В регистре это выполняется с помощью сдвига числа: влево - при умножении, вправо - при делении. Представим себе ЭВМ с 10-разрядными регистрами для хранения десятичных чисел. Для такой ЭВМ при умножении 0000057342 на 1000 получим 0057342000; а при делении на 100 - 0000000573.

Такой способ умножения и деления на степень основания системы счисления применим для систем счисления с любым основанием. Следовательно, в ЭВМ с двоичной системой счисления умножение и деление чисел на 2n может осуществляться посредством сдвига числа влево или вправо на n двоичных разрядов. Например, умножение 16-битового числа 0000010011011110 на 810 или 10002 равно 0010011011110000; деление числа 0000010011011110 на 410 или на 1002 равно 0000000100110111. Эти примеры показывают, что происходит с положительными числами. Однако непонятно, что произойдёт при сдвиге отрицательных чисел. При сдвигах влево проблем нет, поскольку 1111111111111000 равно -810, а 1111111111110000 равно -1610. Единственная проблема, которая могла бы возникнуть, связана с тем, что при сдвиге влево на большое число разрядов крайний левый (знаковый) бит может быть обнулён. Но это есть не что иное, как случай переполнения, которое аналогично переполнению при сдвиге положительного числа влево, когда происходит потеря левых битов или когда в знаковом разряде оказывается 1. Переполнение такого типа возникает всегда, когда делается попытка в некоторую область памяти поместить число, значение которого выходит за пределы диапазона представимых чисел.

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

0000000000010000 = 16

 

1111111111110000 = -16

 

разделить на 4

 

0000000000000100 = 4

 

1111111111111100 = -4

(пустые разряды заполняются нулями)

(пустые разряды заполняются единицами)

В наборе инструкций ЭВМ семейства VAX имеется две инструкции для выполнения операций такого типа[2]:

ASHL    N,A,B

и

ASHQ    N,X,Y

В обоих случаях N - 8-битовое слово со знаком, определяющее количество разрядов; на которое следует произвести сдвиг. Если N положительно, то происходит сдвиг влево. Если N отрицательно, то происходит сдвиг вправо. Фактически это аналогично умножению на 2n или на 2-n. При выполнении инструкции ASHL операнд A - содержимое длинного слова с адресом A - сдвигается на N разрядов и полученный результат помещается в длинное слово с адресом B. Инструкция ASHQ выполняется аналогично, за исключением того, что операнды X и Y имеют формат 64-битовых квадраслов.

Указанные инструкции устанавливают биты N и Z в зависимости от значения результата. Бит V будет установлен, если при сдвиге влево получен результат, значение которого выходит за пределы диапазона представимых чисел. Поскольку эти инструкции предназначены для работы только с числами со знаком, бит С всегда сбрасывается.

Остановимся на проблеме потери битов при сдвиге вправо. Понятно, что при сдвиге положительного числа вправо потерянные биты есть не что иное, как дробный остаток. Отбрасывание этих битов приводит к усечению числа. Оно аналогично усечению чисел при выполнении над целыми числами операции деления (/) в Фортране или при выполнении операции целочисленного деления DIV в Паскале. К сожалению, при сдвиге вправо отрицательных чисел этого не происходит, что видно из следующего примера:

111. . .1111011101 = -35

сдвинем вправо на три разряда, получим

111. . .1111111011 = -5.

В целочисленной арифметике Фортрана и Паскаля деление -35 на 8 даёт -4, а не -5, как в данном примере. Это объясняется тем, что при сдвиге вправо чисел в дополнительном коде округление происходит в меньшую сторону (в алгебраическом смысле). Поэтому -4.375 округляется в меньшую сторону до следующего более отрицательного целого числа. Для того чтобы данный способ деления соответствовал целочисленному делению в Фортране и Паскале, необходимы дополнительные инструкции (см. п. 6 в упр. 6.2).

Рассматривая инструкции сдвига, нельзя не упомянуть ещё об одной инструкции сдвига, которая используется в первую очередь для упаковки и распаковки данных и работает с длинными словами. Ранее было показано, как длинное слово может быть разбито на байты. Однако иногда бывает полезным разбивать длинные слова на части с нечётным числом битов. После этого возникает необходимость в сдвиге содержимого длинного слова с целью выравнивания указанных частей по определённой границе. В гл. 8 подробно обсуждается, зачем нужен такой тип операций. Эта инструкция работает почти так же, как инструкции арифметических сдвигов, за исключением того, что освобождаемые разряды заполняются не нулями и не значением знакового бита, а значениями тех разрядов, которые теряются при сдвиге. Эта инструкция обрабатывает операнд - длинное слово - так, как если бы были замкнуты его концы [3] .

Инструкция имеет формат

ROTL    N,A,B

Так же как в инструкции ASHL, N является 8-битовым числом, задающим количество разрядов, на которое нужно сделать сдвиг. В результате выполнения этой инструкции содержимое длинного слова с адресом A циклически сдвигается на N разрядов и помещается в длинное слово с адресом B. В случае, если N положительно, циклический сдвиг осуществляется влево, если отрицательно - вправо. Например, при циклическом сдвиге числа ^X12345678 на 12 разрядов влево получится число ^X45678123. Напомним, что каждая шестнадцатеричная цифра соответствует четырём битам, следовательно, сдвиг на 12 битов равнозначен сдвигу на три шестнадцатеричных разряда. Сдвиги на количество битов, не кратное четырём, нельзя проиллюстрировать так же просто с помощью шестнадцатеричных чисел: эти числа нужно переводить в двоичный вид. При выполнении инструкции ROTL биты N и Z устанавливаются, как обычно, в зависимости от значений результата, при этом не важно, какие значения имеют биты V и С. После выполнения инструкции бит V сбрасывается, а значение бита С сохраняется.

ОСНОВНЫЕ АЛГОРИТМЫ УМНОЖЕНИЯ И ДЕЛЕНИЯ (ДОПОЛНЕНИЕ)

Пользователям ЭВМ VAX не приходится иметь дело с алгоритмами умножения и деления, поскольку они реализуются аппаратурой процессора ЭВМ VAX. Однако некоторые ЭВМ не имеют инструкций умножения и деления. К ним относятся некоторые модели семейства PDP-11, а также большинство вычислительных систем на базе 8-разрядных микропроцессоров. В этих ЭВМ умножение и деление должно быть реализовано программно. Настоящий раздел посвящён написанию таких программ.

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

По сути двоичное умножение выполняется так же, как десятичное, но оно проще, поскольку умножение производится только на нуль и единицу. Для простоты в качестве примера рассмотрим умножение содержимого 4-битовых "слов". Пусть необходимо умножить 0110 на 0101:

    0110 
    0101
    0110
   0000
  0110 
 0000   
00011110 (8-битовое произведение)

Восьмой бит (0 слева) вводится в связи с возможным переносом.

Отметим, что указанная операция состоит из последовательности сдвигов множимого, за которыми могут следовать операции сложения сдвинутых значений множимого. Сдвинутое значение множимого складывается с произведением, если значение соответствующего бита множителя равно 1. Если этот бит содержит 0, сложения не происходит. Классический способ реализации этого алгоритма состоит в использовании трёх регистров. Два из них содержат множимое и множитель, а третий используется как накопитель, в котором последовательным суммированием вычисляется произведение. Он называется сумматором. Сумматор и регистр множителя обычно объединяются в один сдвоенный регистр для проведения общего сдвига. После завершения операции умножения в этих двух регистрах будет находиться произведение двойной длины.

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

Шаг 1. Поместить множитель и множимое в соответствующие регистры и очистить регистр-сумматор.

Шаг 2. Если младший бит регистра множителя равен 1, прибавить множимое к значению в сумматоре.

Шаг 3. Сдвинуть содержимое сдвоенного регистра (сумматора и множителя) на один разряд вправо.

Шаг 4. Повторить шаги 2 и 3 столько раз, сколько битов содержит регистр множителя.

Шаг 5. В сдвоенном регистре (сумматора и множителя) теперь находится произведение двойной длины.

В качестве примера рассмотрим, что происходит на каждом шаге алгоритма при умножении чисел 0110 и 0101.

Шаг алгоритма Регистр-сумматор Регистр-множитель Регистр-множимое
1 0000 0101 0110
2 0110 0101  
3 0011 0010  
2 0011 0010  
3 0001 1001  
2 0111 1001  
3 0011 1100  
2 0011 1100  
3 0001 1110  
5 Произведение равно 00011110  

Для данного алгоритма стоит отметить следующее.

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

Во-вторых, такой алгоритм работает с числами без знака. Но если его немного модифицировать, то можно использовать и для чисел со знаком. В случае отрицательного множимого модификация будет заключаться в том, что при сдвиге вправо пустые разряды слева должны заполняться значением знакового бита, так же как при выполнении инструкций семейства ASH. В случае отрицательного множителя ситуация несколько усложняется. Напомним, что n-битовое отрицательное число х в дополнительном коде должно интерпретироваться как х-2n. С учётом этого можно модифицировать алгоритм следующим образом: если множитель отрицательный, то необходимо на шаге 5 алгоритма вычесть множимое из содержимого сумматора. Так как непосредственно перед шагом 5 текущее произведение было сдвинуто вправо после прибавления коэффициента 2n-1, то вычитание будет соответствовать умножению множимого на -2n.

На рис. 6.3 представлена программа, реализующая этот алгоритм. Программа выполняет умножение чисел со знаком во многом аналогично тому, как работает инструкция EMUL: выполняется умножение 32-битовых чисел, содержащихся в регистрах R0 и R1; их произведение, имеющее формат квадраслова, помещается в эти же регистры. Отметим, что две инструкции MOVL с меткой 30$ можно было бы заменить одной инструкцией MOVQ MP,R0. Именно так и нужно сделать, так как две инструкции MOVL были использованы исключительно для наглядности. Обратите внимание на то, что в этой программе применяется новая инструкция BLBC. Она относится к следующей паре инструкций:

Мнемоника

Значение

BLBC    A,LOC

Перейти, если младший бит сброшен

BLBS    A,LOC

Перейти, если младший бит установлен

MP:     .BLKL   1           ; МНОЖИТЕЛЬ
AC:     .BLKL   1           ; СУММАТОР (ДОЛЖЕН БЫТЬ ПОСЛЕ MP)
MC:     .BLKL   1           ; МНОЖИМОЕ
MULT:   CLRL    AC          ; ОЧИСТИТЬ AC
        MOVL    R1,MP       ; ЗАДАТЬ МНОЖИТЕЛЬ
        MOVL    R0,MC       ; ЗАДАТЬ МНОЖИМОЕ
        MOVB    #32,R0      ; УСТАНОВИТЬ СЧЁТЧИК БИТОВ
10$:    BLBC    MP,20$      ; ПРОВЕРИТЬ МЛАДШИЙ БИТ MP
        ADDL2   MC,AC       ; ЕСЛИ 1, СЛОЖИТЬ МНОЖИМОЕ
20$:    ASHQ    #-1,AC,AC   ; ВСЕ СДВИНУТЬ ВПРАВО
        DECB    R0          ; И ПОВТОРИТЬ В ЦИКЛЕ 32 РАЗА
        BNEQ    10$
        TSTL    R1          ; ПРОВЕРИТЬ ИСХОДНОЕ ЗНАЧЕНИЕ MP
        BGEQ    30$         ; ЕСЛИ ЧИСЛО ПОЛОЖИТЕЛЬНОЕ,
        SUBL2   MC,AC       ; TO ВЫЧЕСТЬ MC, ЧТОБЫ ПОЛУЧИТЬ -2**N;
30$:    MOVL    MP,R0       ; ПОМЕСТИТЬ ПРОИЗВЕДЕНИЕ
        MOVL    AC,R1       ; В R0 И R1
        RSB

Рис. 6.3. Программа умножения

По инструкции BLBC будет осуществлён переход по адресу LOC, если младший бит содержимого ячейки с адресом A сброшен. Поскольку этот бит - самый младший, не имеет значения, является ли A адресом байта, слова, длинного слова или квадраслова. По инструкции BLBS будет осуществлён переход, если младший бит установлен. Эти инструкции обычно используются для проверки установки признаков ошибок в программах. Оказались полезными они и для реализации шага 2 данного алгоритма.

Необходимо также отметить, что не предпринималось никаких мер для увеличения длины сумматора на один разряд с целью предотвращения переполнения. Это означает, что программа не будет работать, если значение множимого будет слишком большим. Эту проблему можно разрешить, добавив в программу дополнительные инструкции (см. п. 9 упр. 6.2).

Деление может выполняться по алгоритму, который является обратным алгоритму умножения. Но для выявления ситуаций переполнения и деления на нуль в него должна быть включены некоторые дополнительные проверки. Здесь, как и прежде, используется сдвоенный регистр сдвига, состоящий из двух частей: регистра-сумматора и регистра частного. Чтобы исключить возможность переполнения, длину сумматора необходимо увеличить на один разряд, так же как в алгоритме умножения. Первоначально делимое двойной длины помещается в сдвоенный регистр сдвига. Алгоритм описывается следующим образом.

Шаг 1. Поместить делимое двойной длины в сумматор (старшую часть) и в регистр частного (младшую часть). Поместить делитель в регистр делителя.

Шаг 2. В случае, если делитель меньше или равен той части делимого, которая содержится в сумматоре, закончить вычисления с признаком переполнения или деления на нуль.

Шаг 3. Сдвинуть содержимое сдвоенного регистра сумматора частного на один разряд влево.

Шаг 4. В случае, если делимое меньше или равно содержимому сумматора, вычесть делимое из содержимого сумматора и установить младший бит регистра частного равным 1.

Шаг 5. Повторить шаги 3 и 4 столько раз, сколько битов содержит регистр частного.

Шаг 6. Теперь в регистре частного находится частное, а в регистре сумматора - остаток. Рассмотренный алгоритм работает с числами без знака. Для работы с числами со знаком алгоритм необходимо модифицировать (см. п. 11 упр. 6.2).

УПРАЖНЕНИЯ 6.2

  1. Перемножьте следующие пары шестнадцатеричных чисел, представляющих содержимое слов, которые интерпретируются как числа со знаком, и укажите, какие операции приведут к переполнению при выполнении инструкций MULW2:
    а)

    ^X0014;
    ^X002E;

    б)

    ^X00FC;
    ^X0088;

    в)

    ^XFFFF;
    ^X0001;

    г)

    ^XFFFF;
    ^X0002;

    д)

    ^XFFFF;
    ^XFFFF;

    е)

    ^XFF01;
    ^XFEFF.

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

    ^X0DCB/^X002C;

    б)

    ^X0163/^X7716;

    в)

    ^X0B8C/^X0000;

    г)

    ^X0000/^X0B8C;

    д)

    ^XFFF8/^XFFFB;

    е)

    ^X8000/^XFFFF.

  3. Площадь треугольника равна A = b*h/2, где b - длина основания и h - высота. Напишите программу на языке ассемблера для ЭВМ VAX, которая вводит значения b и h, вычисляет и печатает значение А. Программа должна выполняться в цикле до тех пор, пока в качестве входного значения b не будет введён 0. Для представления чисел используйте формат длинного слова.
  4. Напишите программу на языке ассемблера, которая вводит список чисел и выводит на печать их среднее арифметическое. Длина списка может быть различной, но ввод списка заканчивается, когда в нём встречается число 0 (число 0 не учитывается при вычислении среднего арифметического).
  5. Простым числом называется целое число, большее или равное 2, которое не имеет других множителей, кроме 1 и самого себя. Напишите программу на языке ассемблера, которая вычисляет и распечатывает первые 50 простых чисел.
  6. Какие инструкции следует использовать совместно с инструкцией ASHL, чтобы при сдвигах вправо округление дробных остатков и положительных, и отрицательных чисел производилось бы без ошибок?
  7. Напишите программу на языке ассемблера, которая вводит 10 пар чисел в формате длинного слова и интерпретирует их как числа двойной точности со знаком (64 бита). Ваша программа должна вывести на печать две пары чисел, интерпретируемые как наибольшее и наименьшее числа двойной точности.
  8. Проанализируйте программу, показанную на рис. 6.3, Как можно модифицировать эту программу, чтобы включить в неё дополнительный операнд, аналогичный третьему операнду инструкции EMUL? Внесите необходимые изменения и напишите основную программу, которая вводит некоторое количество троек чисел и производит над каждой тройкой операции умножения с суммированием, причём сначала с помощью инструкции EMUL. Программа должна выводить на печать оба множества результатов для сравнения.
  9. * В дополнение к модификациям, сделанным в п. 8. упр. 6.2, модифицируйте программу так, чтобы устранить возможность переполнения из-за того, что длина сумматора на один разряд короче, чем необходимо. Выполните программу с критическими значениями исходных данных и убедитесь, что результаты совпадают с результатами выполнения инструкции EMUL.
  10. Напишите и протестируйте программу на языке ассемблера, реализующую алгоритм деления чисел без знака, приведённый в последнем разделе данной главы.
  11. * Как можно модифицировать алгоритм деления чисел без знака, приведённый в последнем разделе данной главы, чтобы он мог работать для чисел со знаком? Реализуйте алгоритм в виде подпрограммы на языке ассемблера и убедитесь в том, что при выполнении подпрограммы получаются те же результаты, что и при выполнении инструкции EDIV. Как быть в тех случаях, когда возникает переполнение из-за того, что делитель оказывается слишком большим и сумматору не хватает одного бита для представления правильного результата? Как можно учесть эти ситуации? Протестируйте вашу программу на критических значениях исходных данных.

 

< НАЗАД ОГЛАВЛЕНИЕ ВПЕРЁД >


[1] Аналогия с часами в данном случае относится только к сложению и вычитанию положительных чисел.

[2] Эти инструкции называются инструкциями арифметического сдвига. - Прим. ред.

[3] Операнд по сути рассматривается как замкнутое кольцо битов. Подобные инструкции называются инструкциями циклического сдвига. - Прим. ред.

Performed by © gid, 2012-2024.