Применение подпрограмм при программировании
При написании программ часто при реализации алгоритма работы устройства приходится повторять одни и те же операторы (например операторы, работающие с параллельным или последовательным портом). Было бы неплохо использовать один и тот же участок кода, вместо того, чтобы повторять одни и те же операторы несколько раз.
Участок программы, к которому можно обращаться из различных мест программы для выполнения некоторых действий называется подпрограммой.
Проблема, с которой приходится сталкиваться при многократном использовании участков кодов, это в какое место памяти программ возвращаться после завершения подпрограммы? Обращение к подпрограмме производится из нескольких мест основной программы:
Для того, чтобы получить возможность возвращаться на команду, следующую за командой вызова подпрограммы, требуется запомнить адрес этой команды. Адрес возврата хранится в особых ячейках памяти. После выполнения подпрограммы необходимо осуществить переход к адресу, который записан в этих ячейках.
Для обращения к подпрограмме и возврата из неЈ в систему команд микропроцессоров вводят специальные команды. В микроконтроллерах семейства 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. Ячейка памяти, в которую в данный момент может быть записан адрес возврата из подпрограммы, называется вершиной стека. Количество ячеек памяти, предназначенных для организации стека, называется глубиной стека. Последняя ячейка памяти, в которую можно производить запись называется дном стека. Логическая организация стека приведена на следующем рисунке:
Первоначально стек выполнялся аппаратно на отдельных ячейках памяти, затем его стали размещать в обычной памяти данных микропроцессоров. Это позволило в каждом конкретном случае устанавливать необходимую для программы глубину стека. Оставшуюся память можно использовать для размещения глобальных и локальных переменных программы. Глубина стека устанавливается при помощи записи начального адреса вершины стека в указатель стека. Глубина стека устанавливается один раз после включения питания в процедуре инициализации контроллера.
Например в микроконтроллерах семейства MCS-51 при занесении информации в стек содержимое указателя стека увеличивается (стек растЈт вверх), поэтому стек размещается в самой верхней части памяти данных. Для того, чтобы установить глубину стека 28 байт, необходимо вычесть из адреса максимальной ячейки внутренней памяти микроконтроллера глубину стека и записать полученное значение в указатель стека SP:
Кроме содержимого программного счЈтчика часто требуется запоминать содержимое внутренних регистров и флагов процессора, локальных переменных подпрограммы. Стек оказался удобным средством и для этой задачи. Сохранение локальных переменных в стеке позволило осуществлять вызов подпрограммы самой из себя (реализовывать рекурсивные алгоритмы). Это привело к введению в систему команд специальных команд работы со стеком. В микроконтроллерах семейства MCS-51 это команды PUSH и POP. Использование этих команд показывается на следующем примере:
[Назад] [Содержание] [Вперёд]