Применение подпрограмм при программировании.
При написании программ часто при реализации алгоритма работы устройства приходится повторять одни и те же операторы (например, операторы, работающие с параллельным или последовательным портом). Было бы неплохо использовать один и тот же участок кода, вместо того, чтобы повторять одни и те же операторы несколько раз.
Участок программы, к которому можно обращаться из различных мест программы для выполнения некоторых действий называется подпрограммой.
Проблема, с которой приходится сталкиваться при многократном использовании участков кодов - это в какое место памяти программ возвращаться после завершения подпрограммы. Обращение к подпрограмме производится из нескольких мест основной программы:
Рис. 3.2. Иллюстрация состояния программного счетчика при работе с подпрограммами
Для того, чтобы получить возможность возвращаться на команду, следующую за командой вызова подпрограммы, требуется запомнить адрес этой команды. Адрес возврата хранится в особых ячейках памяти. После выполнения подпрограммы необходимо осуществить переход к адресу, который записан в этих ячейках.
Для обращения к подпрограмме и возврата из нее в систему команд микропроцессоров вводят специальные команды. В микроконтроллерах семейства MCS-51 это командыLCALL, ACALL для вызова подпрограммы и команда RET для возврата из подпрограммы.
Пример использования подпрограммы:
...
MOV A,#56
CALL PeredatByte
...
MOV A,#37
CALL PeredatByte
...
;********************************************
;Подпрограмма передачи байта
;через последовательный порт
;********************************************
PeredatByte:
JB TI,$ ;Если предыдущий байт передан
MOV SBUF,G_Per ;то передать очередной байт
RET
Внимание! Ни в коем случае нельзя попадать в подпрограмму любым способом кроме команды вызова подпрограммы CALL! В противном случае команда возврата из подпрограммы передаст управление случайному адресу! По этому адресу могут быть расположены данные, которые в этом случае будут интерпретированы как программа, или обратиться к внешней памяти, откуда будут считываться случайные числа.
Очень часто требуется из одной подпрограммы обращаться к другой подпрограмме. Такое обращение к подпрограмме называется вложенным. Количество вложенных подпрограмм называется уровнем вложенности подпрограмм. Максимально допустимый уровень вложенности подпрограмм определяется количеством ячеек памяти, предназначенных для хранения адресов возврата из подпрограмм.
Ячейки памяти, в которых хранятся адреса возврата из подпрограмм называются стеком. Логически эти ячейки памяти организованы так, чтобы считывание последнего записанного адреса производилось первым, а первого записанного адреса производилось последним. Такая логическая организация формируется специальным счетчиком. Этот счетчик называется указателем стека SP. Ячейка памяти, в которую в данный момент может быть записан адрес возврата из подпрограммы, называется вершиной стека. Количество ячеек памяти, предназначенных для организации стека, называется глубиной стека. Последняя ячейка памяти, в которую можно производить запись называется дном стека. Логическая организация стека приведена на следующем рис. 3.3.
Первоначально стек выполнялся аппаратно на отдельных ячейках памяти, затем его стали размещать в обычной памяти данных микропроцессоров. Это позволило в
каждом конкретном случае устанавливать необходимую для программы глубину
стека. Оставшуюся память можно использовать для размещения глобальных и
локальных переменных программы. Глубина стека устанавливается при помощи
Рис. 3.3. Логическая организация стека
записи начального адреса вершины стека в указатель стека. Глубина стека устанавливается один раз после включения питания в процедуре инициализации контроллера.
Глобальными называются переменные, используемые при выполнении всей программы, включая микропрограммы.
Локальными называются переменные, используемые только данной подпрограммой.
Например, в микроконтроллерах семейства MCS-51 при занесении информации в стек содержимое указателя стека увеличивается - стек растет вверх, поэтому стек размещается в самой верхней части памяти данных. Для того, чтобы установить глубину стека 28 байт, необходимо вычесть из адреса максимальной ячейки внутренней памяти микроконтроллера глубину стека и записать полученное значение в указатель стека SP:
Кроме содержимого программного счетчика часто требуется запоминать содержимое внутренних регистров и флагов процессора, локальных переменных подпрограммы. Стек оказался удобным средством и для этой задачи. Сохранение локальных переменных в стеке позволило осуществлять вызов подпрограммы самой из себя (реализовывать рекурсивные алгоритмы). Это привело к введению в систему команд специальных команд работы со стеком. В микроконтроллерах семейства MCS-51 это команды PUSH и POP. Использование этих команд показывается на следующем примере:
Подпрограммы предназначены для выполнения определенных действий над внутренними устройствами микроконтроллера, внешними устройствами, подключенными к выводам микроконтроллера или числами, хранящимися в памяти этой микросхемы. В любом случае, с точки зрения программы, операции производятся над переменными. Переменные могут быть локальными или глобальными.