Процессор

Процессор

Обращение к подпрограммам - передача управления

Для работы с подпрограммами в составе системы команд любого процессора есть две команды (см. рисунок ниже):

  1. Команда обращения к подпрограмме ОП (для этой команды используются мнемоники call, jsr). Эта команда должна автоматически запоминать адрес возврата, т.е. адрес команды, следующей за командой call. В современных процессорах при выполнении команды обращения к ПП адрес возврата запоминается автоматически в стеке.
  2. Команда возврата из ПП (используются мнемоники ret, rts).

 

Связь с подпрограммой по управлению

Расширения системы команд по мере развития микропроцессорной техники

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

  1. В специализированных графических процессорах используются специальные команды, облегчающие выполнение операций линейной интерполяции, которые очень часто используются при преобразованиях графических данных.
  2. В специализированных графических процессорах могут быть команды, выполняющие достаточно сложные операции с группами простых элементов данных, например, с двумерными блоками в памяти (это фрагменты изображений). Более подробно этот вопрос затронем, обсуждая графические данные.
  3. В процессорах Pentium MMX реализована группа новых команд. Каждая из команд MMX способна выполнять одно и то же действие одновременно над несколькими "короткими" операндами, расположенными в "длинных" MMX-регистрах. Здесь реализована идеология SIMD (“Single Instruction - Multiple Data” - "одна команда -несколько элементов данных”), позволяющая получать несколько результатов при выполнении каждой MMX-команды. Это частный случай более обширной группы команд, упомянутой в предыдущем пункте.
  4. В разновидности процессоров, известной под названием "цифровые сигнальные процессоры" (DSP - Digital Signal Processors) часто реализованы команды, облегчающие вычисление "цифровой свертки" Y=Sum(Xi*Ki). Операция цифровой свертки очень часто используется в алгоритмах обработки сигналов. Сигнальные процессоры нередко имеют также своеобразную архитектуру, программную модель, хорошо приспособленную для действий с отсчетами сигналов.
  5. В однокристальных микроконтроллерах M68HC12 фирмы Motorola реализованы специальные команды, облегчающие программирование алгоритмов "нечеткой логики" (Fuzzy Logic).

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

Для обращения к подпрограмме используется специальная команда вызова подпрограммы. В системе команд х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).

Обращение к подпрограммам - обмен данными

При обращении к подпрограмме могут передаваться: сами данные или адрес участка памяти, где находятся данные. Данные могут находиться:

  • а) в регистре/ах, если данных мало или если в процессоре много регистров;
  • б) в стеке;
  • в) в оговоренном известном месте памяти (например, в DEC16 - программная память, слова, следующие за командой вызова ПП).

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

Пример на языке С.

int Calc (int x, int y) {
int iX, iY;
iX=2*x+y;
iY=3*y-x;
return iX*iY;
}

Эта процедура принимает два параметра x и y и возвращает результат вычисления несложного арифметического выражения в глобальную ёпеременную z. В процедуре объявлены две локальные переменные. Далее приведен результат компиляции этой процедуры после его дизассемблирования полноэкранным отладчиком TurboDebugger (см. таблицу ниже).

Посмотрим теперь структуру стека (см. таблица ниже). Столбец 2) содержит адреса стека как они получились при компиляции рассматриваемого примера, столбец 3) показывает состояние этих адресов до начала помещения параметров в стек перед вызовом процедуры, столбец 4) - состояние стека после входа в процедуру и выделения места под локальные переменные. В столбце 1) отмечен участок стека, составляющий стековый кадр для рассматриваемого примера.

 

После входа в процедуру в bp заносится базовый адрес стекового кадра. Благодаря этому, к параметрам можно получить доступ косвенно-регистровой адресацией через bp с положительным смещением [bp+disp], а к локальным переменным - с отрицательным смещением [bp-disp].

Поддержка возможности рекурсивного вызова

Рекурсивный вызов (когда ПП вызывает сама себя) иногда бывает полезен. Классический пример - вычисление факториала с использованием соотношения

N ! = N * (N-1) !

Другой пример - решение задачи о "Ханойской башне".

Рекурсия при вызове подпрограммы может быть как прямая (ПП вызывает сама себя) так и цепная (подпрограмма A вызывает подпрограмму B, та, в свою очеред,ь вызывает подпрограмму C, а затем подпрограмма C вызывает подпрограмму A).

Проблем при рекурсивном вызове несколько:

  1. Запоминание адресов возврата: каждый экземпляр ПП имеет свой адрес возврата;
  2. Каждый экземпляр ПП должен иметь собственные наборы внутренних переменных;
  3. Иногда требуется, чтобы экземпляры ПП, вызванные позднее, имели доступ к внутренним переменным ранее вызванных экземпляров.

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

push           par1
push           par2
call              subr

Вначале ПП применяется, как мы уже видели, типовая последовательность команд:

subr:           push      bp; запоминание старого значения регистра базы bp
mov        bp,sp;             передача в bp адреса начала стекового кадра

Теперь можно к локальным переменным ПП обращаться, используя адресацию относительно регистра базы:

mov          ax,[bp+4]
mov          bx,[bp+6]

Для обеспечения возможности использовать параметры и переменные "внешних" экземпляров ПП при рекурсивном вызове стековый кадр должен содержать ссылки на "предыдущие" стековые кадры. В системе команд процессора могут быть команды, облегчающие формирование стекового кадра. Например, в х86 есть команды ENTER и LEAVE:

enter frmsiz, level

Первый параметр команды определяет количество байтов, требуемых в стеке для временных (локальных) переменных. Второй параметр показывает уровень "рекурсивности" (для самого внешнего уровня level=1). Для доступа к элементам стекового кадра используется адресный регистр BP, который указывает на начало (самый старший адрес) стекового кадра.

Структура стекового кадра в х86 включает три компонента (см. рисунок ниже):

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

 

Выполнение команды ENTER:

При выполнении команды делаются следующие действия (описание делается в предположении, что происходит рекурсивный вход в подпрограмму):

  1. TmpREG = SP; Запомнить адрес начала следующего стекового кадра
  2. BP -> в стек; Создание динамической связи
  3. Повторить (level-1) раз:; Создание индикатора BP -=2, [BP] -> в стек; Копирование элементов индикатора из предыдущего
    кадра
  4. TmpREG -> в стек; Добавление в индикатор ссылки на данный кадр
  5. BP = TmpREG; Установить BP на начало создаваемого кадра
  6. SP -= frmsiz; Выделение в стеке блока под временные переменные

Подкатегории

Яндекс.Метрика