操作碼 |
指令 |
說明 |
8F /0 |
POP m16 |
將棧頂彈入 m16;堆疊指針遞增 |
8F /0 |
POP m32 |
將棧頂彈入 m32;堆疊指針遞增 |
58+ rw |
POP r16 |
將棧頂彈入 m16;堆疊指針遞增 |
58+ rd |
POP r32 |
將棧頂彈入 m32;堆疊指針遞增 |
1F |
POP DS |
將棧頂彈入 DS;堆疊指針遞增 |
07 |
POP ES |
將棧頂彈入 ES;堆疊指針遞增 |
17 |
POP SS |
將棧頂彈入 SS;堆疊指針遞增 |
0F A1 |
POP FS |
將棧頂彈入 FS;堆疊指針遞增 |
0F A9 |
POP GS |
將棧頂彈入 GS;堆疊指針遞增 |
將棧頂的值載入到目標運算元指定的位置,然後遞增堆疊指針。目標運算元可以是通用暫存器、記憶體位置或段暫存器。
堆疊段的地址大小屬性確定堆疊指針大小(16 位或 32 位 - 源地址大小),目前程式碼段的運算元大小屬性確定堆疊指針的遞增量(2 位元組或 4 位元組)。例如,如果這些地址大小屬性與運算元大小屬性為 32,則 32 位 ESP 暫存器(堆疊指針)遞增 4,如果這些屬性為 16,則 16 位 SP 暫存器遞增 2。(堆疊段的段描述符中的 B 標誌確定堆疊的地址大小屬性,目前程式碼段的段描述符中的 D 標誌與字首確定目標運算元的運算元大小屬性與地址大小屬性)。
如果目標運算元是段暫存器 DS、ES、FS、GS 或 SS 之一,則載入到暫存器的值必須是有效的段選擇器。在保護模式中,將段選擇器彈入段暫存器時,會導致將該段選擇器關聯的段描述符資訊自動載入到段暫存器的隱藏(陰影)部分,並且會使選擇器與描述符資訊生效(請參閱下面的“操作”部分)。
空值 (0000-0003) 可以彈入 DS、ES、FS 或 GS 暫存器,且不會導致一般保護性錯誤。不過,隨後試圖對段暫存器中載入了空值的段進行任何引用時,都將導致一般保護性異常 (#GP)。在這種情況下,不會發生任何記憶體引用,並且段暫存器儲存的值為空。
POP 指令無法將值彈入 CS 暫存器。要將堆疊中的值載入到 CS 暫存器,請使用 RET 指令。
如果將 ESP 暫存器用作基址暫存器,以便在記憶體中確定目標運算元的地址,則 POP 指令會在遞增 ESP 暫存器之後,計算運算元的有效地址。對於 16 位堆疊的情況,如果執行 POP 指令之後,ESP 通過舍位變成 0h,則產生的記憶體寫入位置取決於具體的處理器系列。
POP ESP 指令會在舊棧頂的數據寫入目標運算元之前,遞增堆疊指針 (ESP)。
POP SS 指令會抑制所有中斷(包括 NMI 中斷),直到執行下一條指令之後。此操作可以確保依次執行 POP SS 與 MOV ESP, EBP 指令,而不會在中斷期間遇到失效的堆疊。不過,使用 LSS 指令才是載入 SS 與 ESP 暫存器的首選方法。
IF StackAddrSize 32
THEN
IF OperandSize 32
THEN
DEST SS:ESP; (* copy a doubleword *)
ESP ESP + 4;
ELSE (* OperandSize 16*)
DEST SS:ESP; (* copy a word *)
ESP ESP + 2;
FI;
ELSE (* StackAddrSize 16* )
IF OperandSize 16
THEN
DEST SS:SP; (* copy a word *)
SP SP + 2;
ELSE (* OperandSize 32 *)
DEST SS:SP; (* copy a doubleword *)
SP SP + 4;
FI;
FI;
在保護模式中,載入段暫存器會導致執行特殊的檢查與操作,具體如下所述。這些檢查的對象是段選擇器及其指向的段描述符。
IF SS is loaded;
THEN
IF segment selector is null
THEN #GP(0);
FI;
IF segment selector index is outside descriptor table limits
OR segment selector's RPL CPL
OR segment is not a writable data segment
OR DPL CPL
THEN #GP(selector);
FI;
IF segment not marked present
THEN #SS(selector);
ELSE
SS segment selector;
SS segment descriptor;
FI;
FI;
IF DS, ES, FS, or GS is loaded with non-null selector;
THEN
IF segment selector index is outside descriptor table limits
OR segment is not a data or readable code segment
OR ((segment is a data or nonconforming code segment)
AND (both RPL and CPL > DPL))
THEN #GP(selector);
IF segment not marked present
THEN #NP(selector);
ELSE
SegmentRegister segment selector;
SegmentRegister segment descriptor;
FI;
FI;
IF DS, ES, FS, or GS is loaded with a null selector;
THEN
SegmentRegister segment selector;
SegmentRegister segment descriptor;
FI;
無。
#GP(0) - 如果試圖將空的段選擇器載入到 SS 暫存器。如果目標運算元位於不可寫的段。如果記憶體運算元有效地址超出 CS、DS、ES、FS 或 GS 段限制。如果 DS、ES、FS、或 GS 暫存器用於訪問記憶體,並且它包含空的段選擇器。
#GP(選擇器) - 如果段選擇器索引超出描述符表格限制。如果載入 SS 暫存器,並且段選擇器的 RPL 與段描述符的 DPL 不等於 CPL。如果載入 SS 暫存器,且指向的段不是可寫的數據段。如果載入 DS、ES、FS 或 GS 暫存器,且指向的段不是數據段或可讀程式碼段。如果載入 DS、ES、FS 或 GS 暫存器,且指向的段是數據段或非相容程式碼段,但 RPL 與 CPL 都大於 DPL。
#SS(0) - 如果目前棧頂不在堆疊段內。如果記憶體運算元有效地址超出 SS 段限制。
#SS(選擇器) - 如果載入 SS 暫存器且指向的段標記為不存在。
#NP - 如果載入 DS、ES、FS 或 GS 暫存器且指向的段標記為不存在。
#PF(錯誤程式碼) - 如果發生頁錯誤。
#AC(0) - 如果在目前特權級別為 3 且啟用對齊檢查的情況下進行未對齊的記憶體引用。
#GP - 如果記憶體運算元有效地址超出 CS、DS、ES、FS 或 GS 段限制。
#GP(0) - 如果記憶體運算元有效地址超出 CS、DS、ES、FS 或 GS 段限制。
#PF(錯誤程式碼) - 如果發生頁錯誤。
#AC(0) - 如果在啟用對齊檢查的情況下進行未對齊的記憶體引用。