AVR202: 16-битная арифметика Печать
Добавил(а) microsin   

В апноуте AVR202 [1] раскрываются вопросы реализации 16-разрядной арифметики на языке ассемблера 8-разрядного микроконтроллера AVR:

• Простые подпрограммы расширяемые до 32 бит или любой длины слова данных.
• Плотность кода и скорость обработки сопоставимы с аналогичными показателям 16-разрядных микроконтроллеров.
• Есть запускаемый пример кода.

Прим. переводчика: описанный в этом даташите код имеет смысл использовать только в тех редких случаях, когда Вы программируете на языке ассемблера, и хотите добиться от своей программы экстремального быстродействия. При программировании на языке C сложение чисел любой разрядности реализуется довольно просто средствами самого языка.

Список всех реализаций с их ключевыми параметрами приведен в таблице 1.

Таблица 1. Арифметические действия и их параметры эффективности.

Операция Размер кода
(в словах)
Время выполнения
(в циклах ядра)
Сложение двух 16-битных регистровых переменных 2 2
Сложение 16-битной непосредственной константы и 16-битной регистровой переменной 2 2
Вычитание 16-битных регистровых переменных 2 2
Вычитание 16-битной непосредственной константы из 16-битной регистровой переменной 2 2
Сравнение двух 16-битных регистровых переменных 2 2
Сравнение 16-битной непосредственной константы и 16-битной регистровой переменной 3 3
Инверсия в дополнительном коде 16-битной регистровой переменной 4 4

Примечание: размер кода для AVR отсчитывается иногда по словам, что создает некоторую путаницу. Одна инструкция ассемблера AVR занимает как минимум 1 слово (2 байта).

[16-битное сложение регистров]

Операция выполняется по следующему алгоритму:

1. Складываются младшие байты слов.
2. Затем складываются старшие байты слов с учетом переноса, если он возник на шаге 1.

Можно таким же образом складывать дополнительное количество n байт, если учитывать перенос, возникающий от сложения предыдущего байта.

[Сложение 16-битного регистра и 16-битной непосредственной константы]

Под термином "непосредственная" (immediate) понимается операнд, входящий в состав команды языка ассемблера. Поскольку у AVR нет инструкции прибавления непосредственной константы или прибавления непосредственной константы с учетом переноса, то вместо этого используется вычитание непосредственной константы и вычитание непосредственной константы с учетом переноса. Звучит немного дико, но работает:

1. Сначала выполняется операция непосредственного вычитания (subtract immediate) проинвертированного числа из младшего регистра.
2. Затем делается то же самое для старшего байта с учетом переноса: выполняется subtract immediate with carry старшего байта проинвертированного числа из старшего регистра.

Таким же образом можно добавлять другие n-байт с учетом переноса.

[16-битное вычитание регистров]

Операция выполняется по следующему алгоритму:

1. Вычитаются младшие байты слов.
2. Затем вычитаются старшие байты слов с учетом переноса, если он возник на шаге 1.

Можно таким же образом вычитать дополнительное количество n байт, если учитывать перенос, возникающий от вычитания предыдущего байта.

[Вычитание из 16-битного регистра 16-битной непосредственной константы]

Операция выполняется по следующему алгоритму:

1. Вычитается из младшего байта регистра младший байт непосредственной константы.
2. Затем вычитается из старшего байта регистра старший байт непосредственной константы с учетом переноса, если он возник на шаге 1.

Можно таким же образом вычитать дополнительное количество n байт, если учитывать перенос, возникающий от вычитания предыдущего байта.

[Сравнение двух регистровых 16-битных переменных]

Операция выполняется по следующему алгоритму:

1. Сначала сравниваются младшие байты.
2. Затем сравниваются старшие байты с учетом переноса, если он возник на шаге 1.

Обратите внимание, что инструкция ассемблера "сравнение с учетом переноса" (Compare with Carry) поддерживает распространение нуля (zero-propagation). Это означает, что все инструкции ветвления по условию могут использовать двухшаговые операции сравнения. Можно сравнивать дополнительно n-количество байт, если сравнивать их с учетом переноса от сравнения на предыдущем шаге.

[Сравнение регистровой 16-битной переменной и 16-битной непосредственной константы]

Операция выполняется по следующему алгоритму:

1. Сравнивается младший регистр с младшим байтом непосредственной константы.
2. Старший байт непосредственной константы сохраняется в третий регистр.
3. Сравниваются старший регистр переменной с третьим регистром с учетом переноса, если он возник на шаге 1.

[Инверсия в дополнительном коде 16-битной регистровой переменной]

Здесь применяется арифметика в дополнительном коде, которая нужна для операции сложения непосредственной константы с регистром. Операция выполняется по следующему алгоритму:

1. Инвертируется младший байт.
2. Инвертируется старший байт.
3. Вычитается $FF из младшего байта.
4. Вычитается с учетом переноса $FF из старшего байта.

Примечание: шаги 3 и 4 эквивалентны добавлению $0001 к 16-разрядному числу.

;**** AVR202 ***************************************************************
;*
;* Title: 16-bit Arithmetics
;* Version: 1.1
;* Last updated: 97.07.04
;* Target: AT90Sxxxx (подойдет для всех AVR)
;*
;* Support E-mail: avr@atmel.com
;*
;* Описание: этот апноут перечисляет реализации следующих операций
;* сложения/вычитания/сравнения:
;*
;* "add16" ADD 16+16
;* "adddi16" ADD 16+Immediate(16)
;* "sub16" SUB 16-16
;* "subi16" SUB 16-Immediate(16)
;* "cp16" COMPARE 16/16 
;* "cpi16" COMPARE 16/Immediate 
;* "neg16" NEGATION 16
;***************************************************************************
.cseg
   ldi   r16,0x12 ;Инициализация нескольких регистров для демонстрации
   ldi   r17,0x34 ; подпрограмм ниже. Все ожидаемые результаты представлены
   ldi   r18,0x56 ; в комментариях к коду.
   ldi   r19,0x78 ;
;*************************************************************************** ;* "add16" - сложение 16-битных регистров ;* ;* Этот пример складывает 2 регистровых переменных - 2 пары регистров ;* (add1l,add1h) и (add2l,add2h). Результат будет помещен в (add1l, add1h). ;* ;* Количество слов программы: 2 ;* Количество циклов: 2 ;* Используемые младшие регистры: не используются ;* Используемые старшие регистры: 4 ;* ;* Примечание: сумма и слагаемое используют те же самые регистры. Это ;* означает, что одно слагаемое будет перезаписано суммой. ;*************************************************************************** ;**** Регистровые переменные .def add1l = r16 .def add1h = r17 .def add2l = r18 .def add2h = r19
;***** Код add16: add add1l, add2l ;складываются младшие байты adc add1h, add2h ;складываются старшие байты с учетом переноса ;Ожидаемый результат: 0xAC68
;*************************************************************************** ;* "addi16" - сложение 16-разрядной регистровой переменной ;* с immediate-константой. ;* ;* Этот пример складывает регистровую переменную (addi1l,addi1h) ;* с непосредственной 16-битной константой, определенной оператором .equ. ;* Результат помещается в (addi1l, addi1h). ;* ;* Количество слов программы: 2 ;* Количество циклов: 2 ;* Используемые младшие регистры: не используются ;* Используемые старшие регистры: 2 ;* ;* Примечание: сумма и слагаемое используют те же самые регистры. Это ;* означает, что одно слагаемое будет перезаписано суммой. ;*************************************************************************** ;***** Регистровые переменные .def addi1l = r16 .def addi1h = r17
;***** 16-битная immediate-константа .equ addi2 = 0x1234
;***** Код addi16: subi add1l, low(-addi2) ;Добавить младший байт ( x -(-y)) = x + y sbci add1h, high(-addi2) ;Добавить старший байт с учетом переноса ;Ожидаемый результат 0xBE9C
;*************************************************************************** ;* "sub16" - вычитание 16-битных регистровых переменных ;* ;* В этом примере вычитается пара регистровых переменных (sub1l,sub1h) ;* из другой пары (sub2l, sub2h). Результат помещается в регистры ;* sub1l, sub1h. ;* ;* Количество слов программы: 2 ;* Количество циклов: 2 ;* Используемые младшие регистры: не используются ;* Используемые старшие регистры: 4 ;* ;* Примечание: результат и "sub1" используют одни и те же регистры, поэтому ;* "sub1" будет перезаписан результатом. ;*************************************************************************** ;***** Регистровые переменные .def sub1l = r16 .def sub1h = r17 .def sub2l = r18 .def sub2h = r19
;***** Код sub16: sub sub1l,sub2l ;Вычитание младших байтов sbc sub1h,sub2h ;Вычитание старших с учетом переноса ;Ожидаемый результат 0x4646
;*************************************************************************** ;* "subi16" - вычитание 16-битной immediate-константы из 16-битного ;* регистра. ;* ;* В этом примере вычитается непосредственное 16-разрядное число subi2 ;* из 16-разрядного регистра (subi1l,subi1h). Результат помещается в ;* регистры subi1l, subi1h. ;* ;* Количество слов программы: 2 ;* Количество циклов: 2 ;* Используемые младшие регистры: не используются ;* Используемые старшие регистры: 2 ;* ;* Примечание: результат и "subi1" используют одни и те же регистры, поэтому ;* "subi1" будет перезаписан результатом. ;*************************************************************************** ;***** Регистровые переменные .def subi1l = r16 .def subi1h = r17
;***** 16-битная immediate-константа .equ subi2 = 0x1234
;***** Код subi16: subi subi1l,low(subi2) ;Вычесть младшие байты sbci subi1h,high(subi2) ;Вычесть старшие с учетом переноса ;Ожидаемый результат 0x3412
;*************************************************************************** ;* "cp16" - сравнение двух 16-битных чисел ;* ;* В этом примере сравниваются 2 регистровые пары (cp1l,cp1h) и ;* (cp2l,cp2h). Если они равны, то устанавливается флаг нуля (zero flag), ;* иначе он будет очищен. ;* ;* Количество слов программы: 2 ;* Количество циклов: 2 ;* Используемые младшие регистры: не используются ;* Используемые старшие регистры: 4 ;*************************************************************************** ;***** Регистровые переменные .def cp1l = r16 .def cp1h = r17 .def cp2l = r18 .def cp2h = r19
;***** Код cp16: cp cp1l,cp2l ;Сравнить младшие байты cpc cp1h,cp2h ;Сравнить старшие байты с учетом переноса ; от предыдущей операции ncp16: ;Ожидаемый результат Z=0 ;*************************************************************************** ;* "cpi16" - сравнение 16-разрядного регистра и 16-разрядной ;* immediate-константы ;* ;* В этом примере сравнивается регистровая пара (cpi1l,cpi1h) со значением ;* cpi2. Если они равны, то установится zero flag, иначе он будет очищен. ;* Это работает благодаря распространению нуля в системе команд AVR. ;* Также установится перенос, если результат будет отрицательным. Это ;* означает, что после сравнения могут использоваться все инструкции ;* ветвления по условию. ;* ;* Количество слов программы: 3 ;* Количество циклов: 3 ;* Используемые младшие регистры: не используются ;* Используемые старшие регистры: 3 ;*************************************************************************** ;***** Регистровые переменные .def cp1l =r16 .def cp1h =r17 .def c_tmp=r18
.equ cp2 = 0x3412 ;immediate-величина для сравнения
;***** Код cpi16: cpi cp1l,low(cp2) ;Сравнение младших байт ldi c_tmp,high(cp2) ; cpc cp1h,c_tmp ;Сравнение старшего байта ;Ожидаемый результат Z=1, C=
;*************************************************************************** ;* "neg16" - инверсия в дополнительном коде 16-разрядного регистра ;* ;* Этот пример делает инверсию регистровой пары (ng1l,ng1h). Результат ;* операции перезапишет содержимое этой регистровой пары. ;* ;* Количество слов программы: 4 ;* Количество циклов: 4 ;* Используемые младшие регистры: не используются ;* Используемые старшие регистры: 2 ;*************************************************************************** ;***** Регистровые переменные .def ng1l = r16 .def ng1h = r17
;***** Код ng16: com ng1l ;Инверсия младшего байта com ng1h ;Инверсия старшего байта subi ng1l,low(-1) ;Прибавление 0x0001, сначала младший, sbci ng1h,high(-1) ; затем старший байт. ;Ожидаемый результат 0xCBEE
;*************************************************************************** ;* Конец примеров. ;*************************************************************************** forever: rjmp forever

[Ссылки]

1. AVR202: 16-bit Arithmetics site:atmel.com.
2. AVR201: использование аппаратного перемножителя AVR.