操作碼 |
指令 |
說明 |
CC |
INT 3 |
中斷 3 - 偵錯程式陷阱 |
CD ib |
INT imm8 |
立即數位元組指定的中斷向量編號 |
CE |
INTO |
中斷 4 - 如果上溢標誌是 1 |
INT n 指令產生對目標運算元指定的中斷或異常處理程式的呼叫(請參閱“IA-32 英特爾(R) 體系結構軟件開發人員手冊”第 1 卷第 6 章中標題為“中斷與異常”的部分)。目標運算元指定從 0 到 255 的中斷向量編號,編碼形式是 8 位無符號立即數。每個中斷向量編號提供一個指向 IDT 中的門描述符的索引。頭 32 箇中斷向量編號由英特爾(R) 保留供系統使用。其中一些中斷用於內部產生的異常。
INT n 指令是通用助記符,用於執行軟體產生的中斷處理程式呼叫。INTO 指令是專用助記符,用於呼叫上溢異常 (#OF),即中斷向量編號 4。上溢中斷檢查 EFLAGS 暫存器中的 OF 標誌,如果 OF 標誌設定為 1,則呼叫上溢中斷處理程式。
INT 3 指令產生特殊的單位元組操作碼 (CC),用於呼叫除錯異常處理程式。(這個單位元組形式可用於將任何指令的第一個位元組替換成斷點或其它單位元組指令,而不會覆蓋其它程式碼,因此非常有用)。為進一步支援它作為除錯斷點的功能,CC 操作碼產生的中斷同常規的軟體中斷還存在以下不同:
在 VME 模式中,不會發生中斷重定向;中斷由保護模式處理程式處理。不發生虛 8086 模式 IOPL 檢查。中斷可以在任何 IOPL 級別執行,而不觸發錯誤。
請注意,INT 3 的“普通”雙位元組操作碼 (CD03) 沒有這些特殊功能。英特爾(R) 與 Microsoft* 彙編器不會從任何助記符產生 CD03 操作碼,但是此操作碼可以使用直接的數值程式碼定義或自我修改程式碼建立。
INT n 指令(包括 INTO 與 INT 3 指令)的操作與使用 CALL 指令進行的遠呼叫類似。主要差異在於,使用 INT n 指令時,會將 EFLAGS 暫存器壓入堆疊,放在返回地址的前面。(返回地址是 CS 與 EIP 暫存器的當前值組成的遠地址)。從中斷過程的返回使用 IRET 指令處理,它從堆疊彈出 EFLAGS 資訊與返回地址。
中斷向量編號指定中斷描述符表格(IDT)中的中斷描述符;也就是說,它提供對 IDT 的索引。所選的中斷描述符又包含指向中斷或異常處理過程的指針。在保護模式中,IDT 包含一個陣列的 8 位元組描述符,每個都是一個中斷門、陷阱門或任務門。在實地址模式中,IDT 是一個陣列的 4 位元組遠指針(2 位元組程式碼段選擇器與 2 位元組指令指針),每個都直接指向所選段中的過程。(請注意,在實地址模式中,IDT 稱為中斷向量表格,它的指針稱為中斷向量)。
以下決策表指示在表格的上半部分給定的條件下,執行表格下半部分中的哪項操作。決策表下半部分中的每個 Y 代表此指令(#GP 除外)的“操作”部分定義的過程。
PE |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
VM |
- |
- |
- |
- |
- |
0 |
1 |
1 |
IOPL |
- |
- |
- |
- |
- |
- |
<3 |
=3 |
DPL/CPL 關係 |
- |
DPL< CPL |
- |
DPL> CPL |
DPL= CPL 或 C |
DPL< CPL 與 NC |
- |
- |
中斷型別 |
- |
S/W |
- |
- |
- |
- |
- |
- |
門型別 |
- |
- |
任務 |
陷阱或中斷 |
陷阱或中斷 |
陷阱或中斷 |
陷阱或中斷 |
陷阱或中斷 |
實地址模式 |
Y |
|
|
|
|
|
|
|
保護模式 |
|
Y |
Y |
Y |
Y |
Y |
Y |
Y |
陷阱或中斷門 |
|
|
|
Y |
Y |
Y |
Y |
Y |
特權級別間中斷 |
|
|
|
|
|
Y |
|
|
特權級別內中斷 |
|
|
|
|
Y |
|
|
|
虛 8086 模式中斷 |
|
|
|
|
|
|
|
Y |
任務門 |
|
|
Y |
|
|
|
|
|
#GP |
|
Y |
|
Y |
|
|
Y |
|
備註:- 表示無意義;Y 表示執行操作;空白表示不執行操作。
處理器在虛 8086 模式中執行時,IOPL 確定 INT n 指令的操作。如果 IOPL 小於 3,則處理器會產生一般保護性異常 (#GP);如果 IOPL 是 3,則處理器執行特權級別 0 的保護模式中斷。要執行特權級別 0 的保護模式中斷,中斷門的 DPL 必須設定為三,且中斷處理過程的目標 CPL 必須是 0。
中斷描述符表格暫存器 (IDTR) 指定 IDT 的線性基址與限制。處理器加電或復位之後,IDTR 的初始基址值是 0。
以下操作說明不僅適用於 INT n 與 INTO 指令,也適用於外部中斷與異常。
IF PE=0
THEN
GOTO REAL-ADDRESS-MODE;
ELSE (* PE=1 *)
IF (VM=1 AND IOPL < 3 AND INT n)
THEN
#GP(0);
ELSE (* protected mode or virtual-8086 mode interrupt *)
GOTO PROTECTED-MODE;
FI;
FI;
REAL-ADDRESS-MODE:
IF ((DEST * 4) + 3) is not within IDT limit THEN #GP; FI;
IF stack not large enough for a 6-byte return information THEN #SS; FI;
Push (EFLAGS[15:0]);
IF 0; (* Clear interrupt flag *)
TF 0; (* Clear trap flag *)
AC 0; (*Clear AC flag*)
Push(CS);
Push(IP);
(* No error codes are pushed *)
CS IDT(Descriptor (vector_number * 4), selector));
EIP IDT(Descriptor (vector_number * 4), offset)); (* 16 bit offset AND 0000FFFFH *)
END;
PROTECTED-MODE:
IF ((DEST * 8) + 7) is not within IDT limits
OR selected IDT descriptor is not an interrupt-, trap-, or task-gate type
THEN #GP((DEST * 8) + 2 + EXT);
(* EXT is bit 0 in error code *)
FI;
IF software interrupt (* generated by INT n, INT 3, or INTO *)
THEN
IF gate descriptor DPL < CPL
THEN #GP((vector_number * 8) + 2 );
(* PE=1, DPL<CPL, software interrupt *)
FI;
FI;
IF gate not present THEN #NP((vector_number * 8) + 2 + EXT); FI;
IF task gate (* specified in the selected interrupt table descriptor *)
THEN GOTO TASK-GATE;
ELSE GOTO TRAP-OR-INTERRUPT-GATE; (* PE=1, trap/interrupt gate *)
FI;
END;
TASK-GATE: (* PE=1, task gate *)
Read segment selector in task gate (IDT descriptor);
IF local/global bit is set to local
OR index not within GDT limits
THEN #GP(TSS selector);
FI;
Access TSS descriptor in GDT;
IF TSS descriptor specifies that the TSS is busy (low-order 5 bits set to 00001)
THEN #GP(TSS selector);
FI;
IF TSS not present
THEN #NP(TSS selector);
FI;
SWITCH-TASKS (with nesting) to TSS;
IF interrupt caused by fault with error code
THEN
IF stack limit does not allow push of error code
THEN #SS(0);
FI;
Push(error code);
FI;
IF EIP not within code segment limit
THEN #GP(0);
FI;
END;
TRAP-OR-INTERRUPT-GATE
Read segment selector for trap or interrupt gate (IDT descriptor);
IF segment selector for code segment is null
THEN #GP(0H + EXT); (* null selector with EXT flag set *)
FI;
IF segment selector is not within its descriptor table limits
THEN #GP(selector + EXT);
FI;
Read trap or interrupt handler descriptor;
IF descriptor does not indicate a code segment
OR code segment descriptor DPL > CPL
THEN #GP(selector + EXT);
FI;
IF trap or interrupt gate segment is not present,
THEN #NP(selector + EXT);
FI;
IF code segment is non-conforming AND DPL < CPL
THEN IF VM=0
THEN
GOTO INTER-PRIVILEGE-LEVEL-INTERRUPT;
(* PE=1, interrupt or trap gate, nonconforming *)
(* code segment, DPL<CPL, VM=0 *)
ELSE (* VM=1 *)
IF code segment DPL 0 THEN #GP(new code segment selector); FI;
GOTO INTERRUPT-FROM-VIRTUAL-8086-MODE;
(* PE=1, interrupt or trap gate, DPL<CPL, VM=1 *)
FI;
ELSE (* PE=1, interrupt or trap gate, DPL CPL *)
IF VM=1 THEN #GP(new code segment selector); FI;
IF code segment is conforming OR code segment DPL CPL
THEN
GOTO INTRA-PRIVILEGE-LEVEL-INTERRUPT;
ELSE
#GP(CodeSegmentSelector + EXT);
(* PE=1, interrupt or trap gate, nonconforming *)
(* code segment, DPL>CPL *)
FI;
FI;
END;
INTER-PREVILEGE-LEVEL-INTERRUPT
(* PE=1, interrupt or trap gate, non-conforming code segment, DPL<CPL *)
(* Check segment selector and descriptor for stack of new privilege level in current TSS *)
IF current TSS is 32-bit TSS
THEN
TSSstackAddress (new code segment DPL * 8) + 4
IF (TSSstackAddress + 7) > TSS limit
THEN #TS(current TSS selector); FI;
NewSS TSSstackAddress + 4;
NewESP stack address;
ELSE (* TSS is 16-bit *)
TSSstackAddress (new code segment DPL * 4) + 2
IF (TSSstackAddress + 4) > TSS limit
THEN #TS(current TSS selector); FI;
NewESP TSSstackAddress;
NewSS TSSstackAddress + 2;
FI;
IF segment selector is null THEN #TS(EXT); FI;
IF segment selector index is not within its descriptor table limits
OR segment selector's RPL DPL of code segment,
THEN #TS(SS selector + EXT);
FI;
Read segment descriptor for stack segment in GDT or LDT;
IF stack segment DPL DPL of code segment,
OR stack segment does not indicate writable data segment,
THEN #TS(SS selector + EXT);
FI;
IF stack segment not present THEN #SS(SS selector+EXT); FI;
IF 32-bit gate
THEN
IF new stack does not have room for 24 bytes (error code pushed)
OR 20 bytes (no error code pushed)
THEN #SS(segment selector + EXT);
FI;
ELSE (* 16-bit gate *)
IF new stack does not have room for 12 bytes (error code pushed)
OR 10 bytes (no error code pushed);
THEN #SS(segment selector + EXT);
FI;
FI;
IF instruction pointer is not within code segment limits THEN #GP(0); FI;
SS:ESP TSS(NewSS:NewESP) (* segment descriptor information also loaded *)
IF 32-bit gate
THEN
CS:EIP Gate(CS:EIP); (* segment descriptor information also loaded *)
ELSE (* 16-bit gate *)
CS:IP Gate(CS:IP); (* segment descriptor information also loaded *)
FI;
IF 32-bit gate
THEN
Push(far pointer to old stack); (* old SS and ESP, 3 words padded to 4 *);
Push(EFLAGS);
Push(far pointer to return instruction); (* old CS and EIP, 3 words padded to 4*);
Push(ErrorCode); (* if needed, 4 bytes *)
ELSE(* 16-bit gate *)
Push(far pointer to old stack); (* old SS and SP, 2 words *);
Push(EFLAGS(15..0]);
Push(far pointer to return instruction); (* old CS and IP, 2 words *);
Push(ErrorCode); (* if needed, 2 bytes *)
FI;
CPL CodeSegmentDescriptor(DPL);
CS(RPL) CPL;
IF interrupt gate
THEN IF 0 (* interrupt flag to 0 (disabled) *); FI;
TF 0;
VM 0;
RF 0;
NT 0;
END;
INTERRUPT-FROM-VIRTUAL-8086-MODE:
(* Check segment selector and descriptor for privilege level 0 stack in current TSS *)
IF current TSS is 32-bit TSS
THEN
TSSstackAddress (new code segment DPL * 8) + 4
IF (TSSstackAddress + 7) > TSS limit
THEN #TS(current TSS selector); FI;
NewSS TSSstackAddress + 4;
NewESP stack address;
ELSE (* TSS is 16-bit *)
TSSstackAddress (new code segment DPL * 4) + 2
IF (TSSstackAddress + 4) > TSS limit
THEN #TS(current TSS selector); FI;
NewESP TSSstackAddress;
NewSS TSSstackAddress + 2;
FI;
IF segment selector is null THEN #TS(EXT); FI;
IF segment selector index is not within its descriptor table limits
OR segment selector's RPL DPL of code segment,
THEN #TS(SS selector + EXT);
FI;
Access segment descriptor for stack segment in GDT or LDT;
IF stack segment DPL DPL of code segment,
OR stack segment does not indicate writable data segment,
THEN #TS(SS selector + EXT);
FI;
IF stack segment not present THEN #SS(SS selector+EXT); FI;
IF 32-bit gate
THEN
IF new stack does not have room for 40 bytes (error code pushed)
OR 36 bytes (no error code pushed);
THEN #SS(segment selector + EXT);
FI;
ELSE (* 16-bit gate *)
IF new stack does not have room for 20 bytes (error code pushed)
OR 18 bytes (no error code pushed);
THEN #SS(segment selector + EXT);
FI;
FI;
IF instruction pointer is not within code segment limits THEN #GP(0); FI;
tempEFLAGS EFLAGS;
VM 0;
TF 0;
RF 0;
IF service through interrupt gate THEN IF 0; FI;
TempSS SS;
TempESP ESP;
SS:ESP TSS(SS0:ESP0); (* Change to level 0 stack segment *)
(* Following pushes are 16 bits for 16-bit gate and 32 bits for 32-bit gates *)
(* Segment selector pushes in 32-bit mode are padded to two words *)
Push(GS);
Push(FS);
Push(DS);
Push(ES);
Push(TempSS);
Push(TempESP);
Push(TempEFlags);
Push(CS);
Push(EIP);
GS 0; (*segment registers nullified, invalid in protected mode *)
FS 0;
DS 0;
ES 0;
CS Gate(CS);
IF OperandSize=32
THEN
EIP Gate(instruction pointer);
ELSE (* OperandSize is 16 *)
EIP Gate(instruction pointer) AND 0000FFFFH;
FI;
(* Starts execution of new routine in Protected Mode *)
END;
INTRA-PRIVILEGE-LEVEL-INTERRUPT:
(* PE=1, DPL CPL or conforming segment *)
IF 32-bit gate
THEN
IF current stack does not have room for 16 bytes (error code pushed)
OR 12 bytes (no error code pushed); THEN #SS(0);
FI;
ELSE (* 16-bit gate *)
IF current stack does not have room for 8 bytes (error code pushed)
OR 6 bytes (no error code pushed); THEN #SS(0);
FI;
IF instruction pointer not within code segment limit THEN #GP(0); FI;
IF 32-bit gate
THEN
Push (EFLAGS);
Push (far pointer to return instruction); (* 3 words padded to 4 *)
CS:EIP Gate(CS:EIP); (* segment descriptor information also loaded *)
Push (ErrorCode); (* if any *)
ELSE (* 16-bit gate *)
Push (FLAGS);
Push (far pointer to return location); (* 2 words *)
CS:IP Gate(CS:IP); (* segment descriptor information also loaded *)
Push (ErrorCode); (* if any *)
FI;
CS(RPL) CPL;
IF interrupt gate
THEN
IF 0; FI;
TF 0;
NT 0;
VM 0;
RF 0;
FI;
END;
EFLAGS 暫存器壓入堆疊。根據執行 INT 指令時處理器的操作模式,可能會清除 IF、TF、NT、AC、RF 及 VM 標誌(請參閱“操作”部分)。如果中斷使用任務門,則可能會設定或清除任何標誌,具體由新任務的 TSS 中的 EFLAGS 映像控制。
#GP(0) - 如果 IDT、中斷門、陷阱門或任務門中的中斷指針超出程式碼段限制。
#GP(選擇器) - 如果中斷門、陷阱門或任務門中的段選擇器為空。如果中斷門、陷阱門、任務門、程式碼段或 TSS 段選擇器索引超出描述符表格限制。如果中斷向量編號超出 IDT 限制。如果 IDT 描述符不是中斷描述符、陷阱描述符或任務描述符。如果中斷由 INT n、INT 3 或 INTO 指令產生,且中斷描述符、陷阱描述符或任務描述符的 DPL 小於 CPL。如果中斷門或陷阱門中的段選擇器不指向程式碼段的段描述符。如果 TSS 的段選擇器將自己的區域性/全域性位設定為區域性。如果 TSS 段描述符指出 TSS 忙或不可用。
#SS(0) - 如果將返回地址、標誌或錯誤程式碼壓入堆疊時超出堆疊段邊界,並且未發生堆疊切換。
#SS(選擇器) - 如果載入 SS 暫存器且指向的段標記為不存在。如果發生堆疊切換時,壓入的返回地址、標誌、錯誤程式碼或堆疊段指針超出新堆疊段的邊界。
#NP(選擇器) - 如果程式碼段、中斷門、陷阱門、任務門或 TSS 不存在。
#TS(選擇器) - 如果 TSS 中堆疊段選擇器的 RPL 不等於中斷門或陷阱門訪問的程式碼段的 DPL。如果 TSS 中堆疊段選擇器指向的堆疊段描述符的 DPL 不等於中斷門或陷阱門的程式碼段描述符的 DPL。如果 TSS 中的堆疊段選擇器為空。如果 TSS 的堆疊段不是可寫的數據段。如果堆疊段的段選擇器索引超出描述符表格限制。
#PF(錯誤程式碼) - 如果發生頁錯誤。
#GP - 如果記憶體運算元有效地址超出 CS、DS、ES、FS 或 GS 段限制。如果中斷向量編號超出 IDT 限制。
#SS - 如果壓入時發生堆疊限制衝突。如果壓入的返回地址、標誌或錯誤程式碼超出堆疊段的邊界。
#GP(0)(用於 INT n、INTO 或 BOUND 指令)- 如果 IOPL 小於 3 或中斷、陷阱、任務門描述符的 DPL 不等於 3。如果 IDT 或中斷、陷阱、任務門中的指令指針超出程式碼段界限。
#GP(選擇器) - 如果中斷門、陷阱門或任務門中的段選擇器為空。如果中斷門、陷阱門、任務門、程式碼段或 TSS 段選擇器索引超出描述符表格限制。如果中斷向量編號超出 IDT 限制。如果 IDT 描述符不是中斷描述符、陷阱描述符或任務描述符。如果中斷由 INT n 指令產生,且中斷描述符、陷阱描述符或任務描述符的 DPL 小於 CPL。如果中斷門或陷阱門中的段選擇器不指向程式碼段的段描述符。如果 TSS 的段選擇器將自己的區域性/全域性位設定為區域性。
#SS(選擇器) - 如果載入 SS 暫存器且指向的段標記為不存在。如果壓入的返回地址、標誌、錯誤程式碼、堆疊段指針或數據段超出堆疊段的邊界。
#NP(選擇器) - 如果程式碼段、中斷門、陷阱門、任務門或 TSS 不存在。
#TS(選擇器) - 如果 TSS 中堆疊段選擇器的 RPL 不等於中斷門或陷阱門訪問的程式碼段的 DPL。如果 TSS 的堆疊段的堆疊段描述符的 DPL 不等於中斷門或陷阱門的程式碼段描述符的 DPL。如果 TSS 中的堆疊段選擇器為空。如果 TSS 的堆疊段不是可寫的數據段。如果堆疊段的段選擇器索引超出描述符表格限制。
#PF(錯誤程式碼) - 如果發生頁錯誤。
#BP - 如果執行 INT 3 指令。
#OF - 如果執行 INTO 指令,且 OF 標誌設定為 1。