Главная страница
Библиотека (скачать книги)
Скачать софт
Введение в программирование
Стандарты для C++
Уроки по C#
Уроки по Python
HTML
Веб-дизайн
Ассемблер в среде Windows
ActiveX
Javascript
Общее о Линукс
Линукс - подробно
Линукс - новое
Delphi
Паскаль для начинающих
Турбопаскаль
Новости
Партнеры
Наши предложения
Архив новостей





ГЛАВА 7
Арифметические операции
Как мы уже говорили, выполнение арифметических операций в 8-разрядном МК связано с некоторыми трудностями, т. к. размер операндов простого сложения или вычитания ограничен целым значением 255 (или, для чисел со знаком, значением от -128 до +127). А если учитывать, что результат этих операций (не говоря уж об умножении или делении) может легко выйти за пределы восьми разрядов, то ограничения становятся еще сильнее. Поэтому для работы с многоразрядными и дробными числами приходится изобретать разные приемы, а для этого необходимо, как минимум, вспомнить школьную арифметику, и не только ее.
Подумаем сначала — ас какими числами приходится работать на практике? Если говорить о целых числах, то большинство реальных нужд вполне укладывается в трехбайтовое значение (224 или 16 777 216). Этот же диапазон дает достаточное для практики значение точности (семь десятичных разрядов), большие числа обычно округляют и записывают в виде "мантисса-порядок", с добавкой степени 10. То же касается и разрядности дробных чисел. При этом следует учесть, что любая арифметическая операция дает погрешности округления, которые могут накапливаться. Углубляться в этот достаточно сложный вопрос мы не будем — нам достаточно того факта, что, оперируя с трехбайтовыми числами, как результатом обычных операций деления и умножения, мы не выходим за пределы погрешности в шестом знаке, что значительно превышает разрешение рядовых 10-разрядных АЦП— с числом градаций 1024, означающем ошибку уже в третьем, максимум (в реальности так не бывает) в четвертом десятичном знаке.
Потому, хотя в типовых приложениях для микропроцессоров оперируют либо 16-, либо 32-разрядными двоичными числами, мы в большинстве случаев ограничимся трехбайтовыми (24-разрядными) числами — нет смысла занимать дефицитный регистр (причем в арифметических операциях— и не один), если он все равно всегда будет равен нулю. Однако возможность получения полных 32 разрядов также не следует упускать из виду — во многих случаях это может понадобиться (например, как результат промежуточных операций) и большинство описанных далее процедур мы будем рассчитывать как на 24 разряда, так и на 32.

Стандартные арифметические операции
О том, как выполнять сложение и вычитание 8-разрядных чисел с учетом переноса в следующий разряд, мы уже говорили в главе б при обсуждении штатных команд AVR. Эту методику легко распространить на числа любой разрядности, например:
add datalO,data20 /младшие разряды
adc datall,data21 ;байт1 + С
adc datal2,data22 ;байт2 + С
adc da_tal3,data23 /старшие разряды + С

Здесь складываются 32-разрядные числа, представленные побайтно в регистрах datal3:datal2:datall:datal0 И data23:data22:data21:data20, результат оказывается в datai3— dataio. Эту процедуру несложно оформить в виде макроса (листинг 7.1).
.macro _Add32
add @3,@7 /младшие разряды
adc @2,@6 ;байт1 + С
adc @1,@5 /байт2 + С
adc @0,@4 /старшие разряды + С .endmacro

Привызове такого макроса операнды указывают в "арабском" порядке, начиная со старшего, сначала все байты первого слагаемого, затем все байты второго слагаемого, результат займет байты первого слагаемого. Например, такой вызов:
_Add32 ZH,ZL,YH,YL,datal,data2,ХН,XL
предполагает, что в регистрах ZH, ZL,YH,YL находятся байты первого слагаемого (старший в ZH, младший в YL), а в datal,data2,XH,XL— байты второго (старший в datal, младший в XL), результат будет записан в ZH, ZL, YH, YL.
Аналогично записывается операция вычитания с заменой add на sub, a adc на sbc. Для 24- или 16-разрядных исходных чисел достаточно из этих процедур убрать лишние строки и байты в исходных данных. Если предполагается, что результат сложения двух, например, 24-разрядных чисел займет более трех байтов, то первое слагаемое (оно же результат) должно по-прежнему занимать четыре байта (листинг 7.2).

.macro _Add24_32
add @3,@б /младшие разряды
adc @2,@5 ;байт1 + С
adc @1,@4 ;байт2 + С
adc @0,0 /старший разряд + С .endmacro

Соответственно, вызов такого макроса может, например, выглядеть, как _Add24 ZH,ZL,YH,YL,datal,ХН,XL
где в регистрах ZH,ZL,YH,YL находятся байты первого слагаемого (старший в ZH, младший в YL), а в datal,XH,XL — байты второго (старший в datal, младший в XL), результат будет записан, как и ранее, в ZH, ZL, YH, YL.

Умножение многоразрядных чисел
Несколько сложнее с умножением и особенно делением. Приводимые в "апп-нотах" алгоритмы 16-разрядной арифметики для наших целей придется творчески переработать, тем более что в них имеются ошибки. На рис. 7.1 приведена блок-схема алгоритма перемножения двух 16-разрядных беззнаковых чисел (MPY16U), скопированная из PDF-файла Application note AVR200. Утолщенными линиями показано необходимое исправление. Точно такое же исправление следует сделать в алгоритме перемножения двух 8-разрядных чисел (MPY8U).

Ассемблерный текст процедур умножения можно найти в той же "аппноте", только представленной в виде asm-файла (его можно заполучить из комплекта поставки AVR Studio или скачать с сайта Atmel, если в таблице с перечнем Application notes щелкнуть по значку диска, а не PDF). Для внесения изменений найдите в тексте процедуру mpyi6u, и переставьте метку mi6u_i вместо команды brcc noad8, перед которой она стоит, двумя командами ранее — перед командой lsr mpi6uH. Аналогичную манипуляцию нужно проделать в процедуре mpy8u с меткой m8u_i, если вы желаете воспользоваться 8-разрядным умножением.
На практике, как мы говорили ранее, 32-разрядное число (макс. 4 294 967 296, т. е. более девяти десятичных разрядов) с точки зрения точности в большинстве случаев избыточно. Если мы ограничимся 24 разрядами результата, то нам придется пожертвовать частью диапазона исходных чисел так, чтобы сумма двоичных разрядов сомножителей не превышала 24. Например, можно перемножать два 12-разрядных числа (в пределах 0-4095 каждое) или 10-разрядное (скажем, результат измерения АЦП) на 14-разрядный коэффициент (до 16 383). Так как при умножении точность не теряется, то этого оказывается более чем достаточным, чтобы обработать большинство практических величин.



     
 

Библиотека программиста. 2009.
Администратор: admin@programmer-lib.ru