Сборник по задачам и примерам Assembler



Ввод чисел с консоли

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

Ввод целых десятичных чисел из диапазона 0..99

Для значений из диапазона 0..99 взаимное преобразование между символьной десятичной и двоичной формами может производиться командами умножения и деления двоично-десятичных (BCD-чисел) — ММ и AAD.


:prg06_01.asm - программа ввода с консоли двоичного числа из диапазона 0..99
:в десятичном символьном представлении
;Вход: число в десятичной системе счисления, вводимое в символьном виде с клавиатуры.
;Выход: двоичное число в регистре А1.
buf_Oahstruc
len_bufdb 3 :длина buf_0ah
len_in db 0 действительная длина введенного слова (без учета Odh)
bufjn db 3 dup (20h) :буфер для ввода (с учетом Odh)
ends
.data
bufbuf_0ah<>
adr bufdd buf
.code
;.........
;вводим 2 символа с клавиатуры, контроль на допустимые значения не делаем
Ids dx.adr__buf
nrav ah.Oah
int 21h
xor ax.ax
cmp buf .lenjn.2 ;сколько чисел введено реально?
jneml
mov ah.buf.buf_in ml: mov al,buf.buf_in+l
andax.0f0fh преобразование в неупакованное десятичное представление
aad :в al двоичный эквивалент исходного двузначного десятичного значения


fbld string_pack :помещаем в стек сопроцессора 'fistp string_bin ;и извлекаем эквивалентное двоичное представление в поле string_bin '¦.........
Приведенная программа преобразует любое значение из диапазона 0..1018-!. Интересно отметить количественное значение максимальной двоичной величины, соответствующее верхней границе диапазона, — это +0de0b6b3a763ffffl6. Запомните его, оно пригодится нам при рассмотрении обратного преобразования Для вывода на консоль — из двоичного в десятичное представление. Извлечь значение нужной разрядности можно, если ввести директивой label соответствующие идентификаторы в исходный текст программы (что и сделано в нашем сегменте кода):
Ввод целых десятичных чисел из диапазона 0..4 294 967 295
Если исходное значение выходит за диапазон 0..99, то здесь следует иметь в виду возможность возникновения ситуации, при которой значение вводимого десятичного числа превышает диапазон, допустимый форматами типов целочисленных данных, поддерживаемых, в частности, арифметическими командами микропроцессора. Для Pentium III это 8, 16 и 32 бита. Допустимые диапазоны значений для этих форматов (числа без знака):

  • для операнда размером 8 бит — 0..255;
  • для операнда размером 16 бит — 0..65 535;
  • для операнда размером 32 бита — 0..4 294 967 295.

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

А10 = an.,x10nl + an.2x10"-2 + ... + а,х10 + а0х10°.

Вычисление данного полинома лучше производить по схеме Горнера:

А10 - (...(0+an.,)x10+an.2)x10+ ... + а,)х10+а0.

Например, число 3405 по этим формулам может быть представлено так:

3405=Зх103+4х102+Ох101+5х100=(((0+3)х10+4)х10+0)х10+5.

Ниже приведена программа преобразования целого десятичного числа в символьном виде из диапазона 0..4 294 967 295 в эквивалентное двоичное представление. Для ввода числа с клавиатуры используем функцию 3fh MS D0S. Она удобна тем, что возвращает количество действительно введенных символов в регистре AL.

:prg06_02.asm - программа преобразования целого десятичного числа в символьном виде :из диапазона 0..4294967295 в эквивалентное двоичное представление.
:Вход: ввод с клавиатуры числа в десятичной системе счисления (не более 10 цифр).
:Выход: двоичное число-результат преобразования в регистре ЕАХ.
add eax.edx
mul ten
jc exit_e
inc si
loop ml m2: mov dl .[si]
anddl.Ofh преобразуем ASCI I->BCD
add еах^х;результат преобразования в регистре ЕАХ
jncexit результат вышел за границы операнда exit_e: .выводим строку string_e на экран

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

Ввод целых десятичных чисел из диапазона 0..999 999 999 999 999 999

Второй способ преобразования десятичных чисел хотя и выглядит несколько экзотически, вполне работоспособен. Данный способ предполагает использование особенностей некоторых команд сопроцессора. В материале урока 19 «Архитектура и программирование сопроцессора» учебника мы перечисляли форматы данных, которые поддерживает сопроцессор. Перечислим их еще раз:

  • двоичные целые числа в трех форматах — 16, 32 и 64 бита;
  • упакованные целые десятичные (BCD) числа — максимальная длина -18 упакованных десятичных цифр (9 байт);
  • вещественные числа в трех форматах — коротком (32 бита), длинном (64 бита), расширенном (80 бит).

Для нас интерес представляют форматы целых двоичных и упакованных десятичных (BCD) чисел, а также команды обмена этими значениями с вершиной сопроцессора. Процесс преобразования десятичного целого числа, вводимого с клавиатуры, показан в программе ниже. Необходимо отметить, что этот способ преобразования позволяет расширить диапазон значений 0..999 999 999 999 999 999.

:prg06_03.asm - программа ввода целых десятичных чисел из диапазона
:0..999 999 999 999 999 999 и преобразования их в эквивалентное двоичное представление.
;Вход: ввод с клавиатуры числа в десятичной системе счисления
:в диапазоне значений 0..999 999 999 999 999 999.
:Выход: двоичное число-результат преобразования в области памяти stnng_bin.
.data
db 0 :барьер. если введенное количество цифр нечетно string db 20 dup (0) максимальное исходное число состоит из 18 цифр (20 - с учетом
Od0ah)
len_string=$-string adr_string dd string
string_pack dt 0 :сюда упаковывается исходное значение
len_string_pack=$-string_pack adr_string_pack dd string_pack результат - двоичное значение различной разрядности:

string_bin_byte label byte
string_bin_word label word
string_bin_word label word
string_bin_dword label dword

string_bindq 0 ;поле для результата - эквивалентного двоичного представления

--------вводим с экрана----------------............-----
movbx.O стандартный дескриптор - клавиатура
movcx. len_str"ing
lea dx.string :формируем указатель на строку string
movah.3fh :номер функции DOS
int 21h
jc exit ;переход в случае ошибки
;в регистре AL - количество действительно введенных десятичных цифр :преобразуем строку с десятичными числами в ее двоичный эквивалент
mov ex.ax
subcx,2 корректируем счетчик цикла (чтобы не учитывать OdOah, вводимые 3fh)
jeexz exit :число не было введено
Ids si ,adr_string
add si,cx
dec si указатель на последнюю введенную десятичную цифру
les di.adr_string_pack ml: std :флаг df=l - работаем со строкой string, начиная с ее конца
хог ах.ах
lodsb
and al. Of h
shl ax.8
lodsb
shl al .4
add al.ah :в AL две очередные упакованные цифры
eld :флаг df-1 - работаем со строкой string_pack. начиная с ее начала
stosb
sub ex.2
emp ex. 0
J9 ml ;конец преобразования в упакованное представление
fI nit инициализируем сопроцессор ;теперь преобразуем в эквивалентное двоичное представление:
fbld string_pack :помещаем в стек сопроцессора 'fistp string_bin ;и извлекаем эквивалентное двоичное представление в поле string_bin

Приведенная программа преобразует любое значение из диапазона 0..1018-!. Интересно отметить количественное значение максимальной двоичной величины, соответствующее верхней границе диапазона, — это +0de0b6b3a763ffffl6. Запомните его, оно пригодится нам при рассмотрении обратного преобразования Для вывода на консоль — из двоичного в десятичное представление. Извлечь значение нужной разрядности можно, если ввести директивой label соответствующие идентификаторы в исходный текст программы (что и сделано в нашем сегменте кода):

string_bin_byte label byte
string_bin_word label word
string_bin_word label word
string_bin_dword label dword

Ввод целых десятичных чисел из диапазона 0..до бесконечности

Для преобразования десятичного числа произвольной разрядности из символьного представления в двоичное потрудиться придется несколько больше. Основа для этой работы была создана в материале, посвященном арифметическим операциям для чисел произвольной разрядности. Поэтому наши действия при разработке программы преобразования напомнит игру с конструктором, когда из готовых компонент будет создаваться новый продукт.
Исходными компонентами программы преобразования десятичного числа произвольной разрядности из символьного представления в двоичное будут являться макрокоманда умножения N-байтного числа на число размером М байт и программа сложения чисел размером N байт без учета знака. Алгоритм вычисления двоичного эквивалента будет таким же, как рассмотренный выше, — вычисление полинома по схеме Горнера. Ниже приведен вариант реализующей его программы. Расположение байтов результата — по схеме, естественной для микропроцессоров Intel, то есть младший байт располагается по младшему адресу.

:prg06_04.asm - программа ввода целых десятичных чисел из диапазона 0..». ;Вход: ввод с клавиатуры числа в десятичной системе счисления длиной до 20 цифр. ;Выход: двоичное число - результат преобразования в области памяти string_bin.
:см. описание макрокоманд add_unsign_N_l и mul_unsign_NM в главе 1 add_unsIgn_N_lmacro summand_l, summand_2, N
endm mul_unsign_NM macro u.i.v.j.w
endm .data
string db 22 dup (0) максимальное число состоит из 20 цифр (22 - с учетом OdOah) len_string-$-string tendd 10
string_bindb 10 dup (0) максимальная длина двоичного числа - 10 байт 1en_stri ng_bi n-$-stri ng_bi n
carry db 0 :перенос сложения последних байтов
adr_string_bindd string_bin string_bin_w db len_string_bin+l dup (0) результат умножения для макроса
;mul_unsign_NM = len_string_bin+l байт len_string_bin_w =$-string_bin_w adr_string_bin_w dd string_bin_w k db 0 :перенос 0 < k < 255
b dw lOOh ;размер машинного слова
.code
movbx.O стандартный дескриптор - клавиатура
mov cx.len_string
lea dx.string ;формируем указатель на строку string
movah,3fh :номер функции DOS
int 21h
jc exit :переход в случае ошибки :в регистре AL - количество действительно введенных десятичных цифр
mov ecx.eax
sub есх.2 корректируем счетчик цикла (чтобы не учитывать OdOah. вводимые 3fh)
jcxz $+4 :число не было введено
jmp $+5
jmp exit cont_l:dec ecx ;не умножать на 10 последнюю цифру числа
jcxz $+4 юднозначное число
tjmp S+5 jmp m2 lea si.string ;формируем указатель на строку string хог еах.еах :еах:=0 ml: хог edx.edx
mov dl.[si]
and dl.Ofh преобразуем ASCI I->BCD add_unsign_N_lstring_bin.dl. len_string_bin ¦.умножаем на 10
mul_unsign_NM string_bin. len_string_bin.ten,l. string_bin_w :копируем string bin_w в string_bin
eld
push si push ex
Ids si .adr_sthng_bin_w
lesdi.adr_string_bin
mov cx.len_string_bin_w repmovsb pop ex pop si
inc si dec ex jcxm2 ' jmp ml m2: mov dl .[si] and dl.Ofh
¦ add_unsign_N_lstring_bin.dl, len_string_bin
¦ результат преобразования - в строке string_bin

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

Ввод вещественных чисел

Теперь у нас все готово для того, чтобы выполнить ввод с клавиатуры символьного представления вещественного числа и преобразование его в соответствующий двоичный эквивалент. В уроке 19 «Архитектура и программирование сопроцессора» учебника мы обсуждали понятие вещественного числа. Отмечалось, что вещественное число имеет две формы записи — с плавающей точкой (34.89) и научную (3.45е-3=3.45х103). Для преобразования вещественного числа из символьного представления в эквивалентное двоичное можно предложить несколько способов. Самый простой — использовать возможность загрузки в сопроцессор упакованного BCD-числа. В этом случае алгоритм преобразования состоит в следующем. Символьная строка с вещественным числом вводится в память, где она преобразуется в упакованное BCD-число. При вводе указанной символьной строки запоминается положение плавающей точки. Полученное упакованное BCD-число загружается в сопроцессор, после чего оно делится на 10 в степени, соответствующей положению плавающей точки в исходном числе. Для малых чисел (в диапазоне до |1018-1|) этот способ вполне хорош. Его можно расширить, если вводить число в научном формате, при этом процесс перевода мантиссы аналогичен рассмотренному выше, но при подготовке к делению на степень 10 необходимо учесть значение степени, указанное после символа «е». Но все равно, несмотря на расширения диапазона, разряднорть мантиссы ограничена 18 цифрами. Устранить этот недостаток можно, используя операции с числами произвольной разрядности. Этот способ интересен своей универсальностью, поэтому уделим ему основное внимание.
Итак, разработаем программу ввода вещественного числа с клавиатуры в одном из двух возможных форматов — простом формате с плавающей точкой. Доработать программу для использования научного формата для вас не составит труда.
В качестве знаков, разделяющих мантиссу на целую и дробную части, можно использовать как запятую, так и точку. Суть алгоритма преобразования состоит в следующем. Производится ввод с клавиатуры символов вещественного числа. После ввода анализируются символы буфера, куда было помещены символы введенного числа, на предмет выяснения положения плавающей точки. Обнаруженная позиция запоминается. Относительно нее введенные символы делятся на символы цифр целой и дробной частей. Используя алгоритм преобразования символьного представления десятичного числа в двоичный эквивалент, преобразуется целая часть вещественного числа. Дробная часть вещественного числа также преобразуется в двоичный эквивалент. Это преобразование производится с использованием сопроцессора по формуле: ((...(u_m/b+u1_m)/b+...+u_2)/b+u.,)/b, где un — символы десятичных цифр дробной части вещественного числа u_mu1_n,..-u.2u_,, b=10. После того как данное выражение вычислено (его результат находится в вершине стека сопроцессора), производится сложение его результата с преобразованной целой частью вещественного числа. Все, теперь в вершине стека сопроцессора находится вещественное число — эквивалент своего исходного символьного представления. Текст программы преобразования вещественного числа из символьного представления достаточно велик и по этой причине приведен на дискете (prg06_05.asm). Заметьте, что с целью экономии места никаких проверок
на правильность формата вводимого вещественного числа в программе не делается.
Последнее замечание — об ограничениях на размерность исходного числа. Здесь следует различать размерности целой и дробной частей. Что касается дробной части, то здесь вообще ограничений нет, за исключением тех, которые накладывает сам сопроцессор на вводимые в его регистры значения. Для целой части узкое место — максимальная размерность операнда в команде целочисленного сложения FIADD, которая составляет 32 бита.
Ввод шестнадцатеричных и двоичных чисел мы рассматривать не будем, так как общие принципы их ввода аналогичны рассмотренным выше для десятичных чисел. Потребность в вводе с клавиатуры шестнадцатеричных и двоичных чисел возникает значительно реже, чем десятичных.