Преобразование данных

 

{toc_noshowall}Теперь, вероятно, вы уже поняли, в чем заключается проблема, связанная с преобразованием данных. Раньше считывание десятичных чисел с карт мы выполняли с помощью макро RWD. Результатом работы RWD было полное слово, содержащее исходное число, записанное в двоичной форме. Выполнение RWD на самом деле заключалось в вызове подпрограммы преобразования исходных EBCDIC-кодов в зонный формат, зонного формата в упакованный и, наконец, упакованного в обычную двоичную запись. WWD производило описанные преобразования в обратном порядке, и в конце концов результирующая строка EBCDIC-кодов выводилась устройством печати. Прочитав данную главу, вы сможете обходиться без макро RWD и WWD, поскольку вам будет известно, как можно организовать выполняемые ими преобразования без них.

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

Рис. 15.2. Взаимосвязь преобразований для рассматриваемых форматов данных.

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

А1. Устройство чтения с перфокарт осуществляет перевод кодов Холлерита в коды EBCDIC. Полученная строка кодов EBCDIC записывается в память.

А2. Для определения знака числа исследуется знаковый символ, при этом предполагается, что выполнены обычные соглашения относительно кодирования знаков. Такое исследование обычно означает проверку на наличие знаков + или — перед числом, а также, возможно, на отсутствие знака. Если код знака присутствует, то он удаляется и в код последнего символа вносится соответствующее изменение для получения зонного формата.

АЗ. По команде PACK число из зонного формата преобразуется в упакованный формат. Полученное число можно использовать в качестве операнда в командах десятичной арифметики.

А4. По команде CVB (Convert to Binary — ПРЕОБРАЗОВАНИЕ В ДВОИЧНУЮ) число из упакованного формата переводится в привычный нам двоичный и записывается в виде полного слова. Теперь число можно использовать в качестве операнда в командах арифметики с фиксированной точкой.

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

В1. По команде CVD (Convert to Decimal — ПРЕОБРАЗОВАНИЕ В ДЕСЯТИЧНУЮ) исходное число преобразуется в упакованный формат.

В2. После выполнения команды UNPK (Unpack — РАСПАКОВАТЬ) получается десятичное число в зонном формате.

ВЗ. Исследуется содержимое знаковой зоны, и перед числом помещается код соответствующего алгебраического знака. В знаковую зону помещается F16, и, таким образом, преобразование исходного числа в код EBCDIC завершено.

В4. Полученные EBCDIC-коды посылаются устройству печати в качестве очередной части выводимой строки. Производится окончательный перевод кодов EBCDIC в печатный текст.

Преобразование кодов Холлерита в коды EBCDIC и кодов EBCDIC в печатные символы (шаги А1 и В4) выполняются автоматически. Рассмотрим теперь выполнение представляющих интерес для программиста шагов А2, АЗ, А4, В1, В2, ВЗ. В процессе ввода-вывода перечисленные шаги выполняются именно в этом порядке.

 

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

rfcdddd

где d означает любую цифру от 0 до 9. Пусть это число записано по адресу EBCDICNO. На рис. 15.3 приведены команды, обеспечивающие определение знака и запись соответствующего кода в знаковую зону. Последняя команда заменяет код знака на 0.

Проследим выполнение этих команд на примере. Пусть ячейка EBCDICNO содержит коды символов числа — 1234. Команды 1 и 2 определяют, что это число отрицательное. Команда 5 выполняет логическое умножение последнего байта числа на DFle. Последний байт содержит F4ie, поэтому в результате получается D4]e. Теперь в EBCDICNO мы имеем

60F1F2F3D4

И наконец, команда 6 производит замену знакового кода 6010 на F016. Таким образом, окончательно получается

F0F1F2F3D4

Это и есть исходное число в зонном формате.

В рассмотренном примере предполагалось выполнение значительно более сильных ограничений на формат обрабатываемого числа, чем это на самом деле требуется. В качестве примера преобразования EBCDIC — зонный формат, не требующего выполнения столь сильных условий, рассмотрим преобразование кодов чисел, состоящих из десяти знаков. В качестве первого знака может выступать один из алгебраических знаков, + или —. Число с отсутствующим алгебраическим знаком считается положительным. Пробелы рассматриваются как нули. Последними знаками чисел должны быть цифры. (Описанный формат напоминает формат ПО ФОРТРАНа.)

Рис. 15.3. Преобразование 5-значного числа из кода EBCDIC в зонный формат.

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

,

На рис. 15.4 изображена программа, преобразующая коды EBCDIC чисел, удовлетворяющих сформулированным условиям, в зонный формат. При этом предполагается, что исходные коды хранятся по адресу EBNO. Этот пример отличается от предыдущего, прежде всего тем, что в нем производится проверка на наличие знакового кода. В предыдущем примере предполагалось, что первый байт числа всегда содержит знаковый символ. Теперь же нужно искать первый значащий символ. В программе на рис. 15.4 предполагается, что этим символом могут быть знаки + или — или цифра от 0 до 9. Если знаковый символ присутствует, то в знаковую зону записывается соответствующий код, а сам знак заменяется нулем. Если же первым отличным от пробела символом является цифра, то число считается положительным и в знаковую зону записывается код плюса.

Рис. 15.4. Преобразование 10-значного числа из кода EBCDIC в зонный формат.

В программе, изображенной на рис. 15.4, первые три команды производят начальную установку регистров 2, 4 и 5, использующихся в процессе поиска первого отличного от пробела символа в области EBNO. Если такой символ найден, осуществляется переход на FOUND. Заметим, что в регистре 2 в этот момент находится адрес байта, содержащего код найденного символа. Затем производится проверка символа и выполняются действия, аналогичные рассмотренным в программе рис. 15.3.

 

Такое преобразование осуществляется в случаях, когда вводимую числовую информацию необходимо в дальнейшем использовать для выполнения арифметических операций. Преобразование производится с помощью специально для этого предназначенной команды PACK (УПАКОВАТЬ)

_______________________________________________________________

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

PACK

Преобразовать D2 + (B2))L2 К виду упакованного десятичного и поместить в (D1 —(— (В1))L1

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

В обычных командах (например, MVC) второй байт содержит уменьшенную на 1 длину операндов. В командах с раздельным указанием длин второй байт подразделяется на две 4-битовые части, каждая из которых содержит уменьшенную на 1 длину соответствующего операнда. Кодом операции, соответствующей команде PACK, является F2. В команде

PACK 5(10,3),64(12,4)

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

F29B30054040

Поскольку максимальным числом, которое можно записать в четырех двоичных разрядах, является 15 и поскольку реальная длина операндов на 1 превышает указываемую в машинных командах, максимальная длина операндов команд формата SS рассматриваемого типа равна 16 байтам.

По команде PACK зонное десятичное число длиной в L2 байтов, хранящееся в памяти по адресу D2+(B2), переводится в упакованную форму. Получившийся результат записывается в L1-байтовое поле по адресу D1+(B1). Непосредственный процесс проведения преобразования нами уже был рассмотрен. Два последних полубайта второго операнда меняются местами, получившийся код записывается в последний байт первого операнда. Затем последовательно, справа налево удаляются все зонные части. Оставшиеся цифры группируются по две и такими группами записываются в последовательные байты области, определенной в качестве первого операнда. Второй операнд при этом не изменяется.

Рассмотрим простой пример. Предположим, что мы имеем зонное десятичное число

F2F3D6

хранящееся по адресу ZD. Команда

PACK PD(2),ZD(3)

выполняет упаковку 3-байтового зонного числа, записанного по адресу ZD, в 2-байтовое число и помещает его по адресу PD. В результате в PD получится 236D.

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

PACK А(3),В(4)

Пусть первоначально содержимое В имеет вид

В: F1F2F6C3

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

А: 01263С

Очевидно, что минимальным количеством (М) байтов, необходимым для записи упакованного числа, содержащего N цифр, является

M =  + 1

[x] в данном случае означает целую часть числа х.

Таким образом, для записи 8-значного упакованного числа требуется по крайней мере

+1 = 5

байтов, столько же, сколько и для записи 9-значного числа.

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

D: F7F2F4D5

команда

PACK F(6),D(4)

выполнит преобразование 4-значного зонного числа, хранящегося по адресу D, в упакованное число. Результат будет помещен в виде 6-байтового упакованного числа в область F. Итак, окончательно мы получим

F: 00000007245D

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

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

G: F2F5F6F4F7F8C2

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

PACK H(2),G(7) .

получится

Н: 782С

Первые четыре цифры зонного десятичного числа G игнорируются.

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

Q: 23Е49С25

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

PACK S(5),Q(4)

даст

0000034С52

Описанное свойство команды PACK дает возможность применять ее для преобразования шестнадцатеричной информации в двоичную (см. упражнение 7 в конце данной главы). Содержимое зонных частей всех байтов зонных десятичных чисел, за исключением последнего, никак не влияет на окончательный результат, т. е. на получающееся упакованное число. Старшие байты второго операнда команды PACK, содержащие нули в цифровой части, не учитываются при получении чисел в упакованном формате. Заметим, что коды пробела и знака «—» содержат нули в цифровой части. Рассмотрим 6-байтовое представление числа —100. На перфокарте оно может быть набито в виде

bb—100

В коде EBCDIC это же выглядит как

404060F1F0F0

Для обеспечения возможности преобразования этого числа в упакованный формат необходимо поместить D в знаковую зону:

404060F1F0D0

Если эта информация находится в памяти по адресу HDRED, то выполнение команды

PACK PD(4),HDRED(6)

даст

PD: 0000100D

Другими словами, если старшие байты числа содержат коды пробелов или минусов, то при выполнении преобразования вместо них получатся незначащие нули. Отметим, что со знаком + дело обстоит иначе: его EBCDIC-код равен 4Е16. Выполнение

PACK PD(4),HDRED(6)

в случае

HDRED: 40404EF1F0CQ

приведет к результату

000E100C

Очевидно, это совсем не то, что нам нужно, поскольку ни одна из цифр полученного упакованного числа не должна превышать А10.

 

Такие преобразования обычно выполняются с помощью команды CVB (Convert to Binary — ПРЕОБРАЗОВАНИЕ В ДВОИЧНУЮ).

CVB R1,D2(X2, В2) ConVert to Binary R1 двоичный

эквивалент

(D2+(X2)+(B2))D

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

Нетрудно догадаться, как производится само преобразование. Пусть двойное слово PD содержит

PD: 0000000000000 ЮС

(десятичное 10). После выполнения команды

CVB 6,PD

в регистре G получится

(6) =0000000А

что является двоичным эквивалентом числа 10. Если бы содержимым PD являлось

PD: 000000000000032D

(десятичное —32), то выполнение этой же команды дало бы

(6) = FFFFFFE0

При выполнении команды CVB могут быть зафиксированы два особых случая. Первый из них, особый случай деления с фиксированной точкой, распознается тогда, когда исходное число слишком велико, чтобы уместиться в 32 двоичных разрядах (т. е. оно меньше чем —2147483648 или больше чем 2147483647). Если содержимое второго операнда команды не является упакованным десятичным числом, то фиксируется особый случай в данных.

При проведении различного рода преобразований данных всегда необходимо помнить, что для перевода числа из упакованного десятичного формата в двоичный его требуется сначала записать в двойное слово. Таким образом, если нам хочется получить двоичную форму числа, то упаковывать его лучше сразу в двойное слово, т. е. указывая 8 в качестве длины первого операнда в команде PACK. Пусть, например, необходимо произвести преобразование 3-байтового зонного десятичного числа в двоичное и результат поместить в регистр 7. Выполнить требуемое преобразование можно с помощью команд

PACK PD(8),ZD(3)

CVD 7,PD

где память для PD резервируется так:

PD DS 1D

 

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

На первом этапе исходное двоичное число преобразовывается в упакованное десятичное. По команде CVD (Convert to Decimal — ПРЕ-ОБРАЗОВАНИЕ В ДЕСЯТИЧНУЮ) содержимое регистра первого операнда переводится в упакованную десятичную форму и записывается в память по указываемому в команде адресу.

CVD R1,D2(X2,B2)

ConVert to Decimal

Десятичный эквивалент (R1) — D2 + (Х2) + (B2)D

Второй операнд команды CVD является двойным словом. В это двойное слово записывается получившееся упакованное число, которое спереди в целях выравнивания дополняется необходимым количеством нулей.

Итак, если

(5)=00000340

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

CVD 5,PD

получим

PD: 000000000000832С

При этом предполагается, что PD определено в программе как двойное слово. (Если PD не выровнено по границе двойного слова, фиксируется особый случай спецификации.)

Аналогично, если

(6)=FFFFFF40

и PACDECK определено как двойное слово, то результатом выполнения команды

CVD 6,PACDECK

будет

PACDECK: 000000000000192D

что представляет отрицательное число.

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

 

Теперь для получения очередного приближения к EBCDIC-представлению число необходимо «распаковать». Это значит, что его нужно пополнить зонными кодами и половинки последнего байта поменять местами. Все эти действия выполняются командой UNPK (Unpack — РАСПАКОВАТЬ), работающей совершенно аналогично команде упаковки (PACK).

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

UNPacK

Преобразовать (D2 + (B2))L2 в зонное десятичное и за- писать в (D1+(B1))LI

Содержимое L2-байтового поля, начинающегося по адресу D2+(B2), должно представлять выровненное по правому краю поля упакованное десятичное целое. По окончании выполнения команды в L1 байтах, начинающихся байтом D1+(B1), будет записано требуемое число в зонном формате. Выполнение распаковки проходит в несколько этапов. Сначала половинки последнего байта второго операнда меняются местами и результат помещается в последний байт области первого операнда. Затем, начиная с правой половины предпоследнего байта исходного числа, последовательно просматриваются все его цифры. Происходит преобразование в соответствующие коды путем добавления к каждой цифре зонной части. Каждый получившийся код записывается в очередной байт первого операнда, пока вся L1-байтовая область не будет заполнена. Содержимое второго операнда командой UNPK не меняется.

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

PD: 50134D

ZD: 7777777777

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

UNPK ZD(5),PD(3)

На первом шаге цифры 4 и D поменяются местами, и результат запишется в ZD:

ZD: 77777777D4

Затем цифра 3 дополнится спереди кодом F, и, таким образом, получится

ZD: 777777F3D4

Следующей рассматриваемой цифрой является 1:

ZD: 777777F1F3D4

затем 0

ZD: 77F0F1F3D4

и, наконец, 5

ZD: F5F0F1F3D4

Мы получили окончательный результат.

N-байтовое упакованное число в зонном виде требует для своего размещения не менее 2N—1 байтов. Если длина первого операнда UNPK больше, чем требуется для записи преобразованного числа, то неиспользованная область будет заполнена кодами F0, что эквивалентно добавлению к числу слева незначащих нулей. Если же длина первого операнда слишком мала, то не уместившиеся в отведенную область коды будут утеряны. Пусть, например,

PD: 0123456С

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

UNPK ZD(10),PD(4)

Ее результатом будет

ZD: F0F0F0F0FIF2F3F4F5C6

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

UNPK ZD(3),PD(4)

было бы

ZD: F4F5C6

Если нашей конечной целью является распечатка некоторой строки, то разумно распакованные числа помещать сразу в соответствующие позиции. Таким образом, в качестве длины первого операнда команды UNPK обычно указывается то количество позиций, которое мы хотим отвести для печати данного числа. Например, если 6-байтовое упакованное десятичное число, хранящееся по адресу PD, мы хотим распечатать в виде 12-разрядного числа, начиная с 15-й позиции строки, записанной в области OUTPUT, следует выполнить команду

UNPK OUTPUT+15(12),PD(6)

При этом учитывается, что первый символ строки является символом управления кареткой.

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

DAT: 1ABCDE53

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

UNPK DIS(7),DAT(4)

даст

DIS: F1FAFBFCFDFE35

 

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

...FdFdFdFdSd

где d представляют десятичные цифры, a S — код знака, либо С1с, либо D16. Например, в качестве одного из таких чисел может выступать

ZD: F0F0F0F1F9D7

равное десятичному числу —197. Если число в таком виде будет передано устройству печати в качестве части выводимой строки, то в соответствующем месте листинга будет напечатано

00019Р

Здесь возникли две «ошибки». Во-первых, не был напечатан знак числа, а во-вторых, на месте последней цифры вместо 7 появилось Р. Причина возникновения этих ошибок понятна: число еще не переведено в коды EBCDIC.

Рис. 15.5. Преобразование 10-байтового зонного десятичного числа в код EBCDIC.

Выполнение преобразования в коды EBCDIC происходит в два этапа:

1. Производится проверка кода знака, и соответствующий символ записывается перед числом. В нашем предыдущем примере в качестве кода знака выступал символ D, поэтому при переводе в код EBCDIC число дополнилось бы спереди знаком —. Итак, результатом выполнения первого шага было бы

—0019Р

2. Происходит замена кода знака на символ F. Если использовать число из предыдущего примера, то получится

—00197

что уже больше похоже на требуемый результат.

На рис. 15.5 приведена последовательность команд, выполняющая преобразование 10-байтового зонного числа, хранящегося по адресу ZONEDECI в коды EBCDIC. Здесь использованы некоторые логические команды. По команде ТМ производится сравнение кода знака с D16. Если в результате операции сравнения не выявлены все единицы, т. е. если биты 0, 1 и 3 последнего байта ZONEDECI не все установлены в единицу, то, следовательно, знаковым кодом является С16, и в первый байт числа записывается +. В противном случае число отрицательно, .и в его первый байт записывается —. Команда 01 с меткой SIGNFIX меняет код знака на код зоны Fie.

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

+000013684

или

—000000001

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

Рис. 15.6. Преобразование 10-байтового зонного десятичного числа в код EBCDIC с удалением незначащих нулей.

В результате выполнения этой программы числа могут выдаваться на печать, например, в виде

13684

или

—1

Теперь положительные числа при распечатке не снабжаются знаками.

Аналогичный формат распечатки можно получить также при использовании команд ED и EDMK, рассматриваемых в главе 20.

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