- •1.1.2 Регистры
- •Index, индекс приемника; sp - stack pointer, указатель стека; cs -
- •1.2.1 Представление целых чисел.
- •1.2.2 Особенности выполнения арифметических опреаций
- •1.2.3 Представление символов и строк
- •1.2.4 Представление адресов
- •1.3.3 Запись команд в masm
- •1.4.3), А в общих чертах он такой: ссылки на сегмент команд могут быть
- •1.4.3 Сегментирование, базирование и индексирование адресов
- •1.4.5 Начальная загрузка сегментных регистров
- •1.4.6 Ссылки вперед
- •2) Внутрисегментный относительный длинный переход.
- •1.5.2 Условные переходы.
- •1.5.3 Команды управление циклом
- •1.6. Строковые операции
- •1.7.2 Основные стековые команды
- •1.7.4 Процедуры в языке ассемблера
1.4.3), А в общих чертах он такой: ссылки на сегмент команд могут быть
только в командах перехода, а ссылки практически во всех других коман-
дах (кроме строковых и стековых) - это ссылки на сегмент данных. Нап-
ример, в команде пересылки
MOV AX,X
имя X воспринимается как ссылка на данное, а потому автоматически вос-
станавливается до адресной пары DS:X. В команде же безусловного пере-
хода по адресу, находящемуся в регистре BX,
JMP BX
абсолютный адрес перехода определяется парой CS:[BX].
Итак, если в ссылке на какую-то ячейку памяти не указан явно сег-
ментный регистр, то этот регистр берется по умолчанию. Явно же сегмен-
тные регистры надо указывать, только если по каким-то причинам регистр
по умолчанию не подходит. Если, например, в команде пересылки нам надо
сослаться на стек (скажем, надо записать в регистр AH байт стека, по-
меченный именем X), тогда нас уже не будет устраивать договоренность о
том, что по умолчанию операнд команды MOV сегментируется по регистру
DS, и потому мы обязаны явно указать иной регистр - в нашем случае ре-
гистр SS, т.к. именно он указывает на стек:
MOV AH,SS:X
Однако такие случаи встречаются редко и потому в командах, как прави-
ло, указываются только смещения.
Отметим, что в MASM сегментный регистр записывается в самой коман-
де непосредственно перед смещением (именем переменной, меткой и т.п.),
однако на уровне машинного языка ситуация несколько иная. Имеется 4
специальные однобайтовые команды, называемые префиксами замены сегмен-
та (обозначаемые как CS:, DS:, SS: и ES:). Они ставятся перед коман-
дой, операнд-адрес которой должен быть просегментирован по регистру,
отличному от регистра, подразумеваемому по умолчанию. Например, приве-
денная выше символическая команда пересылки - это на самом деле две
машинные команды:
SS:
MOV AH,X
1.4.3 Сегментирование, базирование и индексирование адресов
Поскольку сегментирование адресов - это разновидность модификации
адресов, то в ПК адрес, указываемый в команде, в общем случае модифи-
цируется по трех регистрам - сегментному, базовому и индексному. В це-
лом, модификация адреса производится в два этапа. Сначала учитываются
только базовый и индексный регистры (если они, конечно, указаны в ко-
манде), причем вычисление здесь происходит в области 16-битовых адре-
сов; полученный в результате 16-битовый адрес называется исполнитель-
ным (эффективным) адресом. Если в команде не предусмотрено обращение к
памяти (например, она загружает адрес в регистр), то на этом модифика-
ция адреса заканчивается и используется именно исполнительный адрес
(он загружается в регистр). Если же нужен доступ к памяти, тогда на
втором этапе исполнительный адрес рассматривается как смещение и к не-
му прибавляется (умноженное на 16) содержимое сегментного регистра,
указанного явно или взятого по умолчанию, в результате чего получается
абсолютный (физический) 20-битовый адрес, по которому реально и проис-
ходит обращение к памяти.
Отметим, что сегментный регистр учитывается только в "последний"
момент, непосредственно перед обращением к памяти, а до этого работа
ведется только с 16-битовыми адресами. Если учесть к тому же, что сег-
ментные регистры, как правило, не указываются в командах, то можно в
общем-то считать, что ПК работает с 16-битовыми адресами.
Как уже сказано, если в ссылке на ячейку памяти не указан сегмент-
ный регистр, то он определяется по умолчанию. Это делается по следую-
щим правилам.
1) В командах перехода адрес перехода сегментируется по регистру
CS и только по нему, т.к. абсолютный адрес команды, которая должна
быть выполнена следующей, всегда определяется парой CS:IP (попытка из-
менить в таких командах сегментный регистр будет безуспешной).
Отметим, что сегментиорвание по регистру CS касается именно адреса
перехода, а не адреса той ячейки, где он может находиться. Например, в
команде безусловного перехода по адресу, находящемуся в ячейке X:
JMP X
имя X сегментируется по регистру DS, а вот адрес перехода, взятый из
ячейки X, уже сегментируется по регистру CS.
2) Адреса во всех других командах, кроме строковых (STOS, MOVS,
SCAS и CMPS), по умолчанию сегментируются:
- по регистру DS, если среди указанных регистров-модификаторов
нет регистра BP;
- по регистру SS, если один из модификаторов - регистр BP.
Таким образом, адреса вида A, A[BX], A[SI], A[DI], A[BX][SI] и
A[BX][DI] сегментируются по регистру DS, а адреса A[BP], A[BP][SI] и
A[BP][DI] - по регистру SS, т.е. адреса трех последних видов использу-
ются для доступа к ячейкам стека.
3) В строковых командах STOS, MOVS, SCAS и CMPS, имеющих два опе-
ранда-адреса, на которые указывают индексные регистры SI и DI, один из
операндов (на который указывает SI) сегментируется по регистру DS, а
другой (на него указывает DI) - по регистру ES.
1.4.4 Программные сегменты. Директива ASSUME
Рассмотрим, как сегментирование проявляется в программах на MASM.
Для того чтобы указать, что некоторая группа предложений программы
на MASM образуют единый сегмент памяти, они оформляются как программ-
ный сегмент: перед ними ставится директива SEGMENT, после них - дирек-
тива ENDS, причем в начале обеих этих директив должно быть указано од-
но и то же имя, играющее роль имени сегмента. Программа же в целом
представляет собой последовательность таких программных сегментов, в
конце которой указывается директива конца программы END, например:
DT1 SEGMENT ;программный сегмент с именем DT1
A DB 0
B DW ?
DT1 ENDS
;
DT2 SEGMENT ;программный сегмент DT2
C DB 'hello'
DT2 ENDS
;
CODE SEGMENT ;программный сегмент CODE
ASSUME CS:CODE, DS:DT1, ES:DT2
BEG: MOV AX,DT2
MOV DS,AX
MOV BH,C
...
CODE ENDS
END BEG ;конец текста программы
Предложения программного сегмента ассемблер размещает в одном сег-
менте памяти (в совокупности они не должны занимать более 64Кб) начи-
ная с ближайшего свободного адреса, кратного 16. Номер (первые 16 би-
тов начального адреса) этого сегмента становится значением имени сег-
мента. В MASM это имя относится к константным выражениям, а не адрес-
ным, в связи с чем в команде
MOV AX,DT2
второй операнд является непосредственным, поэтому в регистр AX будет
записано начало (номер) сегмента DT2, а не содержимое начальной ячейки
этого сегмента.
Имена же переменных (A, B, C) и метки (BEG) относятся к адресным
выражениям, и им ставится в соответствие адрес их ячейки относительно
"своего" сегмента: имени A соответствует адрес 0, имени B - адрес 1,
имени C - адрес 0, а метке BEG - адрес 0.
Все ссылки на предложения одного программного сегмента ассемблер
сегментирует по умолчанию по одному и тому же сегментному регистру. По
какому именно - устанавливается специальной директивой ASSUME. В нашем
примере эта директива определяет, что все ссылки на сегмент CODE долж-
ны, если явно не указан сегментный регистр, сегментироваться по регис-
тру CS, все ссылки на DT1 - по регистру DS, а все ссылки на DT2 - по
регистру ES.
Встретив в тексте программы ссылку на какое-либо имя (например, на
имя C в команде MOV AX,C), ассемблер определяет, в каком программном
сегменте оно описано (у нас - в DT2), затем по информации из директивы
ASSUME узнает, какой сегментный регистр поставлен в соответствие этому
сегменту (у нас - это ES), и далее образует адресную пару иэ данного
регистра и смещения имени (у нас - ES:0), которую и записывает в фор-
мируемую машинную команду. При этом ассемблер учитывает используемое в
ПК соглашение о сегментных регистрах по умолчанию: если в адресной па-
ре, построенной им самим или явно заданной в программе, сегментный ре-
гистр совпадает с регистром по умолчанию, то в машинную команду зано-
сится лишь смещение. Если, скажем, в нашем примере встретится команда
MOV CX,B, тогда по имени В ассемблер построит пару DS:1, но раз опе-
ранд-адрес команды MOV по умолчанию сегментируется по регистру DS, то
записывать этот регистр в машинную команду излишне и ассемблер записы-
вает в нее только смещение 1.
Таким образом, директива ASSUME избавляет программистов от необхо-
димости выписывать полные адресные пары не только тогда, когда исполь-
зуются сегментные регистры по умолчанию (как в случае с именем B), но
тогда, когда в машинной команде нужно было бы явно указать сегментный
регистр (как в случае с именем C). В MASM сегментный регистр в ссылке
на имя требуется указывать лишь тогда, когда имя должно по каким-либо
причинам сегментироваться по регистру, отличному от того, что постав-
лен в соответствие всему сегменту, в котором это имя описано.
Однако все это справедливо только при соблюдении следующих усло-
вий. Во-первых, директива ASSUME должна быть указана перед первой ко-
мандой программы. В противном случае ассемблер, просматривающий текст
программы сверху вниз, не будет знать, как сегментировать имена из ко-
манд, расположенных до этой директивы, и потому зафиксирует ошибку.
Во-вторых, в директиве ASSUME следует каждому сегменту ставить в соот-
ветствие сегментный регистр: если ассемблеру встретится ссылка на имя
из сегмента, которому не соответствует никакой сегментный регистр, то
он зафиксирует ошибку. Правда, в обоих случаях можно избежать ошибки,
но для этого в ссылках необходимо явно указывать сегментный регистр.
