logo

Система команд x86
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

CALL

Вызов процедуры

Влияние команды на флаги и форматы команды:

OF

DF

IF

TF

SF

ZF

AF

PF

CF

                 

 

Код

Команда

Описание

Проц.

E8 cw

CALL rel16

Ближний вызов, 16-битное смещение указывается отноcительно следующей команды

8086

E8 cd

CALL rel32

Ближний вызов, 32-битное смещение указывается отноcительно следующей команды

Intel386

FF /2

CALL r/m16

Ближний вызов,  2-байтный адрес указывается в r/m16

8086

FF /2

CALL r/m32

Ближний вызов,  4-байтный адрес указывается в r/m32

Intel386

9A cd

CALL ptr16:16

Дальний вызов, указывается 4-байтный непосредственный адрес

8086

9A cp

CALL ptr16:32

Дальний вызов, указывается 6-байтный непосредственный адрес

Intel386

FF /3

CALL m16:16

Дальний вызов по адресу, указанному в операнде в памяти

8086

FF /3

CALL m16:32

Дальний вызов по адресу, указанному в операнде в памяти

Intel386

Операция:

IF (Вызов с операндами типа rel16 или rel32)

THEN (* Ближний относительный вызов *)

   IF Указатель команды выходит за границу кодового сегмента THEN #GP(0); FI;

   IF OperandSize = 16

   THEN

      IF В стеке нет свободных 2-х байт THEN #SS(0); FI;

      Push(IP);

      EIP = (EIP + rel16) AND 0000FFFFh;

   ELSE (* OperandSize = 32 *)

      IF В стеке нет свободных 4-х байт THEN #SS(0); FI;

      Push(EIP);

      EIP = EIP + rel32;

   FI;

FI;

IF (Вызов с операндами типа r/m16 или r/m32)

THEN (* Ближний абсолютный вызов *)

   IF Указатель команды выходит за границу кодового сегмента THEN #GP(0); FI;

   IF OperandSize = 16

   THEN

      IF В стеке нет свободных 2-х байт THEN #SS(0); FI;

      Push(IP);

      EIP = [r/m16] AND 0000FFFFh;

   ELSE (* OperandSize = 32 *)

      IF В стеке нет свободных 4-х байт THEN #SS(0); FI;

      Push(EIP);

      EIP = [r/m32];

   FI;

FI;

 

IF ( PE = 0 OR (PE = 1 AND VM = 1) )

(* Реальный или виртуальный-8086 режим *)

      AND (команда типа far CALL)

         (* Т.е. тип операнда m16:16, m16:32, ptr16:16 или ptr16:32 *)

THEN

   IF OperandSize = 16

   THEN

      IF В стеке нет свободных 4-х байт THEN #SS(0); FI;

      IF Указатель команды выходит за границу кодового сегмента THEN #GP(0); FI;

      Push(CS);

      Push(IP); (* адрес следующей инструкции; 16 бит *)

   ELSE (* OperandSize = 32 *)

      IF В стеке нет свободных 6-ти байт THEN #SS(0); FI;

      IF Указатель команды выходит за границу кодового сегмента THEN #GP(0); FI;

      Push(CS); (* 32 бита *)

      Push(EIP); (* адрес следующей инструкции; 32 бита *)

   FI;

   IF (операнды типа m16:16 или m16:32)

   THEN (* косвенный дальний вызов *)

      IF OperandSize = 16

      THEN

         CS:IP = [m16:16];

         EIP = EIP AND 0000FFFFh; (* очищает старшие 16 бит *)

      ELSE (* OperandSize = 32 *)

         CS:EIP = [m16:32];

      FI;

   FI;

   IF (операнды типа ptr16:16 или ptr16:32)

   THEN (* прямой дальний вызов *)

      IF OperandSize = 16

      THEN

         CS:IP = ptr16:16;

         EIP = EIP AND 0000FFFFh; (* очищает старшие 16 бит *)

      ELSE (* OperandSize = 32 *)

         CS:EIP = ptr16:32;

      FI;

   FI;

FI;

 

IF (PE = 1 AND VM = 0) (* Защищенный режим, не V86 режим *)

      AND (команда типа far CALL)

THEN

   Если операнд в памяти, проверяется доступность двойного слова по эффективному адресу, #GP(0), если нарушен предел;

   Новый CS селектор должен быть не нулевым, иначе #GP(0);

   Проверяет, чтобы индекс нового CS селектора попадал в пределы таблицы дескрипторов, иначе #GP(новый CS селектор);

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

      GO TO CONFORMING-CODE-SEGMENT; (* Согласованный кодовый сегмент *)

      GO TO NONCONFORMING-CODE-SEGMENT; (* Несогласованный кодовый сегмент *)

      GO TO CALL-GATE; (* Шлюз вызова *)

      GO TO TASK-GATE; (* Шлюз задачи *)

      GO TO TASK-STATE-SEGMENT; (* TSS — сегмент состояния задачи *)

   ELSE #GP(CS селектор);

FI;

 

CONFORMING-CODE-SEGMENT:

   IF DPL > CPL THEN #GP(CS селектор); FI;

   Сегмент должен присутствовать, иначе #NP(CS селектор);

   Стек должен иметь достаточно места для адреса возврата, иначе #SS(0);

   Указатель инструкции должен быть в пределах кодового сегмента, иначе #GP(0);

   Push(CS);

   IF OperandSize = 16

      THEN

         Push(IP);

      ELSE (* OperandSize = 32 *)

         Push(EIP);

   FI;

   Загрузить дескриптор кодового сегмента в регистр CS;

   Загрузить CS с новым селектором кодового сегмента;

   СS.RPL = CPL;

   Загрузить новое смещение в EIP (с расширением нулями для OperandSize = 16);

 

NONCONFORMING-CODE-SEGMENT:

   IF RPL > CPL THEN #GP(CS селектор); FI;

   IF DPL ¹ CPL THEN #GP(CS селектор); FI;

   Сегмент должен присутствовать, иначе #NP(CS селектор);

   Стек должен иметь достаточно места для адреса возврата, иначе #SS(0);

   Указатель инструкции должен быть в пределах кодового сегмента, иначе #GP(0);

   IF OperandSize = 16

      THEN

         Push(IP);

      ELSE (* OperandSize = 32 *)

         Push(EIP);

   FI;

   Загрузить дескриптор кодового сегмента в регистр CS;

   Загрузить CS с новым селектором кодового сегмента;

   СS.RPL = CPL;

   Загрузить новое смещение в EIP (с расширением нулями для OperandSize = 16);

 

CALL-GATE:

   DPL ³ CPL, иначе #GP(селектор шлюза вызова);

   DPL ³ RPL, иначе #GP(селектор шлюза вызова);

   Шлюз вызова должен присутствовать, иначе #NP(селектор шлюза вызова);

   Проверка селектора кодового сегмента в дескрипторе шлюза вызова и выбираемого им дескриптора:

      Селектор должен быть не нулевым, иначе #GP(0);

      Индекс селектора должен попадать в пределы таблицы дескрипторов, иначе #GP(CS селектор);

      AR байт выбранного дескриптора задает кодовый сегмент, иначе #GP(CS селектор);

      DPL выбранного дескриптора должен быть £ CPL, иначе #GP(CS селектор);

      Выбранный кодовый сегмент должен присутствовать, иначе #NP(CS селектор);

      IF (несогласованный кодовый сегмент) AND (DPL < CPL)

         THEN GO TO MORE-PRIVILEGE ELSE GO TO SAME-PRIVILEGE FI;

 

MORE-PRIVILEGE:

   Получить новый SS селектор для нового уровня привилегий из TSS;

   Проверка селектора и дескриптора для нового SS:

      Селектор должен быть не нулевым, иначе #TS(0);

      Индекс селектора должен попадать в пределы таблицы дескрипторов, иначе #TS(SS селектор);

      RPL селектора должен быть равен DPL кодового сегмента, иначе #TS(SS селектор);

      DPL стекового сегмента должен быть равен DPL кодового сегмента, иначе #TS(SS селектор);

      AR байт дескриптора должен задавать разрешенный для записи сегмент данных, иначе #TS(SS селектор);

      Сегмент должен присутствовать, иначе #SS(SS селектор);

      IF 32-битный шлюз вызова

      THEN

         Новый стек должен иметь место для параметров плюс 16 байт, иначе #SS(SS селектор);

         EIP должен быть в пределах кодового сегмента, иначе #GP(0);

         Загрузить новое значение SS:eSP из TSS;

         Загрузить новое значение CS:EIP из шлюза;

      ELSE (* 16-битный шлюз *)

         Новый стек должен иметь место для параметров плюс 8 байт, иначе #SS(SS селектор);

         IP должен быть в пределах кодового сегмента, иначе #GP(0);

         Загрузить новое значение SS:eSP из TSS;

         Загрузить новое значение CS:IP из шлюза;

      FI;

      Загрузить дескриптор CS;

      Загрузить дескриптор SS;

      Поместить длинный указатель старого стека в новый стек;

      Получить счетчик двойных слов из шлюза вызова, замаскировав 5 бит;

      Скопировать параметры из старого стека в новый стек;

      Поместить адрес возврата в новый стек;

      Установить CPL в значение DPL кодового сегмента;

      CS.RPL = CPL;

 

SAME-PRIVILEGE:

   IF 32-битный шлюз вызова

   THEN

      Стек должен иметь место для 6 байт адреса возврата (расширяемых до 8 байт), иначе #SS(0);

      EIP должен быть в пределах кодового сегмента, иначе #GP(0);

      Загрузить новое значение CS:EIP из шлюза;

   ELSE (* 16-битный шлюз *)

      Стек должен иметь место для 4 байт адреса возврата, иначе #SS(0);

      IP должен быть в пределах кодового сегмента, иначе #GP(0);

      Загрузить новое значение CS:IP из шлюза;

   FI;

      Загрузить дескриптор кодового сегмента;

      Поместить адрес возврата в стек;

      CS.RPL = CPL;

 

TASK-GATE:

   DPL шлюза задачи должен быть ³ CPL, иначе #GP(селектор шлюза);

   DPL шлюза задачи должен быть ³ RPL, иначе #GP(селектор шлюза);

   Шлюз задачи должен присутствовать, иначе #NP(селектор шлюза);

   Проверка селектора TSS в дескрипторе шлюза задачи и соответствующего дескриптора TSS:

      Селектор должен задавать GDT (бит TI=0), иначе #GP (TSS селектор);

      Индекс селектора должен попадать в пределы таблицы GDT, иначе #GP(TSS селектор);

      AR байт TSS дескриптора должен задавать свободный TSS, иначе #GP(TSS селектор);

      TSS должен присутствовать, иначе #NP(TSS селектор);

   SWITCH-TASKS (с вложением) в TSS;

   eIP должен быть в пределах кодового сегмента, иначе #GP(0);

 

TASK-STATE-SEGMENT:

   TSS DPL должен быть ³ CPL, иначе #GP(TSS селектор);

   TSS DPL должен быть ³ RPL, иначе #GP(TSS селектор);

   AR байт TSS дескриптора должен задавать свободный TSS, иначе #GP(TSS селектор);

   TSS должен присутствовать, иначе #NP(TSS селектор);

   SWITCH-TASKS (с вложением) в TSS;

   eIP должен быть в пределах кодового сегмента, иначе #GP(0);

Описание:

Команда CALL передает управление указанной операндом процедуре. После завершения процедуры (команда возврата выполняется внутри самой процедуры), выполнение продолжается с команды, следующей за командой CALL. Действие различных форм команды описано ниже.

При ближних вызовах (вызовы с операндами типа r/m16, r/m32, rel16, rel32), изменение или сохранение сегментных регистров не происходит. Формы команды CALL rel16 и CALL rel32 прибавляют знаковое смещение к адресу следующей за CALL команды, чтобы определить адрес перехода на вызываемую процедуру. Форма CALL rel16 используется с атрибутом размера операнда 16 бит, CALL rel32 — с атрибутом размера операнда 32 бита. Вычисленный таким образом адрес помещается в 32-битном регистре EIP. В случае CALL rel16 верхние 16 бит регистра EIP очищаются, приводя результат к значению, не выходящему за пределы 16 бит. Команды CALL r/m16 и CALL r/m32 задают регистр или местоположение ячейки памяти, из которых выбирается абсолютное смещение в сегменте. Смещение, выбранное из r/m, является 32 битным в случае 32-битного атрибута размера операнда (r/m32), или 16-битным для 16-битного атрибута размера операнда (r/m16). Смещение команды, следующей за командой CALL, помещается в стек. Оно будет использовано ближней командой RET для возврата из процедуры. Ни одна из этих форм команды CALL регистр CS не изменяет.

Дальние вызовы, (CALL ptr16:16 и CALL ptr16:32) используют четырех и шестибайтные операнды в качестве дальних указателей на вызываемую процедуру. Формы команды CALL m16:16 и CALL m16:32 выбирают дальний указатель из указанного местоположения в памяти (разрешены все режимы адресации). В режиме реальной адресации или в режиме V86 дальний указатель обеспечивает 16 бит для регистра CS и 16 или 32 бита для регистра EIP (в зависимости от атрибута размера операнда). Эти формы команды в качестве адреса возврата помещают в стек значения регистров CS и (E)IP.

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

  • дальний вызов на том же уровне привилегий;
  • межуровневый дальний вызов;
  • переключение задачи.

Если вызов осуществляется без переключения задачи, то флаги не изменяются. Переключение задачи вызывает перезагрузку регистра (E)FLAGS значением из TSS.

 

Стек после дальнего 16- или 32-разрядного CALL в защищенном режиме

Рис. 6.3. Стек после дальнего 16- или 32-разрядного CALL в защищенном режиме

Особые ситуации защищенного режима:

#PF(Код ошибки), страничная ошибка.
#AC(0) при невыровненной ссылке в память при текущем уровне привилегий равном 3.
Для дальних вызовов: #GP, #NP, #SS и #TS как указано в пункте "Операция".
Для ближних прямых вызовов: #GP(0), если местоположение процедуры не попадает в пределы кодового сегмента; #SS(0), если сохранение адреса возврата вызывает нарушение границ стекового сегмента.
Для ближних косвенных вызовов: #GP(0), если используется некорректный эффективный адрес операнда в памяти в сегментах CS, DS, ES, FS или GS; #SS(0) при использовании некорректного эффективного адреса в сегменте SS; #GP(0), если местоположение процедуры не попадает в пределы кодового сегмента.

Особые ситуации режима реальной адресации:

#GP, если любая часть операнда находится вне пространства эффективных адресов в сегментах CS, DS, ES, FS или GS.
#SS, если любая часть операнда находится вне пространства эффективных адресов в сегменте SS.

Особые ситуации режима V86:

Такие же, как и в режиме реальной адресации.
#PF(Код ошибки), страничная ошибка.
#AC(0) при невыровненной ссылке в память.

Замечание:

Любые вызовы из 32-битного кодового сегмента к 16-битным сегментам должны осуществляться из первых 64Кбайт адресного пространства 32-битного сегмента, так как в этом случае в стеке сохраняется только 16-битное смещение адреса возврата.

Если вам удасться придумать программу в которой реально потребуется использование команды CALL [ESP], то имейте ввиду, что она может исполняться ошибочно на некоторых микропроцессорах Intel. Обратитесь к технической документации Intel за описанием всех возможных случаев проявления ошибки.


Входит в группу команд: Базовая система команд CPU



Все права защищены © 1997-2011 Перепечатка возможна только по согласованию с владельцем авторских прав. admin@club155.ru

Яндекс.Метрика               Сервер радиолюбителей России - схемы, документация,

 соревнования, дипломы, программы, форумы и многое другое!   схемы новости электроники