vvppvv 0 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба Доброго дня! Возникла необходимость подсчитать паритет битов в байте, четность. Как в последовательном порту. Но там это делается само, а тут надо, чтобы старший 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 паттернов с уже готовой чётностью. Но что-то тоже неэстетично. Может есть какие другие, более лаконичные алгоритмы для чётности в байте? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба 8 minutes ago, vvppvv said: Но что-то тоже неэстетично. Почему же? Быстро по скорости исполнения, правда по занимаемому коду не совсем для AVR. Эх, забыл я уже эту архитектуру. С 2013 года с ним не работаю. А то может быть что-то смог подсказать. На ассемблере приходилось много писать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vvppvv 0 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба 1 minute ago, haker_fox said: Почему же? Быстро по скорости исполнения, правда по занимаемому коду не совсем для AVR. С размером кода проблем нет, камень на 128к или 256к флеш. Просто если таблица, то используется больше регистров. Сначала надо сохранить регистры указателя, после загрузить в них начало таблицы, вычислить смещение (прибавить входной байт), получить ответ, восстановить испорченные регистры... А тут портится только регистр флагов. 8 minutes ago, haker_fox said: Эх, забыл я уже эту архитектуру А я подсел. :)) Аккурат, с 1997 года. Нравится то, что разрабатывалась она "в обратную", от Си к ассемблеру. Многое нравится, а вот паритета нет. В древнем MCS-51 в регистре флагов (PSW) есть бит четности, а тут украли... :(( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба u8 addEven(u8 byte) { u8 retVal = byte; byte ^= byte << 4; byte ^= byte << 2; byte ^= byte << 1; byte = byte & 0x80; return retVal | byte; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vvppvv 0 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба 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 } Спасибо! На Си смотрится короче. Результат получается длиннее и дольше :)) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба Да я сразу не прочитал, что Вам надо в 7й бит добавлять четность, поэтому исправил. Оптимизация включена? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vvppvv 0 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба 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 тактов, примерно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Freibier 3 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба 6 часов назад, vvppvv сказал: Видимо, теоретически достижимый предел (4+2=6 тактов), это использовать табличный метод, причем пожертвовав ОЗУ (из флеша дольше выковыривать) Если флеша не жалко и таблицу разместить так чтобы избежать SUBI и SBCI, то пару тактов можно сэкономить. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
quark 48 8 декабря, 2022 Опубликовано 8 декабря, 2022 · Жалоба 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 9 декабря, 2022 Опубликовано 9 декабря, 2022 · Жалоба Можно сократить таблицу до 16 элементов: сделав SWAP с последующим XOR, обнулив старший ниббл, получаем 4-битный индекс. Получив значение по этому индексу, делаем с исходным байтом данных OR с 0x80 либо 0x0 из этой таблицы - profit. Не уверен, что по тактам будет быстрее, ибо одна подготовка пролога сжирать будет добрую часть тела функции. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vvppvv 0 9 декабря, 2022 Опубликовано 9 декабря, 2022 · Жалоба 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. Какой хитрый алгоритм, надо будет попробовать. )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 9 декабря, 2022 Опубликовано 9 декабря, 2022 · Жалоба Портятся и портятся... Что не так? У Вас их 32 штуки. Если так рассуждать, то идеальная программа - это ее отсутствие)) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vvppvv 0 9 декабря, 2022 Опубликовано 9 декабря, 2022 · Жалоба В общем, погонял я в симуляторе этот алгоритм: #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 и дёрнет. А там уже кака. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
quark 48 9 декабря, 2022 Опубликовано 9 декабря, 2022 · Жалоба 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. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться