Выделено из темы про ассемблер AVR. Начало тут. Цитаты оттуда:
shads писал(а):
Сообразил тут декодер радиоканала на тиньке 13-й, в двух вариантах, на С и на асме, так вот мож кому интересно будет узнать, разница в весе кода - больше чем в 2 раза, на С - 870 байт, а на асме тоже самое - 420 байт..... Помоему асма остается актуальной для таких мелкашек как tiny13 http://asis-kbr.ru/forum/viewtopic.php?f=9&t=122
avreal писал(а):
Я даже больше скажу -- если написать на асме программу размером байт десять, то С-шный вариант проиграет раз в пять-восемь, так как С (по крайней мере WinAVR) и всю таблицу прерываний заполняет переходом на ловушку необработанных прерываний, и прочие «общеподготовительные» вещи могут место занять. Т.е. у С-шной программы даже без желания автора обычно несколько другой функционал. ... Увидел всё по линку, гляну по свободе. На носу куча выходных
Ну вот и глянул.
Табличка результатов на разных версиях avr-gcc. orig -- код по ссылке patch -- то, что я с ним сделал. Ключи компиляции везде одинаковые, взяты из стандартного проекта. Для 4.7.1 ещё добавлялся ключ -fno-ivopts, чтобы он менше чудил с оптимизацией induction variables в циклах. Они там что-то в «корневом» gcc похимичили, для процессоров с развитыми адресациями точно стало лучше, для AVR компилятор иногда слишком «мудрит» и сам себя перехитрить умудряется.
«MobileChessBoard» -- это такие сборки свежего avr-gcc для Windows.
Т.е. проигрыш у ассемблера уже не два, а полтора раза (675 / 450)
Но сейчас я не о том, насколько проигрывает C ассемблеру на проектах такого размера, а о том, что можно сделать с нормальным в принципе С-шным кодом для уменьшения размера прошивки байт так с 870-ти до байт так 670-ти (больше, чем на четверть).
Причём смотрел «обще-микроконтроллерно-С-шные» вещи, там осталась одна большая беда именно avr-gcc, IAR должен бы ещё короче сделать на несколько десятков байт, но мне лень проверять.
Пришиваю новый исходник, надеюсь, оно будет работать Немного позже (сейчас гости пришли) напишу немного по принципы — «общие» и что там осталось специфического для avr-gcc
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Гы..... С трудом узнал свою программу..... Даже не верилось что она заработает, НО..... она работает..... Спасибо за уделенное внимание. Хотя для понимания она стала сложнее (по крайней мере для меня, т.к. я тока начал писать на С), но надо будет обязательно разобраться, чего это вы там такого понаписали
однако в конце-концов всеравно все сводится к машинным кодам корректность программы на С во многом зависит от качества компилятора (и знаний правильных приемов работы с оным), а в ассемблере львиная доля - знание аппаратной структуры и схемотехники устройств в идеале - знание того и другого (да еще минимум на всех базовых семействах) из С более перспективным выглядит микроси - единый подход ко всем 4-м "ходовым" семействам.... но... полные версии уж "оччень платные"
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
GCC-шные штуки, эквивалентные IAR-овским #pragma inline и т.п. Команды компилятору принудительно «инлайнить» (встраивать по месту как макросы) и ни в коем случае не инлайнить определённые функции. Будут использованы ниже.
Код:
//прототипы функций static void Receive(void); static void Int100Hz(void);
Каждая функция используются только в данном файле, поэтому её стоит объявить как static. При этом компилятор знает, что снаружи она не может быть вызвана и поступает с ней по собственному усмотрению. Если функция вызывается из одного места, то он практически гарантированно её встроит по месту, а не будет вызывать. Если функция вызывается из нескольких мест, но она короткая — тоже может встроить. При встраивании единожды вызываемой функции экономи размера кода небольшая, но есть. Для небольшой функции без static может быть вообще плохо — он её и по месту встроит (маленькая, экономия на саом вызове и на стандартной передаче параметров может быть больше размера функции), но и отдельно тело тоже оставит. Так как при компиляции из .c в .o компилятор не знает, что файл один, оставляет функцию на случай, если ее кто-то снаружи вызовет. Т.е. функция займёт место во флеше дважды. Кроме того, static-объекты не захламляют глобальное пространство имён, не занимают место (и время) в соответствующих таблицах при работе компилятора и линкера. Объявлять как static все функции и переменные, которые не используются за пределами файла — «правило хорошего тона».
Код:
enum { DW_B0 = 0, DW_B1 = 1, DW_B2 = 2, DW_B3 = 3 }; // LITTLE ENDIAN CPU // enum { DW_B0 = 3, DW_B1 = 2, DW_B2 = 1, DW_B3 = 0 }; // BIG ENDIAN CPU union { uint32_t dw; uint8_t b[4]; } ReceiveData; //4 байта для чтения кода с радиоканала
Я уже об этом писал, для приёма битового потока удобно объявить объединение, т.е. наложение в памяти двух переменных: 32-битного целого и 4-бійтового массива. Слдвиг делается в 32-битовое поле dw, а разбор потом — из байтового массива. Перед ним «для порядку» объявлено перечислимый тип, который описывает порядок байтов в 32-битном слове. Надо было бы ещё и #ifdef LITTLE_ENDIAN или там #if BYTE_ORDER == LITTLE_ENDIAN, но тут я так, для демонстрации того, что такую вещь можно при желании сделать переносимой и она скомпилируется для любого процессора. Это место сэкономило довольно много кода — при записи в EEPROM и при сравнивании с таблицей кодов пропали >> 8, >> 16, которые хоть и делаются пересылкой байтов, но все же… Было:
В ассемблерном варианте для задержки везде вызівается функция с параметром времени в 10мс квантах. В С-шном везде было _delay_ms(). Но это макрос, который по месту вставляет все те вложенные циклы, при многократном применении флеш разлетается ужасающим образом. Это для малых, микросекундных задержек там три команды выходит, не так и жалко. Но вот тут понадобилось NOINLINE, так как функция короткая и некоторые из оттестированных версий компиляторов норовили вставить тело по месту. У меня NOINLINE и компания вместе с уже упоминавшимися INIT() сидят в gcc_macros.h и я для таких небольших функций со специально вынесенным отдельно кодом ставлю практически рефлекторно.
Пойду попью чаю…
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Последний раз редактировалось avreal Пт авг 24, 2012 20:26:42, всего редактировалось 1 раз.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
Я, кстати, достаточно подробно разбирал, чего генерирует AVR-GCC в нагрузку к основному коду.
О, значит про это можно не писать Только добавлю (недавно писал на этом форуме), что обработчик незадействованого прерывания можно определить в своей программе, он заменит тот по умолчанию переход на старт.
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Ооо.... Спасибо avreal, многое постараюсь применить, я тут как раз одну страшную весч затеял, думаю в итоге под 8кб коду будет (mega8), так что надо будет экономить. Пока только начало, нашкрябал 1,5кб..... Спойлер
#define LPortOut PORTC /*порт записи линии 1,2*/ #define LPortIn PINC /*порт чтения линии 1,2*/ #define LPin1 (1<<0) /*пин линии 1*/ #define LPin2 (1<<1) /*пин линии 2*/
#define LinePortOut PORTD /*порт записи в линии*/ #define LinePinOut (1<<3) /*пин записи в линию*/ #define LinePortIn PIND /*порт чтения с линии*/ #define LinePinIn (1<<2) /*пин чтения с линии*/
//ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ volatile char volatilei; /*вставка для борьбы с оптимизацией*/
//#define EEDataLen 60 /*длина буфера данных в EEPROM*/ //unsigned char EEData[EEDataLen] EEMEM; //массив данных в EEPROM //unsigned char EEPnt EEMEM; //указатель на байт массива в EEPROM (0-59)
//####################################################################################################################### //####################################################################################################################### //Главная функция int main (void) { DDRB = 0xff; //линии порта на вывод (данные дисплея) PORTB = 0; //подтяжки нет
DDRC = (BuzPin|CtrlPin); //линия порта на вывод для buzzer PORTC = (BtnPin & (~DisplayType)); //подтяжка для кнопки с учетом типа дисплея
DDRD = (DispStrb0|DispStrb1|DispStrb2|DispStrb3|LinePinOut); //линий порта на вывод PORTD = 0; //подтяжки нет
TIMSK = 0x80; //режим работы - прерывание по совпадению TCCR2 = 0x0e; //режим таймера T2 = CTC (0x08 сброс при совпадении)(0x06 предделитель на 256) OCR2 = 77; //регистр сравнения (сброс таймера счетч по совп)
sei (); //разрешаем глобально прерывания
unsigned int integer = 500; DispMessageShow (TxtHallo);
//---------- //функция вывода на дисплей числа 0-9999
void DispIntShow (unsigned int integer) { unsigned char position = 0; //позиция печати на дисплее
if (integer < 1000) DispData [position ++] = 0; //очистить тысячи if (integer < 100) DispData [position ++] = 0; //очистить сотни if (integer < 10) DispData [position ++] = 0; //очистить десятки
switch (position) { case 0: DispSimbShow (div (&integer, 1000), position ++); case 1: DispSimbShow (div (&integer, 100), position ++); case 2: DispSimbShow (div (&integer, 10), position ++); case 3: DispSimbShow (integer, position); } }
unsigned char div (unsigned int *pDivident, unsigned int divisor) { unsigned char result =0; while (*pDivident >= divisor) { *pDivident -= divisor; result ++; } return result; }
//---------- //функция редактирования числа //аргумент 1 - указатель на редактируемые данные //аргумент 2 - максимально возможно нижнее значение //аргумент 3 - максимально возможное верхнее значение
void DispIntEdit (unsigned int* pInteger, unsigned int IntMin, unsigned int IntMax) { unsigned int IntegerTemp = *pInteger; //редактируем пока промежуточную IntegerTemp
while (1) { Flags1 |= b1DispFlash; //включить режим дисплея - FLASH DispIntShow (IntegerTemp); //вывести значение на дисплей unsigned char BtnValue = MenuBtnPressWait (0, 0);//ожидаем нажатия кнопки Flags1 &= ~b1DispFlash; //выключить режим дисплея - FLASH
if (BtnValue == LongRight){ *pInteger = IntegerTemp; //выход с сохранением данных DispMessageShow (TxtDone); _delay_ms(1500); return; }
if ((!BtnValue)||(BtnValue == LongLeft)){ DispMessageShow (TxtRet); //выход кнопкой без сохранения и по таймауту _delay_ms(1500); return; }
if (BtnValue == ShortUp){ if (IntegerTemp < IntMax) //увеличить значение IntegerTemp ++; }
if (BtnValue == ShortDown){ if (IntegerTemp > IntMin) //уменьшить значение IntegerTemp --; }
if (BtnValue == LongUp){ if (IntegerTemp < IntMax) //увеличить значение IntegerTemp ++; }
if (BtnValue == LongDown){ if (IntegerTemp > IntMin) //уменьшить значение IntegerTemp --; } } }
//---------- //функция индикации строк меню, и ожидание нажатия кнопки //аргумент 1 - строка меню №1 (если 0 - не выводится) //аргумент 2 - строка меню №2 (если 0 - не выводится) //значение на выходе - байт маска нажатой кнопки (если 0 - выход по таймауту)
unsigned char MenuBtnPressWait (unsigned char *pMenu1, unsigned char *pMenu2) { #define TimeOut 10 /*если указанное кол-во секунд, кнопка не нажимается, то выход из ф-ции*/ unsigned char Button; //регистр маски нажатой кнопки
for (unsigned char i =0; i<(TimeOut/2); i++) { if (pMenu1) DispMessageShow (pMenu1); //выводим строку 1 Button = BtnPressWait1Sec(); if (Button) return Button; //кнопка нажата, возвращаем маску кнопок
if (pMenu2) DispMessageShow (pMenu2); //выводим строку 2 Button = BtnPressWait1Sec(); if (Button) return Button; //кнопка нажата, возвращаем маску кнопок } return 0; //выход по таймауту }
//---------- //функция ожидания нажатия кнопки в течение 1 сек //значение на выходе - маска кнопок *pButtonFlags (если timeout, то на выходе 0) unsigned char BtnPressWait1Sec (void) { for (unsigned char i=0; i<10; i++) //крутимся 10 раз по 100мс т.е. 1 сек { volatilei ++; if (*pButtonFlags) { unsigned char temp = *pButtonFlags; //кнопка нажата *pButtonFlags = 0; return temp; //возвращаем маску нажатой кнопки } _delay_ms(100); } return 0; //секунда закончилась, возвращаем значение 0 }
//---------- //функция динамического отображения данных на 4-х разрядном дисплее. // void DispFresh (void) { static unsigned char strobe; //переменная активного строба дисплея и опроса кнопок (0-3)
ButtonGet (strobe); //ф-ция обработки опроса кнопок (аргумент - номер включенного строба индикатора 0-3)
if (++ strobe >= 4){ //инкремент строба знакоместа strobe =0;
//здесь обслуживание переменных с частотой 100Гц if (++ DispFlash == 100) //инкремент счетчика FLASH (за секунду счет 0-99) DispFlash = 0; }
if (DisplayType) { //обработка дисплея с ОА DispStrobePort &= ~(DispStrb0 |DispStrb1 |DispStrb2 |DispStrb3);//погасить все разряды DispDataPort = ~(DispData [strobe]); //вывод данныx
if (Flags1 & b1DispFlash){ //проверка включен ли режим FLASH if (DispFlash >= 50) DispDataPort = DisplayType; //при счетчике 0-49 индикация данных, 50-99 погасить дисплей }
switch (strobe){ //включение обновленного знакоместа case 0: DispStrobePort |= DispStrb0; break; case 1: DispStrobePort |= DispStrb1; break; case 2: DispStrobePort |= DispStrb2; break; case 3: DispStrobePort |= DispStrb3; } } else { //обработка дисплея с ОК DispStrobePort |= (DispStrb0 |DispStrb1 |DispStrb2 |DispStrb3);//погасить все разряды DispDataPort = DispData [strobe]; //вывод данных
if (Flags1 & b1DispFlash){ //проверка включен ли режим FLASH if (DispFlash >= 50) DispDataPort = DisplayType; //при счетчике 0-49 индикация данных, 50-99 погасить дисплей }
switch (strobe){ //включение обновленного знакоместа case 0: DispStrobePort &= ~DispStrb0; break; case 1: DispStrobePort &= ~DispStrb1; break; case 2: DispStrobePort &= ~DispStrb2; break; case 3: DispStrobePort &= ~DispStrb3; } } }
//---------- //ф-ция обработки нажатий клавиш (вызывается с частотой 400 Гц) //аргумент - номер включенного строба индикатора 0-3 (0 - left, 1 - down, 2 - right, 3 - up)
void ButtonGet (unsigned char strobe) { static unsigned char BtnLockBit; //ащелка (защита от дребезга) static unsigned char BtnLockCoun; //счетчик защелки (защита от дребезга) static unsigned char BtnLongCoun; //счетчик длинного нажатия static unsigned char BtnStack; //здесь накапливаем за время опроса 4-х стробов, маску нажатых кнопок static unsigned char BtnTemp; //бит последней нажатой кнопки #define BtnLock 30 /*время обработки дребезга в милисекундах (10-100)*/ #define BtnLongPress 2000 /*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
if (!((BtnPort ^ DisplayType) & BtnPin)) //грубый опрос кнопок во время обработки стробов, с учетом типа индикатора (ОК, ОА) BtnStack = (1 << strobe);
if (strobe <3) return; //выход первые 3 строба (обработка данных только после последнего 4-го строба)
if (BtnStack) { //клавиша нажата BtnTemp = BtnStack; BtnStack = 0; if (BtnLockCoun < (BtnLock/10)){ BtnLockCoun++; return; } BtnLockBit=1; //нажатие зафиксировано if (BtnLongCoun >= (BtnLongPress/10)) return; BtnLongCoun ++; if (BtnLongCoun >= (BtnLongPress/10)) *pButtonFlags |= (BtnTemp << 4); //установка бита длинного нажатия (старшие 4 бита флагов ButtonByte) } else { //клавиша отжата if (BtnLockCoun != 0){ BtnLockCoun --; return; } if (! (BtnLockBit)) //отжатие зафиксировано return; BtnLockBit =0; if (BtnLongCoun < (BtnLongPress/10)){ *pButtonFlags |= (BtnTemp); //установка бита короткого нажатия (младшие 4 бита флагов ButtonByte) DispFlash = 0; //сбросить на начало цикл FLASH дисплея } BtnLongCoun = 0; } }
Библиотечные функции работы с EEPROM принимают «как бы указатель» на переменную в EEPROM. Оно выглядит красиво, но EEMEM только атрибут размещения и всё равно тот указатель как указатель не работает (в gcc 4.7 добавлены пространства памяти и в avr-gcc они использованы, но это другая история). Соответственно в функции eeprom_* нужно передавать 16-битный аргумент и для вычисления адресов используется 16-разрядная арифметика. Но ведь в ATtiny13 всего 64 байта EEPROM! Не ленимся и пишем свои функции, принимающие 8-битный номер ячейки. Тоже NOINLINE, так как иначе некоторые версии компилятора их встраивают (особенно Read, она совсем короткая).
Теперь иллюстрация к моим словам «т.е. у С-шной программы даже без желания автора обычно несколько другой функционал.» Тут с некоторым желанием, но всё же. Исходно:
Всё вроде бы нормально. Ассемблерная программа зануляла не глядя всю EEPROM-ину, а тут одна из переменных зануляется отдельно. Лишний вызов (а в исходном варианте ещё и с передачей 16-разрядного адреса). И сохранить «высокоуровневость» с занулением только необходимой части, причём не обязательно начиная с нулевого адреса, и сэкономить код можно объединением массива и индекса в структуру (имена такие с целью упрощения редактирования текста). Зануляем всю структуру одним махом:
Код:
#define EEDataLen 60 /*длина буфера данных в EEPROM */ struct { unsigned char Data[EEDataLen]; //массив данных в EEPROM unsigned char Pnt; //указатель на байт массива в EEPROM (0-59) } EE EEMEM;
uint8_t from = (unsigned)&EE; uint8_t to = (unsigned)&EE + sizeof(EE); do { EEWrite(from, 0); ++from; } while(from < to); //цикл стирания данных EEPROM
//сдвиг буфера и запись в младший разряд принятого бита unsigned long ultemp = ReceiveData.dw << 1; if (BitLoPartCoun < BitHiPartCoun) ultemp |= 1; ReceiveData.dw = ultemp;
об этом я тоже уже писал — компилятору тут желательна подсказка в виде временной переменной и он перестаёт делать лишнее сохранение после сдвига.
Это я сразу закомментировал, еще до создания union для приёмного буфера. Старший байт в программе нигде не анализировался, ничего страшного, если там поболтается мусор.
Ну вот, собственно, и всё… Не так уж и много изменил. Сделать было быстрее, чем описать
Что осталось: 1. Обще-С-шная беда, особенно сильно проявляющаяся у AVR -- у него много регистров, а при обработке прерывания их нужно сохранить. Если обработчик делает всё «сам», то компилятор знает, какие регистры он задействовал и сохраняет/восстанавливает только их. Если из обработчика вызывается какая-то функция, то в обработчике сохраняются все регистры, которые вызываемая функция не обязана сохранять. Даже если она их не использует — компилятор-то про это «не знает» и сохраняет на всякий случай все *) Из-за обилия регистров у AVR стоит избегать вызовов не-inline функций из обработчиков. Вот даже тут если поменять функции EERead/EEwrite на INLINE, то код увеличится, но ненамного, так как пропадёт с десяток push/pop (т.е. десятка четыре байт). Для avr-gcc 4.3.4 из убунты 10.04 NOINLINE даёт 680 байт, а INLINE, размноживший тела функций по месту, 712.
2. Чисто avr-gcc-шная беда. Он почему-то экономит указательные регистры и предпочитает 4-байтовые lds/sts, которых там в обработчике немеряно. Даже если собрать все используемые в Receive() переменные в одну структуру, завести в подпрограмме указатель на эту структуру (под использование ldd/std), то он всё равно радостно скажет «да это же не какая-то неизвестная и каждій раз другая структура, это вот она одна конкретная фиксированная я ее вижу», выбросит указатель и будет напрямую lds/sts использовать. IAR, насколько я знаю, умеет сам собирать рядом объявленные переменные в структуру и использовать смещение к адресной регистровой паре. Прикидочно, на этом в данной программе можно сэкономить ещё около полусотни байт. avr-gcc иногда удаётся убедить использовать указатель, но это если вообще не поленюсь, то уж не в эти выходные.
*) Keil/MCS51 умеет вкупе с линкером делать глобальную регистровую оптимизацию, анализируя по дереву вызовов использование регистров. Не помню, работает ли это с обработчиками прерываний, они у меня на асме написаны были. На 25-килобайтной программе эта оптимизация давала около килобайта выиграша (для всех своих ассмблерных подпрограмм я тоже дал описания используемых регистров, иначе от них вверх по дереву шло «неизвестно, а значит надо беречь всё»). Возможно, в GCC что-то поправится в связи с LTO, но, опять таки, не уверен, что на обработчики преріваний это повлияет.
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Последний раз редактировалось avreal Пт авг 24, 2012 21:41:25, всего редактировалось 1 раз.
#pragma offtopic А AVReal еще не поддерживает FTBB?
Ну вот как меньше буду по форумам сидеть, так и найду время
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Мелкая правка: Поменял имена типа LedPin на LedMask и завёл LedPIN как именно регистр PIN, запись в него инвертирует ножку. Таким образом мигание светодиодом ещё укоротилось (это же и для асм-варианта полезно).
Тяжёлая артиллерия (заставляем avr-gcc работать со структурой через указатель и смещения — struct_receiver.c): Объединил переменные состояния приёма и буфер приёма в одну структуру (связанные логикой программы переменные я чаще всего сразу объединяю в структуру), добавил в Receive() переменную-указатель и макрос для предзагрузки указателя (там в исходниках ссылка на сайт, где обсуждается этот фокус). После столь убедительной просьбы avr-gcc начинает таки использовать ldd/std и размер программы для всех версий, кроме 3.4.6, упал до значений в диапазоне 600-620 байт, в минимуме 598. Уже только на треть больше 450-байтового ассемблерного варианта у которого все переменные в регистрах и прямая работа с ними на месте. Было бы переменных больше и они не лезли бы все в регистры, так разница была бы еще меньше. Хотя асмовый вариант тоже можно немного сократить.
Если ещё такое же сделать для Int100Hz(), причём в ту же структуру затолкать и счётчик Div100, то размер упадёт ниже отметки 600 байт уже для большинства версий компилятора.
Веником его, веником (reg_struct_receiver.c): Почти все глобальные переменные размещены в регистрах при помощи конструкций вида
При этом компилятор все равно иногда работает с ними плохо («I like to move it move it»), вместо dec r2 может влупить mov r16, r2 $ subi r16, 1 $ mov r2, r16, но всё равно код выходит короче и быстрее.
Повтор таблички с новой колонкой ptr для результатов со структурой и указателем и с колонкой reg для принудительного размещения переменных в регистрах. Не зря я не люблю насиловать компилятор размещением в регистрах. Две версии (*** в соответствующей колонке) просто взглюкнули, выбросив почти весь код receive():
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Последний раз редактировалось avreal Сб авг 25, 2012 12:09:31, всего редактировалось 1 раз.
Так это ж нужно садиться и делать хоть маленькую, но дополнительную работу Надо сначала отлежаться/отлизаться
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Уххххх...... Высший пилотаж! Я уж и боюсь вникать во все эти тонкости, стока их.....
Для начала попробую разобраться, как это вы просите компилятор использовать ldd/std вместо команд прямой загрузки, ато он немерено жрет ресурсов чтобы переменную загрузить и выгрузить. Я на асме вообще всегда резервировал регистровую пару с адресом начала оперативки, и всегда пользовался ldd/std. Честно говоря сначала в шоке был когда увидел что на обращение к переменной С тратит целых 8 байт..... это кроме операций с самой переменной.....
Со структурами пока страшновато, постепенно думаю буду вникать в ваши уроки.
Для начала попробую разобраться, как это вы просите компилятор использовать ldd/std вместо команд прямой загрузки ... Я на асме вообще всегда резервировал регистровую пару с адресом начала оперативки, и всегда пользовался ldd/std. ... Со структурами пока страшновато, постепенно думаю буду вникать в ваши уроки.
Вот когда на асме регистровая пара на начало оперативки — фактически, все переменные начинают характеризоваться не своим абсолютным адресом, а смещением относительно того начала. Структуры в С чем-то на это похожи. Объединяя переменные в структуру мы говорим компилятору «они должны лежать вместе, рядом, нас будут интересовать смещения относительно начала структуры». Причём самих переменны=-структур может быть несколько одинаковых, при работе с каждой пользуемся смещениями относительно её начала. Адресация к «полям» — элементам структуры (переменной соответствующего типа) идёт через точку, например
Код:
имя_структуры.имя_поля = 0;
Это кже неплохая подсказка компилятору о том, что можно в указательную пару загрузить адрес начала структуры и работать дальше по смещению. Но в оптимизаторе gcc/avr-gcc где-то недоработка. Возможно, несколько неправильно расставлены «стоимости» операций / «ценность» регистров. И он почему-то считает нежелательным занимать пару регистров. Приходится обращаться через указатель
Код:
имя_указателя_на_структуру->имя_поля = 0;
и загружать вручную указатель в пару макросом PRELOAD() После ручной загрузки регистровая пара «уже потрачена» и компилятор её использует.
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Подобные костыли и порождают трудноуловимые глюки...
А подробнее? Конкретно что за костыль, какие, к примеру, глюки? Из собственного либо чужого опыта…
HHIMERA писал(а):
Не потому ли компилерописатели "пропустили" подобные "обороты"??? ))))))))
Шо, и тут всемирный заговор?
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Понятно. Ляпнуть было не лениво, а отвечать — лениво.
Вы — трепло.
Можете считать мой пост вопросом, а не утверждением, так Вам будет проще, не надо будет отвечать за свои слова.
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 41
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения