Обработка данных в ASCII-формате 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Обработка данных в ASCII-формате



Для достижения высокой производительности компьютер выполняет арифметические операции над числами в двоичном формате. Этот формат не вызывает особых трудностей, если данные определены в самой программе. Однако во многих случаях новые данные вводятся программой с клавиатуры в виде ASCII символов в десятичном формате. Аналогично вывод информации на экран осуществляется в кодах ASCII. Например, число 2310 в двоичном представлении выглядит как 00010111 или 1716; но в коде ASCII на каждый символ требуется один байт и число 2310 в ASCII-коде имеет внутреннее представление 3233, где 32 – ASCII-код цифры 2, 33 – ASCII-код цифры 3.

Для выполнения арифметических операций над числами, представленными в ASCII-формате, такими как 31323334, требуется специальная обработка. С помощью следующих ассемблерных команд можно выполнять арифметические операции непосредственно над числами, представленными в ASCII-формате:

AAA (ASCII Adjust for Addition - коррекция для сложения ASCII-кода)

AAD (ASCII Adjust for Division - коррекция для деления ASCII-кода)

AAM (ASCII Adjust for Multiplication - коррекция для умножения ASCII-кода)

AAS (ASCII Adjust for Subtraction - коррекция для вычитания ASCII-кода)

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

Сложение в ASCII-формате

Рассмотрим процесс сложения чисел 8 и 4 в ASCII-формате:

34

Сумма: 6C

Полученная сумма неправильна ни для ASCII-формата, ни для двоичного формата. Однако, игнорируя левую 6 и прибавив 6 к C: C16 + 616 = 1210 – получим правильный результат в десятичном формате. Этот пример слегка упрощен, но он хорошо демонстрирует процесс, который выполняет команда AAA при коррекции.

В качестве примера, предположим, что регистр AX содержит код 003816 цифры 810, а регистр BX код 003416 цифры 410. Сложение и коррекция этих чисел кодируется следующими командами:

ADD AL,BL; сложить 34 и 38, в регистре AX содержится 006C

AAA; коррекция после сложения, в регистре AX содержится 0102

Команда AAA проверяет правую шестнадцатеричную цифру (4 бита) в регистре AL. Если эта цифра находится между A и F или флаг AF равен 1, то к регистру AL прибавляется 6, а к регистру AH прибавляется 1, флаги AF и CF устанавливаются в 1. Во всех случаях команда AAA устанавливает в 0 левую шестнадцатеричную цифру в регистре AL. Для получения окончательного ASCII-представления достаточно просто поставить тройки на место левых шестнадцатеричных цифр:

OR AX,3030h; результат в регистре AX 3132

Пример, показанный выше, демонстрирует сложение однобайтных ASCII-чисел. Сложение многобайтных ASCII-чисел требует организации цикла, который выполняет побайтное сложение справа налево с учетом переноса.

Пример 1.5.12. Программа, показанная ниже, складывает два трехбайтовых ASCII-числа в четырехбайтовую сумму. Первое слагаемое 57810 расположено в переменной X в виде массива байтов 35h, 37h, 38h, второе слагаемое 69410 расположено в переменной Y в виде массива байтов 36h, 39h, 34h, сумма размещается в переменной Z.

.data

X byte 35h, 37h, 38h; первое слагаемое 578d

Y byte 36h, 39h, 34h; второе слагаемое 694d

Z byte 4 DUP (0); сумма

S byte '+'; символ суммы для вывода

E byte '='; символ равенства для вывода

outHandle DWORD 0; дескриптор стандартного устройства вывода

bytesWritten DWORD?; число выведенных байтов

inHandle DWORD 0; дескриптор стандартного устройства ввода

bytesRead DWORD?; число введённых байтов

Q BYTE?; одиночный символ массива ввода

.code

start:

CLC; сброс флага переноса CF

MOV esi,Lengthof X-1; смещение мл. байта слагаемого

MOV edi,esi

inc edi; длина суммы на 1 байт больше длины слагаемого

MOV ecx,Lengthof X; счётчик цикла

M:; начало цикла

MOV ah,0; освобождение места для возможного переноса

MOV al,X[esi]; загрузка байта первого слагаемого

ADC al,Y[esi]; сложение двух байтов и переноса

AAA; ASCII-коррекция после сложения

PUSHF

OR al,30h; получение ASCII-кода цифры суммы

POPF

MOV Z[edi],al; сохранение цифры суммы в памяти

DEC esi; смещение следующего байта слагаемых

DEC edi; смещение следующего байта суммы

LOOP M

OR ah,30h; получение ASCII-кода старшей цифры суммы

MOV Z[edi],AH; сохранение старшей цифры суммы в памяти

INVOKE GetStdHandle, STD_OUTPUT_HANDLE

mov outHandle,eax

INVOKE WriteConsole, outHandle, offset X, lengthof X, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset S, lengthof S, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset Y, lengthof Y, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset E, lengthof E, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset Z, lengthof Z, offset bytesWritten,0

INVOKE GetStdHandle, STD_INPUT_HANDLE

mov inHandle,eax

INVOKE ReadConsole, inHandle, offset Q, 1, offset bytesRead, 0

INVOKE ExitProcess,0

END start

Пояснения к программе:

§ В программе используется команда ADC, так как любое сложение может вызвать перенос, который должен быть прибавлен к следующему (слева) байту. Команда CLC устанавливает флаг переноса CF в нулевое состояние.

§ Команда MOV очищает регистр AH на каждом витке цикле, так как команда AAA может прибавить к нему единицу. Заметьте, что использование команд XOR или SUB для очистки регистра AH изменяет флаг CF, поэтому их использование нежелательно.

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

§ Программа использует команду OR после команды AAA для занесения левой тройки. Так как при этом устанавливается флаг CF, что изменяет результат команды ADC, то для сохранения регистра флагов используется команда PUSHF, и, затем, восстановление регистра флагов командой POPF.

§ В результате, в переменной Z получается сумма 127210 в ASCII-формате 31h, 32h, 37h, 32.

Вместо команд PUSHF и POPF можно использовать команды LAHF (Load AH with Flags – загрузка флагов в регистр AH) и SAHF (Store AH in Flag register – запись флагов из регистра AH во регистр флагов). Команда LAHF загружает в регистр AH флаги SF, ZF, AF, PF и CF; а команда SAHF записывает содержимое регистра AH в указанные флаги. В приведенном примере, однако, регистр AH уже используется для арифметических переполнений, поэтому команды LAHF и SAHF здесь недопустимы.

Упражнение 1.5.8. На основе приведённого примера разработать программу сложения двух шестибайтных ASCII-чисел в семибайтную сумму. Первым слагаемым 57834210 инициализировать переменную X, вторым слагаемым 69409310 инициализировать переменную Y, сумму разместить в переменной Z.

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

Вычитание в ASCII-формате

Команда AAS (ASCII Adjust for Subtraction - коррекция для вычитания ASCII-кодов) выполняется аналогично команде AAA. Команда AAS проверяет правую шестнадцатеричную цифру (четыре бита) в регистре AL. Если эта цифра лежит между A и F или флаг AF равен 1, то из регистра AL вычитается 6, а из регистра AH вычитается 1, флаги AF и CF устанавливаются в 1. Во всех случаях команда AAS устанавливает в 0 левую шестнадцатеричную цифру в регистре AL. В следующих двух примерах предполагается, что переменная X содержит 3816, а переменная Y – 3416:

Пример А: AX AF

MOV AL,X;0038

SUB AL,Y;0034 0

AAS;0004 0

Пример Б: AX AF

MOV AL,Y;0034

SUB AL,X;00FC 1

AAS;FF06 1

В примере А команде AAS не требуется выполнять коррекцию. В примере Б, так как правая цифра в регистре AL равна C, команда AAS вычитает 6 из регистра AL и 1 из регистра AH и устанавливает в 1 флаги AF и CF. Результат (который должен быть равен -4) имеет шестнадцатеричное представление FF06, т.е. десятичное дополнение числа -4 (минус четыре).

Упражнение 1.5.10. Разработать программу вычитания из одного четырёхбайтного числа, вводимого с клавиатуры, другого четырёхбайтного числа, также вводимого с клавиатуры. Знаковый результат вывести на экран. В программе учесть возможность получения отрицательного числа.

Умножение в ASCII-формате

Команда AAM (ASCII Adjust for Multiplication – коррекция для умножения ASCII кодов) выполняет корректировку результата умножения ASCII кодов в регистре AX. Однако шестнадцатеричные цифры должны быть очищены от троек и полученные данные уже не будут являться действительными ASCII-кодами. (В руководствах фирмы IBM для таких данных используется термин – распакованный десятичный формат). Например, число 1234 в ASCII-формате 31323334 имеет распакованное десятичное представление 01020304. Кроме этого, надо помнить, что коррекция осуществляется только для одного байта, поэтому можно умножать только однобайтные числа; для более длинных чисел необходима организация цикла.

Команда AAM делит содержимое регистра AL на 1010 (A16) и записывает частное в регистр AH, а остаток в AL. Предположим, что в регистре AL содержится 3516, а в регистре CL содержится 3916. Следующие команды умножают содержимое регистра AL на содержимое CL и преобразуют результат в ASCII-формат:

AX:

AND CL,0FH; преобразовать CL в 09

AND AL,0FH; преобразовать AL в 05 0005

MUL CL; умножить AL на CL 002D

AAM; распаковать 0405

OR AX,3030H; преобразовать в ASCII-формат 3435

Команда MUL записывает число 2D16 (4510) в регистр AX, после чего команда AAM делит это значение на A16, записывая частное 04 в регистр AH и остаток 05 в регистр AL. Затем команда OR преобразует распакованное десятичное число в ASCII-формат.

Пример 1.5.13. Следующая программа демонстрирует умножение трёхбайтового множимого 90010, расположенного в переменной X, на однобайтовый множитель 910, расположенный в переменной Y. Так как команда AAM может иметь дело только с однобайтовыми числами, то в программе организован цикл, который обрабатывает байты справа налево. Окончательный четырёхбайтовый результат умножения, в данном примере 180010, размещается в переменной Z.

.data

X byte 39h, 30h, 30h; умножаемое 900d

Y byte 39h; множитель 9

Z byte 0,0,0,0; произведение

S byte ' * '; символ умножения

E byte ' = '; символ равенства для вывода

outHandle DWORD 0; дескриптор стандартного устройства вывода

bytesWritten DWORD?; число выведенных байтов

inHandle DWORD 0; дескриптор стандартного устройства ввода

bytesRead DWORD?; число введённых байтов

Q BYTE?; одиночный символ массива ввода

.code

start:

MOV esi,Lengthof X-1; смещение мл. байта умножаемого

MOV edi,esi

inc edi; длина произведения на 1 байт больше длины умножаемого

MOV ecx,Lengthof X; счётчик цикла

mov bl,Y; копия множителя

AND bl,0Fh; распаковка копии множителя

M:; начало цикла

MOV al,X[esi]; загрузка байта умножаемого

AND al,0Fh; распаковка байта умножаемого

MUL bl; умножение распакованных байтов

AAM; коррекция после умножения

ADC al,Z[edi]; сложение рез-та умножения с переносом

AAA; коррекция после сложения

OR al,30h; ASCII-формат результата

MOV Z[edi],al; запись результата в память

DEC edi; получение адреса байта переменной Z

MOV Z[eDI],ah; сохранение результата

DEC esi; получение адреса байта умножаемого

LOOP M; цикл

OR Z[edi],30h; ASCII-формат старшего байта результата

INVOKE GetStdHandle, STD_OUTPUT_HANDLE

mov outHandle,eax

INVOKE WriteConsole, outHandle, offset X, lengthof X, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset S, lengthof S, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset Y, lengthof Y, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset E, lengthof E, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset Z, lengthof Z, offset bytesWritten,0

INVOKE GetStdHandle, STD_INPUT_HANDLE

mov inHandle,eax

INVOKE ReadConsole, inHandle, offset Q, 1, offset bytesRead, 0

INVOKE ExitProcess,0

END start

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

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

Деление в ASCII-формате

Команда AAD (ASCII Adjust for Division - коррекция для деления ASCII-кодов) выполняет корректировку ASCII кода делимого перед делением. Однако прежде необходимо очистить левые тройки ASCII-кодов для получения распакованного десятичного формата. Команда AAD может оперировать с двухбайтными делимыми в регистре AX. Предположим, что регистр AX содержит делимое 28d в ASCII-формате 3238 и регистр CL содержит делитель 7 также в ASCII-формате 37. Следующие команды выполняют коррекцию для последующего деления:

AX:

AND CL,0F; преобразовать CL в распак.дес. 07

AND AX,0F0F; преобразовать AX в распак.дес. 0208

AAD; преобразовать в двоичный 001C

DIV CL; разделить на 7 0004

Команда AAD умножает содержимое AH на A16 (1010), прибавляет полученный результат 1416 (2010) к регистру AL и очищает регистр AH. Значение 1C16 есть шестнадцатеричное представление десятичного числа 28. Делитель может быть только однобайтовый от 01 до 09.

Пример 1.5.14. Следующая программа выполняет деление четырехбайтового делимого 103d, расположенного в переменной X, на однобайтовый делитель 5, расположенный в переменной Y. В программе организован цикл обработки делимого слева направо. Остаток от деления находится в регистре ah. Окончательные результаты: в переменной Z расположено частное 303d в ASCII-формате 333033, а в переменной R расположен остаток 3 в ASCII-формате 33.

.data

X byte 31h, 35h, 30h, 33h; делимое 1503d

Y byte 35h; делитель 5

Z byte 0,0,0,0; частное

R byte 0; остаток

S byte ' div '; символ целочисленного деления

E byte ' = '; символ равенства

W byte 0Ah,0Dh,'mod = '; остаток с новой строки

outHandle DWORD 0; дескриптор стандартного устройства вывода

bytesWritten DWORD?; число выведенных байтов

inHandle DWORD 0; дескриптор стандартного устройства ввода

bytesRead DWORD?; число введённых байтов

Q BYTE?; одиночный символ массива ввода

.code

start:

MOV esi,0; смещение старшего байта делимого

MOV edi,esi; смещение старшего байта результата

MOV ecx,Lengthof X; счётчик цикла

mov bl,Y; копия делителя в регистре bl

AND bl,0Fh; распаковка копии делителя

mov ah,0; сброс ah перед корректировкой командой AAD

M:; начало цикла

MOV AL,X[esi]; загрузка очередного байта делимого

AND AL,0Fh; его распаковка

AAD; корректировка ax перед делением

DIV bl; деление ax на регистр bl

OR al,30h; ASCII-формат результата деления

MOV Z[edi],al; сохранение результата деления

INC esi; адрес следующего байта делимого

INC edi; адрес следующего байта результата

LOOP M; цикл

or ah,30h; ASCII-формат остатка

mov R,ah; сохранение остатка

INVOKE GetStdHandle, STD_OUTPUT_HANDLE

mov outHandle,eax

INVOKE WriteConsole, outHandle, offset X, lengthof X, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset S, lengthof S, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset Y, lengthof Y, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset E, lengthof E, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset Z, lengthof Z, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset W, lengthof W, offset bytesWritten,0

INVOKE WriteConsole, outHandle, offset R, lengthof R, offset bytesWritten,0

INVOKE GetStdHandle, STD_INPUT_HANDLE

mov inHandle,eax

INVOKE ReadConsole, inHandle, offset Q, 1, offset bytesRead, 0

INVOKE ExitProcess,0

END start

Если делитель больше одного байта, то необходимо построить другой цикл для обработки делителя.

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

Обработка данных в двоично-десятичном формате (BCD)

В предыдущем примере 1.5.14 деления было получено частное в ASCII-формате 30333030, которое представлялось на экране в виде 030010. Если сжать ASCII-формат, сохраняя только правые цифры каждого байта, то получим 0300. Такой формат называется двоично-десятичным (BCD - Binary Coded Decimal) или упакованным. Он содержит только десятичные цифры от 0 до 9. Длина двоично-десятичного представления в два раза меньше ASCII-представления. Преобразование чисел из ASCII-формата в BCD-формат и обратно может выполняться как справа налево (от младших к старшим разрядам), так и слева направо (от старших к младшим разрядам). Ниже приведён способ преобразования числа 76d, хранимого в регистре ax в ASCII-формате 3736h, в упакованный формат 76h.

MOV AX,3736h; ax = 3736h (0011 0111 0011 0110b)

SHL AL,4; ax = 3760h (0011 0111 0110 0000b)

SHL AX,4; ax = 7600h (0111 0110 0000 0000b)

После второго сдвига регистр ah содержит упакованное число 76h.

В двоично-десятичном представлении (BCD-формате) можно выполнять сложение и вычитание чисел. Для этих целей имеются две корректирующие команды:

DAA (Decimal Adjustment for Addition - десятичная коррекция после сложения)

DAS (Decimal Adjustment for Subtraction - десятичная коррекция после вычитания)

Пример 1.5.15. Следующая программа демонстрирует сложение трёхбайтовых чисел в BCD-формате. Числа 22334510 и 91223310 хранятся в переменных X и Y соответственно. Окончательный четырёхбайтовый результат записывается в переменную Z.

.data

X byte 22h, 33h, 45h; первое слагаемое 223345h

Y byte 91h, 22h, 33h; второе слагаемое 912233h

Z byte lengthof X + 1 dup (0); рез-тат на 1 байт больше

.code

start:

mov esi,lengthof X-1; индекс младшего байта слагаемых

MOV ecx,lengthof X; счётчик цикла

CLC; сброс флага переноса перед ADC

ZZZ:; начало цикла

MOV al,X[esi]; загрузить байт первого слагаемого

ADC al,Y[esi]; прибавить байт второго слагаемого

DAA; десятичная коррекция

MOV Z[ecx],al; записать байта результата

DEC esi; индекс следующего байта слагаемых

LOOP ZZZ; цикл

ADC ah,0; получение в ah флага переноса

MOV Z[ecx],ah; записать старшего байта результата

DbgDump offset Z, lengthof X + 1; просмотр результата

invoke ExitProcess, 0

end start

Упражнение 1.5.13. Разработать программу сложение шестибайтных чисел в BCD-формате. Числа 109234057836 и 902861069427 хранятся в переменных X и Y соответственно. Семибайтный результат разместить в переменной Z.

Упражнение 1.5.14. Разработать программу вычитания из шестибайтного числа 902861069427, представленного в BCD-формате, числа 109234057836, также представленного в BCD-формате. Указанные числа должны храниться в переменных X и Y соответственно. Шестибайтный результат разместить в переменной Z.



Поделиться:


Последнее изменение этой страницы: 2016-06-29; просмотров: 916; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.135.202.224 (0.077 с.)