Привет All!
До недавней поры софт для AVR я писал полностью на asm'е, но последний проект решил попробовать на C написать. Взял последний пакет с avrfreaks.net и сел писать. Hо поминутно заглядывал в ассемблерные листинги того, что генерируется. Чем больше я писал, тем хуже генерировался код. Моя задача в принципе не требует большого быстродействия, и памяти в контроллере хватает, но я не могу понять, это у меня руки кривые, или компилятор такой. Вопрос к тем, кто пишет свои проекты под AVR, используя WinAVR GCC. Есть ли какие-либо негласные правила о том, что "лучше писать не так, а так...", какие-нибудь советы? Чтобы от генерируемого кода волосы дыбом не вставали.
Примеры того, что меня убило (в контексте большого проекта):
C: ... unTemp = ((PINC << 8) | PINA) ^ unLastState; ...
Asm:1316: 83 b3 in r24, 0x13 ; 19 1318: 99 27 eor r25, r25 131a: f8 2e mov r15, r24 131c: ee 24 eor r14, r14 131e: 89 b3 in r24, 0x19 ; 25 1320: 99 27 eor r25, r25 1322: e8 2a or r14, r24 1324: f9 2a or r15, r25 1326: 80 91 6f 00 lds r24, 0x006F 132a: 90 91 70 00 lds r25, 0x0070 132e: e8 26 eor r14, r24 1330: f9 26 eor r15, r25 Пишется в 4 команды.
- Asm: ... 846: c9 de rcall .-622 ; 0x5da 848: 08 95 ret 84a: 08 95 ret ... Зачем 2 ret'а?
- ... if(!g_MenuInnerState.ucMenuEditGoodResult) 85a: 80 91 81 00 lds r24, 0x0081 85e: 98 2f mov r25, r24 860: 90 71 andi r25, 0x10 ; 16 862: 84 fd sbrc r24, 4 864: 04 c0 rjmp .+8 ; 0x86e { g_MenuInnerState.unInnerState = 0; 866: 90 93 82 00 sts 0x0082, r25 ... Хитро. Я конечно понимаю, что он при условии в 0x82 записывает 0. Hо сделав это по-русски код уменьшится на 1 команду. (Во второй ветви условия r25 не используется.)
- "Классика жанра". Имея массив структур и switch, обращающийся в разных ветках к разным полям одного и того же элемента массива получаем n-раз повторяющийся код для вычисления адреса n-элемента. Т.е. используя
switch(...) case 1: array[n].field1 = ...; break; case 2: array[n].field2 = ...; break; ...
получаем рок-н-ролл. Приходится подсказывать: ElementType *el = array + n; switch(...) case 1: el->field1 = ...; break; case 2: el->field2 = ...; break; ...
... или я слишком требователен к компилятору?
Чтобы не быть голословным, кусок makefile'а:
MCU = atmega8515 FORMAT = ihex TARGET = main OPT = s SRC = $(TARGET).c compMgr.c display.c ASRC = DisplayAsm.S CFLAGS = -g -O$(OPT) \
-funsigned-char -funsigned-bitfields -fshort-enums \
-Wall -Wstrict-prototypes\
-Wa,-adhlns=$(<:.c=.lst) -DDEBUG \ $(patsubst %,-I%,$(EXTRAINCDIRS)) CFLAGS += -std=gnu99 ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
Пакет: WinAVR-20040404-bin-install.exe
До свидания, All.