Опять, без надобности, полез в глухие дебри - вроде и размер прошивки устраивает и отзывчатость, но решил посмотреть что получается при использовании структур.
Задача простая вне прерывания получать менять 8-битную переменную. Вроде как не нужно атомарный доступ. Решил глянуть, что выдаст асемблер.
Код:
typedef struct {
bool test : 1;
bool _unused1 : 1;
bool _unused2 : 1;
bool _unused3 : 1;
bool _unused4 : 1;
bool _unused5 : 1;
bool _unused6 : 1;
bool _unused7 : 1;
} compare_enabled_t;
typedef struct {
...
compare_enabled_t is_enabled;
byte_t x;
} clock_struct_t;
использование
Код:
volatile clock_struct_t clock = { ... , .test = {0}, .is_enabled = {.test = false}};
byte_t clock_x = 0;
void on_lo_press(void) {
printf("on_lo_long\n");
_NOP();
clock.is_enabled.test = false;
_NOP();
clock.x &= ~_BV(0);
_NOP();
clock_x &= ~_BV(0);
_NOP();
clock.x &= 32;
_NOP();
clock_x = 32;
_NOP();
}
Стандартная оптимизация по размеру, О2 оптимизация и О3 оптимизация дают здесь одинаковый ассемблерный код(хотя в общем размер бинарника Os/O2 отличается на 25%):
Код:
/* #APP */
; 30 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
ldi r30,lo8(clock+10)
ldi r31,hi8(clock+10)
ld r24,Z
andi r24,lo8(~(1<<0))
st Z,r24
/* #APP */
; 32 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
ldi r30,lo8(clock)
ldi r31,hi8(clock)
ldd r24,Z+11
andi r24,lo8(-2)
std Z+11,r24
/* #APP */
; 34 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
lds r24,clock_x
andi r24,lo8(-2)
sts clock_x,r24
/* #APP */
; 36 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
ldd r24,Z+11
andi r24,lo8(32)
std Z+11,r24
/* #APP */
; 38 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
ldi r24,lo8(32)
sts clock_x,r24
/* #APP */
В итоге вместо одного вопроса возникло целых два:
1) когда нельзя неатомарно менять переменную? Про аккуратность с некоторыми регистрами знаю(иногда лучше присвоение).
2) зачем такие костыли со структурами? Почему компилятор не может вычислить адрес относительно начала структуры? Выходит даже структуры - это зло. А я хотел в основном цикле много чего творить со сделанной атомарно копией структуры, а затем, если надо, атомарно менять оригинал(или не атомарно, если член 8bit). Что же тогда на ардуино где и классы присутствуют творится...
Добавлено after 36 minutes 6 seconds:P.S. Работа по ссылке уже интересней:
Спойлер
Код:
/* #APP */
; 30 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
ldd r24,Z+10
andi r24,lo8(~(1<<0))
std Z+10,r24
/* #APP */
; 32 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
ldd r24,Z+11
andi r24,lo8(-2)
std Z+11,r24
/* #APP */
; 34 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
lds r24,clock_x
andi r24,lo8(-2)
sts clock_x,r24
/* #APP */
; 36 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
ldd r24,Z+11
andi r24,lo8(32)
std Z+11,r24
/* #APP */
; 38 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
ldi r24,lo8(32)
sts clock_x,r24
/* #APP */
; 40 "modules/buttons.c" 1
nop
; 0 "" 2
/* #NOAPP */
А я много лет считал, что это для программиста отличается...
Добавлено after 4 hours 12 minutes 26 seconds:P.P.S. Похоже я пере-перестраховался, так что испугался или наоборот

Счетчик и члены сравнения меняются из прерывания по таймеру, а читаются в основном цикле - тут нужно атомарное копирование, а все остальное настройки меняются и читаются только в основном цикле, так что пофиг сколько они занимают байт, всё должно быть OK.
Ну, а что касается структур - тут всё странно:
Код:
clock_struct_t _approx_clock;
const clock_struct_t * approx_clock;
while(1) {
_approx_clock = get_current_copy_atomic();
approx_clock = &_approx_clock;
...
}
и
Код:
clock_struct_t approx_clock;
while(1) {
approx_clock = get_current_copy_atomic();
...
}
Дают абсолютно одинаковый ассемблерный код. Причём, оптимальный.