Команда ZAP. Пересылка десятичной информации

{toc_noshowall}В предыдущем разделе были рассмотрены команды, реализующие арифметические операции над десятичными числами, а также команда десятичного сравнения. Теперь мы перейдем к рассмотрению некоторых команд, позволяющих производить различного рода пересылки десятичных чисел. Конечно же, команда MVC также может быть использована в этих целях, ведь она предназначена для выполнения пересылок произвольного содержимого ячеек памяти. Однако команды, которые будут рассмотрены нами теперь, являются более специальными: они могут производить укорачивание или удлинение операндов наряду с непосредственной пересылкой, с их помощью можно также выполнять сдвиги информации.

Вы помните, что при рассмотрении команды МР мы столкнулись с определенного рода проблемой. Необходимо было каким-то образом организовать дополнение десятичного числа спереди желаемым количеством нулей. Эта задача была нами решена простой пересылкой числа в область, предварительно заполненную нулями. Команда ZAP (СЛОЖЕНИЕ С ОЧИСТКОЙ) позволяет получить тот же результат гораздо более эффективно. Эта команда также может оказаться чрезвычайно полезной в некоторых других ситуациях.

ZAP D1(L1,B1),D2(L2,B2)

Zero and Add Packed

D1 +(B1)?0L1;D1+(B1)? (D1 +(B1))L1 + (D2 + (B2))L3

Выполнение команды ZAP проходит в два этапа. Сначала поле первого операнда заполняется десятичными нулями. Первоначальное содержимое этой L1-байтовой области при выполнении команды не играет роли. Затем второй операнд складывается по правилам десятичной арифметики с «очищенным» первым операндом. Этот шаг практически выполняется аналогично выполнению команды АР с нулевым первым операндом.

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

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

ZAP С(7),В(5)

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

678901234D

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

С: 0000678901234D

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

ZAP С(3),В(5)

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

Возвращаясь к примеру использования команды МР, описанному в разд. 16.2.2, где требуется умножить 5-байтовое десятичное число, находящееся в памяти по адресу NUM1, на 6-байтовое число, расположенное по адресу NUM2, мы теперь знаем, каким образом можно дополнить первый операнд таким количеством незначащих нулей, чтобы произведение поместилось в поле первого операнда независимо от содержимого полей NUM1 и NUM2. Использование команды ZAP, производящей пересылку NUM1 в некоторую рабочую область и одновременно дополняющей его нужным количеством нулей, позволяет достичь желаемой цели:

ZAP ТЕМР1(11),NUM1(5)

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

1038000 10С

то в результате выполнения команды ZAP содержимое поля TEMP примет следующий вид:

TEMPI: 0000000000001038000 10С

независимо от его первоначального содержимого. Значение вырабатываемого признака результата будет равно 2. Далее следует выполнить ту же, что и раньше, команду МР:

МР ТЕМР1(1 !),NUM2(6)

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

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

MVC NUM — 2(5), NUM

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

NC NUM + 2(3), = X'F0000F'

Таким образом, если исходное содержимое области памяти, начинающейся по адресу NUM—2, выглядело как

NIJM—2: FECB038602795C

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

0386027950000С

Итак, пара команд MVC и NC может быть использована для получения произведения произвольного числа на любую четную степень 10 или для сдвига десятичной информации влево на любое целое число байтов. Но как быть, если требуется что-либо умножить на нечетную степень числа 10? Ведь выполнение подобного действия включает сдвиг содержимого памяти на 4 бита. Как, кроме того, можно использовать сдвиги для выполнения деления десятичных чисел?

ЭВМ Системы 360 не предусматривают непосредственных ответов на поставленные вопросы. В подобных случаях могут быть использованы команды MVZ, MVN и MVO, однако применение команд МР и DP позволяет обычно получить те же результаты более эффективно и, главное, более просто. Все же, как мы увидим в дальнейшем, команды MVZ, MVN и MVO в некоторых случаях могут оказаться весьма полезными.

ЭВМ Системы 370 реализуют специальную команду десятичной арифметики SRP (СДВИГ С ОКРУГЛЕНИЕМ ДЕСЯТИЧНЫЙ), которая является настоящей командой десятичного сдвига. Поскольку возможностью выполнения команд MVZ (ПЕРЕСЫЛКА ЗОН), MVN (ПЕРЕСЫЛКА ЦИФР) и MVO (ПЕРЕСЫЛКА СО СДВИ¬ГОМ) обладают, все ЭВМ Системы 370, то мы сначала рассмотрим их, а затем перейдем к рассмотрению команды SRP и определяемых ею дополнительных возможностей.

 

MVZ D1(L,B1),D2(B2)

MoVe Zones

Зонные части (D1+(B1))? зонные части (D2 + (B2))L

MVN D1(L,B1),D2(B2)

MoVe Numeric

Цифровые части (D1 + (В 1))? цифровые части (D2+(B2))L

MVO D1(L,B1),D2(B2)

MoVe with Offset

См. текст

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

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

Пусть

Р1: 12345678

Р2: ABCDEABC

Выполнение команды

MVZ Р1(4),Р2

даст следующий результат:

Зонные части Р2 записаны в Р1 вместо его зонных частей. Аналогично по команде

MVN Р1(4),Р2

производится замещение вторых половин всех байтов поля Р1 вторыми половинами соответствующих байтов поля Р2:

Р2: ABCDEABC

PI: 1B3D5A7C

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

MVZ Р1 + 1(3),Р1

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

По команде

MVN Р2(2),Р2+2

цифровые части двух последних байтов Р2 будут сдвинуты на 2 байта влево:

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

NUM: 3456789С

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

NUM: 345С789С

такой сдвиг можно осуществить с помощью команды

MVN NUM+l(l),NUM+3

Отметим, что в результате выполнения команды DP мы получили бы другой ответ. Это объясняется тем, что в данном случае мы не позаботились о вычислении остатка. Полученное нами число является двухбайтовым, и поэтому при последующем использовании его в качестве операнда команд десятичной арифметики на месте длины следует указывать 2.

Команда MVZ бывает очень полезна при выполнении различного рода преобразований данных. Например, при переводе числа из кода EBCDIC в зонный десятичный формат необходимо, в частности, поместить внутрь числа соответствующий код знака. Если код знака должен быть помещен по адресу INPT+3, то пара команд

OI INPT 4-3,X'F0'

N1 INPT +3,X'DF'

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

MVZ INPT+3(l),MZONE

при условии, что наша программа содержит предложение

MZONE DC X'DO'

Рис. 16.1. Команда MVO. Сплошные вертикальные линии обозначают границы байтов, пунктирные — полубайтов. До начала выполнения G содержит FFFFFD.

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

MVZ OUTPT+5(l), = X'FO'

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

Пересылка производится по одному байту, при этом порядок рассмотрения байтов второго операнда последовательный, справа налево. Фактически, команда MVO выполняется так же, как и MVC. Отличия заключаются только в «косой» пересылке (рис. 16.1), или пересылке со сдвигом, возможности дополнения незначащих нулей и отсутствии фиксации переполнения.

На рис. 16.1 приведена общая схема выполнения команды MVO. Здесь же приведен конкретный пример. Еще несколько примеров, вероятно, окончательно прояснят для вас назначение и способ выполнения команды MVO. Пусть . ;

NUMS: 123456789С

ANS: АААААААА

Ниже приведены выполняемые команды и соответствующие результаты:

MVO ANS(2),NUMS(1)

ANS: 012ААААА

MVO ANS(3),NUMS(1)

ANS: 00012AA A

MVO ANS(3),NUMS(2)

ANS: 01234AAA

MVO ANS(2),NUMS(5)

ANS: 89CAAAAA

Рассмотрим еще один пример. Предположим, что по адресу Q записано 4-байтовое десятичное число:

Q: 6742198С

Требуется умножить это число на 1000, т. е. сдвинуть его влево на три позиции. Предположим, кроме того, что окончательный результат мы хотим получить в виде 8-байтового числа, записанного по адресу PROD, и что первоначально содержимое PROD выглядит как

PROD: FFFFFFFFFFFFFFFF

Выполним сначала «косую» пересылку содержимого Q в область PROD.

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

MVO PROD(7),Q(4)

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

PROD: 000006742198CFFF

Теперь код знака из поля Q помещаем в соответствующее место поля PROD:

MVN PROD+7(l),Q+3

После выполнения этой команды содержимое области PROD будет иметь следующий вид:

000006742198CFFC

Заменяем три последние цифры нулями:

NC PROD+6(2),=X'OOOF'

Окончательно будем иметь

PROD: 000006742198000С

т. е. именно то, что мы и хотели получить.

Эта команда, предусмотренная в ЭВМ Системы 370, придает материалу предыдущего раздела в значительной мере только теоретический интерес. По команде SRP (СДВИГ С ОКРУГЛЕНИЕМ ДЕСЯТИЧ¬НЫЙ) можно производить сдвиги десятичных чисел на любое количество десятичных позиций вправо и влево, заполняя одновременно освобождающиеся части информационных полей нулями. При сдвиге вправо получаемые результаты можно округлять в соответствии с правилами, заданными программистом. Команда SRP является командой формата SS, правда, несколько необычной.

SRP D1(L1,B1),D2(B2),L3

Shift and Round Packed

Сдвинуть (D1 +(B1))L1(IBM 370) на (D2+ (B2)) позиций влево и округлить, используя в качестве коэффициента 13

Первый байт поля операнда имеет адрес D1+(B1). L1 определяет длину в байтах операнда, находящегося в памяти по этому адресу. Последние 6 битов числа D2+(В2) задают величину и направление сдвига точно так же, как в аналогичной команде двоичного сдвига. Отрицательное значение представляется дополнительным кодом. Если такое 6-битовое число является положительным, то сдвиг происходит влево, если отрицательным — то вправо. Величина сдвига в обоих случаях определяется значением модуля числа. Например, если группа из последних шести битов (битов 26—31) содержит 0001012, то содержимое первого операнда команды будет сдвинуто влево на пять десятичных позиций. Если число, определяющее направление и величину сдвига, имеет значение 1111002, то сдвиг будет осуществлен на четыре позиции вправо.

В поле 13 указывается коэффициент округления. На языке ассемблера коэффициент округления может быть задан с помощью одной десятичной цифры. Для того чтобы понять, как происходит выполнение команды SRP с использованием коэффициента округления, попытаемся точно установить, что же на самом деле понимается под округлением чисел. Предположим, что необходимо округлить заданное число до ближайшего целого. При округлении 12.7 получится 13, при округлении 1.4 — 1, при округлении 6.5 — 7. В данном случае работает следующий алгоритм: если дробная часть числа по модулю менее 0.5, то в качестве результата округления выбирается наибольшее целое, не превосходящее данное число; в противном случае выбирается значение на единицу большее.

Формально это можно записать следующим образом:

R(x)=[x+0.5]

R(x) в данном случае представляет результат округления,

[x+0.5]= целая часть у

Итак, 0.5 представляет коэффициент округления при округлении до ближайшего целого. Достаточно часто в качестве коэффициентов округления используются также 0 (округление вниз или усечение) и 1 (округление вверх).

Сдвиг десятичного числа вправо соответствует его делению на степень 10. Например, если 1278 сдвинуть вправо на две позиции, то мы получим 12.78, которое может быть округлено до ближайшего целого (коэффициент округления 0.5):

R () =R(12.78) = [12.78 + 0.5]=13

При выполнении команды SRP в качестве коэффициента округления берется число, получаемое от деления 13 на 10. По окончании выполнения сдвига 13 добавляется к последней вышедшей справа за пределы разрядной сетки операнда цифре. Получающееся значение переноса прибавляется к результату сдвига. Таким образом, выполнение команды SRP с 13=5 и значением первого операнда, равным

01278С

даст при сдвиге вправо на две позиции:

Выполнение той же самой команды со значением операнда

01213С

будет проходить следующим образом:

Команда SRP имеет обычный формат SS. Отличие заключается в том, что вместо длины второго операнда (L2) в данном случае указывается коэффициент округления (13).

Например, по команде

F072C2004002

будет произведен сдвиг 8-байтового (не забывайте добавлять 1 к L1—1) десятичного числа, находящегося в памяти по адресу 2016 + (12); при этом в качестве коэффициента округления будет использовано 0. 2. Величина и направление сдвига определяются значением последних шести битов суммы 2+(4).

При выполнении команды SRP происходит предварительная проверка содержимого операнда команды. Если операнд не является правильным упакованным десятичным числом, то фиксируется особый случай в данных. Если производится попытка «выдвижения» старших значащих цифр числа за пределы разрядной сетки, то фиксируется десятичное переполнение. Признак результата вырабатывается по общим правилам для команд десятичной арифметики АР и SP.

Применение команды SRP не вызывает затруднений. Количество десятичных позиций, на которое желательно произвести сдвиг, засылается в регистр В2, который указывается в качестве базового при адресации второго операнда. В качестве коэффициента округления обычно используется либо 0.5, либо 0 (0.5 для округления до ближайшего целого, 0 — для округления вниз). Остается только указать адрес первого операнда.

Пусть, например, по адресу SNO записано 6-байтовое десятичное число, и мы его хотим умножить на 1000, т. е. сдвинуть на три десятичные позиции влево.

Используем регистр 7 для хранения информации о направлении и величине сдвига:

LA 7,3

SRP SNO(6),0(7),5

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

00000368427D

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

00368427000D

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

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

ZAP TEMP(8),SNO(6)

LA 7,3

SRP TEMP(8),0(7),5

Предположим теперь, что по адресу BALL находится 5-байтовое десятичное число и что это число мы хотим разделить на 10000=104,. округляя результат до ближайшего целого. Поскольку для выполнения деления необходим сдвиг вправо, в соответствующий регистр мы должны поместить число —4. Выполнение приводимых ниже команд даст следующие результаты:

LH `7, = Н' — 4'

SRP BALL(5),0(7),5

До

После

Признак результата

BALL: 010358723D

000001036D

1

000000100С

000000000С

0

000056607С

000000006С

2