←предыдущая следующая→
1 2 3 4 5 6 7 8 9 10 11 12
слово; оператор PTR
уточняет, ячейку какого размера мы имеем в виду.)
И еще одно замечание. Если в символьной команде, оперирующей со словами, указан непосредственный операнд размером в байт, как, например, в команде
MOV AX,80h
то возникает некоторая неоднозначность: что будет записано в регистр AX - число 0080h (+128) или 0FF80h (-128)? В подобных ситуациях ассемблер формирует машинную команду, где операнд-байт расширен до слова, причем расширение происходит со знаком, если операнд был записан как отрицательное число, и без знака в остальных случаях. Например:
MOV AX,-128 ; => MOV AX,0FF80h (A:=-128)
MOV AX,128 ; => MOV AX,0080h (A:=+128) MOV AX,80h ; => MOV AX,0080h (A:=+128)
1.4. СЕГМЕНТИРОВНИЕ
1.4.1 Сегменты памяти. Сегментные регистры.
Первые модели ПК имели оперативную память объемом 2^16 байтов (64Кб) и потому использовали 16-битовые адреса. В последующих моделях память была увеличена до 2^20 байтов (1Мб=1000Кб), для чего уже необходимы 20-битовые адреса. Однако в этих ПК ради сохранения преемственности были сохранены 16-битовые адреса: именно такие адреса хранятся в регистрах и указываются в командах, именно такие адреса получаются в результате модмфикации по базовым и индексным регистрам. Как же удается 16-битовыми адресами ссылаться на 1Мб памяти?
Эта проблема решается с помощью сегментирования адресов (неявного базирования адресов). В ПК вводится понятие "сегмент памяти". Так называется любой участок памяти размером до 64Кб и с начальным адресом, кратным 16. Абсолютный (20-битовый) адрес A любой ячейки памяти можно
представить как сумму 20-битового начального адреса (базы) B сегмента, которому принадлежит ячейка, и 16-битового смещения D - адреса этой ячейки, отсчитанного от начала сегмента: A=B+D. (Неоднозначность выбора сегмента не играет существенной роли, главное - чтобы сумма B и D давала нужный адрес.) Адрес B заносится в некоторый регистр S, а в команде, где должен быть указан адрес A, вместо него записывается пара из регистра S и смещения D (в MASM такая пара, называемая адресной парой или указателем, записывается как S:D). Процессор же устроен так, что при выполнении команды он прежде всего по паре S:D вычисляет абсолютный адрес A как сумму содержимого регистра S и смещения D и только затем обращается к памяти по этому адресу A. Вот так, заменяя в командах абсолютные адреса на адресные пары, и удается адресовать всю память 16-битовыми адресами (смещениями).
В качестве регистра S разрешается использовать не любой регистр, а только один из 4 регистров, называемых сегментными: CS, DS, SS и ES. В связи с этим одновременно можно работать с 4 сегментами памяти: начало одного из них загружается в регистр CS и все ссылки на ячейки этого сегмента указываются в виде пар CS:D, начало другого заносится в DS и все ссылки на его ячейки задаются в виде пар DS:D и т.д. Если одновременно надо работать с большим числом сегментов, тогда нужно своевременно спасать содержимое сегментных регистров и записывать в них начальные адреса пятого, шестого и т.д. сегментов.
Отметим, что используемые сегменты могут быть расположены в памяти произвольным образом: они могут не пересекаться, а могут пересекаться и даже совпадать. Какие сегменты памяти использовать, в каких сегментных регистрах хранить их начальные адреса - все это личное дело автора машинной программы.
Как и все регистры ПК, сегментные регистры имеют размер слова. Поэтому возникает вопрос: как удается разместить в них 20-битовые начальные адреса сегментов памяти? Ответ такой. Поскольку все эти адреса кратны 16 (см. выше), то в них младшие 4 бита (последняя 16-ричная цифра) всегда нулевые, а потому эти биты можно не хранить явно, а лишь подразумевать. Именно так и делается: в сегментном регистре всегда хранятся только первые 16 битов (первые четыре 16-ричные цифры) начального адреса сегмента (эта величина называется номером сегмента или просто сегментом). При вычислении же абсолютного адреса A по паре S:D процессор сначала приписывает справа к содержимому регистра S четыре нулевых бита (другими словами, умножает на 16) и лишь затем прибавляет смещение D, причем суммирование ведется по модулю 2^20:
Aабс = 16*[S]+D (mod 2^20)
Если, например, в регистре CS хранится величина 1234h, тогда адресная пара 1234h:507h определяет абсолютный адрес, равный 16*1234h+507h = 12340h+507h = 12847h.
1.4.2 Сегментные регистры по умолчанию
Согласно описанной схеме сегментирования адресов, замену абсолютных адресов на адресные пары надо производить во всех командах, имеющих операнд-адрес. Однако разработчики ПК придумали способ, позволяющий избежать выписывания таких пар в большинстве команд. Суть его в том, что заранее договариваются о том, какой сегментный регистр на ка-
кой сегмент памяти будет указывать, и что в командах задается только смещение: не указанный явно сегментный регистр автоматически восстанавливается согласно этой договоренности. И только при необходимости нарушить эту договоренность надо полностью указывать адресную пару.
Что это за договоренность?
Считается, что регистр CS всегда указывает на начало области памяти, в которой размещены команды программы (эта область называется сегментом команд или сегментом кодов), и потому при ссылках на ячейки этой области регистр CS можно не указывать явно, он подразумевается по умолчанию. (Отметим попутно, что абсолютный адрес очередной команды, подлежащей выполнению, всегда задается парой CS:IP: в счетчике команд IP всегда находится смещение этой команды относительно адреса из регистра CS.) Аналогично предполагается, что регистр DS указывает на сегмент данных (область памяти с константами, переменными и другими величинами программы), и потому во всех ссылках на этот сегмент регистр DS можно явно не указывать, т.к. он подразумевается по умолчанию. Регистр SS, считается, указывает на стек - область памяти, доступ к которой осуществляется по принципу "последним записан - первым считан" (см. 1.7), и потому все ссылки на стек, в которых явно не указан сегментный регистр, по умолчанию сегментируются по регистру SS. Регистр ES считается свободным, он не привязан ни к какому сегменту памяти и его можно использовать по своему усмотрению; чаще всего он применяется для доступа к данным, которые не поместились или сознательно не были размещены в сегменте данных.
С учетом такого распределения ролей сегментных регистров машинные программы обычно строятся так: все команды программы размещаются в одном сегменте памяти, начало которого заносится в регистр CS, а все данные размещаются в другом сегменте, начало которого заносится в регистр DS; если нужен стек, то под него отводится третий сегмент памяти, начало которого записывается в регистр SS. После этого практически во всех командах можно указывать не полные адресные пары, а лишь смещения, т.к. сегментные регистры в этих парах будут восстанавливаться автоматически.
Здесь, правда, возникает такой вопрос: как по смещению определить, на какой сегмент памяти оно указывает? Точный ответ приведен ниже (см. 1.4.3), а в общих чертах он такой: ссылки на сегмент команд могут быть только в командах перехода, а ссылки практически во всех других командах (кроме строковых и стековых) - это ссылки на сегмент данных. Например, в команде пересылки
MOV AX,X
имя X воспринимается как ссылка на данное, а потому автоматически восстанавливается до адресной пары DS:X. В команде
←предыдущая следующая→
1 2 3 4 5 6 7 8 9 10 11 12
|
|