Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Последовательность действий - побитовая инверсия и сдвиг
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Dx!
Компилятор WinAVR-20090313

Неужели ((~PIND) >> 4) и (~(PIND >> 4)) должны давать одинаковый результат?

PIND у меня равен 0b1110????

~(PIND >> 4) получается 0b11110001 как и должно быть.

(~PIND) >> 4 получается тоже 0b11110001, хотя мне кажется должно быть 0b00000001

Я ошибаюсь?
IgorKossak
Как определён Ваш PIND?
777777
Цитата(Dx! @ Sep 28 2010, 20:15) *
PIND у меня равен 0b1110????

(~PIND) >> 4 получается тоже 0b11110001, хотя мне кажется должно быть 0b00000001

PIND равен 00001110. После инвертирования получится 11110001. А если учесть, что компилятор в выражениях расширяет операнды до int, то единиц будет еще больше. Ну и сдвиг на 4 с расширением знака даст все единицы, причем в обоих случаях. Как у тебя получилось что-то другое - хз
IgorKossak
Под ???? подразумевались недостающие четыре бита?
rezident
Нормальные компиляторы на такой оператор выдают предупреждение или ремарку. Например, IAR "ругается" таким образом
Цитата
Remark[Pa091]: operator operates on value promoted to int (with possibly unexpected result)

Если уж приспичило писать именно так, а не другим способом инвертировать биты, то наложите байтовую маску после инвертирования, но до сдвига.
Dx!
Цитата(IgorKossak @ Sep 28 2010, 20:20) *
Как определён Ваш PIND?

Он не мой - это регистр ввода с порта.

Цитата(777777 @ Sep 28 2010, 20:53) *
PIND равен 00001110. После инвертирования получится 11110001. А если учесть, что компилятор в выражениях расширяет операнды до int, то единиц будет еще больше. Ну и сдвиг на 4 с расширением знака даст все единицы, причем в обоих случаях. Как у тебя получилось что-то другое - хз

Ничего что у меня сначала инверсия, потом сдвиг? Или раскрывать скобки уже не обязательно?

Код
temp = PIND;
temp = ~temp;
SomeVar = temp >> 4;

Работает адекватно.

Цитата(IgorKossak @ Sep 28 2010, 21:35) *
Под ???? подразумевались недостающие четыре бита?

Любые биты, в данном случае не имеющие значения и ничего не меняющие.

Цитата(rezident @ Sep 28 2010, 21:35) *
Нормальные компиляторы на такой оператор выдают предупреждение или ремарку. Например, IAR "ругается" таким образом

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

И что мне это даст?
Гораздо проще
Код
((~PIND) >> 4) & 0xf;

Но вопрос в другом - мне казалось что сначала должна была быть инверсия, потом сдвиг. Я не прав? Или данная конструкция что-то нарушает и не может быть применена?
rezident
Цитата(Dx! @ Sep 29 2010, 00:10) *
И что мне это даст?
Гораздо проще
Код
((~PIND) >> 4) & 0xf;
У вас то же самое, что я предлагаю, но по-моему более корректно будет
Код
(((~PIND)&0xFF)>>4)

Цитата(Dx! @ Sep 29 2010, 00:10) *
Но вопрос в другом - мне казалось что сначала должна была быть инверсия, потом сдвиг. Я не прав? Или данная конструкция что-то нарушает и не может быть применена?
Все правильно, инверсия, а потом сдвиг. Только перед инверсией беззнаковая переменная типа volatile unsigned char преобразуется в переменную типа signed int и только потом инвертируется и сдвигается сообразно правилам сдвига для знаковой переменной. Если вы хотите работать именно с байтовой переменной, то пишите
Код
((PIND^0xFF) >> 4)
Petka
Цитата(Dx! @ Sep 28 2010, 20:15) *
Компилятор WinAVR-20090313

Неужели ((~PIND) >> 4) и (~(PIND >> 4)) должны давать одинаковый результат?

PIND у меня равен 0b1110????

~(PIND >> 4) получается 0b11110001 как и должно быть.

(~PIND) >> 4 получается тоже 0b11110001, хотя мне кажется должно быть 0b00000001

Я ошибаюсь?

Разбираем по шагам:
PIND = 0b1110**** (PIND это 8 битное число)
результат инвертирования (почему смотри ниже) будет иметь тип int (в avr-gcc тип int это знаковое 16 битное число). поэтому 8 битное беззнаковое число будет расширено до знакового 16 битного
~PIND == ~(0b00000000 1110****) == 0b11111111 0001***
(~PIND) >> 4 с правой стороны этого выражения числовая константа (без явного указания типа), а по стандарту си она должна интерпретироваться как тип int. При операции сдвига меньший операнд по стандарту си должен быть расширен до бОльшего (именно по этой причине компилятор делал инвертирование в 16 битах). т.е. имеем: (~PIND) >> 4 == 0b11111111 0001**** >> 4 == 0b11111111 11110001
итого, если результат приводится к 8ми битному типу то должно получиться 0b11110001.
Если это не так, то надо более точно разбираться.
IgorKossak
Цитата(Petka @ Sep 28 2010, 22:04) *
Если это не так, то надо более точно разбираться.

Вот я и пытаюсь добиться определения PIND в WinAVR ибо "Он не мой - это регистр ввода с порта." это не определение на языке C. Отсюда и плясать надо.
Petka
Цитата(IgorKossak @ Sep 28 2010, 23:15) *
Вот я и пытаюсь добиться определения PIND в WinAVR ибо "Он не мой - это регистр ввода с порта." это не определение на языке C. Отсюда и плясать надо.

Код
#define PIND    _SFR_IO8(0x09)
Dx!
Да, всё так, большое спасибо. Теперь всё встало на свои места, всё понятно. Прошу прощения за причинённые неудобства 8)
Petka
Цитата(Petka @ Sep 28 2010, 23:18) *
Код
#define PIND    _SFR_IO8(0x09)

Если совсем правильно, то
Код
(*(volatile uint8_t *)(mem_addr))

где mem_addr это адрес порта D
таким образом PIND это uint8_t т.е. ровно 8 битное беззнаковое число.
Dx!
Просто мы же в разделе AVR, мне казалось что особо указывать что есть PIND не нужно. Хотя (*(volatile uint8_t *)(mem_addr)) я не нашёл - оно где?
Petka
Цитата(Dx! @ Sep 28 2010, 23:32) *
Просто мы же в разделе AVR, мне казалось что особо указывать что есть PIND не нужно. Хотя (*(volatile uint8_t *)(mem_addr)) я не нашёл - оно где?

Код
include/avr/sfr_defs.h
IgorKossak
Не прошло и полгода!
И хоть после драки кулаками не машут, но на будущее. Не у всех установлен WinAVR, а получить результирующий ответ можно было уже после ответа на второй пост.
Dx!
Неужели в IAR или кейле этот (или любой другой стандартный) регистр имеет другое имя или не восемь бит без знака? 0_0
_Pasha
Люди! Радуйтесь, что сдвиг вправо знакового числа арифметический. В более кривых компиляторах (в частности, МСС18) - этого нету. В любом случае идет заполнение нулями. smile3046.gif
Сергей Борщ
Цитата(_Pasha @ Sep 28 2010, 23:14) *
Люди! Радуйтесь, что сдвиг вправо знакового числа арифметический.
Результат сдвига вправо отрицательного числа по стандарту отдан на откуп компилятору. Почему они так решили - загадка.
Цитата
5 The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 /2^E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.
Petka
Цитата(Сергей Борщ @ Sep 29 2010, 02:31) *
Результат сдвига вправо отрицательного числа по стандарту отдан на откуп компилятору. Почему они так решили - загадка.

могу только дополнить кусочком из книги K&R:
"...Сдвиг вправо числа со знаком в некоторых системах приводит к заполнению этих битов (прим: освободившихся) значением знакового бита ("арифметический сдвиг") , а в других - нулями ("логический сдвиг")..."

P.S. Однако, в случае топикстартера всё равно логический или арифметический сдвиг был применён. результат будет одинаковым.
Dx!
Как заключение - остановился на варианте
somevar = (uint8_t)(~PIND) >> 4;
ибо
Код
----------------------------------------------------
    PORTB = (~PIND) >> 4;
  80:    89 b1           in    r24, 0x09; 9
  82:    90 e0           ldi    r25, 0x00; 0
  84:    80 95           com    r24
  86:    90 95           com    r25
  88:    95 95           asr    r25
  8a:    87 95           ror    r24
  8c:    95 95           asr    r25
  8e:    87 95           ror    r24
  90:    95 95           asr    r25
  92:    87 95           ror    r24
  94:    95 95           asr    r25
  96:    87 95           ror    r24
  98:    85 b9           out    0x05, r24; 5
----------------------------------------------------
    PORTB = ((~PIND) >> 4)&0xf;
  80:    89 b1           in    r24, 0x09; 9
  82:    90 e0           ldi    r25, 0x00; 0
  84:    80 95           com    r24
  86:    90 95           com    r25
  88:    95 95           asr    r25
  8a:    87 95           ror    r24
  8c:    95 95           asr    r25
  8e:    87 95           ror    r24
  90:    95 95           asr    r25
  92:    87 95           ror    r24
  94:    95 95           asr    r25
  96:    87 95           ror    r24
  98:    8f 70           andi    r24, 0x0F; 15
  9a:    85 b9           out    0x05, r24; 5
----------------------------------------------------
    PORTB = ((~PIND)&0xff) >> 4;
  80:    89 b1           in    r24, 0x09; 9
  82:    80 95           com    r24
  84:    90 e0           ldi    r25, 0x00; 0
  86:    95 95           asr    r25
  88:    87 95           ror    r24
  8a:    95 95           asr    r25
  8c:    87 95           ror    r24
  8e:    95 95           asr    r25
  90:    87 95           ror    r24
  92:    95 95           asr    r25
  94:    87 95           ror    r24
  96:    85 b9           out    0x05, r24; 5
----------------------------------------------------
    PORTB = ((uint8_t)(~PIND)) >> 4;
  80:    89 b1           in    r24, 0x09; 9
  82:    80 95           com    r24
  84:    82 95           swap    r24
  86:    8f 70           andi    r24, 0x0F; 15
  88:    85 b9           out    0x05, r24; 5
----------------------------------------------------
    PORTB = (uint8_t)(~PIND) >> 4;
  80:    89 b1           in    r24, 0x09; 9
  82:    80 95           com    r24
  84:    82 95           swap    r24
  86:    8f 70           andi    r24, 0x0F; 15
  88:    85 b9           out    0x05, r24; 5
----------------------------------------------------
    uint8_t tmpD;
    tmpD = PIND;
  80:    89 b1           in    r24, 0x09; 9
    tmpD = ~tmpD;

    PORTB = tmpD >> 4;
  82:    80 95           com    r24
  84:    82 95           swap    r24
  86:    8f 70           andi    r24, 0x0F; 15
  88:    85 b9           out    0x05, r24; 5
----------------------------------------------------

Даже swap а не тупо сдвиг. Компиленно с -O2.
rezident
Цитата(Dx! @ Sep 30 2010, 15:05) *
Как заключение - остановился на варианте
somevar = (uint8_t)(~PIND) >> 4;
Вы еще один вариант не проверили
Код
somevar = (PIND^0xFF) >> 4;

777777
Цитата(rezident @ Sep 30 2010, 14:47) *
Вы еще один вариант не проверили
Код
somevar = (PIND^0xFF) >> 4;

Какая разница? Все равно он приводит операнды к типу int
Dx!
Код
PORTB = (PIND^0xFF) >> 4;
  80:    89 b1           in    r24, 0x09; 9
  82:    80 95           com    r24
  84:    82 95           swap    r24
  86:    8f 70           andi    r24, 0x0F; 15
  88:    85 b9           out    0x05, r24; 5

Оптимизация 8)
rezident
Цитата(777777 @ Sep 30 2010, 16:53) *
Какая разница? Все равно он приводит операнды к типу int
Не обязательно. Зависит от платформы, компилятора и оптимизации. На MSP430, например, преобразуется в команды работы с байтами, а не словами.
777777
Цитата(rezident @ Sep 30 2010, 15:25) *
Не обязательно. Зависит от платформы, компилятора и оптимизации. На MSP430, например, преобразуется в команды работы с байтами, а не словами.

Да, кейл тоже творчески подходит к стандарту и знает где можно от него отойти ради более эффективного кода. А гцц слишком тупо ему следует.
Petka
Цитата(777777 @ Sep 30 2010, 16:00) *
Да, кейл тоже творчески подходит к стандарту и знает где можно от него отойти ради более эффективного кода. А гцц слишком тупо ему следует.

Не бывает тупого следования стандарту.

Бывает либо соответствие стандарту, либо несоответствие стандарту.
Если компилятор не соответствует стандарту, то одинаковый код на разных платформах будет давать разный результат.
rezident
Цитата(Petka @ Sep 30 2010, 18:24) *
Не бывает тупого следования стандарту.

Бывает либо соответствие стандарту, либо несоответствие стандарту.
Если компилятор не соответствует стандарту, то одинаковый код на разных платформах будет давать разный результат.

Вы молоко покупаете в магазине? Откуда вы знаете автоматическая дойка на ферме стоит или доярки вручную каждую корову доят? Вы знаете входной параметр - молоко коровье, значит получено от коровы и видите/потребляете результат - молоко коровье пастеризованное, упакованное в полиэтилен, тетрапак или ПЭТ. А промежуточные этапы получения результата могут быть разными.
777777
Цитата(Petka @ Sep 30 2010, 16:24) *
Не бывает тупого следования стандарту.

Бывает либо соответствие стандарту, либо несоответствие стандарту.
Если компилятор не соответствует стандарту, то одинаковый код на разных платформах будет давать разный результат.

Все верно. Но бывают случаи, когда несоответствие стандарту дает лучшие результаты, чем соответствие. В частности, стандарт требует при выполнении операций приводить операнды к типу int. Может это и хорошо когда размер int соответствует естественному размеру переменной на данной платформе. Но в 8-разрядных процессорах это не так - int не может быть 8-битным, а выполнение операций в 16-разрядных переменных приводит к неэффективности кода.
Другой пример - различные адресные пространства. Стандарт предназначен для машин фон-неймановской архитертуры и ничего не хочет знать о том, что указатель может указывать как на пространство кода, так и на пространство данных. А в кейле можно объявить указатель на нужное нам пространство и просто разыменовывать его *p безо всяких уродских pgm_read_byte и компилятор генерирует нужный код в зависимости от типа указателя.
Petka
Цитата(777777 @ Sep 30 2010, 19:51) *
Но в 8-разрядных процессорах это не так - int не может быть 8-битным, а выполнение операций в 16-разрядных переменных приводит к неэффективности кода.

int может быть 8ми битным, для этого надо указать gcc правильный ключик.
Цитата
Другой пример - различные адресные пространства. Стандарт предназначен для машин фон-неймановской архитертуры и ничего не хочет знать о том, что указатель может указывать как на пространство кода, так и на пространство данных. А в кейле можно объявить указатель на нужное нам пространство и просто разыменовывать его *p безо всяких уродских pgm_read_byte и компилятор генерирует нужный код в зависимости от типа указателя.

Это ещё одна причина использовать архитектуры где флэш и оперативная память адресуются одинаково без ущерба для производительности (ARM, например).
777777
Цитата(Petka @ Sep 30 2010, 20:00) *
int может быть 8ми битным, для этого надо указать gcc правильный ключик.

И что, от этого компиоятор будет генерировать вычисление выражений по-другому? Будет приводить к 8-битному int?
Цитата(Petka @ Sep 30 2010, 20:00) *
Это ещё одна причина использовать архитектуры где флэш и оперативная память адресуются одинаково без ущерба для производительности (ARM, например).

Такое впечатление, что стандарт C для вас бог, а все остальное для него подстраиваться.
Вообще-то первичным является железо, а все остальное делается для него (причем сначала появляются компиляторы, а потом стандарты для них). Поэтому и существуют до сих пор гарвардские процессоры, поскольку они удобнее в качестве микроконтроллеров, поэтому придумывают новые архитектуры - сигнальные процессоры, многопроцессорные системы. И если для кого-то из них не хватает возможностей языка - значит его надо расширять или менять.
Petka
Цитата(777777 @ Sep 30 2010, 20:58) *
И что, от этого компилятор будет генерировать вычисление выражений по-другому? Будет приводить к 8-битному int?

да.
Сергей Борщ
Цитата(777777 @ Sep 30 2010, 18:51) *
Все верно. Но бывают случаи, когда несоответствие стандарту дает лучшие результаты, чем соответствие. В частности, стандарт требует при выполнении операций приводить операнды к типу int. Может это и хорошо когда размер int соответствует естественному размеру переменной на данной платформе. Но в 8-разрядных процессорах это не так - int не может быть 8-битным, а выполнение операций в 16-разрядных переменных приводит к неэффективности кода.
Стандарт не запрещает компилятору выкинуть в процессе оптимизации действия, не влияющие на результат. Что мы и видели на примере выражения PORTB = (PIND^0xFF) >> 4; То, что он не пришел к такому же коду на других выражениях - говорит лишь о несовершенстве оптимизатора, но никак не о кривости стандарта. Если компиляторы соответствуют стандарту - результат работы скомпилированного на любом из них кода будет одинаков (оптимальность кода стандарт не оговаривает). А вот если компиляторы нарушают стандарт - счастливой отладки и забудьте о переносимости.
777777
Цитата(Сергей Борщ @ Oct 1 2010, 00:19) *
(оптимальность кода стандарт не оговаривает).

Вот и плохо! Можкт для обычных программ оптимальность и не важна, но для микроконтроллеров это самый главный параметр.
Цитата(Сергей Борщ @ Oct 1 2010, 00:19) *
А вот если компиляторы нарушают стандарт - счастливой отладки

Я некоторое время программировал на кейле для 51-го - очень приятный компилятор несмотря на несоответствие стандарту. Просто к нарушению стандарта нужно подходить с умом.
Цитата(Сергей Борщ @ Oct 1 2010, 00:19) *
и забудьте о переносимости.

Какая переносимость, о чем вы?! Все носятся с этим флагом, хотя прекрасно понимают, что никакой переносимости сейчас нет и она никому не нужна. Даже перенести с WinAVR на IAR вы не сможете - библиотеки, специфичные для контроллера, никто не стандартизировать не собирается. Да и куча мелких фич типа обращения к программной памяти или к отдельным битам порта у всех делается по-разному. Или может вы собираетесь в некотором устройстве поменять AVR на ARM и перенести туда свою программу? Это еще смешнее. Тут вообще о переносимости нет смысла говорить, это будет совершенно другая программа.
Давайте уж честно скажем себе: единственное, для чего нужен язык высокого уровня - для облегчения программирования. Чтобы при программировании думать об алгоритме, а не о регистрах, методах адресации и их ограничениях. А раз так, то он должет быть максимально приспособлен под платформу, а не под абстрактный стандарт.
xelax
Цитата(777777 @ Oct 1 2010, 09:01) *
Какая переносимость, о чем вы?! Все носятся с этим флагом, хотя прекрасно понимают, что никакой переносимости сейчас нет и она никому не нужна. Даже перенести с WinAVR на IAR вы не сможете - библиотеки, специфичные для контроллера, никто не стандартизировать не собирается. Да и куча мелких фич типа обращения к программной памяти или к отдельным битам порта у всех делается по-разному. Или может вы собираетесь в некотором устройстве поменять AVR на ARM и перенести туда свою программу? Это еще смешнее. Тут вообще о переносимости нет смысла говорить, это будет совершенно другая программа.
Давайте уж честно скажем себе: единственное, для чего нужен язык высокого уровня - для облегчения программирования. Чтобы при программировании думать об алгоритме, а не о регистрах, методах адресации и их ограничениях. А раз так, то он должет быть максимально приспособлен под платформу, а не под абстрактный стандарт.


Полнейшая чушь.

Переносимость не то что ВАЖНА, а без неё в принципе никак нельзя. Слишком узко мыслите, в рамках своих локальных задач. Посмотрите на успешные проекты различных кроссплатформенных операционок, различных сетевых стеков, всевозможных библиотек и т.д.. Без стандарта реализация подобных проектов была бы немыслима. Если следовать стандарту С и писать код на чистом как слеза С, то 99% вероятностью, код скомпилируется и заработает на любой платформе (1% на hardware features). Например мы сейчас имеем код который работает на 5 различных архитектурах (AVR, AVR32, ARM7, Cortex, x086) и собирается без проблем двумя компиляторами IAR и GCC.
А ещё забыл, на писишке ещё в Visual Stidio всё собирается, но это так баловство было, для теорет. исследований.
_Pasha
Цитата(777777 @ Oct 1 2010, 08:01) *
Просто к нарушению стандарта нужно подходить с умом.

Не нужно подходить к нарушению стандарта и даже думать об этом. Тогда получаем то. что надо: на любой платформе Ц есть Ц, неподдерживаемые прагмы игнорируются.
Цитата
Какая переносимость, о чем вы?! Все носятся с этим флагом, хотя прекрасно понимают, что никакой переносимости сейчас нет и она никому не нужна.

Пример. Пусть у Вас есть проектик, такой себе никакой - готовый девайс №1 на одной платформе, для которого надо только софта, и контроллер №2 на другой платформе, который общается с №1 по уарту. Протокольная часть - общая, во избежание ошибок и чтобы побыстрее - пишется один раз. Если не будет переносимости, будет ПЦ в отладке и как минимум двойные трудозатраты.
ЗЫ уже давно хочется например, чтобы исчезли (вернее, не появлялись) квалификаторы для eeprom flash - чтобы описания шли типа того
Код
const char str1[];// во флеше - это какбы понятно
volatile const char str2[]; // в еепроме - если он имеется, иначе опять же во флеше
IgorKossak
Всё это очень интересно, но обсуждалось уже многократно.
Тема перерастает в религиозный спор и уже далеко ушла от первоначального вопроса, который, судя по всему благополучно разрешился.
Тему закрываю.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.