После того, как в программе определены сегменты, ассемблер должен выяснить, как будут установлены сегментные регистры во время выполнения программы. В примере на рис.3.9 использованы только три сегмента, а в большой программе можно использовать намного больше сегментов. С помощью четырех сегментных регистров большая программа может адресовать только часть всех своих сегментов в каждый момент времени. Ассемблеру необходимо сообщить, какие сегменты будут адресоваться во время выполнения программы; Это делается с помощью оператора ASSUME, который описывает для ассемблера установку регистров. Программист обязан связать каждый сегментный регистр с сегментом, который он адресует в данный момент.
Рисунок 3.9 иллюстрирует манипуляции с тремя сегментами: DATA, BUFFER и CODE. Имена, выбранные для этих сегментов - произвольны. Их выбрал программист, и они не имеют значения для ассемблера. Вы можете, например, взять сегмент CODE и использовать его только для данных, и наоборот. Лучше всего называть сегменты так, чтобы смысл их имени был связан с действиями программы. В рассмотренном примере сегменты DATA и BUFFER содержат по одной ячейке данных. Сегмент CODE вместе с тремя командами содержит ячейку данных. Конечно ни к чему, чтобы программа определяла сегмент только с одной ячейкой, как в данном примере. В программе требуется задавать много сегментов тогда, когда она работает с данными во многих местах адресного пространства микропроцессора 8088. Например, написанная для IBM PC программа управления устройствами может ссылаться к памяти в системной области данных, устанавливать вектор прерывания в младших адресах памяти, а выполняться в любом другом месте памяти ЭВМ. Каждое из этих полей - отдельный сегмент, и он должен быть определен в программе.
Оператор ASSUME на рис.3.9 вынуждает ассемблер генерировать код, зная, что сегментные регистры установлены следующим образом: регистр CS содержит стартовый адрес сегмента CODE, регистр DS указывает на сегмент DATA, а регистр ES определяет сегмент BUFFER. Оператор ASSUME только сообщает об этом ассемблеру, а ассемблер генерирует код, предполагая, что сегментные регистры загружены так, как показано в программе. Назначения сегментных регистров оператором ASSUME остаются в силе в течение ассемблирования до тех пор, пока другой оператор ASSUME не укажет новое значение. Ассемблер рассматривает операторы ASSUME последовательно, даже если программа образует цикл вокруг них либо обходит эти операторы. Оператор ASSUME действует до тех пор, пока ассемблер не увидит следующий оператор ASSUME при последовательном просмотре исходного текста. Следует отметить, что оператор ASUUME не обязательно должен описывать все сегментные регистры (в примере не определен сегментный регистр SS). Практически встречаются даже фрагменты, в которых содержимое сегментного регистра неизвестно в программе. В таких случаях оператор ASSUME должен указывать сегмент NOTHING. Например, оператор
ASSUME ES:NOTHING
ASSUME ES:N
сообщает ассемблеру, что программа не знает, на какой сегмент указывает дополнительный сегментный регистр. Так как содержимое регистра неизвестно, ассемблер не будет использовать его ни для каких адресных вычислений.
Важно отметить, что оператор ASSUME не порождает машинных команд. Он является директивой ассемблеру указывающей, что сегментные регистры установлены согласно информации в операторе, а задача программиста - обеспечить правильную установку регистров. Следовательно, ассемблер не может проверить, соответствует ли оператор ASSUME содержимому регистров во время выполнения. Поскольку при выполнении программа может попасть к оператору ASSUME по-разному, правильное написание этого оператора - забота программиста. В примере программы на рис.3.9 предполагается, что сегментные регистры были установлены до выполнения этого участка программы. Если сегментные регистры установлены неверно, программа будет выполняться неправильно, даже если ассемблирование было без ошибок.
Первая команда увеличивает значение переменной VAR1, находящейся в сегменте данных. Ассемблер предполагает, что регистр DS указывает на сегмент DATA в соответствии с оператором ASSUME. Так как регистр DS используется по умолчанию для доступа к данным, ассемблер не порождает для этой команды префикса подавления сегмента, и соответствующая этой команде ассемблера четырехбайтовая машинная команда не содержит сегментного префикса.
Вторая команда использует переменную VAR2, которая была определена в сегменте, названном BUFFER. Программа сообщила ассемблеру, что дополнительный сегментный регистр будет указывать на сегмент BUFFER. Ассемблер порождает четырехбайтовую машинную команду, увеличивающую переменную VAR2, но ей предшествует однобайтовый префикс, подавляющий использование регистра DS. Байт префикса 26H требует, чтобы микропроцессор использовал регистр ES при формировании 20-битового адреса памяти. Ассемблер помечает двоеточием префикс в распечатке объектного кода.
Третья команда ссылается на переменную VAR3 в сегменте CODE. Оператор ASSUME связывает этот сегмент с регистром CS, и ассемблер автоматически порождает соответствующий префикс подавления сегмента. В этом случае префикс 2EH предписывает микропроцессору использовать регистр CS при вычислении абсолютного адреса.
Первоначально оператор ASSUME воспринимается программистом, как никчемный. При написании первых программ про оператор ASSUME, как правило, забывают, и ассемблер порождает при этом массу сообщений об ошибках, чтобы помочь вам вспомнить об этом операторе. Но со временем оператор ASSUME начинает помогать программисту, работающему на языке ассемблера, управлять размещением структур данных в программе. Программист должен помнить о загрузке в сегментный регистр адреса данных, необходимых программе, а ассемблер облегчит трудности, связанные с поисками местонахождения данных каждой команды и сегментного регистра, необходимого для вычисления соответствующего адреса.