[EXT]
Все для программиста!
  Статьи   Форум   Исходники   Каталог   Хостинг  
 Справочник функций

Ваш аккаунт

Логин:
Пароль:

Забыли пароль?
Регистрация

Почтовая рассылка



Подписчиков: 12748 Последний выпуск: 04.10.2006

Расширенный Ассемблер: NASM

Следующая глава | Предыдущая глава | Содержание | Указатель


Глава 9: Смешивание 16- и 32-битного кода.

Перевод: AsmOS group, © 2001
Эта глава повествует о некоторых проблемах, связанных с использованием необычных способов адресации и инструкций перехода, возникающих при написании кода операционных систем, таких мест как инициализация защищенного режима, которые требуют, чтобы код, который оперирует с сегментами смешанных адресаций, например, код из 16-битного сегмента пытается модифицировать данные в 32-битном, или инструкции перехода между сегментами разной разрядности.

9.1 Переходы между сегментами смешанной разрядности

Наиболее распространенные формы инструкций смешанной разрядности встречаются при написании 32-битной ОС: как только закончился ваш код настройки в 16-битном режиме, например, загрузка ядра, вам придется запускать его, переключаясь в защищенный режим и прыгая в 32-битный сегмент на точку входя ядра. В полностью 32-биной ОС, есть потребность в использовании только инструкций смешанной разрядности, потому что любой код до ее создания может быть выполнен как чистый 16-битный, и все написанное для нее после, может быть чистым 32-битным.

Пусть переход должен быть осуществлен по 48-битному дальнему адресу, так как сегмент, в который происходит переход — 32-битный. Однако, он должен быть ассемблирован в 16-битном сегменте, поэтому, если мы для примера напишем,

          jmp 0x1234:0x56789ABC  ; ошибка!

то это будет неправильно работать, так как смещение будет обрезано до 0x9ABC и переход окажется обычным 16-битным.

В коде инициализации ядра Линукса, из-за неспособности as86 генерировать необходимые инструкции, их кодируют вручную, используя директиву DB. NASM способен сделать это лучше, он действительно самостоятельно генерирует правильный код. Вот как это делается правильно:

          jmp dword 0x1234:0x56789ABC  ; правильно
Префикс DWORD (строго говоря, он должен следовать после двоеточия, так как это объявление размера двойного слова для смещения, но NASM поддерживает и такую форму записи, потому что обе они не двусмысленны) сообщает, что смещение должно рассматриваться как дальнее, в предположении, что вы умышленно совершаете переход их 16-битного сегмента в 32-битный.

Вы можете сделать обратное действие, делая переход из 32-битного сегмента в 16-битный, указав префикс WORD:

          jmp word 0x8765:0x4321 ; 32 to 16 bit

Если префикс WORD указан в 16-битном режиме, или префикс DWORD в 32-битном, они будут проигнорированы, потому что они переводят NASM в режим, в котором он и так находится.

9.2 Адресация между сегментами различной разрядности

Если ваша ОС является смесью 16 и 32-битной, или если вы пишете расширитель ДОС, наверняка захотите использовать несколько 16-битных и несколько 32-битных сегментов. Но с другой стороны, вам придется писать код в 16-битном сегменте, который обращается к данным в 32-битном сегменте, или наоборот.

Если данные в 32-битном сегменте расположены в передлах первых 64К сегмента, то к ним можно обращаться, используя обычные 16-битные операции, но рано или поздно, вам понадобится совершать 32-битную адресацию из 16-битного сегмента.

Самый легкий путь сделать это, убедится, что вы используете регистр для адреса, с этих пор каждый эффективный адрес, содержащийся в 32-битном регистре принудительно будет 32-битным адресом. Поэтому вы можете сделать так

          mov eax,offset_into_32_bit_segment_specified_by_fs 
          mov dword [fs:eax],0x11223344

Это хорошо, но немного громоздко (потому что мы проигрываем инструкцию и регистр) если вам уже известно точное смещение. Архитектура x86 поддерживает 32-битную эффективную адресацию, чтобы указать только 4-байтное смещение, поэтому почему NASM не мог бы генерировать инструкцию лучше для этих целей?

Он может. Как описано в параграфе 9.1, понадобится только предварить адрес ключевым словом DWORD, и это будет 32-битным адресом:

          mov dword [fs:dword my_offset],0x11223344

Еще, как описано в параграфе 9.1, NASM не придирчив к использованию префикса DWORD, он может идти до или после замещения сегмента, поэтому неплохо будет выглядеть код с этой инструкцией вот так:

          mov dword [dword fs:my_offset],0x11223344

Не удивляйтесь, что префикс DWORD вне квадратных скобок, он контролирует размер данных, сохраняемых по этому адресу, а один внутри квадратных скобок, который указывает на длину адреса. Последнее можно очень просто продемонстрировать:

          mov word [dword 0x12345678],0x9ABC

Это объявление помещает 16 бит данных по адресу, указанному по 32-битному смещению.

Вы также можете указать префикс WORD или DWORD месте с префиксом FAR для косвенных дальних переходов или вызовов. Например:

          call dword far [fs:word 0x4321]

Эта инструкция содержит адрес, указанный 16 битным смещением; она загружает 48-битный дальний указатель из него (16-битного сегмента и 32-битного смещения), и вызывает этот адрес.

9.3 Другие инструкции смешанного размера

Другой способ, который вы можете использовать для доступа к данным, является применение инструкций для работы со строками (LODSx, STOSx и так далее) или инструкции XLATB. Поскольку эти инструкции не имеют параметров, может показаться, что нет простого способа заставить их работать с 32-битными адресами из 16-битного сегмента.

Как раз для этого и предназначены префиксы a16 и a32 NASM-а. Если вы пишите LODSB в 16-битном сегменте, но предполагаете обращаться к строке из 32-битного сегмента, загрузите адрес в ESI и затем напишите

          a32 lodsb

Этот префикс даставит использовать 32-битную адресацию, это означает, что LODSB загружает из [DS:ESI] вместо [DS:SI]. Чтобы получить доступ к строке в 16-битном сегменте из 32-битного, можно использовать соответствующий префикс a16.

Префиксы a16 и a32 могут быть применимы к любой инструкции из таблицы инструкций NASM-а, но для большинства из них создаются необходимые формы адресации и без них. Эти префиксы необходимы только для инструкций с неявной адресацией: CMPSx (параграф A.24), SCASx (параграф A.229), LODSx (параграф A.117), STOSx (параграф A.243), MOVSx (параграф A.137), INSx (параграф A.98), OUTSx (параграф A.149) и XLATB (параграф A.269). Также, различные инструкции push и pop (PUSHA и POPF также как и более частоиспользуемые PUSH и POP) могут использоваться с префиксами a16 и a32 для выбора SP или ESP для использования в качестве указателя стека, в случае, если размер сегмента стека имеет разрядность, отличную от кодового сегмента.

PUSH и POP, когда они используются с сегментными регистрами в 32-битном режиме, также имеют немного необычное поведение, когда они помещают в стек и выталкивают из него 4 байта за один раз, из них два верхних игнорируются и нижние два используются как значения сегментых регистров, с которыми вызывались инструкции. Чтобы зафиксировать 16-битное поведение инструкций push и pop для операций с сегментными регистрами, вы можете использовать префикс размера операнда o16:

          o16 push ss 
          o16 push ds

Этот код сохраняет двойные слово стекового пространства, запихивая два сегментных регистра в пространство, которое при нормальных обстоятельствах используется только под одну операцию помещения в стек.

(Вы также можете использовать префикс o32 для указания 32-битного поведения, когда вы в 16-битном режиме, но это оказывается менее полезно.)


Следующая глава | Предыдущая глава | Содержание | Указатель

Оставить комментарий

Оставлять комментарии могут только зарегистрированные пользователи.

Если вы не являетесь зарегистрированным пользователем, то вам необходимо зарегистрироваться. Регистрация бесплатна. Если вы уже зарегистрированы на CodeNet, то вам необходимо ввести логин и пароль в верхней (Alt-U) части страницы.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог