Главная » Микроконтроллеры
Призовой фонд
на март 2017 г.
1. UNI-T UT-39C
Паяльник
2. Тестер компонентов LCR-T4
Паяльник
3. 100 руб.
От пользователей

Похожие статьи:


Умножение и деление

К сожалению, деление и умножение чисел в дополнительном коде существенно отличаются от подобных действий над беззнаковыми числами. Существуют различные алгоритмы, но все они значительно сложнее и не слишком хорошо подходят для системы команд AVR. Поэтому на практике чаще всего прибегают к действиям над модулями чисел. Для этого, в первую очередь, анализируют и запоминают знаки чисел, затем изменяют знак у отрицательных чисел (преобразуют в равные по модулю положительные числа) и используют беззнаковые операции умножения и деления над их модулями. Полученный результат (беззнаковое число), корректируют с учетом знаков исходных множителей (делимого и делителя) по известным законам алгебры: при одинаковых знаках произведение (частное) остается без изменений, при разных знаках - знак результата должен быть изменен на противоположный.

Рассмотрим пример знакового деления 2-разредного делимого (размещается в регистрах R17:R16) на 1-байтовый делитель (в регистре R20) с помощью подпрограммы беззнакового деления  div16_8.

Для изменения знака 16-разрядных чисел на противоположный, можно использовать следующую подпрограмму:

 

  neg16:  
     com   R16         ;R17:R16 = 0xFFFF - R17:R16 + 1
     com   R17 
     subi  R16,low(-1)
     sbci  R17,high(-1)
     ret

Вначале необходимо определить знак будущего частного. Для этого удобно произвести операцию “Исключающее ИЛИ” между знаковыми разрядами обеих чисел и скопировать результат во флаг пользователя T. Если исходные знаки делимого и делителя одинаковые (T=0), то частное – будет неотрицательным числом. В ином случае (T=1) частное отрицательное число, которое нужно будет скорректировать после беззнакового деления:

     eor   R17,R20 ;сравниваем знаковые биты 
     bst   R17,7   ;запоминаем во флаге T знак будущего частного
     eor   R17,R20  ;восстанавливаем R17 

Далее, анализируя все те же знаковые разряды, преобразуют отрицательные числа в равные им по модулю положительные:

	sbrc  R20,7    ;если делитель R20 < 0,
    neg   R20      ;то изменяем знак R20 
	sbrc  R17,7    ;если делимое R17:R16 < 0,
	rcall neg16    ;то изменяем знак R17:R16

Теперь в регистрах R17:R16 и R20 находятся два неотрицательных числа и можно использовать беззнаковое деление. После этого, в случае необходимости, нужно учесть целочисленный остаток (на выходе из подпрограммы деления он находится в R18) и округлить частное до ближайшего целого числа:

     rcall div16_8  ;вызываем подпрограмму беззнакового деления
     lsr   R20      ;если целочисленный остаток от деления 
	 cpc   R18,R20  ;больше половины делителя (R18 > R20),
     brcs  ncr
	 subi  R16,low(-1)  ;то округляем частное в большую 
     sbci  R17,high(-1)  ;сторону, добавляя к нему 1

Полученное частное в регистрах  R17:R16 нужно скорректировать с учетом его истинного знака:

ncr: brtc  nxt      ;если T=1 (частное R17:R16 < 0),   
     rcall neg16    ;то изменяем его знак на противоположный
nxt:.

Как видно, такой подход вносит лишь незначительные затруднения при программировании знаковых операций деления и умножения. Но существует ряд случаев в которых операции над беззнаковыми числами и числами, представленными дополнительном коде, практически неразличимы.

В первую очередь это относится к умножению. У AVR имеются специально разработанные для знаковых операций команды muls Rd,Rr, mulsu Rd,Rr. Это дает возможность эффективно использовать вычислительную схему подобную той, которая используется для умножения беззнаковых чисел. Например, для двух 16-разрядных чисел (индексом S - Sign отмечены знаковые числа, а индексом U - Unsign беззнаковые) (XH:XL)S = 28*XHS + XLU и (YH:YL)S = 28*YHS + YLU она будет иметь вид:

(XH:XL)S*(YH:YL)S = (28*XHS + XLU)*(28*YHS + YLU) = 216*XHS*YHS + 28*(XHS*YLU + YHS*XLU) + XLU*YLU.

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

На рис.1 показано, в какой последовательности происходит отыскание произведения. Для умножения XLU*YLU надо использовать команду mul Rd,Rr, для XHS*YHS инструкцию muls Rd,Rr, а для XHS*YLU и YHS*XLU - mulsu Rd,Rr (на месте Rd должен обязательно находится знаковый множитель!). Подпрограмма такого знакового умножения:

; R23:R22:R21:R20 = R17:R16 * R19:R18
; R23:R22:R21:R20 – знаковое произведение
; R17:R16 – знаковое множимое
; R19:R18 – знаковый множитель
; R1,R0 - вспомогательные регистры

muls16_16:
     mul   R16,R18  ;находим XLU*YLU = R16*R18 и заносим его в
     movw  R20,R0   ;младшие байты произведения R21:R20
     muls  R17,R19  ;находим XHS*YHS = R17*R19 и заносим его в
     movw  R22,R0   ;старшие байты произведения R23:R22
     mulsu R17,R18  ;находим XHs*YLU = R17*R18 и прибавляем его к
     clr   R17      ;байтам R23:R22:R21 произведения
     sbci  R17,0
     add   R21,R0   
     adc   R22,R1    
     adc   R23,R17   
     mulsu R19,R16  ;находим YHS*XLU = R19*R16 и прибавляем его к 
     clr   R17
     sbci  R17,0
     add   R21,R0   ;байтам R23:R22:R21 произведения 
     adc   R22,R1
     adc   R23,R17  
     ret 

Еще проще найти произведение однобайтового числа XS на 2-байтовое (YH:YL)S:
XS*(YH:YL)S = XS*(28*YHS + YLU) = 28*XS*YHS + XS*YLU.

Подпрограмма знакового умножения 8x16:

; R21:R20:R19 – знаковое произведение 
; R18 – знаковое множимое
; R17:R16 – знаковый множитель
; R1,R0 – вспомогательные регистры

muls8_16:
     mul   R18,R17  ;находим XS*YHS = R18*R17 и заносим его в   
     movw  R20,R0   ;старшие байты произведения R21:R20   
     mulsu R18,R16  ;находим XS*YLU = R18*R16 
     clr   R18      
     sbci  R18,0         
     add   R19,R0   ;и прибавляем его  
     add   R20,R1   ;к произведению R21:R20:R19  
     adc   R21,R18     
     ret

Деление и умножение на целую степень 2 в дополнительном коде, как и в случае с беззнаковыми числами, равносильно сдвигу числа на один разряд вправо или в лево соответственно. Но здесь существует и одно различие: знаковый разряд числа должен оставаться неизменным. Сдвиг чисел в дополнительном коде иногда называют арифметическим сдвигом. Так выглядит арифметический сдвиг влево (умножение на 2)
(-46)*2 = 0b11010010 << 1 = 0b10100100 = -92
и арифметический сдвиг вправо (деление на 2)
(-46)/2 = 0b11010010 >> 1 = 0b11101001 = -23 .

Последнее действие выполняется командой asr Rd.

При округлении отрицательных чисел нужно придерживаться следующего правила. Если после знакового деления
XS/YS = ZS + RS
образовался целочисленный остаток |RS| ≥ 0,5*|YS|, то к частному
ZS нужно добавить единицу; в противном случае ZS остается без изменений. Например,
XS = -23 = 0b11101001, YS = 2,
XS/YS = ZS + RS = [-12] + {1},
ZS ≈ (-12) + 1 = -11 = 0b11110101.

Подобие алгоритмов деления на целую степень 2 у беззнаковых и знаковых чисел, позволяет очень просто реализовать важный частный случай деления на 3 (см. рис.8,  Беззнаковые числа) на основе того же ряда
XS/3 = XS/2 - XS/4 + XS/8 - XS/16 + XS/32 - …

Подпрограмма деления 2-байтового знакового числа:

; R19:R18 = R17:R16/3
; R19:R18 – знаковое частное
; R17:R16 – знаковое делимое
; R20 – вспомогательный регистр
 
divs16to3:
    clr   R18      ;очищаем вспомогательные регистры R18,R19
    clr   R19      ;при входе в подпрограмму   
ds1: rcall ashft_right
	brcc  PC+2     ;если C=1, 
    ret            ;то завершаем деление
    add   R18,R16  ;в ином случае добавляем к накопителю
	adc   R19,R17  ;очередной нечётный член ряда
	rcall shift_right
	brcc  PC+2     ;если C=1,
	ret            ;то завершаем деление
    sub   R18,R16  ;в ином случае вычитаем из накопителя
	sbc   R19,R17  ;очередной чётный член ряда
    rjmp  ds1

ashft_right: 
    asr   R17      ;производим знаковое деление R17:R16 / 2,
	ror   R16      ;получая очередной член ряда
	clr   R20      ;если R20 = R17:R16 + С = 0x10000
	adc   R20,R16  ;( т.е. знаковое число R17:R16 = 0),
	clr   R20      ;то выходим из подпрограммы с флагом С=1
 	adc   R20,R17
	ret

Перейти к следующей части:

Теги:

Котов Игорь Юрьевич Опубликована: 2012 г. 0 0
Я собрал 0 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (1) | Я собрал (0) | Подписаться

0
Григорий #
В подпрограмме muls8_16, возможно, ошибка в первой команде (строка 7). Вместо mul, должно быть muls (умножение знаковых). Поправьте если неправ.
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется электрическое сопротивление?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Pickit 2 - USB-программатор PIC-микроконтроллеров
Pickit 2 - USB-программатор PIC-микроконтроллеров
ELM327 OBD II — адаптер с поддержкой CAN Набор 4WD Kit Bluetooth
вверх