микропроцессора 8088, они размещаются в ячейках последовательно, начиная с
младшего байта числа. Такой способ несколько непривычен для большинства
специалистов, не имевших дела с микропроцессорами фирмы "Интел". Если Ваша
программа работает с отдельными байтами в памяти, необходимо учитывать
такой способ хранения.
Арифметический сопроцессор 8087 использует несколько специальных
форматов, включающих четырехбайтовый целочисленный формат и три формата с
плавающей запятой: двухбайтный, четырехбайтный и десятибайтный, а также
десятичный формат с двадцатью десятичными цифрами. Микропроцессор 8088
непосредственно не использует эти форматы, но если к IBM/PC подключен
арифметический сопроцессор 8087, эти форматы становятся как бы расширением
набора форматов данных.
3.2. Память, часть 2: что такое адрес
Каждая ячейка памяти имеет адрес, который используется для ее
нахождения. Адреса - это числа, начиная с нуля для первой ячейки
увеличивающиеся по направлению к последней ячейке памяти. Поскольку адреса
- это те же числа, компьютер может использовать арифметические операции
для вычисления адресов памяти.
Архитектура каждого компьютера накладывает собственные ограничения на
величину адресов. Наибольший возможный адрес определяет обьем адресного
пространства компьютера или то, какой обьем памяти он может использовать.
Обычно компьютер использует память меньшего обьема, чем допускается его
возможностями адресации. Если архитектура компьютера предусматривает
небольшое адресное пространство, это накладывает суровые ограничения на
возможности такого компьютера.
IBM/PC использует возможности адресации микропроцессора 8088
полностью. Адреса в 8088 имеют длину 20 бит, следовательно, процессор
позволяет адресовать два в двадцатой степени байта или 1024 К.
Такое большое адресное пространство позволяет свободно использовать
ресурсы памяти для специальных целей, что мы увидим в следующем разделе.
Перед этим необходимо разобраться в том, как 16-разрядный компьютер
работает с 20-разрядными адресами и какие ограничения это может наложить
на программы пользователя.
Большая часть арифметических операций, которые может выполнять
микропроцессор 8088, ограничивается манипуляцией с 16-разрядными числами,
что дает диапазон значений от 0 до 65.535 или 64 К. Поскольку полный адрес
должен состоять из 20 разрядов, необходимо было разработать способ
управления 20 разрядами. Решение было найдено путем использования принципа
сегментированной адресации.
Если взять 16-ти разрядное число и добавить к нему в конце четыре
двоичных нуля, то получится 20-ти разрядное число, которое может
использоваться как адрес. Добавлением четырех нулей или сдвиг числа влево
на четыре разряда фактически означает умножение числа на 16 и теперь
диапазон значений будет составлять 1.024К. К сожалению, число с четырьмя
нулями в конце может адресовать только одну из 16 ячеек памяти - ту, адрес
которой оканчивается на четыре нуля. Все остальные ячейки, адреса которых
оканчиваются на любую из остальных 16 комбинаций из четырех бит, не могут
быть адресованы при таком методе адресации.
Для окончательного решения проблемы 20-разрядной адресации
используются два 16-разрядных числа. Считается, что одно из них имеет еще
четыре нуля в конце (выходящие за пределы разрядной сетки). Такое как бы
20-разрядное число называется сегментной частью адреса. Второе
шестнадцатиричное число не сдвигается на четыре разряда и используется в
своем нормальном виде. Это число называется относительной частью адреса.
Сложением этих двух чисел получают полный 20-разрядный адрес, позволяющий
адресовать любую из 1.024 К ячеек памяти в адресном пространстве IBM/PC.
Сегментная часть адреса задает ячейку с адресом, кратным 16, эта ячейка
называется границей параграфа. Окончательное значение указывает конкретную
ячейку на определенном удалении от границы параграфа. Рисунок 3.1
показывает как это делается.
Чтобы лучше усвоить этот момент, рассмотрим все еще раз. Полный
20-разрядный адрес задается двумя частями, каждая из которых представляет
собой 16-разрядное число. Сегментная часть адреса обрабатывается так, как
будто он имеет четыре дополнительных нуля в конце. Эта сегментная часть
может относиться к любой части всего адресного пространства - но она может
указывать только на шестнадцатиричную границу, то есть, на адрес,
оканчивающийся на четыре нуля. Относительная часть адреса прибавляется к
сегментной части, образуя полный адрес. Относительная часть адреса может
задавать любую ячейку памяти, отстоящую от ячейки, указываемой сегментной
частью, не более чем на 64К.
***
Рис. 3.1. Адресация памяти:
1-обычный 16-разрядный регистр адреса; 2-16бит (4 шестнадцатиричные цифры)
4 бита на шестнадцатиричную цифру; 3-сдвиговый регистр сегмента;
4-объединенные регистры сегмента адреса и сегмента; 5-дополнительный
шестнадцатиричный ноль; 6-результат - 20-разрядный адрес
Хотя относительная часть адреса могла бы задавать только четыре
последних цифры адреса, она принимает значения от 0 до без единицы 64К.
Большая часть манипуляций с адресами связана с относительной частью
адреса. Сегментная часть адреса фактически становится базовым адресом для
рабочей области размером ,;К, которую позволяет адресовать относительная
часть адреса.
Имеется удобный способ записи сегментированных адресов, использование
которого программой DEBUG (описанной в главе 6) Вы еще увидите. Кроме
того, он хорошо прослеживается в ассемблерных листингах, например в том,
который приведен в приложении А к техническому руководству фирмы IBM.
Сначала записывается сегментная часть адреса, после нее следует двоеточие,
а затем относительная часть адреса. Например, если сегментная часть адреса
(в шестнадцатиричной форме) 2222, а относительная часть - 3333, то полный
сегментированный адрес будет записываться как 2222:3333. Фактический
20-разрядный адрес будет в шестнадцатиричном виде иметь значение 25553,
получаемое таким простым сложением:
22220
+
3333
____________
25553
(В конце этого раздела мы приведем несколько примеров работы с
сегментированными адресами на Бейсике и Паскале).
Для работы с сегментированными адресами микропроцессор 8088 имеет
специальные регистры сегментов, предназначенные для хранения сегментной
части адресов. Загрузив в регистр сегмента некоторое значение, можно
адресовать следующие за ним 64К ячеек памяти. Без изменения значения в
регистре сегмента компьютер может работать только с 64К байтами из общего
адресного пространства в 1.024К. Путем изменения значения в регистре
сегмента можно адресовать любую ячейку памяти.
Чтобы иметь возможность в каждый момент времени работать более чем с
64К памяти, в микропроцессоре 8088 предусмотрены четыре различных регистра
сегмента, каждый из которых имеет особое назначение. Память компьютера
используется для различных целей - часть ее занимает программа, другая
часть используется для хранения данных, с которыми в данный момент
работает программа. Поэтому два регистра сегмента выделены для программы и
для данных. Для указания базового адреса программного или кодового
сегмента используется регистр CS. Для указания сегмента данных
используется регистр DS. Еще одна область памяти, используемая для
специальных целей, называется стеком и ее адрес указывается регистром
стека SS. И, наконец, для обеспечения дополнительных возможностей
адресации имеется регистр дополнительного сегмента (или сегмента
расширения), ES.
Когда программа подготавливается к выполнению, операционная система,
такая как DOC, выбирает ячейки каких разделов будут использоваться для
размещения кодовой части программ, данных и стека. В регистры сегментов
CS,DS и SS заносятся адреса этих ячеек. При выполнении программы адреса в
этих регистрах позволяют находить нужные ячейки памяти.
Следует понять, что эти регистры совсем не обязательно должны
указывать на сегменты памяти, расположенные далеко друг от друга. Они
могут указывать на любые ячейки, находящиеся далеко или близко друг от
друга и даже располагающиеся в одном разделе. Если необходимо всего пару
тысяч байт для размещения программы и данных, кодовый сегмент и сегмент
данных могут располагаться рядом. И хотя фрагменты кодового сегмента и
сегмента данных используются при выполнении программы по-разному,
64К-байтные области, адресуемые соответствующими регистрами сегментов,
могут отсекаться. На рис.3.2 показано как эти три сегмента -
кодовый,данных и стека - могут использоваться и как области этих сегментов
могут пересечься.
***
Рис.3.2. Сегменты кода, данных и стека:
1-регистр сегмента; 2-указывает 64К памяти; 2-нам требуется; 3-16К кодовой
части; 4-32К данных; 5-8К стека; 6-мы выделяем для них место в памяти и
устанавливаем значения соответствующих регистров; 7-регистры обеспечивают
доступ к большому обьему памяти, чем необходимо; 8-избыток
Если программа не изменяет содержимого своих регистров сегментов, то
она может использовать только 64К данных и кодовую часть обьемом 64К. С
другой стороны, если программа будет манипулировать содержимым регистров
сегментов, то она сможет работать с данными любого обьема, вплоть до
1024К. Может использоваться любой из режимов работы, но на практике обычно
фиксируют содержимое регистра сегмента данных и при необходимости изменяют
содержимое регистра кодового сегмента. Такой способ организации работы
поддерживается набором команд микропроцессора 8088, обеспечивающим очень
удобный способ загрузки регистра кодового сегмента CS, путем использования
команд FARCALL и FARRET.
Практические результаты такой организации выразились в том, что ДОС и
языковые процессоры используют программные соглашения, которые позволяют
увеличивать обьем программ практически неограниченно, в то время как их
адресуемая область данных ограничена 64К. Вы легко заметите это
ограничение, работая с Паскалем или компилятором Бейсика. Для
"встроенного" интерпретатора Бейсика для IBM/PC собственно выполняемой
программой является сам интерпретатор, а то что Вы считаете своей
программой на Бейсике фактически является частью данных интерпретатора.
Таким образом, для интерпретатора Бейсика суммарный обьем кодовой части и
данных Вашей программы не должен превышать 64К, которые позволяет
адресовать регистр данных DS.
Как Бейсик, так и Паскаль лишь до определенной степени позволяют
манипулировать сегментированными адресами. Вы можете непосредственно
изменять содержимое регистров CS,DS,SS и ES-языковой процессор должен
управлять этими регистрами, иначе все может совершенно запутаться. Однако,
определенный способ использования в программах сегментированной адресации
все же имеется.
Ниже приводится описание использования такого способа в Бейсике.
Раздел сегмента может быть определен с помощью оператора DEFSEG. Некоторые
языковые средства Бейсика, например, операторы PEEK и POKE, работают с
адресами относительными к значению, заданному оператором DEFSEG.
Например, если взять упоминавшийся выше адрес 2222:3333, то на
бейсике доступ к его значению может осуществляться следующим образом: