Сегментный регистр смещает основание доступа к памяти.Адрес в 16-битном режиме рассчитывается следующим образом:
address = ((twenty_bit_t)segment << 4) + offset
, где (мнимый) fifty_bit_t - это тип, длина которого не менее 20 бит.
Адресное пространство в 16-битном вещественномрежим составляет 1 МБ или 20 бит.Сегмент позволяет вам влиять на эти верхние четыре бита и делает это с детализацией 16 байтов (часто называемой «абзацем» в те дни).При добавлении единицы к значению сегмента указатель помещается в память на 16 байт впереди.
Ваше смещение ограничено 16-разрядным значением, поэтому для доступа к нему необходимо было использовать «дальние указатели».Дальний указатель имеет длину 32 бита.Но на самом деле не 32-битные, старшие 16 бит - это просто сегмент, который перекрывает смещение.
Дальние указатели значительно дороже обычных указателей, потому что (с типичными для того времени компиляторами) каждый раз, когда они разыменовываютсядругой указатель он должен был загрузить регистр сегмента.Часто компилятор просто загружал регистры сегментов каждый раз.
Было несколько «моделей» (как мы их тогда называли).В основном это была матрица всех комбинаций указателей на ближний / дальний код, указатели на ближний / дальний данные (и IIRC - способ иметь> 64 КБ глобальных переменных).
Компиляторы обеспечивали детальный контроль над тем, является ли каждый указательдалеко или близко.Это была оптимизация, позволяющая использовать ближний код и близкие данные для кода / данных с большим доступом и добавлять дополнительные директивы расширения для объявления определенных указателей или функций по мере необходимости.
Дальние вызовы и возвраты не были такими серьезнымивлияют на данные, так как указатели разыменовываются гораздо чаще, чем функции.
x86 ЦП имеют специальные инструкции (и префиксы) для обработки дальних вызовов и возвратов и дальних указателей.Даже 32-битные ОС должны как минимум инициализировать регистры сегментов.Некоторые системные инструкции x86 требуют использования сегментных регистров.Однако в 32-битном режиме значение регистров сегмента имеет совершенно другое значение, чем все, что я описал выше.
Связывание и загрузка
Объектные файлыиметь блоки кода и данных, каждый из которых предназначен для входа в определенный сегмент (по имени).Каждый сегмент также помечается как код или данные и т. Д. Компоновщик выясняет, что необходимо, вычисляет, насколько велик каждый сегмент, вычисляет адрес для всего и запоминает расположение любых указателей, которые находятся в исполняемом файле.Операнды дальнего вызова и инициализированные дальние указатели будут вычисляться так, как будто адрес загрузки исполняемого файла равен нулю, и для каждого из них создается запись о перемещении.
DOS обрабатывает память динамически, поэтому ваша программа будет загружена снепредсказуемый адрес.Чтобы справиться с этим, исполняемые файлы DOS имеют «перемещения», которые представляют собой список указателей на код и инициализированные данные.При загрузке DOS просматривает этот список и добавляет базовый сегмент адреса загрузки к значению, указанному в записи перемещения.Например, перемещение кода будет указывать прямо на байты, которые представляют сегмент в инструкции вызова.Это исправило все инициализированные глобальные дальние указатели и любые операнды инструкций удаленного вызова.
(Начиная с 286, вычисление адреса фактически стало почти 21-битным - с регистром сегмента с FFFF + смещение FFFF, адрес будет 10FFEF- (1 МБ + 64 КБ - 16 В). HIMEM.SYS на самом деле имеет вызов API для выделения большой памяти, полностью или ничего. Обычно люди позволяют DOS получить ее, поставив DOS = HIGH в свой файл config.sys.)