PIC и mikroPascal

Оптимизация кода для компилятора MikroPascal for PIC

mikroPascalНе секрет, что любой компилятор формирует избыточный машинный код.  Если те же самые функции реализовать на ассемблере, то в результате можно получить существенно меньшую по объему управляющую программу. Для современных больших микроконтроллеров и микропроцессоров это не имеет столь решающего значения, как для чипов на основе 8-ми разрядных кристаллов. Оснащаются они, как правило, небольшим объемом памяти и соответственно требовательны к объему кода. Ниже описано, как можно сократить результирующий машинный код, пользуясь некоторыми правилами построения исходных текстов программ.

Библиотечные функции

Среда разработки MikroPascal for PIC предоставляет пользователю множество дополнительных библиотек, существенно облегчающих написание кода. Часть функций из них имеют особенность компилирования, так называемую «inline». Встречая такую функцию, компилятор каждый раз вставляет машинный код, соответствующий ей, непосредственно в место вызова. Если в программе будет множество вызовов, то соответственно появится и множество однотипных фрагментов. В частности таковыми являются всеми любимые функции задержки delay_ms(), delay_us(). Связано это с тем, что разработчики компилятора обещает высокую точность задания временного интервала при их использовании. Но жертвовать приходится увеличением объема кода. Соответственно, если в программе используются «фирменные» задержки с одним и тем же временем и нет необходимости в высокой точности отсчета этих интервалов, то все эти задержки следует оформить в отдельную процедуру и вызывать по мере необходимости.

Работа с данными

Следующее правило касается присвоения значения переменным, а если быть точнее единичного значения. При записи строчки i:=1; компилятор вместо нее подставляет две ассемблерные команды. Первая из них заносит 1 в аккумулятор, вторая загружает ее в ячейку памяти. Но если обратиться к двоичному коду, то данная операция есть ни что иное как установка 1 в младшем разряде переменной. Далее следует вспомнить, что PIC-и битово ориентированные процессоры. Для реализации той же самой задачи можно использовать только одну ассемблерную команду BSF – установка бита в ячейке памяти. В паскале ее аналогом является setbit(i,0), или прямой вызов i.0:=1;. То же самое можно сказать и о всех других числах, кратных степени двойки. Только записывать 1 необходимо в соответствующий разряд. Казалось бы зачем это необходимо, но в программах часто встречаются так называемые флаговые переменные, которые принимают всего два значения 0 или 1. Если их встречается большое количество, то применяя вышеизложенные правила можно выиграть несколько дополнительных ячеек памяти программ. Казалось бы в продолжении начатой темы можно рассмотреть и операции увеличения/уменьшения на 1.Но компилятор в этом случае самостоятельно использует необходимые команды инкремента и декремента.

Несколько тяжело mikroPascal реализует операции по обмену полубайтами в одном байте. В ассемблере PIC для этого предусмотрена специальная команда, но в компиляторе получается более громоздкий результат. То же самое относится и к командам побитного сдвига. В программах при работе с данными часто используется сдвиг байта на необходимое число бит. В процессоре для этого предусмотрены специальные команды RRF и RLF. MicroPASCAL также реализует команды SHR и SHL. В программе они заменяются на необходимое число последовательно идущих ассемблерных команд, утяжеленных обнулением крайнего бита. Дело в том, что ассемблерные команды реализуют сдвиг через бит переноса. Его значение записывается в крайний бит, противоположный направлению сдвига. Соответственно если были сдвинуты какие-то единичные биты, они появятся с противоположной стороны, через один шаг. MicroPASCAL дополнительно затирает эти биты, чем увеличивает объем кода. Зато часто используемая операция маскирования полубайта с последующим сдвигом оставшейся части, здесь может быть реализована только сдвигом в нужную сторону на 4 разряда, что даст в выигрыше минимум 2 ассемблерные команды.

Операторы условий

Следующим  правилом оптимизации кода становится отказ от сложных условий. Конечно, хорошо в одном операторе прописать все необходимые условия с помощью большой логической функции, но гораздо меньший по объему код получится,  если использовать необходимое число вложенных условий. Результат нескольких, последовательно идущих, простых IF THEN оказывается лучше одного условия. Еще выгоднее использовать конструкцию IF THEN ELSE. Она дает на 3 команды меньший код, чем  два  условия IF THEN на истинный и ложный результат. При этом в некоторых случаях еще лучший результат даст схема построения программы с предварительным исполнением одной ветви. Любой оператор условия представляет собой ветвление в машинном коде программы. Это значит что формируется два потока команд, один из которых исполняется при «истинном» результате логического условия в операторе ветвления, а второй при «ложном». Если сначала запрограммировать обязательное выполнение одной из ветвей, а только затем проверить условие, потребуется всего один переход вместо двух. Код может выполняться медленнее, но будет компактней. Кстати, такая схема чем-то напоминает технологию глубокого предсказания переходов в больших процессорах.

В продолжении условий, оператор множественного выбора CASE дает более объемный машинный код, чем несколько отдельных условий. Поэтому вместо него по возможности следует использовать необходимое количество обычных IF THEN.

Циклы

Циклы, несмотря на разницу в записи, имеют одинаковый результат по коду. Так конструкция:

 For i:=1 to 5 do begin end;

равнозначна более громоздкой

 i:=1;
 while i<5 do begin i:=i+1 end;

Но и в том и в другом случае можно выиграть одну команду, если начинать цикл с нулевого значения:

 For i:=0 to 4 do begin end;

 

Если резюмировать выше сказанное, то получаются следующие правила для работы с PIC-ами, позволяющие получить компактный код:

1.Без необходимости не использовать переменные большой разрядности. Стараться обходиться байтовыми.

2.Не использовать тяжелые математические функции.

3.По максимуму использовать битовые функции при установке значений. Обнуление предпочтительней записи ненулевого числа.

4.Не использовать сложные условия с логическими функциями. Разбивать при необходимости на несколько вложенных условий.

5.Сводить часто используемые фрагменты и библиотечные функции в процедуры.

Ну и конечно не забывать включать мозги. Как раньше говорили программисты: Любую программу можно уменьшить минимум на одну команду.

 

You have no rights to post comments