我的问题不是关于 BX 用作返回值,而不是将其放置在全局内存位置或堆栈中的事实。我观察到此代码最近在评论中发布。该代码适用于使用BIOS的实模式鼠标处理程序。保存/恢复FLAGS寄存器状态的两个小功能如下:
EFLAGS_IF equ 0x200 ; Bit mask for IF flag in FLAGS register
; Function: save_if_flag
; save the current state of the Interrupt Flag (IF)
;
; Inputs: None
; Returns: BX = 0x200 if interrupt flag is set, 0 otherwise
save_if_flag:
pushf
pop bx ; Get FLAGS into BX
and bx, EFLAGS_IF ; BX=0 if IF is clear, BX=0x200 if set
ret
; Function: restore_if_flag
; restore Interrupt Flag (IF) state
;
; Inputs: BX = save Interrupt Flag state
; Clobbers: None
; Returns: EFLAGS IF flag restored
restore_if_flag:
test bx, bx ; Is saved Interrupt Flag zero?
jz .if_off ; If zero, then disable interrupts & finish
sti ; Otherwise enable interrupts
ret ; We're finished
.if_off:
cli ; Disable interrupts
ret
我想了解为什么restore_if_flag
函数会这样做:
restore_if_flag:
test bx, bx ; Is saved Interrupt Flag zero?
jz .if_off ; If zero, then disable interrupts & finish
sti ; Otherwise enable interrupts
ret ; We're finished
.if_off:
cli ; Disable interrupts
ret
而不是像这样简单地使用POPF
:
restore_if_flag:
push bx
popf
ret
为什么使用 STI / CLI 显式保存/恢复中断标志,而不是简单地使用 POPF 恢复先前的FLAGS寄存器?
答案 0 :(得分:3)
您正在查看的代码很有用。撰写此内容的人很可能知道POPF
在所有不同的操作模式下对中断标志( IF )的处理方式都不相同。
他们可能会尝试避免以下两种代码模式:
sti
pushf ; Save flags including interrupts (IF)
cli
; Do work here with interrupts off
popf ; Restore interrupts (re-enable IF)
; Interrupts may still be off at this point depending on mode and IO Privileges
或
cli
pushf ; Save flags including interrupts (IF)
sti
; Do work here with interrupts on
popf ; Restore interrupts to previous state
; Interrupts may still be on at this point depending on mode and IO privileges
这2种情况是 IF 实际发生了更改,并且预计 POPF 会将 IF 恢复到以前的值。如果您查看第一个图表,我已经圈了 N 。在这些情况下,您处于保护模式,兼容模式或64位模式,并且当前特权级别(CPL)为1,2,3,并且IOPL(IO特权级别)
因为没有故障,内核不知道有尝试更改 IF 的想法,因此没有机会虚拟化 IF 。当 STI 和 CLI 没有适当的IOPL特权时,它们将作为特权指令使用,并且会出错,从而使 IF 可以被虚拟化。 CPU。
为什么要在原始代码中进行明确的 STI / CLI ? STI / CLI 将保证在您没有适当的IOPL特权来更新 IF 的情况下内核可以拦截的错误。 POPF 可能允许 IF 与程序认为标志应为的概念不同步。通过使用 STI 和 CLI 更改 IF ,您可以使内核更轻松地使事物保持同步。
DPMI(DOS保护模式接口)specification讨论了此问题,并对此进行了描述:
2.3中断标志管理 popf和iret指令可能不会修改中断标志的状态 因为大多数DPMI实现都将以IOPL
这意味着以下代码序列将使中断保持禁用:
; ; (Assume interrupts are enabled at this point) ; pushf cli . . popf ; Interrupts are still OFF!
请注意,由于DPMI的某些实现将维护虚拟中断 保护模式DOS程序的状态,中断标志的当前值 可能无法反映当前的虚拟中断状态。保护模式程序 应该使用虚拟中断状态服务来确定当前 中断标志状态(请参阅第99页)。
由于cli和sti是特权指令,它们将引起保护 违反,DPMI提供程序将模拟指令。因为 处理异常所涉及的开销,应使用cli和sti作为 尽可能少。一般而言,您应该遵循以下任一说明 至少需要300个时钟。