Главная » Ассемблер, Железо » Перевод чисел из десятичного кода в двоичный и наоборот

0

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

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

Таблица 2.1. Форматы данных математического сопроцессора

Рассмотрим порядок перевода целого десятичного числа, записанного в ASCII-коде, в двоичный код.

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

2.    Число в упакованном формате занести в стек сопроцессора.

Таблица 2.1 (продолжение)

3.    Извлечь число из стека сопроцессора и сохранить его в памяти в заданном целочисленном или вещественном формате.

Таблица 2.2. Команды передачи данных математического сопроцессора

Команда

Выполняемая операция

FBLD

Преобразовать упакованное десятичное число

 

в расширенный вещественный формат и занести в стек

 

сопроцессора

FBSTP

Число, находящееся в вершине стека сопроцессора,

 

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

 

из стека

FILD

Преобразовать целое число в расширенный вещественный

 

формат и занести в стек сопроцессора

FIST

Число, находящееся в вершине стека сопроцессора,

 

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

FISTP

Число, находящееся в вершине стека сопроцессора, сохранить

 

в заданном целочисленном формате и извлечь из стека

FLD

Преобразовать вещественное число в расширенный

 

формат и занести в стек сопроцессора

FST

Число, находящееся в вершине стека сопроцессора,

 

сохранить в заданном вещественном формате

FSTP

Число, находящееся в вершине стека сопроцессора, сохранить

 

в заданном вещественном формате и извлечь из стека

ПРИМЕЧАНИЕ

Целые числа в двоичном коде можно хранить как в целочисленных, так и в вещественных форматах.

Операция перевода целого числа из двоичного кода в десятичный выполняется в обратном порядке.

[1]Число, хранящееся в одном из возможных двоичных форматов (целочисленном или вещественном), занести в стек сопроцессора при помощи соответствующей команды загрузки.

[1]Извлечь число из стека сопроцессора в упакованном десятичном формате.

[1]Преобразовать число из упакованного формата в ASCII-код. При переводе вещественных чисел из десятичного формата в двоичный необходима дополнительная операция — масштабирование. Масштабирование заключается в умножении преобразуемого числа на 10\ где N — количество десятичных знаков после запятой. Рассмотрим порядок действий.

1.     Преобразовать число из ASCII-кода в упакованный десятичный формат как целое. Запятая при этом игнорируется, однако под- считывается количество знаков после запятой N.

2.         Число в упакованном формате занести в стек сопроцессора.

3.     Умножить число на константу 10Л Для хранения констант в оперативной памяти можно использовать специальный массив вещественных чисел, индексом в котором служит N.

[1]Результат извлечь из стека сопроцессора и сохранить в памяти в заданном вещественном формате.

При переводе в двоичный код десятичного числа, записанного в научном формате с порядком, равным К, умножение выполняется на 10K N, где N — количество знаков мантиссы числа после запятой.

При переводе вещественного числа из двоичного кода (X) в десятичный (D) также приходится выполнять дополнительные преобразования. После выполнения перевода число должно состоять из нормализованной мантиссы (М) и порядка (Р):

0=МхЮр

Порядок выполнения операций описан ниже. 1. Вычислить десятичный логарифм числа X. Целая часть результата представляет собой порядок числа Р.

2.    Разделить число X на 10р. Результат является нормализованной мантиссой числа М.

3.    Извлечь из стека математического сопроцессора М и Р и сохранить в памяти в упакованном формате.

4.    Преобразовать М и Р из упакованного формата в ASCII-код.

Вычисление логарифмов осуществляется сопроцессором с помощью универсальной команды FY12X (выполняющей вычисление значения 1 од2Х и умножение результата на Y). Десятичный логарифм вычисляется по формуле:

Для обеспечения максимальной точности преобразования в наборе команд сопроцессора есть группа операций загрузки в стек констант, наиболее часто используемых при вычислениях. Значение 1 од102 загружается командой FLDLG2.

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

•     процедура Int32_to_String осуществляет перевод 32-разрядного двоичного целого числа (со знаком) в текстовую строку, представляющую собой десятичное число в ASCII-коде;

•     процедура String_to_Int32 осуществляет перевод целого десятичного числа, записанного в виде текстовой строки в ASCII- коде, в двоичное 32-разрядное число;

•     процедуры ShowDecByte, ShowDecWord и ShowDecDWord осуществляют преобразование в десятичный код (при помощи функции Int32_ to_String) и вывод в заданную позицию экрана 8-, 16- и 32-разрядных двоичных чисел соответственно;

•     процедура DoubleF1oat_to_String преобразует вещественное число из двоичного формата (с удвоенной точностью) в текстовую строку, представляющую собой десятичное число в формате с плавающей точкой (в ASCII-коде);

•     процедура Doub1eFloat_to_ExpForm преобразует вещественное число из двоичного формата (с удвоенной точностью) в текстовую строку, представляющую собой десятичное число в научном формате (то есть с мантиссой и экспонентой);

•     процедура Stnng_to_DoubleFloat осуществляет перевод вещественного десятичного числа, записанного в формате с плавающей точкой в виде текстовой строки в ASCII-коде, в двоичное число удвоенной точности;

•     процедура BCD_to_ASCII преобразует целое десятичное число из кода BCD в ASCII-код (процедура является вспомогательной и не должна вызываться извне, то есть из программ, не принадлежащих к данному модулю);

•     процедура ShowDataStnng предназначена для вывода в заданную позицию экрана текстовой строки, полученной в результате преобразования двоичного числа (любого типа) в десятичное;

•     процедура Get Integer обеспечивает ввод с клавиатуры десятичного целого числа со знаком;

•     процедура GetFloat обеспечивает ввод вещественного десятичного числа в формате с плавающей точой.

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

DATASEG

; Количество разрядов мантиссы (1-18) MaxPositions DW 10

; Количество знаков числа после запятой (1-17) NumberSymbolsAD DW 5

; Машинный ноль Data_Minimum DQ l.E-110 : Константы (10 в степени N) MConst DQ 1.0Е1,1.0Е2,1.0E3,1.0E4,1.0E5 DQ 1.0E6,1.0E7,1.0Е8Д.0Е9,1.0E10 DQ 1.0E11,1.0E12,1.0E13,1.0E14,1. 0E15 DQ 1. 0E16,1. 0E17,1. 0E18,1. 0E19,1. 0E20 DQ 1.0E21,1.0E22,1.0E23,1.0E24,1.0E25 DQ 1.0E26,1.0E27,1.0E28,1.0E29,1.0E30 DQ 1. 0E31,1. 0E32,1.0E33,1.0E34,1.0E35 DQ 1.0E36,1.0E37,1.0E38.1.0E39,1.0E40 DQ 1.0E41,1.0E42,1.0E43,1.0E44,1.0E45 DQ 1.0E46,1.0E47,1.0E48,1.0E49,1.0E50 DQ 1.0E51,1.0E52,1.0E53,1.0E54,1.0E55 DQ 1.0E56,1.0E57.1.0E58.1.0E59,1.0E60 DQ 1. 0E61,1. 0E62,1. 0E63.1. 0E64,1. 0E65 DQ 1.0E66,1.0E67,1.0E68,1.0E69,1.0E70 DQ 1.0E71,1.0E72,1.0E73,1.0E74,1.0E75 DQ 1.0E76,1.0E77,1.0E78,1.0E79,1.0E80 DQ 1.0E81.1.0E82,1.0E83,1.0E84,1.0E85

Листинг 2.5 (продолжение)

DQ 1.0Е86,1.0Е87.1.0Е88,1.0Е89,1.0Е90 DQ 1.0Е91,1.0Е92,1.0Е93,1.0Е94,1.0Е95 DQ 1.0Е96,1.0Е97,1.0Е98.1.0Е99.1.0Е100 DQ 1.0Е101,1.0Е102,1.0Е103.1.0Е104,1.0Е105 DQ 1.0Е106,1.0Е107.1.0Е108,1.0Е109.1.0Е110 DQ 1.0Е111,1.0Е112,1.0Е113,1.0Е114,1.0Е115 DQ 1.0Е116,1.0Е117.1.0Е118,1.0Е119,1.0Е120 DQ 1.0Е121,1.0Е122.1.0Е123,1.0Е124,1.0Е125 DQ 1.0Е126.1.0Е127,1.0Е128

; Данные передаются в математические процедуры : через общую область памяти (главный сегмент данных)

; 32-разрядное целое число Data_Int32 DD ?

; Число с плавающей точкой двойной точности

Data_Double DQ ?

; Модуль числа с плавающей точкой

Data_Abs DQ ?

; Число в BCD-формате

DataBCD DT ?

; Управляющее слово сопроцессора

Data_Contro1 DW ?

; Вспомогательный флаг

Data_Flag

DB ?

: Целая часть десятичного логарифма числа Data_LoglO DW ?

; Знак результата (если не 0 – отрицательное число) Data_Sign D8 ?

: Строка для хранения числа в коде ASCII Data_String

DB 32 DUP (?) : Структура для вывода результата OutD_Stnng

DB 34 DUP (?) EVEN

CODESEG

;* ПРЕОБРАЗОВАТЬ 32-РАЗРЯДНОЕ ЦЕЛОЕ ЧИСЛО В СТРОКУ *

;* Входные параметры:*

;* Data_Int32 – 32-разрядное число.*

;* Выходные параметры:*

;* Data_Stnng – строка-результат.*

PROC Int32_to_String near pushad

pushDS

pushES

movAX.[CS:MainDataSeg]

movDS.AX

movES.AX eld

; Перевести число из двоичного кода в код BCD

fninit;сброс сопроцессора

: Загрузить число в двоичном коде fild [Data_Int32]

; Извлечь число в коде BCD fbstp [Data_BCD]

; Результат записывать в строку DataString

mov DI,offset DataString call BCD_to_ASCII

; Записать признак конца строки (код 0) xor AL.AL stosb pop ES pop DS popad ret

ENDP Int32_to_Stri ng

;* ПРЕОБРАЗОВАТЬ СТРОКУ В 32-РАЗРЯДНОЕ ЦЕЛОЕ ЧИСЛО * ;* Входные параметры:*

;* Data String – число в коде ASCII.*

;* Выходные параметры:*

;* Data_Int32 – 32-разрядное число в двоичном коде. *

PROC String_to_Int32 near pushad push DS

mov AX, [CS: Mai nDataSeg]

mov DS.AX

eld

; Очищаем Data_BCD

mov [dword ptr Data_BCD],0

mov [dword ptr Data_BCD+4],0

mov [word ptr Data_BCD+8],0 : Очищаем байт знака

mov [Data_Sign],0

; Заносим в SI указатель на строку

mov SI,offset DataString

: Пропускаем пробелы перед числом

mov СХ.64 ;защита от зацикливания

@@ShiftIgnore: lodsb

cmp AL,’ ‘ jne

@@ShiftIgnoreEnd loop PPShiftlgnore

jmp short

@@Error

@@ShiftIgnoreEnd:

Листинг 2.5(продолжение)

; Проверяем знак числа cmp AL,’-‘ jne

@@Positive

mov [Data_Sign],BOh lodsb (^Positive:

; 32-битное двоичное число соответствует : десятичнону, имеющему до 9 разрядов

mov СХ,9

@@ASCIItoBCDConversion:

: Символы числа должны быть цифрами cmp AL,’О’ jb РФЕггог cmp AL,’9′ ja

@@Error

; Пишем очередную цифру в младшую тетраду BCD and AL.OFh

or [byte ptr DataBCD],AL

; Проверка на конец строки cmp [byte ptr SI],0

je

@@ASCIItoBCDConversi onEnd

; Сдвигаен BCD на 4 разряда влево

; (сдвигаен старшие 2 байта)

mov АХ,[word ptr Data_BCD+6] shld [word ptr Data_BCD+8],AX.4 : (сдвигаен средние 4 байта)

mov ЕАХ,[dword ptr Data_BCD] shld [dword ptr Data_BCD+4],EAX,4

; (сдвигаен младшие 4 байта) shl [dword ptr Data_BCD].4

; Загружаем следующий синвол в AL lodsb

loop

@@ASCIItoBCDConversi on cmp AL.O

jne

@@Error переполнение разрядной сетки

; ПРЕОБРАЗОВАТЬ ЧИСЛО ИЗ КОДА BCD В ЦЕЛОЕ ЧИСЛО

@@ASCIItoBCDConversi onEnd:

; Вписать знак в старший байт

mov AL,[Data_Sign]

mov [byte ptr Data_BCD+9],AL

; Сбросить регистры сопроцессора fmmt

: Загрузить в сопроцессор число в BCD-формате fbld [Data_BCD]

: Выгрузить число в двоичнон формате fistp [Data_Int32]

jmp short P0End

@@Error:; При любой ошибке обнулить результат

mov [Data_Int32],0

@@End: pop DS popad ret

ENDP String_to_Int32

;* ВЫВОД БАЙТА НА ЭКРАН В ДЕСЯТИЧНОМ КОДЕ*

;* Подпрограмма выводит содержимое регистра AL в * ;* шестнадцатеричном коде в указанную позицию экрана. * ;* Координаты позиции передаются через глобальные * ;* переменные ScreenString и ScreenColumn. После * ;* выполнения операции вывода байта происходит*

;* автоматическое приращение значений этих переменных. *

•idcir ihhWrlf A A" A A A A A A A A’A^f А Л Л Л ‘Л1 А ААААА А А ААААА АААААAA A A A1 A A AAA А А

PROC ShowDecByte NEAR push ЕАХ and EAX.OFFh call ShowDecDWord pop EAX ret

ENDP ShowDecByte

‘•*ВЫВОД 16-РАЗРЯДНОГО СЛОВА НА ЭКРАН*

;*В ДЕСЯТИЧНОМ КОДЕ*

;* Параметры:*

;* АХ – число, которое будет выведено на экран. * ;* Номер строки передается через глобальную*

;* переменную ScreenString, номер столбца – через * ;* переменную ScreenColumn, цвет текста определяется * ;* глобальной переменой TextColorAndBackground. *

•A A AAA A A A A1 A1 A A1 A1 A A A A A A A A1 A A A AAA А’ А А1 А1 А А А АААААА A A A A AAA А А А А ‘А А

PROC ShowDecWord NEAR push ЕАХ and EAX.OFFFFh cal1 ShowDecDWord pop EAX ret

ENDP ShowDecWord

;’*ВЫВОД 32-РАЗРЯДНОГО СЛОВА НА ЭКРАН*

;*В ДЕСЯТИЧНОМ КОДЕ*

;* Параметры:*

;* ЕАХ – число, которое будет выведено на экран. * ;* Номер строки передается через глобальную*

;* переменную ScreenString, номер столбца – через * ;* переменную ScreenColumn, цвет текста определяется * ;* глобальной переменой TextColorAndBackground. *

Листинг 2.5(продолжение)

PROC ShowOecDWord NEAR pushad push DS push ES

; Настроить регистр DS на глобальный сегмент данных

mov DI,[CS:Mai nDataSeg]

mov DS.DI

; Перевести число в десятичный код

mov [Data_Int32],ЕАХ call Int32_to_String

; Настроить регистры ES:DI для

; прямого вывода в видеопамять

; Загрузить адрес сегмента видеоданных в ES

mov AX,0B800h

mov ES.AX

; Умножить номер строки на длину

; строки в байтах

mov АХ,[ScreenString]

mov DX.160 mul DX

; Прибавить к полученному произведению номер

; колонки (дважды)

add АХ,[ScreenColumn]

add АХ,[ScreenColumn]

; Переписать результат в индексный регистр

mov DI.AX

eld

; Использовать цвет символов, заданный по умолчанию

mov АН,[TextColorAndBackground]

; Вывести число на экран

mov СХДО

mov SI,offset Data_String

@@NextChar:

lodsbзагрузить цифру в AL

and AL.AL ;проверка на 0 (конец строки) jz

@@EndOfString stosw;вывести цифру на экран

loop

@@NextChar

@@EndOfString:

pop ES pop DS popad ret

ENDP ShowOecDWord

;* ПРЕОБРАЗОВАНИЕ ЧИСЛА С ПЛАВАЮЩЕЙ ТОЧКОЙ В СТРОКУ * ;* Число имеет формат с удвоенной точностью, результат * ;* выдается в десятичном коде, в "бытовом" формате с *

;* фиксированным количеством знаков после запятой.*

;* Входные параметры:*

;* Data_Double – преобразуемое число;*

;* NumberSymbolsAD – количество знаков после*

;*запятой (0-17).*

;* Выходные параметры:*

;* Data_String – строка-результат.*

PROC DoubleFloat_to_St п ng near pushad push DS push ES

mov AX, [CS.-MainDataSeg]

mov DS.AX

mov ES.AX

; Результат записывать в строку DataString

mov DI.offset Data_String

; Сдвигаем число влево на NumberSymbolsAD : десятичных разрядов

fninit;сброс сопроцессора

fid [Data_Double] загрузить число

mov ВХ,[NumberSymbolsAD] cmp BX.O

je

@@NoShifts ;нет цифр после запятой jl

@@Error:ошибка

dec ВХ

shl ВХ.З;умножаен на В

add ВХ.offset MConst fmul [qword ptr ВХ] -.умножить на константу

@@NoShifts:

; Извлечь число в коде BCD fbstp [Data_BCD] : Проверить результат на переполнение

mov АХ,[offset Data_BCD + 8] cmp АХ,OFFFFh ;"десятичное" переполнение?

je

@@Overflow

; Выделить знак числа и записать его в ASCII-коде

mov AL,[offset Data_BCD + 9] and AL.AL jz

@@NoSign

mov AL,’-‘ stosb

@@NoSign:

; Распаковать число в код ASCII

mov BX.B ;смещение последней пары цифр

mov СХ.9 ;счетчик пар цифр

; Определить позицию десятичной точки в числе

mov DX.18

sub DX.[NumberSymbolsAD]

Листинг 2.5(продолжение)

js

@@Error :ошибка, если отрицательная jz РФЕггог ;или нулевая позиция

@@NextPair:

: Загрузить очередную пару разрядов

mov AL.[ВХ + offset DataBCD]

mov AH.AL

; Выделить, перевести в ASCII и

; сохранить старшую тетраду shr AL.4

add AL,’О’ stosb

dec DX jnz

@@N0

mov AL.’.’ stosb

@@N0:

; Выделить, перевести в ASCII и

; сохранить младшую тетраду

mov AL.AH and AL.OFh

add AL,’0′ stosb

dec DX jnz

@@N1

mov AL,’.’ stosb

@@N1: dec BX

loop

@@NextPa1r

mov AL,0 stosb

; Убрать незначащие нули слева

mov DI,offset Data_String

mov SI,offset Data String

: Пропустить знак числа, если он есть

cmp [byte ptr SI], ‘•’

jne

@@N2

inc SI

inc DI

@@N2:

; Загрузить в счетчик цикла количество разрядов

; числа плюс 1 (байт десятичной точки)

mov CX,18+1+1

; Пропустить незначащие нули

@@N3: cmp [byte ptr SI].’0′ jne

@@N4 cmp [byte ptr SI+1]

je

@@N4

inc SI loop №N3

; Ошибка – нет значащих цифр

jmp short

@@Error

; Скопировать значащую часть числа в начало строки

@@N4: rep movsb

jmp short

@@End

: Ошибка

@@Error:

mov AL,’E’ stosb

mov AL,’R’ stosb

mov AL.’R’

stosb

xor AL.AL stosb

jmp short

@@End

; Переполнение разрядной сетки

@@0verflow:

mov AL, ‘#’ stosb

xor AL.AL stosb

; Конец процедуры

@@End: pop ES pop DS popad ret

ENDP DoubleFloat to String

;* ПРЕОБРАЗОВАТЬ ЧИСЛО С ПЛАВАЮЩЕЙ ТОЧКОЙ В СТРОКУ * ;* Число имеет формат с удвоенной точностью, результат * ;* выдается в десятичном коде в научном формате, т.е. * ;* с нормализованной мантиссой и порядком.*

;* Входные параметры:*

;* Data Double – преобразуемое число:*

;* MaxPositions – количество разрядов мантиссы (1-18). * ;* Выходные параметры:*

;* DataString – строка-результат.*

PROC DoubleFloat_to_ExpForm near pushad push DS push ES

mov AX,[CS:MainDataSeg]

mov DS.AX

mov ES.AX

Листинг 2.5 (продолжение)

; Результат записывать в строку Data_String

mov DI.offset DataString

: Определить знак числа

fninit;сброс сопроцессора

fid [Oata Double] загрузить число fxamпротестировать число

fstsw АХ

test ah.10bпроверить знак

jz

@@z0

; Число отрицательное

mov AL.’-‘;записать знак "минус"

stosb

: Запомнить модуль числа fabs

e^zO: fst [Data_Abs]

: Произвести проверку на "машинный ноль" fcom [Data_Minimum] fstsw АХ and АН. 1000001b jnz

@@Zero

; Устанавить режим округления "вниз" (01)

fstcw [DataControl] :считать слово состояния and [Data_Control],0F7FFh or [DataControl ]. 0400h fldcw [Data_Control]

: Вычислить десятичный логарифм числа f 1 dl g2

fxch ST(1) fyl2x

: Записать десятичный порядок fistp [Data_Logl0]

; Устанавить режим округления "к ближайшему" (00)

fstcw [Data Control] ;считать слово состояния and [Data_Control].0F3FFh fldcw [DataControl]

: Нормализовать десятичную мантиссу

fninit;сброс сопроцессора

fid [Data Abs] ;загрузить модуль числа

mov BX.[MaxPositions] sub BX,[Data_Logl0] cmp BX.O

je

@@z2:сдвиги не нужны

jl

@@zl;нужен сдвиг вправо

; Сдвинуть число влево

; на [ВХ] десятичных разрядов

dec BX cmp BX.127

ja

@@Zero;машинный ноль

shl BX,3;умножаем на 8

add BX,offset MConst fmul [qword ptr BX] :умножить на константу

jmp short

@@z2

; Сдвинуть число вправо

; на [BX] десятичных разрядов

@@zl: neg BX dec BX cmp BX,127

ja ^Overflow

; переполнение shl ВХ.З.-умножаем на 8

add BX.offset MConst fdiv [qword ptr BX] :разделить на константу

@@z2: fbstp [DataBCD] ;извлечь число в коде BCD

; Записать точку перед мантиссой

mov SI.DI ;запомнить позициш точки

mov AL,’.’

stosb

; Преобразовать мантиссу в код ASCII

call BCD_to_ASCII

; Поменять местами точку и первую цифру мантиссы

mov АХ,[SI] xchg AL.AH

mov [SI],AX

; Перевести порядок результата в код BCD

; Проверить значение порядка cmp [Data_Logl0],0

je (a@End

; Записать признак порядка

mov AL.’e’ stosb

fninit;сброс сопроцессора

: Загрузить десятичный порядок в двоичном коде fild [Data_LoglO] : Извлечь порядок в коде BCD fbstp [DataBCD]

-, Результат записать в строку Data_Str1ng call BCD_to_ASCII

jmp short

@@End

@@Zero:

; Машинный ноль

mov AL.’O’ stosb

jmp short @PEnd PPOverflow:

Листинг 2.5 (продолжение)

: Переполнение разрядной сетки

mov AL. ‘#’

stosb

@@End: : Записать признак конца строки (код 0) xor AL.AL stosb

pop ES pop DS popad ret

ENDP DoubleFloat_to_ExpForm

.A A A A A A

;* ПРЕОБРАЗОВАТЬ СТРОКУ В ЧИСЛО С ПЛАВАЮЩЕЙ ТОЧКОЙ *

;* (число имеет обычный, "бытовой" формат)*

;* Входные параметры:*

;* DataString – число в коде ASCII.*

;* Выходные параметры:*

;* DataDouble – число в двоичном коде.*

PROC Stri ngtoDoubl eFl oat near pushad push DS

mov AX,[CS:MainDataSeg]

mov DS,AX

eld

: Очищаем DataBCD

mov [dword ptr Data_BCD],0

mov [dword ptr Data_BCD+4].0

mov [word ptr Data_BCD+8],0

; Очищаем байт знака

mov [Data_Sign],0

; Заносим в SI указатель на строку

mov SI.offset DataString : Пропускаем пробелы перед числом

mov СХ,64 ;защита от зацикливания

@@ShiftIgnore: lodsb

cmp AL.’ " jne

@@ShiftIgnoreEnd loop

@@ShiftIgnore

jmp @PError @PShiftIgnoreEnd:

; Проверяем знак числа cmp AL,’-‘ jne

@@Positive

mov [Data_Sign],80h lodsb

Positive:

mov [Data_Flag].0 :признак наличия точки

mov DX,0;позиция точки

mov CX.18;макс. число разрядов

@@ASCIItoBCDConversion:

cmp AL,’.';точка?

jne

@@NotDot

cmp [Data_Flag],0 ;точка не встречалась?

jne

@@Error

mov [Data_Flag],l

lodsb

cmp AL,0;конец строки?

jne

@@NotDot

jmp

@@ASCIItoBCDConversionEnd 0@NotDot:

; Увеличить на 1 значение позиции точки,

; если она еще не встречалась cmp [Data_Flag],0 jnz

@@Figures

inc DX

@@Figures:

; Символы числа должны быть цифрами cmp AL.’O’ jb

@@Error cmp AL,’9′ ja

@@Error

; Пишем очередную цифру в младшую тетраду BCD and AL.OFh

or [byte ptr Data_BCD],AL : Проверка на конец строки cmp [byte ptr SI],0

je

@@ASC11toBCDConversi onEnd

; Сдвигаем BCD на 4 разряда влево

; (сдвигаем старшие 2 байта)

mov АХ.[word ptr DataBCD+6] shld [word ptr Data_BCD+8],AX,4

; (сдвигаем средние 4 байта)

mov EAX,[dword ptr DataBCD] shld [dword ptr Data_BCD+4].EAX,4 : (сдвигаем младшие 4 байта) shl [dword ptr DataBCD],4 : Загружаем следующий символ в AL lodsb

loop

@@ASCIItoBCDConvers i on

; Если 19-й символ не 0 и не точка.

; то ошибка переполнения

cmp AL,’.’

jne

@@NotDot2

inc CX

lodsb

Листинг 2.5 (продолжение)

@@NotDot2:

cmp AL,0

jne

@@Error переполнение разрядной сетки

; ПРЕОБРАЗОВАТЬ ЧИСЛО ИЗ КОДА BCD В ВЕЩЕСТВЕННОЕ ЧИСЛО

@@ASCIItoBCDConversionEnd:

; Вписать знак в старший байт

mov AL,[Data_Sign]

mov [byte ptr Data_BCD+9],AL

; Сбросить регистры сопроцессора fmnit

; Загрузить в сопроцессор число в BCD-формате

fbld [DataBCD]

: Вычислить номер делителя

mov ВХ,18+1

sub ВХ.СХ

sub BX.DX

cmp ВХ.О

je

@@NoDiv

dec ВХ

shl BX,3:умножаем на 8

add ВХ.offset MConst fdiv [qword ptr ВХ] ;разделить на константу

@@NoDiv:; Выгрузить число в двоичном формате fstp [DataDouble]

jmp short

@@End

@PError:; При любой ошибке обнулить результат

fldz ;занести ноль в стек сопроцессора fstp [Data_Double]

@@End: pop DS popad ret

ENDP StringtoDoubleFloat

;* ПРЕОБРАЗОВАТЬ ЧИСЛО ИЗ КОДА BCD В КОД ASCII * ;* (вспомогательная функция, регистры не сохраняет) * ;* Входные параметры:*

;* Data_BCD – число в BCD-формате.*

;* Регистр DI – указатель на строку результата. *

PROC BCD to ASCI I near

: Выделить знак числа и записать его в ASCII-коде

mov AL,[offset Data_BCD + 9] and AL.AL jz

@@n0

mov AL.’-‘ stosb

: Пропустить незначащие (нулевые) разряды слева (а@пО:

mov ВХ.В

mov CX.9

@@nl: ;проверяем на 0 очередную пару разрядов cmp [byte ptr BX+offset Data_BCD],0 jne

@@n2 dec BX loop

@@nl

: Если значение числа равно нулю, записать символ : нуля в строку результата и выйти из программы

mov AL.’O’

stosb

jmp short

@@End : Пропустить незначащий ноль в старшей : тетраде (если он есть)

@@п2:

; Загрузить первую значащую пару разрядов

mov AL.[BX + offset Data_BCD]

mov AH.AL

: Выделить, перевести в ASCII и

; сохранить старшую тетраду shr AL.4 cmp AL.O

; Если 0 – пропустить старшую тетраду

je

add AL.’O’

stosb

; Выделить, перевести в ASCII и : сохранить младшую тетраду

@@пЗ:

mov AL.AH and AL.OFh

add AL.’O’ stosb dec BX dec CX

jz

@@End -.выход, если это последний разряд

: Распаковать остальные разряды числа (если они есть) РРп4:

; Загрузить очередную пару разрядов

mov AL.[ВХ + offset DataBCD]

mov AH.AL

; Выделить, перевести в ASCII и : сохранить старшую тетраду shr AL.4

add AL.’O’ stosb

; Выделить, перевести в ASCII и : сохранить младшую тетраду

mov AL.AH and AL.OFh

Листинг 2.5 (продолжение)

add AL,’О’ stosb

dec ВХ loop

@@n4

@PEnd:

ret

ENDP BCD to ASCII

;* ОТОБРАЗИТЬ ЧИСЛО В КОДЕ ASCII НА ЭКРАН * ;* Подпрограмма отображает DataString на экран. * ;* Координаты позиции передаются через глобальные * ;* переменные ScreenString и ScreenColumn. После * ;* выполнения операции происходит автоматическое * ;* приращение значений этих переменных.*

PROC ShowDataString near pusha

push ES

mov AX.[CS:MainDataSeg]

mov ES.AX

eld

: Занести координаты сторки

mov DI.offset OutD_String

mov AX,[ScreenStri ng] stosb

mov AX,[ScreenColumn] stosb

; Копировать Data String в OutD String

mov SI,offset Data_String

mov CX.31 rep movsb

; Поставить символ-ограничитель

mov AL.0 stosb

mov SI,offset OutD_String

call ShowString

pop ES

popa

ret

ENDP ShowDataString

111 I..1 Л 1 ‘ 1  ‘ ‘ ‘ I  » 11, L 1 I, ,|, I..!   II.I.ilil I I I1  

• ляAAA ЛА ЛЛ А АА А ЛАД Д А А АхЛллчАА А дя*лХляллХллЖ я *Лдяя

;*ВВЕСТИ ЦЕЛОЕ ЧИСЛО С КЛАВИАТУРЫ*

;* Подпрограмма обеспечивает ввод 9-разрядного * ;* десятичного целого числа со знаком.*

;* Координаты поля ввода задаются через переменные * ;* ScreenString и ScreenColumn. Цвет определяется * ;* переменной TextColorAndBackground.*

PROC GetInteger near pushad

; Инициализировать переменные

@@ClearField:

; Обнулить счетчик цифр

mov ВХ.О

: Установить координаты поля ввода

mov DI.offset OutD_String

mov AX,[ScreenStri ng]

mov [DI], AL

inc DI

mov AX,[ScreenColumn]

mov [DI],AL

inc DI

; Очистить строку (заполнить пробелами)

mov [dword ptr DI],20202020h

mov [dword ptr DI+4],20202020h

mov [word ptr DI+8],2020h

mov [byte ptr DI+10L0 : конец строки

: Цикл ввода PPGetNextChar:

; Отобразить число на экране

mov SI.offset OutD_String call ShowString ‘

; Установить курсор в позицию ввода push [ScreenColumn]

add [ScreenColumn],BX call SetCursorPosition pop [ScreenColumn] : Принять байт с клавиатуры call GetChar

; Цифра или команда? cmp AL.O

jz

@@Command :если ноль – введена команда

; Проверка на специальные символы cmp AL.’-‘

je 1Р0М

; Проверка на диапазон ‘0’-‘9′ cmp AL,’01 jb

@@Error cmp AL,’9′ ja

@@Error

: Проверяем количество символов (не более 10) cmp ВХ.10 jae

@@Error

inc BX

; Записываем символ в число

mov [DS:DI],AL

inc DI

jmp short

@@GetNextChar

Листинг 2.5 (продолжение)

: Проанализировать код команды ^Command:

cmp AH.BEsc;очистить поле

je

@@ClearField

@@TestRubout:

cup AH,BRUBOUT ;"Забой" jne PPTestEnter cmp BX.O

je @PError

: Передвинуть признак конца строки : на разряд влево dec DI dec BX

mov [byte ptr DS:DI],’ ‘

jmp short

@@GetNextChar

@@TestEnter:

cmp AH,B_Enter завершение ввода числа jne

@@Error

; Перевести число в двоичный код

mov SI,offset Data String

mov DI,offset OutD_String

add DI,2

mov CX.10 Mil:

mov AL.[DI]

cmp AL.’ ‘;конец числа?

jbe @PEndOfString

mov [SI],AL

inc SI

inc DI loop

@@il №EndOfString:

mov [byte ptr SI],0 :конец строки call String_to_Int32

jmp short @PEnd

; Ошибка при вводе

@@Error:

call Beep

jmp

@@GetNextChar

@@End: popad ret

ENDP Getlnteger

;* ВВЕСТИ ВЕЩЕСТВЕННОЕ ЧИСЛО С КЛАВИАТУРЫ * ;* Подпрограмиа обеспечивает ввод вещественного *

;* числа в обычном формате (до 18 цифр).*

;* Координаты поля ввода задаются через переменные * ;* ScreenString и ScreenColumn. Цвет определяется * ;* переменной TextColorAndBackground.*

• А А А1 А1 А1 А А AAA А Л* А А А А’А1‘А’ Л1 А А ‘А’ А Л1 АтА А А А AAA A A A A A1 AAA A A A1 А А А А А А Л А

PROC GetFloat near pushad

; Инициализировать переменные

@@ClearField:

; Обнулить счетчик цифр

mov BX,0

; Установить координаты поля ввода

mov DI. offset OutD String

mov AX.[ScreenSt ri ng]

mov [DI].AL

inc DI

mov AX.[ScreenColumn]

mov [DI],AL

inc DI

; Очистить строку (заполнить пробелами)

mov [dword ptr DI],20202020h

mov [dword ptr DI+4],20202020h

mov [dword ptr DI+8],20202020h

mov [dword ptr DI+12],20202020h

mov [dword ptr DI+16],20202020h

mov [byte ptr DI+20L0

; конец строки : Цикл ввода

@@GetNextChar:

; Отобразить число на экране

mov SI,offset OutD_String call ShowString : Установить курсор в позицию ввода push [ScreenColumn]

add [ScreenColumn],BX call SetCursorPosition pop [ScreenColumn]

; Принять байт с клавиатуры call GetChar

; Цифра или команда? cmp AL.O

jz PPConmand .-если ноль – введена команда

; Проверка на специальные символы

cmp AL,’-‘

je <а@М

cmp AL,’,’

je <a@M

; Проверка на диапазон ‘0’-‘9′ сто AL.’O’

Листинг 2.5 (продолжение)’

jb GPError cmp AL,’9′ ja

@@Error

; Проверяем количество символов (не более 20) <?@М: cmp ВХ.20 jae

@@Error

inc ВХ

; Записываем символ в число

mov [DS:DI],AL

inc DI

jmp short

@@GetNextChar

; Проанализировать код команды

@@Command:

cmp AH.B Esc:очистить поле

je

@@clearField

@@TestRubout:

cmp AH.BRUBOUT ;"Забой" jne

@@TestEnter cmp BX.O

je PPError

; Передвинуть признак конца строки : на разряд влево dec DI dec ВХ

mov [byte ptr DS:DI],’ ‘

jmp short

@@GetNextChar

@@TestEnter:

cmpAH.BEnter ;завершение ввода числа

jne@@Error

; Перевести число в двоичный код

movSI,offset DataString

movDI,offset DutD_String

addDI,2

movCX.20

<№il: movAL,[DI]

cmpAL,’ ‘;конец числа?

jbe@@EndOfString

mov[SI],AL

1ncSI

incDI

loopP0il

@@EndOfString:

mov[byte ptr SI],0 ;конец строки

callString_to_DoubleFloat

jmp short

@@End

; Ошибка при вводе

@@Еггог:

cal1 Веер

jmp

@@GetNextChar

@@End: popad ret

ENDP GetFloat

ENDS

ПРИМЕЧАНИЕ

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

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

Листинг 2.6. Тестирование подпрограмм перевода вещественных чисел

IDEAL

Р386

LOCALS

MODEL MEDIUM

; Подключить файл мнемонических обозначений

; кодов управляющих клавиш и цветовых кодов

Include "1istl_03.inc"

; Подключить файл макросов

Include "1istl_04.inc"

SEGMENT sseg para stack ‘STACK’

DB 400h DUP(?)

ENDS

DATASEG

Txtl

DB LIGHTMAGENTA,0,18

DB "Тестирование подпрограмм перевода чисел",О

DB LIGHTGREEN.9.0,"Введите целое число:",О

Листинг 2.6 (продолжение)

Txt2

DB LIGHTGREEN .10.0

DB "Число в шестнадцатеричном коде:",0

DB LIGHTGREEN,11,0

DB "Обратный перевод в десятичный код:",0

DB LIGHTGREEN,13,0

DB "Введите вещественное число:",0

Txt3

DB LIGHTGREEN,14,0,"Число в научной формате:",0

DB LIGHTGREEN,15,0,"Число в обычном формате:",0

DB YELLOW,24,29,"Нажмите любую клавишу",0

ENDS

C0DESEG

;* Основной модуль программы *

• А А А А А А А* А* А* А" А* А’ А’ А А А А А* А А А "А" А’ А А’ А А А А

PROC MathFunctionsTest

mov AX.DGR0UP

mov DS.AX

mov [CS:MainDataSeg],AX

; Установить текстовый режии и очистить экран

mov АХ.З int 10h

; Вывести заголовок

MShowColorText 2,Txtl

: Задать цвет символов числа

mov [TextColorAndBackground],LIGHTGREY

; Ввести и вывести целое число

; Ввести целое число

mov [ScreenStri ng],9

mov [ScreenColumn],21 call Getlnteger

; Вывести комментарии MShowColorText 3,Txt2

; Вывести число в шестнадцатеричном коде MShowHexDWord 10,32,[Data_Int32]

; Вывести число в десятичном коде

mov [ScreenStri ng],11

mov [ScreenColumn],35 call Int32_to_String call ShowDataString

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

; Ввести целое число

mov [ScreenStri ng],13

mov [ScreenColumn],28 call GetFloat

; Вывести комментарии

MShowColorText 3,Txt3

; Вывести число в научной форнате

mov [ScreenStri ng].14

mov [ScreenColumn],25 call DoubleFloat_to_ExpForm call ShowDataString

; Вывести число в обычной форнате

mov [ScreenString],15

mov [ScreenColumn],25 call DoubleFloat_to_String call ShowDataString

mov[ScreenStri ng],25

mov[ScreenColumn],0 call SetCursorPosition

callGetChar

; Переустановить текстовый режин и очистить экран

mov АХ.З int 10h : Выход в DOS

mov AH,4Ch int 21h ENDP MathFunctionsTest ENDS

; Подключить процедуры вывода данных на экран

Include "listl_02.inc"

; Подключить процедуры перевода чисел

Include "list2_05.inc"

END

Источник: Кулаков В. К90 Программирование на аппаратном уровне: специальный справочник (+дискета). 2-е издание. — СПб.: Питер, 2003. — 847 е.: ил.

По теме:

  • Комментарии