Anatoly Vorobey (avva) wrote,
Anatoly Vorobey
avva

Category:

процессорные кунштюки (программистское)

Две интересных записи в веблогах старых майкрософтовцев:

The hunt for a faster syscall trap - о том, как в Windows/386 (версия, которая предшествовала Windows 3.0) переходили из V86-режима в защищённый (protected mode) при помощи ошибочной инструкции.

Faster Syscall Trap redux - о том, как OS/2 под 286-м процессором использовала ещё более извращённый трюк для возвращения из защищённого режима в реальный режим (real mode) - при помощи втройне ошибочной инструкции (triple fault)!

Я могу рассказать что-то похожее, но далеко не столь впечатляющее, в области CPU-трюков. Пробуя различные инструкции и режимы своего 386-го, году в 92-м или 93-м я обнаружил один интересный трюк (не сомневаюсь, что он был известен многим знатокам ассемблера в то время, но я его придумал сам и тем гордился).

Когда процессор 80386 работал в "реальном режиме", доступная для инструкций процессора память ограничивалась 64 килобайтами (16-битный режим); для того, чтобы использовать больше памяти, существовала система сегментных регистров и доступов через эти сегменты - система, как помнят все, кому приходилось иметь с ней дело, исключительно неудобная.

После перехода в "защищённый режим" CPU переходил к 32-битной адресации и всю память можно было видеть "напрямую". Сегментные регистры оставались (и есть в интеловских процессорах до сих пор), но, как правило, их конфигурируют так, что каждый сегмент (кода, данных, дополнительных данных итп.) указывает на всю протяжённость виртуальной памяти с нуля и до 4Gb, т.е. по сути дела сегменты не используют (есть определённые исключения из этой практики, но не буду сейчас на них останавливаться). С другой стороны, если вы обычном пользуетесь DOS-ом (а не какими-нибудь Линуксами, которые тогда были в зачаточном состоянии, или Windows 95, которого тогда просто не было), то переходить в защищённый режим довольно неудобно - DOS из его не вызвать, очень многие вещи поэтому приходится делать самому, все обычные программы и TSRы ("резидентные" утилиты, бегущие в DOSe и дополняющие его возможности) перестают работать итп. Программу, работающую с файлами, рисующую что-то итп., гораздо удобнее было писать в "реальном" режиме; но с другой стороны, это идиотское ограничение в 64k очень мешало.

У 80386 не было двух разных наборов инструкций для работы с 16-битными данными, регистрами и адресами (в "реальном" режиме или в режиме эмуляции V86) и для работы с 32-битными данными, регистрами и адресами. Все обычные инструкции - арифметические, инструкции обмены данными между регистрами и памятью, итд. итп. - существовали только в одном варианте, но их аргументы - адреса в памяти, или непосредственно числа - были 16- или 32-битными в зависимости от того, в каком режиме находился процессор. Имелась также префиксная инструкция размером в один байт, которая меняла "размерность" следующей за ней инструкции с 16 на 32 бита или наоборот. Например, если процессор находился в 32-битном режиме, то обычно все адреса или числа в инструкциях, которые он читал, должны были быть 32-битными, но если стоял этот префикс, они были 16-битными. И наоборот.

Казалось бы, можно было, работая в "реальном" режиме, использовать этот префикс, и вместе с ним - инструкции с 32-битными адресами, выходя за пределы сегмента размером в 64Kb. Но на практике это вызывает ошибку доступа к памяти, процессор не даёт выйти за пределы сегмента.

Трюк, который я придумал, состоял в том, чтобы перейти в защищённый режим, а потом в нём очень аккуратно выставить все регистры так, какими они "должны" быть в реальном режиме, и вернуться реальный режим путём сброса бита в контрольном регистре процессора (бита, который регулирует 16<-->32 битность процессора). Но при этом делается одно небольшое изменение: размеры этих сегментов я определял на 64Kb (какими они являются обычно в реальном режиме), а все 4Gb. Иными словами, этот трюк опирается на тот факт, что "внутри" процессора реальный режим является частным случаем защищённого: разница только в том, что 32-битность меняется на 16-битность, и все сегментные дескрипторы забиты определёнными дефолтными значениями. Путём перехода в защищённый режим, некоторых изменений сегментных дескрипторов с целью "освободить" сегменты от 64kb-ной защиты, и возвращение в реальный режим мы и получаем то, что хотели. Теперь процессор бежит в реальном режиме, но используя префикс адресации, можно работать со всей имеющейся памятью одновременно - а это исключительно удобно.

Я не использовал этот трюк ни для чего серьёзного, кроме прототипа одной игры, которую писал (но всё осталось на уровне прототипа) и нескольких тестовых программ. Но всё равно приятно было.

P.S. Погуглил немного; судя по всему обычное название для этого трюка - "flat real mode". Если поискать Гуглом эту фразу, можно найти несколько более подробных объяснений с примерами.
Subscribe
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 33 comments