23

从我读过的内容来看,似乎有 9 个不同的标志。是否可以直接读取/更改它们?我知道我可以知道例如在执行 cmp/jmp 指令后是否设置了零标志,但我问是否可以执行类似的操作

mov eax, flags

或者其他的东西。

另外,对于写作,是否可以手动设置它们?

4

6 回答 6

34

可以使用特定指令直接设置或清除某些标志:

  • CLCSTCCMC:清除、设置和补充进位标志
  • CLISTI:清除并设置中断标志(应该以原子方式完成)
  • CLDSTD:清除和设置方向标志

对于符号、零、辅助进位、奇偶校验和进位标志的读写,您可以使用LAHF将低 8 位(这 5 个标志加上 3 个不确定位)加载到 AH 寄存器中,您可以使用SAHF来存储这些来自 AH 的值返回到标志寄存器。

您也可以使用PUSHF指令将标志压入堆栈,在堆栈上读取和修改它们,然后使用POPF 1指令将它们存储回标志寄存器。

请注意,您不能使用 POPF 设置 VM 和 RF 标志——它们保留它们以前的值。同样,您只能在特权级别 0 执行时更改 I/O 特权级别,而中断标志只能在特权级别至少与 I/O 特权级别相同时更改。


脚注1:

请注意,这popf在现代 CPU 上相当慢;请参阅Agner Fog 的优化指南和说明表。它是微编码的,因为在内核模式下它能够更改 IF 和 AC,以及 IO 特权级别。无论当前 CPU 上的模式如何,我们都会受到惩罚,因为解码器对模式不敏感。

如果可能的话,使用 lahf/sahf 而不是 pushf/popf 来提高性能,或者保存一个你关心的标志,setc al然后稍后add al, 255设置CF = (AL!=0)。或setnc al/sub al, 1或其他。基于 0 或 1 寄存器设置或清除 SF 或 OF 的序列也很简单,有/没有反转标志。

于 2009-09-14T04:05:54.570 回答
8

您可以使用 pushf 和 popf 指令将标志推送到堆栈上,您可以修改它们,然后将它们弹出。

于 2009-09-10T18:05:55.093 回答
7

如果您只需要标志寄存器的低字节(包含 SF、ZF、AF、PF、CF),那么有一个奇怪但方便的指令 LAHF(哈哈),它将标志寄存器的低 8 位加载到AH 及其对应的 SAHF 将 AH 存储到标志中。

特别是对于进位标志,x86 提供了 CLC、STC 和 CMC,分别用于清除、设置和补充进位标志。

于 2009-09-14T03:46:06.163 回答
2

SETcc

该指令系列是观察某些标志/标志组合的另一种方式。

它根据各个 FLAGS 设置字节的值。

例如,对于CF

stc
setc al
; al == 1

clc
setc al
; al == 0

带有断言的可运行 GitHub 上游

JCC

该指令系列当然是某些标志的另一种可能性,可用于实现SETcc

jc set
mov al, 0
jmp end
set:
mov al, 1
end:

带有断言的可运行 GitHub 上游

于 2015-06-20T09:48:49.450 回答
1
  • LAHF:将状态标志加载到 AH
  • 复制 EFLAGS 寄存器的低字节,包括符号、零和进位标志。
  • 将标志的副本保存在变量中以便妥善保管

    .data
     saveflags BYTE ? 
    .code 
     lahf ; load flags into AH 
     mov saveflags,ah ; save them into a variable 
    
  • SAHF:将 AH 存储到状态标志中

  • 将 AH 复制到 EFLAGS 寄存器的低字节
  • 检索之前存储的标志值

    .code
     mov ah, saveflags ; load save flags into AH 
     sahf ; copy into flags register 
    
于 2012-11-21T12:31:16.877 回答
1

最简单的方法是使用pushf/popf

如果您想搬入eflags,请eax使用下面的代码。

pushf                  # push eflags into stack
pop %eax               # pop it into %eax
于 2019-01-23T11:54:52.710 回答