Перейти к содержанию
    

Подсчитать четность битов в байте для AVR

Доброго дня!

Возникла необходимость подсчитать паритет битов в байте, четность. Как в последовательном порту. Но там это делается само, а тут надо, чтобы старший 7-ой бит дополнял весть байт до четности. Т.е.  0х21 -> 0x21, 0x20 -> 0xA0  (в битах: 0010.0001 -> 0010.0001, а 0010.0000 -> 1010.0000).

Пока реализовал ассемблерную вставку как инлайн-функцию, где побитно перебираю все 7 бит в байте. Компилятор IAR AVR.

#pragma inline = forced
char cSetParityBit (char cin)
  {                               // в R16 - исходный байт
  asm (" andi R16, 0x7F \n"       // сбросить 7-ой бит чётности в 0
       " sbrc R16,6     \n"
       " subi R16,128   \n"
       " sbrc R16,5     \n"
       " subi R16,128   \n"
       " sbrc R16,4     \n"
       " subi R16,128   \n"
       " sbrc R16,3     \n"
       " subi R16,128   \n"
       " sbrc R16,2     \n"
       " subi R16,128   \n"
       " sbrc R16,1     \n"
       " subi R16,128   \n"
       " sbrc R16,0     \n"
       " subi R16,128   \n");
  return cin;                     // возврат R16 с результатом
  }

Всё работает, но на вид реализация выглядит слишком "в лоб", как-то неспортивно. :)) Можно, конечно, ещё организовать таблицу во флеш, забить 127 паттернов с уже готовой чётностью. Но что-то тоже неэстетично. Может есть какие другие, более лаконичные алгоритмы для чётности в байте?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 minutes ago, vvppvv said:

Но что-то тоже неэстетично. 

Почему же? Быстро по скорости исполнения, правда по занимаемому коду не совсем для AVR.

Эх, забыл я уже эту архитектуру. С 2013 года с ним не работаю. А то может быть что-то смог подсказать. На ассемблере приходилось много писать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 minute ago, haker_fox said:

Почему же? Быстро по скорости исполнения, правда по занимаемому коду не совсем для AVR.

С размером кода проблем нет, камень на 128к или 256к флеш. Просто если таблица, то используется больше регистров. Сначала надо сохранить регистры указателя, после загрузить в них начало таблицы, вычислить смещение (прибавить входной байт), получить ответ, восстановить испорченные регистры... А тут портится только регистр флагов.

 

8 minutes ago, haker_fox said:

Эх, забыл я уже эту архитектуру

А я подсел. :)) Аккурат, с 1997 года. Нравится то, что разрабатывалась она "в обратную", от Си к ассемблеру. Многое нравится, а вот паритета нет. В древнем MCS-51 в регистре флагов (PSW) есть бит четности, а тут украли... :((

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

u8 addEven(u8 byte) {
  u8 retVal = byte;
  
  byte ^= byte << 4;
  byte ^= byte << 2;
  byte ^= byte << 1;
  byte  = byte & 0x80;
  
  return retVal | byte;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

    334          char isEven(char inbyte)
   \                     isEven:
    335            {
    336            inbyte ^= inbyte << 4;
   \   00000000   2F10               MOV     R17, R16
   \   00000002   9512               SWAP    R17
   \   00000004   7F10               ANDI    R17, 0xF0
   \   00000006   2701               EOR     R16, R17
    337            inbyte ^= inbyte << 2;
   \   00000008   2F10               MOV     R17, R16
   \   0000000A   0F11               LSL     R17
   \   0000000C   0F11               LSL     R17
   \   0000000E   2701               EOR     R16, R17
    338            inbyte ^= inbyte << 1;
    339            
    340            return inbyte >> 7 ^ 1;
   \   00000010   FB07               BST     R16, 7
   \   00000012   E010               LDI     R17, 0
   \   00000014   F910               BLD     R17, 0
   \   00000016   FB06               BST     R16, 6
   \   00000018   E020               LDI     R18, 0
   \   0000001A   F920               BLD     R18, 0
   \   0000001C   2712               EOR     R17, R18
   \   0000001E   E001               LDI     R16, 1
   \   00000020   2701               EOR     R16, R17
   \   00000022   9508               RET
    341            }

Спасибо! На Си смотрится короче. Результат получается длиннее и дольше :))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да я сразу не прочитал, что Вам надо в 7й бит добавлять четность, поэтому исправил.

Оптимизация включена?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 minute ago, Arlleex said:

Оптимизация включена?

Да, "High" "Speed". Я вот как раз и думал, что наверняка есть какой-то алгоритм со свапом в байте... Но в результате, то на то и получается.

Видимо, теоретически достижимый предел (4+2=6 тактов), это использовать табличный метод, причем пожертвовав ОЗУ (из флеша дольше выковыривать)

    355            PORTB = cCrc8Table [PINA];
   \   00000000   B100               IN      R16, 0x00
   \   00000002   E0F0               		LDI     R31, 0
   \   00000004   2FE0               		MOV     R30, R16
   \   00000006   ....               		SUBI    R30, LOW((-(cCrc8Table) & 0xFFFF))
   \   00000008   ....               		SBCI    R31, (-(cCrc8Table) & 0xFFFF) >> 8
   \   0000000A   8100               		LD      R16, Z
   \   0000000C   B905               OUT     0x05, R16

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

mov  r17, r16
swap r17
eor  r17, r16
mov  r18, r17
lsl  r17
lsl  r17
eor  r17, r18
mov  r18, r17
lsl  r17
eor  r17, r18
andi r17, 0x80
or   r16, r17


В R16 на входе и на выходе из функции - аргумент и результат.

12 тактов, примерно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 часов назад, vvppvv сказал:

Видимо, теоретически достижимый предел (4+2=6 тактов), это использовать табличный метод, причем пожертвовав ОЗУ (из флеша дольше выковыривать)

Если флеша не жалко и таблицу разместить так чтобы избежать SUBI и SBCI, то пару тактов можно сэкономить. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 часов назад, Arlleex сказал:
mov  r17, r16
swap r17
eor  r17, r16
mov  r18, r17
lsl  r17
lsl  r17
eor  r17, r18
mov  r18, r17
lsl  r17
eor  r17, r18
andi r17, 0x80
or   r16, r17


В R16 на входе и на выходе из функции - аргумент и результат.

12 тактов, примерно.

 

На этом ассемблере не приходилось писать.
Но, попробую...

ANDI R16,0x7F
MOV  R17,R16
SWAP R17
EOR  R17,R16
ANDI R17,0xC3
SUBI R17,0xBF
ORI  R17,0x7C
SUBI R17,0xFE
ANDI R17,0x80
OR   R16,R17

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно сократить таблицу до 16 элементов: сделав SWAP с последующим XOR, обнулив старший ниббл, получаем 4-битный индекс.
Получив значение по этому индексу, делаем с исходным байтом данных OR с 0x80 либо 0x0 из этой таблицы - profit.

Не уверен, что по тактам будет быстрее, ибо одна подготовка пролога сжирать будет добрую часть тела функции.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

16 hours ago, Freibier said:

Если флеша не жалко и таблицу разместить так чтобы избежать SUBI и SBCI, то пару тактов можно сэкономить. 

Можно. 8-ми битный индекс просто помещать в младший регистр указателя. Но как сказать компилятору, что регистр Z портится в моей функции? Он же ж, гат, за всем следит и всё оптимизирует. ))) Можно выбрать регистровую пару, скажем, R10:R9, и туда сохранять ZH:ZL, после восстанавливать. Тогда выигрыш потеряется.

 

23 hours ago, Arlleex said:

В R16 на входе и на выходе из функции - аргумент и результат.

12 тактов, примерно.

Хорошо, но портятся 2 регистра, R17 и R18.

 

7 hours ago, quark said:

На этом ассемблере не приходилось писать.
Но, попробую...

 

Хм... Очень лаконично. Спасибо! Портится всего один регистр R17. Какой хитрый алгоритм, надо будет попробовать. ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Портятся и портятся... Что не так? У Вас их 32 штуки. Если так рассуждать, то идеальная программа - это ее отсутствие))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем, погонял я в симуляторе этот алгоритм:

#pragma inline = forced
unsigned int cSetParityBitNew (char cin)
  {                           // в R16 - исходный байт
  asm (" ANDI R16,0x7F \n"    // сбросить 7-ой бит чётности в 0
       " MOV  R17,R16  \n"
       " SWAP R17      \n"
       " EOR  R17,R16  \n"
       " ANDI R17,0xC3 \n"
       " SUBI R17,0xBF \n"
       " ORI  R17,0x7C \n"
       " SUBI R17,0xFE \n"
       " ANDI R17,0x80 \n"
       " OR   R16,R17  \n");
  return (unsigned int) cin;  // возврат R16 с результатом (R17...?)
  }

Работает. 10 тактов, против изначально моих 15-ти. Логика мне непонятна, но считает этот Шайтан-алгоритм всё верно. Спасибо за подсказку! :-)))

Теперь вопрос, как сказать компилятору, что в моей функции портится регистр R17? Я прилепил костыль, на сколько хватило моих познаний и фантазии, попрошу сильно не смеяться. Обозначил в прототипе возврат 16-ти битного целого (а это значит R17:R16). Надеюсь, так IAR AVR поймёт, что значение R17 поменялось. 

58 minutes ago, Arlleex said:

Портятся и портятся... Что не так? У Вас их 32 штуки. Если так рассуждать, то идеальная программа - это ее отсутствие))

Э не-е-ет, так не пойдёт. Иначе придётся распрощаться с оптимизацией IAR! Много лет назад я эти грабли уже проходил, "Странно, почему, когда включаешь оптимизацию, то программа перестаёт нормально работать?" Компилятор пасёт состояния всех регистров и переменных. Если он когда-то в R17 положил ноль, и больше его не трогал, то когда в следующий раз ему понадобится ноль в каком-нибудь порту, то он его с R17 и дёрнет. А там уже кака. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, vvppvv сказал:

Логика мне непонятна, но считает этот Шайтан-алгоритм всё верно.

После инструкции EOR R17,R16 имеем результат побитного сложения полубайтов в двух экземплярах - в старшем и младшем полубайтах R17 один и тот же.
Оставляем в старшем полубайте R17 два старших бита результата, в младшем - два младших, обнуляя остальные биты инструкцией ANDI R17,0xC3.
Чтобы сложить два бита двухбитного числа, достаточно к нему арифметически прибавить единицу. Тогда в старшем бите получится результат побитного сложения. Делаем это одновременно для обеих пар старших и младших битов инструкцией SUBI R17,0xBF (на самом деле это ADDI R17,0x41, но такой инструкции нет). Результаты побитного сложения имеем в 7-м и в 1-м битах R17. Заполняем единицами биты 6,5,4,3,2 инструкцией ORI R17,0x7C. Суммируем побитно 7-й и 1-й биты инструкцией SUBI R17,0xFE (на самом деле это ADDI R17,0x02). Имеем конечный результат в 7-м бите R17.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...