4927 subscribers

Микроконтроллеры для начинающих. Часть 26. Команды вызова подпрограмм и прерываний и возврата из них

489 full reads
1k story viewsUnique page visitors
489 read the story to the endThat's 46% of the total page views
2,5 minutes — average reading time

Нам осталось познакомиться с последней группой команд передачи управления - вызова подпрограмм, прерываний, возврата из подпрограмм, возврата из прерываний. Мы уже знаем довольно много, поэтому сегодняшняя статья не должна показаться трудной.

Переход по заданному адресу и вызов подпрограмм

Вызов подпрограммы похож на обычный переход. Но есть одно небольшое, но очень важное, отличие. Когда мы вызываем подпрограмму нам необходимо обеспечить возврат из нее в четко определенное место. А именно, к команде непосредственно следующей за командой вызова.

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

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

Микроконтроллеры для начинающих. Часть 26. Команды вызова подпрограмм и прерываний и возврата из них

Дело в том, что подпрограмма может вызываться из разных мест программы, неоднократно. Передать управление на ее начальный адрес легко, он известен. А вот как вернуться назад? В данном случае у нас два разных адреса возврата, что невозможно реализовать обычными командами перехода. Обратите внимание, что проблема возникает не при вызове из основной программы, а при возврате из подпрограммы.

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

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

Команда вызова подпрограммы обычно имеет мнемонику CALL или производную от нее. Но бывают и исключения. Так в DEC PDP команда называлась JSR (Jump to SubRoutine). Команда возврата обычно имеет мнемонику RETURN или производную от нее (в DEC PDP - RST).

Работают эти команды очень просто. Синим показан вызов, а зеленым возврат

Вызов подпрограммы и возврат из нее. Иллюстрация моя
Вызов подпрограммы и возврат из нее. Иллюстрация моя
Вызов подпрограммы и возврат из нее. Иллюстрация моя

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

А теперь я хочу обратить ваше внимание на один важный факт. Все вызовы подпрограмм происходят синхронно. То есть, основная программа всегда знает, что вызывается подпрограмма, так как сама это делает. Казалось бы, это мелочь, но она скоро будет нам важна.

Прерывания

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

Я уже кратко говорил в статье "Микроконтроллеры для начинающих. Часть 24. Команды перехода. Безусловные переходы", что прерывания бывают двух основных типов. Синхронные, программные, прерывания вызываются использованием специальных команд, так что программа всегда знает, что происходит вызов прерывания. Асинхронные прерывания вызываются процессором на аппаратном уровне. Например, при каких то событиях или при возникновении неустранимой проблемы в работе программы. Асинхронные прерывания могут произойти в любой момент, вне зависимости от того, что происходит в программе и вне зависимости от ее на то желания.

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

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

Кажется сложным? Да, прерывания тема сложная. Причем очень сильно зависима от конкретного процессора. К счастью для нас, с точки зрения машинных команд, все гораздо проще и очень похоже во всех микроконтроллерах, которые мы рассматриваем.

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

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

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

Давайте посмотрим, какие действия выполняет процессор при возникновении прерываний, и как происходит передача управления. Как и ранее, синим будет показан вызов, а зеленым возврат.

Вызов обработчика прерывания переходом по адресу вектора. Показан вектор вмещающий две команды. Иллюстрация моя
Вызов обработчика прерывания переходом по адресу вектора. Показан вектор вмещающий две команды. Иллюстрация моя
Вызов обработчика прерывания переходом по адресу вектора. Показан вектор вмещающий две команды. Иллюстрация моя

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

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

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

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

Полное состояние процессора

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

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

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

Список машинных команд

Вот теперь мы готовы познакомиться со списком команд.

CALL - вызов подпрограммы. Эта команда есть во всех рассматриваемых нами микроконтроллерах. Она, конечно, выполняется немного по разному, но для нас пока это важно.

CALLW - специфическая для старших моделей 8-битных PIC команды вызова подпрограммы. Адрес подпрограммы задается в PCLATH:WREG. В PIC18 такой команды нет.

RCALL - вызов подпрограммы по относительному адресу в PIC18 и AVR.

CALLR - тоже самое, но для STM8.

ICALL - специфическая для AVR команда. Вызов подпрограммы, адрес которой задается индексным регистром Z. Есть не во всех микроконтроллерах. Не трудно заметить, что команды ICALL и CALLW в некоторой степени родственники.

EICALL - расширенная версия команды ICALL. Здесь дополнительно привлекается регистр EIND. Есть не во всех микроконтроллерах.

CALLF - специфическая для STM8 команда дальнего вызова подпрограммы.

RESET - команда сброса в старших моделях PIC и PIC18. Она выполняет несколько действий, в том числе, передает управление на вектор сброса. Если помните, я говорил о таком. По этой причине я и упоминаю о ней в данной статье.

TRAP - единственная команда вызова программного синхронного прерывая. Доступна только в STM8. Основное ее назначение - использование в отладчиках.

RETURN - возврат из подпрограммы в PIC.

RET - возврат из подпрограммы в AVR и STM8.

RETLW - специфическая для PIC команда возврата из подпрограммы с возвращаемым в WREG значением.

RETF - специфическая для STM8 команда дальнего возврата из подпрограммы.

RETFIE - возврат из прерывания в PIC.

RETI - возврат из прерывания в AVR.

IRET - возврат из прерывания в STM8.

Заключение

Ну вот мы и закончили знакомство с еще одним, очень интересным, семейством машинных команд - командами передачи управления. Но мы рассмотрели еще не все машинные команды. Наберитесь терпения, осталось совсем немного не изученных команд.

До новых встреч!