Ревич, Ю.В. Программирование микроконтроллеров AVR: от Arduino к ассемблеру

1 92 Часть //. Программирование микроконтроллеров АVR на ассемблере ; в программе : push var_l ; переменная var_l в программе помещается в стек push var_2 ; переменная var_2 в программ е помещается в стек procedure ; ; вызывается макрос procedure рор var_2 ; после него результат извлекается из стека рор var_l ; второй результат извлекается из стека в качестве переменных var_l и var_2 допустимы любые регистры, при этом дейст­ вия внутри процедуры всегда будут совершаться с заданными регистрами var_loc. Обратите внимание, что когда таких переменных несколько, важно соблюдать пра­ вильный порядок их помещения в стек и извлечения оттуда согласно принципу «первым вошел - последним вышел» (в программах на языках высокого уровня за порядком переменных в стеке следят специальные форматы вызова функций типа stctcall и подобные). ПОДРОБНОСТИ Конечно, если вы хотите сэкономить регистры, то в коде процедуры еще должны быть команды промежуточного сохранения и затем восстановления содержимого регист­ ров, соответствующих переменным var_loc2 и var_loc2 , - иначе, понятно, никакой экономии не получится , наоборот. Сохранение/восстановление делается , конечно, не через стек, а через обычные ячейки памяти . Здесь эти команды опущены , чтобы со­ средоточиться на логике использования стека . Но мы знаем, что макрос - все-таки не процедура и не функция, это просто такой способ структурирования текста программы с целью сокращения записи . Потому в приведенном примере мы, скорее, безосновательно раздули код на кучу команд рор и push, обеспечив себе только возможность временно использовать иные регист­ ры (в результирующем коде после двух push перед текстом макроса всегда будут сразу идти два рор, аналогично и в конце макроса) . В реальных программах хочется все-таки иметь настоящую процедуру-функцию, когда один и тот же код процеду­ ры может вызываться несколько раз, и при этом действительно имеется экономия регистров. Тут все оказывается несколько сложнее, потому что при вызове про­ цедуры на вершине стека оказывается адрес возврата, и потерять его - верный способ развалить программу в полный хлам. Есть два способа организации этого процесса. Оба основываются на картине рас­ пределения памяти в стеке (рис . 7 . 1 ) до и после вызова подпрограммы, а также в начале ее выполнения Здесь для примера рассмотрен случай, когда в стеке содер­ жатся две переменные: var_1 и var_2 (не адреса этих переменных, а их содержимое). На вершину стека указывает содержимое регистров SPL и sРн (в которые мы, если помните, в начале программы загружали константу RAМEND ) . Канонический способ доступа к переменной var_l из подпрограммы заключается в том, чтобы к двухбай­ товой переменной SPH : SPL прибавить 1 (стек растет в сторону уменьшения адресов) и по полученному адресу извлечь из памяти значение (для переменной var_2 приба­ вить 2 и т. д. , если их больше двух). Потом по тем же адресам сохраняются новые. значения . При этом перед выходом из подпрограммы вам ничего делать не надо, поскольку адрес возврата остался на своем месте . Именно так и поступают в «больших» микропроцессорах [ 1 3 ) .

RkJQdWJsaXNoZXIy MTExODQxMg==