Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ASM.docx
Скачиваний:
3
Добавлен:
31.07.2019
Размер:
616.28 Кб
Скачать

26Дескрипторы сегментов. Глобальные и локальные таблицы дескрипторов. Регистры gdtr и ldtr. Селекторы.

Как говорилось раньше, сегментация памяти защищённого режима представляет собой совсем другую форму адресации: вместо привычной для реального режима адресации «сегмент:смещение» используется адресация «селектор:смещение». Рассмотрим структуру селектора:

Рисунок 11. Формат селектора

Биты 3-15 указывают номер дескриптора в выбранной таблице дескрипторов (о таблицах чуть ниже). Таким образом, селектор может описать 213=8192 дескрипторов для одной таблицы. Каждый дескриптор в таблице описывает сегмент, при чём он не обязательно должен быть сегментом кода или данных (о типах сегментов будет написано ниже). Бит 2 – флаг TI (Table Indicator). Этот флаг указывает, какая из двух таблиц дескрипторов будет использоваться для загрузки дескриптора. Если флаг равен нулю, то используется глобальная таблица дескрипторов (GDT – Global Descriptor Table), в другом случае используется локальная таблица дескрипторов (LDT –Local Descriptor Table). Теперь поймём, что значит фраза «загрузка дескриптора». Дело в том, что, начиная с процессора 80286, размер сегментных регистров стал равным не двум байтам, а десяти, доступными из которых остались только два младших (в них и грузится селектор). Однако при загрузке селектора в младшие два байта сегментного регистра, в старшие 8 байт автоматически загружается дескриптор из используемой таблицы, по которому и ведётся дальнейшая адресация. Биты 0-1 содержат запрашиваемый уровень привилегий (RPL – Requested Privilege Level) доступа к сегменту, то есть с какими привилегиями программа обращается к сегменту, описанному дескриптором. О привилегиях доступа будет сказано позже, и пока будем считать их равными нулю (то есть, самыми высокими). Пока в наших примерах достаточно пользоваться сегментными дескрипторами. Рассмотрим структуру сегментного дескриптора:

Рисунок 12. Формат сегментного дескриптора

Теперь рассмотрим формат дескриптора поподробнее. База – это линейный адрес, с которого начинается сегмент. Лимит – это максимальное смещение от начала сегмента, то есть лимит равен размеру сегмента минус один байт. Таким образом, получается, что минимальный размер сегмента может быть равен одному байту, а максимальный – одному мегабайту или четырём гигабайтам (это зависит от, того, умножается размер сегмента на 4 килобайта или нет). G – флаг гранулярности (Granularity), который, как раз, указывает, в чём измеряется размер сегмента: если G=0, то размер сегмента считается в байтах (то есть, максимальный размер сегмента получается 220=1 мегабайт), если G=1, то размер сегмента считается в 4-килобайтных блоках (страницах), то есть, максимальный размер сегмента равен 220*212=4 гигабайт, при чём размер сегмента всегда кратен 4 килобайтам. D/B – флаг, указывающий разрядность сегмента: 0 – сегмент 16-разрядный, 1 – сегмент 32 разрядный (этот флаг ещё называют BIG). Бит 53 зарезервирован и всегда должен быть равен нулю. Бит AVL является свободным (Available) и может использоваться по усмотрению программы. Флаг P определяет присутствие сегмента в памяти (Presentation). Если P=1, то сегмент присутствует в памяти, если P=0, то для использования сегмента его следует сначала загрузить в память. Таким образом, флаг P предназначен для организации виртуальной памяти. Надо заметить, что способ организации механизма подкачки памяти при помощи флага P при господстве модели FLAT (к ней мы ещё вернёмся) не нашёл применения: страничная адресация оказалась гораздо проще. Биты DPL определяют привилегии доступа к сегменту (Descriptor Privelege Level). Всего существует 4 уровня доступа, в которых 0 – самый высокий приоритет, 3 – самый низкий приоритет. Таким образом, чтобы программе было возможным использовать сегмент, описанный данным дескриптором, в селекторе, используемом программой биты RPL должны принимать не меньшее значение, чем биты DPL дескриптора. В противном случае возникает аппаратная ошибка (прерывание), названная исключением, которая может быть обработана ядром ОС. Флаг S и тип сегмента следует рассматривать вместе. Если S установлен, то это значит, что сегмент является системным (System), в противном случае сброшенный флаг S означает, что дескриптор описывает сегмент данных или кода. Если сегмент является системным, то указывается его тип (о типах системных дескрипторов будет написано позже), в другом случае дескриптор описывает либо сегмент данных, либо сегмент кода. Поле «тип сегмента» для сегмента данных выглядит так:

Рисунок 13. Поле «тип сегмента» сегмента данных

Для сегмента кода поле выглядит немного иначе:

Рисунок 14. Поле «тип сегмента» сегмента кода

Флаг А также используется для организации виртуальной памяти и является флагом доступа (Accessed), то есть при любом обращении к описываемому сегменту флаг будет автоматически установлен (то есть, по этому биту операционная система решает, какие сегменты следует временно сбросить на диск в swap-файл, а с какими сегментами следует повременить). То есть, ОС может периодически сбрасывать этот бит, и если он через некоторое время не станет равным единице, то данный сегмент можно сбросить, допустим, на жёсткий диск, освободив тем самым память. Для сегмента данных определяются два характерных флага: Флаг D задаёт направление роста сегмента (Direction). Обычно, если D=1, то выбранный сегмент является сегментом стека и растёт «задом наперёд». Если же D=0, то выбранный сегмент является сегментом данных. На самом деле, значение D=1 практически не используется. Флаг W определяет возможность записи в сегмент (Writable). То есть, сброшенный флаг указывает, что сегмент данных доступен только для чтения. Если флаг установлен, то сегмент доступен также и для записи. Для сегмента кода определяются следующие два характерных флага: Бит C называется битом подчинения (Conforming), но рассмотрен он будет позже. Флаг R указывает на доступность сегмента кода для чтения (Readable). То есть, если флаг сброшен, читать из сегмента кода нельзя. Из рассмотрения этих двух полей можно сделать вывод: писать прямо в сегмент кода категорически запрещено, как и запускать программу, находящуюся в сегменте данных. Поля P, DPL, S и тип сегмента образуют единую группу – права доступа сегмента, и для операции с этой группой введены специальные команды процессора, которые будут рассмотрены позже. Теперь перейдём к рассмотрению структуры таблиц дескрипторов. Таблицы LDT (Local Descriptor Table), IDT (Interrupt Descriptor Table) и GDT (Global Descriptor Table) имеют одинаковую структуру – это массивы дескрипторов. Сами таблицы не являются сегментами – это области физической памяти. Отличием IDT от GDTи LDT является то, что в ней хранятся особые дескрипторы, которые мы пока затрагивать не будем. Единственным отличием GDT от LDT является то, что таблицаGDT может быть всего лишь одна, а таблиц LDT может быть много. Кроме этого, нулевой дескриптор в таблице GDT не может быть использован и должен содержать нули (так называемый «нулевой дескриптор»). Структуры таблиц выглядят следующим образом:

Рисунок 15. Структура таблиц дескрипторов

Остаётся ещё один вопрос: каким образом процессор находит в памяти эти таблицы. Вспомним структуру реального режима: таблица прерываний хранится, начиная с нулевого логического адреса и занимает 400h байт. Процессору не составляло труда её обнаружить, так как на любой машине под таблицу были зарезервированы самые младшие 400h байт адресного пространства. В защищённом режиме всё несколько иначе: таблицы могут располагаться в любом месте оперативной памяти, и их адреса нужно где-то фиксировать. Для этого в процессорах Intel (начиная с модели 80286) появились новые машинно-специфичные регистры: GDTR(определяет расположение и размер таблицы GDT), LDTR (определяет селектор таблицы LDT), IDTR (определяет расположение и размер таблицыIDT). Регистры GDTR и LDTR схожи по строению: они имеют одинаковый размер (6 байт) и содержат в себе одинаковую структуру данных о расположении и размере таблицы в памяти. Регистр LDTR отличается от этих двух регистров по размеру (10 байт) и содержит в себе селектор дескриптора сегмента данных таблицы LDT и сам дескриптор, который находится в таблице GDT. Рассмотрим структуру данных, хранящихся в регистрах GDTR и IDTR:

Рисунок 16. Структура данных регистров GDTR и IDTR

Линейный адрес таблицы указывает, в какой области оперативной памяти находится таблица. Размер таблицы считается в байтах, при чём значение битов 0-15 должно быть на единицу меньше реального размера таблицы. Структура регистра LDTR выглядит следующим образом:

Рисунок 17. Структура данных регистра LDTR

Из всех 10 байт нам доступны лишь младшие два байта (биты 0-15), в которые следует загрузить селектор дескриптора таблицы LDT, находящийся в таблицеGDT. В таком случае общий механизм преобразования логического адреса в линейный происходит по схеме, изображённой на рисунке 18.

Рисунок 18. Формирование линейного адреса в защищённом режиме

Операции над регистрами GDTR, LDTR, IDTR перечислены в следующей таблице:

Команда

Описание

lgdt

загрузить регистр GDTR

sgdt

прочитать значение регистра GDTR

lldt

загрузить регистр LDTR

sldt

прочитать значение регистра LDTR

lidt

загрузить регистр IDTR

sidt

прочитать значение регистра IDTR

Таблица 1. Операции над регистрами GDTR, LDTR, IDTR

Подробнее с использованием этих команд познакомимся в следующих статьях. Теперь рассмотрим команды, которые переопределяют сегментные регистры. Команды позволяют переопределить сегментные регистры как явно, так и неявно. Команды явного переопределения сегментных регистров перечислены ниже:

Команда

Описание

mov

запись в регистр значения

pop

извлечение из стека

lds

загрузка регистра DS

les

загрузка регистра ES

lfs

загрузка регистра FS

lgs

загрузка регистра GS

lss

загрузка регистра SS

Таблица 2. Команды явного переопределения сегментных регистров

К командам неявного переопределения регистров относятся следующие:

Команда

Описание

call

вызов функции

jmp

прыжок

ret

возвращение из функции

sysenter

быстрый системный вызов

sysexit

быстрый возврат из системного вызова

iret

выход из обработчика прерывания

int, into, int3

вызов прерывания

Таблица 3. Команды неявного переопределения сегментных регистров

Команды явного переопределения регистров предназначены для операций над регистрами DS, ES, FS, GS, SS. Чтобы попытаться изменить сегментный регистр CS, нам потребуется воспользоваться неявными командами. Теперь мы располагаем достаточной информацией, чтобы реализовать выполнение 32-битного кода. Но это уже будет в следующем номере.

Команды сдвига. Логические команды. Команды обработки бит.

and операнд_1,операнд_2 — операция логического умножения. Команда выполняет поразрядно логическую операцию И (конъюнкцию) над битами операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.

or операнд_1,операнд_2 — операция логического сложения. Команда выполняет поразрядно логическую операцию ИЛИ (дизъюнкцию) над битами операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.

хог операнд_1,операнд_2 — операция логического исключающего сложения. Команда выполняет поразрядно логическую операцию исключающего ИЛИ над битами операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.

test операнд_1,операнд_2 — операция проверки (способом логического умножения). Команда выполняет поразрядно логическую операцию над битами операндов операнд_1 и операнд_2. Состояние операндов остается прежним, изменяются только флаги ZF, SF, и PF, что дает возможность анализировать состояние отдельных битов операнда без изменения их состояния в исходных операндах.

not операнд — операция логического отрицания. Команда выполняет поразрядное инвертирование (замену значения на обратное) каждого бита операнда.Результат записывается на место операнда.

bsf операнд_1,операнд_2 — сканирование битов вперед (Bit Scaning Forward).Команда просматривает (сканирует) биты второго операнда от младшего к старшему (от бита 0 до старшего бита) в поисках первого бита, установленного в 1.Если таковой обнаруживается, в первый операнд заносится номер этого битав виде целочисленного значения. Если все биты второго операнда равны 0, то флаг нуля ZF устанавливается в 1, в противном случае флаг ZF сбрасывается в 0.

mov al,02h

bsf bx.al ;bx=l

jz ml ;переход, если al=00h

bsr операнд_1,операнд_2 — сканирование битов в обратном порядке (Bit Scaning Reset). Команда просматривает (сканирует) биты второго операнда от старшего к младшему (от старшего бита к биту 0) в поисках первого бита, установленного в 1. Если таковой обнаруживается, в первый операнд заносится номер этого бита в виде целочисленного значения. При этом важно, что позиция первого единичного бита слева все равно отсчитывается относительно бита 0. Если все биты второго операнда равны 0, то флаг нуля ZF устанавливается в 1, в противном случае флаг ZF сбрасывается в 0.

bt операнд,смещение_бита ( команда проверки бита ВТ (Bit Test) переносит значение бита в флаг CF )

Например

bt ax,5 ;проверить значение бита 5

jnc ml ;переход, если бит = 0

бита в флаг CF и затем устанавливает проверяемый бит в 1:

bts операнд,смещение_бита (команда проверки и установки бита BTS (Bit Test and Set) переносит значение бита в флаг CF и затем устанавливает проверяемый бит в 1)

Например,

mov ax,10

bts pole,ах ;проверить и установить 10-й бит в pole

jc ml ;переход, если проверяемый бит был равен 1

btr операнд,смещение_бита (команда проверки и сброса бита BTR (Bit Test and Reset) переносит значение бита во флаг CF и затем устанавливает этот бит в 0)

btc операнд,смещение_бита (команда проверки и инвертирования бита ВТС (Bit Test and Convert) переносит значение бита в флаг CF и затем инвертирует значение этого бита)

shl операнд,счетчик_сдвигов — логический сдвиг влево (Shift Logical Left). Содержимое операнда сдвигается влево на количество битов, определяемое значением счетчик_сдвигов. Справа в позицию младшего бита вписываются нули.

shr операнд,счетчик_сдвигов — логический сдвиг вправо (Shift Logical Right).Содержимое операнда сдвигается вправо на количество битов, определяемое значением счетчик_сдвигов. Слева в позицию старшего (знакового) бита вписываются нули.

sаl операнд,счетчик_сдвигов — арифметический сдвиг влево (Shift Arithmetic Left).Содержимое операнда сдвигается влево на количество битов, определяемое значением счетчик_сдвигов. Справа (в позицию младшего бита) вписываются нули. Команда SAL не сохраняет знака, но устанавливает флаг OF в случае смены знака очередным выдвигаемым битом. В остальном команда SAL полностью аналогична команде SHL;

sar операнд,счетчик_сдвигов — арифметический сдвиг вправо (Shift Arithmetic Right). Содержимое операнда сдвигается вправо на количество битов, определяемое значением счетчик_сдвигов. Слева в операнд вписываются нули. Команда SAR сохраняет знак, восстанавливая его после сдвига каждого очередного бита.

rol операнд,счетчик_сдвигов — циклический сдвиг влево (Rotate Left). Содержимое операнда сдвигается влево на количество битов, определяемое операндом счетчик_сдвигов. Сдвигаемые влево биты записываются в тот же операнд справа.

гог операнд,счетчик_сдвитов — циклический сдвиг вправо (Rotate Right). Содержимое операнда сдвигается вправо на количество битов, определяемое операндом счетчик_сдвигов. Сдвигаемые вправо биты записываются в тот же операнд слева.

rcl операнд,счетчик_сдвигов — циклический сдвиг влево через перенос (Rotatethrough Carry Left). Содержимое операнда сдвигается влево на количество битов, определяемое операндом счетчик_сдвигов. Сдвигаемые биты поочередно становятся значением флага переноса CF;

rcr операнд,счетчик_сдвигов — циклический сдвиг вправо через перенос (Rotatethrough Carry Right). Содержимое операнда сдвигается вправо на количество битов, определяемое операндом счетчик_сдвигов. Сдвигаемые биты поочередно становятся значением флага переноса CF.

shld операнд_1,операнд_2,счетчик_сдвигов — сдвиг влево двойной точности. Команда сдвигает биты первого операнда влево и заполняет его справа значения ми битов, вытесняемых из второго операнда, согласно схеме на рис. 9.6. Количество сдвигаемых битов определяется значением счетчика сдвигов, которое может лежать в диапазоне 0...31. Это значение может задаваться непосредственнов третьем операнде или содержаться в регистре CL. Значение второго операндане меняется;

shrd операнд_1,операнд_2,счетчик_сдвигов — сдвиг вправо двойной точности. Команда сдвигает биты первого операнда вправо и заполняет его слева значениями битов, вытесняемых из второго операнда, согласно схеме на рис. 9.7. Количество сдвигаемых битов определяется значением счетчика сдвигов, которое может лежать в диапазоне 0...31. Это значение может задаваться непосредственно в третьем операнде или содержаться в регистре CL. Значение второго операнда не меняется.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]