logo

Главная Система команд x86 Базовая система команд CPU INT 3

Система команд x86

Программирование - Архитектура и система команд микропроцессоров 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

INT 3

Вызов процедуры прерывания отладки 3

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

OF

DF

IF

TF

SF

ZF

AF

PF

CF

 

 

0

0

 

 

 

 

 

 

Код

Команда

Описание

Проц.

Пример

CC

INT 3

Вызов прерывания 3 (#BP, точка останова)

8086

int 3

CD ib

INT imm8

Вызов прерывания imm8

8086

int 13

CE

INTO

Вызов прерывания 4 (#OF, переполнение), если EFLAGS.OF=1

8086

into

Описание:

Команда INT 3 предназначена для генерации прерывания 3 и по описанию операции идентична команде INT n за исключением того, что номер прерывания здесь не присутствует непосредственно в коде операции, но неявно задается равным 3.

Данное прерывание предназначено для использования отладчиком, который размещает специальную однобайтную команду INT 3 (код CCh) вместо первого байта команд или вместо однобайтных команд.

Существует второй способ вызова данного прерывания с помощью двухбайтного кода INT 3 (код CD03h). Однако данный метод на практике не применяется, все ассемблеры x86 по умолчанию интерпретируют мнемонику INT 3 как однобайтную команду с кодом CCh (но это не исключает возможность ручного программирования двухбайтного кода). Помимо размера кода, отличается и процесс обработки одно- и двухбайтных команд INT 3. Прерывание, сгенерированное однобайтной командой в режиме EV86 (CR4.VME = 1), не подвергается перенаправлению по карте перенаправления прерываний (как это описано для Режима 2Режима 3Режима 5) и всегда обрабатывается обработчиком защищенного режима через дескриптор в таблице IDT. Кроме того, в режиме V86 для данного прерывания не осуществляется проверок поля IOPL и, соответственно, не может быть сгенерирована ошибка общей защиты #GP когда EFLAGS.IOPL < 3, то есть однобайтная команда не является IOPL-чувствительной.

Операция:

Представленный здесь алгоритм описывает не только поведение процессора при выполнении команды INT 3, но и при поступлении любого внешнего прерывания или генерации особой ситуации.

 

IF CR0.PE = 0

   THEN GOTO REAL-ADDRESS-MODE;

   ELSE

      IF ( EFLAGS.VM = 1 AND EFLAGS.IOPL < 3 AND

          ( INT n ) AND

          ( CR4.VME = 0 OR CR4.VME = 1 AND IRB[n] = 1 ) 

         ) (* IRB[n] - бит, соответствующий прерыванию n в карте перенаправления прерываний *)

         THEN

            #GP(0); (Программное прерывание INT n в режиме: (1) V86 при EFLAGS.IOPL < 3, (2) EV86 Режим 2 *)

         ELSE (* Защищенный режим или режим V86/EV86 *)

            IF ( EFLAGS.VM = 1 AND CR4.VME = 1 AND

                ( INT n ) AND IRB[n] = 0 )

               THEN GOTO EV86-MODE; (* EV86: Режим 3 или Режим 5 *)

               ELSE GOTO PROTECTED-MODE; (* Аппаратные прерывания, особые ситуации; программные прерывания INT n в режиме: (1) Защищенный режим, (2) V86 при EFLAGS.IOPL = 3, (3) EV86 Режим 1 или Режим 4 *)

            FI;

      FI;

FI;

END;

 

REAL-ADDRES-MODE:

IF ( (Номер прерывания * 4) + 3 выходит за пределы сегмента при обращении к таблице векторов прерываний IVT ) THEN #GP; FI;

IF ( В стеке нет места для 6 байт ) THEN #SS; FI;

Push(FLAGS);

EFLAGS.IF = 0; (* Сброс флага прерываний *)

EFLAGS.TF = 0; (* Сброс флага ловушки *)

EFLAGS.AC = 0; (* Сброс флага режима контроля выравнивания *)

Push(CS);

Push(IP);

(* В стек не заносятся коды ошибок *)

CS = IVT[Номер прерывания * 4].selector;

EIP = IVT[Номер прерывания * 4].offset AND 0x0000FFFFh; (* старшие 16-бит регистра EIP обнуляются *)

(* Продолжение работы в режиме реальной адресации ... *)

END;

 

EV86-MODE: (* CR0.PE = 1, EFLAGS.VM = 1, CR4.VME = 1, Режим EV86 - программное прерывание INT n, при IRB[n] = 0 - Режим 3 или Режим 5 *)

IF (В стеке задачи V86 нет места для 6 байт) THEN #SS(0); FI;

tempFLAGS = FLAGS;

tempFLAGS.IOPL = 0; (* см. Примечание *)

tempFLAGS.NT = 0;

IF EFLAGS.IOPL = 3 (* Режим 3 *)

   THEN EFLAGS.IF = 0; (* Сброс флага разрешения прерываний *)

   ELSE (* EFLAGS.IOPL < 3 - Режим 5 *)

      tempFLAGS.IF = EFLAGS.VIF;

      EFLAGS.VIF = 0; (* Сброс виртуального флага прерываний *)

FI;

EFLAGS.TF = 0; (* Сброс флага ловушки *)

Push(tempFLAGS);

Push(CS);

Push(IP);

(* В стек не заносятся коды ошибок *)

CS = IVT_V86[Номер прерывания * 4].selector; (* Таблица векторов прерываний IVT_V86 располагается в начале адресного пространства задачи V86 *)

EIP = IVT_V86[Номер прерывания * 4].offset AND 0x0000FFFFh; (* старшие 16-бит регистра EIP обнуляются *)

(* Продолжение работы в режиме EV86 ... *)

END;

 

PROTECTED-MODE: (* CR0.PE = 1, Аппаратные прерывания, особые ситуации; программные прерывания INT n в режиме: (1) Защищенный режим, (2) V86 при EFLAGS.IOPL = 3, (3) EV86 Режим 1 или Режим 4 *)

IF ( (Номер прерывания * 8) + 7 не попадает в пределы таблицы IDT ) THEN #GP(номер прерывания * 8 + 2 + EXT); FI;

(* Здесь и далее в параметрах кода ошибки слагаемое +2 означает установку бита IDT кода ошибки, а слагаемое +EXT - означает установку бита EXT кода ошибки в соответствии с тем, было ли вызвавшее ошибку прерывание программным EXT = 0 или внешним EXT = 1 *)

Байт AR дескриптора должен задавать шлюз прерывания, шлюз ловушки или шлюз задачи, иначе #GP(номер прерывания * 8 + 2 + EXT);

IF (Программное прерывание или особая ситуация) (* Т.е. один из случаев INT n, INT 3, INT01, BOUND или INTO *)

   THEN

      IF ( CPL > DPL шлюза )

         THEN

            #GP(номер прерывания * 8 + 2); (* CR0.PE = 1, DPL шлюза < CPL, программное прерывание *)

      FI;

FI;

Шлюз должен присутствовать, иначе #NP(номер прерывания * 8 + 2 + EXT);

IF (Шлюз задачи)

   THEN GOTO TASK-GATE;

   ELSE

      GOTO TRAP-OR-INT-GATE; (* CR0.PE = 1, шлюз прерывания или ловушки *)

FI;

END;

 

TRAP-OR-INT-GATE: (* Защищенный режим или режим V86/EV86, шлюз ловушки или прерывания *)

Проверка нового селектора CS, заданного в дескрипторе шлюза, и соответствующего ему дескриптора из LDT или GDT:

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

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

   Выбранный дескриптор должен быть дескриптором сегмента кода, иначе #GP(селектор + EXT);

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

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

   THEN

      IF EFLAGS.VM = 0

         THEN GOTO INT-TO-INTER-PRIV; (* CR0.PE = 1, EFLAGS.VM = 0, шлюз прерывания или ловушки, несогласованный кодовый сегмент, DPL сегмента кода < CPL *)

         ELSE (* EFLAGS.VM = 1 *)

            IF ( DPL нового сегмента кода ≠ 0 ) THEN #GP(Селектор кодового сегмента + EXT); FI;

            GOTO INT-FROM-V86-MODE;(* CR0.PE = 1, EFLAGS.VM = 1, шлюз прерывания или ловушки, DPL сегмента кода = 0, CPL = 3 *)

      FI;

   ELSE (* CR0.PE = 1, шлюз прерывания или ловушки, согласованный кодовый сегмент или несогласованный кодовый сегмент с DPL = CPL *)

      IF EFLAGS.VM = 1 THEN #GP(Селектор кодового сегмента + EXT); FI;

      IF ( (Кодовый сегмент согласованный) OR (DPL сегмента кода = CPL) )

         THEN GOTO INT-TO-INTRA-PRIV; (* CR0.PE = 1, шлюз прерывания или ловушки, DPL сегмента кода ≤ CPL для согласованного сегмента, DPL сегмента кода = CPL для несогласованного сегмента *)

         ELSE #GP(Селектор кодового сегмента + EXT); (* DPL > CPL для согласованного сегмента или DPL ≠ CPL для несогласованного сегмента *)

      FI;

FI;

END;

 

INT-TO-INTER-PRIV: (* Защищенный режим, шлюз прерывания или ловушки, несогласованный кодовый сегмент, DPL сегмента кода < CPL *)

IF ( Текущий TSS 32-битный )

   THEN

      TSSstackAddress = (new code segment DPL * 8) + 4

      IF ( (TSSstackAddress + 5) > Предел TSS ) (* (TSSstackAddress + 7) > Предел TSS - для некоторых моделей процессоров *)

         THEN #TS(Селектор текущего TSS + EXT);

      FI;

      NewSS = [База TSS + TSSstackAddress + 4]; (* загружается 2 байта *)

      NewESP = [База TSS + TSSstackAddress]; (* загружается 4 байта *)

   ELSE (* Текущий TSS 16-битный *)

      TSSstackAddress = (new code segment DPL * 4) + 2

      IF ( (TSSstackAddress + 3) > Предел TSS ) (* (TSSstackAddress + 4) > Предел TSS - для некоторых моделей процессоров *)

         THEN #TS(Селектор текущего TSS + EXT);

      FI;

      NewESP = [База TSS + TSSstackAddress]; (* загружается 2 байта *)

      NewSS = [База TSS + TSSstackAddress + 2]; (* загружается 2 байта *)

FI;

Проверка селектора нового стекового сегмента NewSS и соответствующего ему дескриптора из LDT или GDT:

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

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

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

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

   Дескриптор должен иметь формат дескриптора сегмента данных, разрешенного для записи (W = 1), иначе #TS(SS селектор + EXT);

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

IF (32-битный шлюз)

   THEN Новый стек должен иметь место для 20 байт (24 байт, если есть код ошибки), иначе #SS(EXT)

   ELSE Новый стек должен иметь место для 10 байт (12 байт, если есть код ошибки), иначе #SS(EXT)

FI;

Новый указатель инструкции должен попадать в пределы нового кодового сегмента, иначе #GP(EXT); (* указатель инструкции определяется значением поля OFFSET из дескриптора шлюза *)

SS:ESP = TSS(NewSS:NewESP); (* Загрузить новые значения SS и eSP из TSS *)

IF (32-битный шлюз)

   THEN CS:EIP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 32-битного шлюза *)

   ELSE CS:IP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 16-битного шлюза *)

FI;

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

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

IF (32-битный шлюз)

   THEN

      Push(Длинный указатель на старый стек - SS:ESP); (* 3 слова дополняются до 4 *)

      Push(EFLAGS);

      Push(Длинный указатель на точку возврата - CS:EIP); (* 3 слова дополняются до 4 *)

      Push(Код ошибки); (* Если присутствует, 4 байта *)

   ELSE

      Push(Длинный указатель на старый стек - SS:SP); (* 2 слова *)

      Push(FLAGS);

      Push(Длинный указатель на точку возврата - CS:IP); (* 2 слова *)

      Push(Код ошибки); (* Если присутствует, 2 байта *)

FI;

CPL = DPL нового кодового сегмента;

CS.RPL = CPL;

IF (Шлюз прерывания) THEN EFLAGS.IF = 0 FI; (* Сбросить флаг прерывания *)

EFLAGS.TF = 0;

EFLAGS.NT = 0;

EFLAGS.RF = 0;

(* Продолжение работы в защищенном режиме на уровне с большими привилегиями ... *)

END;

 

INT-FROM-V86-MODE: (* Режим V86/EV86, шлюз прерывания или ловушки, DPL = 0, CPL = 3 *)

(* Текущий TSS всегда 32-битный в режиме V86 *)

IF ( Предел TSS < 9 ) (* Предел TSS < 11 - для некоторых моделей процессоров *)

   THEN #TS(Селектор текущего TSS + EXT);

FI;

NewSS = [База TSS + 8]; (* загружается 2 байта *)

NewESP = [База TSS + 4]; (* загружается 4 байта *)

 

Проверка селектора нового стекового сегмента NewSS и соответствующего ему дескриптора из LDT или GDT:

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

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

   RPL селектора должен быть равен нулю, иначе #TS(SS селектор + EXT);

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

   Дескриптор должен иметь формат дескриптора сегмента данных, разрешенного для записи (W = 1), иначе #TS(SS селектор + EXT);

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

IF (32-битный шлюз)

   THEN

      Новый стек должен иметь место для 36 байт (40 байт, если есть код ошибки), иначе #SS(EXT)   

      Новый указатель инструкции должен попадать в пределы нового кодового сегмента, иначе #GP(EXT); (* указатель инструкции определяется значением поля OFFSET из дескриптора шлюза *)

      TempEflags = EFLAGS;

      EFLAGS.VM = 0; (* Процессор выходит из режима V86 для обработки прерывания в защищенном режиме *)

      EFLAGS.NT = 0;

      EFLAGS.TF = 0;

      EFLAGS.RF = 0;

      IF (Шлюз прерывания)

         THEN EFLAGS.IF = 0;

      FI;

      TempSS = SS;

      TempESP = ESP;

      CPL = 0; (* Переключение на нулевой уровень привилегий *)

      SS:ESP = TSS(NewSS:NewESP); (* Загрузить значения SS0 и ESP0 из TSS *)

      Push(GS); (* расширяется до двух слов *)

      Push(FS); (* расширяется до двух слов *)

      Push(DS); (* расширяется до двух слов *)

      Push(ES); (* расширяется до двух слов *)

      GS = 0; (* Сегментные регистры обнуляются. Недопустимо последующее использование нулевых селекторов в защищенном режиме *)

      FS = 0;

      DS = 0;

      ES = 0;

      Push(TempSS); (* расширяется до двух слов *)

      Push(TempESP);

      Push(TempEflags);

      Push(CS); (* расширяется до двух слов *)

      Push(EIP);

      Push(Код ошибки); (* Если присутствует, 4 байта *)

      CS:EIP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 32-битного шлюза *)

 

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

      Новый стек должен иметь место для 18 байт (20 байт, если есть код ошибки), иначе #SS(EXT)

      (* сохранение в стеке 16-битных значений регистров происходит аналогично 32-битному шлюзу *)

      (* Возврат из прерывания командой IRET обратно в режим V86 из 16-битного сегмента будет невозможен, так как флаг VM не сохранится в стеке и не восстановится из образа EFLAGS при возврате *)

FI;

(* Продолжение работы в защищенном режиме на нулевом уровне привилегий ... *)

END;

 

INT-TO-INTRA-PRIV: (* CR0.PE = 1, DPL = CPL или согласованный сегмент с DPL ≤ CPL *)

IF (32-битный шлюз)

   THEN Новый стек должен иметь место для 12 байт (16 байт, если есть код ошибки), иначе #SS(EXT)

   ELSE Новый стек должен иметь место для 6 байт (8 байт, если есть код ошибки), иначе #SS(EXT)

FI;

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

IF (32-битный шлюз)

   THEN

      Push(EFLAGS);

      Push(Длинный указатель на точку возврата); (* 3 слова дополняются до 4 *)

      CS:EIP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 32-битного шлюза *)

      Push(Код ошибки); (* Если присутствует, 4 байта *)

   ELSE

      Push(FLAGS);

      Push(Длинный указатель на точку возврата); (* 2 слова *)

      CS:IP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 16-битного шлюза *)

      Push(Код ошибки); (* Если присутствует, 2 байта *)

FI;

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

CS.RPL = CPL;

IF (Шлюз прерывания) THEN EFLAGS.IF = 0; FI;

EFLAGS.TF = 0;

EFLAGS.NT = 0;

EFLAGS.RF = 0;

(* Продолжение работы в защищенном режиме без изменения уровня привилегий ... *)

END;

 

TASK-GATE: (* CR0.PE = 1, шлюз задачи *)

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

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

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

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

   Дескриптор TSS должен иметь тип свободного TSS (TYPE.B = 0), иначе #GP(TSS селектор + EXT);

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

SWITCH-TASKS(С вложением) в TSS; (* здесь "С вложением" означает тот факт, что при инициализации контекста новой задачи будет установлен флаг EFLAGS.NT = 1, а в поле LINK ее сегмента TSS будет скопирован селектор TSS прерываемой (старой) задачи – см. Адресация и многозадачность: Средства поддержки мультизадачности *)

IF (Прерывание представляет собой особую ситуацию с кодом ошибки)

   THEN

      В стеке должно быть место для кода ошибки, иначе #SS(EXT);

      Push(код ошибки);

FI;

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

(* Загрузка контекста новой задачи сопровождается дополнительными проверками как это описано в разделе Адресация и многозадачность: Средства поддержки мультизадачности *)

(* Продолжение работы в контексте новой задачи ... *)

END;

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

Представленный здесь перечень особых ситуаций характеризует поведение процессора не только при выполнении команды INT 3, но и при поступлении любого внешнего прерывания или генерации особой ситуации. Бит EXT в коде ошибки используется для индикации внешнего по отношению к прерванной программе события (внешнего прерывания).

#GP(индекс прерывания * 8 + 2 + EXT), если:

#GP(0 + EXT), если указатель команды (OFFSET) в дескрипторе шлюза из таблицы IDT не попадает в пределы кодового сегмента.

#GP(код ошибки), если:

#TS(код ошибки), если:

#NP(код ошибки), если:

#SS(0 + EXT), если:

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

#BP, если исполняется команда INT 3.
#OF, если исполняется команда INTO и флаг EFLAGS.OF = 1.

Intel386 … :
#PF(Код ошибки) при страничной ошибке.
#UD при использовании префикса LOCK.
#DB, если исполняется команда INT01.

Intel486 … :
#AC(EXT) при невыровненной ссылке в память, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC = 1, CPL = 3).

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

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

#GP, если вектор прерывания превышает предел таблицы векторов прерываний (IVT) (предел таблицы векторов прерываний может быть задан при программировании регистра IDTR).
#SS, при нарушении границ стекового сегмента во время сохранения данных в стеке.
#BP, если исполняется команда INT 3.
#OF, если исполняется команда INTO и флаг EFLAGS.OF = 1.

Intel386 … :
#UD при использовании префикса LOCK.
#DB, если исполняется команда INT01.

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

Представленный здесь перечень особых ситуаций характеризует поведение процессора не только при выполнении команды INT 3, но и при поступлении любого внешнего прерывания или генерации особой ситуации. Бит EXT в коде ошибки используется для индикации внешнего по отношению к прерванной программе события (внешнего прерывания).

#GP(индекс прерывания * 8 + 2 + EXT), если:

#GP(0), если исполняется команда INT n когда текущий уровень привилегий ввода/вывода EFLAGS.IOPL < 3 (кроме режима EV86).

#GP(0 + EXT), если указатель команды (OFFSET) в дескрипторе шлюза из таблицы IDT не попадает в пределы кодового сегмента.

#GP(код ошибки), если:

#TS(код ошибки), если:

#NP(код ошибки), если:

#SS(0 + EXT), если:

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

#BP, если исполняется команда INT 3.
#OF, если исполняется команда INTO и флаг EFLAGS.OF = 1.

Intel386 … :
#PF(Код ошибки) при страничной ошибке.
#UD при использовании префикса LOCK.
#DB, если исполняется команда INT01.

Intel486 … :
#AC(EXT) при невыровненной ссылке в память, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC = 1, CPL = 3).

Pentium (в режиме EV86):
#GP(0), если исполняется команда INT n когда текущий уровень привилегий ввода/вывода EFLAGS.IOPL < 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 1.


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




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

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

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