Выделено из темы про ассемблер 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 писал(а):
Не потому ли компилерописатели "пропустили" подобные "обороты"??? ))))))))
Шо, и тут всемирный заговор?
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Понятно. Ляпнуть было не лениво, а отвечать — лениво.
Вы — трепло.
Можете считать мой пост вопросом, а не утверждением, так Вам будет проще, не надо будет отвечать за свои слова.
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения