Преобразуем ASCII-символы в двоичный код...и - обратно:
На процессе преобразования (прямого и обратного)
остановимся поподробнее, поскольку - это самая интересная часть проекта. Начнём с того, что
внимательно рассмотрим весь процесс.
Набранное число состоит из символов. Когда мы
нажимаем на кнопку арифмометра комп, при помощи API-функции (см. процедуру
nabor), добавляет символ на
дисплей. Кроме того, этот символ (символы) остаются в переменной
buf db 10 dup (0).
В этой переменной мы задаём размер буфера и, попутно, очищаем его (заполняем нoлями).
На каждую
цифру-символ, от 0 до 9, уходит 1 байт, а на hex-представление - 1 тетрада. Т.е., в hex-варианте
в байт помещается 2 символа и первый - это
3 (см. таблицу ASCII-кодов), а второй,
в нашем случае, это цифра 0-9.
И так, набор ASCII-символов в переменной
buf. Именно из этого источника мы
будем брать символы,
по-байтно, для преобразования
их в двоичный код. Для перевода ASCII-символа (hex-символа) в двоичный код мы
удалим из него
3.
К примеру, мы набрали число 1234. В переменной
buf
оно сохранилось в виде:
1234 --> 31323334h
Теперь мы должны умножить соответствующий
символ на степень основания и сложить получившиеся результаты. Но...степень основания мы
не узнаем, пока не прочтём все цифры и по их количеству не определим порядок числа. М-да...
Оказывается - выход есть:
((((0)*10+1)*10 + 2)*10 + 3)*10 + 4 = 1234
Спасибо товарищу Горнеру за
оригинальное решение.
Код процедуры:
asc2bin proc
pushad
xor ebx,ebx ;обнулимся...
xor eax,eax ;EAX=0, накопитель дв.кода
mov edi,ten ;10 - основание системы счисления
lea ecx,buf ;адрес 1-го ASCII-символа
b_cycl:
mov bl,[ecx] ;добавим в EBX ASCII-символ
or bl,bl ;закончились символы?
jz fin ;завершаем преобразование
and bl,0Fh ;преобразуем ASCII-символ
mul edi ;EDX:EAX = EAX*10
add eax,ebx ;промежуточная сумма
jc exit ;если CF=1
inc ecx ;к следующему символу
jmp b_cycl ;повторим
fin:
mov bin,eax ;сохраним дв.код
popad
exit: ;для возможной обработки CF
ret
asc2bin endp
рис.8
Теперь в переменной "bin" у нас содержимое "buf" в двоичном виде.
Обратное преобразование:
полученный, в результате арифметической операции, двоичный код мы будем делить на основание
системы счисления, а остатки от деления преобразовывать в ASCII-символы,
путём прибавления к ним
3 и, опять -
по-байтно.
Код процедуры:
bin2asc proc
pushad
mov eax,dword ptr res
lea edi,buf
mov ebx,ten ;10 - основание системы счисления
xor ecx,ecx ;обнуляем счётчик
conv:
xor edx,edx ;обнулимся...
div ebx ;EAX=частное, EDX=остаток
add dl,"0" ;cформируем ASCII-символ ("0"->30h)
push edx ;сохраним для разворота
inc ecx ;ECX+1 --> для "rev"
test eax,eax ;преобразование закончено?
jnz conv ;если нет - продолжаем
rev: ;разворачиваем символы
pop edx ;символ из стека в DL
mov [edi],dl ;занесём ASCII-символ в буфер
inc edi ;смещаемся
loop rev ;повторяем, пока ЕСХ не 0
mov byte ptr [edi],0 ;добавляем 0 в конец строки
exit:
popad
ret
bin2asc endp
рис.9
Что-бы перейти к операциям с дробными (вещественными) числами, т.е. добавить на
арифмометр кнопку с символом ".", необходимо продолжить преобразование в направлении ASCII-
символ --> вещественное число и подключить к арифметике
сопроцессор.
Непростая...арифметика
Она - действительно непростая. Наш арифмометр работает с 4-мя
основными арифметическими действиями, но расчитан на переменные типа "dword" и "qword". А такие
числа напрямую складывать и умножать - нельзя. Мы будем иметь дело с
длинными числами.
Посмотрим, что это такое на примере операции сложения:
plus --> plu proc --> plu_ex -->
pluex proc --> add_32 procЧто-бы не запутатся в арифметике, разобьём её на вспомогательные процедуры
(см.
рис.9). Нас интересует последняя процедура:
;из файла data.inc:
;bin dd 0,0 - 2-е число в дв.виде
;bin1 dd 0,0 - 1-е число в дв.виде
;res dq 0,0,0,0 - итог операции (сложения) в дв.виде
add_32 proc
push eax ;сохраняем ЕАХ
mov eax,bin1
add eax,bin ;складываем младшие дв.слова
mov dword ptr res,eax
mov eax,bin1+4
adc eax,bin+4 ;складываем старшие дв.слова, с учётом переноса
mov dword ptr res+4,eax
pop eax ;восстанавливаем ЕАХ
ret
add_32 endp
Команды ADC (сложение с учетом переноса) и SBB (вычитание с учетом займа) специально
были введены для подобных операций. При сложении сначала складывают самые младшие (байты,
слова) двойные слова командой ADD, а затем складывают все остальное командами ADC, двигаясь
от младшего конца числа к старшему.
Например:
4357 --> 43 57
+ 5128 --> + 51 + 28
---- -- --
9485 = 94 + 85
Т.е. складываем младшие разряды, потом - старшие, а потом - "складываем" результаты.
При этом учитываем, что вместо "85" могло получится, к примеру, "105".
P.S.
При пользовании
арифмометром необходимо помнить:
-- арифмометр работает только с целыми, положительными
числами
-- при делении арифмометр выдаёт только частное
-- код арифмометра не
обрабатывает ошибки - программа просто зависнет...или отключится.
Удачи !
22.08.2018г.
...назад
дальше...