Потому пришлось разрабатывать свои операции. Например, часто встречается необходимость вычислить среднее значение для уточнения результата по сумме отдельных измерений. Если даже само измерение укладывается в 16 разрядов, то сумма нескольких таких результатов уже должна занимать 3 байта. В то же время делитель — число измерений — может быть и относительно небольшим, и укладываться в один байт. В листинге 15.2 я привожу процедуру деления 32-разрядных чисел (на всякий случай) на однобайтное число, которая представляет собой модификацию оригинальной процедуры из Application notes 200. Как и ранее, названия переменных отражают назначение процедуры — деление состояния некоего 4-байтового счетчика на число циклов счета (определения регистров-переменных не приводятся, комментарии сохранены из оригинального текста «аппноты», они соответствуют блок-схеме алгоритма, размещенной в pdf-файле).
Листинг 15.2
;********
;div32x8» — 32/8 деление беззнаковых чисел
;Делимое и результат в count_HH (старший), countTH,
;countTM, countTL (младший)
;делитель в cikle
;требуется четыре временных регистра dremL — dremHH
;из диапазона r16-r31
;для хранения остатка
;********
div32x8:
clr dremL ;clear remainder Low byte
clr dremM ;clear remainder
clr dremH ;clear remainder
sub dremHH,dremHH ;clear remainder High byte and carry
ldi cnt,33 ;init loop counter
d_1: rol countTL ;shift left dividend
rol countTM
rol countTH
rol count_HH
dec cnt ;decrement counter
brne d_2 ;if done
ret ;return
d_2: rol dremL ;shift dividend into remainder
rol dremM
rol dremH
rol dremHH
sub dremL,cikle ;remainder = remainder — divisor
sbci dremM,0
sbci dremH,0
sbci dremHH,0
brcc d_3 ;if result negative
add dremL,cikle ;restore remainder
clr temp
adc dremM,temp
adc dremH,temp
adc dremHH,temp
clc ;clear carry to be shifted into result
rjmp d_1 ;else
d_3: sec ;set carry to be shifted into result rjmp d_1
;******** конец 32x8
Многие подобные задачи на деление удается решить значительно более простым и менее громоздким методом, если заранее подгадать так, чтобы делитель оказался кратным степени двойки. Тогда все деление сводится, как мы знаем, к сдвигу разрядов вправо столько раз, какова степень двойки. Для примера предположим, что мы некую величину измерили 64 раза, и хотим узнать среднее. Пусть сумма укладывается в 2 байта, тогда вся процедура деления будет такой:
;деление на 64
clr count_data ;счетчик до 6
div64L:
lsr dataH ;сдвинули старший
ror dataL ;сдвинули младший с переносом
inc count_data
cpi count_data,6
brne div64L
He правда ли, гораздо изящнее и понятнее? Попробуем от радости решить задачку, которая на первый взгляд требует, по крайней мере, знания высшей алгебры — умножить некое число на дробный коэффициент (вещественное число с «плавающей запятой»). Теоретически для этого требуется представить исходные числа в виде «мантисса — порядок», сложить порядки и перемножить мантиссы (см. [10]). Нам же неохота возиться с этим представлением, т. к. мы не проектируем универсальный компьютер, и в подавляющем большинстве реальных задач все конечные результаты у нас представляют собой целые числа.
На самом деле эта задача решается очень просто, если ее свести к последовательному умножению и делению целых чисел, представив реальное число в виде целой дроби с оговоренной точностью. Например, число 0,48576 можно представить как 48 576/100 000. И если нам требуется на такой коэффициент умножить, к примеру, результат какого-то измерения, равный 976, то тогда можно действовать, не выходя за рамки диапазона целых чисел: сначала умножить 976 на 48 576 (получится заведомо целое число 47 410 176), а потом поделить результат на 105, чисто механически перенеся запятую на пять разрядов. Получится 474,10176 или, если отбросить дробную часть, 474. Большая точность нам и не требуется, т. к. и исходное число было трехразрядным.
Улавливаете, к чему я клоню? С числами в десятичном виде хорошо работать руками, просто отсчитывая разряды. Нам же делить на сто тысяч в 8-разрядном МК крайне неудобно — представляете, насколько громоздкая процедура получится? Наше ноу-хау будет состоять в том, что мы для того, чтобы «вогнать» дробное число в целый диапазон, будем использовать не десятичную дробь, а двоичную — деление тогда сведется к описанной «механической» процедуре сдвига, аналогичной переносу запятой в десятичном виде.
Читать дальше
Конец ознакомительного отрывка
Купить книгу