从我读过的内容来看,似乎有 9 个不同的标志。是否可以直接读取/更改它们?我知道我可以知道例如在执行 cmp/jmp 指令后是否设置了零标志,但我问是否可以执行类似的操作
mov eax, flags
或者其他的东西。
另外,对于写作,是否可以手动设置它们?
可以使用特定指令直接设置或清除某些标志:
对于符号、零、辅助进位、奇偶校验和进位标志的读写,您可以使用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 的序列也很简单,有/没有反转标志。
您可以使用 pushf 和 popf 指令将标志推送到堆栈上,您可以修改它们,然后将它们弹出。
如果您只需要标志寄存器的低字节(包含 SF、ZF、AF、PF、CF),那么有一个奇怪但方便的指令 LAHF(哈哈),它将标志寄存器的低 8 位加载到AH 及其对应的 SAHF 将 AH 存储到标志中。
特别是对于进位标志,x86 提供了 CLC、STC 和 CMC,分别用于清除、设置和补充进位标志。
SETcc
该指令系列是观察某些标志/标志组合的另一种方式。
它根据各个 FLAGS 设置字节的值。
例如,对于CF
:
stc
setc al
; al == 1
clc
setc al
; al == 0
JCC
该指令系列当然是某些标志的另一种可能性,可用于实现SETcc
:
jc set
mov al, 0
jmp end
set:
mov al, 1
end:
将标志的副本保存在变量中以便妥善保管
.data
saveflags BYTE ?
.code
lahf ; load flags into AH
mov saveflags,ah ; save them into a variable
SAHF:将 AH 存储到状态标志中
检索之前存储的标志值
.code
mov ah, saveflags ; load save flags into AH
sahf ; copy into flags register
最简单的方法是使用pushf/popf。
如果您想搬入eflags
,请eax
使用下面的代码。
pushf # push eflags into stack
pop %eax # pop it into %eax