Почему бы не использовать POPF для восстановления состояния флага прерывания?
Мой вопрос не о том, что 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, а не просто восстанавливать предыдущий регистр FLAGS с помощью POPF?
1 ответ
Код, на который вы смотрите, имеет свои достоинства. Вероятно, человек, который написал это, знает, что 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
В этих двух случаях IF был фактически изменен, и ожидается, что POPF восстановит IF до его предыдущего значения. Если вы просматриваете первую диаграмму, я обвел N. Это те случаи, когда вы находитесь в защищенном режиме, режиме совместимости или 64-битном режиме, и текущий уровень привилегий (CPL) равен 1,2,3, а IOPL (уровень привилегий IO)
Поскольку в этом нет ошибки, ядро не имеет ни малейшего представления о том, что была попытка изменить IF, поэтому у него нет шансов виртуализировать IF. STI и CLI действуют как привилегированные инструкции, когда они не имеют надлежащих привилегий IOPL, и будут отказывать ядру, где IF может виртуализироваться процессором.
На вопрос, почему в исходном коде делается явная STI/CLI? STI/CLI гарантирует ошибку, которую ядро может перехватить в тех случаях, когда у вас нет надлежащих привилегий IOPL для обновления IF. POPF может позволить IF стать не синхронизированным с понятием того, что программа думает, что флаг должен быть. Используя STI и CLI для изменения IF, вы позволяете ядру легче поддерживать синхронизацию.
Спецификация DPMI (интерфейс защищенного режима DOS) обсуждает эту проблему и описывается следующим образом:
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 часов.