Система команд x86 |
|
Программирование - Архитектура и система команд микропроцессоров x86 |
CALL
Влияние команды на флаги и форматы команды:
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 |
Описание:
Команда 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.
Рис. 6.3. Стек после дальнего 16- или 32-разрядного CALL в защищенном режиме
Операция:
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);
Особые ситуации защищенного режима:
#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 за описанием всех возможных случаев проявления ошибки.
Все права защищены © Алексей Ровдо, 1994-2023. Перепечатка возможна только по согласованию с владельцем авторских прав. admin@club155.ru