подпрограммы

Ассемблер #Ассемблер

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

Общие принципы

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

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

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

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

Вызов подпрограмм и возврат из них

Вызов подпрограммы, как уже стало ясно из вышесказанного, - это передача управления по адресу начала подпрограммы с одновременным запоминанием в стеке адреса возврата (то есть адреса машинной команды, непосредственно следующей за командой вызова). Процессор i386 предусматривает для этой цели команду call; аналогично команде jmp, аргумент команды call может быть непосредственным (адрес перехода задан непосредственно в команде, например, меткой), регистровым (адрес передачи управления находится в регистре) и типа "память" (переход нужно осуществить по адресу, прочитанного из заданного места памяти). Команда call не имеет короткой формы; поскольку дальняя форма нам, как обычно, не требуется в силу отсутствия сегментов, остаётся только одна форма - близкая (near), которую мы всегда и используем.

Возврат из подпрограммы производится командой ret (от слова return). В своей простейшей форме эта команда не имеет аргументов. Выполняя эту команду, процессор извлекает 4 байта с вершины стека и записывает их в регистр EIP, в результате чего управление передаётся по адресу, который находится в памяти на вершине стека.

Оценка статьи 0

Комментарии

Пока комментариев нет.