 |
- регистры SI и DI могут часто использоваться аналогично регист-
рам общего назначения. Кроме того, их используют как индексные
регистры. В Турбо Си их также используют как регистровые пере-
менные;
- регистр SP указывает на текущую вершину стека и является смеще-
нием относительно стекового сегмента;
- регистр BP является вторичным указателем стека, обычно исполь-
зуется для индексирования при поиске параметров, передаваемых в
стеке.
Базовый индексный регистр (BP) используется в функциях Си
как базовый адрес для аргументов и автоматических переменных. Па-
раметры имеют положительные смещения относительно BP, которые су-
щественно зависят от модели памяти и числа регистров, сохраняемых
при вызове функциий. BP всегда указывает на сохраненную ранее ве-
личину BP. Функции, которые не имеют параметров и не имеют в объ-
явлении аргументов, не должны вообще использовать или сохранять
BP.
Автоматические переменные имеют отрицательное смещение от
BP, первая такая переменная имеет наибольшее отрицательное смеще-
ние.
- 319,320 -
Сегментация памяти.
----------------------------------------------------------------
Микропроцессор 8086 имеет сегментированную архитектуру памя-
ти. Он обеспечивает общее адресное пространство величиной 1 Мб,
однако может прямо адресовать только 64К. Логическую единицу па-
мяти размером 64К называют сегментом, отсюда и название "сегмен-
тированная архитектура памяти".
Теперь возникают вопросы: сколько различных сегментов су-
ществует, где они располагаются и как 8086 понимает, где они рас-
положены?
- 8086 работает с 4 различными сегментами: кода, данных,
стека и дополнительных данных.Кодовый сегмент содержит машинную
программу; сегмент данных - данные; стековый сегмент - стек; до-
полнительный сегмент обычно используют для дополнительных дан-
ных;
- 8086 имеет 4 16-битных сегментных регистра, называемых CS,
DS, SS и ES; они указывают на сегменты кода, данных, стека и до-
полнительных данных соответственно;
- сегмент может быть расположен в любом месте памяти.По
причине, которая будет рассмотрена далее, сегмент всегда начина-
ется с адреса, кратного 16 (в десятичной системе).
Вычисление адреса
-----------------------------------------------------------------
Итак, каким же образом 8086 использует эти сегментные регис-
тры для вычисления адреса? Полный адрес 8086 получает из 2-х
16-битных величин: адреса сегмента и смещения. Предположим, что
адрес сегмента данных - величина в DS регистре - 2F84 (HEX-код),
и вы хотите вычислить реальный адрес некоторых данных, которые
имеют смещение 0532 (HEX-код) от начала сегмента данных. Как это
делается?
Вычисление адреса происходит следующим образом: величина
сегментного регистра сдвигается на 4 бита (1 разряд HEX-кода)
влево и затем складывается со смещением. Полученная в результате
20-битная величина есть реальный адрес данных, что иллюстрирует
- 321,322 -
следующая запись:
DS регистр (сдвинутый) 0010 1111 1000 0100 0000 = 2F840
смещение 0000 0101 0011 0010 = 00532
адрес 0010 1111 1101 0111 0010 = 2FD72
Начальный адрес сегмента - всегда 20-битное число, однако
сегментный регистр содержит только 16 бит. Т.о., младшие 4 бита
всегда предполагаются равными нулю. Это означает, как мы уже от-
мечали, что сегменты могут начинаться только с адреса, кратного
16-и, т.е. с адреса, в котором младшие 4 бита (младшая HEX-цифра)
нулевые.
T.о., если DS-регистр содержит величину 2F84, то сегмент
данных реально начинается с адреса 2F840. Как отмечалось, отрезок
памяти из 16 байт называется параграфом, т.е. этот сегмент всегда
начинается на границе параграфа. Стандартно адрес представляется
в форме "сегмент:смещение". Например, рассмотренный выше адрес
будет записан как 2F84:0532. Заметим, что т.к. смещение может ме-
няться, приведенная пара "сегмент:смещение" не единственна. Все
следующие адреса указывают на одну и ту же ячейку памяти:
0000:0123
0002:0103
0008:00А3
0010:0023
0012:0003
И еще: сегменты могут перекрываться. Например, все четыре
сегмента могут начинаться с одного и того же адреса, что означа-
ет, что ваша программа не будет иметь больше чем 64К адресного
пространства, в котором находятся ваша программа, данные и стек.
Указатели типа NEAR, FAR И HUGE
-----------------------------------------------------------------
Какие указатели применяют в моделях памяти в Турбо Си? Очень
многие. Тип указателей, используемых для программы и данных, по
умолчанию определяется типом модели памяти, выбранной вами. Одна-
ко вы можете принудительно объявить указатель (или функцию), име-
ющий специфический тип, невзирая на модель, которая используется.
Указатели бывают 3-х типов: ближние или near (16 бит), дальние
- 323,324 -
или far (32 бита) и огромные или huge (также 32 бита). Рассмотрим
каждый из них.
Указатели типа NEAR
-----------------------------------------------------------------
16-битный (near) указатель используется совместно с одним из
сегментных регистров для вычисления реального адреса. Например,
реальный адрес функции получается путем сложения 16-битной вели-
чины указателя и функции с содержимым программного сегмента (CS),
сдвинутым влево. Аналогично, near-указатель к данным содержит
смещение относительно адреса в регистре сегмента данных (DS).
Ближние указатели просты в использовании, т.к. все арифметические
операции (такие, как сложение) можно выполнять, не заботясь о
сегменте.
Указатели типа FAR
-----------------------------------------------------------------
Указатель типа far (32-битный) содержит не только смещение в
сегменте, а также (как другую 16-битную величину) адрес сегмента,
который затем сдвигается влево и добавляется к смещению. Исполь-
зуя far-указатели, вы можете иметь множественные программные сег-
менты, что позволяет создавать программы величиной более 64К. Та-
ким же образом far-указатели могут адресовать данные, занимающие
более 64К памяти.
Когда вы используете far-указатели к данным, вы должны знать
о потенциальных проблемах в манипулировании указателями. Как объ-
яснялось в разделе "Вычисление адреса", вы можете иметь много
различных пар "сегмент:смещение", указывающих на один и тот же
адрес. Например, все far- указатели 0000:0120, 0010:0020 и
0012:0000 указывают на один и тот же 20-битный адрес. Однако, ес-
ли вы имеете 3 различных переменных far-указателя - a, b и c, со-
держащие эти 3 величины соответственно, то все следующие выраже-
ния будут работать неправильно:
if (a==b) ...
if (b==c) ...
if (a==c) ...
- 325,326 -
Указанная проблема появляется, когда вы хотите сравнить far-
указатели, используя операторы >, >=, <, <=. В этих случаях толь-
ко смещение (как беззнаковое) может использоваться в целях срав-
нения. Предположим, что а, b и c имеют значения, описанные выше;
тогда следующие выражения будут верны:
if (a>b) ...
if (b>c) ...
if (a>c) ...
Операторы равенства (==) и неравенства (!=) используют
32-битную величину как unsigned long (беззнаковую длину),а не как
полный адрес памяти. Операторы сравнения ( >, >=, < и <=) исполь-
зуют только смещение.
Для операторов == и != необходимы все 32 бита для того, что-
бы можно было произвести сравнение с NULL указателем
(0000:0000).В противном случае, если для проверки равенства вы
используете только смещение, то любой указатель со смещением 0000
будет равен NULL указателю, а это не является тем, что вы хотите.
Еще вам необходимо знать следующее. Если вы прибавляете ве-
|