Рассмотрите внимательно текст процедуры 16-разрядного умножения в «аппноте» 200 и обратите внимание на две особенности. Во-первых, для представления результата в общем случае требуется 4 байта (регистра), т. е. результат будет 32-разрядным числом, что понятно. Поэтому, во-вторых, разработчики используют в целях экономии одни и те же регистры для множителя и младших байтов результата, но называют их разными именами. Такой прием мы уже обсуждали, и ясно, для чего он применяется — для большей внятности текста процедуры. И тем не менее, вообще говоря, это недопустимо — слишком легко забыть про то, что под разными именами кроется один и тот же регистр, и использовать его еще где-то (не будете же вы, в самом деле, держать неиспользуемыми аж целых 7 регистров только для того, чтобы один раз где-то что-то перемножить, правда?). Потому мы в дальнейшем обойдемся без таких фокусов — лучше написать внятные комментарии, а имена оставить уникальные, даже если они и не окажутся «говорящими».
Рис. 15.1. Процедура перемножения двух 16-разрядных чисел из pdf-файла Atmel Application note AVR200 с исправленной ошибкой
На практике, как мы говорили ранее, 32-разрядное число (максимум 4 294 967 296, т. е. более 9 десятичных разрядов) с точки зрения точности в большинстве случаев избыточно. Если мы ограничимся 24 разрядами результата, то нам придется пожертвовать частью диапазона исходных чисел так, чтобы сумма двоичных разрядов сомножителей не превышала 24. Например, можно перемножать два 12-разрядных числа (в пределах 0—4095 каждое) или 10-разрядное (скажем, результат измерения АЦП) на 14-разрядный коэффициент (до 16 383). Так как при умножении точность не теряется, то этого оказывается более чем достаточным, чтобы обработать большинство практических величин.
Процедура перемножения двух таких величин в исходном 16-разрядном виде, с представлением результата в трехбайтовой форме может быть легко получена из исправленной нами процедуры MPY16U по «аппноте» 200, но я решил воспользоваться тем обстоятельством, что для контроллеров семейства Mega определены аппаратные операции умножения (в Приложении 4 я их не привожу). Тогда алгоритм сильно упрощается, причем он легко модифицируется как для 32-, так и для 24-разрядного результата. Таким образом, для Tuny и Classic по-прежнему следует пользоваться обычными процедурами из «аппноты» (исправленными), а алгоритм для Mega приведен в листинге 15.1 (в названиях исходных переменных отражен факт основного назначения такой процедуры — для умножения неких данных на некий коэффициент). Сокращения LSB и MSB, которые нам еще встретятся не раз, означают least (most) significant bit — младший (старший) значащий разряд, по-русски МЗР и СЗР соответственно.
Листинг 15.1
.def dataL = r4 ;multiplicand low byte
.def dataH = r5 ;multiplicand high byte
.def KoeffL = r2 ;multiplier low byte
.def koeffH = r3 ;multiplier high byte
.def temp = r16 ;result byte 0 (LSB — младший разряд)
.def temp2 = r17 ;result byte 1
.def temp3 = r18 ;result byte 2 (MSB — старший разряд)
…
;**********
;умножение двух 16-разрядных величин, только для Меда
;исходные величины dataH: dataL и KoeffH: KoeffL
;результат 3 байта temp2:temp1:temp;
;**********
Mu1616:
clr temp2 ;очистить старший
mul dataL,KoeffL ;умножаем младшие
mov temp,r0 ;в r0 младший результата операции mu1
mov tempi,r1 ;в r01 старший результата операции mu1
mul dataH,KoeffL ;умножаем старший на младший
add temp1,r1 ;в r0 младший результата операции mu1
adc temp2,r1 ;в r01 старший результата операции mu1
mul dataL,KoeffH ;умножаем младший на старший
add temp1,r0 ;в r0 младший результата операции mu1
adc temp2,r01 ;в r01 старший результата операции mu1
mul dataH,KoeffH ;умножаем старший на старший
add temp2,r0 ;4-й разряд нам тут не требуется, но он — в r01
ret
;**********
Как видите, эта процедура легко модифицируется под любую разрядность результата, если нужно получить полный 32-разрядный диапазон, просто добавьте еще один регистр для старшего разряда ( temp3, к примеру) и одну строку кода перед командой ret:
adc temp3,r01
Естественно, можно просто обозначить r01через temp3, тогда и добавлять ничего не придется.
Процедуры деления для многобайтовых чисел
Деление — значительно более громоздкая процедура, чем умножение, требует больше регистров и занимает больше времени (MPY16U из «аппноты» занимает 153 такта, по уверению разработчиков, а аналогичная операция деления двух 16-разрядных чисел — от 235 до 251 тактов). Операции деления двух чисел (и для 8-, и для 16-разрядных) приведены в той же «аппноте» 200, и на этот раз без ошибок, но они не всегда удобны на практике: часто нам приходится делить результат какой-то ранее проведенной операции умножения или сложения, а он нередко выходит за пределы двух байтов.
Читать дальше
Конец ознакомительного отрывка
Купить книгу