Разработка прикладных программ. Подпрограммы. Циклы.
Группа команд передачи управления

План лекции:

1. Формализованный подход к разработке прикладных программ

2. Подпрограммы

3. Циклы

4. Группа команд передачи управления

1.  Формализованный подход к разработке прикладных программ

Если целевая функция контроллера сформулирована, т. е. задача на разработку поставлена, то для получения текста исходной программы необходимо выполнить ряд последовательных действий:

1.  подробное описание задачи;

2.  анализ задачи;

3.  инженерную интерпретацию задачи, желательно с привлечением того или иного аппарата формализации (граф автомата, сети Петри, матрицы состояний и связности и т. п.) ;

4.  разработку общей блок-схемы алгоритма (БСА) работы конт­роллера;

5.  разработку детализированных БСА отдельных процедур, выде­ленных на основе модульного принципа составления программ;

6.  детальную проработку интерфейса контроллера и внесение исправ­лений в общую и детализированные БСА;

7.  распределение рабочих регистров и памяти МК;

8.  формирование текста исходной программы.

В результате работы по трем первым пунктам данного перечня полу­чают так называемую функциональную спецификацию прикладной про­граммы МК, в которой основное внимание уделяется детализации спо­собов формирования входной и выходной информации.

На языке схем алгоритмов разработчик описывает метод, выбранный для решения поставленной задачи. Довольно часто бывает, что одна и та же задача может быть решена различными методами. Способ решения задачи, выбранный на этапе ее инженерной интерпретации, на основе ко­торого формируется БСА, определяет не только качество разрабаты­ваемой прикладной программы, но и качественные показатели конечно­го изделия.

НЕ нашли? Не то? Что вы ищете?

Разработка БСА очень похожа на разработку аппаратурных средств. систем автоматики и обработки данных. В основу разработки БСА положена та же самая процедура модульного проектирования, которая традиционно используется разработчиками аппаратурных средств. Отличие состоит в том, что при разработке аппаратурных средств в ка­честве ’’строительного” материала используются логические схемы, триггеры, регистры и другие интегральные элементы, а при создании программного обеспечения разработчик оперирует командами, подпро­граммами, таблицами и другими программными объектами из арсенала средств обработки данных.

Так как алгоритм есть точно определенная процедура, предписываю­щая контроллеру однозначно определенные действия по преобразованию ’’сырых” исходных данных в обработанные выходные данные, то разра­ботка БСА требует предельной точности и однозначности используемой атрибутики: символических имен переменных, констант (уставок), подпрограмм (модулей), символических адресов таблиц, портов ввода/ вывода и т. п. Основное внимание при разработке БСА следует уделить тому разделу функциональной спецификации прикладной программы, в котором приводится описание аппаратуры сопряжения МК с объектом управления. (Это описание для успешной разработки программного обеспечения должно быть детализировано вплоть до электрических и временных характеристик каждого входного и выходного сигнала или устройства.)

Секрет успеха разработки прикладной программы МК заключается в использовании метода декомпозиции, при котором вся задача последо­вательно разделяется на меньшие функциональные модули, каждый из которых можно анализировать, разрабатывать и отлаживать отдельно от других. При выполнении прикладной программы в МК управление без всяких двусмысленностей передается от одного функционального мо­дуля к другому. Схема связности этих функциональных модулей, каж­дый из которых реализует некоторую процедуру, образует общую (или системную) БСА прикладной программы. Это разделение задачи на мо­дули и субмодули выполняется последовательно до такого уровня, ког­да разработка БСА модуля становится простым и понятным делом. Метод последовательной декомпозиции обладает достаточной гибкостью, что позволяет привести степень детализации БСА в соответствие со слож­ностью процедуры. Не следует стесняться при выполнении декомпози­ции дойти до модулей, которые почти тривиальны. Ведь именно эту цель (получение! очень простого и ’’прозрачного” алгоритма модуля) пре­следует разработчик, когда он стремится заставить МК надежно выпол­нять требуемую работу по управлению объектом. Язык графических образов БСА можно использовать на любом уровне детализации описа­ния модулей вплоть до того, что каждому оператору БСА будет соот­ветствовать единственная команда МК.

Структурное программирование есть процесс построения прикладной программы из строгого набора программных модулей, каждый из кото­рых реализует определенную процедуру обработки данных. Програм­мные модули должны иметь только одну точку входа и одну точку выхода. Только в этом случае отдельные модули можно разрабатывать и отлаживать независимо, а затем объединять в законченную приклад­ную программу с минимальными проблемами их взаимосвязей. Источ­ником подавляющего большинства ошибок программирования является использование модулей, имеющих один вход и несколько выходов. При необходимости организации множественных ветвлений в программе декомпозицию задачи выполняют таким образом, чтобы каждый функ­циональный модуль имел только один вход и один выход. Для этого условные операторы (имеющие два выхода) или включают внутрь мо­дуля, объединяя их с операторами обработки, или выносят в систему межмодульных связей, формируя тем самым БСА более высокого ранга.

В международном стандарте на программный продукт HIPO (Hie­rarchy—Input—Process—Output) (”хай-по”) декларируется аналогичный подход к разработке прикладных программ.

Разработка БСА функционального модуля программы имеет ярко выраженный итеративный характер, т. е. требует многократных проб, прежде чем возникает уверенность, что алгоритм реализации процедуры правильный и завершенный. Вне зависимости от функционального назна­чения процедуры при разработке ее БСА необходимо придерживаться следующей очередности работы:

1.  Определить, что должен делать модуль (это уже было сделано при разработке системной БСА, но теперь разработчик имеет дело с фрагмен­том прикладной программы, а не с целой программой, и, следовательно, может потребоваться доопределение и уточнение целевого назначения процедуры).

2.  Определить способы получения модулем исходных данных (от дат­чиков через порты ввода, или из таблиц в памяти, или через рабочие регистры). Для реализации ввода исходных данных в модуль в его БСА надо включить соответствующие операторы.

3.  Определить необходимость какой-либо предварительной обработки введенных исходных данных (маскирование, сдвиг, масштабирование, перекодировка). Если до использования ’’сырых” данных требуется их предобработка, то в состав БСА включаются соответствующие опера­торы.

4.  Определить способ преобразования входных данных в требуемые выходные. Используя операторы процедур и условные операторы приня­тия решения, отобразить на языке БСА выбранный метод содержатель­ной обработки исходных данных.

5.  Определить способы выдачи из модуля обработанных данных (пе­редать в память, или в вызывавшую программу, или в порты вывода информации) . Необходимые действия отобразить в БСА.

6.  Определить необходимость какой-либо постобработки выводимых данных (изменение формата, перекодирование, масштабирование, мас­кирование) . Ввести в БСА операторы подготовки данных для вывода из модуля.

7.  Вернуться к п. 1 настоящего перечня работ и проанализировать по­лученный результат. Выполнить итеративную корректировку БСА с целью сделать ее простой, логичной, стройной и обладающей четким графическим образом.

8.  Проверить работоспособность алгоритма на бумаге путем подста­новки в него действительных данных. Убедиться в его сходимости и результативности.

9.  Рассмотреть предельные случаи и попытаться определить граничные значения информационных объектов, с которыми оперирует алгоритм, за пределами которых он теряет свойства конечности, сходимости или результативности. (Особое внимание при этом следует уделить анализу возможных ситуаций переполнения разрядной сетки МК, изменения знака результата операции, деления на переменную, которая может при­нять нулевое значение.)

10.  Провести мысленный эксперимент по определению работоспособ­ности алгоритма в реальном масштабе времени, когда стохастические события, происходящие в объекте управления, могут оказать влияние на работу алгоритма. При этом самому тщательному анализу следует под­вергнуть реакцию алгоритма на возможные прерывания с целью опреде­ления критических операторов, которые необходимо защитить от преры­ваний. Кроме того, в ходе этого мысленного эксперимента следует про­анализировать логику алгоритма с целью определения таких последова­тельностей операторов, при выполнении которых МК может ”не заме­тить” кратковременных событий в объекте управления. При обнаруже­нии таких ситуаций в логику БСА следует внести коррективы.

Практика разработки программного обеспечения для МК показала, что последовательное использование описанной поэтапной процедуры проектирования алгоритмов, составляющей основу метода структурно­го программирования, позволяет уверенно получать работоспособные прикладные программы. Дисциплинированное следование этой поэтап­ной процедуре проектирования прикладных программ обеспечивает успех проекта! В противном случае вы рискуете заболеть страшным программным заболеванием, которое называется «вползающие осо­бенности». Эта инфекция возникает, когда неадекватная спецификация задачи позволяет вползать в программу организмам, называемым «изящные особенности». Те изменения, которые легко учесть на этапе планирования, могут потребовать огромных усилий на этапе реализации программы. Болезнь эта к моменту обнаружения становится уже серьез­ной и привела к фатальному концу много программных проектов. Чаще всего носителями этой болезни являются профессиональные программисты, которые способны заразить ею программирующих про­фессионалов. Если Вы стали жертвой «вползающих особенностей», то должны или начать заново разрабатывать функциональную специфи­кацию программного обеспечения, или быть готовыми к исключительно высоким трудозатратам на этапе отладки прикладной программы.

2.  Подпрограммы

Подпрограмма (англ. subroutine) — поименованная или иным образом идентифицированная часть компьютерной программы, содержащая описание определённого набора действий. Подпрограмма может быть многократно вызвана из разных частей программы.

Назначение подпрограмм

Подпрограммы изначально появились как средство оптимизации программ по объёму занимаемой памяти — они позволили не повторять в программе идентичные блоки кода, а описывать их однократно и вызывать по мере необходимости. К настоящему времени данная функция подпрограмм стала вспомогательной, главное их назначение — структуризация программы с целью удобства её понимания и сопровождения.

·  Выделение набора действий в подпрограмму и вызов её по мере необходимости позволяет логически выделить целостную подзадачу, имеющую типовое решение. Такое действие имеет ещё одно (помимо экономии памяти) преимущество перед повторением однотипных действий: любое изменение (исправление ошибки, оптимизация, расширение функциональности), сделанное в подпрограмме, автоматически отражается на всех её вызовах, в то время как при дублировании каждое изменение необходимо вносить в каждое вхождение изменяемого кода.

·  Даже в тех случаях, когда в подпрограмму выделяется однократно производимый набор действий, это оправдано, так как позволяет сократить размеры целостных блоков кода, составляющих программу, то есть сделать программу более понятной и обозримой...

Механизм подпрограмм, их описание и вызов

В случае ассемблера подпрограмма представляет собой последовательность команд (операторов), отдельную от основной части программы и имеющую в конце специальную команду выхода из подпрограммы. Обычно подпрограмма также имеет имя, по которому её можно вызвать, хотя ряд языков программирования допускает использование и неименованных подпрограмм.

Вызов подпрограммы выполняется с помощью команды вызова, включающей в себя имя подпрограммы.

Внимание! Ни в коем случае нельзя попадать в подпрограмму любым способом кроме команды вызова подпрограммы CALL! В противном случае команда возврата из подпрограммы передаст управление случайному адресу! По этому адресу могут быть расположены данные, которые в этом случае будут интерпретированы как программа.

Очень часто требуется из одной подпрограммы обращаться к другой подпрограмме. Такое обращение к подпрограмме называется вложенным. Количество вложенных подпрограмм называется уровнем вложенности подпрограмм. Максимально допустимый уровень вложенности подпрограмм определяется количеством ячеек памяти, предназначенных для хранения адресов возврата из подпрограмм, т. е. размером сегмента стека.

Параметры подпрограмм

Подпрограммы часто используются для многократного выполнения стереотипных действий над различными данными. Подпрограмма обычно имеет доступ к объектам данных, описанным в основной программе, поэтому для того, чтобы передать в подпрограмму обрабатываемые данные, их достаточно присвоить, например, глобальным переменным.

3.  Циклы

Цикл — разновидность управляющей конструкции, предназначенная для организации многократного исполнения набора инструкций. Также циклом может называться любая многократно исполняемая последовательность инструкций, организованная любым способом (например, с помощью условного перехода).

Последовательность инструкций, предназначенная для многократного исполнения, называется телом цикла. Единичное выполнение тела цикла называется итерацией. Выражение определяющее, будет в очередной раз выполняться итерация, или цикл завершится, называется условием выхода или условием окончания цикла (либо условием продолжения в зависимости от того, как интерпретируется его истинность — как признак необходимости завершения или продолжения цикла). Переменная, хранящая текущий номер итерации, называется счётчиком итераций цикла или просто счётчиком цикла. Цикл не обязательно содержит счётчик, счётчик не обязан быть один — условие выхода из цикла может зависеть от нескольких изменяемых в цикле переменных, а может определяться внешними условиями (например, наступлением определённого времени), в последнем случае счётчик может вообще не понадобиться.

Исполнение любого цикла включает первоначальную инициализацию переменных цикла, проверку условия выхода, исполнение тела цикла и обновление переменной цикла на каждой итерации.

Виды циклов

Безусловные циклы

Иногда в программах используются циклы, выход из которых не предусмотрен логикой программы. Такие циклы называются безусловными, или бесконечными. Специальных синтаксических средств для создания бесконечных циклов, ввиду их нетипичности, языки программирования не предусматривают, поэтому такие циклы создаются с помощью конструкций, предназначенных для создания обычных (или условных) циклов – в ассемблере – с помощью команд безусловных переходов.

Цикл с предусловием, цикл с постусловием

Цикл с предусловием — цикл, который выполняется пока истинно некоторое условие, указанное перед его началом. Это условие проверяется до выполнения тела цикла, поэтому тело может быть не выполнено ни разу (если условие с самого начала ложно).

Цикл с постусловием — цикл, в котором условие проверяется после выполнения тела цикла. Отсюда следует, что тело всегда выполняется хотя бы один раз.

На языке ассемблера циклы с предусловием или с постусловием реализуются с помощью команд условных переходов, команд сравнения.

Цикл с выходом из середины

Синтаксически такой цикл оформляется с помощью трёх конструкций: начала цикла, конца цикла и команды выхода из цикла. Конструкция начала маркирует точку программы, в которой начинается тело цикла, конструкция конца — точку, где тело заканчивается. Внутри тела должна присутствовать команда выхода из цикла, при выполнении которой цикл заканчивается и управление передаётся на оператор, следующий за конструкцией конца цикла. Естественно, чтобы цикл выполнился более одного раза, команда выхода должна вызываться не безусловно, а только при выполнении условия выхода из цикла.

Принципиальным отличием такого вида цикла от рассмотренных выше является то, что часть тела цикла, расположенная после начала цикла и до команды выхода, выполняется всегда (даже если условие выхода из цикла истинно при первой итерации), а часть тела цикла, находящаяся после команды выхода, не выполняется при последней итерации.

Легко видеть, что с помощью цикла с выходом из середины можно легко смоделировать и цикл с предусловием (разместив команду выхода в начале тела цикла), и цикл с постусловием (разместив команду выхода в конце тела цикла).

Цикл cо счётчиком

Цикл со счётчиком — цикл, в котором некоторая переменная изменяет своё значение от заданного начального значения до конечного значения с некоторым шагом, и для каждого значения этой переменной тело цикла выполняется один раз. В языке программирования Ассемблер для микроконтроллеров семейства AVR цикл со счетчиком на прямую не реализуется. Для создания цикла со счётчиком необходимо использовать комбинацию нескольких команд: изменения счетчика и команды условного перехода. В качестве счётчика (так называемой «переменной цикла»), используется один из регистров общего назначения. Перед циклом в нем задается требуемое количество проходов. Обычно каждое выполнение тела цикла регистр-счетчик декрементируется, и после этого выходит из цикла, если остаточное количество проходов равно нулю, иначе переходит на метку, символизирующую начало цикла.

ldi r16,<количество проходов цикла>

<метка>: ; метка начала тела цикла

..... ; тело цикла

dec r16 ; уменьшение счетчика оставшихся проходов

brne <метка> ; команда зацикливания, если счетчик не равен нулю

4.  Группа команд передачи управления

RJMP K

Безусловный относительный переход, для передачи управления в пределах 2k слов вперед и назад относительно текущего счетчика команд. K - 12-ти разрядная константа, вычисляется компилятором, вам в программе достаточно написать

rjmp Label

Для кристаллов с ПЗУ программ не более 8 кБ перекрывает весь диапазон адресов

IJMP

Безусловный косвенный переход. Управление передастся на адрес, который находится в регистровой паре Z. Основное предназначение - вычисляемый переход, что-то типа паскалевского CASE.

RCALL K

Обращение к подпрограмме. Передача управления работает точно так же, как у команды RJMP, но в стеке сохраняется адрес следующей за RCALL команды (см. описание RET). Подпрограммы применяются для уменьшения размеров программы и улучшения ее "читабельности", в них выносятся часто используемые фрагменты вычислений. Так, в примере простейшей мигалки это подпрограмма задержки Delay05S

ICALL

Аналогично RCALL, но управление передается на адрес, указываемый регистровой парой Z

RET

Возврат из подпрограммы. Адрес, куда передается управление, извлекается из стека.

RETI

Возврат из обработчика прерывания. Адрес, куда передается управление, извлекается из стека, и устанавливается бит разрешения прерываний в SREG

CPSE Rd. Rs

Сравнивает Rd и Rs, и пропускает следующую команду, если они равны. При этом флаги признаков в SREG не меняются! Вообще команда какая-то странная, я ее практически не использовал никогда.

Далее следуют три команды сравнения, которые сами по себе никаких ветвлений не вызывают, но устанавливают признаки в SREG - которые потом используются для ветвлений. наверное, именно поэтому во всех описаниях команды сравнения относятся к группе команд передачи управления. Не буду и я ничего переделывать, тем более что тогда пришлось бы разъединить CP и CPSE. Пусть уж живут вместе :-)

CP Rd, Rs

Сравнение Rd и Rs. По сути, это та же команда вычитания, только результат вычисления нигде не сохраняется. Изменяются признаки: H S V N Z C

CPC Rd, Rs

То же - но вычитается еще и бит переноса C. Используется для реализации сравнения многобайтных чисел, например, для сравнения двухбайтных чисел, размещенных в регистрах R23:R22 и R21:R20 необходимо выполнить следующее:

cp R22,R20 ;младшие байты
cpc R23,R21 ;и старшие

Изменяются признаки: H S V N Z C

CPI Rd, K

Сравнение регистра Rd и константы. Работает со "старшими" регистрами. Изменяются признаки: H S V N Z C

SBRC Rd, b

SBRS Rd, b

Проверка бита b (b=0..7) в регистре Rd и пропуск следующей команды, если он очищен (SBRC) или установлен (SBRS). Биты признаков не изменяются.

Поскольку обычно требуется "обойти" более чем одну команду, то делается так:

sbrs R17,3 ;перепрыгиваем команду RJMP
rjmp OBHOD

; тут выполняется то, что должно быть выполнено при единичном значении R17.3!

OBHOD:

SBIC IO, b
SBIS IO, b

То же, что и SBRC - только проверяются биты не регистров, а портов ввода-вывода. Напомню, что побитовое обращение к портам ввода-вывода возможно только для портов с адресами до 0x20!

BRBS
BRBC

Две абсолютно бесполезные мнемоники. По сути, это общее обозначение всех последующих мнемоник, но я не думаю, что найдется человек, пишущий

brbs 0, Label ; перейти, если бит 0 SREG равен 1

если можно написать

brcs Label ; перейти, если C-разряд установлен (BRanch if Carry Set)

Хотя это абсолютно одно и то же! Но во втором случае гораздо читабельнее.

Далее идут команды - ветвления, то есть передача управления по условию. Флаги условий - в регистре состояния SREG. Передача управления возможна на 64 слова назад или 63 слова вперед относительно текущего счетчика команд. Если необходим переход на большее расстояние, используют пару команд с противоположным условием:

brne OBHOD
rjmp LABEL ; переход по "равно"

OBHOD:

BREQ
BRNE

Переход при Z=1 (нулевой результат)

Переход при Z=0 (НЕнулевой результат)

BRCS
BRCC

Переход при C=1 (перенос)

Переход при C=0 (отсутствие переноса)

BRGE
BRLT

Переход при S=1 (больше или равно)

Переход при S=0 (меньше)

BRSH
BRLO

То же самое что BRCC/BRCS

BRMI
BRPL

Переход при N=1 (отрицательный результат)

Переход при N=0 (положительный результат. 0 - тоже положительный!)

BRHS
BRHC

Переход при H=1 (перенос из младшей тетрады)

Переход при H=0

BRTS
BRTC

Переход при T=1

Переход при T=0

BRVS
BRVC

Переход при V=1 (переполнение)

Переход при V=0

BRIE
BRID

Переход при I=1 (прерывания разрешены)

Переход при I=0 (прерывания запрещены)

Осталось описать три специфические команды...

NOP

Пустая операция. Не делает ничего, кроме того, что занимает один такт процессора. Имеет код операции 0x000, что дает возможность "забить" ею любую другую команду без стирания всей программы (подробнее об этом чуть позже)

SLEEP

Перевод процессора в режим пониженного энергопотребления.

WDR

Сброс сторожевого таймера.

Примеры решения задач

Дан массив чисел в ячейках резидентной памяти данных с адресами $0080..$00A5. Обнулить все ячейки массива.

ldi xh, $00 ; установка начального адреса

ldi xl, $80 ;ячеек памяти

clr r16 ; очистка временного регистра

loop: st x+, r16 ; обнуление текущей ячейки массива и

; переход к следующей ячейке

cpi xl, $0A6 ;проверка на достижение конца массива

brne loop ; если конец не достигнут