Процессор

Обращение к подпрограммам - сохранение/восстановление контекста

Для обращения к подпрограмме используется специальная команда вызова подпрограммы. В системе команд х86 она имеет мнемонику call.

Минимальный набор действий, которые выполняет такая команда, это:

  1. сохранение адреса возврата, т.е. адреса команды, следующей за call;
  2. загрузка в счетчик команд адреса первой исполняемой команды ПП.

Для возврата из ПП в системе команд есть специальная команда (в i*86 ее мнемоника ret). Эта команда загружает ранее сохраненный адрес возврата в счетчик команд.

В х86 имеются две разновидности команды call: ближний near вызов (внутри текущего сегмента кода) и дальний far вызов (в другой сегмент кода). В случае использования дальнего вызова в стеке сохраняется, наряду со счетчиком команд (e)ip, и сегментный регистр cs.

Соответственно имеются и две команды возврата ret near и ret far. Состояние стека при ближнем и дальнем вызовах иллюстрируется следующим рисунком (см. рисунок ниже).

 

Состояние стека при ближнем и дальнем вызовах

Понятие контекста (вектора состояния): в каждой конкретной точке выполнения программы - содержимое всех переменных элементов (регистров, ячеек памяти), которое требуется установить/восстановить, чтобы стало возможным запустить выполнение программы с этой точки.

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

Рассмотрим типовую последовательность команд, используемую при вызове подпрограммы. Пусть подпрограмма использует два входных параметра (два аргумента) типа int, возвращает значение тоже типа int и использует две локальные переменные. Две команды mov в вызывающей части помещают в стек параметры, после чего выполняется команда вызова подпрограммы call.

Первые две команды подпрограммы сохраняют старое значение ebp (элемент контекста) и устанавливают ebp на адрес внутри стекового кадра. Следующая команда (sub esp,8) занимает в стеке место под две локальные переменные.

Теперь подпрограмма может, используя адресацию косвенно-регистровую со смещением через регистр ebp, иметь доступ к параметрам, используя положительные значения смещения (в примере для доступа к первому параметру смещение должно быть равно 12), и к локальным переменным, используя отрицательные смещения (в примере смещение -8 позволяет обращаться к локальной переменной 2).

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

После возврата следует освободить место в стеке, занятое параметрами. В нашем примере это делает команда add esp, 8 , однако в архитектуре х86 это может быть сделано с помощью разновидности команды возварата из подпрограммы ret n, она не только осуществляет возварт, но и извлекает из стека (в "никуда") число байтов, указываемое параметром n.

Для облегчения сохранения-восстановления контекста в разных реализациях процессоров могут быть использованы:

1) Специальные команды:

  • В iх86+ команды pusha, popa сохраняют в стеке - восстанавливают регистры данных и адресные в таком порядке: (e)ax, (e)cx, (e)dx, (e)bx, (e)sp, (e)bp, (e)si, (e)di. При этом значение (e)sp берется то, которое было до начала выполнения команды pusha.
  • В Motorola 60ххх есть команды movem (MOVE Multiple), сохраняющие/восстанавливающие регистры в/из памяти. Особенность - можно указать (битовой маской), какие регистры сохранять. Место сохранения указывается с использованием стандартных способов адресации.

2) Несколько экземпляров наборов регистров:

  • TMS9900 - регистров данных и адресов не было вообще. В качестве регистров использовалось несколько ячеек памяти, положение которых в адресном пространстве указывал специальный регистр - указатель рабочей области (аналогично в Transputer фирмы Inmos).
  • Transputer (семейство процессоров фирмы Inmos) - регистров данных/адресов всего 3, и они образуют стек. Переход (и к подпрограмме) производится только, когда стек пуст, поэтому сохранять надо только адрес возврата.
  • IA-64 (Itanium) динамическое переименование регистров в регистровом файле при вызове процедуры (см. Intel IA-64 Architecture Software Developer's Manual, vol.2, IA-64 System Architecture, раздел 6 - IA-64 Register Stack Engine).
Яндекс.Метрика